diff --git a/.all-contributorsrc b/.all-contributorsrc index d8f00878dd..c361fe7f22 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -400,6 +400,18 @@ "example" ] }, + { + "login": "zaytsevand", + "name": "Andrey Zaytsev", + "avatar_url": "https://avatars.githubusercontent.com/u/5207748?v=4", + "profile": "https://github.com/zaytsevand", + "contributions": [ + "code", + "example", + "doc", + "test" + ] + }, { "login": "codingtenshi", "name": "Tenshi Codes", @@ -439,6 +451,17 @@ "test" ] }, + { + "login": "anaysarkar7", + "name": "Anay Sarkar", + "avatar_url": "https://avatars.githubusercontent.com/u/53341181?v=4", + "profile": "https://linktr.ee/anaysarkar7", + "contributions": [ + "example", + "code", + "test" + ] + }, { "login": "LouisXhaferi", "name": "Louis Xhaferi", diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..06ec17bab4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.github/ +docs/ diff --git a/.eslintignore b/.eslintignore index d82e24ca22..0701a4ffd0 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,9 @@ node_modules docs lib -output \ No newline at end of file +output +src/generators/template +test/generators/template +examples/integrate-with-react +src/processors/TemplateInputProcessor.ts +test/processors/TemplateInputProcessor.spec.ts \ No newline at end of file diff --git a/.eslintrc b/.eslintrc index 681325a8cc..5efde8c889 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,14 +4,16 @@ "@typescript-eslint", "sonarjs", "security", - "github" + "github", + "prettier" ], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "plugin:sonarjs/recommended", - "plugin:security/recommended" + "plugin:security/recommended", + "prettier" ], "rules": { "strict": 0, @@ -78,10 +80,6 @@ ], "no-spaced-func": 2, "semi-spacing": 2, - "quotes": [ - 2, - "single" - ], "key-spacing": [ 2, { @@ -89,19 +87,8 @@ "afterColon": true } ], - "indent": [ - 2, - 2 - ], "no-lonely-if": 2, "no-floating-decimal": 2, - "brace-style": [ - 2, - "1tbs", - { - "allowSingleLine": true - } - ], "comma-style": [ 2, "last" @@ -183,7 +170,9 @@ "prefer-const": 2, "prefer-spread": 2, "prefer-template": 2, - "@typescript-eslint/no-unused-vars": 2 + "@typescript-eslint/no-unused-vars": 2, + "prettier/prettier": 2, + "sonarjs/no-identical-functions": "off" }, "overrides": [ { diff --git a/.github/workflows/if-nodejs-release.yml b/.github/workflows/if-nodejs-release.yml index 73bffb04ad..15eb3ac743 100644 --- a/.github/workflows/if-nodejs-release.yml +++ b/.github/workflows/if-nodejs-release.yml @@ -10,6 +10,7 @@ on: - master # below lines are not enough to have release supported for these branches # make sure configuration of `semantic-release` package mentions these branches + - next - next-spec - next-major - next-major-spec diff --git a/.gitignore b/.gitignore index d6c16e1b48..3cc80de7e1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ node_modules .vscode coverage lib -*.DS_Store \ No newline at end of file +*.DS_Store +.idea +output diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..fc98bd3760 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "trailingComma": "none", + "singleQuote": true, + "printWidth": 80 +} \ No newline at end of file diff --git a/API.md b/API.md index 16026a4649..4dbac13865 100644 --- a/API.md +++ b/API.md @@ -15,9 +15,6 @@ https://www.asyncapi.com/docs/specifications/v2.2.0#schemaObject https://www.asyncapi.com/docs/specifications/v2.3.0#schemaObject

-
CommonInputModel
-

This class is the wrapper for simplified models and the rest of the context needed for further generate typed models.

-
CommonModel

Common internal representation for a model.

@@ -30,6 +27,9 @@
Draft7Schema

JSON Draft7Schema Draft 7 model

+
InputMetaModel
+

Since each input processor can create multiple meta models this is a wrapper to a MetaModel to make that possible.

+
OpenapiV3Schema

OpenAPI 3.0 -> 3.0.4 schema model

Based on Draft 6, but with restricted keywords and definitions @@ -77,34 +77,63 @@ Modifications

SwaggerInputProcessor

Class for processing Swagger inputs

+
TemplateInputProcessor
+

Class for processing X input

+
LoggerClass

Logger class for the model generation library

This class acts as a forefront for any external loggers which is why it also implements the interface itself.

-## Members +## Functions
-
DefaultPropertyNames
-

Default property names for different aspects of the common model

+
convertToUnionModel()
+

Converts a CommonModel into multiple models wrapped in a union model.

+

Because a CommonModel might contain multiple models, it's name for each of those models would be the same, instead we slightly change the model name. +Each model has it's type as a name prepended to the union name.

+

If the CommonModel has multiple types

-
CommonNamingConventionImplementation
-

A CommonNamingConvention implementation shared between generators for different languages.

+
isDictionary()
+

Determine whether we have a dictionary or an object. because in some cases inputs might be: +{ "type": "object", "additionalProperties": { "$ref": "#" } } which is to be interpreted as a dictionary not an object model.

-
- -## Functions - -
-
getUniquePropertyName(rootModel, propertyName)
-

Recursively find the proper property name.

-

This function ensures that the property name is unique for the model

+
getOriginalInputFromAdditionalAndPatterns()
+

Return the original input based on additionalProperties and patternProperties.

+
+
convertAdditionalAndPatterns()
+

Function creating the right meta model based on additionalProperties and patternProperties.

+
+
NO_DUPLICATE_PROPERTIES(constrainedObjectModel, objectModel, propertyName, namingFormatter)
+

Because a lot of the other constrain functions (such as NO_NUMBER_START_CHAR, NO_EMPTY_VALUE, etc) they might manipulate the property names by append, prepend, or manipulate it any other way. +We then need to make sure that they don't clash with any existing properties, this is what this function handles. +If so, prepend reserved_ to the property name and recheck.

+
+
NO_DUPLICATE_ENUM_KEYS(constrainedEnumModel, enumModel, enumKey, namingFormatter, enumKeyToCheck, onNameChange, onNameChangeToCheck)string
+

Because a lot of the other constrain functions (such as NO_NUMBER_START_CHAR, NO_EMPTY_VALUE, etc) they might manipulate the enum keys by append, prepend, or manipulate it any other way. +We then need to make sure that they don't clash with any existing enum keys, this is what this function handles. +If so, prepend reserved_ to the enum key and recheck.

+
+
renderJavaScriptDependency(toImport, fromModule, moduleSystem)
+

Function to make it easier to render JS/TS dependencies based on module system

+
+
makeUnique(array)
+

Function to make an array of ConstrainedMetaModels only contain unique values (ignores different in memory instances)

+
+
lengthInUtf8Bytes()
+

Convert a string into utf-8 encoding and return the byte size.

hasPreset(presets, preset)

Returns true if and only if a given preset is already included in a list of presets Check is done using referential equality

+
trySplitModel(model, options, models)
+

Try split the model

+
+
split(model, options, models)
+

Overwrite the nested models with references where required.

+
interpretAdditionalItems(schema, model, interpreter, interpreterOptions)

Interpreter function for additionalItems keyword.

@@ -115,6 +144,10 @@ Check is done using referential equality

Interpreter function for allOf keyword.

It either merges allOf schemas into existing model or if allowed, create inheritance.

+
interpretAnyOf(schema, model, interpreter, interpreterOptions)
+

Interpreter function for anyOf keyword.

+

It puts the schema reference into the items field.

+
interpretConst(schema, model)

Interpreter function for const keyword for draft version > 4

@@ -133,24 +166,24 @@ Check is done using referential equality

interpretNot(schema, model, interpreter, interpreterOptions)

Interpreter function for not keyword.

+
interpretOneOf(schema, model, interpreter, interpreterOptions)
+

Interpreter function for oneOf keyword.

+

It puts the schema reference into the items field.

+
+
interpretOneOfWithAllOf(schema, model, interpreter, interpreterOptions)
+

Interpreter function for oneOf keyword combined with the allOf keyword.

+

It merges the allOf schemas into all of the oneOf schemas. Shared properties are merged. The oneOf schemas are then added as union to the model.

+
+
interpretOneOfWithProperties(schema, model, interpreter, interpreterOptions)
+

Interpreter function for oneOf keyword combined with properties.

+

It merges the properties of the schema into the oneOf schemas. Shared properties are merged. The oneOf schemas are then added as union to the model.

+
interpretPatternProperties(schema, model, interpreter, interpreterOptions)

Interpreter function for patternProperties keyword.

interpretProperties(schema, model, interpreter, interpreterOptions)

Interpreter function for interpreting properties keyword.

-
postInterpretModel(model)
-

Post process the interpreted model. By applying the following:

- -
-
trySplitModels(model, iteratedModels)
-

This function splits up a model if needed and add the new model to the list of models.

-
-
ensureModelsAreSplit(model, iteratedModels)
-

Split up all models which should and use ref instead.

-
isEnum(model)

Check if CommonModel is an enum

@@ -163,6 +196,13 @@ Check is done using referential equality

interpretName(schema)

Find the name for simplified version of schema

+
isClass(obj)
+

Return true or false based on whether the input object is a regular object or a class

+

Taken from: https://stackoverflow.com/a/43197340/6803886

+
+
mergePartialAndDefault()
+

Merge a non optional value with custom optional values to form a full value that has all properties sat.

+
@@ -173,39 +213,37 @@ Abstract generator which must be implemented by each language **Kind**: global class * [AbstractGenerator](#AbstractGenerator) - * [.generateCompleteModels(input, options)](#AbstractGenerator+generateCompleteModels) - * [.generate(input)](#AbstractGenerator+generate) + * [.getDependencyManagerInstance()](#AbstractGenerator+getDependencyManagerInstance) + * [.generateCompleteModels()](#AbstractGenerator+generateCompleteModels) + * [.generate()](#AbstractGenerator+generate) * [.processInput(input)](#AbstractGenerator+processInput) + * [.getPresets()](#AbstractGenerator+getPresets) + + +### abstractGenerator.getDependencyManagerInstance() +This function returns an instance of the dependency manager which is either a factory or an instance. + +**Kind**: instance method of [AbstractGenerator](#AbstractGenerator) -### abstractGenerator.generateCompleteModels(input, options) +### abstractGenerator.generateCompleteModels() Generates the full output of a model, instead of a scattered model. OutputModels result is no longer the model itself, but including package, package dependencies and model dependencies. **Kind**: instance method of [AbstractGenerator](#AbstractGenerator) - -| Param | Description | -| --- | --- | -| input | | -| options | to use for rendering full output | - -### abstractGenerator.generate(input) +### abstractGenerator.generate() Generates a scattered model where dependencies and rendered results are separated. **Kind**: instance method of [AbstractGenerator](#AbstractGenerator) - -| Param | -| --- | -| input | - ### abstractGenerator.processInput(input) -Process any of the input formats to the appropriate CommonInputModel type. +Process any of the input formats to the appropriate InputMetaModel type and split out the meta models +based on the requirements of the generators **Kind**: instance method of [AbstractGenerator](#AbstractGenerator) @@ -213,23 +251,18 @@ Process any of the input formats to the appropriate CommonInputModel type. | --- | | input | + + +### abstractGenerator.getPresets() +Get all presets (default and custom ones from options) for a given preset type (class, enum, etc). + +**Kind**: instance method of [AbstractGenerator](#AbstractGenerator) ## AbstractRenderer Abstract renderer with common helper methods **Kind**: global class - - -### abstractRenderer.addDependency(dependency) -Adds a dependency while ensuring that only one dependency is preset at a time. - -**Kind**: instance method of [AbstractRenderer](#AbstractRenderer) - -| Param | Description | -| --- | --- | -| dependency | complete dependency string so it can be rendered as is. | - ## AsyncapiV2Schema @@ -254,12 +287,6 @@ Takes a deep copy of the input object and converts it to an instance of Asyncapi | --- | | object | - - -## CommonInputModel -This class is the wrapper for simplified models and the rest of the context needed for further generate typed models. - -**Kind**: global class ## CommonModel @@ -276,21 +303,21 @@ Common internal representation for a model. * [.isRequired(propertyName)](#CommonModel+isRequired) ⇒ boolean * [.addItem(itemModel, originalInput)](#CommonModel+addItem) * [.addItemTuple(tupleModel, originalInput, index)](#CommonModel+addItemTuple) + * [.addItemUnion(unionModel)](#CommonModel+addItemUnion) * [.addEnum(enumValue)](#CommonModel+addEnum) * [.removeEnum(enumValue)](#CommonModel+removeEnum) * [.addProperty(propertyName, propertyModel, originalInput)](#CommonModel+addProperty) * [.addAdditionalProperty(additionalPropertiesModel, originalInput)](#CommonModel+addAdditionalProperty) - * [.addAdditionalItems(additionalItemsModel, originalInput)](#CommonModel+addAdditionalItems) * [.addPatternProperty(pattern, patternModel, originalInput)](#CommonModel+addPatternProperty) + * [.addAdditionalItems(additionalItemsModel, originalInput)](#CommonModel+addAdditionalItems) * [.addExtendedModel(extendedModel)](#CommonModel+addExtendedModel) - * [.getNearestDependencies()](#CommonModel+getNearestDependencies) * _static_ * [.toCommonModel(object)](#CommonModel.toCommonModel) ⇒ * [.mergeProperties(mergeTo, mergeFrom, originalInput, alreadyIteratedModels)](#CommonModel.mergeProperties) * [.mergeAdditionalProperties(mergeTo, mergeFrom, originalInput, alreadyIteratedModels)](#CommonModel.mergeAdditionalProperties) * [.mergeAdditionalItems(mergeTo, mergeFrom, originalInput, alreadyIteratedModels)](#CommonModel.mergeAdditionalItems) - * [.mergePatternProperties(mergeTo, mergeFrom, originalInput, alreadyIteratedModels)](#CommonModel.mergePatternProperties) * [.mergeItems(mergeTo, mergeFrom, originalInput, alreadyIteratedModels)](#CommonModel.mergeItems) + * [.mergePatternProperties(mergeTo, mergeFrom, originalInput, alreadyIteratedModels)](#CommonModel.mergePatternProperties) * [.mergeTypes(mergeTo, mergeFrom)](#CommonModel.mergeTypes) * [.mergeCommonModels(mergeTo, mergeFrom, originalInput, alreadyIteratedModels)](#CommonModel.mergeCommonModels) @@ -380,6 +407,17 @@ If a item already exist it will be merged. | originalInput | corresponding input that got interpreted to this model | | index | | + + +### commonModel.addItemUnion(unionModel) +Adds a union model to the model. + +**Kind**: instance method of [CommonModel](#CommonModel) + +| Param | +| --- | +| unionModel | + ### commonModel.addEnum(enumValue) @@ -431,31 +469,31 @@ If another model already exist the two are merged. | additionalPropertiesModel | | | originalInput | corresponding input that got interpreted to this model corresponding input that got interpreted to this model | - + -### commonModel.addAdditionalItems(additionalItemsModel, originalInput) -Adds additionalItems to the model. -If another model already exist the two are merged. +### commonModel.addPatternProperty(pattern, patternModel, originalInput) +Adds a patternProperty to the model. +If the pattern already exist the two models are merged. **Kind**: instance method of [CommonModel](#CommonModel) | Param | Description | | --- | --- | -| additionalItemsModel | | +| pattern | | +| patternModel | | | originalInput | corresponding input that got interpreted to this model | - + -### commonModel.addPatternProperty(pattern, patternModel, originalInput) -Adds a patternProperty to the model. -If the pattern already exist the two models are merged. +### commonModel.addAdditionalItems(additionalItemsModel, originalInput) +Adds additionalItems to the model. +If another model already exist the two are merged. **Kind**: instance method of [CommonModel](#CommonModel) | Param | Description | | --- | --- | -| pattern | | -| patternModel | | +| additionalItemsModel | | | originalInput | corresponding input that got interpreted to this model | @@ -471,12 +509,6 @@ It is only allowed to extend if the other model have $id and is not already bein | --- | | extendedModel | - - -### commonModel.getNearestDependencies() -Returns an array of unique `$id`s from all the CommonModel's this model depends on. - -**Kind**: instance method of [CommonModel](#CommonModel) ### CommonModel.toCommonModel(object) ⇒ @@ -531,10 +563,10 @@ Merge two common model additionalItems together | originalInput | corresponding input that got interpreted to this model | | alreadyIteratedModels | | - + -### CommonModel.mergePatternProperties(mergeTo, mergeFrom, originalInput, alreadyIteratedModels) -Merge two common model pattern properties together +### CommonModel.mergeItems(mergeTo, mergeFrom, originalInput, alreadyIteratedModels) +Merge items together, prefer tuples over simple array since it is more strict. **Kind**: static method of [CommonModel](#CommonModel) @@ -545,10 +577,10 @@ Merge two common model pattern properties together | originalInput | corresponding input that got interpreted to this model | | alreadyIteratedModels | | - + -### CommonModel.mergeItems(mergeTo, mergeFrom, originalInput, alreadyIteratedModels) -Merge items together, prefer tuples over simple array since it is more strict. +### CommonModel.mergePatternProperties(mergeTo, mergeFrom, originalInput, alreadyIteratedModels) +Merge two common model pattern properties together **Kind**: static method of [CommonModel](#CommonModel) @@ -636,6 +668,12 @@ Takes a deep copy of the input object and converts it to an instance of Draft7Sc | --- | | object | + + +## InputMetaModel +Since each input processor can create multiple meta models this is a wrapper to a MetaModel to make that possible. + +**Kind**: global class ## OpenapiV3Schema @@ -715,8 +753,8 @@ Class for processing AsyncAPI inputs * [.shouldProcess(input)](#AsyncAPIInputProcessor+shouldProcess) * [.tryGetVersionOfDocument(input)](#AsyncAPIInputProcessor+tryGetVersionOfDocument) * _static_ - * [.convertToInternalSchema(schema)](#AsyncAPIInputProcessor.convertToInternalSchema) * [.isFromParser(input)](#AsyncAPIInputProcessor.isFromParser) + * [.isFromNewParser(input)](#AsyncAPIInputProcessor.isFromNewParser) @@ -751,23 +789,21 @@ Try to find the AsyncAPI version from the input. If it cannot undefined are retu | --- | | input | - - -### AsyncAPIInputProcessor.convertToInternalSchema(schema) -Reflect the name of the schema and save it to `x-modelgen-inferred-name` extension. + -This keeps the the id of the model deterministic if used in conjunction with other AsyncAPI tools such as the generator. +### AsyncAPIInputProcessor.isFromParser(input) +Figure out if input is from the AsyncAPI parser. **Kind**: static method of [AsyncAPIInputProcessor](#AsyncAPIInputProcessor) -| Param | Description | -| --- | --- | -| schema | to reflect name for | +| Param | +| --- | +| input | - + -### AsyncAPIInputProcessor.isFromParser(input) -Figure out if input is from the AsyncAPI js parser. +### AsyncAPIInputProcessor.isFromNewParser(input) +Figure out if input is from the new AsyncAPI parser. **Kind**: static method of [AsyncAPIInputProcessor](#AsyncAPIInputProcessor) @@ -830,6 +866,7 @@ Class for processing JSON Schema * [.processDraft7(input)](#JsonSchemaInputProcessor+processDraft7) * [.processDraft4(input)](#JsonSchemaInputProcessor+processDraft4) * [.processDraft6(input)](#JsonSchemaInputProcessor+processDraft6) + * [.handleRootReference()](#JsonSchemaInputProcessor+handleRootReference) * _static_ * [.reflectSchemaNames(schema, namesStack, name, isRoot)](#JsonSchemaInputProcessor.reflectSchemaNames) * [.ensureNamePattern(previousName, ...newParts)](#JsonSchemaInputProcessor.ensureNamePattern) @@ -890,6 +927,14 @@ Process a draft-6 schema | --- | --- | | input | to process as draft-6 | + + +### jsonSchemaInputProcessor.handleRootReference() +This is a hotfix and really only a partial solution as it does not cover all cases. + +But it's the best we can do until we find or build a better library to handle references. + +**Kind**: instance method of [JsonSchemaInputProcessor](#JsonSchemaInputProcessor) ### JsonSchemaInputProcessor.reflectSchemaNames(schema, namesStack, name, isRoot) @@ -1051,6 +1096,12 @@ Converts a Swagger 2.0 Schema to the internal schema format. | schema | to convert | | name | of the schema | + + +## TemplateInputProcessor +Class for processing X input + +**Kind**: global class ## LoggerClass @@ -1070,32 +1121,102 @@ Sets the logger to use for the model generation library | --- | --- | | logger | to add | - + + +## convertToUnionModel() +Converts a CommonModel into multiple models wrapped in a union model. + +Because a CommonModel might contain multiple models, it's name for each of those models would be the same, instead we slightly change the model name. +Each model has it's type as a name prepended to the union name. + +If the CommonModel has multiple types + +**Kind**: global function + + +## isDictionary() +Determine whether we have a dictionary or an object. because in some cases inputs might be: +{ "type": "object", "additionalProperties": { "$ref": "#" } } which is to be interpreted as a dictionary not an object model. + +**Kind**: global function + + +## getOriginalInputFromAdditionalAndPatterns() +Return the original input based on additionalProperties and patternProperties. + +**Kind**: global function + + +## convertAdditionalAndPatterns() +Function creating the right meta model based on additionalProperties and patternProperties. + +**Kind**: global function + + +## NO\_DUPLICATE\_PROPERTIES(constrainedObjectModel, objectModel, propertyName, namingFormatter) +Because a lot of the other constrain functions (such as NO_NUMBER_START_CHAR, NO_EMPTY_VALUE, etc) they might manipulate the property names by append, prepend, or manipulate it any other way. +We then need to make sure that they don't clash with any existing properties, this is what this function handles. +If so, prepend `reserved_` to the property name and recheck. + +**Kind**: global function + +| Param | Description | +| --- | --- | +| constrainedObjectModel | the current constrained object model, which contains already existing constrained properties | +| objectModel | the raw object model which is non-constrained to the output language. | +| propertyName | one of the properties in objectModel which might have been manipulated | +| namingFormatter | the name formatter which are used to format the property key | -## DefaultPropertyNames -Default property names for different aspects of the common model + -**Kind**: global variable - +## NO\_DUPLICATE\_ENUM\_KEYS(constrainedEnumModel, enumModel, enumKey, namingFormatter, enumKeyToCheck, onNameChange, onNameChangeToCheck) ⇒ string +Because a lot of the other constrain functions (such as NO_NUMBER_START_CHAR, NO_EMPTY_VALUE, etc) they might manipulate the enum keys by append, prepend, or manipulate it any other way. +We then need to make sure that they don't clash with any existing enum keys, this is what this function handles. +If so, prepend `reserved_` to the enum key and recheck. -## CommonNamingConventionImplementation -A CommonNamingConvention implementation shared between generators for different languages. +**Kind**: global function +**Returns**: string - the potential new enum key that does not clash with existing enum keys. -**Kind**: global variable - +| Param | Description | +| --- | --- | +| constrainedEnumModel | the current constrained enum model, which contains already existing constrained enum keys | +| enumModel | the raw enum model which is non-constrained to the output language. | +| enumKey | one of the enum keys in enumModel which might have been manipulated. | +| namingFormatter | the name formatter which are used to format the enum key. | +| enumKeyToCheck | the enum key to use for checking if it already exist, defaults to enumKey. | +| onNameChange | callback to change the name of the enum key that needs to be returned. | +| onNameChangeToCheck | callback to change the enum key which is being checked as part of the existing models. | -## getUniquePropertyName(rootModel, propertyName) -Recursively find the proper property name. + -This function ensures that the property name is unique for the model +## renderJavaScriptDependency(toImport, fromModule, moduleSystem) +Function to make it easier to render JS/TS dependencies based on module system **Kind**: global function | Param | | --- | -| rootModel | -| propertyName | +| toImport | +| fromModule | +| moduleSystem | + + + +## makeUnique(array) +Function to make an array of ConstrainedMetaModels only contain unique values (ignores different in memory instances) + +**Kind**: global function +| Param | Description | +| --- | --- | +| array | to make unique | + + + +## lengthInUtf8Bytes() +Convert a string into utf-8 encoding and return the byte size. + +**Kind**: global function ## hasPreset(presets, preset) @@ -1109,6 +1230,34 @@ Check is done using referential equality | presets | the list to check | | preset | the preset to check for | + + +## trySplitModel(model, options, models) ⇒ +Try split the model + +**Kind**: global function +**Returns**: whether the new or old MetaModel to use. + +| Param | +| --- | +| model | +| options | +| models | + + + +## split(model, options, models) ⇒ +Overwrite the nested models with references where required. + +**Kind**: global function +**Returns**: an array of all the split models + +| Param | +| --- | +| model | +| options | +| models | + ## interpretAdditionalItems(schema, model, interpreter, interpreterOptions) @@ -1153,6 +1302,22 @@ It either merges allOf schemas into existing model or if allowed, create inherit | interpreter | | | interpreterOptions | to control the interpret process | + + +## interpretAnyOf(schema, model, interpreter, interpreterOptions) +Interpreter function for anyOf keyword. + +It puts the schema reference into the items field. + +**Kind**: global function + +| Param | Description | +| --- | --- | +| schema | | +| model | | +| interpreter | | +| interpreterOptions | to control the interpret process | + ## interpretConst(schema, model) @@ -1232,10 +1397,12 @@ Interpreter function for not keyword. | interpreter | | | interpreterOptions | to control the interpret process | - + -## interpretPatternProperties(schema, model, interpreter, interpreterOptions) -Interpreter function for patternProperties keyword. +## interpretOneOf(schema, model, interpreter, interpreterOptions) +Interpreter function for oneOf keyword. + +It puts the schema reference into the items field. **Kind**: global function @@ -1246,10 +1413,12 @@ Interpreter function for patternProperties keyword. | interpreter | | | interpreterOptions | to control the interpret process | - + -## interpretProperties(schema, model, interpreter, interpreterOptions) -Interpreter function for interpreting properties keyword. +## interpretOneOfWithAllOf(schema, model, interpreter, interpreterOptions) +Interpreter function for oneOf keyword combined with the allOf keyword. + +It merges the allOf schemas into all of the oneOf schemas. Shared properties are merged. The oneOf schemas are then added as union to the model. **Kind**: global function @@ -1260,41 +1429,49 @@ Interpreter function for interpreting properties keyword. | interpreter | | | interpreterOptions | to control the interpret process | - + -## postInterpretModel(model) -Post process the interpreted model. By applying the following: -- Ensure models are split as required +## interpretOneOfWithProperties(schema, model, interpreter, interpreterOptions) +Interpreter function for oneOf keyword combined with properties. + +It merges the properties of the schema into the oneOf schemas. Shared properties are merged. The oneOf schemas are then added as union to the model. **Kind**: global function -| Param | -| --- | -| model | +| Param | Description | +| --- | --- | +| schema | | +| model | | +| interpreter | | +| interpreterOptions | to control the interpret process | - + -## trySplitModels(model, iteratedModels) -This function splits up a model if needed and add the new model to the list of models. +## interpretPatternProperties(schema, model, interpreter, interpreterOptions) +Interpreter function for patternProperties keyword. **Kind**: global function | Param | Description | | --- | --- | -| model | check if it should be split up | -| iteratedModels | which have already been split up | +| schema | | +| model | | +| interpreter | | +| interpreterOptions | to control the interpret process | - + -## ensureModelsAreSplit(model, iteratedModels) -Split up all models which should and use ref instead. +## interpretProperties(schema, model, interpreter, interpreterOptions) +Interpreter function for interpreting properties keyword. **Kind**: global function | Param | Description | | --- | --- | -| model | to ensure are split | -| iteratedModels | which are already split | +| schema | | +| model | | +| interpreter | | +| interpreterOptions | to control the interpret process | @@ -1340,3 +1517,22 @@ Find the name for simplified version of schema | --- | --- | | schema | to find the name | + + +## isClass(obj) +Return true or false based on whether the input object is a regular object or a class + +Taken from: https://stackoverflow.com/a/43197340/6803886 + +**Kind**: global function + +| Param | +| --- | +| obj | + + + +## mergePartialAndDefault() +Merge a non optional value with custom optional values to form a full value that has all properties sat. + +**Kind**: global function diff --git a/CODEOWNERS b/CODEOWNERS index e0ce36c98f..ce4a305a3b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -7,23 +7,23 @@ # The default owners are automatically added as reviewers when you open a pull request unless different owners are specified in the file. # Core Champions that does a little of everything -* @magicmatatjahu @jonaslagoni @asyncapi-bot-eve +* @magicmatatjahu @jonaslagoni @asyncapi-bot-eve # Documentation champions /docs # Input Champions for AsyncAPI input -*/processors/AsyncAPIInputProcessor*.ts +*/processors/AsyncAPIInputProcessor*.ts # Input Champions for TypeScript input */processors/TypeScriptInputProcessor*.ts @ron-debajyoti # Input Champions for OpenAPI input -*/processors/OpenAPIInputProcessor*.ts -*/processors/SwaggerInputProcessor*.ts +*/processors/OpenAPIInputProcessor*.ts +*/processors/SwaggerInputProcessor*.ts # Input Champions for JSON Schema input -*/processors/JsonSchemaInputProcessor*.ts +*/processors/JsonSchemaInputProcessor*.ts # Language Champions for TypeScript and it's presets */generators/typescript @Samridhi-98 @@ -41,4 +41,10 @@ */generators/csharp @ron-debajyoti # Language Champions for Dart and it's presets -*/generators/dart \ No newline at end of file +*/generators/dart + +# Language Champions for Rust and its presets +*/generators/rust @leigh-johnson + +# Language Champions for Kotlin and it's presets +*/generators/kotlin @LouisXhaferi diff --git a/Dockerfile b/Dockerfile index d8c30d2aee..10e5b9a616 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update -yq \ # Install nodejs RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \ - && apt-get install -yq nodejs + && apt-get install -yq nodejs # Install golang RUN curl -fsSL https://golang.org/dl/go1.16.8.linux-amd64.tar.gz | tar -C /usr/local -xz @@ -17,9 +17,25 @@ RUN apt install apt-transport-https dirmngr gnupg ca-certificates -yq \ && apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF \ && echo "deb https://download.mono-project.com/repo/debian stable-buster main" | tee /etc/apt/sources.list.d/mono-official-stable.list \ && apt update -yq \ - && apt install mono-devel -yq + && apt install mono-devel -yq -# Setup library -COPY package-lock.json . +# Install rust +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + +# Install Python +RUN apt-get install -yq python + +# Install Kotlin +RUN apt install -yq wget unzip \ + && cd /usr/lib \ + && wget -q https://github.com/JetBrains/kotlin/releases/download/v1.8.0/kotlin-compiler-1.8.0.zip \ + && unzip -qq kotlin-compiler-*.zip + +ENV PATH $PATH:/usr/lib/kotlinc/bin + +# Setup library +RUN apt-get install -yq chromium + +COPY package.json package-lock.json ./ RUN npm install -COPY . . \ No newline at end of file +COPY . ./ diff --git a/README.md b/README.md index 0b7e594649..e67d73fda1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -[![AsyncAPI Modelina](./assets/readme-banner.png)](https://www.asyncapi.com/tools/modelina) - -Modelina is the official AsyncAPI SDK to generate data models (i.e. Java/TypeScript classes, Go Structs, etc) from AsyncAPI documents, among other supported inputs. +[![AsyncAPI Modelina](./docs/img/readme-banner.png)](https://www.asyncapi.com/tools/modelina) [![blackbox pipeline status]()](https://github.com/asyncapi/modelina/actions/workflows/blackbox-testing.yml?query=branch%3Amaster++) [![Coverage Status](https://coveralls.io/repos/github/asyncapi/modelina/badge.svg?branch=master)](https://coveralls.io/github/asyncapi/modelina?branch=master) @@ -11,40 +9,29 @@ Modelina is the official AsyncAPI SDK to generate data models (i.e. What Does Modelina Do? -## Features +

Modelina put YOU in control of your data models, here is how...

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modelina lets you generate data models from many types of inputs + +```typescript +const asyncapi = ... +const jsonschema = ... +const openapi = ... +const metamodel = ... +... +const models = await generator.generate( + asyncapi | jsonschema | openapi | metamodel +); +``` +
Use the same inputs across a range of different generators + +```typescript +const generator = new TypeScriptGenerator(); +const generator = new CsharpGenerator(); +const generator = new JavaGenerator(); +const generator = new RustGenerator(); +... +const models = await generator.generate(input); +``` +
Easily let you interact with the generated models. + +- Want to show the generated models on a website? Sure! +- Want to generate the models into files? Sure! +- Want to combine all the models into one single file? Sure! + +Whatever interaction you need, you can create. + +```typescript +const models = await generator.generate(input); +for (const model in models) { + const generatedCode = generatedModel.result; + const dependencies = generatedModel.dependencies; + const modeltype = generatedModel.model.type; + const modelName = generatedModel.modelName; + ... +} +``` +
Easily modify how models are constrained into the output + +```typescript +const generator = new TypeScriptGenerator({ + constraints: { + modelName: ({modelName}) => { + // Implement your own constraining logic + return modelName; + } + } +}); +``` +
Seamlessly layer additional or replacement code on top of each other to customize the models to your use-case + +```typescript +const generator = new TypeScriptGenerator({ + presets: [ + { + class: { + additionalContent({ content }) { + return `${content} +public myCustomFunction(): string { + return 'A custom function for each class'; +}`; + }, + } + } + ] +}); +const models = await generator.generate(input); +``` +
Seamlessly lets you combine multiple layers of additional or replacement code + +```typescript +const myCustomFunction1 = { + class: { + additionalContent({ content }) { + return `${content} +public myCustomFunction(): string { +return 'A custom function for each class'; +}`; + }, + } +}; +const myCustomFunction2 = {...}; +const generator = new TypeScriptGenerator({ + presets: [ + myCustomFunction1, + myCustomFunction2 + ] +}); +const models = await generator.generate(input); +``` +
-The following table provides a short summary of available features for supported output languages. +## Features -To see the complete feature list for each language, please click the individual links for each language. +The following table provides a short summary of available features for supported output languages. To see the complete feature list for each language, please click the individual links for each language.
@@ -67,11 +179,11 @@ To see the complete feature list for each language, please click the individual - + - + @@ -82,8 +194,12 @@ To see the complete feature list for each language, please click the individual - - + + + + + +
Supported inputsdescription
AsyncAPIWe support the following AsyncAPI versions: 2.0.0, 2.1.0, 2.2.0, 2.3.0 and 2.4.0, which generates models for all the defined message payloads.We support the following AsyncAPI versions: 2.0.0 -> 2.5.0, which generates models for all the defined message payloads.
JSON SchemaWe support the following OpenAPI versions: Swagger 2.0 and OpenAPI 3.0, which generates models for all the defined request and response payloads.
TypeScript fileWe currently support TypeScript type file as input for model generationTypeScriptWe currently support TypeScript types as file input for model generation
Meta modelThis is the internal representation of a model for Modelina, it is what inputs gets converted to, and what generators are provided to generate code. Instead of relying on an input processor, you can create your own models from scratch and still take advantage on the generators and the features.
@@ -93,7 +209,7 @@ To see the complete feature list for each language, please click the individual - + @@ -119,29 +235,69 @@ To see the complete feature list for each language, please click the individual + + + + + + + + + + + +
Supported outputsFeatures
JavaDart Class and enum generation: json_annotation
RustStruct/tuple and enum generation: generation of `implement Default`, generate serde macros, custom indentation type and size, etc
PythonClass and enum generation: custom indentation type and size, etc
KotlinClass and enum generation: use of data classes where appropriate, custom indentation type and size, etc
## Roadmap +This is the roadmap that is currently in focus by the [CODEOWNERS](./CODEOWNERS) + - [Reach version 1.0](https://github.com/asyncapi/modelina/milestone/3) +## Requirements +The following are a requirement in order to use Modelina. + +- [NodeJS](https://nodejs.org/en/) >= 14 + ## Documentation -Documentation for this library can be found under the [documentation folder](./docs/README.md). +A feature in Modelina cannot exists without an example and documentation for it. You can find all the [documentation here](./docs/README.md). ## Examples Do you need to know how to use the library in certain scenarios? We have gathered all the examples in a separate folder and they can be found under the [examples folder](./examples). +## Versioning and maintenance +As of version 1, Modelina has a very strict set of changes we are allowed to do before it requires a major version change. In short, any changes that change the generated outcome are not allowed as it's a breaking change for the consumer of the generated models. + +Here is a list of changes we are allowed to do that would not require a breaking change: +- Adding new features (that do not change existing output), such as generators, presets, input processors, etc. +- Change existing features, by providing options that default to current behavior. This could be a preset that adapts the output based on options, as long as the API of Modelina and the API of the generated models does not have any breaking changes. +- Bug fixes where the generated code is otherwise unusable (syntax errors, etc). + +Breaking changes are allowed and expected at a frequent rate, of course where it makes sense we will try to bundle multiple changes together. + +We of course will do our best to uphold this, but mistakes can happen, and if you notice any breaking changes please let us know! + +Because of the number of the limited number of champions, only the most recent major version will be maintained. + ## Development -To setup your development environment please read the [development](./docs/development.md) document. +We try to make it as easy for you as possible to set up your development environment to contribute to Modelina. You can find the development documentation [here](./docs/development.md). ## Contributing +Without contributions, Modelina would not exist, it's a community project we build together to create the best possible building blocks, and we do this through [champions](./docs/champions.md). -Read our [contributor](./docs/contributing.md) guide. +We have made quite a [comprehensive contribution guide](./docs/contributing.md) to give you a lending hand in how different features and changes are introduced. + +If no documentation helps you, here is how you can reach out to get help: +- On the [official AsycnAPI slack](https://asyncapi.com/slack-invite) under the `#04_tooling` channel +- Tag a specific [CODEOWNER](./CODEOWNERS) in your PR +- Generally, it's always a good idea to do everything in public, but in some cases, it might not be possible. In those circumstances you can contact the following: + - [jonaslagoni](https://github.com/jonaslagoni) (on [AsyncAPI Slack](https://asyncapi.com/slack-invite), [Twitter](https://twitter.com/jonaslagoni), [Email](mailto:jonas-lt@live.dk), [LinkedIn](https://www.linkedin.com/in/jonaslagoni/)) ## Contributors ✨ -Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): +Thanks go out to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): @@ -193,19 +349,22 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Kenneth Aasan
Kenneth Aasan

💻 ⚠️ Amit Kumar Sharma
Amit Kumar Sharma

⚠️ 📖 💡 - - Tenshi Codes
Tenshi Codes

🚇 - Yushi OMOTE
Yushi OMOTE

🐛 💻 - Zbigniew Malcherczyk
Zbigniew Malcherczyk

🐛 🚇 - 200Puls
200Puls

💻 ⚠️ + + Andrey Zaytsev
Andrey Zaytsev

💻 💡 📖 ⚠️ + Tenshi Codes
Tenshi Codes

🚇 + Yushi OMOTE
Yushi OMOTE

🐛 💻 + Zbigniew Malcherczyk
Zbigniew Malcherczyk

🐛 🚇 + 200Puls
200Puls

💻 ⚠️ + Anay Sarkar
Anay Sarkar

💡 💻 ⚠️ Louis Xhaferi
Louis Xhaferi

💻 ⚠️ 📖 💡 🚧 + -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome! diff --git a/docs/README.md b/docs/README.md index f77ea98c35..e4c4080952 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,6 +4,7 @@ +- [The Processing](#the-processing) - [Contributing](#contributing) - [Usage](#usage) - [Advanced](#advanced) @@ -12,12 +13,18 @@ - [Generators](#generators) - [Presets](#presets) - [Interpretation of JSON Schema](#interpretation-of-json-schema) +- [Migration](#migration) - [Languages](#languages) This document gives the overview of all the available documentation for Modelina. +### [The Processing](./input-processing.md) +Contains information how the internals of the processing works. + +This document contains all the information you need to understand the internal meta model and processes. + ### [Contributing](./contributing.md) Contains all the information you need to contribute to this project. @@ -39,13 +46,20 @@ Details which different generator options are supported. ### [Presets](./presets.md) Goes more in-depth into how the preset system works, which enables full customization of generators. -### [Interpretation of JSON Schema](./interpretation_of_JSON_Schema.md) +### [Interpretation of JSON Schema](./inputs/JSON_Schema.md) Explains how a JSON Schema is interpreted to a data model. +### [Migration](./migration.md) +As time goes on, major versions are inevitible and expected! You can find the migration guides here. + ### Languages Each language has its own limitations, corner cases, and features; thus, each language has separate documentation. - [C#](./languages/Csharp.md) +- [Dart](./languages/Dart.md) - [Go](./languages/Go.md) - [Java](./languages/Java.md) - [JavaScript](./languages/JavaScript.md) -- [TypeScript](./languages/TypeScript.md) \ No newline at end of file +- [Rust](./languages/Rust.md) +- [Python](./languages/Python.md) +- [TypeScript](./languages/TypeScript.md) +- [Kotlin](./languages/Kotlin.md) diff --git a/docs/advanced.md b/docs/advanced.md index 0708817f53..209a5472f2 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -1,4 +1,5 @@ # Advanced use-cases for Modelina + This document contains many of the advanced use-cases that you may stumble upon when pushing the limits of Modelina. @@ -9,19 +10,19 @@ This document contains many of the advanced use-cases that you may stumble upon - [Generate models to separate files](#generate-models-to-separate-files) - [Include a custom function in the data model](#include-a-custom-function-in-the-data-model) - [Use the models for data transfer](#use-the-models-for-data-transfer) -- [Extend the logic of an existing renderer](#extend-the-logic-of-an-existing-renderer) -- [Build your own model renderer](#build-your-own-model-renderer) -- [Create your own models from the ground up, instead of a supported input](#create-your-own-models-from-the-ground-up-instead-of-a-supported-input) - [Adapting input and outputs](#adapting-input-and-outputs) - [Add logging to library](#add-logging-to-library) - [Change the generated indentation type and size](#change-the-generated-indentation-type-and-size) -- [Change the naming format for properties](#change-the-naming-format-for-properties) -- [Change the naming format for data models](#change-the-naming-format-for-data-models) +- [Change the type mapping](#change-the-type-mapping) +- [Changing the constrain rules](#changing-the-constrain-rules) ## Generate each model in the same file -TODO + +This example shows us how to generate each model in the same file + +Check out this [example out for a live demonstration](../examples/generate-all-models-within-same-file). ## Generate models to separate files @@ -31,35 +32,30 @@ The reason for splitting the functionality is because in certain environments (l The file generators all follow the same pattern regardless of output language, which is the following format - `FileGenerator`. -Supported by: -- Java -- TypeScript -- C# -- Go -- JavaScript - > It is not supported in browsers. Check out this [example out for a live demonstration](../examples/generate-to-files). ## Include a custom function in the data model + Sometimes you want to include custom functionality into the generated models, this can be done through a custom preset using the hook `additionalContent`. Check out this [example out for a live demonstration](../examples/include-custom-function). ## Use the models for data transfer -TODO - -## Extend the logic of an existing renderer -TODO -## Build your own model renderer -TODO +One of the primary use-cases for the generated models, is to serialize and deserilize it to for example JSON, XML or binary. Each generator can have multiple ways to achieve this, and even support multiple libraries. This is achieved through presets, you can find them here for each output: -## Create your own models from the ground up, instead of a supported input -TODO +- [C#](./languages/Csharp.md#generate-serializer-and-deserializer-functionality) +- [Dart](./languages/Dart.md#generate-serializer-and-deserializer-functionality) +- Go currently does not support this. +- [Java](./languages/Java.md#generate-serializer-and-deserializer-functionality) +- [JavaScript](./languages/JavaScript.md#generate-serializer-and-deserializer-functionality) +- [Rust](./languages/Rust.md) +- [TypeScript](./languages/TypeScript.md#generate-serializer-and-deserializer-functionality) ## Adapting input and outputs + Sometimes you simply cannot make two things work together as you wished, maybe the input does not support it, or Modelina natively doesn't. However, because of the nature with presets, we can make it work anyway. > With great customization comes a great responsibility. Always make sure to raise your issue before trying workarounds, maybe you are not alone in the problem, and it should be natively supported, so please make your due diligence before venturing into this :pray: And always feel free to reach out on the AsyncAPI slack channel if you want some quicker feedback! @@ -67,11 +63,13 @@ Sometimes you simply cannot make two things work together as you wished, maybe t Check out this [example for a demonstration of how to adapt the input and out to a specific use-case](../examples/adapting-input-and-output). ## Add logging to library + When you generate models, by default, nothing is logged to the console or elsewhere. If you want to integrate a logging implementation specific to your needs, this library allows you to implement a detached logging module. The library uses 4 different logging levels: + - `debug`: for specific details only relevant to debugging - `info`: for general information relevant to the user - `warn`: for warnings a user may need if the output is not as expected @@ -80,12 +78,23 @@ The library uses 4 different logging levels: Check out this [example out for a live demonstration](../examples/custom-logging). ## Change the generated indentation type and size + In some scenarios, depending on how you stitch them together, you might need to change the indentation type or size and both of these cases are fully supported. Check out this [example out for a live demonstration](../examples/indentation-type-and-size). -## Change the naming format for properties -TODO +## Change the type mapping + +Sometimes, the default type mapping simply dont use the types you expected, or fit into your use-case. Thats why the entire mapping from [MetaModels](./internal-model.md#the-meta-model) to constrained types can be altered. + +Check out this [example out for a live demonstration](../examples/change-type-mapping). + +## Changing the constrain rules + +When moving from a [MetaModel](./internal-model.md#the-meta-model) to a [ConstrainedMetaModel](./internal-model.md#the-constrained-meta-model) it means we bind the input to a specific output. That output has specific constraints that the input MUST adhere to, [read more about this here](constraints.md). + +There can be multiple reasons why you want to change the default constrain rules, and therefore you can form them to your needs. + +Check out this [example out for a live demonstration](../examples/overwrite-default-constraint/) for how to overwrite the default constraints. -## Change the naming format for data models -TODO +Check out this [example out for a live demonstration](../examples/overwrite-naming-formatting/) for how to overwrite the naming formatting for models. diff --git a/docs/constraints.md b/docs/constraints.md new file mode 100644 index 0000000000..9100960a4d --- /dev/null +++ b/docs/constraints.md @@ -0,0 +1,36 @@ +# Constraints +Because we cannot control what is provided as input, we must constrain the internal model to be valid for the output ([read more about the process here](./internal-model.md)). What is considered valid entirely depends on the output so each have their own set of rules. + +As an example lets consider TypeScript as an output. Consider the model name, which are a simple string, but a string which can contain ANY characters and formats. Some of the constraints we have found for the naming of a model are: + +- Only a section of special characters are allowed as name for a model. For example `&name` cannot be rendered as class name, because it is syntactically incorrect: +```ts +class &name {} +``` +- Numbers may not be starting characters. For example `1Name` cannot be rendered as class name, because it is syntactically incorrect: +```ts +class 1name {} +``` + +There are many rules as such, but to get the full description about the default constraints here: + +- [C#](./constraints/CSharp.md) +- [Dart](./constraints/Dart.md) +- [Go](./constraints/Go.md) +- [Java](./constraints/Java.md) +- [JavaScript](./constraints/JavaScript.md) +- [Rust](./constraints/Rust.md) +- [TypeScript](./constraints/TypeScript.md) + +Even though there are many of these constraints, there might be reasons you want to customize the behavior to make it suit your use-case. Therefore each of the constraint rules can be overwritten completely and allow for you to implement your own behavior. + +We define these as two types, either you only want to change part of the logic, or you want to overwrite the entire constraint logic. +- [Overwriting the formatter](../examples/overwrite-naming-formatting) and keep the rest of the constraints as is. +- [Overwriting the entire naming constraint logic](../examples/overwrite-default-constraint) keeping none of the existing functionality which handles edge cases. It is recommended to **NOT** use this if it can be avoided, as you will limit yourself to what inputs can be generated to models. So make sure you know what you are doing :laughing: + +# Type mapping +To make it easier to use the meta models in presets and generators, we need to figure out the types for each model. This is to enable you to access the types from a property rather then calling a function. This is especially relevant because Modelina cannot fit all use-cases out of the box, and we therefore strive to make it tailorable to what ever your needs may be. The type mapping is one of those things that enable you to fine tune the types for your purpose. + +Of course it's not all output formats that have a type such as JavaScript, therefore these are only used for strongly typed outputs. + +You can checkout this example how to [change the type mapping](../examples/change-type-mapping/). diff --git a/docs/constraints/CSharp.md b/docs/constraints/CSharp.md new file mode 100644 index 0000000000..aff6525b7b --- /dev/null +++ b/docs/constraints/CSharp.md @@ -0,0 +1,38 @@ +# C# Constraints + +These are the C# specific constraints applied to the MetaModel before reaching the presets. [Read here to get a more general idea on the overall process](../input-processing.md) of converting a MetaModel to a ConstrainedMetaModel. + +## Model Naming +These are the constraints that is applied to model naming. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For csharp `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|C# has a list of reserved keywords ([see the full list here](../../src/generators/csharp/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| + +## Property naming +These are the constraints that is applied to object properties and the naming of them. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For csharp `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|C# has a list of reserved keywords ([see the full list here](../../src/generators/csharp/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_PROPERTIES|No duplicate properties|If any of the above constraints changes the property name, we must make sure that no duplicates exist within the same object. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| + + +## Enum key constraints +These are the constraints that is applied to enum keys. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For csharp `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|C# has a list of reserved keywords ([see the full list here](../../src/generators/csharp/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_KEYS|No duplicate enum keys|If any of the above constraints changes the enum key, we must make sure that no duplicates exist within the same enum. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| diff --git a/docs/constraints/Dart.md b/docs/constraints/Dart.md new file mode 100644 index 0000000000..235ebc7b99 --- /dev/null +++ b/docs/constraints/Dart.md @@ -0,0 +1,38 @@ +# Dart Constraints + +These are the Dart specific constraints applied to the MetaModel before reaching the presets. [Read here to get a more general idea on the overall process](../input-processing.md) of converting a MetaModel to a ConstrainedMetaModel. + +## Model Naming +These are the constraints that is applied to model naming. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For dart `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Dart has a list of reserved keywords ([see the full list here](../../src/generators/dart/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| + +## Property naming +These are the constraints that is applied to object properties and the naming of them. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For dart `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Dart has a list of reserved keywords ([see the full list here](../../src/generators/dart/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_PROPERTIES|No duplicate properties|If any of the above constraints changes the property name, we must make sure that no duplicates exist within the same object. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| + + +## Enum key constraints +These are the constraints that is applied to enum keys. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For dart `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Dart has a list of reserved keywords ([see the full list here](../../src/generators/dart/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_KEYS|No duplicate enum keys|If any of the above constraints changes the enum key, we must make sure that no duplicates exist within the same enum. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| diff --git a/docs/constraints/Go.md b/docs/constraints/Go.md new file mode 100644 index 0000000000..ad89332bc6 --- /dev/null +++ b/docs/constraints/Go.md @@ -0,0 +1,38 @@ +# Go Constraints + +These are the Go specific constraints applied to the MetaModel before reaching the presets. [Read here to get a more general idea on the overall process](../input-processing.md) of converting a MetaModel to a ConstrainedMetaModel. + +## Model Naming +These are the constraints that is applied to model naming. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For go `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Go has a list of reserved keywords ([see the full list here](../../src/generators/go/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| + +## Property naming +These are the constraints that is applied to object properties and the naming of them. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For go `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Go has a list of reserved keywords ([see the full list here](../../src/generators/go/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_PROPERTIES|No duplicate properties|If any of the above constraints changes the property name, we must make sure that no duplicates exist within the same object. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| + + +## Enum key constraints +These are the constraints that is applied to enum keys. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For go `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Go has a list of reserved keywords ([see the full list here](../../src/generators/go/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_KEYS|No duplicate enum keys|If any of the above constraints changes the enum key, we must make sure that no duplicates exist within the same enum. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| diff --git a/docs/constraints/Java.md b/docs/constraints/Java.md new file mode 100644 index 0000000000..545918abea --- /dev/null +++ b/docs/constraints/Java.md @@ -0,0 +1,38 @@ +# Java Constraints + +These are the Java specific constraints applied to the MetaModel before reaching the presets. [Read here to get a more general idea on the overall process](../input-processing.md) of converting a MetaModel to a ConstrainedMetaModel. + +## Model Naming +These are the constraints that is applied to model naming. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For java `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Java has a list of reserved keywords ([see the full list here](../../src/generators/java/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| + +## Property naming +These are the constraints that is applied to object properties and the naming of them. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For java `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Java has a list of reserved keywords ([see the full list here](../../src/generators/java/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_PROPERTIES|No duplicate properties|If any of the above constraints changes the property name, we must make sure that no duplicates exist within the same object. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| + + +## Enum key constraints +These are the constraints that is applied to enum keys. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For java `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Java has a list of reserved keywords ([see the full list here](../../src/generators/java/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_KEYS|No duplicate enum keys|If any of the above constraints changes the enum key, we must make sure that no duplicates exist within the same enum. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| diff --git a/docs/constraints/JavaScript.md b/docs/constraints/JavaScript.md new file mode 100644 index 0000000000..06adfda051 --- /dev/null +++ b/docs/constraints/JavaScript.md @@ -0,0 +1,38 @@ +# JavaScript Constraints + +These are the JavaScript specific constraints applied to the MetaModel before reaching the presets. [Read here to get a more general idea on the overall process](../input-processing.md) of converting a MetaModel to a ConstrainedMetaModel. + +## Model Naming +These are the constraints that is applied to model naming. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For javascript `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|JavaScript has a list of reserved keywords ([see the full list here](../../src/generators/javascript/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| + +## Property naming +These are the constraints that is applied to object properties and the naming of them. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For javascript `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|JavaScript has a list of reserved keywords ([see the full list here](../../src/generators/javascript/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_PROPERTIES|No duplicate properties|If any of the above constraints changes the property name, we must make sure that no duplicates exist within the same object. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| + + +## Enum key constraints +These are the constraints that is applied to enum keys. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For javascript `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|JavaScript has a list of reserved keywords ([see the full list here](../../src/generators/javascript/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_KEYS|No duplicate enum keys|If any of the above constraints changes the enum key, we must make sure that no duplicates exist within the same enum. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| diff --git a/docs/constraints/Rust.md b/docs/constraints/Rust.md new file mode 100644 index 0000000000..a7173be728 --- /dev/null +++ b/docs/constraints/Rust.md @@ -0,0 +1,37 @@ +# Rust Constraints + +These are the Rust specific constraints applied to the MetaModel before reaching the presets. [Read here to get a more general idea on the overall process](../input-processing.md) of converting a MetaModel to a ConstrainedMetaModel. + +## Model Naming +These are the constraints that is applied to model naming. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For Rust `_`, ` ` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Rust has a list of reserved keywords ([see the full list here](../../src/generators/rust/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| + +## Property naming +These are the constraints that is applied to object properties and the naming of them. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For Rust `_`, ` ` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Rust has a list of reserved keywords ([see the full list here](../../src/generators/rust/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using snake case| +|NO_DUPLICATE_PROPERTIES|No duplicate properties|If any of the above constraints changes the property name, we must make sure that no duplicates exist within the same object. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| + +## Enum key constraints +These are the constraints that is applied to enum keys. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For Rust `_` and ` ` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|Rust has a list of reserved keywords ([see the full list here](../../src/generators/rust/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_KEYS|No duplicate enum keys|If any of the above constraints changes the enum key, we must make sure that no duplicates exist within the same enum. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| diff --git a/docs/constraints/TypeScript.md b/docs/constraints/TypeScript.md new file mode 100644 index 0000000000..993b02d94c --- /dev/null +++ b/docs/constraints/TypeScript.md @@ -0,0 +1,38 @@ +# TypeScript Constraints + +These are the TypeScript specific constraints applied to the MetaModel before reaching the presets. [Read here to get a more general idea on the overall process](../input-processing.md) of converting a MetaModel to a ConstrainedMetaModel. + +## Model Naming +These are the constraints that is applied to model naming. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For typescript `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|TypeScript has a list of reserved keywords ([see the full list here](../../src/generators/typescript/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| + +## Property naming +These are the constraints that is applied to object properties and the naming of them. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For typescript `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|TypeScript has a list of reserved keywords ([see the full list here](../../src/generators/typescript/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_PROPERTIES|No duplicate properties|If any of the above constraints changes the property name, we must make sure that no duplicates exist within the same object. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| + + +## Enum key constraints +These are the constraints that is applied to enum keys. The `Rule key` is what you can use in the options to overwrite the default behavior. See [constraint customization](../constraints.md#Customization). + +|Rule key|Rule|Resolution| +|---|---|---| +|NO_SPECIAL_CHAR|No special characters| Special characters are replaced by their name, for example `!` is replaced with `exclamation`. For typescript `_` and `$` are an exception to this rule. | +|NO_NUMBER_START_CHAR|No numbers as starting characters|Default behavior is pre pending `number_` in front of the first character| +|NO_EMPTY_VALUE|No empty values|Default behavior is to use `empty` as name. | +|NO_RESERVED_KEYWORDS|No reserved keywords|TypeScript has a list of reserved keywords ([see the full list here](../../src/generators/typescript/Constants.ts))| +|NAMING_FORMATTER|Must be formatted equally|Model name is formatted using pascal case| +|NO_DUPLICATE_KEYS|No duplicate enum keys|If any of the above constraints changes the enum key, we must make sure that no duplicates exist within the same enum. If any is encountered `reserved_` is pre-pended. This is done recursively until no duplicates are found.| diff --git a/docs/contributing.md b/docs/contributing.md index 2f32c04a54..5e50e76c29 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -4,6 +4,8 @@ First of all, thank you 🙇🏾‍♀️ for considering contributing to Modeli This contribution guide is an extension to the core contributing guide that can be found [here](https://github.com/asyncapi/.github/blob/master/CONTRIBUTING.md). Please make sure you go through that beforehand. 🙂👍🏽 +If you have any questions, are unsure how your use-case fits in, or want something clarified, don't hesitate to [reach out on slack](https://asyncapi.com/slack-invite), we are always happy to help out! + ## Acceptance criteria and process Even though we love contributions, we need to maintain a certain standard of what can be merged into the codebase. @@ -23,7 +25,7 @@ The Acceptance Criteria for _adding new features_ requires a few things in order 1. **No documentation, no feature:** If a user cannot understand a new feature, that feature basically doesn't exist! Remember to make sure that any and all relevant [documentation](./) is consistently updated. - New features such as new presets, generators or inputs, etc, need associated use case documentation along side [examples](../examples). This is not only to showcase the feature, but to ensure it will always work. Checkout our [adding examples](#-adding-examples) doc for more information on how to do this. -### Adding Examples +### Adding examples The Acceptance Criteria Process for _adding examples_ is not only something we use to showcase features, but also to ensure those features always work. _(This is important since it is picked up by [our CI system](#What-does–the-CI-system-do-when-I-create-a-PR).)_ Adding examples is quite straight forward, so don't feel shy! Here's how to do it: @@ -35,6 +37,114 @@ Adding examples is quite straight forward, so don't feel shy! Here's how to do i Aaaand you are done! :tada: +### Adding a new preset +Presets are for when you want to customize the generated output, they work like middleware that layers on top of each other, you can read more [about presets here](./presets.md). + +Here is how you add a new preset: +1. All presets are located under `src/generators/${language}/presets`, either duplicate an existing preset and adapt it or create an empty TypeScript file. +2. The preset file has the syntax: +```ts +export const LANGUAGE_MY_PRESET: LanguagePreset = { + class: { + // Add preset hooks here + }, + // enum: { + // Add preset hooks here + // } +}; +``` +Replace `LANGUAGE` with the generator the preset is for (for example `TYPESCRIPT`), and replace `LanguagePreset` with the generator the preset is for (for example `TypeScriptPreset`). It is optional which models you add preset hooks for, i.e. you can add preset hooks for `enum` alongside for `class`, but it's not required. Each generator has a set of outputs you can change, read more [about the presets here](./presets.md). + +3. Add your preset to the `src/generators/${language}/presets/index.ts` file. +4. Add an [example](#adding-examples) to showcase your new feature. +5. Add documentation to [the language docs](./languages/) that explain the use case and links to your new example. +6. In most cases you want to add specific tests for edge cases or simply to test the preset. To do this add a new test file in `test/generators/${language}/presets/MyPreset.spec.ts` and replace `MyPreset` with your preset name. Now add a test using the following syntax: +```ts +describe('LANGUAGE_MY_PRESET', () => { + let generator: LanguageGenerator; + beforeEach(() => { + generator = new LanguageGenerator({ presets: [LANGUAGE_MY_PRESET] }); + }); + + test('should render xxx', async () => { + const input = { + $id: 'Clazz', + type: 'object', + properties: { + min_number_prop: { type: 'number' }, + max_number_prop: { type: 'number' }, + }, + }; + const models = await generator.generate(input); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); +}); +``` +Remember to replace `LANGUAGE` and `Language` with the appropriate values. + +Aaaand you are done! :tada: + +### Adding a new input processor +Input processors are the translators from inputs to MetaModel (read more about [the input processing here](./input-processing.md)). + +Here is how you can add a new input processor: +1. Duplicate the [template input processor](../src/processors/TemplateInputProcessor.ts) and rename it to the input you are adding a processor for. +2. Adapt the `shouldProcess` function which is used to detect whether an input processor should process the provided input. +3. Adapt the `process` function which is used to convert the input into meta models. +4. Duplicate the [template input processor tests](../test/processors/TemplateInputProcessor.spec.ts) and rename it to the input you are adding a processor for. +5. Adapt the testing code based on your input and the expected MetaModel conversion. +6. [Export your input processor](../src/processors/index.ts) +7. Add your input processor as part of the [main input processor](../src/processors/InputProcessor.ts) +8. Add a [test for the main input processor](../test/processors/InputProcessor.spec.ts) to ensure that your input processor are accessed accordingly. + +Thats it for the code and tests, now all that remains is docs and examples! :fire: +1. [Add a new example](#adding-examples) showcasing the new supported input. +2. Add the [usage example to the usage document](./usage.md). +3. Add the new supported input to the [main readme file](../README.md#features). + +Aaaand you are done! :tada: + +### Adding a new generator +Generators sits as the core of Modelina, which frames the core concepts of what you can generate. Therefore it's also no small task to create a new one, so dont get discourage, we are here to help you! + +To make it easier to contribute a new generator, and to avoid focusing too much of the internals of Modelina, we created a template generator to get you started. If you encounter discreprencies with the following guide or templates, make sure to raise it as an issue so it can be fixed! + +#### Getting started + +1. Start by copy/pasting the [template generator](../src/generators/template/) and [tests](../test/generators/template/) and rename it to your generator. +2. Search and replace within your new generator and test folder for `Template`, `template` and `TEMPLATE` and replace it with your generator name and match the cases. **Make sure you search and replace it with matching case**. +3. Replace the filenames `Template...` with your generator name. +4. Add your [generator to the generator index file](../src/generators/index.ts). + +Now it's time to adapt the template into what ever it is you are generating: +1. Adapt the [constraint logic](../src/generators/template/constrainer) and the [type constraints](../src/generators/template/TemplateConstrainer.ts) based on what is allowed within your output. Read more about the constraint logic [here](./constraints.md). +2. Add all of the reserved keywords that the models must never generate in the [Constant file](../src/generators/template/Constants.ts). +3. Adapt/create the first renderers. The template by default include two renderers, one for rendering enums and one for classes, but you can create what ever renderers makes sense. For example in Rust it's not called class but struct, so therefore its called a `StructRenderer`. +4. Adapt the file generator and the rendering of the complete models to fit your generator. + +An important note about presets, they are used to extend and build upon the default model, the bare minimum of a data model, so that Modelina can support multiple features. You can read more about [presets here](./presets.md). If you have any questions or want something clarified, don't hesitate to [reach out on slack](https://asyncapi.com/slack-invite). + +Time to adapt the tests, cause without tests, it's just an empty promise. The test that is included in the template is really just placeholders, so make sure you adapt them accordingly to your code. +1. Add a mocked renderer in the [TestRenderers](../test/TestUtils/TestRenderers.ts) file. +2. Adapt the [constrainer tests](../test/generators/template/TemplateConstrainer.spec.ts) based on the output. +3. Adapt the [reserved keywords tests](../test/generators/template/Constants.spec.ts) +4. Adapt the [generator tests](../test/generators/template/TemplateGenerator.spec.ts) +5. Adapt the [renderer tests](../test/generators/template/TemplateRenderer.spec.ts) +6. Add your generator to the [FileGenerators test](../test/generators/FileGenerators.spec.ts) to ensure the models are accurately written to files. +7. Lastly, we have (arguably) the most important tests, [the BlackBox tests](./development.md#blackbox-testing). They are to ensure that real-world inputs generate usable models that do not contain syntax errors. You can read more about the BlackBox tests [here](./development.md#blackbox-testing). + +Lastly, we need to adapt some of the docs to showcase your new awesome generator! Cause if the users cant find it, it dont exist. +1. Add your [generator specific documentation under languages](./languages/) and add it to the [list of generators](./README.md#languages) +2. Add your generator to the list of generators in the [main readme file](../README.md) +3. Add a basic [usage example to the usage documentation](./usage.md), you can see more about how to create [examples here](#adding-examples). + +Aaaand that's it! As a rule of thumb, start small and slowly add more features, don't try to push everything into one PR, as it will take forever to review, code, and merge. + +PR's you can look to for guidance on how the process goes: +- https://github.com/asyncapi/modelina/pull/818 +- https://github.com/asyncapi/modelina/pull/863 + ## FAQs Below are some of the typical questions we've received about contributing to Modelina. @@ -46,7 +156,6 @@ Regular issues are generally not that well described in terms of what needs to b If you find an issue you would like to solve, ping one of the maintainers to help you get started. Some issues may require a higher level of effort to solve than might be easily described within the issue, so don't feel shy to chat with us about individual issues. 😀 - ### What does the CI system do when I create a PR? Because the CI system is quite complex, we've designed it so that individual contributors don't need to understand in depth details. diff --git a/docs/development.md b/docs/development.md index 899022471b..62db7265c8 100644 --- a/docs/development.md +++ b/docs/development.md @@ -15,11 +15,11 @@ You can either build the image and run the needed commands manually or rather us To setup the environment follow these steps: -1. Make sure to use the appropriate node version as listed in the [package.json file](https://github.com/asyncapi/modelina/blob/ffc0cd8673791b262926093e381c17823fbe9565/package.json#L11). If you use `nvm`, you can simply do `nvm use`. +1. Make sure to use the appropriate node version as listed in the [package.json file](https://github.com/asyncapi/modelina/blob/ffc0cd8673791b262926093e381c17823fbe9565/package.json#L11). If you use `nvm`, you can simply do `nvm use` 2. Setup the project by first installing the dependencies `npm install` 3. Make sure the tests pass by running `npm run test` script - You can update snapshots by running `npm run test -- -u` -4. Make sure code is well formatted and secure `npm run lint` +4. Make sure code is well formatted and secure with eslint by running `npm run lint`, you can also auto format your code with `npm run format` ## BlackBox testing diff --git a/docs/generators.md b/docs/generators.md index 454342572e..e036b63a2c 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -28,14 +28,26 @@ ## Generator's options -Each generator extends default options for the generator. It means that the generator can also have additional options. +For Modelina, there exist 3 types of options for the generation process. +1. Default options, are the default options the rest overwrite. +2. Generator options, are used as the baseline that is by default used for each model render. +3. Render options, are the last resort to specialize options for the individual rendering of a model, that overwrite the generator options. -Options are passed as the first argument to the generator's constructor. Check the example: +Generator options are passed as the first argument to the generator's constructor. Check the example: ```ts const generator = new TypeScriptGenerator({ ...options }); ``` +Render options are passed as the first argument to the generator's render function. Check the example: + +```ts +const generator = ... +const model = ... +const inputModel = ... +generator.render(model, inputModel, { ...options }); +``` + Default generator options (common to all generators) are as follows: | Option | Type | Description | Default value | diff --git a/docs/img/ConstrainedMetaModel.png b/docs/img/ConstrainedMetaModel.png new file mode 100644 index 0000000000..8dd7ce0c9f Binary files /dev/null and b/docs/img/ConstrainedMetaModel.png differ diff --git a/docs/img/MetaModel.png b/docs/img/MetaModel.png new file mode 100644 index 0000000000..c1015a86ae Binary files /dev/null and b/docs/img/MetaModel.png differ diff --git a/docs/img/RenderingProcess.png b/docs/img/RenderingProcess.png new file mode 100644 index 0000000000..caa1e352c0 Binary files /dev/null and b/docs/img/RenderingProcess.png differ diff --git a/assets/readme-banner.png b/docs/img/readme-banner.png similarity index 100% rename from assets/readme-banner.png rename to docs/img/readme-banner.png diff --git a/docs/input-processing.md b/docs/input-processing.md new file mode 100644 index 0000000000..8a89682249 --- /dev/null +++ b/docs/input-processing.md @@ -0,0 +1,28 @@ +# The processor +Inputs generally don't have the faintest idea about the constraints of an output, and it is therefore the **meta model** does not have any constraints, and it is perfectly normal and expected to name your properties `my property`, or `"!"#/)"!`. + +Before the model reaches the presets, it gets transformed to a **constrained meta model**. Here it converts the raw **meta model** into only having valid values for the specific output. For example (and this accounts for almost all languages) you cannot render a property with the name `my property`, as most outputs don't allow properties to contain spaces and some common naming format such as `myProperty` or pascal case `MyProperty`. + +This transformation happen in three stages. + +

+ +

+ +1. Process the input and transform it into the meta model. See [the meta model](./internal-model.md#the-meta-model) for more information. +2. Split the meta model into separate models that are rendered separately. See [The splitting of meta models](#The-splitting-of-meta-models) for more information. +3. Constrain the meta models to the output. See [The constrained meta model](./internal-model.md#the-constrained-meta-model) for more information. + +## The splitting of meta models +Each generator requires a different splitting of the **meta model**s because it varies which should be rendered as is, and which need to be rendered separately. + +For example with the current TS generator, we split the following models: +- **ObjectModel**, because we want to generate it into interfaces, or classes +- **EnumModel**, because we want to generate a representation for enums + +For the Java generator, we split the following models: +- **ObjectModel**, because we want to generate it into a Java Class +- **EnumModel**, because we want to generate it into a Java Enum. +- **TupleModel** (TS have these models natively supported, Java don't, so we need to generate alternatives) +- **UnionModel** (TS have these models natively supported, Java don't, so we need to generate alternatives) + diff --git a/docs/interpretation_of_JSON_Schema.md b/docs/inputs/JSON_Schema.md similarity index 61% rename from docs/interpretation_of_JSON_Schema.md rename to docs/inputs/JSON_Schema.md index 3e8943ddac..eb0e846c6f 100644 --- a/docs/interpretation_of_JSON_Schema.md +++ b/docs/inputs/JSON_Schema.md @@ -5,6 +5,129 @@ The library transforms JSON Schema from data validation rules to data definition The algorithm tries to get to a model whose data can be validated against the JSON schema document. As of now we only provide the underlying structure of the schema file for the model, where constraints/annotations such as `maxItems`, `uniqueItems`, `multipleOf`, etc. are not interpreted. +## Patterns +Beside the regular interpreter we also look for certain patterns that are interpreted slightly differently. + +### `oneOf` with `allOf` +If both oneOf and allOf is present, each allOf model is merged into the interpreted oneOf. + +For example take this example: +```json +{ + "allOf":[ + { + "title":"Animal", + "type":"object", + "properties":{ + "animalType":{ + "title":"Animal Type", + "type":"string" + }, + "age":{ + "type":"integer", + "min":0 + } + } + } + ], + "oneOf":[ + { + "title":"Cat", + "type":"object", + "properties":{ + "animalType":{ + "const":"Cat" + }, + "huntingSkill":{ + "title":"Hunting Skill", + "type":"string", + "enum":[ + "clueless", + "lazy" + ] + } + } + }, + { + "title":"Dog", + "type":"object", + "additionalProperties":false, + "properties":{ + "animalType":{ + "const":"Dog" + }, + "breed":{ + "title":"Dog Breed", + "type":"string", + "enum":[ + "bulldog", + "bichons frise" + ] + } + } + } + ] +} +``` +Here animal is merged into cat and dog. + +### `oneOf` with `properties` +If both oneOf and properties are both present, it's interpreted exactly like [oneOf with allOf](#oneof-with-allof). That means that the following: + +```json +{ + "title":"Animal", + "type":"object", + "properties":{ + "animalType":{ + "title":"Animal Type", + "type":"string" + }, + "age":{ + "type":"integer", + "min":0 + } + }, + "oneOf":[ + { + "title":"Cat", + "type":"object", + "properties":{ + "animalType":{ + "const":"Cat" + }, + "huntingSkill":{ + "title":"Hunting Skill", + "type":"string", + "enum":[ + "clueless", + "lazy" + ] + } + } + }, + { + "title":"Dog", + "type":"object", + "additionalProperties":false, + "properties":{ + "animalType":{ + "const":"Dog" + }, + "breed":{ + "title":"Dog Breed", + "type":"string", + "enum":[ + "bulldog", + "bichons frise" + ] + } + } + } + ] +} +``` +where all the defined behavior on the root object are merged into the two oneOf models cat and dog. ## Interpreter The main functionality is located in the `Interpreter` class. This class ensures to recursively create (or retrieve from a cache) a `CommonModel` representation of a Schema. We have tried to keep the functionality split out into separate functions to reduce complexity and ensure it is easy to maintain. @@ -13,7 +136,7 @@ The order of interpretation: - `true` boolean schema infers all model types (`object`, `string`, `number`, `array`, `boolean`, `null`, `integer`) schemas. - `type` infers the initial model type. - `required` are interpreted as is. -- `patternProperties` are interpreted as is, where duplicate patterns for the model are [merged](#Merging-models). +- `patternProperties` are merged together with any additionalProperties, where duplicate pattern models are [merged](#Merging-models). - `additionalProperties` are interpreted as is, where duplicate additionalProperties for the model are [merged](#Merging-models). If the schema does not define `additionalProperties` it defaults to `true` schema. - `additionalItems` are interpreted as is, where duplicate additionalItems for the model are [merged](#Merging-models). If the schema does not define `additionalItems` it defaults to `true` schema. - `items` are interpreted as ether tuples or simple array, where more than 1 item are [merged](#Merging-models). Usage of `items` infers `array` model type. @@ -22,7 +145,7 @@ The order of interpretation: - `dependencies` only apply to schema dependencies, since property dependencies adds nothing to the underlying model. Any schema dependencies are interpreted and then [merged](#Merging-models) together with the current interpreted model. - `enum` is interpreted as is, where each `enum`. Usage of `enum` infers the enumerator value type to the model, but only if the schema does not have `type` specified. - `const` interpretation overwrite already interpreted `enum`. Usage of `const` infers the constant value type to the model, but only if the schema does not have `type` specified. -- [oneOf/anyOf/then/else](#Processing-sub-schemas) +- [allOf/oneOf/anyOf/then/else](#Processing-sub-schemas) - [not](#interpreting-not-schemas) ## Interpreting not schemas @@ -36,13 +159,9 @@ Currently, the following `not` model properties are interpreted: - You cannot use nested `not` schemas to infer new model properties, it can only be used to re-allow them. - boolean `not` schemas are not applied. -## allOf sub schemas -`allOf` is a bit different than the other [combination keywords](#Processing-sub-schemas) since it can imply inheritance. - -So dependant on whether the interpreter option `allowInheritance` is true or false we interpret it as inheritance or [merge](#Merging-models) the models. - ## Processing sub schemas The following JSON Schema keywords are [merged](#Merging-models) with the already interpreted model: +- `allOf` - `oneOf` - `anyOf` - `then` @@ -53,7 +172,7 @@ Because of the recursive nature of the interpreter (and the nested nature of JSO If only one side has a property defined, it is used as is, if both have it defined they are merged based on the following logic (look [here](./input_processing.md#Internal-model-representation) for more information about the CommonModel and its properties): - `additionalProperties` if both models contain it the two are recursively merged together. -- `patternProperties` if both models contain a pattern the corresponding models are recursively merged together. +- `patternProperties` if both models contain it each pattern model are recursively merged together. - `properties` if both models contain the same property the corresponding models are recursively merged together. - `items` are merged together based on a couple of rules: - If both models are simple arrays those item models are merged together as is. diff --git a/docs/integration.md b/docs/integration.md index aaa72fb502..a26f66bd91 100644 --- a/docs/integration.md +++ b/docs/integration.md @@ -1,16 +1,30 @@ -## Integrations +# Integrations +This readme file goes into details how to integrate Modelina into various environments. -- [Integrate Modelina in a website](#integrate-modelina-in-a-website) +- [Integrate Modelina in a browser](#integrate-modelina-in-a-browser) + * [Security NOTICE](#security-notice) - [Integrate Modelina in an AsyncAPI generator template](#integrate-modelina-in-an-asyncapi-generator-template) -## Integrate Modelina in a website -TODO +## Integrate Modelina in a browser + +Integrating Modelina into websites is is one of the core features, and each framework is different, so here are some of examples: + +- [Using Modelina in React](../examples/integrate-with-react/) + +There are a few exceptions to the features Modelina support in a website environment. Those are listed here below: + +- You cannot use the [file generator](./advanced.md#generate-models-to-separate-files) to write to the client's disk. + +### Security NOTICE +Do NOT enable users to write their own option callbacks. This includes but not limits to preset hooks and constrain rules. The reason for this is that in some cases it will enable arbitrary code execution on your webserver (which you most probably don't want!). + +To be on the safeside, only enable the user to chose between the internal options and presets, as you can see the [playground does](https://www.asyncapi.com/tools/modelina). ## Integrate Modelina in an AsyncAPI generator template TODO diff --git a/docs/internal-model.md b/docs/internal-model.md new file mode 100644 index 0000000000..1b4d27f3c0 --- /dev/null +++ b/docs/internal-model.md @@ -0,0 +1,40 @@ +# Internal model + +In order to generate data models from all kinds of inputs, we need a common structure for how we interact with one. That structure is called `MetaModel` often referred to as `Modelina Meta Model`, `Raw Meta Model`, or `MMM`. And there are two parts to it, there is the **meta model** and then the **constrained meta model**. + +## The Meta Model +The **meta model** is what inputs (now and in the future) such as Protobuf, JSON Schema, JSON Type Definition, GraphQL types, are gonna be converted into. This is also an input in it'self that you can provide Modelina to create your own input processor. + +These are the meta models and their meaning: +- **ArrayModel** is an unordered collection of a specific **MetaModel**. +- **TupleModel** is an ordered collection of **MetaModel**s. +- **EnumModel** is group of constants. +- **UnionModel** represent that the model can be either/or other **MetaModel**s. +- **ObjectModel** is a structure, that can be generated to class/interface/struct, etc, depending on the output language +- **DictionaryModel** is a map/dictionary of key/value **MetaModel**s. +- **ReferencedModel** is primarily used for when models should be split up ([see the splitting of meta models](#the-splitting-of-data-models)) and referenced, or it could be an external reference to an external entity. +- **BooleanModel** represent boolean values. +- **IntegerModel** represent natural numbers. +- **FloatModel** represent floating-point numbers. +- **StringModel** represent string values. +- **AnyModel** represent generic values that cannot otherwise be represented by one of the other models. + +

+ +

+ +## The Constrained Meta Model + +Before the **meta models**s reaches the generator, it needs to be `constrained` to the output. + +For example, constraining the **EnumModel** in Java means taking the raw enum key (for the **meta model** there are no constrains to what values may be used) such as `something% something` and convert it to a compliant Java enum key that can be accessed directly in the generator and presets. + +This means that if you accessed `EnumValueModel.key` you would get `something% something`, and with the Java constrained variant `ConstrainedEnumValueModel.key` you get (example) `SOMETHING_PERCENT_SOMETHING`. + +How and what are constrained? + +The answer to this question is not straightforward, cause each output has unique constraints that the meta models must adhere to. You can read more about [the constraint behavior here](constraints.md). + +

+ +

diff --git a/docs/languages/Csharp.md b/docs/languages/Csharp.md index ef0c004226..1d8a757ae1 100644 --- a/docs/languages/Csharp.md +++ b/docs/languages/Csharp.md @@ -6,23 +6,56 @@ There are special use-cases that each language supports; this document pertains -- [Generate serializer and deserializer functionality](#generate-serializer-and-deserializer-functionality) -- [Generate models with equals and GetHashCode methods](#generate-models-with-equals-and-gethashcode-methods) -- [Generate models with auto-implemented properties](#generate-models-with-auto-implemented-properties) -- [Change the collection type for arrays](#change-the-collection-type-for-arrays) -- [Generate custom enum value names](#generate-custom-enum-value-names) -- [Generate models with inheritance](#generate-models-with-inheritance) + * [Generate serializer and deserializer functionality](#generate-serializer-and-deserializer-functionality) + + [To and from JSON](#to-and-from-json) + - [Using native System.Text.Json](#using-native-systemtextjson) + - [Using Newtonsoft/Json.NET](#using-newtonsoftjsonnet) + + [To and from XML](#to-and-from-xml) + + [To and from binary](#to-and-from-binary) + * [Generate models with equals and GetHashCode methods](#generate-models-with-equals-and-gethashcode-methods) + * [Generate models with auto-implemented properties](#generate-models-with-auto-implemented-properties) + * [Change the collection type for arrays](#change-the-collection-type-for-arrays) + * [Generate custom enum value names](#generate-custom-enum-value-names) + * [Generate models with inheritance](#generate-models-with-inheritance) +- [FAQ](#faq) + + [Why is the type `dynamic` or `dynamic[]` when it should be `X`?](#why-is-the-type-dynamic-or-dynamic-when-it-should-be-x) ## Generate serializer and deserializer functionality -Sometimes you want to serialize the data models into JSON. In order to do that use the preset `CSHARP_JSON_SERIALIZER_PRESET` +The most widely used usecase for Modelina is to generate models that include serilization and deserialization functionality to convert the models into payload data. This payload data can of course be many different kinds, JSON, XML, raw binary, you name it. -**External dependencies:** +As you normally only need one library to do this, we developers can never get enough with creating new stuff, therefore there might be one specific library you need or want to integrate with. Therefore there is not one specific preset that offers everything. Below is a list of all the supported serialization presets. + +### To and from JSON +Here are all the supported presets and the libraries they use: + +- [Using native System.Text.Json](#using-native-systemtextjson) + +#### Using native System.Text.Json + +To include functionality that convert the models using the [System.Text.Json](https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/), to use this, use the preset `CSHARP_JSON_SERIALIZER_PRESET`. + +Check out this [example for a live demonstration](../../examples/csharp-generate-json-serializer). + +**External dependencies** Requires [System.Text.Json](https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/), [System.Text.Json.Serialization](https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-6-0) and [System.Text.RegularExpressions](https://docs.microsoft.com/en-us/dotnet/api/system.text.regularexpressions?view=net-6.0) to work. -Check out this [example for a live demonstration](../../examples/csharp-generate-serializer). +#### Using Newtonsoft/Json.NET + +To include functionality that convert the models using the [Newtonsoft/Json.NET](https://www.newtonsoft.com/json) framework, to use this, use the preset `CSHARP_NEWTONSOFT_SERIALIZER_PRESET`. + +Check out this [example for a live demonstration](../../examples/csharp-generate-newtonsoft-serializer). + +**External dependencies** +Requires [`Newtonsoft.Json`, `Newtonsoft.Json.Linq`](https://www.newtonsoft.com/json) and [System.Collections.Generic](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic?view=net-7.0). + +### To and from XML +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! + +### To and from binary +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! ## Generate models with equals and GetHashCode methods @@ -53,4 +86,10 @@ Check out this [example for a live demonstration](../../examples/csharp-overwrit If you want the generated models to inherit from a custom class, you can overwrite the existing rendering behavior and create your own class setup. Check out this [example for a live demonstration](../../examples/csharp-use-inheritance). +# FAQ +This is the most asked questions and answers which should be your GOTO list to check before asking anywhere else. Cause it might already have been answered! + +### Why is the type `dynamic` or `dynamic[]` when it should be `X`? +Often times you might encounter variables which as of type `dynamic` or `dynamic[]`, which is our fallback type when we cannot accurately find the right type. +**If you are encountering this when your input is JSON Schema/OpenAPI/AsyncAPI**, it most likely is because of a property being defined as having multiple types as a union, which the C# generator cannot natively handle and fallback to `dynamic`. For arrays, you have to remember that `additionalItems` is by default `true`, this means that even though you use `items: { type: "string"}` by not setting `additionalItems: false`, it's the same as setting `items: { type: ["array", "boolean", "integer", "null", "number", "object", "string"]}`. \ No newline at end of file diff --git a/docs/languages/Dart.md b/docs/languages/Dart.md index 1d2f1ded6b..5c7e5406d3 100644 --- a/docs/languages/Dart.md +++ b/docs/languages/Dart.md @@ -5,12 +5,33 @@ There are special use-cases that each language supports; this document pertains -- [Include json_annotation for the class and enums](#include-json-annotation-for-the-class) +- [Generate serializer and deserializer functionality](#generate-serializer-and-deserializer-functionality) + * [To and from JSON](#to-and-from-json) + + [JSON annotation](#json-annotation) + * [To and from XML](#to-and-from-xml) + * [To and from binary](#to-and-from-binary) -## Include Json annotation for the class and enums +## Generate serializer and deserializer functionality -When you generate the models with json annotation, generated files include json_annotation package (https://pub.dev/packages/json_annotation/) and their syntax +The most widely used usecase for Modelina is to generate models that include serilization and deserialization functionality to convert the models into payload data. This payload data can of course be many different kinds, JSON, XML, raw binary, you name it. + +As you normally only need one library to do this, we developers can never get enough with creating new stuff, therefore there might be one specific library you need or want to integrate with. Therefore there is not one specific preset that offers everything. Below is a list of all the supported serialization presets. + +### To and from JSON +Here are all the supported presets and the libraries they use: + +- [JSON annotation](#json-annotation) + +#### JSON annotation + +When you generate the models with json annotation, generated files include json_annotation package (https://pub.dev/packages/json_annotation/) and their syntax. Check out this [example for a live demonstration](../../examples/dart-generate-json-annotation). + +### To and from XML +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! + +### To and from binary +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! diff --git a/docs/languages/Java.md b/docs/languages/Java.md index 3c262fb26d..f8745ec24d 100644 --- a/docs/languages/Java.md +++ b/docs/languages/Java.md @@ -12,8 +12,12 @@ There are special use-cases that each language supports; this document pertains - [Include toString function for the class](#include-tostring-function-for-the-class) - [Include JavaDoc for properties](#include-javadoc-for-properties) - [Include Javax validation constraint annotations for properties](#include-javax-validation-constraint-annotations-for-properties) -- [Include Jackson annotations for the class](#include-jackson-annotations-for-the-class) -- [Include JSON marshaling and unmarshaling methods](#include-json-marshaling-and-unmarshaling-methods) +- [Generate serializer and deserializer functionality](#generate-serializer-and-deserializer-functionality) + * [To and from JSON](#to-and-from-json) + + [Jackson annotation](#jackson-annotation) + + [JSON marshaling and unmarshaling methods](#json-marshaling-and-unmarshaling-methods) + * [To and from XML](#to-and-from-xml) + * [To and from binary](#to-and-from-binary) @@ -53,17 +57,38 @@ In some cases, when you generate the models from JSON Schema, you may want to in Check out this [example for a live demonstration](../../examples/java-generate-javax-constraint-annotation). -## Include Jackson annotations for the class +## Generate serializer and deserializer functionality + +The most widely used usecase for Modelina is to generate models that include serilization and deserialization functionality to convert the models into payload data. This payload data can of course be many different kinds, JSON, XML, raw binary, you name it. + +As you normally only need one library to do this, we developers can never get enough with creating new stuff, therefore there might be one specific library you need or want to integrate with. Therefore there is not one specific preset that offers everything. Below is a list of all the supported serialization presets. + +### To and from JSON +Here are all the supported presets and the libraries they use: + +- [Jackson annotation](#jackson-annotation) +- [JSON marshaling and unmarshaling methods](#json-marshaling-and-unmarshaling-methods) + +#### Jackson annotation To generate Java data models with Jackson annotation using `JAVA_JACKSON_PRESET` option. Check out this [example for a live demonstration](../../examples/java-generate-jackson-annotation). -## Include JSON marshaling and unmarshaling methods +**External dependencies** +Requires [com.fasterxml.jackson.annotation](https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations) to work. + +#### JSON marshaling and unmarshaling methods Sometimes you just want to convert your class to JSON without the use of annotations such as Jackson. Check out this [example for a live demonstration](../../examples/java-generate-marshalling). -External dependencies -- Requires [org.json package](https://search.maven.org/artifact/org.json/json/20211205/bundle) to work +**External dependencies** +Requires [org.json package](https://search.maven.org/artifact/org.json/json/20211205/bundle) to work. + +### To and from XML +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! + +### To and from binary +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! diff --git a/docs/languages/JavaScript.md b/docs/languages/JavaScript.md index 770ec6137f..5b918724dd 100644 --- a/docs/languages/JavaScript.md +++ b/docs/languages/JavaScript.md @@ -6,7 +6,11 @@ There are special use-cases that each language supports; this document pertains - [Rendering complete models to a specific module system](#rendering-complete-models-to-a-specific-module-system) -- [Generate un/marshal functions for classes](#generate-unmarshal-functions-for-classes) +- [Generate serializer and deserializer functionality](#generate-serializer-and-deserializer-functionality) + * [To and from JSON](#to-and-from-json) + + [Generate marshalling and unmarshalling functions](#generate-marshalling-and-unmarshalling-functions) + * [To and from XML](#to-and-from-xml) + * [To and from binary](#to-and-from-binary) - [Generate example data function](#generate-example-data-function) @@ -18,15 +22,30 @@ Check out this [example for a live demonstration how to generate the complete Ja Check out this [example for a live demonstration how to generate the complete JavaScript models to use CJS module system](../../examples/javascript-use-cjs). +## Generate serializer and deserializer functionality -## Generate un/marshal functions for classes +The most widely used usecase for Modelina is to generate models that include serilization and deserialization functionality to convert the models into payload data. This payload data can of course be many different kinds, JSON, XML, raw binary, you name it. -Sometimes you want to use the models for data transfers, and while most cases would work out of the box, custom serializer functionality is needed for the advanced cases. If you generated the data models based on a JSON Schema document and you want the serialized data to validate against the schema, this functionality is REQUIRED. +As you normally only need one library to do this, we developers can never get enough with creating new stuff, therefore there might be one specific library you need or want to integrate with. Therefore there is not one specific preset that offers everything. Below is a list of all the supported serialization presets. + +### To and from JSON +Here are all the supported presets and the libraries they use: + +- [Generate marshalling and unmarshalling functions](#generate-marshalling-and-unmarshalling-functions) + +#### Generate marshalling and unmarshalling functions + +Using the preset `JS_COMMON_PRESET` with the option `marshalling` to `true`, renders two function for the class models. One which convert the model to JSON and another which convert the model from JSON to an instance of the class. -Here, this can be done by including the preset `JS_COMMON_PRESET` using the option `marshalling`. Check out this [example out for a live demonstration](../../examples/javascript-generate-marshalling). +### To and from XML +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! + +### To and from binary +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! + ## Generate example data function Generate example instance of the data model including the preset `JS_COMMON_PRESET` using the option `example`. diff --git a/docs/languages/Kotlin.md b/docs/languages/Kotlin.md new file mode 100644 index 0000000000..57fcbc66b0 --- /dev/null +++ b/docs/languages/Kotlin.md @@ -0,0 +1,52 @@ +# Kotlin + +There are special use-cases that each language supports; this document pertains to **Kotlin models**. + +Since `data classes` are used for every model that has got properties, there is no need for additional settings or +features to generate `toString()`, `equals()`, `hashCode()`, getters or setters. + +Classes without properties are depicted by usual `classes`, they get no `toString()`, `equals()` or `hashCode()` +implementation. The default one should suffice here. + + + + + +- [Include KDoc for properties](#include-kdoc-for-properties) +- [Change the collection type for arrays](#change-the-collection-type-for-arrays) +- [Include Javax validation constraint annotations for properties](#include-javax-validation-constraint-annotations-for-properties) +- [Generate serializer and deserializer functionality](#generate-serializer-and-deserializer-functionality) + * [To and from JSON](#to-and-from-json) + * [To and from XML](#to-and-from-xml) + * [To and from binary](#to-and-from-binary) + + +## Include KDoc for properties +To generate models containing `KDoc` from description and examples, use the `KOTLIN_DESCRIPTION_PRESET` option. + +Check out this [example for a live demonstration](../../examples/kotlin-generate-kdoc). + +## Change the collection type for arrays + +Sometimes, we might want to render a different collection type, and instead of the default `Array` use as `List` type. To do so, provide the option `collectionType: 'List'`. + +Check out this [example for a live demonstration](../../examples/kotlin-change-collection-type). + +## Include Javax validation constraint annotations for properties + +In some cases, when you generate the models from JSON Schema, you may want to include `javax.validation.constraint` annotations. + +Check out this [example for a live demonstration](../../examples/kotlin-generate-javax-constraint-annotation). + +## Generate serializer and deserializer functionality + +The most widely used usecase for Modelina is to generate models that include serialization and deserialization functionality to convert the models into payload data. This payload data can of course be many kinds, JSON, XML, raw binary, you name it. + +As you normally only need one library to do this, we developers can never get enough with creating new stuff, therefore there might be one specific library you need or want to integrate with. Therefore, there is not one specific preset that offers everything. Below is a list of all the supported serialization presets. + +### To and from JSON +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! +### To and from XML +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! +### To and from binary +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! diff --git a/docs/languages/Python.md b/docs/languages/Python.md new file mode 100644 index 0000000000..91a9173d4c --- /dev/null +++ b/docs/languages/Python.md @@ -0,0 +1,16 @@ +# Python + +There are special use-cases that each language supports; this document pertains to **Python models**. + + + + + +- [Generate Pydantic models](#generate-pydantic-models) + + + +## Generate Pydantic models +In some cases you might want to use [pydantic](https://pypi.org/project/pydantic/) data validation and settings management using Python type hints for the models. + +You can find an example of its use [here](../../examples/generate-python-pydantic-models/index.ts) diff --git a/docs/languages/Rust.md b/docs/languages/Rust.md new file mode 100644 index 0000000000..79cd94aae9 --- /dev/null +++ b/docs/languages/Rust.md @@ -0,0 +1,57 @@ +# Rust + + + + + +- [Language Features](#language-features) +- [Generator Features](#generator-features) +- [Implement `new`](#implement-new) +- [Implement `default`](#implement-default) +- [Implement `From (serde_json)`](#implement-from_json_string) +- [Implement `Into (serde_json)`](#implement-to_json_stringn) +- [Implement `From (tokio_serde)`](#implement-from-framed-byte-stream) +- [Implement `Into (tokio_serde)`](#implement-into-framed-byte-stream) + + +## Language Features + +Generated code depends on the following Cargo features: + +- derive +- alloc + +## Generator Features + + +| **Feature** | **Status** | **Info** | +|------------------------------|---------------|---------------------------------------------------------------------------------------| +| Union (polymorphic type) | ✅ done | | +| Enum (group of constants) | ✅ done | | +| Array (unordered collection) | ✅ done | | +| Tuple (ordered collection) | ✅ done | | + + +## Implement `new` + +To generate a `new` method, use the preset `RUST_COMMON_PRESET` and provide the option `implementNew: true`. + +## Implement `Default` for enums + +To generate `Default` implementation for enums that provide a default value, use the preset `RUST_COMMON_PRESET` and provide the option `implementDefault: true`. + +## Implement `From` (serde_json) + +TODO + +## Implement `Into` (serde_json) + +TODO + +## Implement `From` (tokio_serde) + +TODO + +## Implement `From` (tokio_serde) + +TOOD \ No newline at end of file diff --git a/docs/languages/TypeScript.md b/docs/languages/TypeScript.md index df2b1e46b0..1f9ff191f9 100644 --- a/docs/languages/TypeScript.md +++ b/docs/languages/TypeScript.md @@ -8,7 +8,12 @@ There are special use-cases that each language supports; this document pertains - [Generate an interface instead of classes](#generate-an-interface-instead-of-classes) - [Generate union types instead of enums](#generate-union-types-instead-of-enums) -- [Generate un/marshal functions for classes](#generate-unmarshal-functions-for-classes) +- [Generate serializer and deserializer functionality](#generate-serializer-and-deserializer-functionality) + * [To and from JSON](#to-and-from-json) + + [Generate marshalling and unmarshalling functions](#generate-marshalling-and-unmarshalling-functions) + * [To and from XML](#to-and-from-xml) + * [To and from binary](#to-and-from-binary) + + [Generate models with jsonbinpack support](#generate-models-with-jsonbinpack-support) - [Generate example data function](#generate-example-data-function) - [Rendering complete models to a specific module system](#rendering-complete-models-to-a-specific-module-system) - [Rendering comments from description and example fields](#rendering-comments-from-description-and-example-fields) @@ -37,14 +42,45 @@ export type Event = "ping" | "pong"; Check out this [example out for a live demonstration](../../examples/typescript-enum-type). -## Generate un/marshal functions for classes +## Generate serializer and deserializer functionality -Sometimes you want to use the models for data transfers, and while most cases would work out of the box, custom serializer functionality is needed for the advanced cases. If you generated the data models based on a JSON Schema document and you want the serialized data to validate against the schema, this functionality is REQUIRED. +The most widely used usecase for Modelina is to generate models that include serilization and deserialization functionality to convert the models into payload data. This payload data can of course be many different kinds, JSON, XML, raw binary, you name it. -This can be done by including the preset `TS_COMMON_PRESET` using the option `marshalling`. +As you normally only need one library to do this, we developers can never get enough with creating new stuff, therefore there might be one specific library you need or want to integrate with. Therefore there is not one specific preset that offers everything. Below is a list of all the supported serialization presets. + +### To and from JSON +Here are all the supported presets and the libraries they use for converting to and from JSON: + +- [Generate marshalling and unmarshalling functions](#generate-marshalling-and-unmarshalling-functions) + +#### Generate marshalling and unmarshalling functions + +Using the preset `TS_COMMON_PRESET` with the option `marshalling` to `true`, renders two function for the class models. One which convert the model to JSON and another which convert the model from JSON to an instance of the class. Check out this [example out for a live demonstration](../../examples/typescript-generate-marshalling). +### To and from XML +Currently not supported, [let everyone know you need it](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md)! + +### To and from binary +Here are all the supported presets and the libraries they use for converting to and from binary: + +- [Generate jsonbinpack functions](#generate-models-with-jsonbinpack-support) + +#### Generate models with jsonbinpack support + +This functionality is for the library [jsonbinpack](https://github.com/sourcemeta/jsonbinpack). + +This preset can ONLY be used with AsyncAPI 2.x and JSON Schema draft 4 to 7 inputs. + +This functionality has two requirements: +1. You MUST manually install the library `jsonbinpack`. +2. You MUST also use the [Generate un/marshal functions for classes](#generate-unmarshal-functions-for-classes) + +This feature allows you to convert models to a buffer, which is highly space-efficient, instead of sending pure JSON data over the wire. + +Check out this [example out for a live demonstration](../../examples/typescript-generate-jsonbinpack/). + ## Generate example data function You might stumble upon a user case (we had one in code generation) where you want a simple example instance of the generated data model. @@ -53,7 +89,6 @@ This can be done by including the preset `TS_COMMON_PRESET` using the option `ex Check out this [example out for a live demonstration](../../examples/typescript-generate-example). - ## Rendering complete models to a specific module system In some cases you might need to render the complete models to a specific module system such as ESM and CJS. diff --git a/docs/migration.md b/docs/migration.md new file mode 100644 index 0000000000..8283cc510a --- /dev/null +++ b/docs/migration.md @@ -0,0 +1,7 @@ +# Migration guides + +As Modelina progresses and evolves over time, sometimes it's inevitable that a major version change is required. + +When it happens, we try to list the breaking changes and a migration guide, to make transitioning from one major version to another as painless as possible. + +- [From v0 to v1](./migrations/version-0-to-1.md) \ No newline at end of file diff --git a/docs/migrations/version-0-to-1.md b/docs/migrations/version-0-to-1.md new file mode 100644 index 0000000000..eb70179bdf --- /dev/null +++ b/docs/migrations/version-0-to-1.md @@ -0,0 +1,58 @@ +# Migration from v0 to v1 +This document contain all the breaking changes and migrations guidelines for adapting your code to the new version. + +## Naming conventions have been removed +In version 0, you where able to control the naming formats for properties and model through the option `namingConvention`. + +This is now removed and replaced with `constraints` which is a mean to simplify the complex task of constraining any type of values to the specifics of an output. Each output has their unique constraints to what is allowed and what is expected as names for properties, models and enums, and enum values. + +To read more about the constrain option here: +- [C#](../constraints/CSharp.md) +- [Dart](../constraints/Dart.md) +- [Go](../constraints/Go.md) +- [Java](../constraints/Java.md) +- [JavaScript](../constraints/JavaScript.md) +- [Rust](../constraints/Rust.md) +- [TypeScript](../constraints/TypeScript.md) + +## CommonModel is no more (almost) +As part of version 1, the previous core model was called `CommonModel` and was a dynamic class which could take the form of any type of model. I.e. one moment it could be defining an object, the next an enum, or multiple at a time. This made it extremely hard to use in the generators, read more about why we decided to change it here: https://github.com/asyncapi/modelina/pull/530 + +This change means that any time you did some custom presets and interacted with `CommonModel`, you now interact with a variant of the [ConstrainedMetaModel](../internal-model.md#the-constrained-meta-model). + +The CommonModel is still being used for the dynamic input processing of JSON Schema. + +### Preset hooks +These are all the preset hook changes: +- Java, class preset hooks had access to `model: CommonModel` which has now been changed to `model: ConstrainedObjectModel` +- Java, enum preset hooks had access to `model: CommonModel` which has now been changed to `model: ConstrainedEnumModel` +- JavaScript, class preset hooks had access to `model: CommonModel` which has now been changed to `model: ConstrainedObjectModel` +- TypeScript, class preset hooks had access to `model: CommonModel` which has now been changed to `model: ConstrainedObjectModel` +- TypeScript, Interface preset hooks had access to `model: CommonModel` which has now been changed to `model: ConstrainedObjectModel` +- TypeScript, enum preset hooks had access to `model: CommonModel` which has now been changed to `model: ConstrainedEnumModel` +- TypeScript, type preset hooks had access to `model: CommonModel` which has now been changed to `model: ConstrainedMetaModel` +- Go, struct preset hooks had access to `model: CommonModel` which has now been changed to `model: ConstrainedObjectModel` +- C#, class preset hooks had access to `model: CommonModel` which has now been changed to `model: ConstrainedObjectModel` +- C#, enum preset hooks had access to `model: CommonModel` which has now been changed to `model: ConstrainedEnumModel` + +General changes: +- Hooks that gave access to properties/fields formally had the arguments `property`, `propertyName` and `type`, these are now wrapped within the [`ConstrainedObjectPropertyModel`](../internal-model.md#the-constrained-meta-model) and can be accessed through the `property` argument. +- Hooks that gave access to enum items, now has the type [`ConstrainedEnumValueModel`](../internal-model.md#the-constrained-meta-model). + +## TypeScript and JavaScript module system +In the previous version you only had to define a the module system type (`ESM` or `CJS`) when generating complete models. However, it happens that presets need to import their own dependencies which then need to adapt to the desire module system. Therefore this option have been moved so you have to adapt your code the following way: + +```ts +const input = ...; + +// Old way +const generator = new JavaScriptGenerator(); +const models = await generator.generateCompleteModels(input, { + moduleSystem: 'ESM' +}) + +// New way +const generator = new JavaScriptGenerator({moduleSystem: 'ESM'}); +const models = await generator.generateCompleteModels(input, {}); + +``` \ No newline at end of file diff --git a/docs/presets.md b/docs/presets.md index 2262d1dd0d..416cd2e0fc 100644 --- a/docs/presets.md +++ b/docs/presets.md @@ -1,118 +1,306 @@ # Presets -The AsyncAPI Model SDK uses **preset** objects to extend the rendered model. -A **preset** is a pure JavaScript object with format `key: value`, where `key` is the name of a model type and `value` is an object containing methods that extend a given rendered part for a given model type, like below example: +Modelina uses something called **presets** to extend the rendered model. You can see it as layers you add ontop of each other which either adds new code to render, or completely overwrite existing generated code. + + + + + + * [Hello world!](#hello-world) + * [Presets in depth](#presets-in-depth) + + [Overwriting existing rendered content](#overwriting-existing-rendered-content) + + [Ap/pre-pending to existng rendered content](#appre-pending-to-existng-rendered-content) + + [Reusing presets (options)](#reusing-presets-options) + + [Adding new dependencies](#adding-new-dependencies) + + [Overriding the default preset](#overriding-the-default-preset) + * [Preset's shape](#presets-shape) + + [Java](#java) + - [**Class**](#class) + - [**Enum**](#enum) + + [JavaScript](#javascript) + - [**Class**](#class-1) + + [TypeScript](#typescript) + - [**Class**](#class-2) + - [**Interface**](#interface) + - [**Enum**](#enum-1) + - [**Type**](#type) + + [Go](#go) + - [**Struct**](#struct) + + [C#](#c%23) + - [**Class**](#class-3) + - [**Enum**](#enum-2) + + [Rust](#rust) + - [**Struct**](#struct-1) + - [**Enum**](#enum-3) + - [**Package**](#package) + - [**Union**](#union) + - [**Tuple**](#tuple) + + [Dart](#dart) + - [**Class**](#class-4) + - [**Enum**](#enum-4) + + [Python](#python) + - [**Class: plain Python**](#class-5) + - [**Class: pydantic**](#class-6) + - [**Enum**](#enum-5) +- [Limitations](#limitations) + * [Hard for two presets to write to the exact same location within a class](#hard-for-two-presets-to-write-to-the-exact-same-location-within-a-class) + + + +## Hello world! +Lets try to look at an example, every generator start with a bare minimal model called the **default preset** and for the TypeScript that preset would render a class to look something like this: +```ts +class Root { + private _email?: string; + + constructor(input: { + email?: string, + }) { + this._email = input.email; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } +} +``` + +The generator renderes the TypeScript class by calling **preset hooks**, which is callbacks that is called for rendering parts of the class. +```html + + + + + + + + + + +``` + +This is what Modelina leverage to customize what is being rendered, because these preset hooks can be **extended or overwritten** by one or more presets. + +Lets take a look at an example, say we wanted to a description for each property of the class, maybe just to say hallo to the world. To do this we pass a custom preset to our generator: + +```ts +import { TypeScriptGenerator } from '@asyncapi/modelina'; + +const generator = new TypeScriptGenerator({ + presets: [ + { + class: { + property({ content }) { + const description = '// Hello world!' + return `${description}\n${content}`; + } + } + } + ] +}); +``` + +This adds a new preset for classes where for each property it runs our callback. The callback then prepends, to the existing `content` that have been rendered by other presets, our comment `// Hello world!`. This now renders all class properties with a comment from us! + +```ts +class Root { + // Hello world! + private _email?: string; + + constructor(input: { + email?: string, + }) { + this._email = input.email; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } +} +``` + +## Presets in depth +A **preset** is a pure JavaScript object with format `key: value`, where `key` is the name of a **model type** and `value` is an object containing callbacks that extend a given rendered part for a given model type, like below example: ```js { // `class` model type class: { - self(...options) { /* logic */ }, + self(...arguments) { /* logic */ }, // `setter` customization method - setter(...options) { /* logic */ }, + setter(...arguments) { /* logic */ }, }, interface: { // `property` customization method - property(...options) { /* logic */ }, - additionalContent(...options) { /* logic */ }, + property(...arguments) { /* logic */ }, + additionalContent(...arguments) { /* logic */ }, }, } ``` -Each language has different model types, which results in different implementable methods in a single preset. For more information, please check the [preset's shape](#presets-shape) section. +Each output has different model types, which results in different implementable methods in a single preset. The different model types can be found in the [preset's shape](#presets-shape) section. + +For each custom preset, the implementation of methods for a given model type is optional. It means that you can implement one or all, depending on your use-case. -## Custom preset +The order of extending a given part of the model is consistent with the order of presets in the array passed as a `presets` option parameter in the generator's constructor. -Below is a custom preset written for TypeScript language, which adds a description to each interface's property and to the model itself as a JavaScript comment. +As shown in the [Hello world!](#hello-world) example, there are many ways to customize the model generation, this section covers the the different parts. -You can find the full preset at [typescript/presets/DescriptionPreset.ts](../src/generators/typescript/presets/DescriptionPreset.ts) +### Overwriting existing rendered content +Since the preset renders in a form of layers, one of the usecases is to overwrite an already existing rendering of some part of the generated model. Lets try an adapt out hello world example, and instead of prepending comments, we can overwrite the already rendered content, for example lets use public property initializer. ```ts import { TypeScriptGenerator } from '@asyncapi/modelina'; -function renderDesc({ renderer, content, model }) { - const desc = model.getFromOriginalInput('description'); - if (desc) { - const renderedDesc = renderer.renderComments(desc); - return `${renderedDesc}\n${content}`; +const generator = new TypeScriptGenerator({ + presets: [ + { + class: { + property({ property }) { + return `public ${property.propertyName}${!property.required ? '?' : ''}: ${property.type};`; + } + } + } + ] +}); +``` +It would render the following class: +```ts +class Root { + public _email?: string; + + constructor(input: { + email?: string, + }) { + this._email = input.email; } - return content; + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } } +``` -const DESCRIPTION_PRESET = { - interface: { - self({ renderer, content, model }) { - return renderDesc({ renderer, content, model }); - }, - property({ renderer, model, content }) { - return renderDesc({ renderer, content, model }); +### Ap/pre-pending to existng rendered content +As the hello world example appended content, this time lets prepend some content to the properties. +```ts +import { TypeScriptGenerator } from '@asyncapi/modelina'; + +const generator = new TypeScriptGenerator({ + presets: [ + { + class: { + property({ content }) { + const description = '// Hello world!' + return `${description}\n${content}`; + } + } } - } -} + ] +}); +``` -const generator = new TypeScriptGenerator({ modelType: 'interface', presets: [DESCRIPTION_PRESET] }); +It would render the following class: +```ts +class Root { + private _email?: string; + // Hello world! + + constructor(input: { + email?: string, + }) { + this._email = input.email; + } -const schema = { - $id: "Address", - type: "object", - description: "Address information", - properties: { - street_name: { type: "string" }, - city: { type: "string", description: "City description" }, - }, - required: ["street_name", "city"], -}; - -const models = await generator.generate(schema); - -// models[0] should have the shape: -/** - * Address information - */ -interface Address { - streetName: string; - /** - * City description - */ - city: string; + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } } ``` -Note that in the `DESCRIPTION_PRESET` object, we first defined which model we want to extend - in our case it's the `interface` - and then we implemented the `self` method, which extends the whole model shape and `property` method, which only extends that part of the model - property for the interface. - -For each custom preset, the implementation of methods for a given model type is optional. It means that you can implement only one, needed by your case extension for the given model type, as in the above example. +### Reusing presets (options) +Sometimes you might want to create different behavior based on user input, this can be done through options that can be provided with the preset. -The order of extending a given part of the model is consistent with the order of presets in the array passed as a `presets` option parameter in the generator's constructor. +Say we want to create a preset with a customizable description that is provided by the use of the preset. To do this we can adapt the [hello world!](#hello-world) example to this: -The user has the possibility to pass options to the custom preset - if preset has defined options - used in a `presets` array. To do that, please pass as an array's element an object with the `preset` field as a reference to the custom preset and define the `options` field with options passed to the preset. Check example: +```ts +import { TypeScriptGenerator } from '@asyncapi/modelina'; -```js -const generator = new TypeScriptGenerator({ presets: [ - SOME_PRESET, - { - preset: PRESET_WITH_OPTIONS, - options: {...}, - }, -] }); +const generator = new TypeScriptGenerator({ + presets: [ + { + preset: { + class: { + property({ content, options }) { + const description = options.description !== undefined ? options.description : '// Hello world!' + return `${description}\n${content}`; + } + } + }, + options: { + description: "Hello dear customizer!" + } + } + ] +}); ``` -## Adding new dependencies +This enables you to reuse presets (even expose them) to multiple generators +```ts +import { TypeScriptGenerator } from '@asyncapi/modelina'; +interface DescriptionOption = { + description: string +} +const descriptionPreset: TypeScriptPreset = { + class: { + property({ content, options }) { + const description = options.description !== undefined ? options.description : '// Hello world!' + return `${description}\n${content}`; + } + } +} -Each preset hook has the possibility of adding its own dependencies that needs to be rendered for the given model. It can be done through the `addDependency` function from `renderer` property. +// One generator prepends `Hello dear customizer!` +const generator = new TypeScriptGenerator({ + presets: [ + { + preset: descriptionPreset, + options: { + description: "Hello dear customizer!" + } + } + ] +}); + +// Second generator prepends `Hello from beyond!` +const generator2 = new TypeScriptGenerator({ + presets: [ + { + preset: descriptionPreset, + options: { + description: "Hello from beyond!" + } + } + ] +}); +``` + +### Adding new dependencies +Sometimes the preset might need to use some kind of foreign dependency. To achieve this each preset hook has the possibility of adding its own dependencies through a dependency manager, which can be accessed in `dependencyManager`. ```ts ... -self({ renderer, content }) { - renderer.addDependency('import java.util.*;'); +self({ dependencyManager, content }) { + dependencyManager.addDependency('import java.util.*;'); return content; } ... ``` -## Overriding the default preset +Some languages has specific helper functions, and some very basic interfaces, such as for Java. -Each implemented generator for appropriate language must have defined the default preset. However, we can override it by passing it as the `defaultPreset` parameter in the generator options. Check the example for TypeScript generator: +In TypeScript because you can have different import syntaxes based on the module system such as [CJS](../examples/typescript-use-cjs/) or [ESM](../examples/typescript-use-esm/), therefore it provies a specific function `addTypeScriptDependency` that takes care of that logic, and you just have to remember `addTypeScriptDependency('ImportanWhat', 'FromWhere')`. + +### Overriding the default preset + +Each implemented generator must have defined a default preset which forms is minimal generated model, that the rest of the presets add to or removes from. This can be overwritten by passing the `defaultPreset` parameter in the generator options. Check the example for TypeScript generator: ```js const DEFAULT_PRESET = { @@ -122,22 +310,22 @@ const DEFAULT_PRESET = { const generator = new TypeScriptGenerator({ defaultPreset: DEFAULT_PRESET }); ``` -> **NOTE**: Each default preset must have implemented each method for a given model type. Keep this in mind when overriding the default preset. +> **NOTE**: Default presets MUST implement all preset hooks for a given model type! ## Preset's shape -For each model type, you can implement two basic methods: +For each model type, you can implement two basic preset hooks: -- `self` - the method for extending the model shape. +- `self` - the method for extending the model shape, this is what calls all additional preset hooks. - `additionalContent` - the method which adds additional content to the model. -Each customization method receives the following arguments: +Each preset hook method receives the following arguments: -- `model` - an instance of the [`CommonModel`](../src/models/CommonModel.ts) class, which described rendered data model. -- `inputModel` - an instance of the [`CommonInputModel`](../src/models/CommonInputModel.ts) class. +- `model` - a [`ConstrainedMetaModel`](../src/models/CommonModel.ts) variation which depends on the preset type. +- `inputModel` - an instance of the [`InputMetaModel`](../src/models/InputMetaModel.ts) class. - `renderer` - an instance of the class with common helper functions to render appropriate model type. - `content` - rendered content from previous preset. -- `options` - options passed to preset defined in the `presets` array. +- `options` - options passed to preset defined in the `presets` array, it's type depends on the specific preset. Below is a list of supported languages with their model types and corresponding additional preset's methods with extra arguments based on the character of the customization method. @@ -145,80 +333,206 @@ Below is a list of supported languages with their model types and corresponding #### **Class** +This preset is a generator for the meta model `ConstrainedObjectModel` and [can be accessed through the `model` argument](#presets-shape). + | Method | Description | Additional arguments | |---|---|---| | `ctor` | A method to extend rendered constructor for a given class. | - | -| `property` | A method to extend rendered given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | -| `setter` | A method to extend setter for a given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | -| `getter` | A method to extend getter for a given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | +| `property` | A method to extend rendered given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `setter` | A method to extend setter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `getter` | A method to extend getter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | #### **Enum** +This preset is a generator for the meta model `ConstrainedEnumModel` and [can be accessed through the `model` argument](#presets-shape). + | Method | Description | Additional arguments | |---|---|---| -| `item` | A method to extend enum's item. | an `item` containing the value of enum's item. | +| `item` | A method to extend enum's item. | `item` object as a [`ConstrainedEnumValueModel`](./internal-model.md#the-constrained-meta-model) instance, which contains the value and key of enum's item. | ### JavaScript #### **Class** +This preset is a generator for the meta model `ConstrainedObjectModel` and [can be accessed through the `model` argument](#presets-shape). + | Method | Description | Additional arguments | |---|---|---| | `ctor` | A method to extend rendered constructor for a given class. | - | -| `property` | A method to extend rendered given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | -| `setter` | A method to extend setter for a given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | -| `getter` | A method to extend getter for a given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | +| `property` | A method to extend rendered given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `setter` | A method to extend setter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `getter` | A method to extend getter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | ### TypeScript #### **Class** +This preset is a generator for the meta model `ConstrainedObjectModel` and [can be accessed through the `model` argument](#presets-shape). + | Method | Description | Additional arguments | |---|---|---| | `ctor` | A method to extend rendered constructor for a given class. | - | -| `property` | A method to extend rendered given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | -| `setter` | A method to extend setter for a given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | -| `getter` | A method to extend getter for a given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | +| `property` | A method to extend rendered given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `setter` | A method to extend setter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `getter` | A method to extend getter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | #### **Interface** +This preset is a generator for the meta model `ConstrainedObjectModel` and [can be accessed through the `model` argument](#presets-shape). + | Method | Description | Additional arguments | |---|---|---| -| `property` | A method to extend rendered given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | +| `property` | A method to extend rendered given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | #### **Enum** +This preset is a generator for the meta model `ConstrainedEnumModel` and [can be accessed through the `model` argument](#presets-shape). + | Method | Description | Additional arguments | |---|---|---| -| `item` | A method to extend enum's item. | an `item` containing the value of enum's item. | +| `item` | A method to extend enum's item. | `item` object as a [`ConstrainedEnumValueModel`](./internal-model.md#the-constrained-meta-model) instance, which contains the value and key of enum's item. | #### **Type** +This preset is a generator for all meta models `ConstrainedMetaModel` and [can be accessed through the `model` argument](#presets-shape). + There are no additional methods. ### Go #### **Struct** +This preset is a generator for the meta model `ConstrainedObjectModel` and [can be accessed through the `model` argument](#presets-shape). + | Method | Description | Additional arguments | |---|---|---| -| `field` | A method to extend rendered given field. | `fieldName` as a name of a given field, `field` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | +| `field` | A method to extend rendered given field. | `field` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | ### C# #### **Class** +This preset is a generator for the meta model `ConstrainedObjectModel` and [can be accessed through the `model` argument](#presets-shape). + +| Method | Description | Additional arguments | +|---|---|---| +| `ctor` | A method to extend rendered constructor for a given class. | - | +| `property` | A method to extend rendered given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `accessor` | A method to extend rendered given property accessor. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `setter` | A method to extend setter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `getter` | A method to extend getter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | + +#### **Enum** + +This preset is a generator for the meta model `ConstrainedEnumModel` and [can be accessed through the `model` argument](#presets-shape). + +| Method | Description | Additional arguments | +|---|---|---| +| `item` | A method to extend enum's item. | `item` object as a [`ConstrainedEnumValueModel`](./internal-model.md#the-constrained-meta-model) instance, which contains the value and key of enum's item. | + +### Rust +#### **Struct** + +This preset is a generator for the meta model `ConstrainedObjectModel` and [can be accessed through the `model` argument](#presets-shape). + +| Method | Description | Additional arguments | +|---|---|---| +| `field` | A method to extend rendered given field. | `field` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `fieldMacro` | | `field` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `structMacro` | | `field` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | + +#### **Enum** + +This preset is a generator for the meta model `ConstrainedEnumModel` and [can be accessed through the `model` argument](#presets-shape). + +| Method | Description | Additional arguments | +|---|---|---| +| `item` | A method to extend enum's item. | `item` object as a [`ConstrainedEnumValueModel`](./internal-model.md#the-constrained-meta-model) instance, which contains the value and key of enum's item, `itemIndex`. | +| `itemMacro` | | `item` object as a [`ConstrainedEnumValueModel`](./internal-model.md#the-constrained-meta-model) instance, which contains the value and key of enum's item, `itemIndex`. | +| `structMacro` | | `item` object as a [`ConstrainedEnumValueModel`](./internal-model.md#the-constrained-meta-model) instance, which contains the value and key of enum's item, `itemIndex`. | + +#### **Package** + +This preset is a generator for the crate package file. + +| Method | Description | Additional arguments | +|---|---|---| +| `manifest` | | `packageOptions`, `InputMetaModel` | +| `lib` | | `packageOptions`, `inputModel` | + +#### **Union** + +This preset is a generator for the meta model `ConstrainedUnionModel` and [can be accessed through the `model` argument](#presets-shape). + +| Method | Description | Additional arguments | +|---|---|---| +| `item` | | `ConstrainedMetaModel` | +| `itemMacro` | | `ConstrainedMetaModel` | +| `structMacro` | | `ConstrainedMetaModel` | + +#### **Tuple** + +This preset is a generator for the meta model `ConstrainedTupleModel` and [can be accessed through the `model` argument](#presets-shape). + +| Method | Description | Additional arguments | +|---|---|---| +| `field` | | `field` object as a [`ConstrainedTupleValueModel`](./internal-model.md#the-constrained-meta-model) instance, `fieldIndex`. | +| `structMacro` | | `field` object as a [`ConstrainedTupleValueModel`](./internal-model.md#the-constrained-meta-model) instance, `fieldIndex`. | + +### Dart +#### **Class** + +This preset is a generator for the meta model `ConstrainedObjectModel` and [can be accessed through the `model` argument](#presets-shape). + | Method | Description | Additional arguments | |---|---|---| | `ctor` | A method to extend rendered constructor for a given class. | - | -| `property` | A method to extend rendered given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | -| `accessor` | A method to extend rendered given property accessor. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | -| `setter` | A method to extend setter for a given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | -| `getter` | A method to extend getter for a given property. | `propertyName` as a name of a given property, `property` object as a [`CommonModel`](../src/models/CommonModel.ts) instance. | +| `property` | A method to extend rendered given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `accessor` | A method to extend rendered given property accessor. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `setter` | A method to extend setter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `getter` | A method to extend getter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | + +#### **Enum** + +This preset is a generator for the meta model `ConstrainedEnumModel` and [can be accessed through the `model` argument](#presets-shape). + +| Method | Description | Additional arguments | +|---|---|---| +| `item` | A method to extend enum's item. | `item` object as a [`ConstrainedEnumValueModel`](./internal-model.md#the-constrained-meta-model) instance, which contains the value and key of enum's item. | + +### Python + +#### **Class** +This preset is a generator for the meta model `ConstrainedObjectModel` and [can be accessed through the `model` argument](#presets-shape). +| Method | Description | Additional arguments | +|---|---|---| +| `ctor` | A method to extend rendered constructor for a given class. | - | +| `property` | A method to extend rendered given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `setter` | A method to extend setter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `getter` | A method to extend getter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | #### **Enum** +This preset is a generator for the meta model `ConstrainedEnumModel` and [can be accessed through the `model` argument](#presets-shape). + | Method | Description | Additional arguments | |---|---|---| -| `item` | A method to extend enum's item. | an `item` containing the value of enum's item. | +| `item` | A method to extend enum's item. | `item` object as a [`ConstrainedEnumValueModel`](./internal-model.md#the-constrained-meta-model) instance, which contains the value and key of enum's item. | +# Limitations + +With features natually comes limitations, and same applies for presets, so here are the known limitations the architecture of presets for Modelina. + +## Hard for two presets to write to the exact same location within a class + +Say you developed two presets, and you wanted to use both at the same time, but they both to add something right before a property. Example could be one wanted to add `@something` and the other `@something_else`. With the way presets work, one will always be rendered before the other. + +```ts +class Root { + @something + @something_else + private _email?: string; +} +``` + +There are no easy way for those two presets to properly together, and there is no easy way to solve this. You can read more about the issue here: https://github.com/asyncapi/modelina/issues/628 \ No newline at end of file diff --git a/docs/usage.md b/docs/usage.md index b9ec91d579..54ec3f43fe 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -10,67 +10,143 @@ For more specific integration options, please check out the [integration documen +- [Generator's options](#generators-options) - [Understanding the output format](#understanding-the-output-format) - [Generate models from AsyncAPI documents](#generate-models-from-asyncapi-documents) + * [Limitations and Compatibility](#limitations-and-compatibility) + + [Message Schema formats](#message-schema-formats) + + [Polymorphism](#polymorphism) - [Generate models from JSON Schema documents](#generate-models-from-json-schema-documents) - [Generate models from Swagger 2.0 documents](#generate-models-from-swagger-20-documents) + * [Limitations and Compatibility](#limitations-and-compatibility-1) + + [Polymorphism](#polymorphism-1) - [Generate models from OpenAPI documents](#generate-models-from-openapi-documents) + + [Limitations and Compatibility](#limitations-and-compatibility-2) + - [Polymorphism](#polymorphism-2) - [Generate models from TypeScript type files](#generate-models-from-typescript-type-files) +- [Generate models from Meta models](#generate-models-from-meta-models) - [Generate Go models](#generate-go-models) - [Generate C# models](#generate-c%23-models) - [Generate Java models](#generate-java-models) - [Generate TypeScript models](#generate-typescript-models) - [Generate JavaScript models](#generate-javascript-models) - [Generate Dart models](#generate-dart-models) +- [Generate Rust models](#generate-rust-models) +- [Generate Python models](#generate-python-models) +- [Generate Kotlin models](#generate-kotlin-models) +## Generator's options + +For Modelina, there exist 3 types of options for the generation process. +1. Default options, are the default options the rest overwrite. +2. Generator options, are used as the baseline options used for each model rendering, unless otherwise specified. +3. Render options, are the last options to specify before the rendering of a model, this is used to specialize the options for individual rendering of a model. + +Generator options are passed as the first argument to the generator's constructor. Check the example: + +```ts +const generator = new TypeScriptGenerator({ ...options }); +``` + +Render options are passed as the first argument to the generator's render function. Check the example: + +```ts +const generator = ... +generator.render(model, inputModel, { ...options }); + +``` + ## Understanding the output format -TODO +The output format is designed for you to use the generated models in further contexts. It might be part of a larger code generation such as AsyncAPI templates. This means that you can glue multiple models together into one large file, or split it out as you see fit. + +All [generate functions](./generators.md) return an array of `OutputModel`s, which contains the following properties. + +| Property | Type | Description | +|---|---|---| +| `result` | String | The rendered content, that depends on whether you render it as a full model or partial model. | +| `model` | [ConstrainedMetaModel](./internal-model.md#the-constrained-meta-model) | The constrained meta model that contains many other information about the rendered model. | +| `modelName` | String | The rendered name of the model. | +| `inputModel` | `InputMetaModel` | Contains all the raw models along side the input they where generated for. Check the code for further information. | +| `dependencies` | String[] | List of rendered dependency imports that the model uses. | ## Generate models from AsyncAPI documents -When providing an AsyncAPI document, the library iterates the entire document and generate models for all defined messages. If any other kind of iteration is wanted, feel free to create a [feature request](https://github.com/asyncapi/modelina/issues/new?assignees=&labels=enhancement&template=enhancement.md). +When providing an AsyncAPI document, the library iterates the entire document and generate models for all defined message payloads. The message payloads are interpreted based on the schema format. + +For JSON Schema it follows the [JSON Schema input processing](#generate-models-from-json-schema-documents). There are two ways to generate models for an AsyncAPI document. -- [Generate from a parsed AsyncAPI document](../examples/asyncapi-from-parser) -- [Generate from a pure JS object](../examples/asyncapi-from-object) +- [Generate from a parsed AsyncAPI 2.x document](../examples/asyncapi-from-parser) +- [Generate from a parsed AsyncAPI 2.x document, from the old v1 parser](../examples/asyncapi-from-v1-parser) +- [Generate from an AsyncAPI 2.x JS object](../examples/asyncapi-from-object) The library expects the `asyncapi` property for the document to be set in order to understand the input format. -The message payloads, since it is a JSON Schema variant, is [interpreted as a such](./interpretation_of_JSON_Schema.md). +The message payloads, since it is a JSON Schema variant, is [interpreted as a such](./inputs/JSON_Schema.md). -## Generate models from JSON Schema documents +### Limitations and Compatibility +These are the current known limitation of the AsyncAPI input. + +#### Message Schema formats +Currently, only a limited number of schema formats are supported and we currently rely on the [AsyncAPI parser](https://github.com/asyncapi/parser-js/) to handle those schema formats and convert them into [AsyncAPI Schema format](https://github.com/asyncapi/parser-js/#custom-message-parsers). At some point in the future, Modelina will support all native schema formats, so it does not matter which standard you use to define the message payloads, you will be able to generate models from it. + +#### Polymorphism -There is one way to generate models for a JSON Schema document. +Through the AsyncAPI Schema you are able to use `discriminator` for achieving polymorphism. Current version of Modelina does not generate the expected inheritance of models, instead it's current behavior is to [merge the schemas together](./inputs/JSON_Schema.md#processing-sub-schemas). This means you will still get access to the properties that missing inherited model has, but without the inheritance. -- [Generate from a pure JS object](../examples/json-schema-draft7-from-object) +Long term if this is something you wish was supported, voice your [opionion here](https://github.com/asyncapi/modelina/issues/108). -We support both draft-4, draft-6, and draft-7 documents. +## Generate models from JSON Schema documents + +There are three ways to generate models for a JSON Schema document. -The library expects the `$schema` property for the document to be set in order to understand the input format. By default, if no other inputs are detected, it defaults to `JSON Schema draft 7`. The process of interpreting a JSON Schema to a model can be read [here](./interpretation_of_JSON_Schema.md). +- [Generate from a JSON Schema draft 7 JS object](../examples/json-schema-draft7-from-object) +- [Generate from a JSON Schema draft 6 JS object](../examples/json-schema-draft6-from-object) +- [Generate from a JSON Schema draft 4 JS object](../examples/json-schema-draft4-from-object) + +The library expects the `$schema` property for the document to be set in order to understand the input format. By default, if no other inputs are detected, it defaults to `JSON Schema draft 7`. The process of interpreting a JSON Schema to a model can be read [here](./inputs/JSON_Schema.md). ## Generate models from Swagger 2.0 documents -There are one way to generate models from a Swagger 2.0 document +There are one way to generate models from a Swagger 2.0 document. -- [Generate from a pure JS object](../examples/swagger2.0-from-object) +- [Generate from a Swagger 2.0 JS object](../examples/swagger2.0-from-object) The Swagger input processor expects that the property `swagger` is defined in order to know it should be processed. -The response payload and `body` parameters, since it is a JSON Schema variant, is [interpreted as a such](./interpretation_of_JSON_Schema.md). +The response payload and `body` parameters, since it is a JSON Schema variant, is [interpreted as a such](./inputs/JSON_Schema.md). + +### Limitations and Compatibility +These are the current known limitation of the Swagger 2.0 input. + +#### Polymorphism + +Through the Swagger 2.0 Schema you are able to use `discriminator` for achieving polymorphism. Current version of Modelina does not generate the expected inheritance of models, instead it's current behavior is to [merge the schemas together](./inputs/JSON_Schema.md#processing-sub-schemas). This means you will still get access to the properties that missing inherited model has, but without the inheritance. + +Long term if this is something you wish was supported, voice your [opionion here](https://github.com/asyncapi/modelina/issues/108). ## Generate models from OpenAPI documents There are one way to generate models from an OpenAPI document -- [Generate from a pure JS object](../examples/openapi-from-object) +- [Generate from OpenAPI 3.0 JS object](../examples/openapi-from-object) The OpenAPI input processor expects that the property `openapi` is defined in order to know it should be processed. -The response and request payloads, since it is a JSON Schema variant, is [interpreted as a such](./interpretation_of_JSON_Schema.md). +The response and request payloads, since it is a JSON Schema variant, is [interpreted as a such](./inputs/JSON_Schema.md). + +#### Limitations and Compatibility +These are the current known limitation of the OpenAPI inputs. + +##### Polymorphism + +Through the OpenAPI 3.0 Schema you are able to use `discriminator` for achieving polymorphism. Current version of Modelina does not generate the expected inheritance of models, instead it's current behavior is to [merge the schemas together](./inputs/JSON_Schema.md#processing-sub-schemas). This means you will still get access to the properties that missing inherited model has, but without the inheritance. + +Long term if this is something you wish was supported, voice your [opionion here](https://github.com/asyncapi/modelina/issues/108). ## Generate models from TypeScript type files @@ -78,7 +154,14 @@ Currently, we support generating models from a TypeScript type file. - [Generate Java model from a TypeScript file](../examples/java-from-typescript-type/) -The TypeScript input processor expects that the typescript file and base directory where it's present, is passed as input, in order to process the types accurately. +The TypeScript input processor expects that the typescript file and base directory where it's present, is passed as input, in order to process the types accurately. The input processor converts the TypeScript types into JSON Schema, which are then passed on to the [JSON Schema processor](#generate-models-from-json-schema-documents). + +## Generate models from Meta models +Sometimes, the supported inputs such as AsyncAPI and JSON Schema wont be enough for your use-case and you want to create your own data models while still utilizing the full sweep of features from the generators. + +You can do that by providing the [internal meta model](./internal-model.md#the-meta-model) as input. Check out this [example out for a live demonstration](../examples/meta-model). + +Check out this [example out for a live demonstration](../examples/meta-model). ## Generate Go models @@ -103,3 +186,15 @@ JavaScript is one of the many output languages we support. Check out this [basic ## Generate Dart models Dart is one of the many output languages we support. Check out this [basic example for a live demonstration](../examples/generate-dart-models) and the following [Dart documentation for more advanced use-cases](./languages/Dart.md). + +## Generate Rust models + +Rust is one of the many output languages we support. Check out this [basic example for a live demonstration](../examples/rust-generate-crate) and the following [Rust documentation for more advanced use-cases](./languages/Rust.md). + +## Generate Python models + +Python is one of the many output languages we support. Check out this [basic example for a live demonstration](../examples/generate-python-models) and the following [Python documentation for more advanced use-cases](./languages/Python.md). + +## Generate Kotlin models + +Kotlin is one of the many output languages we support. Check out this [basic example for a live demonstration](../examples/generate-kotlin-models) as well as [how to generate enums](../examples/generate-kotlin-enums) and the following [Kotlin documentation for more advanced use-cases](./languages/Kotlin.md). diff --git a/examples/README.md b/examples/README.md index 29d6921cfa..b142529989 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,42 +1,124 @@ # Examples -This directory contains a series of self-contained examples that you can use as starting points, or as snippets to pull into your existing applications: +This directory contains a series of self-contained examples to help you get started or show how a specific feature can be utilized. The examples also serve the purpose of being part of the testing workflow to ensure they always work! -- [generate-typescript-models](./generate-typescript-models) - A basic example to generate TypeScript data models -- [typescript-interface](./typescript-interface) - A basic TypeScript generator that outputs interfaces. -- [typescript-enum-type](./typescript-enum-type) - A basic example of how to use Modelina can output different types of enums in TypeScript. -- [typescript-generate-example](./typescript-generate-example) - A basic example of how to use Modelina and output a TypeScript class with an example function. -- [typescript-generate-marshalling](./typescript-generate-marshalling) - A basic example of how to use the un/marshalling functionality of the typescript class. -- [typescript-generate-comments](./typescript-generate-comments) - A basic example of how to generate TypeScript interfaces with comments from description and example fields. -- [typescript-use-esm](./typescript-use-esm) - A basic example that generate the models to use ESM module system. -- [typescript-use-cjs](./typescript-use-cjs) - A basic example that generate the models to use CJS module system. -- [indentation-type-and-size](./indentation-type-and-size) - This example shows how to change the indentation type and size of the generated model. +We love contributions and new examples that does not already exist, you can follow [this guide to contribute one](../docs/contributing.md#adding-examples)! + + +--- + +## Groups of Examples + + + + + +- [Input examples](#input-examples) +- [General examples](#general-examples) +- [Simple generator examples](#simple-generator-examples) +- [Integrations](#integrations) +- [Python](#python) +- [JavaScript](#javascript) +- [Java](#java) +- [C#](#c%23) +- [TypeScript](#typescript) +- [Kotlin](#kotlin) +- [Other examples](#other-examples) + + + +--- + +## Input examples +These examples show a specific input and how they can be used: - [asyncapi-from-object](./asyncapi-from-object) - A basic example where an AsyncAPI JS object is used to generate models. - [asyncapi-from-parser](./asyncapi-from-parser) - A basic example where an AsyncAPI JS object from the [parser-js](https://github.com/asyncapi/parser-js) is used to generate models. +- [asyncapi-from-v1-parser](./asyncapi-from-v1-parser) - A basic example where an AsyncAPI JS object from the old v1 [parser-js](https://github.com/asyncapi/parser-js) is used to generate models. - [json-schema-draft7-from-object](./json-schema-draft7-from-object) - A basic example where a JSON Schema draft 7 JS object is used to generate models. +- [json-schema-draft6-from-object](./json-schema-draft6-from-object) - A basic example where a JSON Schema draft 6 JS object is used to generate models. +- [json-schema-draft4-from-object](./json-schema-draft4-from-object) - A basic example where a JSON Schema draft 4 JS object is used to generate models. - [swagger2.0-from-object](./swagger2.0-from-object) - A basic example where a Swagger 2.0 JS object is used to generate models. -- [java-generate-javax-constraint-annotation](./java-generate-javax-constraint-annotation) - A basic example that shows how Java data models having `javax.validation.constraints` annotations can be generated. -- [java-generate-javadoc](./java-generate-javadoc) - A basic example of how to generate Java models including JavaDocs. +- [meta-model](./meta-model) - A basic example how to provide a meta model for the generator + +## General examples +These are examples that can be applied in all scenarios. + +- [include-custom-function](./include-custom-function) - A basic example where a custom function is included. +- [overwrite-naming-formatting](./overwrite-naming-formatting) - A basic example how to overwrite default naming format constraint in this case, overwriting returning a constant case format. +- [overwrite-default-constraint](./overwrite-default-constraint/) - A basic example how to overwrite the entire constraint logic and not just a single single part of the default behavior, in this case overwriting the model naming constraint. - [custom-logging](./custom-logging) - A basic example where a custom logger is used. - [generate-to-files](./generate-to-files) - A basic example that shows how you can generate the models directly to files. -- [TEMPLATE](./TEMPLATE) - A basic template used to create new examples. -- [java-generate-tostring](./java-generate-tostring) - A basic example that shows how to generate models that overwrite the `toString` method -- [csharp-generate-equals-and-hashcode](./csharp-generate-equals-and-hashcode) - A basic example on how to generate models that overwrite the `Equal` and `GetHashCode` methods -- [csharp-generate-serializer](./csharp-generate-serializer) - A basic example on how to generate models that include function to serialize the data models to JSON -- [csharp-overwrite-enum-naming](./csharp-overwrite-enum-naming) - A basic example on how to generate enum value names. -- [csharp-use-inheritance](./csharp-use-inheritance) - A basic example that shows how to introduce inheritance to classes +- [indentation-type-and-size](./indentation-type-and-size) - This example shows how to change the indentation type and size of the generated model. +- [change-type-mapping](./change-type-mapping/) - A basic example showing how to change the type of a model in some context. +- [change-type-mapping-with-dependency](./change-type-mapping-with-dependency/) - A basic example showing how to use the dependency manager to inject your own custom type with a dependency instead of the default type. + + +## Simple generator examples +These are all the basic generator examples that shows a bare minimal example of a generator: +- [generate-typescript-models](./generate-typescript-models) - A basic example to generate TypeScript data models +- [generate-csharp-models](./generate-csharp-models) - A basic example to generate C# data models +- [generate-python-models](./generate-python-models/) - A basic example showing how to generate Python models. +- [rust-generate-crate](./rust-generate-crate/) - A basic example showing how to generate a Rust package. +- [generate-java-models](./generate-java-models) - A basic example to generate Java data models. +- [generate-go-models](./generate-go-models) - A basic example to generate Go data models - [generate-javascript-models](./generate-javascript-models) - A basic example to generate JavaScript data models +- [generate-kotlin-models](./generate-kotlin-models) - A basic example to generate Kotlin data models + +## Integrations +These are examples of how you can integrate Modelina into a specific scenario: +- [integrate with React](./integrate-with-react/) - A basic example that shows how you can integrate Modelina with React. + +## Python +These are all specific examples only relevant to the Python generator: +- [generate-python-pydantic-models](./generate-python-pydantic-models/) - An example showing how to generate Python pydantic models. + +## JavaScript +These are all specific examples only relevant to the JavaScript generator: + - [javascript-use-esm](./javascript-use-esm) - A basic example that generate the models to use ESM module system. - [javascript-use-cjs](./javascript-use-cjs) - A basic example that generate the models to use CJS module system. - [javascript-generate-marshalling](./javascript-generate-marshalling) - A basic example of how to use the un/marshalling functionality of the javascript class. - [javascript-generate-example](./javascript-generate-example) - A basic example of how to use Modelina and output a JavaScript class with an example function. -- [generate-java-models](./generate-java-models) - A basic example to generate Java data models. -- [generate-go-models](./generate-go-models) - A basic example to generate Go data models -- [include-custom-function](./include-custom-function) - A basic example where a custom function is included. -- [java-generate-equals](./java-generate-equals) - A basic example that shows how to generate models that overwrite the `equal` method -- [generate-csharp-models](./generate-csharp-models) - A basic example to generate C# data models + + +## Java +These are all specific examples only relevant to the Java generator: +- [java-generate-tostring](./java-generate-tostring) - A basic example that shows how to generate models that overwrite the `toString` method - [java-change-collection-type](./java-change-collection-type) - An example to render collections as List in Java. - [java-generate-hashcode](./java-generate-hashcode) - A basic example that shows how to generate models that overwrite the `hashCode` method - [java-from-typescript-type](./java-from-typescript-type/) - A basic example that shows how to generate a Java model from a TypeScript type input file. - [java-generate-marshalling](./java-generate-marshalling) - A basic example of how to use the un/marshalling functionality of the java class. - [java-from-typescript-type-with-options](./java-from-typescript-type-with-options/) - A basic example that shows how to generate a Java model from a TypeScript type input file along with user provided options. +- [java-generate-equals](./java-generate-equals) - A basic example that shows how to generate models that overwrite the `equal` method +- [java-generate-javax-constraint-annotation](./java-generate-javax-constraint-annotation) - A basic example that shows how Java data models having `javax.validation.constraints` annotations can be generated. +- [java-generate-javadoc](./java-generate-javadoc) - A basic example of how to generate Java models including JavaDocs. + +## C# +These are all specific examples only relevant to the C# generator: +- [csharp-generate-equals-and-hashcode](./csharp-generate-equals-and-hashcode) - A basic example on how to generate models that overwrite the `Equal` and `GetHashCode` methods +- [csharp-generate-json-serializer](./csharp-generate-json-serializer) - A basic example on how to generate models that include function to serialize the data models to and from JSON with System.Text.Json. +- [csharp-generate-newtonsoft-serializer](./csharp-generate-newtonsoft-serializer) - A basic example on how to generate models that include function to serialize the data models to and form JSON with Newtonsoft. +- [csharp-overwrite-enum-naming](./csharp-overwrite-enum-naming) - A basic example on how to generate enum value names. +- [csharp-use-inheritance](./csharp-use-inheritance) - A basic example that shows how to introduce inheritance to classes + +## TypeScript +These are all specific examples only relevant to the TypeScript generator: +- [typescript-interface](./typescript-interface) - A basic TypeScript generator that outputs interfaces. +- [typescript-enum-type](./typescript-enum-type) - A basic example of how to use Modelina can output different types of enums in TypeScript. +- [typescript-generate-example](./typescript-generate-example) - A basic example of how to use Modelina and output a TypeScript class with an example function. +- [typescript-generate-marshalling](./typescript-generate-marshalling) - A basic example of how to use the un/marshalling functionality of the typescript class. +- [typescript-generate-comments](./typescript-generate-comments) - A basic example of how to generate TypeScript interfaces with comments from description and example fields. +- [typescript-use-esm](./typescript-use-esm) - A basic example that generate the models to use ESM module system. +- [typescript-use-cjs](./typescript-use-cjs) - A basic example that generate the models to use CJS module system. +- [typescript-generate-jsonbinpack](./typescript-generate-jsonbinpack) - A basic example showing how to generate models that include [jsonbinpack](https://github.com/sourcemeta/jsonbinpack) functionality. + +## Kotlin +These are all specific examples only relevant to the Kotlin generator: +- [generate-kotlin-enums](./generate-kotlin-enums) +- [kotlin-generate-kdoc](./kotlin-generate-kdoc) +- [kotlin-generate-javax-constraint-annotations](./kotlin-generate-javax-constraint-annotation) +- [kotlin-change-collection-type](./kotlin-change-collection-type) + +## Other examples +Miscelanious examples that do not fit into the otherwise grouping. +- [TEMPLATE](./TEMPLATE) - A basic template used to create new examples. diff --git a/examples/TEMPLATE/index.spec.ts b/examples/TEMPLATE/index.spec.ts index a30b648126..4787d8879e 100644 --- a/examples/TEMPLATE/index.spec.ts +++ b/examples/TEMPLATE/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render TEMPLATE', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to render TEMPLATE', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/TEMPLATE/index.ts b/examples/TEMPLATE/index.ts index f18220a373..41e1500508 100644 --- a/examples/TEMPLATE/index.ts +++ b/examples/TEMPLATE/index.ts @@ -13,10 +13,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/adapting-input-and-output/__snapshots__/index.spec.ts.snap b/examples/adapting-input-and-output/__snapshots__/index.spec.ts.snap index 2e909457f3..0086d145d8 100644 --- a/examples/adapting-input-and-output/__snapshots__/index.spec.ts.snap +++ b/examples/adapting-input-and-output/__snapshots__/index.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Should be able to generate a model with functions to serialize the data model into JSON and should log expected output to console 1`] = ` +exports[`Should be able to generate a model with custom input and output and should log expected output to console 1`] = ` Array [ "public class Address { diff --git a/examples/adapting-input-and-output/index.spec.ts b/examples/adapting-input-and-output/index.spec.ts index daa49a7090..8ea9d9cdd4 100644 --- a/examples/adapting-input-and-output/index.spec.ts +++ b/examples/adapting-input-and-output/index.spec.ts @@ -1,15 +1,16 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; -describe('Should be able to generate a model with functions to serialize the data model into JSON ', () => { +describe('Should be able to generate a model with custom input and output ', () => { afterAll(() => { jest.restoreAllMocks(); }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/adapting-input-and-output/index.ts b/examples/adapting-input-and-output/index.ts index 4f422b9a16..2973442ff7 100644 --- a/examples/adapting-input-and-output/index.ts +++ b/examples/adapting-input-and-output/index.ts @@ -1,8 +1,8 @@ import { CSharpGenerator } from '../../src'; -/** +/** * This example shows how you can modify inputs as well as outputs to make it fit your need! - * + * * Related issue triggering the example: https://github.com/asyncapi/modelina/issues/615 */ const jsonSchemaDraft7 = { @@ -10,46 +10,53 @@ const jsonSchemaDraft7 = { $id: 'Address', type: 'object', properties: { - dictionaryProp: { - 'x-custom-type': 'dictionary', - 'x-custom-dictionary-key': 'string', - 'x-custom-dictionary-value': 'object', + dictionaryProp: { + 'x-custom-type': 'dictionary', + 'x-custom-dictionary-key': 'string', + 'x-custom-dictionary-value': 'object', additionalProperties: true } }, additionalProperties: false }; -const generator = new CSharpGenerator({ +const generator = new CSharpGenerator({ presets: [ { class: { - property: (args) => { - if (args.property.getFromOriginalInput('x-custom-type') === 'dictionary') { - args.propertyName = args.renderer.nameProperty(args.propertyName, args.property); - const dictKey = args.property.getFromOriginalInput('x-custom-dictionary-key'); - const dictValue = args.property.getFromOriginalInput('x-custom-dictionary-value'); - //NOTICE: I use public visibility here, so for simplicity no need to add getter/setters, could easily be done though! - return `public Dictionary<${dictKey}, ${dictValue}> ${args.propertyName};`; + property: ({ property, content }) => { + if ( + property.property.originalInput['x-custom-type'] === 'dictionary' + ) { + const dictKey = + property.property.originalInput['x-custom-dictionary-key']; + const dictValue = + property.property.originalInput['x-custom-dictionary-value']; + //NOTICE: I use public visibility here, so for simplicity no need to add getter/setters + return `public Dictionary<${dictKey}, ${dictValue}> ${property.propertyName};`; } - return args.content; + return content; }, - accessor: (args) => { + accessor: ({ property, content }) => { //NOTICE: Disabling accessor factory for specific property type - if (args.property.getFromOriginalInput('x-custom-type') === 'dictionary') { + if ( + property.property.originalInput['x-custom-type'] === 'dictionary' + ) { return ''; } - return args.content; + return content; } } } ] }); -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/asyncapi-from-object/index.spec.ts b/examples/asyncapi-from-object/index.spec.ts index 340f32473e..9f6d4979ad 100644 --- a/examples/asyncapi-from-object/index.spec.ts +++ b/examples/asyncapi-from-object/index.spec.ts @@ -1,13 +1,14 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to process a pure AsyncAPI object', () => { afterAll(() => { jest.restoreAllMocks(); }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/asyncapi-from-object/index.ts b/examples/asyncapi-from-object/index.ts index 58caf69177..e828942f55 100644 --- a/examples/asyncapi-from-object/index.ts +++ b/examples/asyncapi-from-object/index.ts @@ -35,4 +35,6 @@ export async function generate(): Promise { } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/asyncapi-from-parser/index.spec.ts b/examples/asyncapi-from-parser/index.spec.ts index ebd46480c5..73e1fc2aba 100644 --- a/examples/asyncapi-from-parser/index.spec.ts +++ b/examples/asyncapi-from-parser/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to process AsyncAPI object from parser', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to process AsyncAPI object from parser', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/asyncapi-from-parser/index.ts b/examples/asyncapi-from-parser/index.ts index e17abbbf54..49e695f967 100644 --- a/examples/asyncapi-from-parser/index.ts +++ b/examples/asyncapi-from-parser/index.ts @@ -1,4 +1,4 @@ -import { parse } from '@asyncapi/parser'; +import { Parser } from '@asyncapi/parser'; import { TypeScriptGenerator } from '../../src'; const generator = new TypeScriptGenerator(); @@ -29,11 +29,14 @@ const AsyncAPIDocument = { } }; -export async function generate() : Promise { - const parsedDoc = await parse(JSON.stringify(AsyncAPIDocument)); - const models = await generator.generate(parsedDoc as any); +export async function generate(): Promise { + const parser = new Parser(); + const { document } = await parser.parse(JSON.stringify(AsyncAPIDocument)); + const models = await generator.generate(document); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/asyncapi-from-v1-parser/README.md b/examples/asyncapi-from-v1-parser/README.md new file mode 100644 index 0000000000..f56cee9e07 --- /dev/null +++ b/examples/asyncapi-from-v1-parser/README.md @@ -0,0 +1,17 @@ +# AsyncAPI from parser + +A basic example of how to use Modelina with an AsyncAPI instance from the AsyncAPI parser. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/asyncapi-from-v1-parser/__snapshots__/index.spec.ts.snap b/examples/asyncapi-from-v1-parser/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..6623bbd6c5 --- /dev/null +++ b/examples/asyncapi-from-v1-parser/__snapshots__/index.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to process already parsed AsyncAPI document from old parser and should log expected output to console 1`] = ` +Array [ + "class AnonymousSchema_1 { + private _email?: string; + + constructor(input: { + email?: string, + }) { + this._email = input.email; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } +}", +] +`; diff --git a/examples/asyncapi-from-v1-parser/index.spec.ts b/examples/asyncapi-from-v1-parser/index.spec.ts new file mode 100644 index 0000000000..2c1f9a7630 --- /dev/null +++ b/examples/asyncapi-from-v1-parser/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to process already parsed AsyncAPI document from old parser', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/asyncapi-from-v1-parser/index.ts b/examples/asyncapi-from-v1-parser/index.ts new file mode 100644 index 0000000000..5a35d4ab8f --- /dev/null +++ b/examples/asyncapi-from-v1-parser/index.ts @@ -0,0 +1,42 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const parser = require('@asyncapi/parserV1'); +import { TypeScriptGenerator } from '../../src'; + +const generator = new TypeScriptGenerator(); +const AsyncAPIDocument = { + asyncapi: '2.2.0', + info: { + title: 'example', + version: '0.1.0' + }, + channels: { + '/test': { + subscribe: { + message: { + payload: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + } + } + } + } + } + } + } +}; + +export async function generate(): Promise { + const parsedDoc = await parser.parse(JSON.stringify(AsyncAPIDocument)); + const models = await generator.generate(parsedDoc); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/asyncapi-from-v1-parser/package-lock.json b/examples/asyncapi-from-v1-parser/package-lock.json new file mode 100644 index 0000000000..2c2d23acca --- /dev/null +++ b/examples/asyncapi-from-v1-parser/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "asyncapi-from-parser", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/asyncapi-from-v1-parser/package.json b/examples/asyncapi-from-v1-parser/package.json new file mode 100644 index 0000000000..fcc1fe7cf7 --- /dev/null +++ b/examples/asyncapi-from-v1-parser/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "asyncapi-from-v1-parser" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/change-type-mapping-with-dependency/README.md b/examples/change-type-mapping-with-dependency/README.md new file mode 100644 index 0000000000..d4d84e8b9e --- /dev/null +++ b/examples/change-type-mapping-with-dependency/README.md @@ -0,0 +1,17 @@ +# Change type mapping that adds a new dependency + +This example shows how to use a new custom type for the FloatModel, that changes its type from `number` to `MyCustomClass`, which also needs to be added as a dependency. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/change-type-mapping-with-dependency/__snapshots__/index.spec.ts.snap b/examples/change-type-mapping-with-dependency/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..f25a768226 --- /dev/null +++ b/examples/change-type-mapping-with-dependency/__snapshots__/index.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render models with custom dependency and type for property and should log expected output to console 1`] = ` +Array [ + "class Root { + private _someProperty?: MyCustomClass; + + constructor(input: { + someProperty?: MyCustomClass, + }) { + this._someProperty = input.someProperty; + } + + get someProperty(): MyCustomClass | undefined { return this._someProperty; } + set someProperty(someProperty: MyCustomClass | undefined) { this._someProperty = someProperty; } +}", +] +`; \ No newline at end of file diff --git a/examples/change-type-mapping-with-dependency/index.spec.ts b/examples/change-type-mapping-with-dependency/index.spec.ts new file mode 100644 index 0000000000..2053c60040 --- /dev/null +++ b/examples/change-type-mapping-with-dependency/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render models with custom dependency and type for property', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/change-type-mapping-with-dependency/index.ts b/examples/change-type-mapping-with-dependency/index.ts new file mode 100644 index 0000000000..db1eae42ce --- /dev/null +++ b/examples/change-type-mapping-with-dependency/index.ts @@ -0,0 +1,39 @@ +import { TypeScriptGenerator } from '../../src'; + +const generator = new TypeScriptGenerator({ + typeMapping: { + Float: ({ dependencyManager }) => { + // Let let the TypeScript dependency manager handle rendering the dependency based on the TypeScript options (i.e moduleSystem). + dependencyManager.addTypeScriptDependency( + 'MyCustomClass', + '../some/path/to/MyCustomClass' + ); + // Or just do it manually, but then the moduleSystem options is not uphold. This is what most generators support + // dependencyManager.addDependency('import MyCustomClass from \'../some/path/to/class\';'); + + // Return the type to use for this float model + return 'MyCustomClass'; + } + } +}); + +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + someProperty: { + type: 'number' + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/change-type-mapping-with-dependency/package-lock.json b/examples/change-type-mapping-with-dependency/package-lock.json new file mode 100644 index 0000000000..146ab504f5 --- /dev/null +++ b/examples/change-type-mapping-with-dependency/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "change-type-mapping", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/change-type-mapping-with-dependency/package.json b/examples/change-type-mapping-with-dependency/package.json new file mode 100644 index 0000000000..868f297736 --- /dev/null +++ b/examples/change-type-mapping-with-dependency/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "change-type-mapping-with-dependency" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/change-type-mapping/README.md b/examples/change-type-mapping/README.md new file mode 100644 index 0000000000..ac12a50038 --- /dev/null +++ b/examples/change-type-mapping/README.md @@ -0,0 +1,17 @@ +# Change type mapping + +This example shows how to overwrite the default type mapping, and in this case we overwrite the default rendering of the FloatModel `number` and instead use `integer`. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/change-type-mapping/__snapshots__/index.spec.ts.snap b/examples/change-type-mapping/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..6fe0b43e85 --- /dev/null +++ b/examples/change-type-mapping/__snapshots__/index.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render models with custom naming formatter and should log expected output to console 1`] = ` +Array [ + "class Root { + private _email?: integer; + + constructor(input: { + email?: integer, + }) { + this._email = input.email; + } + + get email(): integer | undefined { return this._email; } + set email(email: integer | undefined) { this._email = email; } +}", +] +`; diff --git a/examples/change-type-mapping/index.spec.ts b/examples/change-type-mapping/index.spec.ts new file mode 100644 index 0000000000..be6d7967e6 --- /dev/null +++ b/examples/change-type-mapping/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render models with custom naming formatter', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/change-type-mapping/index.ts b/examples/change-type-mapping/index.ts new file mode 100644 index 0000000000..0dce6690ca --- /dev/null +++ b/examples/change-type-mapping/index.ts @@ -0,0 +1,36 @@ +import { TypeScriptGenerator } from '../../src'; + +const generator = new TypeScriptGenerator({ + typeMapping: { + String: ({ + constrainedModel, + options, + partOfProperty, + dependencyManager + }) => { + return 'integer'; + } + } +}); + +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/change-type-mapping/package-lock.json b/examples/change-type-mapping/package-lock.json new file mode 100644 index 0000000000..146ab504f5 --- /dev/null +++ b/examples/change-type-mapping/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "change-type-mapping", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/change-type-mapping/package.json b/examples/change-type-mapping/package.json new file mode 100644 index 0000000000..96691d73f4 --- /dev/null +++ b/examples/change-type-mapping/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "change-type-mapping" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/csharp-auto-implemented-properties/__snapshots__/index.spec.ts.snap b/examples/csharp-auto-implemented-properties/__snapshots__/index.spec.ts.snap index ba4b29e74e..0ffc62e3a2 100644 --- a/examples/csharp-auto-implemented-properties/__snapshots__/index.spec.ts.snap +++ b/examples/csharp-auto-implemented-properties/__snapshots__/index.spec.ts.snap @@ -4,8 +4,8 @@ exports[`Should be able to render auto-implemented properties in CSharp and shou Array [ "public class Root { - public string[] Emails { get; set; } - public string StringProp { get; set; } + public dynamic[]? Emails { get; set; } + public string? StringProp { get; set; } public double? NumberProp { get; set; } }", ] diff --git a/examples/csharp-auto-implemented-properties/index.spec.ts b/examples/csharp-auto-implemented-properties/index.spec.ts index b06b481db0..1e94fea2e9 100644 --- a/examples/csharp-auto-implemented-properties/index.spec.ts +++ b/examples/csharp-auto-implemented-properties/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render auto-implemented properties in CSharp', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to render auto-implemented properties in CSharp', () => }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/csharp-auto-implemented-properties/index.ts b/examples/csharp-auto-implemented-properties/index.ts index d94599eba9..3905edf65b 100644 --- a/examples/csharp-auto-implemented-properties/index.ts +++ b/examples/csharp-auto-implemented-properties/index.ts @@ -7,7 +7,8 @@ const generator = new CSharpGenerator({ options: { autoImplementedProperties: true } - }] + } + ] }); const jsonSchemaDraft7 = { @@ -27,7 +28,7 @@ const jsonSchemaDraft7 = { }, numberProp: { type: 'number' - }, + } } }; @@ -37,4 +38,6 @@ export async function generate(): Promise { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/csharp-change-collection-type/__snapshots__/index.spec.ts.snap b/examples/csharp-change-collection-type/__snapshots__/index.spec.ts.snap index 65733e9d7f..ad8bad831d 100644 --- a/examples/csharp-change-collection-type/__snapshots__/index.spec.ts.snap +++ b/examples/csharp-change-collection-type/__snapshots__/index.spec.ts.snap @@ -4,9 +4,9 @@ exports[`Should be able to render collections in C# as IEnumerable and should lo Array [ "public class Root { - private IEnumerable email; + private IEnumerable? email; - public IEnumerable Email + public IEnumerable? Email { get { return email; } set { email = value; } diff --git a/examples/csharp-change-collection-type/index.spec.ts b/examples/csharp-change-collection-type/index.spec.ts index 104bee728e..4891431121 100644 --- a/examples/csharp-change-collection-type/index.spec.ts +++ b/examples/csharp-change-collection-type/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render collections in C# as IEnumerable', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to render collections in C# as IEnumerable', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/csharp-change-collection-type/index.ts b/examples/csharp-change-collection-type/index.ts index 2788d91289..f879041f4a 100644 --- a/examples/csharp-change-collection-type/index.ts +++ b/examples/csharp-change-collection-type/index.ts @@ -18,10 +18,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/csharp-generate-equals-and-hashcode/__snapshots__/index.spec.ts.snap b/examples/csharp-generate-equals-and-hashcode/__snapshots__/index.spec.ts.snap index 296a5cdbae..81d4bc11b7 100644 --- a/examples/csharp-generate-equals-and-hashcode/__snapshots__/index.spec.ts.snap +++ b/examples/csharp-generate-equals-and-hashcode/__snapshots__/index.spec.ts.snap @@ -4,9 +4,9 @@ exports[`Should be able to generate a model to overwrite the Equal and GetHashCo Array [ "public class Root { - private string email; + private string? email; - public string Email + public string? Email { get { return email; } set { email = value; } diff --git a/examples/csharp-generate-equals-and-hashcode/index.spec.ts b/examples/csharp-generate-equals-and-hashcode/index.spec.ts index 27b79e2baf..2fc892a932 100644 --- a/examples/csharp-generate-equals-and-hashcode/index.spec.ts +++ b/examples/csharp-generate-equals-and-hashcode/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate a model to overwrite the Equal and GetHashCode methods', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to generate a model to overwrite the Equal and GetHashC }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/csharp-generate-equals-and-hashcode/index.ts b/examples/csharp-generate-equals-and-hashcode/index.ts index 5dc20f5d20..0bd481ab18 100644 --- a/examples/csharp-generate-equals-and-hashcode/index.ts +++ b/examples/csharp-generate-equals-and-hashcode/index.ts @@ -1,9 +1,9 @@ import { CSharpGenerator, CSHARP_COMMON_PRESET } from '../../src'; -const generator = new CSharpGenerator({ +const generator = new CSharpGenerator({ presets: [ { - preset: CSHARP_COMMON_PRESET, + preset: CSHARP_COMMON_PRESET, options: { equal: true, hashCode: true @@ -24,10 +24,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/csharp-generate-serializer/README.md b/examples/csharp-generate-json-serializer/README.md similarity index 100% rename from examples/csharp-generate-serializer/README.md rename to examples/csharp-generate-json-serializer/README.md diff --git a/examples/csharp-generate-serializer/__snapshots__/index.spec.ts.snap b/examples/csharp-generate-json-serializer/__snapshots__/index.spec.ts.snap similarity index 84% rename from examples/csharp-generate-serializer/__snapshots__/index.spec.ts.snap rename to examples/csharp-generate-json-serializer/__snapshots__/index.spec.ts.snap index 9a9502ef4c..cb0626556f 100644 --- a/examples/csharp-generate-serializer/__snapshots__/index.spec.ts.snap +++ b/examples/csharp-generate-json-serializer/__snapshots__/index.spec.ts.snap @@ -5,9 +5,9 @@ Array [ "[JsonConverter(typeof(RootConverter))] public class Root { - private string email; + private string? email; - public string Email + public string? Email { get { return email; } set { email = value; } @@ -45,15 +45,11 @@ internal class RootConverter : JsonConverter string propertyName = reader.GetString(); if (propertyName == \\"email\\") - { - var value = JsonSerializer.Deserialize(ref reader, options); - instance.Email = value; - continue; - } - - - - + { + var value = JsonSerializer.Deserialize(ref reader, options); + instance.email = value; + continue; + } } throw new JsonException(); @@ -69,17 +65,13 @@ internal class RootConverter : JsonConverter writer.WriteStartObject(); - if(value.Email != null) { + if(value.email != null) { // write property name and let the serializer serialize the value itself writer.WritePropertyName(\\"email\\"); - JsonSerializer.Serialize(writer, value.Email, options); + JsonSerializer.Serialize(writer, value.email, options); } - - - - writer.WriteEndObject(); } diff --git a/examples/csharp-generate-json-serializer/index.spec.ts b/examples/csharp-generate-json-serializer/index.spec.ts new file mode 100644 index 0000000000..c171e8b97e --- /dev/null +++ b/examples/csharp-generate-json-serializer/index.spec.ts @@ -0,0 +1,16 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to generate a model with functions to serialize the data model into JSON ', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/csharp-generate-serializer/index.ts b/examples/csharp-generate-json-serializer/index.ts similarity index 70% rename from examples/csharp-generate-serializer/index.ts rename to examples/csharp-generate-json-serializer/index.ts index 2751d2346e..3c620b38de 100644 --- a/examples/csharp-generate-serializer/index.ts +++ b/examples/csharp-generate-json-serializer/index.ts @@ -1,9 +1,7 @@ import { CSharpGenerator, CSHARP_JSON_SERIALIZER_PRESET } from '../../src'; -const generator = new CSharpGenerator({ - presets: [ - CSHARP_JSON_SERIALIZER_PRESET - ] +const generator = new CSharpGenerator({ + presets: [CSHARP_JSON_SERIALIZER_PRESET] }); const jsonSchemaDraft7 = { @@ -18,10 +16,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/csharp-generate-serializer/package-lock.json b/examples/csharp-generate-json-serializer/package-lock.json similarity index 100% rename from examples/csharp-generate-serializer/package-lock.json rename to examples/csharp-generate-json-serializer/package-lock.json diff --git a/examples/csharp-generate-serializer/package.json b/examples/csharp-generate-json-serializer/package.json similarity index 91% rename from examples/csharp-generate-serializer/package.json rename to examples/csharp-generate-json-serializer/package.json index b11ff1f3c6..6e2b7d90c5 100644 --- a/examples/csharp-generate-serializer/package.json +++ b/examples/csharp-generate-json-serializer/package.json @@ -1,6 +1,6 @@ { "config" : { - "example_name" : "csharp-generate-serializer" + "example_name" : "csharp-generate-json-serializer" }, "scripts": { "install": "cd ../.. && npm i", diff --git a/examples/csharp-generate-newtonsoft-serializer/README.md b/examples/csharp-generate-newtonsoft-serializer/README.md new file mode 100644 index 0000000000..0e1f200a54 --- /dev/null +++ b/examples/csharp-generate-newtonsoft-serializer/README.md @@ -0,0 +1,17 @@ +# C# Generate serializer functions for Newtonsoft + +A basic example of how to generate models and which includes a way to serialize them into and from JSON using Newtonsoft. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/csharp-generate-newtonsoft-serializer/__snapshots__/index.spec.ts.snap b/examples/csharp-generate-newtonsoft-serializer/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..8bb4825153 --- /dev/null +++ b/examples/csharp-generate-newtonsoft-serializer/__snapshots__/index.spec.ts.snap @@ -0,0 +1,48 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to generate a model with functions to serialize the data model into JSON and should log expected output to console 1`] = ` +Array [ + "[JsonConverter(typeof(RootConverter))] +public class Root +{ + private string? email; + + public string? Email + { + get { return email; } + set { email = value; } + } +} + +public class RootConverter : JsonConverter +{ + public override Root ReadJson(JsonReader reader, System.Type objectType, Root existingValue, bool hasExistingValue, JsonSerializer serializer) +{ + JObject jo = JObject.Load(reader); + Root value = new Root(); + + if(jo[\\"email\\"] != null) { + value.Email = jo[\\"email\\"].ToObject(serializer); +} + + + return value; +} + public override void WriteJson(JsonWriter writer, Root value, JsonSerializer serializer) +{ + JObject jo = new JObject(); + + if (value.Email != null) +{ + jo.Add(\\"email\\", JToken.FromObject(value.Email, serializer)); +} + + + jo.WriteTo(writer); +} + + public override bool CanRead => true; + public override bool CanWrite => true; +}", +] +`; diff --git a/examples/csharp-generate-newtonsoft-serializer/index.spec.ts b/examples/csharp-generate-newtonsoft-serializer/index.spec.ts new file mode 100644 index 0000000000..c171e8b97e --- /dev/null +++ b/examples/csharp-generate-newtonsoft-serializer/index.spec.ts @@ -0,0 +1,16 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to generate a model with functions to serialize the data model into JSON ', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/csharp-generate-newtonsoft-serializer/index.ts b/examples/csharp-generate-newtonsoft-serializer/index.ts new file mode 100644 index 0000000000..f48930e31f --- /dev/null +++ b/examples/csharp-generate-newtonsoft-serializer/index.ts @@ -0,0 +1,30 @@ +import { + CSharpGenerator, + CSHARP_NEWTONSOFT_SERIALIZER_PRESET +} from '../../src'; + +const generator = new CSharpGenerator({ + presets: [CSHARP_NEWTONSOFT_SERIALIZER_PRESET] +}); + +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/csharp-generate-newtonsoft-serializer/package-lock.json b/examples/csharp-generate-newtonsoft-serializer/package-lock.json new file mode 100644 index 0000000000..98c488824f --- /dev/null +++ b/examples/csharp-generate-newtonsoft-serializer/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "csharp-generate-serializer", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/csharp-generate-newtonsoft-serializer/package.json b/examples/csharp-generate-newtonsoft-serializer/package.json new file mode 100644 index 0000000000..bf39bf4c54 --- /dev/null +++ b/examples/csharp-generate-newtonsoft-serializer/package.json @@ -0,0 +1,12 @@ +{ + "config" : { + "example_name" : "csharp-generate-newtonsoft-serializer" + }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/csharp-generate-required-properties/__snapshots__/index.spec.ts.snap b/examples/csharp-generate-required-properties/__snapshots__/index.spec.ts.snap index 0508544a0c..f94d17bb7f 100644 --- a/examples/csharp-generate-required-properties/__snapshots__/index.spec.ts.snap +++ b/examples/csharp-generate-required-properties/__snapshots__/index.spec.ts.snap @@ -7,7 +7,7 @@ Array [ private bool requiredBoolean; private bool? notRequiredBoolean; private string requiredString; - private string notRequiredString; + private string? notRequiredString; public bool RequiredBoolean { @@ -27,7 +27,7 @@ Array [ set { requiredString = value; } } - public string NotRequiredString + public string? NotRequiredString { get { return notRequiredString; } set { notRequiredString = value; } diff --git a/examples/csharp-generate-required-properties/index.spec.ts b/examples/csharp-generate-required-properties/index.spec.ts index bf35ba6885..8cc0b2cf61 100644 --- a/examples/csharp-generate-required-properties/index.spec.ts +++ b/examples/csharp-generate-required-properties/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render required properties without the question mark at the end if the type of it is not nullable', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to render required properties without the question mark }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/csharp-generate-required-properties/index.ts b/examples/csharp-generate-required-properties/index.ts index a88e60a6f7..5b6bd7495b 100644 --- a/examples/csharp-generate-required-properties/index.ts +++ b/examples/csharp-generate-required-properties/index.ts @@ -7,17 +7,17 @@ const jsonSchemaDraft7 = { additionalProperties: false, properties: { requiredBoolean: { - type: 'boolean', + type: 'boolean' }, notRequiredBoolean: { - type: 'boolean', + type: 'boolean' }, requiredString: { type: 'string' }, notRequiredString: { type: 'string' - }, + } }, required: ['requiredBoolean', 'requiredString'] }; @@ -28,4 +28,6 @@ export async function generate(): Promise { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/csharp-overwrite-enum-naming/__snapshots__/index.spec.ts.snap b/examples/csharp-overwrite-enum-naming/__snapshots__/index.spec.ts.snap index 9af7f71137..cb1994815f 100644 --- a/examples/csharp-overwrite-enum-naming/__snapshots__/index.spec.ts.snap +++ b/examples/csharp-overwrite-enum-naming/__snapshots__/index.spec.ts.snap @@ -6,31 +6,31 @@ Array [ { Ordered, UnderDelivery, - Deliveret, + Delivered, Cancelled } public static class OrderStatusExtensions { - public static dynamic GetValue(this OrderStatus enumValue) + public static int? GetValue(this OrderStatus enumValue) { switch (enumValue) { case OrderStatus.Ordered: return 30; case OrderStatus.UnderDelivery: return 40; - case OrderStatus.Deliveret: return 50; + case OrderStatus.Delivered: return 50; case OrderStatus.Cancelled: return 99; } return null; } - public static OrderStatus? ToOrderStatus(dynamic value) + public static OrderStatus? ToOrderStatus(int? value) { switch (value) { case 30: return OrderStatus.Ordered; case 40: return OrderStatus.UnderDelivery; - case 50: return OrderStatus.Deliveret; + case 50: return OrderStatus.Delivered; case 99: return OrderStatus.Cancelled; } return null; diff --git a/examples/csharp-overwrite-enum-naming/index.spec.ts b/examples/csharp-overwrite-enum-naming/index.spec.ts index 890ec61d94..77828fd4f8 100644 --- a/examples/csharp-overwrite-enum-naming/index.spec.ts +++ b/examples/csharp-overwrite-enum-naming/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate enums with custom value names', () => { afterAll(() => { @@ -8,8 +10,7 @@ describe('Should be able to generate enums with custom value names', () => { test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/csharp-overwrite-enum-naming/index.ts b/examples/csharp-overwrite-enum-naming/index.ts index 9c1776d6d7..dc17450737 100644 --- a/examples/csharp-overwrite-enum-naming/index.ts +++ b/examples/csharp-overwrite-enum-naming/index.ts @@ -1,48 +1,43 @@ import { CSharpGenerator } from '../../src'; +import { DefaultEnumKeyConstraints } from '../../src/generators/csharp/constrainer/EnumConstrainer'; -const generator = new CSharpGenerator({ - presets: [ - { - enum: { - item: ({model, item, content}) => { - // Lets see if an enum has any associated names - const hasCustomName = model.originalInput !== undefined && model.originalInput['x-enumNames'] !== undefined; - if (hasCustomName) { - // Lets see if the specific value has an associated name - const customName = model.originalInput['x-enumNames'][item]; - if (customName !== undefined) { - return customName; - } - } - return content; +const generator = new CSharpGenerator({ + constraints: { + enumKey: ({ enumModel, enumKey }) => { + // Lets see if an enum has an associated custom name + const hasCustomName = + enumModel.originalInput !== undefined && + enumModel.originalInput['x-enumNames'] !== undefined; + if (hasCustomName) { + // Lets see if the specific value has an associated name + const customName = enumModel.originalInput['x-enumNames'][enumKey]; + if (customName !== undefined) { + return customName; } } } - ] + } }); const jsonSchemaDraft7 = { $schema: 'http://json-schema.org/draft-07/schema#', title: 'OrderStatus', type: 'number', - enum: [ - 30, - 40, - 50, - 99 - ], + enum: [30, 40, 50, 99], 'x-enumNames': { 30: 'Ordered', 40: 'UnderDelivery', - 50: 'Deliveret', + 50: 'Delivered', 99: 'Cancelled' } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/csharp-use-inheritance/__snapshots__/index.spec.ts.snap b/examples/csharp-use-inheritance/__snapshots__/index.spec.ts.snap index 97f84cf594..8e329c6baa 100644 --- a/examples/csharp-use-inheritance/__snapshots__/index.spec.ts.snap +++ b/examples/csharp-use-inheritance/__snapshots__/index.spec.ts.snap @@ -4,9 +4,9 @@ exports[`Should be able to render inheritance and should log expected output to Array [ "public class Root : IEvent { - private string[] email; + private string[]? email; - public string[] Email + public string[]? Email { get { return email; } set { email = value; } diff --git a/examples/csharp-use-inheritance/index.spec.ts b/examples/csharp-use-inheritance/index.spec.ts index cdd2dc1010..8f51e17049 100644 --- a/examples/csharp-use-inheritance/index.spec.ts +++ b/examples/csharp-use-inheritance/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render inheritance', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to render inheritance', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/csharp-use-inheritance/index.ts b/examples/csharp-use-inheritance/index.ts index 1c259d2601..c7f9dabd84 100644 --- a/examples/csharp-use-inheritance/index.ts +++ b/examples/csharp-use-inheritance/index.ts @@ -1,27 +1,26 @@ -import { CSharpGenerator } from '../../src'; +import { ConstrainedDictionaryModel, CSharpGenerator } from '../../src'; const generator = new CSharpGenerator({ presets: [ { class: { // Self is used to overwrite the entire rendering behavior of the class - self: async ({renderer, options, model}) => { + self: async ({ renderer, options, model }) => { //Render all the class content const content = [ await renderer.renderProperties(), await renderer.runCtorPreset(), await renderer.renderAccessors(), - await renderer.runAdditionalContentPreset(), + await renderer.runAdditionalContentPreset() ]; - - if (options?.collectionType === 'List' || - model.additionalProperties !== undefined || - model.patternProperties !== undefined) { + + if ( + options?.collectionType === 'List' || + model.containsPropertyType(ConstrainedDictionaryModel) + ) { renderer.addDependency('using System.Collections.Generic;'); } - - const formattedName = renderer.nameType(model.$id); - return `public class ${formattedName} : IEvent + return `public class ${model.name} : IEvent { ${renderer.indent(renderer.renderBlock(content, 2))} }`; @@ -37,6 +36,7 @@ const jsonSchemaDraft7 = { properties: { email: { type: 'array', + additionalItems: false, items: { type: 'string', format: 'email' @@ -45,10 +45,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/custom-logging/index.spec.ts b/examples/custom-logging/index.spec.ts index 9b23762dd2..93398883cd 100644 --- a/examples/custom-logging/index.spec.ts +++ b/examples/custom-logging/index.spec.ts @@ -1,17 +1,27 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -jest.spyOn(global.console, 'debug').mockImplementation(() => { return; }); -jest.spyOn(global.console, 'info').mockImplementation(() => { return; }); -jest.spyOn(global.console, 'warn').mockImplementation(() => { return; }); -jest.spyOn(global.console, 'error').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +jest.spyOn(global.console, 'debug').mockImplementation(() => { + return; +}); +jest.spyOn(global.console, 'info').mockImplementation(() => { + return; +}); +jest.spyOn(global.console, 'warn').mockImplementation(() => { + return; +}); +jest.spyOn(global.console, 'error').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to use custom logging interface', () => { afterAll(() => { jest.restoreAllMocks(); }); - + test('and should log expected output to console', async () => { await generate(); - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/custom-logging/index.ts b/examples/custom-logging/index.ts index a63257e7e4..48ea9aa5a2 100644 --- a/examples/custom-logging/index.ts +++ b/examples/custom-logging/index.ts @@ -27,4 +27,6 @@ export async function generate(): Promise { } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/dart-generate-json-annotation/__snapshots__/index.spec.ts.snap b/examples/dart-generate-json-annotation/__snapshots__/index.spec.ts.snap index d00ab30108..81ea4c84f9 100644 --- a/examples/dart-generate-json-annotation/__snapshots__/index.spec.ts.snap +++ b/examples/dart-generate-json-annotation/__snapshots__/index.spec.ts.snap @@ -3,7 +3,8 @@ exports[`Should be able to render Dart Models and should log expected output to console 1`] = ` Array [ "class Root { - Object? email; + String? email; + Map? additionalProperties; Root(); diff --git a/examples/dart-generate-json-annotation/index.spec.ts b/examples/dart-generate-json-annotation/index.spec.ts index 04d73e2656..2a775dcba2 100644 --- a/examples/dart-generate-json-annotation/index.spec.ts +++ b/examples/dart-generate-json-annotation/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render Dart Models', () => { afterAll(() => { @@ -7,7 +9,7 @@ describe('Should be able to render Dart Models', () => { }); test('and should log expected output to console', async () => { await generate(); - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/dart-generate-json-annotation/index.ts b/examples/dart-generate-json-annotation/index.ts index f27a8e74cb..6266362390 100644 --- a/examples/dart-generate-json-annotation/index.ts +++ b/examples/dart-generate-json-annotation/index.ts @@ -1,4 +1,4 @@ -import {DART_JSON_PRESET, DartFileGenerator} from '../../src'; +import { DART_JSON_PRESET, DartFileGenerator } from '../../src'; const jsonSchemaDraft7 = { $schema: 'http://json-schema.org/draft-07/schema#', @@ -14,11 +14,12 @@ const jsonSchemaDraft7 = { const generator = new DartFileGenerator({ presets: [DART_JSON_PRESET] }); -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); - +if (require.main === module) { + generate(); +} diff --git a/examples/generate-all-models-within-the-same-file/.gitignore b/examples/generate-all-models-within-the-same-file/.gitignore new file mode 100644 index 0000000000..6caf68aff4 --- /dev/null +++ b/examples/generate-all-models-within-the-same-file/.gitignore @@ -0,0 +1 @@ +output \ No newline at end of file diff --git a/examples/generate-all-models-within-the-same-file/README.md b/examples/generate-all-models-within-the-same-file/README.md new file mode 100644 index 0000000000..b8e652df75 --- /dev/null +++ b/examples/generate-all-models-within-the-same-file/README.md @@ -0,0 +1,17 @@ +# Generate all models within the same file + +A basic example of how to generate all models within the same file. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/generate-all-models-within-the-same-file/__snapshots__/index.spec.ts.snap b/examples/generate-all-models-within-the-same-file/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..4ee728936c --- /dev/null +++ b/examples/generate-all-models-within-the-same-file/__snapshots__/index.spec.ts.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to generate models to files and should log expected output to console 1`] = ` +Array [ + " + + public class Root { + private Email email; + + public Email getEmail() { return this.email; } + public void setEmail(Email email) { this.email = email; } +} +public enum Email { + EXAMPLE1_AT_TEST_DOT_COM((String)\\"example1@test.com\\"), EXAMPLE2_AT_TEST_DOT_COM((String)\\"example2@test.com\\"); + + private String value; + + Email(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static Email fromValue(String value) { + for (Email e : Email.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +}", +] +`; diff --git a/examples/generate-all-models-within-the-same-file/index.spec.ts b/examples/generate-all-models-within-the-same-file/index.spec.ts new file mode 100644 index 0000000000..895fc6fb46 --- /dev/null +++ b/examples/generate-all-models-within-the-same-file/index.spec.ts @@ -0,0 +1,24 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; +import * as fs from 'fs'; +import * as path from 'path'; +describe('Should be able to generate models to files', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + const expectedRootDir = __dirname.includes('examples') + ? __dirname + : path.resolve( + __dirname, + './examples/generate-all-models-within-the-same-file/output' + ); + const expectedFilePath = path.resolve(expectedRootDir, 'Root.java'); + await generate(); + expect(spy.mock.calls.length).toBeGreaterThanOrEqual(1); + expect(spy.mock.calls[spy.mock.calls.length - 1]).toMatchSnapshot(); + expect(fs.existsSync(expectedFilePath)); + }); +}); diff --git a/examples/generate-all-models-within-the-same-file/index.ts b/examples/generate-all-models-within-the-same-file/index.ts new file mode 100644 index 0000000000..94b259ec0b --- /dev/null +++ b/examples/generate-all-models-within-the-same-file/index.ts @@ -0,0 +1,34 @@ +import { JavaFileGenerator } from '../../src'; +import { promises as fsPromises } from 'fs'; + +const generator = new JavaFileGenerator(); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + enum: ['example1@test.com', 'example2@test.com'] + } + } +}; +export async function generate(): Promise { + const outputFile = './output'; + const models = await generator.generate(jsonSchemaDraft7); + const modelCode = models.map((outputModel) => { + return outputModel.result; + }); + const imports = models.map((outputModel) => { + return outputModel.dependencies; + }); + const uniqueImports = [...new Set(imports)]; + const codeWithImports = `${uniqueImports.join('\n')} + ${modelCode.join('\n')}`; + + await fsPromises.writeFile(outputFile, codeWithImports); + console.log(codeWithImports); +} +if (require.main === module) { + generate(); +} diff --git a/examples/generate-all-models-within-the-same-file/package-lock.json b/examples/generate-all-models-within-the-same-file/package-lock.json new file mode 100644 index 0000000000..fc55ea73d1 --- /dev/null +++ b/examples/generate-all-models-within-the-same-file/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "generate-all-models-within-the-same-file", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/generate-all-models-within-the-same-file/package.json b/examples/generate-all-models-within-the-same-file/package.json new file mode 100644 index 0000000000..1c189882f6 --- /dev/null +++ b/examples/generate-all-models-within-the-same-file/package.json @@ -0,0 +1,12 @@ +{ + "config": { + "example_name": "generate-all-models-within-the-same-file" + }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/generate-all-models-within-the-same-file/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\generate-all-models-within-the-same-file\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/generate-all-models-within-the-same-file/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/generate-all-models-within-the-same-file/index.spec.ts" + } +} diff --git a/examples/generate-csharp-models/__snapshots__/index.spec.ts.snap b/examples/generate-csharp-models/__snapshots__/index.spec.ts.snap index 8aa6175fb5..139c309014 100644 --- a/examples/generate-csharp-models/__snapshots__/index.spec.ts.snap +++ b/examples/generate-csharp-models/__snapshots__/index.spec.ts.snap @@ -4,9 +4,9 @@ exports[`Should be able to render C# models and should log expected output to c Array [ "public class Root { - private string email; + private string? email; - public string Email + public string? Email { get { return email; } set { email = value; } diff --git a/examples/generate-csharp-models/index.spec.ts b/examples/generate-csharp-models/index.spec.ts index adf3b7871d..88d1f66312 100644 --- a/examples/generate-csharp-models/index.spec.ts +++ b/examples/generate-csharp-models/index.spec.ts @@ -1,4 +1,6 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); import { generate } from './index'; describe('Should be able to render C# models ', () => { @@ -7,8 +9,7 @@ describe('Should be able to render C# models ', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/generate-csharp-models/index.ts b/examples/generate-csharp-models/index.ts index 9cdff58448..cccf80e255 100644 --- a/examples/generate-csharp-models/index.ts +++ b/examples/generate-csharp-models/index.ts @@ -13,10 +13,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/generate-dart-models/__snapshots__/index.spec.ts.snap b/examples/generate-dart-models/__snapshots__/index.spec.ts.snap index 5f0c97ece6..38dc5c817c 100644 --- a/examples/generate-dart-models/__snapshots__/index.spec.ts.snap +++ b/examples/generate-dart-models/__snapshots__/index.spec.ts.snap @@ -3,7 +3,7 @@ exports[`Should be able to render Dart Models and should log expected output to console 1`] = ` Array [ "class Root { - Object? email; + String? email; Root(); }", diff --git a/examples/generate-dart-models/index.spec.ts b/examples/generate-dart-models/index.spec.ts index 04d73e2656..2a775dcba2 100644 --- a/examples/generate-dart-models/index.spec.ts +++ b/examples/generate-dart-models/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render Dart Models', () => { afterAll(() => { @@ -7,7 +9,7 @@ describe('Should be able to render Dart Models', () => { }); test('and should log expected output to console', async () => { await generate(); - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/generate-dart-models/index.ts b/examples/generate-dart-models/index.ts index 711416f428..ba2a819c95 100644 --- a/examples/generate-dart-models/index.ts +++ b/examples/generate-dart-models/index.ts @@ -1,4 +1,4 @@ -import {DartGenerator} from '../../src'; +import { DartGenerator } from '../../src'; const generator = new DartGenerator(); const jsonSchemaDraft7 = { @@ -13,10 +13,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/generate-go-enums/__snapshots__/index.spec.ts.snap b/examples/generate-go-enums/__snapshots__/index.spec.ts.snap index ad0ff99c52..63a132b10e 100644 --- a/examples/generate-go-enums/__snapshots__/index.spec.ts.snap +++ b/examples/generate-go-enums/__snapshots__/index.spec.ts.snap @@ -2,37 +2,70 @@ exports[`Should be able to render Go Enums and should log expected output to console 1`] = ` Array [ - "// Cities represents an enum of string. -type Cities string - -const ( - CitiesLondon Cities = \\"London\\" - CitiesRome = \\"Rome\\" - CitiesBrussels = \\"Brussels\\" -)", + "// Root represents a Root model. +type Root struct { + Cities *Cities + Options *Options +}", ] `; exports[`Should be able to render Go Enums and should log expected output to console 2`] = ` Array [ - "// ClickOptions represents an enum of string. -type ClickOptions string + "// Cities represents an enum of Cities. +type Cities uint const ( - ClickOptionsClickAndPlay ClickOptions = \\"click_and_play\\" - ClickOptionsClickPay = \\"click&pay\\" -)", + CitiesLondon Cities = iota + CitiesRome + CitiesBrussels +) + +// Value returns the value of the enum. +func (op Cities) Value() any { + if op >= Cities(len(CitiesValues)) { + return nil + } + return CitiesValues[op] +} + +var CitiesValues = []any{\\"London\\",\\"Rome\\",\\"Brussels\\"} +var ValuesToCities = map[any]Cities{ + CitiesValues[CitiesLondon]: CitiesLondon, + CitiesValues[CitiesRome]: CitiesRome, + CitiesValues[CitiesBrussels]: CitiesBrussels, +} +", ] `; exports[`Should be able to render Go Enums and should log expected output to console 3`] = ` Array [ - "// Options represents an enum of int. -type Options int + "// Options represents an enum of Options. +type Options uint const ( - OptionsFirstOption Options = iota - OptionsSecondOption -)", + OptionsNumber_123 Options = iota + OptionsNumber_213 + OptionsTrue + OptionsRun +) + +// Value returns the value of the enum. +func (op Options) Value() any { + if op >= Options(len(OptionsValues)) { + return nil + } + return OptionsValues[op] +} + +var OptionsValues = []any{123,213,true,\\"Run\\"} +var ValuesToOptions = map[any]Options{ + OptionsValues[OptionsNumber_123]: OptionsNumber_123, + OptionsValues[OptionsNumber_213]: OptionsNumber_213, + OptionsValues[OptionsTrue]: OptionsTrue, + OptionsValues[OptionsRun]: OptionsRun, +} +", ] `; diff --git a/examples/generate-go-enums/index.spec.ts b/examples/generate-go-enums/index.spec.ts index 606842a972..d7c6363fc5 100644 --- a/examples/generate-go-enums/index.spec.ts +++ b/examples/generate-go-enums/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render Go Enums', () => { afterAll(() => { @@ -7,10 +9,9 @@ describe('Should be able to render Go Enums', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 4 model (root and three different enums type), we double it - expect(spy.mock.calls.length).toEqual(8); + expect(spy.mock.calls.length).toEqual(3); + expect(spy.mock.calls[0]).toMatchSnapshot(); expect(spy.mock.calls[1]).toMatchSnapshot(); expect(spy.mock.calls[2]).toMatchSnapshot(); - expect(spy.mock.calls[3]).toMatchSnapshot(); }); }); diff --git a/examples/generate-go-enums/index.ts b/examples/generate-go-enums/index.ts index 6e09f7f217..5a231a1a85 100644 --- a/examples/generate-go-enums/index.ts +++ b/examples/generate-go-enums/index.ts @@ -9,25 +9,22 @@ const jsonSchemaDraft7 = { cities: { $id: 'cities', type: 'string', - enum: ['London', 'Rome', 'Brussels'], - }, - click_options: { - $id: 'click_options', - type: 'string', - enum: ['click_and_play', 'click&pay'], + enum: ['London', 'Rome', 'Brussels'] }, options: { $id: 'options', - type: 'integer', - enum: ['first_option', 'second_option'], - }, + type: ['integer', 'boolean', 'string'], + enum: [123, 213, true, 'Run'] + } } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/generate-go-models/index.spec.ts b/examples/generate-go-models/index.spec.ts index aec289cd64..d1dd8cee68 100644 --- a/examples/generate-go-models/index.spec.ts +++ b/examples/generate-go-models/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render Go Models', () => { afterAll(() => { @@ -7,7 +9,7 @@ describe('Should be able to render Go Models', () => { }); test('and should log expected output to console', async () => { await generate(); - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/generate-go-models/index.ts b/examples/generate-go-models/index.ts index 5401dc8f93..97cad2944a 100644 --- a/examples/generate-go-models/index.ts +++ b/examples/generate-go-models/index.ts @@ -13,10 +13,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/generate-java-models/index.spec.ts b/examples/generate-java-models/index.spec.ts index 3784c9d664..5ee9058aa0 100644 --- a/examples/generate-java-models/index.spec.ts +++ b/examples/generate-java-models/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render Java Models', () => { afterAll(() => { @@ -7,7 +9,7 @@ describe('Should be able to render Java Models', () => { }); test('and should log expected output to console', async () => { await generate(); - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/generate-java-models/index.ts b/examples/generate-java-models/index.ts index 7909d85845..4cd26d1286 100644 --- a/examples/generate-java-models/index.ts +++ b/examples/generate-java-models/index.ts @@ -13,10 +13,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/generate-javascript-models/index.spec.ts b/examples/generate-javascript-models/index.spec.ts index ed43798407..c6f7545fda 100644 --- a/examples/generate-javascript-models/index.spec.ts +++ b/examples/generate-javascript-models/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render Javascript models', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to render Javascript models', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/generate-javascript-models/index.ts b/examples/generate-javascript-models/index.ts index 3d67efde6c..331af189b9 100644 --- a/examples/generate-javascript-models/index.ts +++ b/examples/generate-javascript-models/index.ts @@ -13,10 +13,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/generate-kotlin-enums/README.md b/examples/generate-kotlin-enums/README.md new file mode 100644 index 0000000000..d1c7c70214 --- /dev/null +++ b/examples/generate-kotlin-enums/README.md @@ -0,0 +1,17 @@ +# Kotlin Enums + +A basic example of how to use Modelina and output a Kotlin enum. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/generate-kotlin-enums/__snapshots__/index.spec.ts.snap b/examples/generate-kotlin-enums/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..cde2c4f39d --- /dev/null +++ b/examples/generate-kotlin-enums/__snapshots__/index.spec.ts.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render Kotlin Enums and should log expected output to console 1`] = ` +Array [ + "enum class Protocol(val value: Any) { + HTTP(\\"HTTP\\"), + NUMBER_1(1), + HTTPS(\\"HTTPS\\"), + TRUE(true); +}", +] +`; diff --git a/examples/generate-kotlin-enums/index.spec.ts b/examples/generate-kotlin-enums/index.spec.ts new file mode 100644 index 0000000000..7afa11cb9c --- /dev/null +++ b/examples/generate-kotlin-enums/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render Kotlin Enums', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/generate-kotlin-enums/index.ts b/examples/generate-kotlin-enums/index.ts new file mode 100644 index 0000000000..050df6da0c --- /dev/null +++ b/examples/generate-kotlin-enums/index.ts @@ -0,0 +1,20 @@ +import { KotlinGenerator } from '../../src'; + +const generator = new KotlinGenerator(); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + additionalProperties: false, + $id: 'protocol', + type: ['string', 'int', 'boolean'], + enum: ['HTTP', 1, 'HTTPS', true] +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/generate-kotlin-enums/package-lock.json b/examples/generate-kotlin-enums/package-lock.json new file mode 100644 index 0000000000..713584b29b --- /dev/null +++ b/examples/generate-kotlin-enums/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "generate-kotlin-enums", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/generate-kotlin-enums/package.json b/examples/generate-kotlin-enums/package.json new file mode 100644 index 0000000000..d14efa1d59 --- /dev/null +++ b/examples/generate-kotlin-enums/package.json @@ -0,0 +1,12 @@ +{ + "config": { + "example_name": "generate-kotlin-enums" + }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/generate-kotlin-models/README.md b/examples/generate-kotlin-models/README.md new file mode 100644 index 0000000000..20ca57b90d --- /dev/null +++ b/examples/generate-kotlin-models/README.md @@ -0,0 +1,17 @@ +# Kotlin Data Models + +A basic example of how to use Modelina and output a Kotlin data model. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/generate-kotlin-models/__snapshots__/index.spec.ts.snap b/examples/generate-kotlin-models/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..d7ee6408d7 --- /dev/null +++ b/examples/generate-kotlin-models/__snapshots__/index.spec.ts.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render Kotlin Models and should log expected output to console 1`] = ` +Array [ + "data class Root( + val email: String, + val cache: Int, + val website: Website, +)", +] +`; diff --git a/examples/generate-kotlin-models/index.spec.ts b/examples/generate-kotlin-models/index.spec.ts new file mode 100644 index 0000000000..010afc6245 --- /dev/null +++ b/examples/generate-kotlin-models/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render Kotlin Models', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(3); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/generate-kotlin-models/index.ts b/examples/generate-kotlin-models/index.ts new file mode 100644 index 0000000000..63ce9e71ef --- /dev/null +++ b/examples/generate-kotlin-models/index.ts @@ -0,0 +1,41 @@ +import { KotlinGenerator } from '../../src'; + +const generator = new KotlinGenerator(); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + }, + cache: { + type: 'integer' + }, + website: { + type: 'object', + additionalProperties: false, + properties: { + domain: { + type: 'string', + format: 'url' + }, + protocol: { + type: 'string', + enum: ['HTTP', 'HTTPS'] + } + } + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/generate-kotlin-models/package-lock.json b/examples/generate-kotlin-models/package-lock.json new file mode 100644 index 0000000000..eb514aba77 --- /dev/null +++ b/examples/generate-kotlin-models/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "generate-kotlin-models", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/generate-kotlin-models/package.json b/examples/generate-kotlin-models/package.json new file mode 100644 index 0000000000..d76aefdcfd --- /dev/null +++ b/examples/generate-kotlin-models/package.json @@ -0,0 +1,12 @@ +{ + "config": { + "example_name": "generate-kotlin-models" + }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/generate-python-complete-models/README.md b/examples/generate-python-complete-models/README.md new file mode 100644 index 0000000000..3b91996fbb --- /dev/null +++ b/examples/generate-python-complete-models/README.md @@ -0,0 +1,17 @@ +# Python models + +A basic example of complete model generation. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/generate-python-complete-models/__snapshots__/index.spec.ts.snap b/examples/generate-python-complete-models/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..016d58009d --- /dev/null +++ b/examples/generate-python-complete-models/__snapshots__/index.spec.ts.snap @@ -0,0 +1,113 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render python models and should generate \`implicit\` or \`explicit\` imports for models implementing referenced types: nested-model 1`] = ` +Array [ + " + +class ObjProperty: + def __init__(self, input): + if hasattr(input, 'number'): + self._number = input.number + if hasattr(input, 'additionalProperties'): + self._additionalProperties = input.additionalProperties + + @property + def number(self): + return self._number + @number.setter + def number(self, number): + self._number = number + + @property + def additionalProperties(self): + return self._additionalProperties + @additionalProperties.setter + def additionalProperties(self, additionalProperties): + self._additionalProperties = additionalProperties +", +] +`; + +exports[`Should be able to render python models and should generate \`implicit\` or \`explicit\` imports for models implementing referenced types: nested-model 2`] = ` +Array [ + " + +class ObjProperty: + def __init__(self, input): + if hasattr(input, 'number'): + self._number = input.number + if hasattr(input, 'additionalProperties'): + self._additionalProperties = input.additionalProperties + + @property + def number(self): + return self._number + @number.setter + def number(self, number): + self._number = number + + @property + def additionalProperties(self): + return self._additionalProperties + @additionalProperties.setter + def additionalProperties(self, additionalProperties): + self._additionalProperties = additionalProperties +", +] +`; + +exports[`Should be able to render python models and should generate \`implicit\` or \`explicit\` imports for models implementing referenced types: root-model-explicit-import 1`] = ` +Array [ + "from ObjProperty import ObjProperty + +class Root: + def __init__(self, input): + if hasattr(input, 'email'): + self._email = input.email + if hasattr(input, 'objProperty'): + self._objProperty = input.objProperty + + @property + def email(self): + return self._email + @email.setter + def email(self, email): + self._email = email + + @property + def objProperty(self): + return self._objProperty + @objProperty.setter + def objProperty(self, objProperty): + self._objProperty = objProperty +", +] +`; + +exports[`Should be able to render python models and should generate \`implicit\` or \`explicit\` imports for models implementing referenced types: root-model-implicit-import 1`] = ` +Array [ + "from ObjProperty import ObjProperty + +class Root: + def __init__(self, input): + if hasattr(input, 'email'): + self._email = input.email + if hasattr(input, 'objProperty'): + self._objProperty = input.objProperty + + @property + def email(self): + return self._email + @email.setter + def email(self, email): + self._email = email + + @property + def objProperty(self): + return self._objProperty + @objProperty.setter + def objProperty(self, objProperty): + self._objProperty = objProperty +", +] +`; diff --git a/examples/generate-python-complete-models/index.spec.ts b/examples/generate-python-complete-models/index.spec.ts new file mode 100644 index 0000000000..136a8562ef --- /dev/null +++ b/examples/generate-python-complete-models/index.spec.ts @@ -0,0 +1,25 @@ +import { generate } from './index'; + +const createLogMock = () => + jest.spyOn(global.console, 'log').mockImplementation(() => { + return; + }); + +describe('Should be able to render python models', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + test('and should generate `implicit` or `explicit` imports for models implementing referenced types', async () => { + const spy = createLogMock(); + + await generate(); + + expect(spy.mock.calls.length).toEqual(4); + + expect(spy.mock.calls[0]).toMatchSnapshot('root-model-implicit-import'); + expect(spy.mock.calls[1]).toMatchSnapshot('nested-model'); + + expect(spy.mock.calls[2]).toMatchSnapshot('root-model-explicit-import'); + expect(spy.mock.calls[3]).toMatchSnapshot('nested-model'); + }); +}); diff --git a/examples/generate-python-complete-models/index.ts b/examples/generate-python-complete-models/index.ts new file mode 100644 index 0000000000..0b8f6e3907 --- /dev/null +++ b/examples/generate-python-complete-models/index.ts @@ -0,0 +1,45 @@ +import { PythonGenerator } from '../../src'; + +export const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + }, + objProperty: { + type: 'object', + properties: { + number: { + type: 'number' + } + } + } + } +}; + +export async function generate(): Promise { + const generator = new PythonGenerator(); + + let models = await generator.generateCompleteModels(jsonSchemaDraft7, { + importsStyle: 'implicit' + }); + + for (const model of models) { + console.log(model.result); + } + + models = await generator.generateCompleteModels(jsonSchemaDraft7, { + importsStyle: 'explicit' + }); + + for (const model of models) { + console.log(model.result); + } +} + +if (require.main === module) { + generate(); +} diff --git a/examples/generate-python-complete-models/package-lock.json b/examples/generate-python-complete-models/package-lock.json new file mode 100644 index 0000000000..d73d77e9df --- /dev/null +++ b/examples/generate-python-complete-models/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "generate-python-complete-models", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/generate-python-complete-models/package.json b/examples/generate-python-complete-models/package.json new file mode 100644 index 0000000000..fc37a4685d --- /dev/null +++ b/examples/generate-python-complete-models/package.json @@ -0,0 +1,12 @@ +{ + "config": { + "example_name": "generate-python-complete-models" + }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/generate-python-models/README.md b/examples/generate-python-models/README.md new file mode 100644 index 0000000000..69736af0fd --- /dev/null +++ b/examples/generate-python-models/README.md @@ -0,0 +1,17 @@ +# Python models + +A basic example of how to use Modelina and output a Python data model. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/generate-python-models/__snapshots__/index.spec.ts.snap b/examples/generate-python-models/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..cedc00dda2 --- /dev/null +++ b/examples/generate-python-models/__snapshots__/index.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render python models and should log expected output to console 1`] = ` +Array [ + "class Root: + def __init__(self, input): + if hasattr(input, 'email'): + self._email = input.email + + @property + def email(self): + return self._email + @email.setter + def email(self, email): + self._email = email +", +] +`; diff --git a/examples/generate-python-models/index.spec.ts b/examples/generate-python-models/index.spec.ts new file mode 100644 index 0000000000..6cc8bf87ba --- /dev/null +++ b/examples/generate-python-models/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render python models', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/generate-python-models/index.ts b/examples/generate-python-models/index.ts new file mode 100644 index 0000000000..d1a618083a --- /dev/null +++ b/examples/generate-python-models/index.ts @@ -0,0 +1,24 @@ +import { PythonGenerator } from '../../src'; + +const generator = new PythonGenerator(); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/generate-python-models/package-lock.json b/examples/generate-python-models/package-lock.json new file mode 100644 index 0000000000..fe81d6b342 --- /dev/null +++ b/examples/generate-python-models/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "generate-python-models", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/generate-python-models/package.json b/examples/generate-python-models/package.json new file mode 100644 index 0000000000..bf159b8288 --- /dev/null +++ b/examples/generate-python-models/package.json @@ -0,0 +1,12 @@ +{ + "config": { + "example_name": "generate-python-models" + }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/generate-python-pydantic-models/README.md b/examples/generate-python-pydantic-models/README.md new file mode 100644 index 0000000000..6f35d20d68 --- /dev/null +++ b/examples/generate-python-pydantic-models/README.md @@ -0,0 +1,17 @@ +# Python Pydantic models + +A basic example of how to use Modelina and output a Python Pydantic data model. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/generate-python-pydantic-models/__snapshots__/index.spec.ts.snap b/examples/generate-python-pydantic-models/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..75df868fc3 --- /dev/null +++ b/examples/generate-python-pydantic-models/__snapshots__/index.spec.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render python models and should log expected output to console: class-model 1`] = ` +Array [ + "class Root(BaseModel): + optionalField: Optional[str] = Field(alias='this field is optional') + requiredField: str = Field(alias='this field is required') + noDescription: Optional[str] = Field() + options: Optional[Options] = Field() +", +] +`; + +exports[`Should be able to render python models and should log expected output to console: enum-model 1`] = ` +Array [ + "class Options(Enum): + NUMBER_123 = 123 + NUMBER_213 = 213 + RESERVED_TRUE = \\"true\\" + RUN = \\"Run\\"", +] +`; diff --git a/examples/generate-python-pydantic-models/index.spec.ts b/examples/generate-python-pydantic-models/index.spec.ts new file mode 100644 index 0000000000..9efc11a47d --- /dev/null +++ b/examples/generate-python-pydantic-models/index.spec.ts @@ -0,0 +1,16 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render python models', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(2); + expect(spy.mock.calls[0]).toMatchSnapshot('class-model'); + expect(spy.mock.calls[1]).toMatchSnapshot('enum-model'); + }); +}); diff --git a/examples/generate-python-pydantic-models/index.ts b/examples/generate-python-pydantic-models/index.ts new file mode 100644 index 0000000000..f4cf95b252 --- /dev/null +++ b/examples/generate-python-pydantic-models/index.ts @@ -0,0 +1,41 @@ +import { PythonGenerator, PYTHON_PYDANTIC_PRESET } from '../../src'; + +const generator = new PythonGenerator({ + presets: [PYTHON_PYDANTIC_PRESET] +}); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + required: ['requiredField'], + properties: { + optionalField: { + type: 'string', + format: 'email', + description: 'this field is optional' + }, + requiredField: { + type: 'string', + format: 'email', + description: 'this field is required' + }, + noDescription: { + type: 'string' + }, + options: { + $id: 'options', + type: ['integer', 'boolean', 'string'], + enum: [123, 213, true, 'Run'] + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/generate-python-pydantic-models/package-lock.json b/examples/generate-python-pydantic-models/package-lock.json new file mode 100644 index 0000000000..46cbfe28b2 --- /dev/null +++ b/examples/generate-python-pydantic-models/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "generate-python-pydantic-models", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/generate-python-pydantic-models/package.json b/examples/generate-python-pydantic-models/package.json new file mode 100644 index 0000000000..08ea82f173 --- /dev/null +++ b/examples/generate-python-pydantic-models/package.json @@ -0,0 +1,12 @@ +{ + "config": { + "example_name": "generate-python-pydantic-models" + }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/generate-to-files/index.spec.ts b/examples/generate-to-files/index.spec.ts index 374bf83a38..315715f07d 100644 --- a/examples/generate-to-files/index.spec.ts +++ b/examples/generate-to-files/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; import * as fs from 'fs'; import * as path from 'path'; describe('Should be able to generate models to files', () => { @@ -7,11 +9,13 @@ describe('Should be able to generate models to files', () => { jest.restoreAllMocks(); }); test('and should log expected output to console', async () => { - const expectedRootDir = __dirname.includes('examples') ? __dirname : path.resolve(__dirname, './examples/generate-to-files'); + const expectedRootDir = __dirname.includes('examples') + ? __dirname + : path.resolve(__dirname, './examples/generate-to-files'); const expectedFilePath = path.resolve(expectedRootDir, 'Root.java'); await generate(); expect(spy.mock.calls.length).toBeGreaterThanOrEqual(1); - expect(spy.mock.calls[spy.mock.calls.length-1]).toMatchSnapshot(); + expect(spy.mock.calls[spy.mock.calls.length - 1]).toMatchSnapshot(); expect(fs.existsSync(expectedFilePath)); }); }); diff --git a/examples/generate-to-files/index.ts b/examples/generate-to-files/index.ts index 727eb06144..fde143a42e 100644 --- a/examples/generate-to-files/index.ts +++ b/examples/generate-to-files/index.ts @@ -13,14 +13,20 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const outputFolder = './examples/generate-to-files/output'; const modelGenerationOptions = { packageName: 'TestPackageName' }; - const models = await generator.generateToFiles(jsonSchemaDraft7, outputFolder, modelGenerationOptions); + const models = await generator.generateToFiles( + jsonSchemaDraft7, + outputFolder, + modelGenerationOptions + ); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/generate-typescript-models/index.spec.ts b/examples/generate-typescript-models/index.spec.ts index e11e37fc0e..dae8ed8c45 100644 --- a/examples/generate-typescript-models/index.spec.ts +++ b/examples/generate-typescript-models/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render TypeScript Models', () => { afterAll(() => { @@ -7,7 +9,7 @@ describe('Should be able to render TypeScript Models', () => { }); test('and should log expected output to console', async () => { await generate(); - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/generate-typescript-models/index.ts b/examples/generate-typescript-models/index.ts index f18220a373..41e1500508 100644 --- a/examples/generate-typescript-models/index.ts +++ b/examples/generate-typescript-models/index.ts @@ -13,10 +13,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/include-custom-function/index.spec.ts b/examples/include-custom-function/index.spec.ts index 1efd876bca..93407a1ca8 100644 --- a/examples/include-custom-function/index.spec.ts +++ b/examples/include-custom-function/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render custom function', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to render custom function', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/include-custom-function/index.ts b/examples/include-custom-function/index.ts index 699deb716c..62cbe2d278 100644 --- a/examples/include-custom-function/index.ts +++ b/examples/include-custom-function/index.ts @@ -9,7 +9,7 @@ const generator = new TypeScriptGenerator({ public someAdditionalFunctionForClasses(): string { return 'Some test'; }`; - }, + } } } ] @@ -26,10 +26,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/indentation-type-and-size/index.spec.ts b/examples/indentation-type-and-size/index.spec.ts index 2e7f9a4e1e..7738f5633d 100644 --- a/examples/indentation-type-and-size/index.spec.ts +++ b/examples/indentation-type-and-size/index.spec.ts @@ -1,4 +1,6 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); import { generate } from './index'; describe('Should render the typescript class with 4 tabs as indentation', () => { @@ -7,8 +9,7 @@ describe('Should render the typescript class with 4 tabs as indentation', () => }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/indentation-type-and-size/index.ts b/examples/indentation-type-and-size/index.ts index b31c977551..ca4fcde522 100644 --- a/examples/indentation-type-and-size/index.ts +++ b/examples/indentation-type-and-size/index.ts @@ -24,4 +24,6 @@ export async function generate(): Promise { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/integrate-with-react/.gitignore b/examples/integrate-with-react/.gitignore new file mode 100644 index 0000000000..4d29575de8 --- /dev/null +++ b/examples/integrate-with-react/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/examples/integrate-with-react/README.md b/examples/integrate-with-react/README.md new file mode 100644 index 0000000000..eac0264de8 --- /dev/null +++ b/examples/integrate-with-react/README.md @@ -0,0 +1,17 @@ +# Generate JavaScript Models + +A basic example to generate JavaScript data models + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/integrate-with-react/package-lock.json b/examples/integrate-with-react/package-lock.json new file mode 100644 index 0000000000..57b80cc400 --- /dev/null +++ b/examples/integrate-with-react/package-lock.json @@ -0,0 +1,27089 @@ +{ + "name": "integrate-with-react", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.3.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", + "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==" + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", + "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.18.9.tgz", + "integrity": "sha512-KzSGpMBggz4fKbRbWLNyPVTuQr6cmCcBhOyXTw/fieOVaw5oYAwcAj4a7UKcDYCPxQq+CG1NCDZH9e2JTXquiQ==", + "dependencies": { + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.11.0", + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/eslint-parser/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz", + "integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==", + "dependencies": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", + "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", + "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", + "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", + "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", + "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz", + "integrity": "sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w==", + "dependencies": { + "@babel/helper-function-name": "^7.18.9", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.11", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", + "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.10.tgz", + "integrity": "sha512-wdGTwWF5QtpTY/gbBtQLAiCnoxfD4qMbN87NYZle1dOZ9Os8Y6zXcKrIaOU8W+TIvFUWVGG9tUgNww3CjXRVVw==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/plugin-syntax-decorators": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", + "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.18.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.18.6.tgz", + "integrity": "sha512-fqyLgjcxf/1yhyZ6A+yo1u9gJ7eleFQod2lkaUsF9DQ7sbbY3Ligym3L0+I2c0WmqNKDpoD9UTb1AKP3qRMOAQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", + "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", + "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", + "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", + "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.9.tgz", + "integrity": "sha512-+G6rp2zRuOAInY5wcggsx4+QVao1qPM0osC9fTUVlAV3zOrzTCnrMAFVnR6+a3T8wz1wFIH7KhYMcMB3u1n80A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-flow": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", + "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", + "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", + "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-identifier": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", + "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.18.9.tgz", + "integrity": "sha512-IrTYh1I3YCEL1trjknnlLKTp5JggjzhKl/d3ibzPc97JhpFcDTr38Jdek/oX4cFbS6By0bXJcOkpRvJ5ZHK2wQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.10.tgz", + "integrity": "sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", + "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", + "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.10.tgz", + "integrity": "sha512-j2HQCJuMbi88QftIb5zlRu3c7PU+sXNnscqsrjqegoGiCgXR569pEdben9vly5QHKL2ilYkfnSwu64zsZo/VYQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-typescript": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.10", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz", + "integrity": "sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A==", + "dependencies": { + "core-js-pure": "^3.20.2", + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", + "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.11", + "@babel/types": "^7.18.10", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", + "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "dependencies": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + }, + "node_modules/@csstools/normalize.css": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", + "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.5.tgz", + "integrity": "sha512-Id/9wBT7FkgFzdEpiEWrsVd4ltDxN0rI0QS0SChbeQiSuux3z21SJCRLu6h2cvCEUmaRi+VD0mHFj+GJD4GFnw==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", + "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", + "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/source-map/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dependencies": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz", + "integrity": "sha512-bcKCAzF0DV2IIROp9ZHkRJa6O4jy7NlnHdWL3GmcUxYWNjLXkK5kfELELwEfSP5hXPfVL/qOGMAROuMQb9GG8Q==", + "dependencies": { + "ansi-html-community": "^0.0.8", + "common-path-prefix": "^3.0.0", + "core-js-pure": "^3.8.1", + "error-stack-parser": "^2.0.6", + "find-up": "^5.0.0", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "@types/webpack": "4.x || 5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <3.0.0", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x || 4.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz", + "integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==" + }, + "node_modules/@sinclair/typebox": { + "version": "0.24.27", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.27.tgz", + "integrity": "sha512-K7C7IlQ3zLePEZleUN21ceBA2aLcMnLHTLph8QWk1JK37L90obdpY+QGY8bXMKxf1ht1Z0MNewvXxWv0oGDYFg==" + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "dependencies": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "dependencies": { + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "dependencies": { + "@babel/types": "^7.12.6" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "dependencies": { + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "dependencies": { + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@testing-library/dom": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.16.1.tgz", + "integrity": "sha512-XEV2mBxgv6DKjL3+U3WEUzBgT2CjYksoXGlLrrJXYP8OvRfGkBonvelkorazpFlp8tkEecO06r43vN4DIEyegQ==", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/@testing-library/jest-dom": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", + "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/react": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.3.0.tgz", + "integrity": "sha512-DB79aA426+deFgGSjnf5grczDPiL4taK3hFaa+M5q7q20Kcve9eQottOG5kZ74KEr55v0tU2CQormSSDK87zYQ==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==" + }, + "node_modules/@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.0.tgz", + "integrity": "sha512-v4Vwdko+pgymgS+A2UIaJru93zQd85vIGWObM5ekZNdXCKtDYqATlEYnWgfo86Q6I1Lh0oXnksDnMU1cwmlPDw==", + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", + "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" + }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.30", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz", + "integrity": "sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "28.1.6", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-28.1.6.tgz", + "integrity": "sha512-0RbGAFMfcBJKOmqRazM8L98uokwuwD5F8rHrv/ZMbrZBwVOWZUyPG6VFNscjYr/vjM3Vu4fRrCPbOs42AfemaQ==", + "dependencies": { + "jest-matcher-utils": "^28.0.0", + "pretty-format": "^28.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + }, + "node_modules/@types/node": { + "version": "18.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.4.tgz", + "integrity": "sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==" + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "node_modules/@types/prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", + "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "node_modules/@types/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "node_modules/@types/react": { + "version": "18.0.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.15.tgz", + "integrity": "sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.6.tgz", + "integrity": "sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" + }, + "node_modules/@types/testing-library__jest-dom": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", + "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", + "dependencies": { + "@types/jest": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", + "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" + }, + "node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.32.0.tgz", + "integrity": "sha512-CHLuz5Uz7bHP2WgVlvoZGhf0BvFakBJKAD/43Ty0emn4wXWv5k01ND0C0fHcl/Im8Td2y/7h44E9pca9qAu2ew==", + "dependencies": { + "@typescript-eslint/scope-manager": "5.32.0", + "@typescript-eslint/type-utils": "5.32.0", + "@typescript-eslint/utils": "5.32.0", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.32.0.tgz", + "integrity": "sha512-/x72MkqLAoOQSOHFxdm17irJ1PNDWtdrMmfacaYniGT26nibak8vxEf9xmoVE+yTYL8N77I2icPtw89Yx6HvNg==", + "dependencies": { + "@typescript-eslint/utils": "5.32.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.32.0.tgz", + "integrity": "sha512-IxRtsehdGV9GFQ35IGm5oKKR2OGcazUoiNBxhRV160iF9FoyuXxjY+rIqs1gfnd+4eL98OjeGnMpE7RF/NBb3A==", + "dependencies": { + "@typescript-eslint/scope-manager": "5.32.0", + "@typescript-eslint/types": "5.32.0", + "@typescript-eslint/typescript-estree": "5.32.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.32.0.tgz", + "integrity": "sha512-KyAE+tUON0D7tNz92p1uetRqVJiiAkeluvwvZOqBmW9z2XApmk5WSMV9FrzOroAcVxJZB3GfUwVKr98Dr/OjOg==", + "dependencies": { + "@typescript-eslint/types": "5.32.0", + "@typescript-eslint/visitor-keys": "5.32.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.32.0.tgz", + "integrity": "sha512-0gSsIhFDduBz3QcHJIp3qRCvVYbqzHg8D6bHFsDMrm0rURYDj+skBK2zmYebdCp+4nrd9VWd13egvhYFJj/wZg==", + "dependencies": { + "@typescript-eslint/utils": "5.32.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.32.0.tgz", + "integrity": "sha512-EBUKs68DOcT/EjGfzywp+f8wG9Zw6gj6BjWu7KV/IYllqKJFPlZlLSYw/PTvVyiRw50t6wVbgv4p9uE2h6sZrQ==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.32.0.tgz", + "integrity": "sha512-ZVAUkvPk3ITGtCLU5J4atCw9RTxK+SRc6hXqLtllC2sGSeMFWN+YwbiJR9CFrSFJ3w4SJfcWtDwNb/DmUIHdhg==", + "dependencies": { + "@typescript-eslint/types": "5.32.0", + "@typescript-eslint/visitor-keys": "5.32.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.32.0.tgz", + "integrity": "sha512-W7lYIAI5Zlc5K082dGR27Fczjb3Q57ECcXefKU/f0ajM5ToM0P+N9NmJWip8GmGu/g6QISNT+K6KYB+iSHjXCQ==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.32.0", + "@typescript-eslint/types": "5.32.0", + "@typescript-eslint/typescript-estree": "5.32.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.32.0.tgz", + "integrity": "sha512-S54xOHZgfThiZ38/ZGTgB2rqx51CMJ5MCfVT2IplK4Q7hgzGfe0nLzLCcenDnc/cSjP568hdeKfeDcBgqNHD/g==", + "dependencies": { + "@typescript-eslint/types": "5.32.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-node/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.0.tgz", + "integrity": "sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "node_modules/array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz", + "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz", + "integrity": "sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.3", + "caniuse-lite": "^1.0.30001373", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axe-core": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", + "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" + }, + "node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dependencies": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "peerDependencies": { + "@babel/core": "^7.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", + "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.2", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", + "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dependencies": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "node_modules/bfj": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", + "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "dependencies": { + "bluebird": "^3.5.5", + "check-types": "^11.1.1", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/bonjour-service": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.13.tgz", + "integrity": "sha512-LWKRU/7EqDUC9CTAQtuZl5HzBALoCYwtLhffW3et7vZMwv3bWLpJf8bRYlMD5OCcDpTfnPgNCV4yo9ZIaJGMiA==", + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "node_modules/browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camel-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001374", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz", + "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/check-types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", + "integrity": "sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==" + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==" + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" + }, + "node_modules/clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/coa/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/coa/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/coa/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==" + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/core-js": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz", + "integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", + "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "dependencies": { + "browserslist": "^4.21.3", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-js-pure": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.24.1.tgz", + "integrity": "sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", + "integrity": "sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==", + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "dependencies": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" + }, + "node_modules/cssdb": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-6.6.3.tgz", + "integrity": "sha512-7GDvDSmE+20+WcSMhP17Q1EVWUrLlbxxpMDqG731n8P99JhnQZHR9YvtjPvEHfjFUjvQJvdpKCjlKOX+xe4UVA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.12.tgz", + "integrity": "sha512-TgvArbEZu0lk/dvg2ja+B7kYoD7BBCmn3+k58xD0qjrGHsFzXY/wKTo9M5egcUCabPol05e/PVoIu79s2JN4WQ==", + "dependencies": { + "cssnano-preset-default": "^5.2.12", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz", + "integrity": "sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew==", + "dependencies": { + "css-declaration-sorter": "^6.3.0", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.2", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.6", + "postcss-merge-rules": "^5.1.2", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.3", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/csso/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + }, + "node_modules/csstype": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", + "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "dependencies": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "node_modules/diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "node_modules/dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", + "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==" + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domhandler/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.211", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz", + "integrity": "sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==" + }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.21.0.tgz", + "integrity": "sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA==", + "dependencies": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.3", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dependencies": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "dependencies": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@babel/plugin-syntax-flow": "^7.14.5", + "@babel/plugin-transform-react-jsx": "^7.14.9", + "eslint": "^8.1.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", + "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", + "dependencies": { + "@babel/runtime": "^7.18.9", + "aria-query": "^4.2.2", + "array-includes": "^3.1.5", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.4.3", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.2", + "language-tags": "^1.0.5", + "minimatch": "^3.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz", + "integrity": "sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==", + "dependencies": { + "array-includes": "^3.1.5", + "array.prototype.flatmap": "^1.3.0", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.1", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-testing-library": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.6.0.tgz", + "integrity": "sha512-y63TRzPhGCMNsnUwMGJU1MFWc/3GvYw+nzobp9QiyNTTKsgAt5RKAOT1I34+XqVBpX1lC8bScoOjCkP7iRv0Mw==", + "dependencies": { + "@typescript-eslint/utils": "^5.13.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6" + }, + "peerDependencies": { + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "dependencies": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/espree": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", + "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/expect/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/expect/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/expect/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/expect/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/expect/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/expect/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/expect/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/expect/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==" + }, + "node_modules/follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", + "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=10", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "webpack": "^5.20.0" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/htmlparser2/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/htmlparser2/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/idb": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.2.tgz", + "integrity": "sha512-jjKrT1EnyZewQ/gCBb/eyiYrhGzws2FeY92Yx8qT9S9GeQAmo4JFVIiWRIfKW/6Ob9A+UDAOW9j9jn58fy2HIg==" + }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "9.0.15", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", + "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-config/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-config/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-jasmine2/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-jasmine2/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-leak-detector/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "dependencies": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "jest": "^27.0.0 || ^28.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", + "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-watch-typeahead/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "dependencies": { + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest/node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dependencies": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz", + "integrity": "sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==", + "dependencies": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.2" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" + }, + "node_modules/language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "dependencies": { + "language-subtag-registry": "~0.3.2" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lower-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", + "dependencies": { + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/no-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz", + "integrity": "sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", + "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", + "dependencies": { + "array.prototype.reduce": "^1.0.4", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", + "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", + "dependencies": { + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/param-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascal-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-browser-comments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "browserslist": ">=4", + "postcss": ">=8" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz", + "integrity": "sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==", + "dependencies": { + "browserslist": "^4.20.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.8", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", + "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-flexbugs-fixes": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "peerDependencies": { + "postcss": "^8.1.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz", + "integrity": "sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz", + "integrity": "sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==", + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz", + "integrity": "sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==", + "dependencies": { + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "dependencies": { + "postcss-selector-parser": "^6.0.6" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nesting": { + "version": "10.1.10", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.10.tgz", + "integrity": "sha512-lqd7LXCq0gWc0wKXtoKDru5wEUNjm3OryLVNRZ8OnW8km6fSNUuFrjEhU3nklxXE2jvd4qrox566acgh+xQt8w==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-normalize": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", + "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", + "dependencies": { + "@csstools/normalize.css": "*", + "postcss-browser-comments": "^4", + "sanitize.css": "*" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "browserslist": ">= 4", + "postcss": ">= 8" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "dependencies": { + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-opacity-percentage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", + "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==", + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "engines": { + "node": "^12 || ^14 || >=16" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.7.2.tgz", + "integrity": "sha512-1q0ih7EDsZmCb/FMDRvosna7Gsbdx8CvYO5hYT120hcp2ZAuOHpSzibujZ4JpIUcAC02PG6b+eftxqjTFh5BNA==", + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.0.4", + "@csstools/postcss-color-function": "^1.1.0", + "@csstools/postcss-font-format-keywords": "^1.0.0", + "@csstools/postcss-hwb-function": "^1.0.1", + "@csstools/postcss-ic-unit": "^1.0.0", + "@csstools/postcss-is-pseudo-class": "^2.0.6", + "@csstools/postcss-normalize-display-values": "^1.0.0", + "@csstools/postcss-oklab-function": "^1.1.0", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.1", + "@csstools/postcss-unset-value": "^1.0.1", + "autoprefixer": "^10.4.7", + "browserslist": "^4.21.0", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^6.6.3", + "postcss-attribute-case-insensitive": "^5.0.1", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.3", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.0", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.8", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.4", + "postcss-double-position-gradients": "^3.1.1", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.3", + "postcss-image-set-function": "^4.0.6", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.0", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.9", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.3", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.4", + "postcss-pseudo-class-any-link": "^7.1.5", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-svgo/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/postcss-svgo/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/postcss-svgo/node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/postcss-svgo/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/postcss-svgo/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/postcss-svgo/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/postcss-svgo/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/postcss-svgo/node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/postcss-svgo/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "dependencies": { + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/react-dev-utils/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", + "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-scripts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", + "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "dependencies": { + "@babel/core": "^7.16.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", + "@svgr/webpack": "^5.5.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.18.1", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "eslint": "^8.3.0", + "eslint-config-react-app": "^7.0.1", + "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "html-webpack-plugin": "^5.5.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^27.4.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", + "mini-css-extract-plugin": "^2.4.5", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", + "prompts": "^2.4.2", + "react-app-polyfill": "^3.0.0", + "react-dev-utils": "^12.0.1", + "react-refresh": "^0.11.0", + "resolve": "^1.20.0", + "resolve-url-loader": "^4.0.0", + "sass-loader": "^12.3.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.1", + "tailwindcss": "^3.0.2", + "terser-webpack-plugin": "^5.2.5", + "webpack": "^5.64.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", + "workbox-webpack-plugin": "^6.4.1" + }, + "bin": { + "react-scripts": "bin/react-scripts.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + }, + "peerDependencies": { + "react": ">= 16", + "typescript": "^3.2.1 || ^4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", + "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "dependencies": { + "minimatch": "3.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/recursive-readdir/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", + "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" + }, + "node_modules/regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/renderkid/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/renderkid/node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/renderkid/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=8.9" + }, + "peerDependencies": { + "rework": "1.0.1", + "rework-visit": "1.0.0" + }, + "peerDependenciesMeta": { + "rework": { + "optional": true + }, + "rework-visit": { + "optional": true + } + } + }, + "node_modules/resolve-url-loader/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.77.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.2.tgz", + "integrity": "sha512-m/4YzYgLcpMQbxX3NmAqDvwLATZzxt8bIegO78FZLl+lAgKJBd1DRAOeEiZcKOIOPjxE6ewHWHNgGEalFXuz1g==", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sanitize.css": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" + }, + "node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "node_modules/selfsigned": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", + "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", + "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, + "node_modules/stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.1", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/stylehacks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "dependencies": { + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/svgo/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/svgo/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/tailwindcss": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.1.7.tgz", + "integrity": "sha512-r7mgumZ3k0InfVPpGWcX8X/Ut4xBfv+1O/+C73ar/m01LxGVzWvPxF/w6xIUPEztrCoz7axfx0SMdh8FH8ZvRQ==", + "dependencies": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.1", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.6", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.14", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "5.0.6", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", + "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.7", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.7.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "node_modules/throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, + "node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/web-vitals": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", + "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==" + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.3.tgz", + "integrity": "sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw==", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-manifest-plugin": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", + "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", + "dependencies": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "peerDependencies": { + "webpack": "^4.44.2 || ^5.47.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workbox-background-sync": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz", + "integrity": "sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz", + "integrity": "sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-build": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.4.tgz", + "integrity": "sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==", + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "6.5.4", + "workbox-broadcast-update": "6.5.4", + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-google-analytics": "6.5.4", + "workbox-navigation-preload": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-range-requests": "6.5.4", + "workbox-recipes": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4", + "workbox-streams": "6.5.4", + "workbox-sw": "6.5.4", + "workbox-window": "6.5.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/workbox-build/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/workbox-build/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/workbox-build/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/workbox-build/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workbox-build/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/workbox-build/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/workbox-build/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz", + "integrity": "sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-core": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz", + "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==" + }, + "node_modules/workbox-expiration": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.4.tgz", + "integrity": "sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-google-analytics": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz", + "integrity": "sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==", + "dependencies": { + "workbox-background-sync": "6.5.4", + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz", + "integrity": "sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-precaching": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.4.tgz", + "integrity": "sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==", + "dependencies": { + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "node_modules/workbox-range-requests": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz", + "integrity": "sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-recipes": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.4.tgz", + "integrity": "sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==", + "dependencies": { + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "node_modules/workbox-routing": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz", + "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-strategies": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz", + "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-streams": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.4.tgz", + "integrity": "sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==", + "dependencies": { + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4" + } + }, + "node_modules/workbox-sw": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.4.tgz", + "integrity": "sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==" + }, + "node_modules/workbox-webpack-plugin": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz", + "integrity": "sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg==", + "dependencies": { + "fast-json-stable-stringify": "^2.1.0", + "pretty-bytes": "^5.4.1", + "upath": "^1.2.0", + "webpack-sources": "^1.4.3", + "workbox-build": "6.5.4" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "webpack": "^4.4.0 || ^5.9.0" + } + }, + "node_modules/workbox-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workbox-webpack-plugin/node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/workbox-window": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.4.tgz", + "integrity": "sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==", + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "6.5.4" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@adobe/css-tools": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", + "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==" + }, + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "requires": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", + "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==" + }, + "@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/eslint-parser": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.18.9.tgz", + "integrity": "sha512-KzSGpMBggz4fKbRbWLNyPVTuQr6cmCcBhOyXTw/fieOVaw5oYAwcAj4a7UKcDYCPxQq+CG1NCDZH9e2JTXquiQ==", + "requires": { + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/generator": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz", + "integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==", + "requires": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "requires": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", + "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", + "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", + "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "requires": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "requires": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", + "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==" + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-replace-supers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", + "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==" + }, + "@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==" + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" + }, + "@babel/helper-wrap-function": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz", + "integrity": "sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w==", + "requires": { + "@babel/helper-function-name": "^7.18.9", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.11", + "@babel/types": "^7.18.10" + } + }, + "@babel/helpers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "requires": { + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", + "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==" + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.10.tgz", + "integrity": "sha512-wdGTwWF5QtpTY/gbBtQLAiCnoxfD4qMbN87NYZle1dOZ9Os8Y6zXcKrIaOU8W+TIvFUWVGG9tUgNww3CjXRVVw==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/plugin-syntax-decorators": "^7.18.6" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", + "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.18.8" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.18.6.tgz", + "integrity": "sha512-fqyLgjcxf/1yhyZ6A+yo1u9gJ7eleFQod2lkaUsF9DQ7sbbY3Ligym3L0+I2c0WmqNKDpoD9UTb1AKP3qRMOAQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-flow": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", + "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", + "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", + "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", + "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-flow-strip-types": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.9.tgz", + "integrity": "sha512-+G6rp2zRuOAInY5wcggsx4+QVao1qPM0osC9fTUVlAV3zOrzTCnrMAFVnR6+a3T8wz1wFIH7KhYMcMB3u1n80A==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-flow": "^7.18.6" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "requires": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", + "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", + "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", + "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "requires": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-identifier": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", + "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-constant-elements": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.18.9.tgz", + "integrity": "sha512-IrTYh1I3YCEL1trjknnlLKTp5JggjzhKl/d3ibzPc97JhpFcDTr38Jdek/oX4cFbS6By0bXJcOkpRvJ5ZHK2wQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.10.tgz", + "integrity": "sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.18.10" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "requires": { + "@babel/plugin-transform-react-jsx": "^7.18.6" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", + "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", + "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.10.tgz", + "integrity": "sha512-j2HQCJuMbi88QftIb5zlRu3c7PU+sXNnscqsrjqegoGiCgXR569pEdben9vly5QHKL2ilYkfnSwu64zsZo/VYQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-typescript": "^7.18.6" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/preset-env": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.10", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-react": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + } + }, + "@babel/preset-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" + } + }, + "@babel/runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz", + "integrity": "sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A==", + "requires": { + "core-js-pure": "^3.20.2", + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", + "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.11", + "@babel/types": "^7.18.10", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", + "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "requires": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + }, + "@csstools/normalize.css": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", + "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" + }, + "@csstools/postcss-cascade-layers": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.5.tgz", + "integrity": "sha512-Id/9wBT7FkgFzdEpiEWrsVd4ltDxN0rI0QS0SChbeQiSuux3z21SJCRLu6h2cvCEUmaRi+VD0mHFj+GJD4GFnw==", + "requires": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "requires": {} + }, + "@csstools/selector-specificity": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", + "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "requires": {} + }, + "@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", + "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==" + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==" + }, + "@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "requires": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + } + }, + "@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "requires": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + } + }, + "@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "requires": { + "@sinclair/typebox": "^0.24.1" + } + }, + "@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "requires": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + } + }, + "@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz", + "integrity": "sha512-bcKCAzF0DV2IIROp9ZHkRJa6O4jy7NlnHdWL3GmcUxYWNjLXkK5kfELELwEfSP5hXPfVL/qOGMAROuMQb9GG8Q==", + "requires": { + "ansi-html-community": "^0.0.8", + "common-path-prefix": "^3.0.0", + "core-js-pure": "^3.8.1", + "error-stack-parser": "^2.0.6", + "find-up": "^5.0.0", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "source-map": "^0.7.3" + } + }, + "@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + } + }, + "@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "requires": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + } + }, + "@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "requires": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + } + } + }, + "@rushstack/eslint-patch": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz", + "integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==" + }, + "@sinclair/typebox": { + "version": "0.24.27", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.27.tgz", + "integrity": "sha512-K7C7IlQ3zLePEZleUN21ceBA2aLcMnLHTLph8QWk1JK37L90obdpY+QGY8bXMKxf1ht1Z0MNewvXxWv0oGDYFg==" + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "requires": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==" + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==" + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==" + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==" + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==" + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==" + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==" + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==" + }, + "@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + } + }, + "@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "requires": { + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "requires": { + "@babel/types": "^7.12.6" + } + }, + "@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "requires": { + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" + } + }, + "@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "requires": { + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" + } + }, + "@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "requires": { + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" + } + }, + "@testing-library/dom": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.16.1.tgz", + "integrity": "sha512-XEV2mBxgv6DKjL3+U3WEUzBgT2CjYksoXGlLrrJXYP8OvRfGkBonvelkorazpFlp8tkEecO06r43vN4DIEyegQ==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "@testing-library/jest-dom": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", + "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "requires": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + } + }, + "@testing-library/react": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.3.0.tgz", + "integrity": "sha512-DB79aA426+deFgGSjnf5grczDPiL4taK3hFaa+M5q7q20Kcve9eQottOG5kZ74KEr55v0tU2CQormSSDK87zYQ==", + "requires": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + } + }, + "@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "requires": { + "@babel/runtime": "^7.12.5" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + }, + "@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==" + }, + "@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.0.tgz", + "integrity": "sha512-v4Vwdko+pgymgS+A2UIaJru93zQd85vIGWObM5ekZNdXCKtDYqATlEYnWgfo86Q6I1Lh0oXnksDnMU1cwmlPDw==", + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/eslint": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", + "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" + }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.30", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz", + "integrity": "sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "requires": { + "@types/node": "*" + } + }, + "@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "28.1.6", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-28.1.6.tgz", + "integrity": "sha512-0RbGAFMfcBJKOmqRazM8L98uokwuwD5F8rHrv/ZMbrZBwVOWZUyPG6VFNscjYr/vjM3Vu4fRrCPbOs42AfemaQ==", + "requires": { + "jest-matcher-utils": "^28.0.0", + "pretty-format": "^28.0.0" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + }, + "@types/node": { + "version": "18.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.4.tgz", + "integrity": "sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==" + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "@types/prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", + "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==" + }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "@types/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "@types/react": { + "version": "18.0.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.15.tgz", + "integrity": "sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.6.tgz", + "integrity": "sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==", + "requires": { + "@types/react": "*" + } + }, + "@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "requires": { + "@types/node": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" + }, + "@types/testing-library__jest-dom": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", + "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", + "requires": { + "@types/jest": "*" + } + }, + "@types/trusted-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", + "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "requires": { + "@types/node": "*" + } + }, + "@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.32.0.tgz", + "integrity": "sha512-CHLuz5Uz7bHP2WgVlvoZGhf0BvFakBJKAD/43Ty0emn4wXWv5k01ND0C0fHcl/Im8Td2y/7h44E9pca9qAu2ew==", + "requires": { + "@typescript-eslint/scope-manager": "5.32.0", + "@typescript-eslint/type-utils": "5.32.0", + "@typescript-eslint/utils": "5.32.0", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.32.0.tgz", + "integrity": "sha512-/x72MkqLAoOQSOHFxdm17irJ1PNDWtdrMmfacaYniGT26nibak8vxEf9xmoVE+yTYL8N77I2icPtw89Yx6HvNg==", + "requires": { + "@typescript-eslint/utils": "5.32.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.32.0.tgz", + "integrity": "sha512-IxRtsehdGV9GFQ35IGm5oKKR2OGcazUoiNBxhRV160iF9FoyuXxjY+rIqs1gfnd+4eL98OjeGnMpE7RF/NBb3A==", + "requires": { + "@typescript-eslint/scope-manager": "5.32.0", + "@typescript-eslint/types": "5.32.0", + "@typescript-eslint/typescript-estree": "5.32.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.32.0.tgz", + "integrity": "sha512-KyAE+tUON0D7tNz92p1uetRqVJiiAkeluvwvZOqBmW9z2XApmk5WSMV9FrzOroAcVxJZB3GfUwVKr98Dr/OjOg==", + "requires": { + "@typescript-eslint/types": "5.32.0", + "@typescript-eslint/visitor-keys": "5.32.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.32.0.tgz", + "integrity": "sha512-0gSsIhFDduBz3QcHJIp3qRCvVYbqzHg8D6bHFsDMrm0rURYDj+skBK2zmYebdCp+4nrd9VWd13egvhYFJj/wZg==", + "requires": { + "@typescript-eslint/utils": "5.32.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.32.0.tgz", + "integrity": "sha512-EBUKs68DOcT/EjGfzywp+f8wG9Zw6gj6BjWu7KV/IYllqKJFPlZlLSYw/PTvVyiRw50t6wVbgv4p9uE2h6sZrQ==" + }, + "@typescript-eslint/typescript-estree": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.32.0.tgz", + "integrity": "sha512-ZVAUkvPk3ITGtCLU5J4atCw9RTxK+SRc6hXqLtllC2sGSeMFWN+YwbiJR9CFrSFJ3w4SJfcWtDwNb/DmUIHdhg==", + "requires": { + "@typescript-eslint/types": "5.32.0", + "@typescript-eslint/visitor-keys": "5.32.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.32.0.tgz", + "integrity": "sha512-W7lYIAI5Zlc5K082dGR27Fczjb3Q57ECcXefKU/f0ajM5ToM0P+N9NmJWip8GmGu/g6QISNT+K6KYB+iSHjXCQ==", + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.32.0", + "@typescript-eslint/types": "5.32.0", + "@typescript-eslint/typescript-estree": "5.32.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.32.0.tgz", + "integrity": "sha512-S54xOHZgfThiZ38/ZGTgB2rqx51CMJ5MCfVT2IplK4Q7hgzGfe0nLzLCcenDnc/cSjP568hdeKfeDcBgqNHD/g==", + "requires": { + "@typescript-eslint/types": "5.32.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==" + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + } + } + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "requires": {} + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" + }, + "address": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.0.tgz", + "integrity": "sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig==" + }, + "adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + } + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==" + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "array.prototype.flat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.flatmap": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.reduce": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz", + "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + } + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "autoprefixer": { + "version": "10.4.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz", + "integrity": "sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==", + "requires": { + "browserslist": "^4.21.3", + "caniuse-lite": "^1.0.30001373", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "axe-core": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", + "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==" + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" + }, + "babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "requires": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "requires": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + } + }, + "babel-plugin-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "requires": {} + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", + "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.2", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", + "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.2" + } + }, + "babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "requires": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "requires": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "bfj": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", + "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "requires": { + "bluebird": "^3.5.5", + "check-types": "^11.1.1", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "bonjour-service": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.13.tgz", + "integrity": "sha512-LWKRU/7EqDUC9CTAQtuZl5HzBALoCYwtLhffW3et7vZMwv3bWLpJf8bRYlMD5OCcDpTfnPgNCV4yo9ZIaJGMiA==", + "requires": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "requires": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001374", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz", + "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==" + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==" + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" + }, + "check-types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", + "integrity": "sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==" + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + }, + "ci-info": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==" + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" + }, + "clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==" + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==" + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + }, + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "core-js": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz", + "integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==" + }, + "core-js-compat": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", + "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "requires": { + "browserslist": "^4.21.3", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + } + } + }, + "core-js-pure": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.24.1.tgz", + "integrity": "sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + }, + "css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-declaration-sorter": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", + "integrity": "sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==", + "requires": {} + }, + "css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + } + }, + "css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "requires": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "requires": {} + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" + }, + "cssdb": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-6.6.3.tgz", + "integrity": "sha512-7GDvDSmE+20+WcSMhP17Q1EVWUrLlbxxpMDqG731n8P99JhnQZHR9YvtjPvEHfjFUjvQJvdpKCjlKOX+xe4UVA==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssnano": { + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.12.tgz", + "integrity": "sha512-TgvArbEZu0lk/dvg2ja+B7kYoD7BBCmn3+k58xD0qjrGHsFzXY/wKTo9M5egcUCabPol05e/PVoIu79s2JN4WQ==", + "requires": { + "cssnano-preset-default": "^5.2.12", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "cssnano-preset-default": { + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz", + "integrity": "sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew==", + "requires": { + "css-declaration-sorter": "^6.3.0", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.2", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.6", + "postcss-merge-rules": "^5.1.2", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.3", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + } + }, + "cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "requires": {} + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "requires": { + "css-tree": "^1.1.2" + }, + "dependencies": { + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + } + } + }, + "csstype": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", + "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" + }, + "damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "requires": { + "execa": "^5.0.0" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==" + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "requires": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "requires": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + } + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==" + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-accessibility-api": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", + "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==" + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" + } + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + } + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "requires": { + "jake": "^10.8.5" + } + }, + "electron-to-chromium": { + "version": "1.4.211", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz", + "integrity": "sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==" + }, + "emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==" + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "requires": { + "stackframe": "^1.3.4" + } + }, + "es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "requires": { + "has": "^1.0.3" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "eslint": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.21.0.tgz", + "integrity": "sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA==", + "requires": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.3", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "requires": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + } + } + }, + "eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "requires": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + } + }, + "eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "requires": { + "@typescript-eslint/experimental-utils": "^5.0.0" + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", + "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", + "requires": { + "@babel/runtime": "^7.18.9", + "aria-query": "^4.2.2", + "array-includes": "^3.1.5", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.4.3", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.2", + "language-tags": "^1.0.5", + "minimatch": "^3.1.2", + "semver": "^6.3.0" + }, + "dependencies": { + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "eslint-plugin-react": { + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz", + "integrity": "sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==", + "requires": { + "array-includes": "^3.1.5", + "array.prototype.flatmap": "^1.3.0", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.1", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.7" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "requires": {} + }, + "eslint-plugin-testing-library": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.6.0.tgz", + "integrity": "sha512-y63TRzPhGCMNsnUwMGJU1MFWc/3GvYw+nzobp9QiyNTTKsgAt5RKAOT1I34+XqVBpX1lC8bScoOjCkP7iRv0Mw==", + "requires": { + "@typescript-eslint/utils": "^5.13.0" + } + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" + }, + "eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "requires": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "espree": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", + "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==" + }, + "expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "requires": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "requires": { + "bser": "2.1.1" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + } + }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==" + }, + "follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" + }, + "fork-ts-checker-webpack-plugin": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", + "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + } + } + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "requires": { + "is-glob": "^4.0.3" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + }, + "gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "requires": { + "duplexer": "^0.1.2" + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==" + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } + } + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "requires": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "dependencies": { + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + } + } + }, + "html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "requires": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + }, + "dependencies": { + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + } + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "requires": {} + }, + "idb": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.2.tgz", + "integrity": "sha512-jjKrT1EnyZewQ/gCBb/eyiYrhGzws2FeY92Yx8qT9S9GeQAmo4JFVIiWRIfKW/6Ob9A+UDAOW9j9jn58fy2HIg==" + }, + "identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "requires": { + "harmony-reflect": "^1.4.6" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + }, + "immer": { + "version": "9.0.15", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", + "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==" + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + }, + "is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" + }, + "is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==" + }, + "istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "requires": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "requires": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + } + } + } + }, + "jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "requires": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + } + }, + "jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "requires": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + } + }, + "jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==" + }, + "jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "requires": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "requires": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "requires": {} + }, + "jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==" + }, + "jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "requires": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + } + }, + "jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" + } + } + }, + "jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + } + }, + "jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "requires": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "requires": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "requires": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + } + } + }, + "@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "requires": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "requires": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", + "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==" + }, + "jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + } + } + }, + "jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==" + }, + "jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "requires": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "dependencies": { + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + }, + "string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "requires": { + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "char-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==" + } + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "requires": { + "ansi-regex": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + } + } + } + } + }, + "jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "requires": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" + }, + "jsx-ast-utils": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz", + "integrity": "sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==", + "requires": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" + }, + "language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" + }, + "language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "requires": { + "language-subtag-registry": "~0.3.2" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==" + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" + }, + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==" + }, + "magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "requires": { + "tmpl": "1.0.5" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", + "requires": { + "fs-monkey": "^1.0.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" + }, + "mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "requires": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + } + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "nwsapi": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz", + "integrity": "sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", + "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", + "requires": { + "array.prototype.reduce": "^1.0.4", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.1" + } + }, + "object.hasown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", + "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", + "requires": { + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==" + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + } + } + }, + "postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-browser-comments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "requires": {} + }, + "postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "requires": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-convert-values": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz", + "integrity": "sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==", + "requires": { + "browserslist": "^4.20.3", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-properties": { + "version": "12.1.8", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", + "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "requires": {} + }, + "postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "requires": {} + }, + "postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "requires": {} + }, + "postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "requires": {} + }, + "postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-flexbugs-fixes": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "requires": {} + }, + "postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "requires": {} + }, + "postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "requires": {} + }, + "postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "requires": {} + }, + "postcss-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", + "requires": { + "camelcase-css": "^2.0.1" + } + }, + "postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + } + }, + "postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + } + }, + "postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "requires": {} + }, + "postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "requires": {} + }, + "postcss-merge-longhand": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz", + "integrity": "sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw==", + "requires": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" + } + }, + "postcss-merge-rules": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz", + "integrity": "sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==", + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "requires": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-params": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz", + "integrity": "sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==", + "requires": { + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "requires": { + "postcss-selector-parser": "^6.0.6" + } + }, + "postcss-nesting": { + "version": "10.1.10", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.10.tgz", + "integrity": "sha512-lqd7LXCq0gWc0wKXtoKDru5wEUNjm3OryLVNRZ8OnW8km6fSNUuFrjEhU3nklxXE2jvd4qrox566acgh+xQt8w==", + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-normalize": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", + "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", + "requires": { + "@csstools/normalize.css": "*", + "postcss-browser-comments": "^4", + "sanitize.css": "*" + } + }, + "postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "requires": {} + }, + "postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-unicode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "requires": { + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "requires": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-opacity-percentage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", + "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==" + }, + "postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "requires": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "requires": {} + }, + "postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-preset-env": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.7.2.tgz", + "integrity": "sha512-1q0ih7EDsZmCb/FMDRvosna7Gsbdx8CvYO5hYT120hcp2ZAuOHpSzibujZ4JpIUcAC02PG6b+eftxqjTFh5BNA==", + "requires": { + "@csstools/postcss-cascade-layers": "^1.0.4", + "@csstools/postcss-color-function": "^1.1.0", + "@csstools/postcss-font-format-keywords": "^1.0.0", + "@csstools/postcss-hwb-function": "^1.0.1", + "@csstools/postcss-ic-unit": "^1.0.0", + "@csstools/postcss-is-pseudo-class": "^2.0.6", + "@csstools/postcss-normalize-display-values": "^1.0.0", + "@csstools/postcss-oklab-function": "^1.1.0", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.1", + "@csstools/postcss-unset-value": "^1.0.1", + "autoprefixer": "^10.4.7", + "browserslist": "^4.21.0", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^6.6.3", + "postcss-attribute-case-insensitive": "^5.0.1", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.3", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.0", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.8", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.4", + "postcss-double-position-gradients": "^3.1.1", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.3", + "postcss-image-set-function": "^4.0.6", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.0", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.9", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.3", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.4", + "postcss-pseudo-class-any-link": "^7.1.5", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-reduce-initial": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "requires": {} + }, + "postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "requires": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "dependencies": { + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "requires": { + "boolbase": "^1.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + } + } + } + }, + "postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + }, + "pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==" + }, + "pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "requires": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "promise": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "requires": { + "asap": "~2.0.6" + } + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + } + } + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + } + } + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "requires": { + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" + } + }, + "react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "requires": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "loader-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", + "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==" + } + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" + }, + "react-scripts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", + "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "requires": { + "@babel/core": "^7.16.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", + "@svgr/webpack": "^5.5.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.18.1", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "eslint": "^8.3.0", + "eslint-config-react-app": "^7.0.1", + "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "fsevents": "^2.3.2", + "html-webpack-plugin": "^5.5.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^27.4.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", + "mini-css-extract-plugin": "^2.4.5", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", + "prompts": "^2.4.2", + "react-app-polyfill": "^3.0.0", + "react-dev-utils": "^12.0.1", + "react-refresh": "^0.11.0", + "resolve": "^1.20.0", + "resolve-url-loader": "^4.0.0", + "sass-loader": "^12.3.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.1", + "tailwindcss": "^3.0.2", + "terser-webpack-plugin": "^5.2.5", + "webpack": "^5.64.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", + "workbox-webpack-plugin": "^6.4.1" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "requires": { + "pify": "^2.3.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "recursive-readdir": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", + "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "requires": { + "minimatch": "3.0.4" + }, + "dependencies": { + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" + }, + "regexpu-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", + "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" + }, + "regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + }, + "renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "requires": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "requires": { + "boolbase": "^1.0.0" + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "requires": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==" + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "rollup": { + "version": "2.77.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.2.tgz", + "integrity": "sha512-m/4YzYgLcpMQbxX3NmAqDvwLATZzxt8bIegO78FZLl+lAgKJBd1DRAOeEiZcKOIOPjxE6ewHWHNgGEalFXuz1g==", + "requires": { + "fsevents": "~2.3.2" + } + }, + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "requires": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "dependencies": { + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "requires": { + "randombytes": "^2.1.0" + } + } + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sanitize.css": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" + }, + "sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "requires": { + "xmlchars": "^2.2.0" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "selfsigned": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", + "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", + "requires": { + "node-forge": "^1" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "source-map-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", + "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", + "requires": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + } + } + }, + "stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + } + } + }, + "string.prototype.matchall": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.1", + "side-channel": "^1.0.4" + } + }, + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + }, + "strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + }, + "style-loader": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "requires": {} + }, + "stylehacks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "requires": { + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "tailwindcss": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.1.7.tgz", + "integrity": "sha512-r7mgumZ3k0InfVPpGWcX8X/Ut4xBfv+1O/+C73ar/m01LxGVzWvPxF/w6xIUPEztrCoz7axfx0SMdh8FH8ZvRQ==", + "requires": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.1", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.6", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.14", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "5.0.6", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1" + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==" + }, + "tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "requires": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "dependencies": { + "type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==" + } + } + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", + "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "requires": { + "@jridgewell/trace-mapping": "^0.3.7", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.7.2" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==" + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "dependencies": { + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + } + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "requires": { + "punycode": "^2.1.1" + } + }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" + }, + "unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, + "update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, + "v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "requires": { + "makeerror": "1.0.12" + } + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "web-vitals": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", + "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==" + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" + }, + "webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.3.tgz", + "integrity": "sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw==", + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "ws": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "requires": {} + } + } + }, + "webpack-manifest-plugin": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", + "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", + "requires": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "requires": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + } + } + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + }, + "workbox-background-sync": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz", + "integrity": "sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==", + "requires": { + "idb": "^7.0.1", + "workbox-core": "6.5.4" + } + }, + "workbox-broadcast-update": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz", + "integrity": "sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-build": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.4.tgz", + "integrity": "sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==", + "requires": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "6.5.4", + "workbox-broadcast-update": "6.5.4", + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-google-analytics": "6.5.4", + "workbox-navigation-preload": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-range-requests": "6.5.4", + "workbox-recipes": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4", + "workbox-streams": "6.5.4", + "workbox-sw": "6.5.4", + "workbox-window": "6.5.4" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "requires": { + "whatwg-url": "^7.0.0" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "requires": { + "punycode": "^2.1.0" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "workbox-cacheable-response": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz", + "integrity": "sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-core": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz", + "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==" + }, + "workbox-expiration": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.4.tgz", + "integrity": "sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==", + "requires": { + "idb": "^7.0.1", + "workbox-core": "6.5.4" + } + }, + "workbox-google-analytics": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz", + "integrity": "sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==", + "requires": { + "workbox-background-sync": "6.5.4", + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "workbox-navigation-preload": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz", + "integrity": "sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-precaching": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.4.tgz", + "integrity": "sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==", + "requires": { + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "workbox-range-requests": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz", + "integrity": "sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-recipes": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.4.tgz", + "integrity": "sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==", + "requires": { + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "workbox-routing": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz", + "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-strategies": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz", + "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-streams": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.4.tgz", + "integrity": "sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==", + "requires": { + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4" + } + }, + "workbox-sw": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.4.tgz", + "integrity": "sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==" + }, + "workbox-webpack-plugin": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz", + "integrity": "sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg==", + "requires": { + "fast-json-stable-stringify": "^2.1.0", + "pretty-bytes": "^5.4.1", + "upath": "^1.2.0", + "webpack-sources": "^1.4.3", + "workbox-build": "6.5.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + } + } + }, + "workbox-window": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.4.tgz", + "integrity": "sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==", + "requires": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "6.5.4" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "requires": {} + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + } + } +} diff --git a/examples/integrate-with-react/package.json b/examples/integrate-with-react/package.json new file mode 100644 index 0000000000..4a01b0263d --- /dev/null +++ b/examples/integrate-with-react/package.json @@ -0,0 +1,29 @@ +{ + "config": { + "example_name": "integrate-with-react" + }, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.3.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "install": "cd ../.. && npm i && npm run build", + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --watchAll=false", + "test:watch": "react-scripts test --watchAll=false", + "eject": "react-scripts eject", + "test:debug": "react-scripts --inspect-brk test --runInBand --no-cache" + }, + "jest": { + "moduleNameMapper": { + "^nimma/legacy$": "/../../node_modules/nimma/dist/legacy/cjs/index.js", + "^nimma/(.*)": "/../../node_modules/nimma/dist/cjs/$1" + } + } +} diff --git a/examples/integrate-with-react/public/index.html b/examples/integrate-with-react/public/index.html new file mode 100644 index 0000000000..351f5d462c --- /dev/null +++ b/examples/integrate-with-react/public/index.html @@ -0,0 +1,32 @@ + + + + + + + + React App + + + +
+ + + diff --git a/examples/integrate-with-react/src/App.js b/examples/integrate-with-react/src/App.js new file mode 100644 index 0000000000..30a5f66a49 --- /dev/null +++ b/examples/integrate-with-react/src/App.js @@ -0,0 +1,33 @@ +import { TypeScriptGenerator } from '../../../'; +import React from 'react'; +import { useState, useEffect } from 'react'; + +export default function App() { + const [models, setModels] = useState(''); + + useEffect(() => { + (async () => { + const generator = new TypeScriptGenerator(); + const input = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + } + } + }; + const models = await generator.generate(input); + const modelsCode = models.map((model) => model.result).join('\n'); + setModels(modelsCode); + })(); + }, []); + + return ( +
+ {models || 'Model generation is processed. Please wait...'} +
+ ); +} diff --git a/examples/integrate-with-react/src/App.test.js b/examples/integrate-with-react/src/App.test.js new file mode 100644 index 0000000000..021fb1ae2e --- /dev/null +++ b/examples/integrate-with-react/src/App.test.js @@ -0,0 +1,10 @@ +import { render, screen, waitFor } from '@testing-library/react'; +import App from './App'; + +test('renders learn react link', async () => { + render(); + const linkElement = screen.getByTestId('App'); + expect(linkElement).toBeInTheDocument(); + const expectedCode = 'class Root {\n private _email?: string;\n\n constructor(input: {\n email?: string,\n }) {\n this._email = input.email;\n }\n\n get email(): string | undefined { return this._email; }\n set email(email: string | undefined) { this._email = email; }\n}'; + await waitFor(() => expect(linkElement.textContent).toEqual(expectedCode)); +}); diff --git a/examples/integrate-with-react/src/index.js b/examples/integrate-with-react/src/index.js new file mode 100644 index 0000000000..12060c793c --- /dev/null +++ b/examples/integrate-with-react/src/index.js @@ -0,0 +1,15 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/examples/integrate-with-react/src/setupTests.js b/examples/integrate-with-react/src/setupTests.js new file mode 100644 index 0000000000..8f2609b7b3 --- /dev/null +++ b/examples/integrate-with-react/src/setupTests.js @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; diff --git a/examples/java-change-collection-type/index.spec.ts b/examples/java-change-collection-type/index.spec.ts index 16f6686758..30a08f7097 100644 --- a/examples/java-change-collection-type/index.spec.ts +++ b/examples/java-change-collection-type/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render collections in Java as List', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to render collections in Java as List', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/java-change-collection-type/index.ts b/examples/java-change-collection-type/index.ts index ec637f7076..e27666e648 100644 --- a/examples/java-change-collection-type/index.ts +++ b/examples/java-change-collection-type/index.ts @@ -10,6 +10,7 @@ const jsonSchemaDraft7 = { properties: { email: { type: 'array', + additionalItems: false, items: { type: 'string', format: 'email' @@ -18,10 +19,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/java-from-typescript-type-with-options/index.spec.ts b/examples/java-from-typescript-type-with-options/index.spec.ts index 00c8385064..ec61906dad 100644 --- a/examples/java-from-typescript-type-with-options/index.spec.ts +++ b/examples/java-from-typescript-type-with-options/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe(`Should be able to generate Java models from a TypeScript type file along with specific options`, () => { @@ -8,9 +10,7 @@ along with specific options`, () => { }); test('and should log expected output to console', async () => { await generate(); - //GenerateWithOptions is called 4x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(4); + expect(spy.mock.calls.length).toEqual(2); expect(spy.mock.calls[1]).toMatchSnapshot(); }); }); - diff --git a/examples/java-from-typescript-type-with-options/index.ts b/examples/java-from-typescript-type-with-options/index.ts index ac5e9b4c6c..ae3a27c05c 100644 --- a/examples/java-from-typescript-type-with-options/index.ts +++ b/examples/java-from-typescript-type-with-options/index.ts @@ -2,24 +2,25 @@ import { JavaGenerator } from '../../src'; import * as path from 'path'; import * as fs from 'fs'; -const generator = new JavaGenerator( - { - processorOptions: { - typescript: { - compilerOptions: { - strictNullChecks: true - } +const generator = new JavaGenerator({ + processorOptions: { + typescript: { + compilerOptions: { + strictNullChecks: true } } } -); +}); const file = path.resolve(__dirname, './typescriptFile.ts'); -const fileContents = fs.readFileSync(path.resolve(__dirname, './typescriptFile.ts'),'utf-8'); +const fileContents = fs.readFileSync( + path.resolve(__dirname, './typescriptFile.ts'), + 'utf-8' +); -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate({ - fileContents, + fileContents, baseFile: file }); @@ -28,4 +29,6 @@ export async function generate() : Promise { } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/java-from-typescript-type/index.spec.ts b/examples/java-from-typescript-type/index.spec.ts index 1992f55ff2..f53358cc65 100644 --- a/examples/java-from-typescript-type/index.spec.ts +++ b/examples/java-from-typescript-type/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate Java model from a TypeScript type file', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to generate Java model from a TypeScript type file', () }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 4x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(4); + expect(spy.mock.calls.length).toEqual(2); expect(spy.mock.calls[1]).toMatchSnapshot(); }); }); diff --git a/examples/java-from-typescript-type/index.ts b/examples/java-from-typescript-type/index.ts index 72495af0fe..e1f8c0e8d4 100644 --- a/examples/java-from-typescript-type/index.ts +++ b/examples/java-from-typescript-type/index.ts @@ -5,11 +5,14 @@ import * as fs from 'fs'; const generator = new JavaGenerator(); const file = path.resolve(__dirname, './typescriptFile.ts'); -const fileContents = fs.readFileSync(path.resolve(__dirname, './typescriptFile.ts'),'utf-8'); +const fileContents = fs.readFileSync( + path.resolve(__dirname, './typescriptFile.ts'), + 'utf-8' +); -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate({ - fileContents, + fileContents, baseFile: file }); @@ -18,4 +21,6 @@ export async function generate() : Promise { } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/java-from-typescript-type/typescriptFile.ts b/examples/java-from-typescript-type/typescriptFile.ts index 48cd48e760..16c206cf19 100644 --- a/examples/java-from-typescript-type/typescriptFile.ts +++ b/examples/java-from-typescript-type/typescriptFile.ts @@ -1,6 +1,6 @@ export type Shape = { size: number; -} +}; export interface InnerData { age: number; diff --git a/examples/java-generate-equals/index.spec.ts b/examples/java-generate-equals/index.spec.ts index 905b579e2b..8e80bf6de6 100644 --- a/examples/java-generate-equals/index.spec.ts +++ b/examples/java-generate-equals/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate a model to overwrite the Equal methods', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to generate a model to overwrite the Equal methods', () }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/java-generate-equals/index.ts b/examples/java-generate-equals/index.ts index ba80aed520..0dbfacbad3 100644 --- a/examples/java-generate-equals/index.ts +++ b/examples/java-generate-equals/index.ts @@ -1,14 +1,14 @@ import { JavaGenerator, JAVA_COMMON_PRESET } from '../../src'; -const generator = new JavaGenerator({ +const generator = new JavaGenerator({ presets: [ { - preset: JAVA_COMMON_PRESET, + preset: JAVA_COMMON_PRESET, options: { equal: true, hashCode: false, classToString: false, - marshalling: false, + marshalling: false } } ] @@ -26,10 +26,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/java-generate-hashcode/index.spec.ts b/examples/java-generate-hashcode/index.spec.ts index 11cc623a40..bc96519f3a 100644 --- a/examples/java-generate-hashcode/index.spec.ts +++ b/examples/java-generate-hashcode/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate a model to overwrite the hashCode method', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to generate a model to overwrite the hashCode method', }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/java-generate-hashcode/index.ts b/examples/java-generate-hashcode/index.ts index 9caabfd64f..6ae1d1788c 100644 --- a/examples/java-generate-hashcode/index.ts +++ b/examples/java-generate-hashcode/index.ts @@ -1,14 +1,14 @@ import { JavaGenerator, JAVA_COMMON_PRESET } from '../../src'; -const generator = new JavaGenerator({ +const generator = new JavaGenerator({ presets: [ { - preset: JAVA_COMMON_PRESET, + preset: JAVA_COMMON_PRESET, options: { equal: false, hashCode: true, classToString: false, - marshalling: false, + marshalling: false } } ] @@ -26,10 +26,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/java-generate-jackson-annotation/__snapshots__/index.spec.ts.snap b/examples/java-generate-jackson-annotation/__snapshots__/index.spec.ts.snap index 400c7945b1..de017ac865 100644 --- a/examples/java-generate-jackson-annotation/__snapshots__/index.spec.ts.snap +++ b/examples/java-generate-jackson-annotation/__snapshots__/index.spec.ts.snap @@ -3,15 +3,15 @@ exports[`Should be able to generate data models for jackson annotation and should log expected output to console 1`] = ` Array [ "public class Root { + @JsonProperty(\\"min_number_prop\\") private Double minNumberProp; + @JsonProperty(\\"max_number_prop\\") private Double maxNumberProp; private Map additionalProperties; - @JsonProperty(\\"min_number_prop\\") public Double getMinNumberProp() { return this.minNumberProp; } public void setMinNumberProp(Double minNumberProp) { this.minNumberProp = minNumberProp; } - @JsonProperty(\\"max_number_prop\\") public Double getMaxNumberProp() { return this.maxNumberProp; } public void setMaxNumberProp(Double maxNumberProp) { this.maxNumberProp = maxNumberProp; } diff --git a/examples/java-generate-jackson-annotation/index.spec.ts b/examples/java-generate-jackson-annotation/index.spec.ts index 34cc2aaa96..7d168c19df 100644 --- a/examples/java-generate-jackson-annotation/index.spec.ts +++ b/examples/java-generate-jackson-annotation/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate data models for jackson annotation', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to generate data models for jackson annotation', () => }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/java-generate-jackson-annotation/index.ts b/examples/java-generate-jackson-annotation/index.ts index 3a1d4e2f31..4f2a7bd585 100644 --- a/examples/java-generate-jackson-annotation/index.ts +++ b/examples/java-generate-jackson-annotation/index.ts @@ -9,7 +9,7 @@ const jsonSchemaDraft7 = { type: 'object', properties: { min_number_prop: { type: 'number' }, - max_number_prop: { type: 'number' }, + max_number_prop: { type: 'number' } } }; @@ -19,4 +19,6 @@ export async function generate(): Promise { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/java-generate-javadoc/index.spec.ts b/examples/java-generate-javadoc/index.spec.ts index 07188ca52a..8db0e012dc 100644 --- a/examples/java-generate-javadoc/index.spec.ts +++ b/examples/java-generate-javadoc/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate JavaDocs', () => { afterAll(() => { @@ -7,7 +9,7 @@ describe('Should be able to generate JavaDocs', () => { }); test('and should log expected output to console', async () => { await generate(); - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/java-generate-javadoc/index.ts b/examples/java-generate-javadoc/index.ts index a40dcc3b7a..2f6395da28 100644 --- a/examples/java-generate-javadoc/index.ts +++ b/examples/java-generate-javadoc/index.ts @@ -1,6 +1,6 @@ import { JavaGenerator, JAVA_DESCRIPTION_PRESET } from '../../src'; -const generator = new JavaGenerator({ +const generator = new JavaGenerator({ presets: [JAVA_DESCRIPTION_PRESET] }); const jsonSchemaDraft7 = { @@ -10,14 +10,20 @@ const jsonSchemaDraft7 = { description: 'Description for class', examples: [{ prop: 'value' }], properties: { - prop: { type: 'string', description: 'Description for prop', examples: ['exampleValue'] }, - }, + prop: { + type: 'string', + description: 'Description for prop', + examples: ['exampleValue'] + } + } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/java-generate-javax-constraint-annotation/__snapshots__/index.spec.ts.snap b/examples/java-generate-javax-constraint-annotation/__snapshots__/index.spec.ts.snap index d295950133..262569e5bf 100644 --- a/examples/java-generate-javax-constraint-annotation/__snapshots__/index.spec.ts.snap +++ b/examples/java-generate-javax-constraint-annotation/__snapshots__/index.spec.ts.snap @@ -3,28 +3,28 @@ exports[`Should be able to generate models with javax.validation.constraints annotations and should log expected output to console 1`] = ` Array [ "public class JavaxAnnotation { + @NotNull + @Min(0) private Double minNumberProp; + @NotNull + @Max(99) private Double maxNumberProp; + @Size(min=2, max=3) private Object[] arrayProp; + @Pattern(regexp=\\"^I_\\") + @Size(min=3) private String stringProp; private Map additionalProperties; - @NotNull - @Min(0) public Double getMinNumberProp() { return this.minNumberProp; } public void setMinNumberProp(Double minNumberProp) { this.minNumberProp = minNumberProp; } - @NotNull - @Max(99) public Double getMaxNumberProp() { return this.maxNumberProp; } public void setMaxNumberProp(Double maxNumberProp) { this.maxNumberProp = maxNumberProp; } - @Size(min=2, max=3) public Object[] getArrayProp() { return this.arrayProp; } public void setArrayProp(Object[] arrayProp) { this.arrayProp = arrayProp; } - @Pattern(regexp=\\"^I_\\") - @Size(min=3) public String getStringProp() { return this.stringProp; } public void setStringProp(String stringProp) { this.stringProp = stringProp; } diff --git a/examples/java-generate-javax-constraint-annotation/index.spec.ts b/examples/java-generate-javax-constraint-annotation/index.spec.ts index b70419b2ad..745c61d7fd 100644 --- a/examples/java-generate-javax-constraint-annotation/index.spec.ts +++ b/examples/java-generate-javax-constraint-annotation/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate models with javax.validation.constraints annotations', () => { afterAll(() => { @@ -7,7 +9,7 @@ describe('Should be able to generate models with javax.validation.constraints an }); test('and should log expected output to console', async () => { await generate(); - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/java-generate-javax-constraint-annotation/index.ts b/examples/java-generate-javax-constraint-annotation/index.ts index 66af786d44..dcd3d36d99 100644 --- a/examples/java-generate-javax-constraint-annotation/index.ts +++ b/examples/java-generate-javax-constraint-annotation/index.ts @@ -1,6 +1,6 @@ import { JavaGenerator, JAVA_CONSTRAINTS_PRESET } from '../../src'; -const generator = new JavaGenerator({ +const generator = new JavaGenerator({ presets: [JAVA_CONSTRAINTS_PRESET] }); const jsonSchemaDraft7 = { @@ -10,16 +10,18 @@ const jsonSchemaDraft7 = { properties: { min_number_prop: { type: 'number', minimum: 0 }, max_number_prop: { type: 'number', exclusiveMaximum: 100 }, - array_prop: { type: 'array', minItems: 2, maxItems: 3, }, + array_prop: { type: 'array', minItems: 2, maxItems: 3 }, string_prop: { type: 'string', pattern: '^I_', minLength: 3 } }, required: ['min_number_prop', 'max_number_prop'] }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/java-generate-marshalling/index.spec.ts b/examples/java-generate-marshalling/index.spec.ts index 28b2ada9c0..57ca72edc4 100644 --- a/examples/java-generate-marshalling/index.spec.ts +++ b/examples/java-generate-marshalling/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate ts data model with marshal und unmarshal functions', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to generate ts data model with marshal und unmarshal fu }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/java-generate-marshalling/index.ts b/examples/java-generate-marshalling/index.ts index 06d21f8afd..d78903b979 100644 --- a/examples/java-generate-marshalling/index.ts +++ b/examples/java-generate-marshalling/index.ts @@ -1,14 +1,14 @@ import { JavaGenerator, JAVA_COMMON_PRESET } from '../../src'; -const generator = new JavaGenerator({ +const generator = new JavaGenerator({ presets: [ { - preset: JAVA_COMMON_PRESET, + preset: JAVA_COMMON_PRESET, options: { equal: false, hashCode: false, classToString: false, - marshalling: true, + marshalling: true } } ] @@ -26,10 +26,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/java-generate-tostring/index.spec.ts b/examples/java-generate-tostring/index.spec.ts index e0ac74b01e..2aa7fefd46 100644 --- a/examples/java-generate-tostring/index.spec.ts +++ b/examples/java-generate-tostring/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate a model to overwrite the toString method', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to generate a model to overwrite the toString method', }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/java-generate-tostring/index.ts b/examples/java-generate-tostring/index.ts index 07ac50142f..a334927179 100644 --- a/examples/java-generate-tostring/index.ts +++ b/examples/java-generate-tostring/index.ts @@ -8,10 +8,10 @@ const generator = new JavaGenerator({ equal: false, hashCode: false, classToString: true, - marshalling: false, - }, - }, - ], + marshalling: false + } + } + ] }); const jsonSchemaDraft7 = { @@ -26,10 +26,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/javascript-generate-example/index.spec.ts b/examples/javascript-generate-example/index.spec.ts index 3436cf41e5..62b9b934c7 100644 --- a/examples/javascript-generate-example/index.spec.ts +++ b/examples/javascript-generate-example/index.spec.ts @@ -1,4 +1,6 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); import { generate } from './index'; describe('Should be able to generate ts data model with example function', () => { @@ -7,8 +9,7 @@ describe('Should be able to generate ts data model with example function', () => }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/javascript-generate-example/index.ts b/examples/javascript-generate-example/index.ts index f9473cbac4..8e3a3b568a 100644 --- a/examples/javascript-generate-example/index.ts +++ b/examples/javascript-generate-example/index.ts @@ -29,4 +29,6 @@ export async function generate(): Promise { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/javascript-generate-marshalling/__snapshots__/index.spec.ts.snap b/examples/javascript-generate-marshalling/__snapshots__/index.spec.ts.snap index d55d967ddd..95fcb810c4 100644 --- a/examples/javascript-generate-marshalling/__snapshots__/index.spec.ts.snap +++ b/examples/javascript-generate-marshalling/__snapshots__/index.spec.ts.snap @@ -19,8 +19,6 @@ Array [ if(this.email !== undefined) { json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; } - - //Remove potential last comma return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; @@ -34,13 +32,15 @@ Array [ instance.email = obj[\\"email\\"]; } + + //Not part of core properties - - + + //Only go over remaining. properties for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"email\\"].includes(key);}))) { - - + } + return instance; } }", diff --git a/examples/javascript-generate-marshalling/index.spec.ts b/examples/javascript-generate-marshalling/index.spec.ts index f279d13040..57ca72edc4 100644 --- a/examples/javascript-generate-marshalling/index.spec.ts +++ b/examples/javascript-generate-marshalling/index.spec.ts @@ -1,4 +1,6 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); import { generate } from './index'; describe('Should be able to generate ts data model with marshal und unmarshal functions', () => { @@ -7,8 +9,7 @@ describe('Should be able to generate ts data model with marshal und unmarshal fu }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/javascript-generate-marshalling/index.ts b/examples/javascript-generate-marshalling/index.ts index dfdc5e2826..a6e5af0b23 100644 --- a/examples/javascript-generate-marshalling/index.ts +++ b/examples/javascript-generate-marshalling/index.ts @@ -30,4 +30,6 @@ export async function generate(): Promise { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/javascript-use-cjs/__snapshots__/index.spec.ts.snap b/examples/javascript-use-cjs/__snapshots__/index.spec.ts.snap index 10f1cd67da..7ab7a1ff6b 100644 --- a/examples/javascript-use-cjs/__snapshots__/index.spec.ts.snap +++ b/examples/javascript-use-cjs/__snapshots__/index.spec.ts.snap @@ -19,23 +19,3 @@ class Person { module.exports = Person;", ] `; - -exports[`Should be able to render models to ESM module system and should log expected output to console 2`] = ` -Array [ - "const Person = require('./Person'); - -class Root { - person; - - constructor(input) { - if (input.hasOwnProperty('person')) { - this.person = input.person; - } - } - - get person() { return this.person; } - set person(person) { this.person = person; } -} -module.exports = Root;", -] -`; diff --git a/examples/javascript-use-cjs/index.spec.ts b/examples/javascript-use-cjs/index.spec.ts index 1bdece5160..987374c1e9 100644 --- a/examples/javascript-use-cjs/index.spec.ts +++ b/examples/javascript-use-cjs/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render models to ESM module system', () => { afterAll(() => { @@ -7,9 +9,7 @@ describe('Should be able to render models to ESM module system', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 2 models, we double it - expect(spy.mock.calls.length).toEqual(4); + expect(spy.mock.calls.length).toEqual(2); expect(spy.mock.calls[1]).toMatchSnapshot(); - expect(spy.mock.calls[2]).toMatchSnapshot(); }); }); diff --git a/examples/javascript-use-cjs/index.ts b/examples/javascript-use-cjs/index.ts index 9aba8f2323..73c63a8484 100644 --- a/examples/javascript-use-cjs/index.ts +++ b/examples/javascript-use-cjs/index.ts @@ -1,6 +1,8 @@ import { JavaScriptGenerator } from '../../src'; -const generator = new JavaScriptGenerator(); +const generator = new JavaScriptGenerator({ + moduleSystem: 'CJS' +}); const jsonSchemaDraft7 = { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', @@ -19,15 +21,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { - const models = await generator.generateCompleteModels( - jsonSchemaDraft7, - { - moduleSystem: 'CJS' - } - ); +export async function generate(): Promise { + const models = await generator.generateCompleteModels(jsonSchemaDraft7, {}); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/javascript-use-esm/__snapshots__/index.spec.ts.snap b/examples/javascript-use-esm/__snapshots__/index.spec.ts.snap index 4eca8f9fdd..d542161615 100644 --- a/examples/javascript-use-esm/__snapshots__/index.spec.ts.snap +++ b/examples/javascript-use-esm/__snapshots__/index.spec.ts.snap @@ -20,24 +20,3 @@ export default Person; ", ] `; - -exports[`Should be able to render models to ESM module system and should log expected output to console 2`] = ` -Array [ - "import Person from './Person'; - -class Root { - person; - - constructor(input) { - if (input.hasOwnProperty('person')) { - this.person = input.person; - } - } - - get person() { return this.person; } - set person(person) { this.person = person; } -} -export default Root; -", -] -`; diff --git a/examples/javascript-use-esm/index.spec.ts b/examples/javascript-use-esm/index.spec.ts index 1bdece5160..987374c1e9 100644 --- a/examples/javascript-use-esm/index.spec.ts +++ b/examples/javascript-use-esm/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render models to ESM module system', () => { afterAll(() => { @@ -7,9 +9,7 @@ describe('Should be able to render models to ESM module system', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 2 models, we double it - expect(spy.mock.calls.length).toEqual(4); + expect(spy.mock.calls.length).toEqual(2); expect(spy.mock.calls[1]).toMatchSnapshot(); - expect(spy.mock.calls[2]).toMatchSnapshot(); }); }); diff --git a/examples/javascript-use-esm/index.ts b/examples/javascript-use-esm/index.ts index 24a7bc17f0..0102284bd3 100644 --- a/examples/javascript-use-esm/index.ts +++ b/examples/javascript-use-esm/index.ts @@ -1,6 +1,8 @@ import { JavaScriptGenerator } from '../../src'; -const generator = new JavaScriptGenerator(); +const generator = new JavaScriptGenerator({ + moduleSystem: 'ESM' +}); const jsonSchemaDraft7 = { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', @@ -19,15 +21,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { - const models = await generator.generateCompleteModels( - jsonSchemaDraft7, - { - moduleSystem: 'ESM' - } - ); +export async function generate(): Promise { + const models = await generator.generateCompleteModels(jsonSchemaDraft7, {}); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/json-schema-draft4-from-object/README.md b/examples/json-schema-draft4-from-object/README.md new file mode 100644 index 0000000000..4177250787 --- /dev/null +++ b/examples/json-schema-draft4-from-object/README.md @@ -0,0 +1,17 @@ +# JSON Schema draft 4 + +A basic example of how to use Modelina with JSON Schema draft 4 input. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/json-schema-draft4-from-object/__snapshots__/index.spec.ts.snap b/examples/json-schema-draft4-from-object/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..f5976be099 --- /dev/null +++ b/examples/json-schema-draft4-from-object/__snapshots__/index.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to process JSON Schema draft 4 object and should log expected output to console 1`] = ` +Array [ + "class Root { + private _email?: string; + + constructor(input: { + email?: string, + }) { + this._email = input.email; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } +}", +] +`; diff --git a/examples/json-schema-draft4-from-object/index.spec.ts b/examples/json-schema-draft4-from-object/index.spec.ts new file mode 100644 index 0000000000..f3d36d299b --- /dev/null +++ b/examples/json-schema-draft4-from-object/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to process JSON Schema draft 4 object', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/json-schema-draft4-from-object/index.ts b/examples/json-schema-draft4-from-object/index.ts new file mode 100644 index 0000000000..90c6caea3d --- /dev/null +++ b/examples/json-schema-draft4-from-object/index.ts @@ -0,0 +1,24 @@ +import { TypeScriptGenerator } from '../../src'; + +const generator = new TypeScriptGenerator(); +const jsonSchemaDraft4 = { + $schema: 'http://json-schema.org/draft-04/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft4); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/json-schema-draft4-from-object/package-lock.json b/examples/json-schema-draft4-from-object/package-lock.json new file mode 100644 index 0000000000..4c01a966be --- /dev/null +++ b/examples/json-schema-draft4-from-object/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "json-schema-draft4-from-object", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/json-schema-draft4-from-object/package.json b/examples/json-schema-draft4-from-object/package.json new file mode 100644 index 0000000000..920dd0d07e --- /dev/null +++ b/examples/json-schema-draft4-from-object/package.json @@ -0,0 +1,12 @@ +{ + "config": { + "example_name": "json-schema-draft4-from-object" + }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/json-schema-draft6-from-object/README.md b/examples/json-schema-draft6-from-object/README.md new file mode 100644 index 0000000000..33ad8c309b --- /dev/null +++ b/examples/json-schema-draft6-from-object/README.md @@ -0,0 +1,17 @@ +# JSON Schema draft 6 + +A basic example of how to use Modelina with JSON Schema draft 6 input. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/json-schema-draft6-from-object/__snapshots__/index.spec.ts.snap b/examples/json-schema-draft6-from-object/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..8854dd3e23 --- /dev/null +++ b/examples/json-schema-draft6-from-object/__snapshots__/index.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to process JSON Schema draft 6 object and should log expected output to console 1`] = ` +Array [ + "class Root { + private _email?: string; + + constructor(input: { + email?: string, + }) { + this._email = input.email; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } +}", +] +`; diff --git a/examples/csharp-generate-serializer/index.spec.ts b/examples/json-schema-draft6-from-object/index.spec.ts similarity index 70% rename from examples/csharp-generate-serializer/index.spec.ts rename to examples/json-schema-draft6-from-object/index.spec.ts index daa49a7090..899a7dfb02 100644 --- a/examples/csharp-generate-serializer/index.spec.ts +++ b/examples/json-schema-draft6-from-object/index.spec.ts @@ -1,11 +1,12 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; -describe('Should be able to generate a model with functions to serialize the data model into JSON ', () => { +describe('Should be able to process JSON Schema draft 6 object', () => { afterAll(() => { jest.restoreAllMocks(); }); - test('and should log expected output to console', async () => { await generate(); //Generate is called 2x, so even though we expect 1 model, we double it diff --git a/examples/json-schema-draft6-from-object/index.ts b/examples/json-schema-draft6-from-object/index.ts new file mode 100644 index 0000000000..335f940431 --- /dev/null +++ b/examples/json-schema-draft6-from-object/index.ts @@ -0,0 +1,22 @@ +import { TypeScriptGenerator } from '../../src'; + +const generator = new TypeScriptGenerator(); +const jsonSchemaDraft6 = { + $schema: 'http://json-schema.org/draft-06/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft6); + for (const model of models) { + console.log(model.result); + } +} +generate(); diff --git a/examples/json-schema-draft6-from-object/package-lock.json b/examples/json-schema-draft6-from-object/package-lock.json new file mode 100644 index 0000000000..54c5139e31 --- /dev/null +++ b/examples/json-schema-draft6-from-object/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "json-schema-draft6-from-object", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/json-schema-draft6-from-object/package.json b/examples/json-schema-draft6-from-object/package.json new file mode 100644 index 0000000000..c79a41c8b8 --- /dev/null +++ b/examples/json-schema-draft6-from-object/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "json-schema-draft6-from-object" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/json-schema-draft7-from-object/index.spec.ts b/examples/json-schema-draft7-from-object/index.spec.ts index 96af1291c6..7e52c3b2ee 100644 --- a/examples/json-schema-draft7-from-object/index.spec.ts +++ b/examples/json-schema-draft7-from-object/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to process JSON Schema draft 7 object', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to process JSON Schema draft 7 object', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/json-schema-draft7-from-object/index.ts b/examples/json-schema-draft7-from-object/index.ts index f18220a373..41e1500508 100644 --- a/examples/json-schema-draft7-from-object/index.ts +++ b/examples/json-schema-draft7-from-object/index.ts @@ -13,10 +13,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/kotlin-change-collection-type/README.md b/examples/kotlin-change-collection-type/README.md new file mode 100644 index 0000000000..72a30068a6 --- /dev/null +++ b/examples/kotlin-change-collection-type/README.md @@ -0,0 +1,17 @@ +# Kotlin change collection type + +A basic example to render collections as Array type in Kotlin. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/kotlin-change-collection-type/__snapshots__/index.spec.ts.snap b/examples/kotlin-change-collection-type/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..987cdd0fbb --- /dev/null +++ b/examples/kotlin-change-collection-type/__snapshots__/index.spec.ts.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render collections in Kotlin as Array and should log expected output to console 1`] = ` +Array [ + "data class Root( + val email: Array, +)", +] +`; diff --git a/examples/kotlin-change-collection-type/index.spec.ts b/examples/kotlin-change-collection-type/index.spec.ts new file mode 100644 index 0000000000..ae82c598e1 --- /dev/null +++ b/examples/kotlin-change-collection-type/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render collections in Kotlin as Array', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/kotlin-change-collection-type/index.ts b/examples/kotlin-change-collection-type/index.ts new file mode 100644 index 0000000000..dbe073e11a --- /dev/null +++ b/examples/kotlin-change-collection-type/index.ts @@ -0,0 +1,30 @@ +import { KotlinGenerator } from '../../src'; + +const generator = new KotlinGenerator({ + collectionType: 'Array' +}); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'array', + additionalItems: false, + items: { + type: 'string', + format: 'email' + } + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/kotlin-change-collection-type/package-lock.json b/examples/kotlin-change-collection-type/package-lock.json new file mode 100644 index 0000000000..5d453bc6a9 --- /dev/null +++ b/examples/kotlin-change-collection-type/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "kotlin-change-collection-type", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/kotlin-change-collection-type/package.json b/examples/kotlin-change-collection-type/package.json new file mode 100644 index 0000000000..7fab9e8213 --- /dev/null +++ b/examples/kotlin-change-collection-type/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "kotlin-change-collection-type" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/kotlin-generate-javax-constraint-annotation/README.md b/examples/kotlin-generate-javax-constraint-annotation/README.md new file mode 100644 index 0000000000..ce46cf5283 --- /dev/null +++ b/examples/kotlin-generate-javax-constraint-annotation/README.md @@ -0,0 +1,17 @@ +# Javax validation constraints annotations + +A basic example that shows how Kotlin data models having `javax.validation.constraints` annotations can be generated. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/kotlin-generate-javax-constraint-annotation/__snapshots__/index.spec.ts.snap b/examples/kotlin-generate-javax-constraint-annotation/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..1337db614d --- /dev/null +++ b/examples/kotlin-generate-javax-constraint-annotation/__snapshots__/index.spec.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to generate models with javax.validation.constraints annotations and should log expected output to console 1`] = ` +Array [ + "data class JavaxAnnotation( + @get:NotNull + @get:Min(0) + val minNumberProp: Double, + @get:NotNull + @get:Max(99) + val maxNumberProp: Double, + @get:Min(101) + val minNumberPropExclusive: Double, + @get:Size(min=2, max=3) + val arrayProp: List, + @get:Pattern(regexp=\\"^I_\\") + @get:Size(min=3) + val stringProp: String, + val additionalProperties: Map, +)", +] +`; diff --git a/examples/kotlin-generate-javax-constraint-annotation/index.spec.ts b/examples/kotlin-generate-javax-constraint-annotation/index.spec.ts new file mode 100644 index 0000000000..745c61d7fd --- /dev/null +++ b/examples/kotlin-generate-javax-constraint-annotation/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to generate models with javax.validation.constraints annotations', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/kotlin-generate-javax-constraint-annotation/index.ts b/examples/kotlin-generate-javax-constraint-annotation/index.ts new file mode 100644 index 0000000000..77ae15ff51 --- /dev/null +++ b/examples/kotlin-generate-javax-constraint-annotation/index.ts @@ -0,0 +1,28 @@ +import { KotlinGenerator, KOTLIN_CONSTRAINTS_PRESET } from '../../src'; + +const generator = new KotlinGenerator({ + presets: [KOTLIN_CONSTRAINTS_PRESET] +}); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'JavaxAnnotation', + type: 'object', + properties: { + min_number_prop: { type: 'number', minimum: 0 }, + max_number_prop: { type: 'number', exclusiveMaximum: 100 }, + min_number_prop_exclusive: { type: 'number', exclusiveMinimum: 100 }, + array_prop: { type: 'array', minItems: 2, maxItems: 3 }, + string_prop: { type: 'string', pattern: '^I_', minLength: 3 } + }, + required: ['min_number_prop', 'max_number_prop'] +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/kotlin-generate-javax-constraint-annotation/package-lock.json b/examples/kotlin-generate-javax-constraint-annotation/package-lock.json new file mode 100644 index 0000000000..3530ecf42b --- /dev/null +++ b/examples/kotlin-generate-javax-constraint-annotation/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "kotlin-generate-javax-constraint-annotation", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/kotlin-generate-javax-constraint-annotation/package.json b/examples/kotlin-generate-javax-constraint-annotation/package.json new file mode 100644 index 0000000000..8fa9568c1b --- /dev/null +++ b/examples/kotlin-generate-javax-constraint-annotation/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "kotlin-generate-javax-constraint-annotation" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/kotlin-generate-kdoc/README.md b/examples/kotlin-generate-kdoc/README.md new file mode 100644 index 0000000000..56e3fcce84 --- /dev/null +++ b/examples/kotlin-generate-kdoc/README.md @@ -0,0 +1,17 @@ +# Kotlin KDoc + +A basic example of how to generate Kotlin models by including KDoc in description and examples. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/kotlin-generate-kdoc/__snapshots__/index.spec.ts.snap b/examples/kotlin-generate-kdoc/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..1bb0756987 --- /dev/null +++ b/examples/kotlin-generate-kdoc/__snapshots__/index.spec.ts.snap @@ -0,0 +1,23 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to generate KDoc and should log expected output to console 1`] = ` +Array [ + "/** + * Description for class + * + * @property prop Description for prop + * @property enum Description for enum + * @property nodesc + * @property additionalProperties + * + * Examples: + * {\\"prop\\":\\"value\\"}, {\\"prop\\":\\"test\\"} + */ +data class KDoc( + val prop: String, + val enum: Enum, + val nodesc: String, + val additionalProperties: Map, +)", +] +`; diff --git a/examples/kotlin-generate-kdoc/index.spec.ts b/examples/kotlin-generate-kdoc/index.spec.ts new file mode 100644 index 0000000000..70fe64fe9c --- /dev/null +++ b/examples/kotlin-generate-kdoc/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to generate KDoc', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(2); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/kotlin-generate-kdoc/index.ts b/examples/kotlin-generate-kdoc/index.ts new file mode 100644 index 0000000000..112ad9fc60 --- /dev/null +++ b/examples/kotlin-generate-kdoc/index.ts @@ -0,0 +1,35 @@ +import { KotlinGenerator, KOTLIN_DESCRIPTION_PRESET } from '../../src'; + +const generator = new KotlinGenerator({ + presets: [KOTLIN_DESCRIPTION_PRESET] +}); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'KDoc', + type: 'object', + description: 'Description for class', + examples: [{ prop: 'value' }, { prop: 'test' }], + properties: { + prop: { + type: 'string', + description: 'Description for prop', + examples: ['exampleValue'] + }, + enum: { + type: 'string', + description: 'Description for enum', + enum: ['A', 'B', 'C'] + }, + nodesc: { type: 'string' } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/kotlin-generate-kdoc/package-lock.json b/examples/kotlin-generate-kdoc/package-lock.json new file mode 100644 index 0000000000..c17a948236 --- /dev/null +++ b/examples/kotlin-generate-kdoc/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "kotlin-generate-kdoc", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/kotlin-generate-kdoc/package.json b/examples/kotlin-generate-kdoc/package.json new file mode 100644 index 0000000000..b6dc26945d --- /dev/null +++ b/examples/kotlin-generate-kdoc/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "kotlin-generate-kdoc" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/meta-model/README.md b/examples/meta-model/README.md new file mode 100644 index 0000000000..2515c55801 --- /dev/null +++ b/examples/meta-model/README.md @@ -0,0 +1,17 @@ +# Meta model + +Using the internal meta model representation, you can create your own data models from scratch, and still utilize the generators full sweep of features. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/meta-model/__snapshots__/index.spec.ts.snap b/examples/meta-model/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..f7b7fdb7a4 --- /dev/null +++ b/examples/meta-model/__snapshots__/index.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to use generator with meta model and should log expected output to console 1`] = ` +Array [ + "class SomeName { + private _testPropertyName?: string; + + constructor(input: { + testPropertyName?: string, + }) { + this._testPropertyName = input.testPropertyName; + } + + get testPropertyName(): string | undefined { return this._testPropertyName; } + set testPropertyName(testPropertyName: string | undefined) { this._testPropertyName = testPropertyName; } +}", +] +`; diff --git a/examples/meta-model/index.spec.ts b/examples/meta-model/index.spec.ts new file mode 100644 index 0000000000..98172f53dc --- /dev/null +++ b/examples/meta-model/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to use generator with meta model', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/meta-model/index.ts b/examples/meta-model/index.ts new file mode 100644 index 0000000000..b9a6a3873d --- /dev/null +++ b/examples/meta-model/index.ts @@ -0,0 +1,30 @@ +import { + TypeScriptGenerator, + ObjectModel, + StringModel, + ObjectPropertyModel, + InputMetaModel +} from '../../src'; + +const generator = new TypeScriptGenerator(); +const customModel = new ObjectModel('SomeName', undefined, {}); +const stringModel = new StringModel('test property name', undefined); +const propertyModel = new ObjectPropertyModel( + stringModel.name, + false, + stringModel +); +customModel.properties[propertyModel.propertyName] = propertyModel; + +const inputModel = new InputMetaModel(); +inputModel.models[customModel.name] = customModel; + +export async function generate(): Promise { + const models = await generator.generate(inputModel); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/meta-model/package-lock.json b/examples/meta-model/package-lock.json new file mode 100644 index 0000000000..1c745a2603 --- /dev/null +++ b/examples/meta-model/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "meta-model", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/meta-model/package.json b/examples/meta-model/package.json new file mode 100644 index 0000000000..59609b479b --- /dev/null +++ b/examples/meta-model/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "meta-model" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/openapi-from-object/index.spec.ts b/examples/openapi-from-object/index.spec.ts index 771d530e6b..ae761cff7f 100644 --- a/examples/openapi-from-object/index.spec.ts +++ b/examples/openapi-from-object/index.spec.ts @@ -1,4 +1,6 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); import { generate } from './index'; describe('Should be able to process a pure OpenAPI object', () => { afterAll(() => { @@ -6,9 +8,8 @@ describe('Should be able to process a pure OpenAPI object', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 2 models, we double it - expect(spy.mock.calls.length).toEqual(4); - expect(spy.mock.calls[2]).toMatchSnapshot(); - expect(spy.mock.calls[3]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(2); + expect(spy.mock.calls[0]).toMatchSnapshot(); + expect(spy.mock.calls[1]).toMatchSnapshot(); }); }); diff --git a/examples/openapi-from-object/index.ts b/examples/openapi-from-object/index.ts index 92a3c9db7d..ba89a24da0 100644 --- a/examples/openapi-from-object/index.ts +++ b/examples/openapi-from-object/index.ts @@ -59,4 +59,6 @@ export async function generate(): Promise { } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/overwrite-default-constraint/README.md b/examples/overwrite-default-constraint/README.md new file mode 100644 index 0000000000..09708fe97b --- /dev/null +++ b/examples/overwrite-default-constraint/README.md @@ -0,0 +1,21 @@ +# Overwrite default constraint + +This example shows how to overwrite the whole constraint logic instead of a small part of it. In this case we create a very simple model name constraint that return the name as pascal case. + +Note the difference here to [overwriting naming formatting](../overwrite-naming-formatting/) is that this removes all of the default constraints such as `no special chars`, etc. + +If you do not handle these special cases you will encounter Modelina generate syntactically incorrect models when given certain inputs. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/overwrite-default-constraint/__snapshots__/index.spec.ts.snap b/examples/overwrite-default-constraint/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..efdd393efc --- /dev/null +++ b/examples/overwrite-default-constraint/__snapshots__/index.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render models with custom constrain logic and should log expected output to console 1`] = ` +Array [ + "class MyOwnCustomModelName { + private _email?: string; + + constructor(input: { + email?: string, + }) { + this._email = input.email; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } +}", +] +`; diff --git a/examples/overwrite-default-constraint/index.spec.ts b/examples/overwrite-default-constraint/index.spec.ts new file mode 100644 index 0000000000..c77360ce9d --- /dev/null +++ b/examples/overwrite-default-constraint/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render models with custom constrain logic', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/overwrite-default-constraint/index.ts b/examples/overwrite-default-constraint/index.ts new file mode 100644 index 0000000000..71c9258a4b --- /dev/null +++ b/examples/overwrite-default-constraint/index.ts @@ -0,0 +1,32 @@ +import { TypeScriptGenerator } from '../../src'; +import { pascalCase } from 'change-case'; + +const generator = new TypeScriptGenerator({ + constraints: { + modelName: ({ modelName }) => { + return 'MyOwnCustomModelName'; + } + } +}); + +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/overwrite-default-constraint/package-lock.json b/examples/overwrite-default-constraint/package-lock.json new file mode 100644 index 0000000000..fbd7f6a516 --- /dev/null +++ b/examples/overwrite-default-constraint/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "overwrite-default-constraint", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/overwrite-default-constraint/package.json b/examples/overwrite-default-constraint/package.json new file mode 100644 index 0000000000..f79e243f28 --- /dev/null +++ b/examples/overwrite-default-constraint/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "overwrite-default-constraint" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/overwrite-naming-formatting/README.md b/examples/overwrite-naming-formatting/README.md new file mode 100644 index 0000000000..925a4f6279 --- /dev/null +++ b/examples/overwrite-naming-formatting/README.md @@ -0,0 +1,17 @@ +# Overwrite naming formatting + +This example shows how to overwrite the naming formatter, and in this case, use constant case formatting instead of the default one. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/overwrite-naming-formatting/__snapshots__/index.spec.ts.snap b/examples/overwrite-naming-formatting/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..9e5830b9d2 --- /dev/null +++ b/examples/overwrite-naming-formatting/__snapshots__/index.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render models with custom naming formatter and should log expected output to console 1`] = ` +Array [ + "class ROOT { + private _email?: string; + + constructor(input: { + email?: string, + }) { + this._email = input.email; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } +}", +] +`; diff --git a/examples/overwrite-naming-formatting/index.spec.ts b/examples/overwrite-naming-formatting/index.spec.ts new file mode 100644 index 0000000000..be6d7967e6 --- /dev/null +++ b/examples/overwrite-naming-formatting/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render models with custom naming formatter', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/overwrite-naming-formatting/index.ts b/examples/overwrite-naming-formatting/index.ts new file mode 100644 index 0000000000..92238982a7 --- /dev/null +++ b/examples/overwrite-naming-formatting/index.ts @@ -0,0 +1,37 @@ +import { + TypeScriptGenerator, + typeScriptDefaultModelNameConstraints +} from '../../src'; +import { constantCase } from 'change-case'; + +const generator = new TypeScriptGenerator({ + constraints: { + modelName: typeScriptDefaultModelNameConstraints({ + NAMING_FORMATTER: (name) => { + return constantCase(name); + } + }) + } +}); + +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/overwrite-naming-formatting/package-lock.json b/examples/overwrite-naming-formatting/package-lock.json new file mode 100644 index 0000000000..55c1cd64b7 --- /dev/null +++ b/examples/overwrite-naming-formatting/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "overwrite-naming-formatting", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/overwrite-naming-formatting/package.json b/examples/overwrite-naming-formatting/package.json new file mode 100644 index 0000000000..8d6de2c986 --- /dev/null +++ b/examples/overwrite-naming-formatting/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "overwrite-naming-formatting" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/rust-generate-crate/.gitignore b/examples/rust-generate-crate/.gitignore new file mode 100644 index 0000000000..6caf68aff4 --- /dev/null +++ b/examples/rust-generate-crate/.gitignore @@ -0,0 +1 @@ +output \ No newline at end of file diff --git a/examples/rust-generate-crate/README.md b/examples/rust-generate-crate/README.md new file mode 100644 index 0000000000..1d598fb27e --- /dev/null +++ b/examples/rust-generate-crate/README.md @@ -0,0 +1,23 @@ +# Rust Data Models + +A basic example of how to use Modelina and output a Rust crate. + +## Requirements + +- [Rust](https://rustup.rs/) + +Rust is required to compile this example. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/rust-generate-crate/__snapshots__/index.spec.ts.snap b/examples/rust-generate-crate/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..2ac9f282df --- /dev/null +++ b/examples/rust-generate-crate/__snapshots__/index.spec.ts.snap @@ -0,0 +1,82 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render Rust Models and should log expected output to console 1`] = ` +Array [ + "// Address represents a Address model. +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct Address { + #[serde(rename=\\"street_name\\")] + pub street_name: String, + #[serde(rename=\\"city\\")] + pub city: String, + #[serde(rename=\\"state\\")] + pub state: String, + #[serde(rename=\\"house_number\\")] + pub house_number: f64, + #[serde(rename=\\"marriage\\", skip_serializing_if = \\"Option::is_none\\")] + pub marriage: Option, + #[serde(rename=\\"members\\", skip_serializing_if = \\"Option::is_none\\")] + pub members: Option>, + #[serde(rename=\\"tuple_type\\", skip_serializing_if = \\"Option::is_none\\")] + pub tuple_type: Option>, + #[serde(rename=\\"array_type\\")] + pub array_type: Vec, + #[serde(rename=\\"enum_type\\", skip_serializing_if = \\"Option::is_none\\")] + pub enum_type: Option>, + #[serde(rename=\\"additionalProperties\\", skip_serializing_if = \\"Option::is_none\\")] + pub additional_properties: Option>, +} + +impl Address { + pub fn new(street_name: String, city: String, state: String, house_number: f64, marriage: Option, members: Option, tuple_type: Option, array_type: Vec, enum_type: Option, additional_properties: Option>) -> Address { + Address { + street_name, + city, + state, + house_number, + marriage, + members: members.map(Box::new), + tuple_type: tuple_type.map(Box::new), + array_type, + enum_type: enum_type.map(Box::new), + additional_properties, + } + } +} +", +] +`; + +exports[`Should be able to render Rust Models and should log expected output to console 2`] = ` +Array [ + "// Members represents a union of types: String, f64, bool +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +#[serde(untagged)] +pub enum Members { + #[serde(rename=\\"MembersOneOf0\\")] + MembersOneOf0(String), + #[serde(rename=\\"MembersOneOf1\\")] + MembersOneOf1(f64), + #[serde(rename=\\"MembersOneOf2\\")] + MembersOneOf2(bool), +} + +", +] +`; + +exports[`Should be able to render Rust Models and should log expected output to console 3`] = ` +Array [ + "// TupleType represents a TupleType model. +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +pub struct TupleType(String, f64); + +impl TupleType { + pub fn new(value_0: String, value_1: f64) -> TupleType { + TupleType(value_0, value_1) + } +} + +", +] +`; diff --git a/examples/rust-generate-crate/index.spec.ts b/examples/rust-generate-crate/index.spec.ts new file mode 100644 index 0000000000..4efd0fd3f0 --- /dev/null +++ b/examples/rust-generate-crate/index.spec.ts @@ -0,0 +1,16 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; +describe('Should be able to render Rust Models', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(6); + expect(spy.mock.calls[0]).toMatchSnapshot(); + expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls[2]).toMatchSnapshot(); + }); +}); diff --git a/examples/rust-generate-crate/index.ts b/examples/rust-generate-crate/index.ts new file mode 100644 index 0000000000..818da3ada6 --- /dev/null +++ b/examples/rust-generate-crate/index.ts @@ -0,0 +1,87 @@ +import { + RustFileGenerator, + RustRenderCompleteModelOptions, + RUST_COMMON_PRESET, + defaultRustRenderCompleteModelOptions, + RustPackageFeatures +} from '../../src/generators'; +import * as path from 'path'; + +const doc = { + $id: '_address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + tuple_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }], + additionalItems: false + }, + array_type: { + type: 'array', + items: { type: 'string' }, + additionalItems: false + }, + enum_type: { + enum: ['Texas', 'Alabama', 'California'], + default: 'California' + } + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + additionalProperties: { + type: 'string' + } +}; + +export async function generate(): Promise { + // initialize the generator from a preset + const generator = new RustFileGenerator({ + presets: [ + { + preset: RUST_COMMON_PRESET, + options: { + implementNew: true, + implementDefault: true + } + } + ] + }); + // Generated files will be written to output/ directory + const outDir = path.join(__dirname, 'output'); + + // Run the file generator with options + const models = await generator.generateToPackage(doc, outDir, { + ...defaultRustRenderCompleteModelOptions, + supportFiles: true, // generate Cargo.toml and lib.rs + package: { + packageName: 'asyncapi-rs-example', + packageVersion: '1.0.0', + // set authors, homepage, repository, and license + authors: ['AsyncAPI Rust Champions'], + homepage: 'https://www.asyncapi.com/tools/modelina', + repository: 'https://github.com/asyncapi/modelina', + license: 'Apache-2.0', + description: 'Rust models generated by AsyncAPI Modelina', + // support 2018 editions and up + edition: '2018', + // enable serde_json + packageFeatures: [RustPackageFeatures.json] as RustPackageFeatures[] + } + } as RustRenderCompleteModelOptions); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/rust-generate-crate/package-lock.json b/examples/rust-generate-crate/package-lock.json new file mode 100644 index 0000000000..3b4f76c1af --- /dev/null +++ b/examples/rust-generate-crate/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "rust-generate-example", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/rust-generate-crate/package.json b/examples/rust-generate-crate/package.json new file mode 100644 index 0000000000..00ebf7040e --- /dev/null +++ b/examples/rust-generate-crate/package.json @@ -0,0 +1,13 @@ +{ + "config": { + "example_name": "rust-generate-crate" + }, + "scripts": { + "install": "cd ../.. && npm i", + "clean": "rm -rf output", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts generate && cargo build --manifest-path=output/Cargo.toml ", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} \ No newline at end of file diff --git a/examples/swagger2.0-from-object/index.spec.ts b/examples/swagger2.0-from-object/index.spec.ts index 285ddfb5b1..6853b3ae1e 100644 --- a/examples/swagger2.0-from-object/index.spec.ts +++ b/examples/swagger2.0-from-object/index.spec.ts @@ -1,4 +1,6 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); import { generate } from './index'; describe('Should be able to process a pure Swagger 2.0 object', () => { afterAll(() => { @@ -6,9 +8,8 @@ describe('Should be able to process a pure Swagger 2.0 object', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 2 models, we double it - expect(spy.mock.calls.length).toEqual(4); - expect(spy.mock.calls[2]).toMatchSnapshot(); - expect(spy.mock.calls[3]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(2); + expect(spy.mock.calls[0]).toMatchSnapshot(); + expect(spy.mock.calls[1]).toMatchSnapshot(); }); }); diff --git a/examples/swagger2.0-from-object/index.ts b/examples/swagger2.0-from-object/index.ts index 41cb6a351c..0b06cdc9a7 100644 --- a/examples/swagger2.0-from-object/index.ts +++ b/examples/swagger2.0-from-object/index.ts @@ -12,7 +12,7 @@ const swaggerDocument = { '/test': { post: { parameters: [ - { + { in: 'body', schema: { $schema: 'http://json-schema.org/draft-07/schema#', @@ -55,4 +55,6 @@ export async function generate(): Promise { } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/typescript-enum-type/__snapshots__/index.spec.ts.snap b/examples/typescript-enum-type/__snapshots__/index.spec.ts.snap index e624a61929..e289f7ad9c 100644 --- a/examples/typescript-enum-type/__snapshots__/index.spec.ts.snap +++ b/examples/typescript-enum-type/__snapshots__/index.spec.ts.snap @@ -3,32 +3,32 @@ exports[`Should be able to render correct enums based on options and should log expected output to console 1`] = ` "Generator output with Union: class Root { - private _event?: Event; + private _eventType?: EventType; constructor(input: { - event?: Event, + eventType?: EventType, }) { - this._event = input.event; + this._eventType = input.eventType; } - get event(): Event | undefined { return this._event; } - set event(event: Event | undefined) { this._event = event; } + get eventType(): EventType | undefined { return this._eventType; } + set eventType(eventType: EventType | undefined) { this._eventType = eventType; } } -type Event = \\"ping\\" | \\"pong\\"; +type EventType = \\"ping\\" | \\"pong\\"; Generator output with Enum: class Root { - private _event?: Event; + private _eventType?: EventType; constructor(input: { - event?: Event, + eventType?: EventType, }) { - this._event = input.event; + this._eventType = input.eventType; } - get event(): Event | undefined { return this._event; } - set event(event: Event | undefined) { this._event = event; } + get eventType(): EventType | undefined { return this._eventType; } + set eventType(eventType: EventType | undefined) { this._eventType = eventType; } } -enum Event { +enum EventType { PING = \\"ping\\", PONG = \\"pong\\", }" diff --git a/examples/typescript-enum-type/index.spec.ts b/examples/typescript-enum-type/index.spec.ts index 6be8ad4584..4c76e999c0 100644 --- a/examples/typescript-enum-type/index.spec.ts +++ b/examples/typescript-enum-type/index.spec.ts @@ -1,24 +1,18 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); import { generate } from './index'; function timeout(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } describe('Should be able to render correct enums based on options', () => { afterAll(() => { jest.restoreAllMocks(); }); - test('and should log expected output to console', async function testIt(): Promise { - // Wait for first call to the `generate` function is over, and we can reliably know the output - if (spy.mock.calls.length !== 6) { - await timeout(50); - return testIt(); - } - + test('and should log expected output to console', async (): Promise => { await generate(); - //Generate is called 2 times, so even though we expect 6 console logs we double it - expect(spy.mock.calls.length).toEqual(12); - const generatedContent = spy.mock.calls.slice(6).join('\n'); + expect(spy.mock.calls.length).toEqual(6); + const generatedContent = spy.mock.calls.join('\n'); expect(generatedContent).toMatchSnapshot(); }); }); - diff --git a/examples/typescript-enum-type/index.ts b/examples/typescript-enum-type/index.ts index 5665212782..c962be46fe 100644 --- a/examples/typescript-enum-type/index.ts +++ b/examples/typescript-enum-type/index.ts @@ -7,7 +7,7 @@ const jsonSchemaDraft7 = { type: 'object', additionalProperties: false, properties: { - event: { + eventType: { type: 'string', enum: ['ping', 'pong'] } @@ -27,4 +27,6 @@ export async function generate(): Promise { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/typescript-generate-comments/__snapshots__/index.spec.ts.snap b/examples/typescript-generate-comments/__snapshots__/index.spec.ts.snap index bfaf196e55..92d0b34a78 100644 --- a/examples/typescript-generate-comments/__snapshots__/index.spec.ts.snap +++ b/examples/typescript-generate-comments/__snapshots__/index.spec.ts.snap @@ -13,32 +13,11 @@ interface Test { * @example Example */ numberProp?: number; - objectProp?: NestedTest; - additionalProperties?: Map | boolean | null>; -}", - ], - Array [ - "/** - * @example Example 1, Example 2 - */ -interface NestedTest { - stringProp?: string; - additionalProperties?: Map | boolean | null>; -}", - ], - Array [ - "/** - * Main Description - */ -interface Test { - stringProp: string; /** - * Description - * @example Example + * @example Example 1, Example 2 */ - numberProp?: number; objectProp?: NestedTest; - additionalProperties?: Map | boolean | null>; + additionalProperties?: Map; }", ], Array [ @@ -47,7 +26,7 @@ interface Test { */ interface NestedTest { stringProp?: string; - additionalProperties?: Map | boolean | null>; + additionalProperties?: Map; }", ], ] diff --git a/examples/typescript-generate-comments/index.spec.ts b/examples/typescript-generate-comments/index.spec.ts index 5e8e211e47..c1b86b78de 100644 --- a/examples/typescript-generate-comments/index.spec.ts +++ b/examples/typescript-generate-comments/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate ts data model with comments', () => { afterAll(() => { @@ -7,7 +9,7 @@ describe('Should be able to generate ts data model with comments', () => { }); test('and should log expected output to console', async () => { await generate(); - expect(spy.mock.calls.length).toEqual(4); + expect(spy.mock.calls.length).toEqual(2); expect(spy.mock.calls).toMatchSnapshot(); }); }); diff --git a/examples/typescript-generate-comments/index.ts b/examples/typescript-generate-comments/index.ts index cf755d0e38..dfdd782bb0 100644 --- a/examples/typescript-generate-comments/index.ts +++ b/examples/typescript-generate-comments/index.ts @@ -1,10 +1,8 @@ -import {TS_DESCRIPTION_PRESET, TypeScriptGenerator} from '../../src'; +import { TS_DESCRIPTION_PRESET, TypeScriptGenerator } from '../../src'; const generator = new TypeScriptGenerator({ modelType: 'interface', - presets: [ - TS_DESCRIPTION_PRESET, - ] + presets: [TS_DESCRIPTION_PRESET] }); const jsonSchemaDraft7 = { $schema: 'http://json-schema.org/draft-07/schema#', @@ -18,15 +16,15 @@ const jsonSchemaDraft7 = { numberProp: { type: 'number', description: 'Description', - examples: 'Example', + examples: 'Example' }, objectProp: { type: 'object', $id: 'NestedTest', properties: { stringProp: { type: 'string' } }, - examples: ['Example 1', 'Example 2'], - }, - }, + examples: ['Example 1', 'Example 2'] + } + } }; export async function generate(): Promise { @@ -35,4 +33,6 @@ export async function generate(): Promise { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/typescript-generate-example/index.spec.ts b/examples/typescript-generate-example/index.spec.ts index 53f18af361..62b9b934c7 100644 --- a/examples/typescript-generate-example/index.spec.ts +++ b/examples/typescript-generate-example/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate ts data model with example function', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to generate ts data model with example function', () => }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/typescript-generate-example/index.ts b/examples/typescript-generate-example/index.ts index e2aaf6b18d..90c67cb9cd 100644 --- a/examples/typescript-generate-example/index.ts +++ b/examples/typescript-generate-example/index.ts @@ -29,4 +29,6 @@ export async function generate(): Promise { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/typescript-generate-jsonbinpack/README.md b/examples/typescript-generate-jsonbinpack/README.md new file mode 100644 index 0000000000..9ef494869d --- /dev/null +++ b/examples/typescript-generate-jsonbinpack/README.md @@ -0,0 +1,17 @@ +# TypeScript data models with jsonbinpack support + +A basic example of how to generate models with [jsonbinpack](https://github.com/sourcemeta/jsonbinpack) support. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/typescript-generate-jsonbinpack/__snapshots__/index.spec.ts.snap b/examples/typescript-generate-jsonbinpack/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..40553a144b --- /dev/null +++ b/examples/typescript-generate-jsonbinpack/__snapshots__/index.spec.ts.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to generate ts data model with marshal und unmarshal functions and should log expected output to console 1`] = ` +Array [ + "class Test { + private _email?: string; + + constructor(input: { + email?: string, + }) { + this._email = input.email; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } + + public marshal() : string { + let json = '{' + if(this.email !== undefined) { + json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; + } + + + //Remove potential last comma + return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; + } + + public static unmarshal(json: string | object): Test { + const obj = typeof json === \\"object\\" ? json : JSON.parse(json); + const instance = new Test({} as any); + + if (obj[\\"email\\"] !== undefined) { + instance.email = obj[\\"email\\"]; + } + + + + return instance; + } + + public async jsonbinSerialize(): Promise{ + const jsonData = JSON.parse(this.marshal()); + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"$id\\":\\"Test\\",\\"type\\":\\"object\\",\\"additionalProperties\\":false,\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); + return jsonbinpack.serialize(jsonbinpackEncodedSchema, jsonData); + } + + public static async jsonbinDeserialize(buffer: Buffer): Promise { + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"$id\\":\\"Test\\",\\"type\\":\\"object\\",\\"additionalProperties\\":false,\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); + const json = jsonbinpack.deserialize(jsonbinpackEncodedSchema, buffer); + return Test.unmarshal(json); + } +}", +] +`; diff --git a/examples/typescript-generate-jsonbinpack/index.spec.ts b/examples/typescript-generate-jsonbinpack/index.spec.ts new file mode 100644 index 0000000000..57ca72edc4 --- /dev/null +++ b/examples/typescript-generate-jsonbinpack/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to generate ts data model with marshal und unmarshal functions', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/typescript-generate-jsonbinpack/index.ts b/examples/typescript-generate-jsonbinpack/index.ts new file mode 100644 index 0000000000..113afc6499 --- /dev/null +++ b/examples/typescript-generate-jsonbinpack/index.ts @@ -0,0 +1,39 @@ +import { + TS_JSONBINPACK_PRESET, + TypeScriptGenerator, + TS_COMMON_PRESET +} from '../../src'; + +const generator = new TypeScriptGenerator({ + presets: [ + { + preset: TS_COMMON_PRESET, + options: { + marshalling: true + } + }, + TS_JSONBINPACK_PRESET + ] +}); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'Test', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + format: 'email' + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/typescript-generate-jsonbinpack/package-lock.json b/examples/typescript-generate-jsonbinpack/package-lock.json new file mode 100644 index 0000000000..48e341a095 --- /dev/null +++ b/examples/typescript-generate-jsonbinpack/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +} diff --git a/examples/typescript-generate-jsonbinpack/package.json b/examples/typescript-generate-jsonbinpack/package.json new file mode 100644 index 0000000000..e3f355d50d --- /dev/null +++ b/examples/typescript-generate-jsonbinpack/package.json @@ -0,0 +1,12 @@ +{ + "config": { + "example_name": "typescript-generate-jsonbinpack" + }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/typescript-generate-marshalling/__snapshots__/index.spec.ts.snap b/examples/typescript-generate-marshalling/__snapshots__/index.spec.ts.snap index af7b5b8984..500812add9 100644 --- a/examples/typescript-generate-marshalling/__snapshots__/index.spec.ts.snap +++ b/examples/typescript-generate-marshalling/__snapshots__/index.spec.ts.snap @@ -19,8 +19,7 @@ Array [ if(this.email !== undefined) { json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; } - - + //Remove potential last comma return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; @@ -34,13 +33,8 @@ Array [ instance.email = obj[\\"email\\"]; } - //Not part of core properties - - - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"email\\"].includes(key);}))) { - - - } + + return instance; } }", diff --git a/examples/typescript-generate-marshalling/index.spec.ts b/examples/typescript-generate-marshalling/index.spec.ts index 28b2ada9c0..57ca72edc4 100644 --- a/examples/typescript-generate-marshalling/index.spec.ts +++ b/examples/typescript-generate-marshalling/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to generate ts data model with marshal und unmarshal functions', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to generate ts data model with marshal und unmarshal fu }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/typescript-generate-marshalling/index.ts b/examples/typescript-generate-marshalling/index.ts index 0bd2e52675..7d17d08024 100644 --- a/examples/typescript-generate-marshalling/index.ts +++ b/examples/typescript-generate-marshalling/index.ts @@ -30,4 +30,6 @@ export async function generate(): Promise { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/typescript-interface/index.spec.ts b/examples/typescript-interface/index.spec.ts index 75da2e1a1e..daf392de36 100644 --- a/examples/typescript-interface/index.spec.ts +++ b/examples/typescript-interface/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render typescript interface', () => { afterAll(() => { @@ -7,8 +9,7 @@ describe('Should be able to render typescript interface', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 1 model, we double it - expect(spy.mock.calls.length).toEqual(2); - expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/examples/typescript-interface/index.ts b/examples/typescript-interface/index.ts index 9497f17523..3e7278bdf6 100644 --- a/examples/typescript-interface/index.ts +++ b/examples/typescript-interface/index.ts @@ -13,10 +13,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { +export async function generate(): Promise { const models = await generator.generate(jsonSchemaDraft7); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/typescript-use-cjs/__snapshots__/index.spec.ts.snap b/examples/typescript-use-cjs/__snapshots__/index.spec.ts.snap index 6945ed97ff..9fe4e31ed3 100644 --- a/examples/typescript-use-cjs/__snapshots__/index.spec.ts.snap +++ b/examples/typescript-use-cjs/__snapshots__/index.spec.ts.snap @@ -3,7 +3,6 @@ exports[`Should be able to render models to ESM module system and should log expected output to console 1`] = ` Array [ " - class Person { private _email?: string; @@ -19,23 +18,3 @@ class Person { module.exports = Person;", ] `; - -exports[`Should be able to render models to ESM module system and should log expected output to console 2`] = ` -Array [ - "const Person = require('./Person'); - -class Root { - private _person?: Person; - - constructor(input: { - person?: Person, - }) { - this._person = input.person; - } - - get person(): Person | undefined { return this._person; } - set person(person: Person | undefined) { this._person = person; } -} -module.exports = Root;", -] -`; diff --git a/examples/typescript-use-cjs/index.spec.ts b/examples/typescript-use-cjs/index.spec.ts index 1bdece5160..987374c1e9 100644 --- a/examples/typescript-use-cjs/index.spec.ts +++ b/examples/typescript-use-cjs/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render models to ESM module system', () => { afterAll(() => { @@ -7,9 +9,7 @@ describe('Should be able to render models to ESM module system', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 2 models, we double it - expect(spy.mock.calls.length).toEqual(4); + expect(spy.mock.calls.length).toEqual(2); expect(spy.mock.calls[1]).toMatchSnapshot(); - expect(spy.mock.calls[2]).toMatchSnapshot(); }); }); diff --git a/examples/typescript-use-cjs/index.ts b/examples/typescript-use-cjs/index.ts index 99d4db931b..3b0809521d 100644 --- a/examples/typescript-use-cjs/index.ts +++ b/examples/typescript-use-cjs/index.ts @@ -1,6 +1,8 @@ import { TypeScriptGenerator } from '../../src'; -const generator = new TypeScriptGenerator(); +const generator = new TypeScriptGenerator({ + moduleSystem: 'CJS' +}); const jsonSchemaDraft7 = { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', @@ -19,15 +21,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { - const models = await generator.generateCompleteModels( - jsonSchemaDraft7, - { - moduleSystem: 'CJS' - } - ); +export async function generate(): Promise { + const models = await generator.generateCompleteModels(jsonSchemaDraft7, {}); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/examples/typescript-use-esm/__snapshots__/index.spec.ts.snap b/examples/typescript-use-esm/__snapshots__/index.spec.ts.snap index a8f1b84637..a8f7734995 100644 --- a/examples/typescript-use-esm/__snapshots__/index.spec.ts.snap +++ b/examples/typescript-use-esm/__snapshots__/index.spec.ts.snap @@ -3,7 +3,6 @@ exports[`Should be able to render models to ESM module system and should log expected output to console 1`] = ` Array [ " - class Person { private _email?: string; @@ -20,24 +19,3 @@ export default Person; ", ] `; - -exports[`Should be able to render models to ESM module system and should log expected output to console 2`] = ` -Array [ - "import Person from './Person'; - -class Root { - private _person?: Person; - - constructor(input: { - person?: Person, - }) { - this._person = input.person; - } - - get person(): Person | undefined { return this._person; } - set person(person: Person | undefined) { this._person = person; } -} -export default Root; -", -] -`; diff --git a/examples/typescript-use-esm/index.spec.ts b/examples/typescript-use-esm/index.spec.ts index 1bdece5160..987374c1e9 100644 --- a/examples/typescript-use-esm/index.spec.ts +++ b/examples/typescript-use-esm/index.spec.ts @@ -1,5 +1,7 @@ -const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { return; }); -import {generate} from './index'; +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; describe('Should be able to render models to ESM module system', () => { afterAll(() => { @@ -7,9 +9,7 @@ describe('Should be able to render models to ESM module system', () => { }); test('and should log expected output to console', async () => { await generate(); - //Generate is called 2x, so even though we expect 2 models, we double it - expect(spy.mock.calls.length).toEqual(4); + expect(spy.mock.calls.length).toEqual(2); expect(spy.mock.calls[1]).toMatchSnapshot(); - expect(spy.mock.calls[2]).toMatchSnapshot(); }); }); diff --git a/examples/typescript-use-esm/index.ts b/examples/typescript-use-esm/index.ts index dfda3d7cc0..2f4fc3587b 100644 --- a/examples/typescript-use-esm/index.ts +++ b/examples/typescript-use-esm/index.ts @@ -1,6 +1,8 @@ import { TypeScriptGenerator } from '../../src'; -const generator = new TypeScriptGenerator(); +const generator = new TypeScriptGenerator({ + moduleSystem: 'ESM' +}); const jsonSchemaDraft7 = { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', @@ -19,15 +21,12 @@ const jsonSchemaDraft7 = { } }; -export async function generate() : Promise { - const models = await generator.generateCompleteModels( - jsonSchemaDraft7, - { - moduleSystem: 'ESM' - } - ); +export async function generate(): Promise { + const models = await generator.generateCompleteModels(jsonSchemaDraft7, {}); for (const model of models) { console.log(model.result); } } -generate(); +if (require.main === module) { + generate(); +} diff --git a/jest.config.js b/jest.config.js index e46ba459df..78d5ad22d0 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,15 +1,13 @@ // eslint-disable-next-line no-undef module.exports = { - coverageReporters: [ - 'json-summary', - 'lcov', - 'text' - ], - preset: 'ts-jest', + coverageReporters: ['json-summary', 'lcov', 'text'], + transform: { + '^.+\\.(t|j)sx?$': '@swc/jest' + }, // The root of your source code, typically /src // `` is a token Jest substitutes roots: [''], - + // Test spec file resolution pattern // Matches parent folder `__tests__` and filename // should contain `test` or `spec`. @@ -17,7 +15,18 @@ module.exports = { // Module file extensions for importing moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], testTimeout: 10000, - collectCoverageFrom: [ - 'src/**' + collectCoverageFrom: ['src/**'], + coveragePathIgnorePatterns: [ + '/src/processors/TemplateInputProcessor.ts', + '/src/generators/template' + ], + moduleNameMapper: { + '^nimma/legacy$': '/node_modules/nimma/dist/legacy/cjs/index.js', + '^nimma/(.*)': '/node_modules/nimma/dist/cjs/$1' + }, + modulePathIgnorePatterns: [ + '/examples/TEMPLATE', + '/test/generators/template', + '/test/processors/TemplateInputProcessor.spec.ts' ] }; diff --git a/package-lock.json b/package-lock.json index 2307d6dfdd..565378144f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,22 +1,26 @@ { "name": "@asyncapi/modelina", - "version": "0.59.9", + "version": "1.0.0-next.43", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@asyncapi/modelina", - "version": "0.59.9", + "version": "1.0.0-next.43", "license": "Apache-2.0", "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.0.9", "@apidevtools/swagger-parser": "^10.0.3", - "@asyncapi/parser": "^1.17.0", + "@asyncapi/parser": "^2.0.0-next-major.11", + "@swc/core": "^1.3.5", + "@swc/jest": "^0.2.23", + "alterschema": "^1.1.1", "change-case": "^4.1.2", "openapi-types": "9.3.0", "typescript-json-schema": "^0.53.0" }, "devDependencies": { + "@asyncapi/parserV1": "npm:@asyncapi/parser@^1.17.2", "@semantic-release/commit-analyzer": "^9.0.1", "@semantic-release/github": "^8.0.1", "@semantic-release/npm": "^8.0.0", @@ -25,15 +29,19 @@ "@typescript-eslint/eslint-plugin": "^4.33.0", "@typescript-eslint/parser": "^4.33.0", "ajv": "^8.6.3", + "concurrently": "^7.5.0", "conventional-changelog-conventionalcommits": "^4.6.1", "cross-env": "^7.0.3", - "eslint": "^7.32.0", + "eslint": "7.32.0", + "eslint-config-prettier": "^8.5.0", "eslint-plugin-github": "^4.3.2", + "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-security": "^1.4.0", "eslint-plugin-sonarjs": "^0.10.0", "jest": "^27.2.5", "jsdoc-to-markdown": "^7.1.0", "markdown-toc": "^1.2.0", + "puppeteer": "^16.0.0", "semantic-release": "^19.0.3", "ts-jest": "^27.0.5", "ts-node": "^10.3.0", @@ -84,12 +92,39 @@ } }, "node_modules/@asyncapi/parser": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-1.17.0.tgz", - "integrity": "sha512-xNXtGZ/hTf8a1C4X65ESKkBJgSx0o9RDyX3LYdQfdQWsDjjmltK4saa9Peu+0Nr2bHU+xaziRo4tOv8QmJ5qfA==", + "version": "2.0.0-next-major.11", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.0.0-next-major.11.tgz", + "integrity": "sha512-fTJUjVnwZHjy2T36K9FSgqhg020CmyGEsaC5wM2nAjMd6bN8NgBFIIswlKaNCEUFZKChBgdlpUHSmjzss2HJGg==", + "dependencies": { + "@asyncapi/specs": "^4.0.0", + "@openapi-contrib/openapi-schema-to-json-schema": "^3.2.0", + "@stoplight/json-ref-resolver": "^3.1.4", + "@stoplight/spectral-core": "^1.14.2", + "@stoplight/spectral-functions": "^1.7.1", + "@stoplight/spectral-parsers": "^1.0.2", + "@stoplight/spectral-rulesets": "^1.14.1", + "@types/json-schema": "^7.0.11", + "@types/urijs": "^1.19.19", + "ajv": "^8.11.0", + "ajv-errors": "^3.0.0", + "ajv-formats": "^2.1.1", + "avsc": "^5.7.5", + "js-yaml": "^4.1.0", + "jsonpath-plus": "^7.2.0", + "node-fetch": "2.6.7", + "ramldt2jsonschema": "^1.2.3", + "webapi-parser": "^0.5.0" + } + }, + "node_modules/@asyncapi/parserV1": { + "name": "@asyncapi/parser", + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-1.17.2.tgz", + "integrity": "sha512-xI9GShIuQVfBz5IZ2c9h6cxcPTnfzI1fch6kYL+nF68Q3U669Un0K0TSeyEa57EfhTap53UIRTj7gFIM9QZTNQ==", + "dev": true, "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.0.6", - "@asyncapi/specs": "^3.2.0", + "@asyncapi/specs": "^4.0.1", "@fmvilas/pseudo-yaml-ast": "^0.3.1", "ajv": "^6.10.1", "js-yaml": "^3.13.1", @@ -99,10 +134,11 @@ "tiny-merge-patch": "^0.1.2" } }, - "node_modules/@asyncapi/parser/node_modules/ajv": { + "node_modules/@asyncapi/parserV1/node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -114,18 +150,20 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@asyncapi/parser/node_modules/argparse": { + "node_modules/@asyncapi/parserV1/node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } }, - "node_modules/@asyncapi/parser/node_modules/js-yaml": { + "node_modules/@asyncapi/parserV1/node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -135,9 +173,12 @@ } }, "node_modules/@asyncapi/specs": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-3.2.0.tgz", - "integrity": "sha512-4tzJfhAk5IetJyUDzxl6oQ5oNDnX8+Q7kM62eksONdhJlfU9KYzx/mUeQrbHTFKBROi6yKXcYD842+ahICk8EA==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-4.0.1.tgz", + "integrity": "sha512-3sKw49Y/TeKT/FO2YX/DlyOekytko5KFwNJ3zMPmai+pyhz3gux2p5zV5kGJRCwApI1gX0GJw6p15a6CZgg01w==", + "dependencies": { + "@types/json-schema": "^7.0.11" + } }, "node_modules/@babel/code-frame": { "version": "7.15.8", @@ -805,6 +846,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/@fmvilas/pseudo-yaml-ast/-/pseudo-yaml-ast-0.3.1.tgz", "integrity": "sha512-8OAB74W2a9M3k9bjYD8AjVXkX+qO8c0SqNT5HlgOqx7AxSw8xdksEcZp7gFtfi+4njSxT6+76ZR+1ubjAwQHOg==", + "dev": true, "dependencies": { "yaml-ast-parser": "0.0.43" } @@ -829,6 +871,84 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "node_modules/@hyperjump/json": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@hyperjump/json/-/json-0.1.0.tgz", + "integrity": "sha512-jWsAOHjweWhi0UEBCN57YZzyTt76Z6Fm/OJXOfNBJbEZt569AcTRsjv6Dqj5t4gQhW9td72oquiyaVp9oHbhBQ==", + "dependencies": { + "@hyperjump/json-pointer": "^0.9.2", + "moo": "^0.5.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jdesrosiers" + } + }, + "node_modules/@hyperjump/json-pointer": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/@hyperjump/json-pointer/-/json-pointer-0.9.6.tgz", + "integrity": "sha512-3szMJLfz+1wtfPHnGi1sHzwFfFdZqIZLCCYtaD47vLZMAQCbtoBRVZn44jJgIQ6v37+8fom5rsxSSIMKWi0zbg==", + "hasInstallScript": true, + "dependencies": { + "just-curry-it": "^5.2.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jdesrosiers" + } + }, + "node_modules/@hyperjump/json-schema": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/@hyperjump/json-schema/-/json-schema-0.23.2.tgz", + "integrity": "sha512-/m5emi8ruTGXxrsy6Pik4ipsjdUZVGePzqwm36oWtEB1DcExHnGheW/BDDBGrGZHdIqFV2PMTIfXjmbg7h8xQQ==", + "hasInstallScript": true, + "dependencies": { + "@hyperjump/json-schema-core": "^0.28.0", + "fastest-stable-stringify": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jdesrosiers" + } + }, + "node_modules/@hyperjump/json-schema-core": { + "version": "0.28.5", + "resolved": "https://registry.npmjs.org/@hyperjump/json-schema-core/-/json-schema-core-0.28.5.tgz", + "integrity": "sha512-+f5P3oHYCQru3s+Ha+E10rIyEvyK0Hfa2oj3+cDoGaVMbT4Jg5TgCoIM7B5rl3t3KRA7EOmrLjKFGeLi5yd1pg==", + "deprecated": "This package was rolled into @hyperjump/json-schema as of v1.0.0", + "hasInstallScript": true, + "dependencies": { + "@hyperjump/json": "^0.1.0", + "@hyperjump/json-pointer": "^0.9.4", + "@hyperjump/pact": "^0.2.3", + "content-type": "^1.0.4", + "node-fetch": "^2.6.5", + "pubsub-js": "^1.9.4", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jdesrosiers" + } + }, + "node_modules/@hyperjump/pact": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@hyperjump/pact/-/pact-0.2.4.tgz", + "integrity": "sha512-BGmyLaUSCMVyHrwXr67rMxgiQHPHwcmVCjROoY8q232EpMz9d9aFCkgGhdx//yEfHM7zgsm0zZ8RD/F89uPySg==", + "hasInstallScript": true, + "dependencies": { + "just-curry-it": "^3.1.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jdesrosiers" + } + }, + "node_modules/@hyperjump/pact/node_modules/just-curry-it": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/just-curry-it/-/just-curry-it-3.2.1.tgz", + "integrity": "sha512-Q8206k8pTY7krW32cdmPsP+DqqLgWx/hYPSj9/+7SYqSqz7UuwPbfSe07lQtvuuaVyiSJveXk0E5RydOuWwsEg==" + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1089,6 +1209,17 @@ "node": ">=8" } }, + "node_modules/@jest/create-cache-key-function": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-27.5.1.tgz", + "integrity": "sha512-dmH1yW+makpTSURTy8VzdUwFnfQh1G8R+DxO2Ho2FFmBbKFEVm+3jWdvFhE2VqB/LATCTokkP0dotjyQyw5/AQ==", + "dependencies": { + "@jest/types": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, "node_modules/@jest/environment": { "version": "27.2.5", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.2.5.tgz", @@ -1390,10 +1521,9 @@ } }, "node_modules/@jest/types": { - "version": "27.2.5", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.2.5.tgz", - "integrity": "sha512-nmuM4VuDtCZcY+eTpw+0nvstwReMsjPoj7ZR80/BbixulhLaiX+fbv8oeLW8WZlJMcsGQsTmMKT/iTZu1Uy/lQ==", - "dev": true, + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", @@ -1409,7 +1539,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1424,7 +1553,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1440,7 +1568,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1451,14 +1578,12 @@ "node_modules/@jest/types/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/@jest/types/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -1467,7 +1592,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -1480,6 +1604,28 @@ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.3.tgz", + "integrity": "sha512-XfZgry4DwEZvSFtS/6Y+R48D7qJYJK6R9/yJFyUFHCIUMEEHuJ4X95TDgJp5QkmzfLYvapMPzskV5HpIDrREug==", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/ternary": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@jsep-plugin/ternary/-/ternary-1.1.3.tgz", + "integrity": "sha512-qtLGzCNzPVJ3kdH6/zoLWDPjauHIKiLSBAR71Wa0+PWvGA8wODUQvRgxtpUA5YqAYL3CQ8S4qXhd/9WuWTZirg==", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1647,6 +1793,14 @@ "@octokit/openapi-types": "^11.2.0" } }, + "node_modules/@openapi-contrib/openapi-schema-to-json-schema": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@openapi-contrib/openapi-schema-to-json-schema/-/openapi-schema-to-json-schema-3.2.0.tgz", + "integrity": "sha512-Gj6C0JwCr8arj0sYuslWXUBSP/KnUlEGnPW4qxlXvAl543oaNQgMgIgkQUA6vs5BCCvwTEiL8m/wdWzfl4UvSw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, "node_modules/@semantic-release/commit-analyzer": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-9.0.2.tgz", @@ -1773,154 +1927,735 @@ "@sinonjs/commons": "^1.7.0" } }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, + "node_modules/@stoplight/better-ajv-errors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.3.tgz", + "integrity": "sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==", + "dependencies": { + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, "engines": { - "node": ">= 10" + "node": "^12.20 || >= 14.13" + }, + "peerDependencies": { + "ajv": ">=8" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" + "node_modules/@stoplight/json": { + "version": "3.20.1", + "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.20.1.tgz", + "integrity": "sha512-FXfud+uWgIj1xv6nUO9WnmgmnVikaxJcbtR4XQt4C42n5c2qua3U05Z/3B57hP5TJRSj+tpn9ID6/bFeyYYlEg==", + "dependencies": { + "@stoplight/ordered-object-literal": "^1.0.3", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^13.6.0", + "jsonc-parser": "~2.2.1", + "lodash": "^4.17.21", + "safe-stable-stringify": "^1.1" + }, + "engines": { + "node": ">=8.3.0" + } }, - "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" + "node_modules/@stoplight/json-ref-readers": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@stoplight/json-ref-readers/-/json-ref-readers-1.2.2.tgz", + "integrity": "sha512-nty0tHUq2f1IKuFYsLM4CXLZGHdMn+X/IwEUIpeSOXt0QjMUbL0Em57iJUDzz+2MkWG83smIigNZ3fauGjqgdQ==", + "dependencies": { + "node-fetch": "^2.6.0", + "tslib": "^1.14.1" + }, + "engines": { + "node": ">=8.3.0" + } }, - "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" + "node_modules/@stoplight/json-ref-readers/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" + "node_modules/@stoplight/json-ref-resolver": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@stoplight/json-ref-resolver/-/json-ref-resolver-3.1.5.tgz", + "integrity": "sha512-uaKLITor7UF+JBtI84zs3aOWM0L79zp7w9TrBTwPtx5SLbaQQ4HadDKgX5yhFOLMApLdhwhiftF4c0GFanOxGg==", + "dependencies": { + "@stoplight/json": "^3.17.0", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^12.3.0 || ^13.0.0", + "@types/urijs": "^1.19.19", + "dependency-graph": "~0.11.0", + "fast-memoize": "^2.5.2", + "immer": "^9.0.6", + "lodash": "^4.17.21", + "tslib": "^2.3.1", + "urijs": "^1.19.11" + }, + "engines": { + "node": ">=8.3.0" + } }, - "node_modules/@types/babel__core": { - "version": "7.1.16", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.16.tgz", - "integrity": "sha512-EAEHtisTMM+KaKwfWdC3oyllIqswlznXCIVCt7/oRNrh+DhgT4UEBNC/jlADNjvw7UnfbcdkGQcPVZ1xYiLcrQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "node_modules/@stoplight/json/node_modules/safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + }, + "node_modules/@stoplight/ordered-object-literal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.4.tgz", + "integrity": "sha512-OF8uib1jjDs5/cCU+iOVy+GJjU3X7vk/qJIkIJFqwmlJKrrtijFmqwbu8XToXrwTYLQTP+Hebws5gtZEmk9jag==", + "engines": { + "node": ">=8" } }, - "node_modules/@types/babel__generator": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz", - "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" + "node_modules/@stoplight/path": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@stoplight/path/-/path-1.3.2.tgz", + "integrity": "sha512-lyIc6JUlUA8Ve5ELywPC8I2Sdnh1zc1zmbYgVarhXIp9YeAB0ReeqmGEOWNtlHkbP2DAA1AL65Wfn2ncjK/jtQ==", + "engines": { + "node": ">=8" } }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, + "node_modules/@stoplight/spectral-core": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.16.0.tgz", + "integrity": "sha512-W/NG+wV2UffwLExboqEa04/JbjGhiSTOl7GghLWYP4NKxZGaO6karP6fIxRBOnm34n1qyoZv9thsjSe92MWcDw==", + "dependencies": { + "@stoplight/better-ajv-errors": "1.0.3", + "@stoplight/json": "~3.20.1", + "@stoplight/path": "1.3.2", + "@stoplight/spectral-parsers": "^1.0.0", + "@stoplight/spectral-ref-resolver": "^1.0.0", + "@stoplight/spectral-runtime": "^1.0.0", + "@stoplight/types": "~13.6.0", + "@types/es-aggregate-error": "^1.0.2", + "@types/json-schema": "^7.0.11", + "ajv": "^8.6.0", + "ajv-errors": "~3.0.0", + "ajv-formats": "~2.1.0", + "es-aggregate-error": "^1.0.7", + "jsonpath-plus": "7.1.0", + "lodash": "~4.17.21", + "lodash.topath": "^4.5.2", + "minimatch": "3.1.2", + "nimma": "0.2.2", + "pony-cause": "^1.0.0", + "simple-eval": "1.0.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/@stoplight/spectral-core/node_modules/@stoplight/types": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.6.0.tgz", + "integrity": "sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ==", "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + }, + "engines": { + "node": "^12.20 || >=14.13" } }, - "node_modules/@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.3.0" + "node_modules/@stoplight/spectral-core/node_modules/jsonpath-plus": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.1.0.tgz", + "integrity": "sha512-gTaNRsPWO/K2KY6MrqaUFClF9kmuM6MFH5Dhg1VYDODgFbByw1yb7xu3hrViE/sz+dGOeMWgCzwUwQtAnCTE9g==", + "engines": { + "node": ">=12.0.0" } }, - "node_modules/@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "dev": true, + "node_modules/@stoplight/spectral-formats": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.4.0.tgz", + "integrity": "sha512-j9VQukDzgqDSi26rK9LqsbXrqtkeIsPSPgEf5/sxRsmeF2bwWUhSjYXgYin4flSZ7owFZjZWQ3o0Qq3iApi2JQ==", "dependencies": { - "@types/node": "*" + "@stoplight/json": "^3.17.0", + "@stoplight/spectral-core": "^1.8.0", + "@types/json-schema": "^7.0.7", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, + "node_modules/@stoplight/spectral-functions": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.7.2.tgz", + "integrity": "sha512-f+61/FtIkQeIo+a269CeaeqjpyRsgDyIk6DGr7iS4hyuk1PPk7Uf6MNRDs9FEIBh7CpdEJ+HSHbMLwgpymWTIw==", "dependencies": { - "@types/istanbul-lib-coverage": "*" + "@stoplight/better-ajv-errors": "1.0.3", + "@stoplight/json": "^3.17.1", + "@stoplight/spectral-core": "^1.7.0", + "@stoplight/spectral-formats": "^1.0.0", + "@stoplight/spectral-runtime": "^1.1.0", + "ajv": "^8.6.3", + "ajv-draft-04": "~1.0.0", + "ajv-errors": "~3.0.0", + "ajv-formats": "~2.1.0", + "lodash": "~4.17.21", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, + "node_modules/@stoplight/spectral-parsers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.2.tgz", + "integrity": "sha512-ZQXknJ+BM5Re4Opj4cgVlHgG2qyOk/wznKJq3Vf1qsBEg2CNzN0pJmSB0deRqW0kArqm44qpb8c+cz3F2rgMtw==", "dependencies": { - "@types/istanbul-lib-report": "*" + "@stoplight/json": "~3.20.1", + "@stoplight/types": "^13.6.0", + "@stoplight/yaml": "~4.2.3", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@types/jest": { - "version": "27.0.2", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.0.2.tgz", - "integrity": "sha512-4dRxkS/AFX0c5XW6IPMNOydLn2tEhNhJV7DnYK+0bjoJZ+QTmfucBlihX7aoEsh/ocYtkLC73UbnBXBXIxsULA==", - "dev": true, + "node_modules/@stoplight/spectral-ref-resolver": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-ref-resolver/-/spectral-ref-resolver-1.0.3.tgz", + "integrity": "sha512-pj+bH4SH8hcWlnV787WD7P0/En7LA3EfZMvG1JUGMW/7bFd9AaZZXNkh5j0ve8qnPlwP8F4SH/2Cnr1tXOXCVw==", "dependencies": { - "jest-diff": "^27.0.0", - "pretty-format": "^27.0.0" + "@stoplight/json-ref-readers": "1.2.2", + "@stoplight/json-ref-resolver": "~3.1.5", + "@stoplight/spectral-runtime": "^1.1.2", + "dependency-graph": "0.11.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" + "node_modules/@stoplight/spectral-rulesets": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.14.1.tgz", + "integrity": "sha512-tn6a5fYPFDwEY+/YyK/hcq2gcR5nSIBt7l+JGELb/2RdTzD5ikj2mfl2ua3uxbqOZytftFoOX5ewGZ0qQNrudw==", + "dependencies": { + "@asyncapi/specs": "^3.2.0", + "@stoplight/better-ajv-errors": "1.0.3", + "@stoplight/json": "^3.17.0", + "@stoplight/spectral-core": "^1.8.1", + "@stoplight/spectral-formats": "^1.4.0", + "@stoplight/spectral-functions": "^1.5.1", + "@stoplight/spectral-runtime": "^1.1.1", + "@stoplight/types": "^13.6.0", + "@types/json-schema": "^7.0.7", + "ajv": "^8.8.2", + "ajv-formats": "~2.1.0", + "json-schema-traverse": "^1.0.0", + "lodash": "~4.17.21", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=12" + } }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true + "node_modules/@stoplight/spectral-rulesets/node_modules/@asyncapi/specs": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-3.2.1.tgz", + "integrity": "sha512-FO+EteK+Gk3zwumrBw6frpp9cJ4oQL5++hBBpfM81w16e9KaiA4sKrzvQsvVjifoZZHNvVEX4D2zoz9i8CLccQ==" }, - "node_modules/@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true + "node_modules/@stoplight/spectral-rulesets/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, + "node_modules/@stoplight/spectral-runtime": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-runtime/-/spectral-runtime-1.1.2.tgz", + "integrity": "sha512-fr5zRceXI+hrl82yAVoME+4GvJie8v3wmOe9tU+ZLRRNonizthy8qDi0Z/z4olE+vGreSDcuDOZ7JjRxFW5kTw==", "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@stoplight/json": "^3.17.0", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^12.3.0", + "abort-controller": "^3.0.0", + "lodash": "^4.17.21", + "node-fetch": "^2.6.7", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true + "node_modules/@stoplight/spectral-runtime/node_modules/@stoplight/types": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-12.5.0.tgz", + "integrity": "sha512-dwqYcDrGmEyUv5TWrDam5TGOxU72ufyQ7hnOIIDdmW5ezOwZaBFoR5XQ9AsH49w7wgvOqB2Bmo799pJPWnpCbg==", + "dependencies": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/@types/minimist": { - "version": "1.2.2", + "node_modules/@stoplight/types": { + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.8.0.tgz", + "integrity": "sha512-5glKswz7y9aACh+a+JegID+4xX//4TsIdv7iPl29hWnOoWrnlPbg3Gjc4nYUXXgMSaSlSsA15JU/0+rE89fR4A==", + "dependencies": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + }, + "engines": { + "node": "^12.20 || >=14.13" + } + }, + "node_modules/@stoplight/yaml": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.3.tgz", + "integrity": "sha512-Mx01wjRAR9C7yLMUyYFTfbUf5DimEpHMkRDQ1PKLe9dfNILbgdxyrncsOXM3vCpsQ1Hfj4bPiGl+u4u6e9Akqw==", + "dependencies": { + "@stoplight/ordered-object-literal": "^1.0.1", + "@stoplight/types": "^13.0.0", + "@stoplight/yaml-ast-parser": "0.0.48", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=10.8" + } + }, + "node_modules/@stoplight/yaml-ast-parser": { + "version": "0.0.48", + "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.48.tgz", + "integrity": "sha512-sV+51I7WYnLJnKPn2EMWgS4EUfoP4iWEbrWwbXsj0MZCB/xOK8j6+C9fntIdOM50kpx45ZLC3s6kwKivWuqvyg==" + }, + "node_modules/@swc/core": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.5.tgz", + "integrity": "sha512-H5YNI9rCViudhEmu9g/Yc8ai6k5/pfy+ItYns0SZ+iSZen+bgWeGb+9p4KRQhzNNC8Lfkfw+ENHzSwZltpWG6Q==", + "hasInstallScript": true, + "bin": { + "swcx": "run_swcx.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-android-arm-eabi": "1.3.5", + "@swc/core-android-arm64": "1.3.5", + "@swc/core-darwin-arm64": "1.3.5", + "@swc/core-darwin-x64": "1.3.5", + "@swc/core-freebsd-x64": "1.3.5", + "@swc/core-linux-arm-gnueabihf": "1.3.5", + "@swc/core-linux-arm64-gnu": "1.3.5", + "@swc/core-linux-arm64-musl": "1.3.5", + "@swc/core-linux-x64-gnu": "1.3.5", + "@swc/core-linux-x64-musl": "1.3.5", + "@swc/core-win32-arm64-msvc": "1.3.5", + "@swc/core-win32-ia32-msvc": "1.3.5", + "@swc/core-win32-x64-msvc": "1.3.5" + } + }, + "node_modules/@swc/core-android-arm-eabi": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.3.5.tgz", + "integrity": "sha512-gIq3fuXiRMtVhTf2ZQ9Z6JeuqvL30JOM0L+S6zAZD4v8lpGJ1ejpw7rghpAsGSG9Qc9oaNjx5yayTg3z/EtBzA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "dependencies": { + "@swc/wasm": "1.2.122" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-android-arm64": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.3.5.tgz", + "integrity": "sha512-SsRA6AhNZK8YXBbv7DAp5Zgv4tOWvPJlEBoOZ0uLIot7oYghWvSVs3jOgEzJSbQLU5U7ad6Q6boBdg0Q/kb2Ew==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "dependencies": { + "@swc/wasm": "1.2.130" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-android-arm64/node_modules/@swc/wasm": { + "version": "1.2.130", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.130.tgz", + "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", + "optional": true + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.5.tgz", + "integrity": "sha512-Jyem+f3/aTKJTRzyvdSfYS358jo7245g7nWmwmhQMgZI3/z2VcYHpIIYOi+dgsBaMRevK9tbsW0TSx805Njzjw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.5.tgz", + "integrity": "sha512-zW1tfS000RlHcqKp1HJK5vXBR0/AHw74qzOK0uh/G1cTczFDX2Hep4IuOxSJ1+7Zx9oFEOKSEY0lPXYbDAlF2w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-freebsd-x64": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.3.5.tgz", + "integrity": "sha512-H2f0NkfqYDC6+vJO6wSBwiGnnR/cK9AQx574izPw3Utmb28zC+FOPAY63QLA/orNHjwHa6B6AuVDNwYuKUrRHQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@swc/wasm": "1.2.130" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-freebsd-x64/node_modules/@swc/wasm": { + "version": "1.2.130", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.130.tgz", + "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", + "optional": true + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.5.tgz", + "integrity": "sha512-PvuhjUCsNQDtwSSXWmmF6tU8jnAcFVRZt6bBNltvPW48oHNmIq9lEZ+hJTSPvqqxLvi9W7HG5ADzsTAaciKeRw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "dependencies": { + "@swc/wasm": "1.2.130" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf/node_modules/@swc/wasm": { + "version": "1.2.130", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.130.tgz", + "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", + "optional": true + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.5.tgz", + "integrity": "sha512-NQ1LVrIvAsSwSoKO6DzHQMxzpvo17v/2LREqBiaNuCwDyYg2yFdgUdVW4FcbrdBK4MurRA2HFZZ/rt5DqAg3+A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.5.tgz", + "integrity": "sha512-DDcM3ciJRBBjyN7qqw/AEEFh61YjiuxOcZ5SqYR0wyfroqOFX1+5JtCGJ9mU2MZ4Vfmxb1v5IFoQ3nfgJDcd8g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.5.tgz", + "integrity": "sha512-AJR0J+b3jMmXuIxqhgrX/7vworHjciUPZuoyY2OrIhSXwMPVbWfb72h9oQdMbARfodTFLVJGQqy2Pij67+C0GQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.5.tgz", + "integrity": "sha512-+Ig/rJ/GOZyQgCO72PFR+oJYUee0zQRsd6fpeuE66rn8P07a26WY1ZfMGw8miWcURccjDgAc1XCwVn6wa/OTCw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.5.tgz", + "integrity": "sha512-9Go5jiGWToT+00/J26E92n/JIHqG2wcaw79Z1+Z7GHrrm5TeL0VMyTMGLMeGFvtje/j+Lv0y4XKed+dKnRvc5w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "dependencies": { + "@swc/wasm": "1.2.130" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc/node_modules/@swc/wasm": { + "version": "1.2.130", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.130.tgz", + "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", + "optional": true + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.5.tgz", + "integrity": "sha512-AIeD5uKVkvXTAbKAwyPFubFrXmQR77PNun59DHZWtRpxgOcHqK6xug9DfUSfc2zMw/ftEe9kNruUUS96023jfQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "dependencies": { + "@swc/wasm": "1.2.130" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc/node_modules/@swc/wasm": { + "version": "1.2.130", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.130.tgz", + "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", + "optional": true + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.5.tgz", + "integrity": "sha512-GtrAkUo5xVTogwTDH9Zms7LELdTKyRll+K9o87P+YOEizCUvA0BPE1N4mu+ZqsI/dv56g2N4gNCD8RVLH3HekQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/jest": { + "version": "0.2.23", + "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.23.tgz", + "integrity": "sha512-ZLj17XjHbPtNsgqjm83qizENw05emLkKGu3WuPUttcy9hkngl0/kcc7fDbcSBpADS0GUtsO+iKPjZFWVAtJSlA==", + "dependencies": { + "@jest/create-cache-key-function": "^27.4.2", + "jsonc-parser": "^3.2.0" + }, + "engines": { + "npm": ">= 7.0.0" + }, + "peerDependencies": { + "@swc/core": "*" + } + }, + "node_modules/@swc/jest/node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + }, + "node_modules/@swc/wasm": { + "version": "1.2.122", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.122.tgz", + "integrity": "sha512-sM1VCWQxmNhFtdxME+8UXNyPNhxNu7zdb6ikWpz0YKAQQFRGT5ThZgJrubEpah335SUToNg8pkdDF7ibVCjxbQ==", + "optional": true + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" + }, + "node_modules/@types/babel__core": { + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.16.tgz", + "integrity": "sha512-EAEHtisTMM+KaKwfWdC3oyllIqswlznXCIVCt7/oRNrh+DhgT4UEBNC/jlADNjvw7UnfbcdkGQcPVZ1xYiLcrQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz", + "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/es-aggregate-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/es-aggregate-error/-/es-aggregate-error-1.0.2.tgz", + "integrity": "sha512-erqUpFXksaeR2kejKnhnjZjbFxUpGZx4Z7ydNL9ie8tEhXPiZTsLeUDJ6aR1F8j5wWUAtOAQWUqkc7givBJbBA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.0.2.tgz", + "integrity": "sha512-4dRxkS/AFX0c5XW6IPMNOydLn2tEhNhJV7DnYK+0bjoJZ+QTmfucBlihX7aoEsh/ocYtkLC73UbnBXBXIxsULA==", + "dev": true, + "dependencies": { + "jest-diff": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.2", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", "dev": true @@ -1960,11 +2695,15 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "node_modules/@types/urijs": { + "version": "1.19.19", + "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.19.tgz", + "integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg==" + }, "node_modules/@types/yargs": { "version": "16.0.4", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, "dependencies": { "@types/yargs-parser": "*" } @@ -1972,8 +2711,17 @@ "node_modules/@types/yargs-parser": { "version": "20.2.1", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", - "dev": true + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" + }, + "node_modules/@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "4.33.0", @@ -2138,6 +2886,17 @@ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", "dev": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -2204,10 +2963,9 @@ } }, "node_modules/ajv": { - "version": "8.6.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", - "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", - "dev": true, + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -2219,11 +2977,61 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", + "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", + "peerDependencies": { + "ajv": "^8.0.1" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ajv/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/alterschema": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/alterschema/-/alterschema-1.1.1.tgz", + "integrity": "sha512-h32BfJpnx3jPU8rdC/CW6iA0Ck+6jIVhj880HULD1cZrBLMLmwKCDd4xn6ykH7pU0quvLIi7LV0I0K4F3A418w==", + "dependencies": { + "@hyperjump/json-schema": "^0.23.2", + "json-e": "^4.4.3", + "lodash": "^4.17.21", + "object-hash": "^3.0.0" + }, + "bin": { + "alterschema": "bindings/node/cli.js" + } }, "node_modules/ansi-colors": { "version": "4.1.1", @@ -2359,9 +3167,9 @@ "dev": true }, "node_modules/array-back": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", - "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.0.tgz", + "integrity": "sha512-mixVv03GOOn/ubHE4STQ+uevX42ETdk0JoMVEjNkSOCT7WgERh7C8/+NyhWYNpE3BN69pxFyJIBcF7CxWz/+4A==", "dev": true, "engines": { "node": ">=12.17" @@ -2436,6 +3244,14 @@ "node": ">=8" } }, + "node_modules/astring": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.3.tgz", + "integrity": "sha512-sRpyiNrx2dEYIMmUXprS8nlpRg2Drs8m9ElX9vVEXaCB4XEAJhKfs7IcX0IwShjuOAjLR6wzIrgoptz1n19i1A==", + "bin": { + "astring": "bin/astring" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2451,6 +3267,14 @@ "gulp-header": "^1.7.1" } }, + "node_modules/avsc": { + "version": "5.7.7", + "resolved": "https://registry.npmjs.org/avsc/-/avsc-5.7.7.tgz", + "integrity": "sha512-9cYNccliXZDByFsFliVwk5GvTq058Fj513CiR4E60ndDwmuXzTJEp/Bp8FyuRmGyYupLjHLs+JA9/CBoVS4/NQ==", + "engines": { + "node": ">=0.11" + } + }, "node_modules/babel-jest": { "version": "27.2.5", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.2.5.tgz", @@ -2618,12 +3442,43 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/before-after-hook": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", "dev": true }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -2707,6 +3562,39 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2740,7 +3628,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -2799,14 +3686,20 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001267", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001267.tgz", - "integrity": "sha512-r1mjTzAuJ9W8cPBGbbus8E0SKcUP7gn03R14Wk8FlAlqhH9hroy9nLqmpuXlfKEw/oILW+FGz47ipXV2O7x8lg==", + "version": "1.0.30001370", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001370.tgz", + "integrity": "sha512-3PDmaP56wz/qz7G508xzjx8C+MC2qEm4SYhSEzC9IBROo+dGXFWRuaXkWti0A9tuI00g+toiriVqxtWMgl350g==", "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] }, "node_modules/capital-case": { "version": "1.0.4", @@ -2885,6 +3778,12 @@ "node": ">=10" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, "node_modules/ci-info": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", @@ -2935,7 +3834,6 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true, "engines": { "iojs": ">= 1.0.0", "node": ">= 0.12.0" @@ -2945,6 +3843,7 @@ "version": "0.0.230", "resolved": "https://registry.npmjs.org/code-error-fragment/-/code-error-fragment-0.0.230.tgz", "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==", + "dev": true, "engines": { "node": ">= 4" } @@ -3181,6 +4080,159 @@ "source-map": "^0.6.1" } }, + "node_modules/concurrently": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.6.0.tgz", + "integrity": "sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.29.1", + "lodash": "^4.17.21", + "rxjs": "^7.0.0", + "shell-quote": "^1.7.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^17.3.1" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/concurrently/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/concurrently/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concurrently/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/concurrently/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/config-master": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", @@ -3209,6 +4261,14 @@ "upper-case": "^2.0.2" } }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/conventional-changelog-angular": { "version": "5.0.13", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", @@ -3361,6 +4421,15 @@ "yarn": ">=1" } }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dev": true, + "dependencies": { + "node-fetch": "2.6.7" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3457,6 +4526,19 @@ "node": ">=10" } }, + "node_modules/date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", + "dev": true, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -3551,15 +4633,18 @@ } }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dependencies": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/del": { @@ -3608,6 +4693,14 @@ "node": ">=0.4.0" } }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -3623,6 +4716,12 @@ "node": ">=8" } }, + "node_modules/devtools-protocol": { + "version": "0.0.1019158", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1019158.tgz", + "integrity": "sha512-wvq+KscQ7/6spEV7czhnZc9RM/woz1AY+/Vpd8/h2HFMwJSdTliu7f/yr1A6vDdJfKICZsShqsYpEQbdhg8AFQ==", + "dev": true + }, "node_modules/diacritics-map": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz", @@ -3662,26 +4761,35 @@ } }, "node_modules/dmd": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.1.0.tgz", - "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.0.0.tgz", + "integrity": "sha512-PwWZlqZnJPETwqZZ70haRa+UDZcD5jeBD3ywW1Kf+jYYv0MHu/S7Ri9jsSoeTMwkcMVW9hXOMA1IZUMEufBhOg==", "dev": true, "dependencies": { - "array-back": "^6.2.2", + "array-back": "^5.0.0", "cache-point": "^2.0.0", - "common-sequence": "^2.0.2", - "file-set": "^4.0.2", + "common-sequence": "^2.0.0", + "file-set": "^4.0.1", "handlebars": "^4.7.7", - "marked": "^4.0.12", + "marked": "^2.0.0", "object-get": "^2.1.1", - "reduce-flatten": "^3.0.1", + "reduce-flatten": "^3.0.0", "reduce-unique": "^2.0.1", "reduce-without": "^1.0.1", "test-value": "^3.0.0", - "walk-back": "^5.1.0" + "walk-back": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" + } + }, + "node_modules/dmd/node_modules/array-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "dev": true, + "engines": { + "node": ">=10" } }, "node_modules/dmd/node_modules/reduce-flatten": { @@ -3831,13 +4939,10 @@ } }, "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", + "dev": true }, "node_modules/env-ci": { "version": "5.0.2", @@ -3896,44 +5001,105 @@ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true, "engines": { - "node": ">=8.12.0" + "node": ">=8.12.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", + "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.6", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, + "node_modules/es-aggregate-error": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.9.tgz", + "integrity": "sha512-fvnX40sb538wdU6r4s35cq4EY6Lr09Upj40BEVem4LEsuW8XgQep9yD5Q1U2KftokNp1rWODFJ2qwZSsAjFpbg==", "dependencies": { - "is-arrayish": "^0.2.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "function-bind": "^1.1.1", + "functions-have-names": "^1.2.3", + "get-intrinsic": "^1.1.3", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, + "node_modules/es-aggregate-error/node_modules/es-abstract": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz", + "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==", "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", + "gopd": "^1.0.1", "has": "^1.0.3", - "has-symbols": "^1.0.2", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "unbox-primitive": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3942,11 +5108,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-aggregate-error/node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-aggregate-error/node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -4116,9 +5307,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -4296,6 +5487,27 @@ "eslint": "^7.23.0" } }, + "node_modules/eslint-plugin-github/node_modules/eslint-plugin-prettier": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz", + "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "eslint": ">=5.0.0", + "prettier": ">=1.13.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-i18n-text": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-i18n-text/-/eslint-plugin-i18n-text-1.0.1.tgz", @@ -4369,19 +5581,19 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz", - "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=12.0.0" }, "peerDependencies": { - "eslint": ">=5.0.0", - "prettier": ">=1.13.0" + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" }, "peerDependenciesMeta": { "eslint-config-prettier": { @@ -4728,6 +5940,14 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -4853,6 +6073,41 @@ "node": ">=0.10.0" } }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4891,6 +6146,16 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fast-memoize": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", + "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==" + }, + "node_modules/fastest-stable-stringify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz", + "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==" + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -4909,6 +6174,15 @@ "bser": "2.1.1" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -5101,6 +6375,12 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "node_modules/fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -5146,8 +6426,24 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/functional-red-black-tree": { "version": "1.0.1", @@ -5155,6 +6451,14 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5173,14 +6477,13 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5211,7 +6514,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -5344,6 +6646,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/globby": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", @@ -5364,6 +6680,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", @@ -5373,7 +6700,8 @@ "node_modules/grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true }, "node_modules/gray-matter": { "version": "2.1.1", @@ -5499,7 +6827,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -5508,10 +6835,9 @@ } }, "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5525,11 +6851,21 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "engines": { "node": ">= 0.4" }, @@ -5541,7 +6877,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -5648,6 +6983,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.1.8", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", @@ -5657,6 +7012,15 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "9.0.16", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", + "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -5755,7 +7119,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, "dependencies": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -5791,7 +7154,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, "dependencies": { "has-bigints": "^1.0.1" }, @@ -5803,7 +7165,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -5822,10 +7183,9 @@ "dev": true }, "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true, + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "engines": { "node": ">= 0.4" }, @@ -5861,7 +7221,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -5920,10 +7279,9 @@ } }, "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true, + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "engines": { "node": ">= 0.4" }, @@ -5941,10 +7299,9 @@ } }, "node_modules/is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", - "dev": true, + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -6010,7 +7367,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -6023,10 +7379,12 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dependencies": { + "call-bind": "^1.0.2" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6047,7 +7405,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -6062,7 +7419,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -6092,12 +7448,11 @@ "dev": true }, "node_modules/is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dependencies": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7952,41 +9307,40 @@ } }, "node_modules/js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.1.tgz", + "integrity": "sha512-KrPTolcw6RocpYjdC7pL7v62e55q7qOMHvLX1UCLc5AAS8qeJ6nukarEJAF2KL2PZxlbGueEbINqZR2bDe/gUw==", "dev": true, "dependencies": { - "xmlcreate": "^2.0.4" + "xmlcreate": "^2.0.3" } }, "node_modules/jsdoc": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", - "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz", + "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==", "dev": true, "dependencies": { "@babel/parser": "^7.9.4", - "@types/markdown-it": "^12.2.3", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", + "js2xmlparser": "^4.0.1", "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", + "markdown-it": "^10.0.0", + "markdown-it-anchor": "^5.2.7", + "marked": "^2.0.3", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", "taffydb": "2.6.2", - "underscore": "~1.13.2" + "underscore": "~1.13.1" }, "bin": { "jsdoc": "jsdoc.js" }, "engines": { - "node": ">=12.0.0" + "node": ">=8.15.0" } }, "node_modules/jsdoc-api": { @@ -8184,6 +9538,14 @@ "node": ">=10" } }, + "node_modules/jsep": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.8.tgz", + "integrity": "sha512-qofGylTGgYj9gZFsHuyWAN4jr35eJ66qJCK4eKDnldohuUoQFbU3iZn2zjvEbd9wOAhP9Wx5DsAAduTyE1PSWQ==", + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -8196,6 +9558,17 @@ "node": ">=4" } }, + "node_modules/json-e": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/json-e/-/json-e-4.4.3.tgz", + "integrity": "sha512-G2tp4fkEzN6hlya4J9dNaRbIxxTW+Lqu+An40E36SNZYexIGPPuNU30VuS0eZam4AieV0R+fvRxZrVFPJEvUbA==", + "dependencies": { + "json-stable-stringify-without-jsonify": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -8208,6 +9581,35 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-schema-migrate": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-0.2.0.tgz", + "integrity": "sha512-dq4/oHWmtw/+0ytnXsDqVn+VsVweTEmzm5jLgguPn9BjSzn6/q58ZiZx3BHiQyJs612f0T5Z+MrUEUUY5DHsRg==", + "dependencies": { + "ajv": "^5.0.0" + } + }, + "node_modules/json-schema-migrate/node_modules/ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", + "dependencies": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "node_modules/json-schema-migrate/node_modules/fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==" + }, + "node_modules/json-schema-migrate/node_modules/json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -8216,8 +9618,7 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" }, "node_modules/json-stringify-safe": { "version": "5.0.1", @@ -8229,6 +9630,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/json-to-ast/-/json-to-ast-2.1.0.tgz", "integrity": "sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==", + "dev": true, "dependencies": { "code-error-fragment": "0.0.230", "grapheme-splitter": "^1.0.4" @@ -8249,6 +9651,11 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonc-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz", + "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==" + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -8270,6 +9677,22 @@ "node >= 0.2.0" ] }, + "node_modules/jsonpath-plus": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz", + "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -8286,6 +9709,11 @@ "node": "*" } }, + "node_modules/just-curry-it": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/just-curry-it/-/just-curry-it-5.2.1.tgz", + "integrity": "sha512-M8qhhO9WVNc3yZgf3qfiNxMIsQlHqFHJ3vMI8N/rkp852h1utOB/N3ebS8jeXGAwYSbkdd0K6zP9eZneUtjHwA==" + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -8329,7 +9757,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, "engines": { "node": ">=6" } @@ -8354,9 +9781,9 @@ "dev": true }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", "dev": true, "dependencies": { "uc.micro": "^1.0.1" @@ -8444,8 +9871,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash._reinterpolate": { "version": "3.0.0", @@ -8468,7 +9894,8 @@ "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", @@ -8559,6 +9986,11 @@ "lodash._reinterpolate": "^3.0.0" } }, + "node_modules/lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", + "integrity": "sha512-1/W4dM+35DwvE/iEd1M9ekewOSTlpFekhw9mhAtrwjVqUr83/ilQiyAvmg4tVX7Unkcfl1KC+i9WdaT4B6aQcg==" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -8648,14 +10080,14 @@ } }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", + "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", "dev": true, "dependencies": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^2.0.0", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" }, @@ -8664,15 +10096,23 @@ } }, "node_modules/markdown-it-anchor": { - "version": "8.6.5", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz", - "integrity": "sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz", + "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==", "dev": true, "peerDependencies": { - "@types/markdown-it": "*", "markdown-it": "*" } }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/markdown-link": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz", @@ -8709,15 +10149,15 @@ } }, "node_modules/marked": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.0.tgz", - "integrity": "sha512-+Z6KDjSPa6/723PQYyc1axYZpYYpDnECDaU6hkaf5gqBieBkMKYReL5hteF2QizhlMbgbo8umXl/clZ67+GlsA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", + "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", "dev": true, "bin": { - "marked": "bin/marked.js" + "marked": "bin/marked" }, "engines": { - "node": ">= 12" + "node": ">= 10" } }, "node_modules/marked-terminal": { @@ -8788,7 +10228,7 @@ "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", "dev": true }, "node_modules/meow": { @@ -8984,6 +10424,12 @@ "node": ">=10" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, "node_modules/mkdirp2": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", @@ -8999,6 +10445,11 @@ "node": ">=0.10.0" } }, + "node_modules/moo": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", + "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -9023,6 +10474,33 @@ "integrity": "sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo=", "dev": true }, + "node_modules/nimma": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/nimma/-/nimma-0.2.2.tgz", + "integrity": "sha512-V52MLl7BU+tH2Np9tDrIXK8bql3MVUadnMIl/0/oZSGC9keuro0O9UUv9QKp0aMvtN8HRew4G7byY7H4eWsxaQ==", + "dependencies": { + "@jsep-plugin/regex": "^1.0.1", + "@jsep-plugin/ternary": "^1.0.2", + "astring": "^1.8.1", + "jsep": "^1.2.0" + }, + "engines": { + "node": "^12.20 || >=14.13" + }, + "optionalDependencies": { + "jsonpath-plus": "^6.0.1", + "lodash.topath": "^4.5.2" + } + }, + "node_modules/nimma/node_modules/jsonpath-plus": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-6.0.1.tgz", + "integrity": "sha512-EvGovdvau6FyLexFH2OeXfIITlgIbgZoAZe3usiySeaIDm5QS+A10DKNpaPBBqqRSZr2HN6HVNXxtwUAr2apEw==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -12049,11 +13527,18 @@ "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", "dev": true }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", - "dev": true, + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -12062,7 +13547,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -12077,14 +13561,13 @@ } }, "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -12381,6 +13864,12 @@ "node": ">=8" } }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -12579,6 +14068,14 @@ "node": ">=4" } }, + "node_modules/pony-cause": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz", + "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -12667,12 +14164,23 @@ "node": ">= 6" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", "dev": true }, + "node_modules/pubsub-js": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.4.tgz", + "integrity": "sha512-hJYpaDvPH4w8ZX/0Fdf9ma1AwRgU353GfbaVfPjfJQf1KxZ2iHaHl3fAUw1qlJIR5dr4F3RzjGaWohYUEyoh7A==" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -12691,6 +14199,94 @@ "node": ">=6" } }, + "node_modules/puppeteer": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-16.0.0.tgz", + "integrity": "sha512-FgSe21IHNHkqv1SiJiob4ANsxVujcINa4p3MaDEMyoZsocbgSgwYE0c9lnF8eoinw4id3vx4DOXwhFdOOwVlDg==", + "deprecated": "< 18.1.0 is no longer supported", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "cross-fetch": "3.1.5", + "debug": "4.3.4", + "devtools-protocol": "0.0.1019158", + "extract-zip": "2.0.1", + "https-proxy-agent": "5.0.1", + "pkg-dir": "4.2.0", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "rimraf": "3.0.2", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "ws": "8.8.1" + }, + "engines": { + "node": ">=14.1.0" + } + }, + "node_modules/puppeteer/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/puppeteer/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/puppeteer/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/puppeteer/node_modules/ws": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -12730,6 +14326,49 @@ "node": ">=8" } }, + "node_modules/ramldt2jsonschema": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/ramldt2jsonschema/-/ramldt2jsonschema-1.2.3.tgz", + "integrity": "sha512-+wLDAV2NNv9NkfEUOYStaDu/6RYgYXeC1zLtXE+dMU/jDfjpN4iJnBGycDwFTFaIQGosOQhxph7fEX6Mpwxdug==", + "dependencies": { + "commander": "^5.0.0", + "js-yaml": "^3.14.0", + "json-schema-migrate": "^0.2.0", + "webapi-parser": "^0.5.0" + }, + "bin": { + "dt2js": "bin/dt2js.js", + "js2dt": "bin/js2dt.js" + } + }, + "node_modules/ramldt2jsonschema/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/ramldt2jsonschema/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/ramldt2jsonschema/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/randomatic": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", @@ -12979,6 +14618,22 @@ "node": ">=0.10.0" } }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -13058,7 +14713,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -13181,6 +14835,15 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz", + "integrity": "sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -13210,6 +14873,19 @@ "ret": "~0.1.10" } }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-stable-stringify": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", @@ -13314,6 +14990,18 @@ "semantic-release": ">=19.0.0" } }, + "node_modules/semantic-release/node_modules/marked": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.16.tgz", + "integrity": "sha512-wahonIQ5Jnyatt2fn8KqF/nIqZM8mh3oRu2+l5EANGMhu6RFjiSG52QNE2eWzFMI94HqYSgN184NurgNG6CztA==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/semantic-release/node_modules/npm": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/npm/-/npm-8.12.1.tgz", @@ -15850,11 +17538,19 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -15896,6 +17592,17 @@ "node": ">=4" } }, + "node_modules/simple-eval": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-eval/-/simple-eval-1.0.0.tgz", + "integrity": "sha512-kpKJR+bqTscgC0xuAl2xHN6bB12lHjC2DCUfqjAx19bQyO3R2EVLOurm3H9AUltv/uFVcSCVNc6faegR+8NYLw==", + "dependencies": { + "jsep": "^1.1.2" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -16020,6 +17727,12 @@ "source-map": "^0.6.0" } }, + "node_modules/spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", + "dev": true + }, "node_modules/spawn-error-forwarder": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", @@ -16214,26 +17927,26 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -16411,6 +18124,34 @@ "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", "dev": true }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -16551,7 +18292,8 @@ "node_modules/tiny-merge-patch": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tiny-merge-patch/-/tiny-merge-patch-0.1.2.tgz", - "integrity": "sha1-Lo3tGcVuoV29OtTtXbHI5a1UTDw=" + "integrity": "sha512-NLoA//tTMBPTr0oGdq+fxnvVR0tDa8tOcG9ZGbuovGzROadZ404qOV4g01jeWa5S8MC9nAOvu5bQgCW7s8tlWQ==", + "dev": true }, "node_modules/tmpl": { "version": "1.0.5", @@ -16644,6 +18386,15 @@ "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=", "dev": true }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -16930,24 +18681,33 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "node_modules/underscore": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", - "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", "dev": true }, "node_modules/unique-string": { @@ -17001,6 +18761,11 @@ "punycode": "^2.1.0" } }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" + }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -17013,6 +18778,14 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "node_modules/utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -17064,6 +18837,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", "dev": true, "dependencies": { "browser-process-hrtime": "^1.0.0" @@ -17099,6 +18873,30 @@ "makeerror": "1.0.x" } }, + "node_modules/webapi-parser": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/webapi-parser/-/webapi-parser-0.5.0.tgz", + "integrity": "sha512-fPt6XuMqLSvBz8exwX4QE1UT+pROLHa00EMDCdO0ybICduwQ1V4f7AWX4pNOpCp+x+0FjczEsOxtQU0d8L3QKw==", + "dependencies": { + "ajv": "6.5.2" + } + }, + "node_modules/webapi-parser/node_modules/ajv": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", + "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", + "dependencies": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.1" + } + }, + "node_modules/webapi-parser/node_modules/fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -17147,7 +18945,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -17284,9 +19081,9 @@ "dev": true }, "node_modules/xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz", + "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==", "dev": true }, "node_modules/xtend": { @@ -17324,7 +19121,8 @@ "node_modules/yaml-ast-parser": { "version": "0.0.43", "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", - "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==" + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true }, "node_modules/yargs": { "version": "16.2.0", @@ -17353,6 +19151,16 @@ "node": ">=10" } }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -17417,12 +19225,38 @@ } }, "@asyncapi/parser": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-1.17.0.tgz", - "integrity": "sha512-xNXtGZ/hTf8a1C4X65ESKkBJgSx0o9RDyX3LYdQfdQWsDjjmltK4saa9Peu+0Nr2bHU+xaziRo4tOv8QmJ5qfA==", + "version": "2.0.0-next-major.11", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.0.0-next-major.11.tgz", + "integrity": "sha512-fTJUjVnwZHjy2T36K9FSgqhg020CmyGEsaC5wM2nAjMd6bN8NgBFIIswlKaNCEUFZKChBgdlpUHSmjzss2HJGg==", + "requires": { + "@asyncapi/specs": "^4.0.0", + "@openapi-contrib/openapi-schema-to-json-schema": "^3.2.0", + "@stoplight/json-ref-resolver": "^3.1.4", + "@stoplight/spectral-core": "^1.14.2", + "@stoplight/spectral-functions": "^1.7.1", + "@stoplight/spectral-parsers": "^1.0.2", + "@stoplight/spectral-rulesets": "^1.14.1", + "@types/json-schema": "^7.0.11", + "@types/urijs": "^1.19.19", + "ajv": "^8.11.0", + "ajv-errors": "^3.0.0", + "ajv-formats": "^2.1.1", + "avsc": "^5.7.5", + "js-yaml": "^4.1.0", + "jsonpath-plus": "^7.2.0", + "node-fetch": "2.6.7", + "ramldt2jsonschema": "^1.2.3", + "webapi-parser": "^0.5.0" + } + }, + "@asyncapi/parserV1": { + "version": "npm:@asyncapi/parser@1.17.2", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-1.17.2.tgz", + "integrity": "sha512-xI9GShIuQVfBz5IZ2c9h6cxcPTnfzI1fch6kYL+nF68Q3U669Un0K0TSeyEa57EfhTap53UIRTj7gFIM9QZTNQ==", + "dev": true, "requires": { "@apidevtools/json-schema-ref-parser": "^9.0.6", - "@asyncapi/specs": "^3.2.0", + "@asyncapi/specs": "^4.0.1", "@fmvilas/pseudo-yaml-ast": "^0.3.1", "ajv": "^6.10.1", "js-yaml": "^3.13.1", @@ -17436,6 +19270,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -17447,6 +19282,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -17455,6 +19291,7 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -17463,9 +19300,12 @@ } }, "@asyncapi/specs": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-3.2.0.tgz", - "integrity": "sha512-4tzJfhAk5IetJyUDzxl6oQ5oNDnX8+Q7kM62eksONdhJlfU9KYzx/mUeQrbHTFKBROi6yKXcYD842+ahICk8EA==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-4.0.1.tgz", + "integrity": "sha512-3sKw49Y/TeKT/FO2YX/DlyOekytko5KFwNJ3zMPmai+pyhz3gux2p5zV5kGJRCwApI1gX0GJw6p15a6CZgg01w==", + "requires": { + "@types/json-schema": "^7.0.11" + } }, "@babel/code-frame": { "version": "7.15.8", @@ -17967,6 +19807,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/@fmvilas/pseudo-yaml-ast/-/pseudo-yaml-ast-0.3.1.tgz", "integrity": "sha512-8OAB74W2a9M3k9bjYD8AjVXkX+qO8c0SqNT5HlgOqx7AxSw8xdksEcZp7gFtfi+4njSxT6+76ZR+1ubjAwQHOg==", + "dev": true, "requires": { "yaml-ast-parser": "0.0.43" } @@ -17988,6 +19829,61 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "@hyperjump/json": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@hyperjump/json/-/json-0.1.0.tgz", + "integrity": "sha512-jWsAOHjweWhi0UEBCN57YZzyTt76Z6Fm/OJXOfNBJbEZt569AcTRsjv6Dqj5t4gQhW9td72oquiyaVp9oHbhBQ==", + "requires": { + "@hyperjump/json-pointer": "^0.9.2", + "moo": "^0.5.1" + } + }, + "@hyperjump/json-pointer": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/@hyperjump/json-pointer/-/json-pointer-0.9.6.tgz", + "integrity": "sha512-3szMJLfz+1wtfPHnGi1sHzwFfFdZqIZLCCYtaD47vLZMAQCbtoBRVZn44jJgIQ6v37+8fom5rsxSSIMKWi0zbg==", + "requires": { + "just-curry-it": "^5.2.1" + } + }, + "@hyperjump/json-schema": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/@hyperjump/json-schema/-/json-schema-0.23.2.tgz", + "integrity": "sha512-/m5emi8ruTGXxrsy6Pik4ipsjdUZVGePzqwm36oWtEB1DcExHnGheW/BDDBGrGZHdIqFV2PMTIfXjmbg7h8xQQ==", + "requires": { + "@hyperjump/json-schema-core": "^0.28.0", + "fastest-stable-stringify": "^2.0.2" + } + }, + "@hyperjump/json-schema-core": { + "version": "0.28.5", + "resolved": "https://registry.npmjs.org/@hyperjump/json-schema-core/-/json-schema-core-0.28.5.tgz", + "integrity": "sha512-+f5P3oHYCQru3s+Ha+E10rIyEvyK0Hfa2oj3+cDoGaVMbT4Jg5TgCoIM7B5rl3t3KRA7EOmrLjKFGeLi5yd1pg==", + "requires": { + "@hyperjump/json": "^0.1.0", + "@hyperjump/json-pointer": "^0.9.4", + "@hyperjump/pact": "^0.2.3", + "content-type": "^1.0.4", + "node-fetch": "^2.6.5", + "pubsub-js": "^1.9.4", + "uri-js": "^4.4.1" + } + }, + "@hyperjump/pact": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@hyperjump/pact/-/pact-0.2.4.tgz", + "integrity": "sha512-BGmyLaUSCMVyHrwXr67rMxgiQHPHwcmVCjROoY8q232EpMz9d9aFCkgGhdx//yEfHM7zgsm0zZ8RD/F89uPySg==", + "requires": { + "just-curry-it": "^3.1.0" + }, + "dependencies": { + "just-curry-it": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/just-curry-it/-/just-curry-it-3.2.1.tgz", + "integrity": "sha512-Q8206k8pTY7krW32cdmPsP+DqqLgWx/hYPSj9/+7SYqSqz7UuwPbfSe07lQtvuuaVyiSJveXk0E5RydOuWwsEg==" + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -18186,6 +20082,14 @@ } } }, + "@jest/create-cache-key-function": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-27.5.1.tgz", + "integrity": "sha512-dmH1yW+makpTSURTy8VzdUwFnfQh1G8R+DxO2Ho2FFmBbKFEVm+3jWdvFhE2VqB/LATCTokkP0dotjyQyw5/AQ==", + "requires": { + "@jest/types": "^27.5.1" + } + }, "@jest/environment": { "version": "27.2.5", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.2.5.tgz", @@ -18417,10 +20321,9 @@ } }, "@jest/types": { - "version": "27.2.5", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.2.5.tgz", - "integrity": "sha512-nmuM4VuDtCZcY+eTpw+0nvstwReMsjPoj7ZR80/BbixulhLaiX+fbv8oeLW8WZlJMcsGQsTmMKT/iTZu1Uy/lQ==", - "dev": true, + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", @@ -18433,7 +20336,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -18442,7 +20344,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18452,7 +20353,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -18460,20 +20360,17 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -18485,6 +20382,18 @@ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, + "@jsep-plugin/regex": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.3.tgz", + "integrity": "sha512-XfZgry4DwEZvSFtS/6Y+R48D7qJYJK6R9/yJFyUFHCIUMEEHuJ4X95TDgJp5QkmzfLYvapMPzskV5HpIDrREug==", + "requires": {} + }, + "@jsep-plugin/ternary": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@jsep-plugin/ternary/-/ternary-1.1.3.tgz", + "integrity": "sha512-qtLGzCNzPVJ3kdH6/zoLWDPjauHIKiLSBAR71Wa0+PWvGA8wODUQvRgxtpUA5YqAYL3CQ8S4qXhd/9WuWTZirg==", + "requires": {} + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -18635,6 +20544,14 @@ "@octokit/openapi-types": "^11.2.0" } }, + "@openapi-contrib/openapi-schema-to-json-schema": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@openapi-contrib/openapi-schema-to-json-schema/-/openapi-schema-to-json-schema-3.2.0.tgz", + "integrity": "sha512-Gj6C0JwCr8arj0sYuslWXUBSP/KnUlEGnPW4qxlXvAl543oaNQgMgIgkQUA6vs5BCCvwTEiL8m/wdWzfl4UvSw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, "@semantic-release/commit-analyzer": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-9.0.2.tgz", @@ -18737,6 +20654,435 @@ "@sinonjs/commons": "^1.7.0" } }, + "@stoplight/better-ajv-errors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.3.tgz", + "integrity": "sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==", + "requires": { + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + } + }, + "@stoplight/json": { + "version": "3.20.1", + "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.20.1.tgz", + "integrity": "sha512-FXfud+uWgIj1xv6nUO9WnmgmnVikaxJcbtR4XQt4C42n5c2qua3U05Z/3B57hP5TJRSj+tpn9ID6/bFeyYYlEg==", + "requires": { + "@stoplight/ordered-object-literal": "^1.0.3", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^13.6.0", + "jsonc-parser": "~2.2.1", + "lodash": "^4.17.21", + "safe-stable-stringify": "^1.1" + }, + "dependencies": { + "safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + } + } + }, + "@stoplight/json-ref-readers": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@stoplight/json-ref-readers/-/json-ref-readers-1.2.2.tgz", + "integrity": "sha512-nty0tHUq2f1IKuFYsLM4CXLZGHdMn+X/IwEUIpeSOXt0QjMUbL0Em57iJUDzz+2MkWG83smIigNZ3fauGjqgdQ==", + "requires": { + "node-fetch": "^2.6.0", + "tslib": "^1.14.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@stoplight/json-ref-resolver": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@stoplight/json-ref-resolver/-/json-ref-resolver-3.1.5.tgz", + "integrity": "sha512-uaKLITor7UF+JBtI84zs3aOWM0L79zp7w9TrBTwPtx5SLbaQQ4HadDKgX5yhFOLMApLdhwhiftF4c0GFanOxGg==", + "requires": { + "@stoplight/json": "^3.17.0", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^12.3.0 || ^13.0.0", + "@types/urijs": "^1.19.19", + "dependency-graph": "~0.11.0", + "fast-memoize": "^2.5.2", + "immer": "^9.0.6", + "lodash": "^4.17.21", + "tslib": "^2.3.1", + "urijs": "^1.19.11" + } + }, + "@stoplight/ordered-object-literal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.4.tgz", + "integrity": "sha512-OF8uib1jjDs5/cCU+iOVy+GJjU3X7vk/qJIkIJFqwmlJKrrtijFmqwbu8XToXrwTYLQTP+Hebws5gtZEmk9jag==" + }, + "@stoplight/path": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@stoplight/path/-/path-1.3.2.tgz", + "integrity": "sha512-lyIc6JUlUA8Ve5ELywPC8I2Sdnh1zc1zmbYgVarhXIp9YeAB0ReeqmGEOWNtlHkbP2DAA1AL65Wfn2ncjK/jtQ==" + }, + "@stoplight/spectral-core": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.16.0.tgz", + "integrity": "sha512-W/NG+wV2UffwLExboqEa04/JbjGhiSTOl7GghLWYP4NKxZGaO6karP6fIxRBOnm34n1qyoZv9thsjSe92MWcDw==", + "requires": { + "@stoplight/better-ajv-errors": "1.0.3", + "@stoplight/json": "~3.20.1", + "@stoplight/path": "1.3.2", + "@stoplight/spectral-parsers": "^1.0.0", + "@stoplight/spectral-ref-resolver": "^1.0.0", + "@stoplight/spectral-runtime": "^1.0.0", + "@stoplight/types": "~13.6.0", + "@types/es-aggregate-error": "^1.0.2", + "@types/json-schema": "^7.0.11", + "ajv": "^8.6.0", + "ajv-errors": "~3.0.0", + "ajv-formats": "~2.1.0", + "es-aggregate-error": "^1.0.7", + "jsonpath-plus": "7.1.0", + "lodash": "~4.17.21", + "lodash.topath": "^4.5.2", + "minimatch": "3.1.2", + "nimma": "0.2.2", + "pony-cause": "^1.0.0", + "simple-eval": "1.0.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "@stoplight/types": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.6.0.tgz", + "integrity": "sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ==", + "requires": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + } + }, + "jsonpath-plus": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.1.0.tgz", + "integrity": "sha512-gTaNRsPWO/K2KY6MrqaUFClF9kmuM6MFH5Dhg1VYDODgFbByw1yb7xu3hrViE/sz+dGOeMWgCzwUwQtAnCTE9g==" + } + } + }, + "@stoplight/spectral-formats": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.4.0.tgz", + "integrity": "sha512-j9VQukDzgqDSi26rK9LqsbXrqtkeIsPSPgEf5/sxRsmeF2bwWUhSjYXgYin4flSZ7owFZjZWQ3o0Qq3iApi2JQ==", + "requires": { + "@stoplight/json": "^3.17.0", + "@stoplight/spectral-core": "^1.8.0", + "@types/json-schema": "^7.0.7", + "tslib": "^2.3.1" + } + }, + "@stoplight/spectral-functions": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.7.2.tgz", + "integrity": "sha512-f+61/FtIkQeIo+a269CeaeqjpyRsgDyIk6DGr7iS4hyuk1PPk7Uf6MNRDs9FEIBh7CpdEJ+HSHbMLwgpymWTIw==", + "requires": { + "@stoplight/better-ajv-errors": "1.0.3", + "@stoplight/json": "^3.17.1", + "@stoplight/spectral-core": "^1.7.0", + "@stoplight/spectral-formats": "^1.0.0", + "@stoplight/spectral-runtime": "^1.1.0", + "ajv": "^8.6.3", + "ajv-draft-04": "~1.0.0", + "ajv-errors": "~3.0.0", + "ajv-formats": "~2.1.0", + "lodash": "~4.17.21", + "tslib": "^2.3.0" + } + }, + "@stoplight/spectral-parsers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.2.tgz", + "integrity": "sha512-ZQXknJ+BM5Re4Opj4cgVlHgG2qyOk/wznKJq3Vf1qsBEg2CNzN0pJmSB0deRqW0kArqm44qpb8c+cz3F2rgMtw==", + "requires": { + "@stoplight/json": "~3.20.1", + "@stoplight/types": "^13.6.0", + "@stoplight/yaml": "~4.2.3", + "tslib": "^2.3.1" + } + }, + "@stoplight/spectral-ref-resolver": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-ref-resolver/-/spectral-ref-resolver-1.0.3.tgz", + "integrity": "sha512-pj+bH4SH8hcWlnV787WD7P0/En7LA3EfZMvG1JUGMW/7bFd9AaZZXNkh5j0ve8qnPlwP8F4SH/2Cnr1tXOXCVw==", + "requires": { + "@stoplight/json-ref-readers": "1.2.2", + "@stoplight/json-ref-resolver": "~3.1.5", + "@stoplight/spectral-runtime": "^1.1.2", + "dependency-graph": "0.11.0", + "tslib": "^2.3.1" + } + }, + "@stoplight/spectral-rulesets": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.14.1.tgz", + "integrity": "sha512-tn6a5fYPFDwEY+/YyK/hcq2gcR5nSIBt7l+JGELb/2RdTzD5ikj2mfl2ua3uxbqOZytftFoOX5ewGZ0qQNrudw==", + "requires": { + "@asyncapi/specs": "^3.2.0", + "@stoplight/better-ajv-errors": "1.0.3", + "@stoplight/json": "^3.17.0", + "@stoplight/spectral-core": "^1.8.1", + "@stoplight/spectral-formats": "^1.4.0", + "@stoplight/spectral-functions": "^1.5.1", + "@stoplight/spectral-runtime": "^1.1.1", + "@stoplight/types": "^13.6.0", + "@types/json-schema": "^7.0.7", + "ajv": "^8.8.2", + "ajv-formats": "~2.1.0", + "json-schema-traverse": "^1.0.0", + "lodash": "~4.17.21", + "tslib": "^2.3.0" + }, + "dependencies": { + "@asyncapi/specs": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-3.2.1.tgz", + "integrity": "sha512-FO+EteK+Gk3zwumrBw6frpp9cJ4oQL5++hBBpfM81w16e9KaiA4sKrzvQsvVjifoZZHNvVEX4D2zoz9i8CLccQ==" + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, + "@stoplight/spectral-runtime": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-runtime/-/spectral-runtime-1.1.2.tgz", + "integrity": "sha512-fr5zRceXI+hrl82yAVoME+4GvJie8v3wmOe9tU+ZLRRNonizthy8qDi0Z/z4olE+vGreSDcuDOZ7JjRxFW5kTw==", + "requires": { + "@stoplight/json": "^3.17.0", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^12.3.0", + "abort-controller": "^3.0.0", + "lodash": "^4.17.21", + "node-fetch": "^2.6.7", + "tslib": "^2.3.1" + }, + "dependencies": { + "@stoplight/types": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-12.5.0.tgz", + "integrity": "sha512-dwqYcDrGmEyUv5TWrDam5TGOxU72ufyQ7hnOIIDdmW5ezOwZaBFoR5XQ9AsH49w7wgvOqB2Bmo799pJPWnpCbg==", + "requires": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + } + } + } + }, + "@stoplight/types": { + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.8.0.tgz", + "integrity": "sha512-5glKswz7y9aACh+a+JegID+4xX//4TsIdv7iPl29hWnOoWrnlPbg3Gjc4nYUXXgMSaSlSsA15JU/0+rE89fR4A==", + "requires": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + } + }, + "@stoplight/yaml": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.3.tgz", + "integrity": "sha512-Mx01wjRAR9C7yLMUyYFTfbUf5DimEpHMkRDQ1PKLe9dfNILbgdxyrncsOXM3vCpsQ1Hfj4bPiGl+u4u6e9Akqw==", + "requires": { + "@stoplight/ordered-object-literal": "^1.0.1", + "@stoplight/types": "^13.0.0", + "@stoplight/yaml-ast-parser": "0.0.48", + "tslib": "^2.2.0" + } + }, + "@stoplight/yaml-ast-parser": { + "version": "0.0.48", + "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.48.tgz", + "integrity": "sha512-sV+51I7WYnLJnKPn2EMWgS4EUfoP4iWEbrWwbXsj0MZCB/xOK8j6+C9fntIdOM50kpx45ZLC3s6kwKivWuqvyg==" + }, + "@swc/core": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.5.tgz", + "integrity": "sha512-H5YNI9rCViudhEmu9g/Yc8ai6k5/pfy+ItYns0SZ+iSZen+bgWeGb+9p4KRQhzNNC8Lfkfw+ENHzSwZltpWG6Q==", + "requires": { + "@swc/core-android-arm-eabi": "1.3.5", + "@swc/core-android-arm64": "1.3.5", + "@swc/core-darwin-arm64": "1.3.5", + "@swc/core-darwin-x64": "1.3.5", + "@swc/core-freebsd-x64": "1.3.5", + "@swc/core-linux-arm-gnueabihf": "1.3.5", + "@swc/core-linux-arm64-gnu": "1.3.5", + "@swc/core-linux-arm64-musl": "1.3.5", + "@swc/core-linux-x64-gnu": "1.3.5", + "@swc/core-linux-x64-musl": "1.3.5", + "@swc/core-win32-arm64-msvc": "1.3.5", + "@swc/core-win32-ia32-msvc": "1.3.5", + "@swc/core-win32-x64-msvc": "1.3.5" + } + }, + "@swc/core-android-arm-eabi": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.3.5.tgz", + "integrity": "sha512-gIq3fuXiRMtVhTf2ZQ9Z6JeuqvL30JOM0L+S6zAZD4v8lpGJ1ejpw7rghpAsGSG9Qc9oaNjx5yayTg3z/EtBzA==", + "optional": true, + "requires": { + "@swc/wasm": "1.2.122" + } + }, + "@swc/core-android-arm64": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.3.5.tgz", + "integrity": "sha512-SsRA6AhNZK8YXBbv7DAp5Zgv4tOWvPJlEBoOZ0uLIot7oYghWvSVs3jOgEzJSbQLU5U7ad6Q6boBdg0Q/kb2Ew==", + "optional": true, + "requires": { + "@swc/wasm": "1.2.130" + }, + "dependencies": { + "@swc/wasm": { + "version": "1.2.130", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.130.tgz", + "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", + "optional": true + } + } + }, + "@swc/core-darwin-arm64": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.5.tgz", + "integrity": "sha512-Jyem+f3/aTKJTRzyvdSfYS358jo7245g7nWmwmhQMgZI3/z2VcYHpIIYOi+dgsBaMRevK9tbsW0TSx805Njzjw==", + "optional": true + }, + "@swc/core-darwin-x64": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.5.tgz", + "integrity": "sha512-zW1tfS000RlHcqKp1HJK5vXBR0/AHw74qzOK0uh/G1cTczFDX2Hep4IuOxSJ1+7Zx9oFEOKSEY0lPXYbDAlF2w==", + "optional": true + }, + "@swc/core-freebsd-x64": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.3.5.tgz", + "integrity": "sha512-H2f0NkfqYDC6+vJO6wSBwiGnnR/cK9AQx574izPw3Utmb28zC+FOPAY63QLA/orNHjwHa6B6AuVDNwYuKUrRHQ==", + "optional": true, + "requires": { + "@swc/wasm": "1.2.130" + }, + "dependencies": { + "@swc/wasm": { + "version": "1.2.130", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.130.tgz", + "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", + "optional": true + } + } + }, + "@swc/core-linux-arm-gnueabihf": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.5.tgz", + "integrity": "sha512-PvuhjUCsNQDtwSSXWmmF6tU8jnAcFVRZt6bBNltvPW48oHNmIq9lEZ+hJTSPvqqxLvi9W7HG5ADzsTAaciKeRw==", + "optional": true, + "requires": { + "@swc/wasm": "1.2.130" + }, + "dependencies": { + "@swc/wasm": { + "version": "1.2.130", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.130.tgz", + "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", + "optional": true + } + } + }, + "@swc/core-linux-arm64-gnu": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.5.tgz", + "integrity": "sha512-NQ1LVrIvAsSwSoKO6DzHQMxzpvo17v/2LREqBiaNuCwDyYg2yFdgUdVW4FcbrdBK4MurRA2HFZZ/rt5DqAg3+A==", + "optional": true + }, + "@swc/core-linux-arm64-musl": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.5.tgz", + "integrity": "sha512-DDcM3ciJRBBjyN7qqw/AEEFh61YjiuxOcZ5SqYR0wyfroqOFX1+5JtCGJ9mU2MZ4Vfmxb1v5IFoQ3nfgJDcd8g==", + "optional": true + }, + "@swc/core-linux-x64-gnu": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.5.tgz", + "integrity": "sha512-AJR0J+b3jMmXuIxqhgrX/7vworHjciUPZuoyY2OrIhSXwMPVbWfb72h9oQdMbARfodTFLVJGQqy2Pij67+C0GQ==", + "optional": true + }, + "@swc/core-linux-x64-musl": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.5.tgz", + "integrity": "sha512-+Ig/rJ/GOZyQgCO72PFR+oJYUee0zQRsd6fpeuE66rn8P07a26WY1ZfMGw8miWcURccjDgAc1XCwVn6wa/OTCw==", + "optional": true + }, + "@swc/core-win32-arm64-msvc": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.5.tgz", + "integrity": "sha512-9Go5jiGWToT+00/J26E92n/JIHqG2wcaw79Z1+Z7GHrrm5TeL0VMyTMGLMeGFvtje/j+Lv0y4XKed+dKnRvc5w==", + "optional": true, + "requires": { + "@swc/wasm": "1.2.130" + }, + "dependencies": { + "@swc/wasm": { + "version": "1.2.130", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.130.tgz", + "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", + "optional": true + } + } + }, + "@swc/core-win32-ia32-msvc": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.5.tgz", + "integrity": "sha512-AIeD5uKVkvXTAbKAwyPFubFrXmQR77PNun59DHZWtRpxgOcHqK6xug9DfUSfc2zMw/ftEe9kNruUUS96023jfQ==", + "optional": true, + "requires": { + "@swc/wasm": "1.2.130" + }, + "dependencies": { + "@swc/wasm": { + "version": "1.2.130", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.130.tgz", + "integrity": "sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==", + "optional": true + } + } + }, + "@swc/core-win32-x64-msvc": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.5.tgz", + "integrity": "sha512-GtrAkUo5xVTogwTDH9Zms7LELdTKyRll+K9o87P+YOEizCUvA0BPE1N4mu+ZqsI/dv56g2N4gNCD8RVLH3HekQ==", + "optional": true + }, + "@swc/jest": { + "version": "0.2.23", + "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.23.tgz", + "integrity": "sha512-ZLj17XjHbPtNsgqjm83qizENw05emLkKGu3WuPUttcy9hkngl0/kcc7fDbcSBpADS0GUtsO+iKPjZFWVAtJSlA==", + "requires": { + "@jest/create-cache-key-function": "^27.4.2", + "jsonc-parser": "^3.2.0" + }, + "dependencies": { + "jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + } + } + }, + "@swc/wasm": { + "version": "1.2.122", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.2.122.tgz", + "integrity": "sha512-sM1VCWQxmNhFtdxME+8UXNyPNhxNu7zdb6ikWpz0YKAQQFRGT5ThZgJrubEpah335SUToNg8pkdDF7ibVCjxbQ==", + "optional": true + }, "@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -18804,6 +21150,14 @@ "@babel/types": "^7.3.0" } }, + "@types/es-aggregate-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/es-aggregate-error/-/es-aggregate-error-1.0.2.tgz", + "integrity": "sha512-erqUpFXksaeR2kejKnhnjZjbFxUpGZx4Z7ydNL9ie8tEhXPiZTsLeUDJ6aR1F8j5wWUAtOAQWUqkc7givBJbBA==", + "requires": { + "@types/node": "*" + } + }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -18816,14 +21170,12 @@ "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", - "dev": true + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==" }, "@types/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, "requires": { "@types/istanbul-lib-coverage": "*" } @@ -18832,7 +21184,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, "requires": { "@types/istanbul-lib-report": "*" } @@ -18848,9 +21199,9 @@ } }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, "@types/json5": { "version": "0.0.29", @@ -18858,28 +21209,6 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, - "@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true - }, - "@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "requires": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true - }, "@types/minimist": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", @@ -18921,11 +21250,15 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "@types/urijs": { + "version": "1.19.19", + "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.19.tgz", + "integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg==" + }, "@types/yargs": { "version": "16.0.4", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, "requires": { "@types/yargs-parser": "*" } @@ -18933,8 +21266,17 @@ "@types/yargs-parser": { "version": "20.2.1", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", - "dev": true + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" + }, + "@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*" + } }, "@typescript-eslint/eslint-plugin": { "version": "4.33.0", @@ -19025,6 +21367,14 @@ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -19074,10 +21424,9 @@ } }, "ajv": { - "version": "8.6.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", - "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", - "dev": true, + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -19088,11 +21437,41 @@ "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" } } }, + "ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "requires": {} + }, + "ajv-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", + "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", + "requires": {} + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + } + }, + "alterschema": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/alterschema/-/alterschema-1.1.1.tgz", + "integrity": "sha512-h32BfJpnx3jPU8rdC/CW6iA0Ck+6jIVhj880HULD1cZrBLMLmwKCDd4xn6ykH7pU0quvLIi7LV0I0K4F3A418w==", + "requires": { + "@hyperjump/json-schema": "^0.23.2", + "json-e": "^4.4.3", + "lodash": "^4.17.21", + "object-hash": "^3.0.0" + } + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -19195,9 +21574,9 @@ "dev": true }, "array-back": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", - "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.0.tgz", + "integrity": "sha512-mixVv03GOOn/ubHE4STQ+uevX42ETdk0JoMVEjNkSOCT7WgERh7C8/+NyhWYNpE3BN69pxFyJIBcF7CxWz/+4A==", "dev": true }, "array-ify": { @@ -19248,6 +21627,11 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, + "astring": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.3.tgz", + "integrity": "sha512-sRpyiNrx2dEYIMmUXprS8nlpRg2Drs8m9ElX9vVEXaCB4XEAJhKfs7IcX0IwShjuOAjLR6wzIrgoptz1n19i1A==" + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -19263,6 +21647,11 @@ "gulp-header": "^1.7.1" } }, + "avsc": { + "version": "5.7.7", + "resolved": "https://registry.npmjs.org/avsc/-/avsc-5.7.7.tgz", + "integrity": "sha512-9cYNccliXZDByFsFliVwk5GvTq058Fj513CiR4E60ndDwmuXzTJEp/Bp8FyuRmGyYupLjHLs+JA9/CBoVS4/NQ==" + }, "babel-jest": { "version": "27.2.5", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.2.5.tgz", @@ -19390,12 +21779,29 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, "before-after-hook": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", "dev": true }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -19463,6 +21869,22 @@ "node-int64": "^0.4.0" } }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -19492,7 +21914,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -19536,9 +21957,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001267", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001267.tgz", - "integrity": "sha512-r1mjTzAuJ9W8cPBGbbus8E0SKcUP7gn03R14Wk8FlAlqhH9hroy9nLqmpuXlfKEw/oILW+FGz47ipXV2O7x8lg==", + "version": "1.0.30001370", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001370.tgz", + "integrity": "sha512-3PDmaP56wz/qz7G508xzjx8C+MC2qEm4SYhSEzC9IBROo+dGXFWRuaXkWti0A9tuI00g+toiriVqxtWMgl350g==", "dev": true }, "capital-case": { @@ -19606,6 +22027,12 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, "ci-info": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", @@ -19647,13 +22074,13 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "code-error-fragment": { "version": "0.0.230", "resolved": "https://registry.npmjs.org/code-error-fragment/-/code-error-fragment-0.0.230.tgz", - "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==" + "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==", + "dev": true }, "coffee-script": { "version": "1.12.7", @@ -19854,6 +22281,117 @@ "source-map": "^0.6.1" } }, + "concurrently": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.6.0.tgz", + "integrity": "sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "date-fns": "^2.29.1", + "lodash": "^4.17.21", + "rxjs": "^7.0.0", + "shell-quote": "^1.7.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^17.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } + } + }, "config-master": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", @@ -19881,6 +22419,11 @@ "upper-case": "^2.0.2" } }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, "conventional-changelog-angular": { "version": "5.0.13", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", @@ -20001,6 +22544,15 @@ "cross-spawn": "^7.0.1" } }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dev": true, + "requires": { + "node-fetch": "2.6.7" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -20080,6 +22632,12 @@ } } }, + "date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", + "dev": true + }, "dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -20150,12 +22708,12 @@ "dev": true }, "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "requires": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "del": { @@ -20191,6 +22749,11 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==" + }, "deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -20203,6 +22766,12 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, + "devtools-protocol": { + "version": "0.0.1019158", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1019158.tgz", + "integrity": "sha512-wvq+KscQ7/6spEV7czhnZc9RM/woz1AY+/Vpd8/h2HFMwJSdTliu7f/yr1A6vDdJfKICZsShqsYpEQbdhg8AFQ==", + "dev": true + }, "diacritics-map": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz", @@ -20230,25 +22799,31 @@ } }, "dmd": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.1.0.tgz", - "integrity": "sha512-0zQIJ873gay1scCTFZvHPWM9mVJBnaylB2NQDI8O9u8O32m00Jb6uxDKexZm8hjTRM7RiWe0FJ32pExHoXdwoQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.0.0.tgz", + "integrity": "sha512-PwWZlqZnJPETwqZZ70haRa+UDZcD5jeBD3ywW1Kf+jYYv0MHu/S7Ri9jsSoeTMwkcMVW9hXOMA1IZUMEufBhOg==", "dev": true, "requires": { - "array-back": "^6.2.2", + "array-back": "^5.0.0", "cache-point": "^2.0.0", - "common-sequence": "^2.0.2", - "file-set": "^4.0.2", + "common-sequence": "^2.0.0", + "file-set": "^4.0.1", "handlebars": "^4.7.7", - "marked": "^4.0.12", + "marked": "^2.0.0", "object-get": "^2.1.1", - "reduce-flatten": "^3.0.1", + "reduce-flatten": "^3.0.0", "reduce-unique": "^2.0.1", "reduce-without": "^1.0.1", "test-value": "^3.0.0", - "walk-back": "^5.1.0" + "walk-back": "^5.0.0" }, "dependencies": { + "array-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "dev": true + }, "reduce-flatten": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.1.tgz", @@ -20378,9 +22953,9 @@ } }, "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", "dev": true }, "env-ci": { @@ -20437,38 +23012,108 @@ } }, "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", + "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.2", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", + "is-callable": "^1.2.6", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-aggregate-error": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.9.tgz", + "integrity": "sha512-fvnX40sb538wdU6r4s35cq4EY6Lr09Upj40BEVem4LEsuW8XgQep9yD5Q1U2KftokNp1rWODFJ2qwZSsAjFpbg==", + "requires": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "function-bind": "^1.1.1", + "functions-have-names": "^1.2.3", + "get-intrinsic": "^1.1.3", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz", + "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "unbox-primitive": "^1.0.2" + } + }, + "string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + } } }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -20721,9 +23366,9 @@ } }, "eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", "dev": true, "requires": {} }, @@ -20859,6 +23504,17 @@ "eslint-rule-documentation": ">=1.0.0", "prettier": "^2.2.1", "svg-element-attributes": "^1.3.1" + }, + "dependencies": { + "eslint-plugin-prettier": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz", + "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + } } }, "eslint-plugin-i18n-text": { @@ -20922,9 +23578,9 @@ "dev": true }, "eslint-plugin-prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz", - "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", "dev": true, "requires": { "prettier-linter-helpers": "^1.0.0" @@ -21047,6 +23703,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -21143,6 +23804,29 @@ "is-extendable": "^0.1.0" } }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -21178,6 +23862,16 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-memoize": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", + "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==" + }, + "fastest-stable-stringify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz", + "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==" + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -21196,6 +23890,15 @@ "bser": "2.1.1" } }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -21352,6 +24055,12 @@ } } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -21384,8 +24093,18 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } }, "functional-red-black-tree": { "version": "1.0.1", @@ -21393,6 +24112,11 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -21405,14 +24129,13 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" } }, "get-package-type": { @@ -21431,7 +24154,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, "requires": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -21541,6 +24263,14 @@ } } }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "requires": { + "define-properties": "^1.1.3" + } + }, "globby": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", @@ -21555,6 +24285,14 @@ "slash": "^3.0.0" } }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", @@ -21564,7 +24302,8 @@ "grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true }, "gray-matter": { "version": "2.1.1", @@ -21676,16 +24415,14 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } }, "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" }, "has-flag": { "version": "3.0.0", @@ -21693,17 +24430,23 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "has-tostringtag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -21783,12 +24526,23 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, "ignore": { "version": "5.1.8", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, + "immer": { + "version": "9.0.16", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", + "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==" + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -21862,7 +24616,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, "requires": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -21889,7 +24642,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, "requires": { "has-bigints": "^1.0.1" } @@ -21898,7 +24650,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -21911,10 +24662,9 @@ "dev": true }, "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" }, "is-ci": { "version": "3.0.0", @@ -21938,7 +24688,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -21976,10 +24725,9 @@ } }, "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" }, "is-number": { "version": "7.0.0", @@ -21988,10 +24736,9 @@ "dev": true }, "is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", - "dev": true, + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "requires": { "has-tostringtag": "^1.0.0" } @@ -22036,17 +24783,18 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" } }, "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "requires": { + "call-bind": "^1.0.2" + } }, "is-stream": { "version": "2.0.1", @@ -22058,7 +24806,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -22067,7 +24814,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -22088,12 +24834,11 @@ "dev": true }, "is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "requires": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2" } }, "isarray": { @@ -23486,35 +26231,34 @@ } }, "js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.1.tgz", + "integrity": "sha512-KrPTolcw6RocpYjdC7pL7v62e55q7qOMHvLX1UCLc5AAS8qeJ6nukarEJAF2KL2PZxlbGueEbINqZR2bDe/gUw==", "dev": true, "requires": { - "xmlcreate": "^2.0.4" + "xmlcreate": "^2.0.3" } }, "jsdoc": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", - "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz", + "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==", "dev": true, "requires": { "@babel/parser": "^7.9.4", - "@types/markdown-it": "^12.2.3", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", + "js2xmlparser": "^4.0.1", "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", + "markdown-it": "^10.0.0", + "markdown-it-anchor": "^5.2.7", + "marked": "^2.0.3", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", "taffydb": "2.6.2", - "underscore": "~1.13.2" + "underscore": "~1.13.1" }, "dependencies": { "escape-string-regexp": { @@ -23663,12 +26407,25 @@ } } }, + "jsep": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.8.tgz", + "integrity": "sha512-qofGylTGgYj9gZFsHuyWAN4jr35eJ66qJCK4eKDnldohuUoQFbU3iZn2zjvEbd9wOAhP9Wx5DsAAduTyE1PSWQ==" + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-e": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/json-e/-/json-e-4.4.3.tgz", + "integrity": "sha512-G2tp4fkEzN6hlya4J9dNaRbIxxTW+Lqu+An40E36SNZYexIGPPuNU30VuS0eZam4AieV0R+fvRxZrVFPJEvUbA==", + "requires": { + "json-stable-stringify-without-jsonify": "^1.0.1" + } + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -23681,6 +26438,37 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "json-schema-migrate": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-0.2.0.tgz", + "integrity": "sha512-dq4/oHWmtw/+0ytnXsDqVn+VsVweTEmzm5jLgguPn9BjSzn6/q58ZiZx3BHiQyJs612f0T5Z+MrUEUUY5DHsRg==", + "requires": { + "ajv": "^5.0.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==" + } + } + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -23689,8 +26477,7 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" }, "json-stringify-safe": { "version": "5.0.1", @@ -23702,6 +26489,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/json-to-ast/-/json-to-ast-2.1.0.tgz", "integrity": "sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==", + "dev": true, "requires": { "code-error-fragment": "0.0.230", "grapheme-splitter": "^1.0.4" @@ -23716,6 +26504,11 @@ "minimist": "^1.2.0" } }, + "jsonc-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz", + "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==" + }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -23732,6 +26525,16 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, + "jsonpath-plus": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz", + "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==" + }, + "jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -23742,6 +26545,11 @@ "through": ">=2.2.7 <3" } }, + "just-curry-it": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/just-curry-it/-/just-curry-it-5.2.1.tgz", + "integrity": "sha512-M8qhhO9WVNc3yZgf3qfiNxMIsQlHqFHJ3vMI8N/rkp852h1utOB/N3ebS8jeXGAwYSbkdd0K6zP9eZneUtjHwA==" + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -23775,8 +26583,7 @@ "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" }, "levn": { "version": "0.4.1", @@ -23795,9 +26602,9 @@ "dev": true }, "linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", "dev": true, "requires": { "uc.micro": "^1.0.1" @@ -23871,8 +26678,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash._reinterpolate": { "version": "3.0.0", @@ -23895,7 +26701,8 @@ "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true }, "lodash.escaperegexp": { "version": "4.1.2", @@ -23986,6 +26793,11 @@ "lodash._reinterpolate": "^3.0.0" } }, + "lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", + "integrity": "sha512-1/W4dM+35DwvE/iEd1M9ekewOSTlpFekhw9mhAtrwjVqUr83/ilQiyAvmg4tVX7Unkcfl1KC+i9WdaT4B6aQcg==" + }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -24059,22 +26871,33 @@ "dev": true }, "markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", + "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", "dev": true, "requires": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^2.0.0", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + } } }, "markdown-it-anchor": { - "version": "8.6.5", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz", - "integrity": "sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz", + "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==", "dev": true, "requires": {} }, @@ -24105,9 +26928,9 @@ } }, "marked": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.0.tgz", - "integrity": "sha512-+Z6KDjSPa6/723PQYyc1axYZpYYpDnECDaU6hkaf5gqBieBkMKYReL5hteF2QizhlMbgbo8umXl/clZ67+GlsA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", + "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", "dev": true }, "marked-terminal": { @@ -24156,7 +26979,7 @@ "mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", "dev": true }, "meow": { @@ -24300,6 +27123,12 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, "mkdirp2": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", @@ -24312,6 +27141,11 @@ "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", "dev": true }, + "moo": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", + "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -24336,6 +27170,27 @@ "integrity": "sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo=", "dev": true }, + "nimma": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/nimma/-/nimma-0.2.2.tgz", + "integrity": "sha512-V52MLl7BU+tH2Np9tDrIXK8bql3MVUadnMIl/0/oZSGC9keuro0O9UUv9QKp0aMvtN8HRew4G7byY7H4eWsxaQ==", + "requires": { + "@jsep-plugin/regex": "^1.0.1", + "@jsep-plugin/ternary": "^1.0.2", + "astring": "^1.8.1", + "jsep": "^1.2.0", + "jsonpath-plus": "^6.0.1", + "lodash.topath": "^4.5.2" + }, + "dependencies": { + "jsonpath-plus": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-6.0.1.tgz", + "integrity": "sha512-EvGovdvau6FyLexFH2OeXfIITlgIbgZoAZe3usiySeaIDm5QS+A10DKNpaPBBqqRSZr2HN6HVNXxtwUAr2apEw==", + "optional": true + } + } + }, "no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -26513,17 +29368,20 @@ "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", "dev": true }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", - "dev": true + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object-to-spawn-args": { "version": "2.0.1", @@ -26532,14 +29390,13 @@ "dev": true }, "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, @@ -26757,6 +29614,12 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -26905,6 +29768,11 @@ } } }, + "pony-cause": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz", + "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==" + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -26968,12 +29836,23 @@ "sisteransi": "^1.0.5" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", "dev": true }, + "pubsub-js": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.4.tgz", + "integrity": "sha512-hJYpaDvPH4w8ZX/0Fdf9ma1AwRgU353GfbaVfPjfJQf1KxZ2iHaHl3fAUw1qlJIR5dr4F3RzjGaWohYUEyoh7A==" + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -26989,6 +29868,63 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "puppeteer": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-16.0.0.tgz", + "integrity": "sha512-FgSe21IHNHkqv1SiJiob4ANsxVujcINa4p3MaDEMyoZsocbgSgwYE0c9lnF8eoinw4id3vx4DOXwhFdOOwVlDg==", + "dev": true, + "requires": { + "cross-fetch": "3.1.5", + "debug": "4.3.4", + "devtools-protocol": "0.0.1019158", + "extract-zip": "2.0.1", + "https-proxy-agent": "5.0.1", + "pkg-dir": "4.2.0", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "rimraf": "3.0.2", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "ws": "8.8.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "ws": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "dev": true, + "requires": {} + } + } + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -27007,6 +29943,41 @@ "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true }, + "ramldt2jsonschema": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/ramldt2jsonschema/-/ramldt2jsonschema-1.2.3.tgz", + "integrity": "sha512-+wLDAV2NNv9NkfEUOYStaDu/6RYgYXeC1zLtXE+dMU/jDfjpN4iJnBGycDwFTFaIQGosOQhxph7fEX6Mpwxdug==", + "requires": { + "commander": "^5.0.0", + "js-yaml": "^3.14.0", + "json-schema-migrate": "^0.2.0", + "webapi-parser": "^0.5.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } + } + }, "randomatic": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", @@ -27209,6 +30180,16 @@ } } }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -27265,8 +30246,7 @@ "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, "requizzle": { "version": "0.2.3", @@ -27346,6 +30326,15 @@ "queue-microtask": "^1.2.2" } }, + "rxjs": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz", + "integrity": "sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -27361,6 +30350,16 @@ "ret": "~0.1.10" } }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, "safe-stable-stringify": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", @@ -27444,6 +30443,12 @@ "tempy": "^1.0.0" } }, + "marked": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.16.tgz", + "integrity": "sha512-wahonIQ5Jnyatt2fn8KqF/nIqZM8mh3oRu2+l5EANGMhu6RFjiSG52QNE2eWzFMI94HqYSgN184NurgNG6CztA==", + "dev": true + }, "npm": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/npm/-/npm-8.12.1.tgz", @@ -29220,11 +32225,16 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shell-quote": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", + "dev": true + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -29259,6 +32269,14 @@ } } }, + "simple-eval": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-eval/-/simple-eval-1.0.0.tgz", + "integrity": "sha512-kpKJR+bqTscgC0xuAl2xHN6bB12lHjC2DCUfqjAx19bQyO3R2EVLOurm3H9AUltv/uFVcSCVNc6faegR+8NYLw==", + "requires": { + "jsep": "^1.1.2" + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -29357,6 +32375,12 @@ "source-map": "^0.6.0" } }, + "spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", + "dev": true + }, "spawn-error-forwarder": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", @@ -29533,23 +32557,23 @@ } }, "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "strip-ansi": { @@ -29685,6 +32709,31 @@ "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", "dev": true }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, "temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -29796,7 +32845,8 @@ "tiny-merge-patch": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tiny-merge-patch/-/tiny-merge-patch-0.1.2.tgz", - "integrity": "sha1-Lo3tGcVuoV29OtTtXbHI5a1UTDw=" + "integrity": "sha512-NLoA//tTMBPTr0oGdq+fxnvVR0tDa8tOcG9ZGbuovGzROadZ404qOV4g01jeWa5S8MC9nAOvu5bQgCW7s8tlWQ==", + "dev": true }, "tmpl": { "version": "1.0.5", @@ -29875,6 +32925,12 @@ "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=", "dev": true }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, "trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -30066,21 +33122,30 @@ "optional": true }, "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" } }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "underscore": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", - "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", "dev": true }, "unique-string": { @@ -30128,6 +33193,11 @@ "punycode": "^2.1.0" } }, + "urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" + }, "url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -30140,6 +33210,11 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==" + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -30213,6 +33288,32 @@ "makeerror": "1.0.x" } }, + "webapi-parser": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/webapi-parser/-/webapi-parser-0.5.0.tgz", + "integrity": "sha512-fPt6XuMqLSvBz8exwX4QE1UT+pROLHa00EMDCdO0ybICduwQ1V4f7AWX4pNOpCp+x+0FjczEsOxtQU0d8L3QKw==", + "requires": { + "ajv": "6.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", + "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.1" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==" + } + } + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -30255,7 +33356,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, "requires": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -30356,9 +33456,9 @@ "dev": true }, "xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz", + "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==", "dev": true }, "xtend": { @@ -30387,7 +33487,8 @@ "yaml-ast-parser": { "version": "0.0.43", "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", - "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==" + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true }, "yargs": { "version": "16.2.0", @@ -30410,6 +33511,16 @@ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index cee0bc17d9..6d08fcf830 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@asyncapi/modelina", - "version": "0.59.9", - "description": "The Model SDK for generating data models", + "version": "1.0.0-next.43", + "description": "Library for generating data models based on inputs such as AsyncAPI, OpenAPI, or JSON Schema documents", "license": "Apache-2.0", "homepage": "https://github.com/asyncapi/modelina", "bugs": { @@ -16,7 +16,8 @@ }, "keywords": [ "asyncapi", - "documentation", + "modelina", + "model-generation", "generator", "model", "sdk" @@ -36,7 +37,10 @@ "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.0.9", "@apidevtools/swagger-parser": "^10.0.3", - "@asyncapi/parser": "^1.17.0", + "@asyncapi/parser": "^2.0.0-next-major.11", + "@swc/core": "^1.3.5", + "@swc/jest": "^0.2.23", + "alterschema": "^1.1.1", "change-case": "^4.1.2", "openapi-types": "9.3.0", "typescript-json-schema": "^0.53.0" @@ -52,17 +56,22 @@ "ajv": "^8.6.3", "conventional-changelog-conventionalcommits": "^4.6.1", "cross-env": "^7.0.3", - "eslint": "^7.32.0", + "eslint": "7.32.0", + "eslint-config-prettier": "^8.5.0", "eslint-plugin-github": "^4.3.2", + "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-security": "^1.4.0", "eslint-plugin-sonarjs": "^0.10.0", "jest": "^27.2.5", "jsdoc-to-markdown": "^7.1.0", "markdown-toc": "^1.2.0", + "puppeteer": "^16.0.0", "semantic-release": "^19.0.3", "ts-jest": "^27.0.5", "ts-node": "^10.3.0", - "typescript": "^4.4.4" + "typescript": "^4.4.4", + "@asyncapi/parserV1": "npm:@asyncapi/parser@^1.17.2", + "concurrently": "^7.5.0" }, "scripts": { "build": "tsc", @@ -73,26 +82,44 @@ "docker:build": "docker build -t asyncapi/modelina .", "docker:test": "npm run docker:build && docker run asyncapi/modelina npm run test", "docker:test:blackbox": "npm run docker:build && docker run asyncapi/modelina npm run test:blackbox", - "test": "cross-env CI=true jest --coverage --testPathIgnorePatterns ./test/blackbox --testPathIgnorePatterns ./examples/TEMPLATE", - "test:examples": "cross-env CI=true jest ./examples --testPathIgnorePatterns ./examples/TEMPLATE", - "test:blackbox": "cross-env CI=true jest ./test/blackbox", + "test": "npm run test:library && npm run test:examples", + "test:update": "npm run test:library -- -u && npm run test:examples:update", + "test:library": "cross-env CI=true jest --coverage --testPathIgnorePatterns ./test/blackbox --testPathIgnorePatterns ./examples", + "test:examples": "npm run test:examples:regular && npm run test:examples:websites", + "test:examples:update": "npm run test:examples:regular -- -u && npm run test:examples:websites -- -u", + "test:examples:regular": "cross-env CI=true jest ./examples --testPathIgnorePatterns ./examples/integrate-with-react", + "test:examples:websites": "cd ./examples/integrate-with-react && npm i && npm run test", + "test:blackbox": "concurrently --group -n csharp,go,java,javascript,python,rust,typescript,kotlin \"npm run test:blackbox:csharp\" \"npm run test:blackbox:go\" \"npm run test:blackbox:java\" \"npm run test:blackbox:javascript\" \"npm run test:blackbox:python\" \"npm run test:blackbox:rust\" \"npm run test:blackbox:typescript\" \"npm run test:blackbox:kotlin\"", + "test:blackbox:csharp": "cross-env CI=true jest ./test/blackbox/blackbox-csharp.spec.ts", + "test:blackbox:go": "cross-env CI=true jest ./test/blackbox/blackbox-go.spec.ts", + "test:blackbox:java": "cross-env CI=true jest ./test/blackbox/blackbox-java.spec.ts", + "test:blackbox:javascript": "cross-env CI=true jest ./test/blackbox/blackbox-javascript.spec.ts", + "test:blackbox:python": "cross-env CI=true jest ./test/blackbox/blackbox-python.spec.ts", + "test:blackbox:rust": "cross-env CI=true jest ./test/blackbox/blackbox-rust.spec.ts", + "test:blackbox:typescript": "cross-env CI=true jest ./test/blackbox/blackbox-typescript.spec.ts", + "test:blackbox:kotlin": "cross-env CI=true jest ./test/blackbox/blackbox-kotlin.spec.ts", "test:watch": "jest --watch", "docs": "npm run docs:markdown", "docs:markdown": "jsdoc2md lib/cjs/index.js -f lib/cjs/**/*.js > API.md", "lint": "eslint --max-warnings 0 --config .eslintrc .", "lint:fix": "eslint --max-warnings 0 --config .eslintrc . --fix", "release": "semantic-release", - "generate:readme:toc": "markdown-toc -i README.md && markdown-toc -i ./docs/usage.md && markdown-toc -i ./docs/integration.md && markdown-toc -i ./docs/advanced.md && markdown-toc -i ./docs/languages/TypeScript.md && markdown-toc -i ./docs/languages/Java.md && markdown-toc -i ./docs/languages/JavaScript.md && markdown-toc -i ./docs/languages/Csharp.md && markdown-toc -i ./docs/README.md && markdown-toc -i ./docs/generators.md", - "generate:assets": "npm run build:prod && npm run docs && npm run generate:readme:toc", + "generate:readme:toc": "markdown-toc -i README.md && markdown-toc -i ./examples/README.md && markdown-toc -i ./docs/languages/Python.md && markdown-toc -i ./docs/usage.md && markdown-toc -i ./docs/integration.md && markdown-toc -i ./docs/advanced.md && markdown-toc -i ./docs/languages/Dart.md && markdown-toc -i ./docs/languages/TypeScript.md && markdown-toc -i ./docs/languages/Java.md && markdown-toc -i ./docs/languages/JavaScript.md && markdown-toc -i ./docs/languages/Csharp.md && markdown-toc -i ./docs/README.md && markdown-toc -i ./docs/generators.md", + "generate:assets": "npm run build:prod && npm run docs && npm run generate:readme:toc && npm run format", "bump:version": "npm --no-git-tag-version --allow-same-version version $VERSION", - "prepublishOnly": "npm run build:prod && npm run generate:readme:toc" + "prepublishOnly": "npm run build:prod && npm run generate:readme:toc && npm run format", + "format": "prettier --config .prettierrc \"./**/*.ts\" --write" }, "publishConfig": { "access": "public" }, "release": { "branches": [ - "master" + "master", + { + "name": "next", + "prerelease": true + } ], "plugins": [ [ diff --git a/src/generators/AbstractDependencyManager.ts b/src/generators/AbstractDependencyManager.ts new file mode 100644 index 0000000000..13c6a47bb5 --- /dev/null +++ b/src/generators/AbstractDependencyManager.ts @@ -0,0 +1,14 @@ +export class AbstractDependencyManager { + constructor(public dependencies: string[] = []) {} + + /** + * Adds a dependency while ensuring that unique dependencies. + * + * @param dependency complete dependency string so it can be rendered as is. + */ + addDependency(dependency: string): void { + if (!this.dependencies.includes(dependency)) { + this.dependencies.push(dependency); + } + } +} diff --git a/src/generators/AbstractFileGenerator.ts b/src/generators/AbstractFileGenerator.ts index 4c22c99336..6ce8c1801e 100644 --- a/src/generators/AbstractFileGenerator.ts +++ b/src/generators/AbstractFileGenerator.ts @@ -1,10 +1,14 @@ -import { CommonInputModel, OutputModel } from '../models'; +import { InputMetaModel, OutputModel } from '../models'; -export type FileGenerator = (content: string, toFile: string) => Promise; +export type FileGenerator = (content: string, toFile: string) => Promise; /** * Abstract file generator which each file generator much implement. */ export interface AbstractFileGenerator { - generateToFiles(input: Record | CommonInputModel, outputDirectory: string, options: RenderCompleteModelOptions): Promise; + generateToFiles( + input: Record | InputMetaModel, + outputDirectory: string, + options: RenderCompleteModelOptions + ): Promise; } diff --git a/src/generators/AbstractGenerator.ts b/src/generators/AbstractGenerator.ts index 8eb722e606..57fc528795 100644 --- a/src/generators/AbstractGenerator.ts +++ b/src/generators/AbstractGenerator.ts @@ -1,62 +1,129 @@ -import { CommonInputModel, CommonModel, OutputModel, Preset, Presets, RenderOutput, ProcessorOptions } from '../models'; +import { + InputMetaModel, + OutputModel, + Preset, + Presets, + RenderOutput, + ProcessorOptions, + MetaModel, + ConstrainedMetaModel +} from '../models'; import { InputProcessor } from '../processors'; import { IndentationTypes } from '../helpers'; -import { isPresetWithOptions } from '../utils'; -export interface CommonGeneratorOptions

{ +import { DeepPartial, isPresetWithOptions } from '../utils'; +import { AbstractDependencyManager } from './AbstractDependencyManager'; + +export interface CommonGeneratorOptions< + P extends Preset = Preset, + DependencyManager extends AbstractDependencyManager = AbstractDependencyManager +> { indentation?: { type: IndentationTypes; size: number; }; defaultPreset?: P; presets?: Presets

; - processorOptions?: ProcessorOptions + processorOptions?: ProcessorOptions; + /** + * This dependency manager type serves two functions. + * 1. It can be used to provide a factory for generate functions + * 2. It can be used to provide a single instance of a dependency manager, to add all dependencies together + * + * This depends on context and where it's used. + */ + dependencyManager?: (() => DependencyManager) | DependencyManager; } export const defaultGeneratorOptions: CommonGeneratorOptions = { indentation: { type: IndentationTypes.SPACES, - size: 2, + size: 2 } }; /** * Abstract generator which must be implemented by each language */ -export abstract class AbstractGenerator { - protected options: Options; - +export abstract class AbstractGenerator< + Options extends CommonGeneratorOptions, + RenderCompleteModelOptions +> { constructor( public readonly languageName: string, - defaultOptions?: Options, - passedOptions?: Options, - ) { - this.options = this.mergeOptions(defaultOptions, passedOptions); - } + public readonly options: Options + ) {} - public abstract render(model: CommonModel, inputModel: CommonInputModel): Promise; - public abstract renderCompleteModel(model: CommonModel, inputModel: CommonInputModel, options: RenderCompleteModelOptions): Promise; + public abstract render( + model: MetaModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise; + public abstract renderCompleteModel( + model: MetaModel, + inputModel: InputMetaModel, + completeOptions: Partial, + options?: DeepPartial + ): Promise; + public abstract constrainToMetaModel( + model: MetaModel, + options: DeepPartial + ): ConstrainedMetaModel; + public abstract getDependencyManager( + options: Options + ): AbstractDependencyManager; + public abstract splitMetaModel(model: MetaModel): MetaModel[]; - public process(input: Record): Promise { - return InputProcessor.processor.process(input, this.options.processorOptions); + public process(input: Record): Promise { + return InputProcessor.processor.process( + input, + this.options.processorOptions + ); + } + + /** + * This function returns an instance of the dependency manager which is either a factory or an instance. + */ + protected getDependencyManagerInstance( + options: Options + ): AbstractDependencyManager { + if (options.dependencyManager === undefined) { + throw new Error( + 'Internal error, could not find dependency manager instance' + ); + } + if (typeof options.dependencyManager === 'function') { + return options.dependencyManager(); + } + return options.dependencyManager; } /** * Generates the full output of a model, instead of a scattered model. - * + * * OutputModels result is no longer the model itself, but including package, package dependencies and model dependencies. - * - * @param input - * @param options to use for rendering full output + * */ - public async generateCompleteModels(input: Record | CommonInputModel, options: RenderCompleteModelOptions): Promise { + public async generateCompleteModels( + input: any | InputMetaModel, + completeOptions: Partial + ): Promise { const inputModel = await this.processInput(input); const renders = Object.values(inputModel.models).map(async (model) => { - const renderedOutput = await this.renderCompleteModel(model, inputModel, options); - return OutputModel.toOutputModel({ + const dependencyManager = this.getDependencyManager(this.options); + const constrainedModel = this.constrainToMetaModel(model, { + dependencyManager + } as DeepPartial); + const renderedOutput = await this.renderCompleteModel( + constrainedModel, + inputModel, + completeOptions, + { dependencyManager } as DeepPartial + ); + return OutputModel.toOutputModel({ result: renderedOutput.result, - modelName: renderedOutput.renderedName, + modelName: renderedOutput.renderedName, dependencies: renderedOutput.dependencies, - model, + model: constrainedModel, inputModel }); }); @@ -64,19 +131,23 @@ export abstract class AbstractGenerator | CommonInputModel): Promise { + public async generate(input: any | InputMetaModel): Promise { const inputModel = await this.processInput(input); const renders = Object.values(inputModel.models).map(async (model) => { - const renderedOutput = await this.render(model, inputModel); - return OutputModel.toOutputModel({ + const dependencyManager = this.getDependencyManager(this.options); + const constrainedModel = this.constrainToMetaModel(model, { + dependencyManager + } as DeepPartial); + const renderedOutput = await this.render(constrainedModel, inputModel, { + dependencyManager + } as DeepPartial); + return OutputModel.toOutputModel({ result: renderedOutput.result, - modelName: renderedOutput.renderedName, + modelName: renderedOutput.renderedName, dependencies: renderedOutput.dependencies, - model, + model: constrainedModel, inputModel }); }); @@ -84,23 +155,38 @@ export abstract class AbstractGenerator | CommonInputModel): Promise { - if (input instanceof CommonInputModel) { - return Promise.resolve(input); + protected async processInput( + input: any | InputMetaModel + ): Promise { + const rawInputModel = + input instanceof InputMetaModel ? input : await this.process(input); + + //Split out the models based on the language specific requirements of which models is rendered separately + const splitOutModels: { [key: string]: MetaModel } = {}; + for (const model of Object.values(rawInputModel.models)) { + const splitModels = this.splitMetaModel(model); + for (const splitModel of splitModels) { + splitOutModels[splitModel.name] = splitModel; + } } - return this.process(input); + rawInputModel.models = splitOutModels; + return rawInputModel; } + /** + * Get all presets (default and custom ones from options) for a given preset type (class, enum, etc). + */ protected getPresets(presetType: string): Array<[Preset, unknown]> { const filteredPresets: Array<[Preset, unknown]> = []; const defaultPreset = this.options.defaultPreset; if (defaultPreset !== undefined) { - filteredPresets.push([defaultPreset[String(presetType)], undefined]); + filteredPresets.push([defaultPreset[String(presetType)], this.options]); } const presets = this.options.presets || []; @@ -120,12 +206,4 @@ export abstract class AbstractGenerator = AbstractGenerator, + RendererModelType extends ConstrainedMetaModel = ConstrainedMetaModel > { constructor( protected readonly options: O, readonly generator: G, protected readonly presets: Array<[Preset, unknown]>, - protected readonly model: CommonModel, - protected readonly inputModel: CommonInputModel, - public dependencies: string[] = [] + protected readonly model: RendererModelType, + protected readonly inputModel: InputMetaModel ) {} - /** - * Adds a dependency while ensuring that only one dependency is preset at a time. - * - * @param dependency complete dependency string so it can be rendered as is. - */ - addDependency(dependency: string): void { - if (!this.dependencies.includes(dependency)) { - this.dependencies.push(dependency); - } - } - renderLine(line: string): string { return `${line}\n`; } @@ -38,11 +27,7 @@ export abstract class AbstractRenderer< return lines.filter(Boolean).join(n); } - indent( - content: string, - size?: number, - type?: IndentationTypes, - ): string { + indent(content: string, size?: number, type?: IndentationTypes): string { size = size || this.options.indentation?.size; type = type || this.options.indentation?.type; return FormatHelpers.indent(content, size, type); @@ -51,24 +36,24 @@ export abstract class AbstractRenderer< runSelfPreset(): Promise { return this.runPreset('self'); } - + runAdditionalContentPreset(): Promise { return this.runPreset('additionalContent'); } - + async runPreset( functionName: string, - params: Record = {}, + params: Record = {} ): Promise { let content = ''; for (const [preset, options] of this.presets) { if (typeof preset[String(functionName)] === 'function') { - const presetRenderedContent: any = await preset[String(functionName)]({ - ...params, - renderer: this, - content, - options, - model: this.model, + const presetRenderedContent: any = await preset[String(functionName)]({ + ...params, + renderer: this, + content, + options, + model: this.model, inputModel: this.inputModel }); if (typeof presetRenderedContent === 'string') { diff --git a/src/generators/csharp/CSharpConstrainer.ts b/src/generators/csharp/CSharpConstrainer.ts new file mode 100644 index 0000000000..cc4ee0b766 --- /dev/null +++ b/src/generators/csharp/CSharpConstrainer.ts @@ -0,0 +1,108 @@ +import { + ConstrainedEnumValueModel, + ConstrainedObjectPropertyModel +} from '../../models'; +import { + defaultEnumKeyConstraints, + defaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; +import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; +import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; +import { CSharpTypeMapping } from './CSharpGenerator'; + +function getFullTypeDefinition( + typeName: string, + partOfProperty: ConstrainedObjectPropertyModel | undefined +) { + return partOfProperty?.required ?? true ? typeName : `${typeName}?`; +} + +const fromEnumValueToType = ( + enumValueModel: ConstrainedEnumValueModel +): string => { + switch (typeof enumValueModel.value) { + case 'boolean': + return 'bool'; + case 'number': + case 'bigint': + if (Number.isInteger(enumValueModel.value)) { + return 'int'; + } + return 'double'; + case 'string': + return 'string'; + case 'object': + default: + return 'dynamic'; + } +}; + +export const CSharpDefaultTypeMapping: CSharpTypeMapping = { + Object({ constrainedModel, partOfProperty }): string { + return getFullTypeDefinition(constrainedModel.name, partOfProperty); + }, + Reference({ constrainedModel, partOfProperty }): string { + return getFullTypeDefinition(constrainedModel.name, partOfProperty); + }, + Any({ partOfProperty }): string { + return getFullTypeDefinition('dynamic', partOfProperty); + }, + Float({ partOfProperty }): string { + return getFullTypeDefinition('double', partOfProperty); + }, + Integer({ partOfProperty }): string { + return getFullTypeDefinition('int', partOfProperty); + }, + String({ partOfProperty }): string { + return getFullTypeDefinition('string', partOfProperty); + }, + Boolean({ partOfProperty }): string { + return getFullTypeDefinition('bool', partOfProperty); + }, + Tuple({ constrainedModel, partOfProperty }): string { + const tupleTypes = constrainedModel.tuple.map((constrainedType) => { + return constrainedType.value.type; + }); + return getFullTypeDefinition(`(${tupleTypes.join(', ')})`, partOfProperty); + }, + Array({ constrainedModel, options, partOfProperty }): string { + const typeString = + options.collectionType && options.collectionType === 'List' + ? `IEnumerable<${constrainedModel.valueModel.type}>` + : `${constrainedModel.valueModel.type}[]`; + + return getFullTypeDefinition(typeString, partOfProperty); + }, + Enum({ constrainedModel, partOfProperty }): string { + const typesForValues: Set = new Set(); + + for (const value of constrainedModel.values) { + const typeForValue = fromEnumValueToType(value); + typesForValues.add(typeForValue); + } + // If there exist more then 1 unique type, then default to dynamic + if (typesForValues.size > 1) { + return 'dynamic'; + } + const [typeForValues] = typesForValues; + return getFullTypeDefinition(typeForValues, partOfProperty); + }, + Union({ partOfProperty }): string { + //Because renderPrivateType( CSharp , partOfProperty) have no notion of unions (and no custom implementation), we have to render it as any value. + + return getFullTypeDefinition('dynamic', partOfProperty); + }, + Dictionary({ constrainedModel, partOfProperty }): string { + return getFullTypeDefinition( + `Dictionary<${constrainedModel.key.type}, ${constrainedModel.value.type}>`, + partOfProperty + ); + } +}; + +export const CSharpDefaultConstraints = { + enumKey: defaultEnumKeyConstraints(), + enumValue: defaultEnumValueConstraints(), + modelName: defaultModelNameConstraints(), + propertyKey: defaultPropertyKeyConstraints() +}; diff --git a/src/generators/csharp/CSharpDependencyManager.ts b/src/generators/csharp/CSharpDependencyManager.ts new file mode 100644 index 0000000000..50d578606b --- /dev/null +++ b/src/generators/csharp/CSharpDependencyManager.ts @@ -0,0 +1,8 @@ +import { AbstractDependencyManager } from '../AbstractDependencyManager'; +import { CSharpOptions } from './CSharpGenerator'; + +export class CSharpDependencyManager extends AbstractDependencyManager { + constructor(public options: CSharpOptions, dependencies: string[] = []) { + super(dependencies); + } +} diff --git a/src/generators/csharp/CSharpFileGenerator.ts b/src/generators/csharp/CSharpFileGenerator.ts index c8af7944d1..da32f8f90e 100644 --- a/src/generators/csharp/CSharpFileGenerator.ts +++ b/src/generators/csharp/CSharpFileGenerator.ts @@ -1,23 +1,43 @@ import { CSharpGenerator, CSharpRenderCompleteModelOptions } from './'; -import { CommonInputModel, OutputModel } from '../../models'; +import { InputMetaModel, OutputModel } from '../../models'; import * as path from 'path'; import { AbstractFileGenerator } from '../AbstractFileGenerator'; import { FileHelpers } from '../../helpers'; +import { DeepPartial } from '../../utils'; -export class CSharpFileGenerator extends CSharpGenerator implements AbstractFileGenerator { +export class CSharpFileGenerator + extends CSharpGenerator + implements AbstractFileGenerator +{ /** - * Generates all the models to an output directory each model with their own separate files. - * + * Generates all the models to an output directory each model with their own separate files. + * * @param input * @param outputDirectory where you want the models generated to * @param options + * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ - public async generateToFiles(input: Record | CommonInputModel, outputDirectory: string, options: CSharpRenderCompleteModelOptions): Promise { + public async generateToFiles( + input: Record | InputMetaModel, + outputDirectory: string, + options: DeepPartial, + ensureFilesWritten = false + ): Promise { let generatedModels = await this.generateCompleteModels(input, options); - generatedModels = generatedModels.filter((outputModel) => { return outputModel.modelName !== undefined; }); + //Filter anything out that have not been successfully generated + generatedModels = generatedModels.filter((outputModel) => { + return outputModel.modelName !== ''; + }); for (const outputModel of generatedModels) { - const filePath = path.resolve(outputDirectory, `${outputModel.modelName}.cs`); - await FileHelpers.writerToFileSystem(outputModel.result, filePath); + const filePath = path.resolve( + outputDirectory, + `${outputModel.modelName}.cs` + ); + await FileHelpers.writerToFileSystem( + outputModel.result, + filePath, + ensureFilesWritten + ); } return generatedModels; } diff --git a/src/generators/csharp/CSharpGenerator.ts b/src/generators/csharp/CSharpGenerator.ts index bb930f4962..c0aab16663 100644 --- a/src/generators/csharp/CSharpGenerator.ts +++ b/src/generators/csharp/CSharpGenerator.ts @@ -3,95 +3,263 @@ import { CommonGeneratorOptions, defaultGeneratorOptions } from '../AbstractGenerator'; -import { CommonModel, CommonInputModel, RenderOutput } from '../../models'; -import { TypeHelpers, ModelKind, CommonNamingConvention, CommonNamingConventionImplementation, FormatHelpers } from '../../helpers'; +import { + ConstrainedEnumModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + InputMetaModel, + MetaModel, + RenderOutput +} from '../../models'; +import { + FormatHelpers, + TypeMapping, + Constraints, + constrainMetaModel, + split, + SplitOptions +} from '../../helpers'; import { CSharpPreset, CSHARP_DEFAULT_PRESET } from './CSharpPreset'; import { EnumRenderer } from './renderers/EnumRenderer'; import { ClassRenderer } from './renderers/ClassRenderer'; import { isReservedCSharpKeyword } from './Constants'; import { Logger } from '../../index'; +import { + CSharpDefaultConstraints, + CSharpDefaultTypeMapping +} from './CSharpConstrainer'; +import { DeepPartial, mergePartialAndDefault } from '../../utils/Partials'; +import { CSharpDependencyManager } from './CSharpDependencyManager'; export interface CSharpOptions extends CommonGeneratorOptions { - collectionType?: 'List' | 'Array'; - namingConvention?: CommonNamingConvention; + collectionType: 'List' | 'Array'; + typeMapping: TypeMapping; + constraints: Constraints; + autoImplementedProperties: boolean; } +export type CSharpTypeMapping = TypeMapping< + CSharpOptions, + CSharpDependencyManager +>; export interface CSharpRenderCompleteModelOptions { - namespace: string + namespace: string; } /** * Generator for CSharp */ -export class CSharpGenerator extends AbstractGenerator { +export class CSharpGenerator extends AbstractGenerator< + CSharpOptions, + CSharpRenderCompleteModelOptions +> { static defaultOptions: CSharpOptions = { ...defaultGeneratorOptions, collectionType: 'Array', defaultPreset: CSHARP_DEFAULT_PRESET, - namingConvention: CommonNamingConventionImplementation + typeMapping: CSharpDefaultTypeMapping, + constraints: CSharpDefaultConstraints, + autoImplementedProperties: false, + // Temporarily set + dependencyManager: () => { + return {} as CSharpDependencyManager; + } + }; + + static defaultCompleteModelOptions: CSharpRenderCompleteModelOptions = { + namespace: 'Asyncapi.Models' }; - constructor( - options: CSharpOptions = CSharpGenerator.defaultOptions, - ) { - super('CSharp', CSharpGenerator.defaultOptions, options); + constructor(options?: DeepPartial) { + const realizedOptions = CSharpGenerator.getCSharpOptions(options); + super('CSharp', realizedOptions); + } + + /** + * Returns the CSharp options by merging custom options with default ones. + */ + static getCSharpOptions(options?: DeepPartial): CSharpOptions { + const optionsToUse = mergePartialAndDefault( + this.defaultOptions, + options + ) as CSharpOptions; + //Always overwrite the dependency manager unless user explicitly state they want it (ignore default temporary dependency manager) + if (options?.dependencyManager === undefined) { + optionsToUse.dependencyManager = () => { + return new CSharpDependencyManager(optionsToUse); + }; + } + return optionsToUse; + } + + /** + * Wrapper to get an instance of the dependency manager + */ + getDependencyManager(options: CSharpOptions): CSharpDependencyManager { + return this.getDependencyManagerInstance( + options + ) as CSharpDependencyManager; + } + + splitMetaModel(model: MetaModel): MetaModel[] { + //These are the models that we have separate renderers for + const metaModelsToSplit: SplitOptions = { + splitEnum: true, + splitObject: true + }; + return split(model, metaModelsToSplit); + } + + constrainToMetaModel( + model: MetaModel, + options: DeepPartial + ): ConstrainedMetaModel { + const optionsToUse = CSharpGenerator.getCSharpOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + return constrainMetaModel( + this.options.typeMapping, + this.options.constraints, + { + metaModel: model, + dependencyManager: dependencyManagerToUse, + options: this.options, + constrainedName: '' //This is just a placeholder, it will be constrained within the function, + } + ); } /** * Render a complete model result where the model code, library and model dependencies are all bundled appropriately. - * + * * For CSharp we need to specify which namespace the model is placed under. - * - * @param model - * @param inputModel + * + * @param model + * @param inputModel * @param options used to render the full output */ - async renderCompleteModel(model: CommonModel, inputModel: CommonInputModel, options: CSharpRenderCompleteModelOptions): Promise { - if (isReservedCSharpKeyword(options.namespace)) { - throw new Error(`You cannot use reserved CSharp keyword (${options.namespace}) as namespace, please use another.`); + async renderCompleteModel( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + completeModelOptions: DeepPartial, + options: DeepPartial + ): Promise { + const completeModelOptionsToUse = mergePartialAndDefault( + CSharpGenerator.defaultCompleteModelOptions, + completeModelOptions + ) as CSharpRenderCompleteModelOptions; + const optionsToUse = CSharpGenerator.getCSharpOptions({ + ...this.options, + ...options + }); + if (isReservedCSharpKeyword(completeModelOptionsToUse.namespace)) { + throw new Error( + `You cannot use reserved CSharp keyword (${completeModelOptionsToUse.namespace}) as namespace, please use another.` + ); } const outputModel = await this.render(model, inputModel); - const outputDependencies = outputModel.dependencies.length === 0 ? '' : `${outputModel.dependencies.join('\n')}\n\n`; + const outputDependencies = + outputModel.dependencies.length === 0 + ? '' + : `${outputModel.dependencies.join('\n')}\n\n`; - const outputContent = `namespace ${options.namespace} + const outputContent = `namespace ${completeModelOptionsToUse.namespace} { -${FormatHelpers.indent(outputDependencies + outputModel.result, this.options.indentation?.size, this.options.indentation?.type)} +${FormatHelpers.indent( + outputDependencies + outputModel.result, + optionsToUse.indentation?.size, + optionsToUse.indentation?.type +)} }`; - return RenderOutput.toRenderOutput({ result: outputContent, renderedName: outputModel.renderedName, dependencies: outputModel.dependencies }); + return RenderOutput.toRenderOutput({ + result: outputContent, + renderedName: outputModel.renderedName, + dependencies: outputModel.dependencies + }); } - render(model: CommonModel, inputModel: CommonInputModel): Promise { - const kind = TypeHelpers.extractKind(model); - switch (kind) { - case ModelKind.UNION: - //We dont support union in Csharp generator, however, if union is an object, we render it as a class. - if (!model.type?.includes('object')) { break; } - return this.renderClass(model, inputModel); - case ModelKind.OBJECT: - return this.renderClass(model, inputModel); - case ModelKind.ENUM: - return this.renderEnum(model, inputModel); + render( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = CSharpGenerator.getCSharpOptions({ + ...this.options, + ...options + }); + if (model instanceof ConstrainedObjectModel) { + return this.renderClass(model, inputModel, optionsToUse); + } else if (model instanceof ConstrainedEnumModel) { + return this.renderEnum(model, inputModel, optionsToUse); } - Logger.warn(`C# generator, cannot generate this type of model, ${model.$id}`); - return Promise.resolve(RenderOutput.toRenderOutput({ result: '', renderedName: '', dependencies: [] })); + Logger.warn( + `C# generator, cannot generate this type of model, ${model.name}` + ); + return Promise.resolve( + RenderOutput.toRenderOutput({ + result: '', + renderedName: '', + dependencies: [] + }) + ); } - async renderEnum(model: CommonModel, inputModel: CommonInputModel): Promise { + async renderEnum( + model: ConstrainedEnumModel, + inputModel: InputMetaModel, + options?: Partial + ): Promise { + const optionsToUse = CSharpGenerator.getCSharpOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const presets = this.getPresets('enum'); - const renderer = new EnumRenderer(this.options, this, presets, model, inputModel); + const renderer = new EnumRenderer( + this.options, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = renderer.nameType(model.$id, model); - return RenderOutput.toRenderOutput({ result, renderedName, dependencies: renderer.dependencies }); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } - async renderClass(model: CommonModel, inputModel: CommonInputModel): Promise { + async renderClass( + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + options?: Partial + ): Promise { + const optionsToUse = CSharpGenerator.getCSharpOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const presets = this.getPresets('class'); - const renderer = new ClassRenderer(this.options, this, presets, model, inputModel); + const renderer = new ClassRenderer( + this.options, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = renderer.nameType(model.$id, model); - return RenderOutput.toRenderOutput({ result, renderedName, dependencies: renderer.dependencies }); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } } diff --git a/src/generators/csharp/CSharpPreset.ts b/src/generators/csharp/CSharpPreset.ts index 8528ebb24e..e5b8e74940 100644 --- a/src/generators/csharp/CSharpPreset.ts +++ b/src/generators/csharp/CSharpPreset.ts @@ -1,19 +1,37 @@ -/* eslint-disable @typescript-eslint/ban-types */ -import { Preset, EnumPreset, ClassPreset, PresetArgs, PropertyArgs } from '../../models'; -import { ClassRenderer, CSHARP_DEFAULT_CLASS_PRESET } from './renderers/ClassRenderer'; -import { CSHARP_DEFAULT_ENUM_PRESET, EnumRenderer } from './renderers/EnumRenderer'; +import { + Preset, + EnumPreset, + ClassPreset, + PresetArgs, + PropertyArgs, + ConstrainedObjectModel +} from '../../models'; +import { CSharpOptions } from './CSharpGenerator'; +import { + ClassRenderer, + CSHARP_DEFAULT_CLASS_PRESET +} from './renderers/ClassRenderer'; +import { + CSHARP_DEFAULT_ENUM_PRESET, + EnumRenderer +} from './renderers/EnumRenderer'; // Our class preset uses custom `accessor` hook to craft getter and setters. -export interface CsharpClassPreset extends ClassPreset { - accessor?: (args: PresetArgs & PropertyArgs) => Promise | string; +export interface CsharpClassPreset extends ClassPreset { + accessor?: ( + args: PresetArgs & PropertyArgs + ) => Promise | string; } -export type CSharpPreset = Preset<{ - class: CsharpClassPreset; - enum: EnumPreset +export type ClassPresetType = CsharpClassPreset; +export type EnumPresetType = EnumPreset; + +export type CSharpPreset = Preset<{ + class: CsharpClassPreset; + enum: EnumPreset; }>; -export const CSHARP_DEFAULT_PRESET: CSharpPreset = { +export const CSHARP_DEFAULT_PRESET: CSharpPreset = { class: CSHARP_DEFAULT_CLASS_PRESET, - enum: CSHARP_DEFAULT_ENUM_PRESET, + enum: CSHARP_DEFAULT_ENUM_PRESET }; diff --git a/src/generators/csharp/CSharpRenderer.ts b/src/generators/csharp/CSharpRenderer.ts index 57363fbfa5..797d784eb1 100644 --- a/src/generators/csharp/CSharpRenderer.ts +++ b/src/generators/csharp/CSharpRenderer.ts @@ -1,123 +1,30 @@ import { AbstractRenderer } from '../AbstractRenderer'; import { CSharpGenerator, CSharpOptions } from './CSharpGenerator'; -import { CommonModel, CommonInputModel, Preset, PropertyType } from '../../models'; +import { Preset, ConstrainedMetaModel, InputMetaModel } from '../../models'; import { FormatHelpers } from '../../helpers/FormatHelpers'; -import { isReservedCSharpKeyword } from './Constants'; +import { CSharpDependencyManager } from './CSharpDependencyManager'; /** * Common renderer for CSharp types - * + * * @extends AbstractRenderer */ -export abstract class CSharpRenderer extends AbstractRenderer { +export abstract class CSharpRenderer< + RendererModelType extends ConstrainedMetaModel +> extends AbstractRenderer { constructor( options: CSharpOptions, generator: CSharpGenerator, presets: Array<[Preset, unknown]>, - model: CommonModel, - inputModel: CommonInputModel, + model: RendererModelType, + inputModel: InputMetaModel, + public dependencyManager: CSharpDependencyManager ) { super(options, generator, presets, model, inputModel); } - /** - * Renders the name of a type based on provided generator option naming convention type function. - * - * This is used to render names of models and then later used if that class is referenced from other models. - * - * @param name - * @param model - */ - nameType(name: string | undefined, model?: CommonModel): string { - return this.options?.namingConvention?.type - ? this.options.namingConvention.type(name, { model: model || this.model, inputModel: this.inputModel, reservedKeywordCallback: isReservedCSharpKeyword }) - : name || ''; - } - - /** - * Renders the name of a property based on provided generator option naming convention property function. - * - * @param propertyName - * @param property - */ - nameProperty(propertyName: string | undefined, property?: CommonModel): string { - return this.options?.namingConvention?.property - ? this.options.namingConvention.property(propertyName, { model: this.model, inputModel: this.inputModel, property, reservedKeywordCallback: isReservedCSharpKeyword }) - : propertyName || ''; - } - - runPropertyPreset(propertyName: string, property: CommonModel, options: any, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('property', { propertyName, property, options, type }); - } - - renderType(model: CommonModel, modelName?: string): string { - if (model.$ref !== undefined) { - return this.nameType(model.$ref); - } - - const isRequired = modelName ? this.model.isRequired(modelName) || this.model.required?.map(x => this.nameProperty(x)).includes(modelName) : false; - - if (Array.isArray(model.type)) { - return model.type.length > 1 ? 'dynamic' : `${this.toCSharpType(model.type[0], model, modelName, isRequired)}`; - } - - return this.toCSharpType(model.type, model, modelName, isRequired); - } - renderComments(lines: string | string[]): string { lines = FormatHelpers.breakLines(lines); - return lines.map(line => `// ${line}`).join('\n'); - } - - toCSharpType(type: string | undefined, model: CommonModel, modelName?: string, isRequired?: boolean): string { - switch (type) { - case 'integer': - case 'int32': - return `int${this.questionMark(isRequired)}`; - case 'long': - case 'int64': - return `long${this.questionMark(isRequired)}`; - case 'boolean': - return `bool${this.questionMark(isRequired)}`; - case 'date': - case 'time': - case 'dateTime': - case 'date-time': - return `System.DateTime${this.questionMark(isRequired)}`; - case 'string': - case 'password': - case 'byte': - return 'string'; - case 'float': - return `float${this.questionMark(isRequired)}`; - case 'double': - case 'number': - return `double${this.questionMark(isRequired)}`; - case 'binary': - return 'byte[]'; - case 'object': - return 'object'; - case 'array': { - let arrayItemModel = model.items; - if (Array.isArray(model.items)) { - arrayItemModel = model.items.reduce((prevModel, currentModel) => { - return CommonModel.mergeCommonModels(CommonModel.toCommonModel(prevModel), CommonModel.toCommonModel(currentModel), {}); - }); - if (model.additionalItems !== undefined) { - arrayItemModel = CommonModel.mergeCommonModels(arrayItemModel, model.additionalItems, {}); - } - } - const newType = arrayItemModel ? this.renderType(arrayItemModel as CommonModel, modelName) : 'dynamic'; - if (this.options.collectionType && this.options.collectionType === 'List') { - return `IEnumerable<${newType}>`; - } - return `${newType}[]`; - } - default: return 'dynamic'; - } - } - - private questionMark(isRequired?: boolean): string { - return isRequired ? '' : '?'; + return lines.map((line) => `// ${line}`).join('\n'); } } diff --git a/src/generators/csharp/Constants.ts b/src/generators/csharp/Constants.ts index 8959d7ab07..92157914e8 100644 --- a/src/generators/csharp/Constants.ts +++ b/src/generators/csharp/Constants.ts @@ -1,3 +1,5 @@ +import { checkForReservedKeyword } from '../../helpers'; + export const RESERVED_CSHARP_KEYWORDS = [ 'abstract', 'as', @@ -78,6 +80,13 @@ export const RESERVED_CSHARP_KEYWORDS = [ 'while' ]; -export function isReservedCSharpKeyword(word: string): boolean { - return RESERVED_CSHARP_KEYWORDS.includes(word); +export function isReservedCSharpKeyword( + word: string, + forceLowerCase = true +): boolean { + return checkForReservedKeyword( + word, + RESERVED_CSHARP_KEYWORDS, + forceLowerCase + ); } diff --git a/src/generators/csharp/constrainer/EnumConstrainer.ts b/src/generators/csharp/constrainer/EnumConstrainer.ts new file mode 100644 index 0000000000..fa22cc1a75 --- /dev/null +++ b/src/generators/csharp/constrainer/EnumConstrainer.ts @@ -0,0 +1,98 @@ +import { ConstrainedEnumModel, EnumModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { + EnumKeyConstraint, + EnumValueConstraint, + FormatHelpers +} from '../../../helpers'; +import { isReservedCSharpKeyword } from '../Constants'; + +export type ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_KEYS: ( + constrainedEnumModel: ConstrainedEnumModel, + enumModel: EnumModel, + value: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude '_', '$' because they are allowed as enum keys + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: ['_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_KEYS: NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toConstantCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, (value) => { + // We don't care about comparing values in lowercase as we are using constant case + // This means the reserved keywords technically never clashes + return isReservedCSharpKeyword(value, false); + }); + } +}; + +export function defaultEnumKeyConstraints( + customConstraints?: Partial +): EnumKeyConstraint { + const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; + + return ({ enumKey, enumModel, constrainedEnumModel }) => { + let constrainedEnumKey = enumKey; + constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey); + constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey); + //If the enum key has been manipulated, lets make sure it don't clash with existing keys + if (constrainedEnumKey !== enumKey) { + constrainedEnumKey = constraints.NO_DUPLICATE_KEYS( + constrainedEnumModel, + enumModel, + constrainedEnumKey, + constraints.NAMING_FORMATTER + ); + } + constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey); + return constrainedEnumKey; + }; +} + +export function defaultEnumValueConstraints(): EnumValueConstraint { + return ({ enumValue }) => { + let normalizedEnumValue; + switch (typeof enumValue) { + case 'boolean': + case 'bigint': + case 'number': { + normalizedEnumValue = enumValue; + break; + } + case 'object': { + normalizedEnumValue = `"${JSON.stringify(enumValue).replace( + /"/g, + '\\"' + )}"`; + break; + } + default: { + normalizedEnumValue = `"${enumValue}"`; + } + } + return normalizedEnumValue; + }; +} diff --git a/src/generators/csharp/constrainer/ModelNameConstrainer.ts b/src/generators/csharp/constrainer/ModelNameConstrainer.ts new file mode 100644 index 0000000000..b7a71c1a55 --- /dev/null +++ b/src/generators/csharp/constrainer/ModelNameConstrainer.ts @@ -0,0 +1,50 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { isReservedCSharpKeyword } from '../Constants'; + +export type ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultModelNameConstraints: ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NAMING_FORMATTER: (value: string) => { + return FormatHelpers.toPascalCase(value); + }, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedCSharpKeyword); + } +}; +export function defaultModelNameConstraints( + customConstraints?: Partial +): ModelNameConstraint { + const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; + + return ({ modelName }) => { + let constrainedValue = modelName; + constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue); + constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue); + constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue); + constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue); + constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); + return constrainedValue; + }; +} diff --git a/src/generators/csharp/constrainer/PropertyKeyConstrainer.ts b/src/generators/csharp/constrainer/PropertyKeyConstrainer.ts new file mode 100644 index 0000000000..647d4398d8 --- /dev/null +++ b/src/generators/csharp/constrainer/PropertyKeyConstrainer.ts @@ -0,0 +1,94 @@ +import { ConstrainedObjectModel, ObjectModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { isReservedCSharpKeyword } from '../Constants'; + +export type PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_PROPERTIES: ( + constrainedObjectModel: ConstrainedObjectModel, + objectModel: ObjectModel, + propertyName: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; + NO_ENCLOSING_NAMES: ( + constrainedObjectModel: ConstrainedObjectModel, + value: string + ) => string; +}; + +export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` and `_` because it gets formatted by NAMING_FORMATTER + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toCamelCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedCSharpKeyword); + }, + NO_ENCLOSING_NAMES: (constrainedObjectModel, value) => { + if (constrainedObjectModel.name.toLowerCase() === value.toLowerCase()) { + return `reserved_${value}`; + } + return value; + } +}; + +export function defaultPropertyKeyConstraints( + customConstraints?: Partial +): PropertyKeyConstraint { + const constraints = { + ...DefaultPropertyKeyConstraints, + ...customConstraints + }; + + return ({ objectPropertyModel, constrainedObjectModel, objectModel }) => { + let constrainedPropertyKey = objectPropertyModel.propertyName; + + constrainedPropertyKey = constraints.NO_SPECIAL_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey); + constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_ENCLOSING_NAMES( + constrainedObjectModel, + constrainedPropertyKey + ); + //If the property name has been manipulated, lets make sure it don't clash with existing properties + if (constrainedPropertyKey !== objectPropertyModel.propertyName) { + constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES( + constrainedObjectModel, + objectModel, + constrainedPropertyKey, + constraints.NAMING_FORMATTER + ); + } + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + return constrainedPropertyKey; + }; +} diff --git a/src/generators/csharp/index.ts b/src/generators/csharp/index.ts index b0e1ace1b5..e841ea45f3 100644 --- a/src/generators/csharp/index.ts +++ b/src/generators/csharp/index.ts @@ -3,3 +3,19 @@ export * from './CSharpFileGenerator'; export { CSHARP_DEFAULT_PRESET } from './CSharpPreset'; export type { CSharpPreset } from './CSharpPreset'; export * from './presets'; + +export { + defaultEnumKeyConstraints as csharpDefaultEnumKeyConstraints, + DefaultEnumKeyConstraints as CsharpDefaultEnumKeyConstraints, + defaultEnumValueConstraints as csharpDefaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; + +export { + DefaultModelNameConstraints as CsharpDefaultModelNameConstraints, + defaultModelNameConstraints as csharpDefaultModelNameConstraints +} from './constrainer/ModelNameConstrainer'; + +export { + DefaultPropertyKeyConstraints as CsharpDefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints as csharpDefaultPropertyKeyConstraints +} from './constrainer/PropertyKeyConstrainer'; diff --git a/src/generators/csharp/presets/CommonPreset.ts b/src/generators/csharp/presets/CommonPreset.ts index 1ba550e6b9..c1002306d2 100644 --- a/src/generators/csharp/presets/CommonPreset.ts +++ b/src/generators/csharp/presets/CommonPreset.ts @@ -1,8 +1,7 @@ import { CSharpRenderer } from '../CSharpRenderer'; import { CSharpPreset } from '../CSharpPreset'; - -import { getUniquePropertyName, FormatHelpers, DefaultPropertyNames } from '../../../helpers'; -import { CommonModel } from '../../../models'; +import { FormatHelpers } from '../../../helpers'; +import { ConstrainedObjectModel } from '../../../models'; export interface CSharpCommonPresetOptions { equal: boolean; @@ -12,25 +11,25 @@ export interface CSharpCommonPresetOptions { /** * Render `equal` function based on model's properties */ -function renderEqual({ renderer, model }: { - renderer: CSharpRenderer, - model: CommonModel, +function renderEqual({ + renderer, + model +}: { + renderer: CSharpRenderer; + model: ConstrainedObjectModel; }): string { - const formattedModelName = renderer.nameType(model.$id); const properties = model.properties || {}; const propertyKeys = Object.keys(properties); - if (model.additionalProperties) { - propertyKeys.push(getUniquePropertyName(model, DefaultPropertyNames.additionalProperties)); - } - for (const [pattern, patternModel] of Object.entries(model.patternProperties || {})) { - propertyKeys.push(getUniquePropertyName(patternModel, `${pattern}${DefaultPropertyNames.patternProperties}`)); - } - let equalProperties = propertyKeys.map(prop => { - const accessorMethodProp = FormatHelpers.upperFirst(renderer.nameProperty(prop)); - return `${accessorMethodProp} == model.${accessorMethodProp}`; - }).join(' && \n'); - equalProperties = `return ${equalProperties !== '' ? equalProperties : 'true'}`; - const methodContent = `if(obj is ${formattedModelName} model) + let equalProperties = propertyKeys + .map((propertyName) => { + const accessorMethodProp = FormatHelpers.upperFirst(propertyName); + return `${accessorMethodProp} == model.${accessorMethodProp}`; + }) + .join(' && \n'); + equalProperties = `return ${ + equalProperties !== '' ? equalProperties : 'true' + }`; + const methodContent = `if(obj is ${model.name} model) { ${renderer.indent('if(ReferenceEquals(this, model)) { return true; }')} ${renderer.indent(equalProperties)}; @@ -47,19 +46,20 @@ ${renderer.indent(methodContent)} /** * Render `hashCode` function based on model's properties */ -function renderHashCode({ renderer, model }: { - renderer: CSharpRenderer, - model: CommonModel, +function renderHashCode({ + renderer, + model +}: { + renderer: CSharpRenderer; + model: ConstrainedObjectModel; }): string { const properties = model.properties || {}; const propertyKeys = Object.keys(properties); - if (model.additionalProperties) { - propertyKeys.push(getUniquePropertyName(model, DefaultPropertyNames.additionalProperties)); - } - for (const [pattern, patternModel] of Object.entries(model.patternProperties || {})) { - propertyKeys.push(getUniquePropertyName(patternModel, `${pattern}${DefaultPropertyNames.patternProperties}`)); - } - const hashProperties = propertyKeys.map(prop => `hash.Add(${FormatHelpers.upperFirst(renderer.nameProperty(prop))});`).join('\n'); + const hashProperties = propertyKeys + .map( + (propertyName) => `hash.Add(${FormatHelpers.upperFirst(propertyName)});` + ) + .join('\n'); return `public override int GetHashCode() { @@ -70,8 +70,8 @@ ${renderer.indent(hashProperties, 2)} } /** - * Preset which adds `Equals`, `GetHashCode` functions to class. - * + * Preset which adds `Equals`, `GetHashCode` functions to class. + * * @implements {CSharpPreset} */ export const CSHARP_COMMON_PRESET: CSharpPreset = { @@ -80,13 +80,15 @@ export const CSHARP_COMMON_PRESET: CSharpPreset = { options = options || {}; const blocks: string[] = []; - if (options.equal === undefined || options.equal === true) { blocks.push(renderEqual({ renderer, model })); } - if (options.hashCode === undefined || options.hashCode === true) { - renderer.addDependency('using System;'); + if (options.equal === undefined || options.equal === true) { + blocks.push(renderEqual({ renderer, model })); + } + if (options.hash === undefined || options.hash === true) { + renderer.dependencyManager.addDependency('using System;'); blocks.push(renderHashCode({ renderer, model })); } return renderer.renderBlock([content, ...blocks], 2); - }, + } } }; diff --git a/src/generators/csharp/presets/JsonSerializerPreset.ts b/src/generators/csharp/presets/JsonSerializerPreset.ts index 44d8bc2da1..7c06befe72 100644 --- a/src/generators/csharp/presets/JsonSerializerPreset.ts +++ b/src/generators/csharp/presets/JsonSerializerPreset.ts @@ -1,118 +1,105 @@ import { CSharpRenderer } from '../CSharpRenderer'; import { CSharpPreset } from '../CSharpPreset'; -import { getUniquePropertyName, DefaultPropertyNames, FormatHelpers, TypeHelpers, ModelKind } from '../../../helpers'; -import { CommonInputModel, CommonModel } from '../../../models'; - -function renderSerializeProperty(modelInstanceVariable: string, model: CommonModel, inputModel: CommonInputModel) { +import { + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ConstrainedReferenceModel +} from '../../../models'; +import { CSharpOptions } from '../CSharpGenerator'; + +function renderSerializeProperty( + modelInstanceVariable: string, + model: ConstrainedObjectPropertyModel +) { let value = modelInstanceVariable; - if (model.$ref) { - const resolvedModel = inputModel.models[model.$ref]; - const propertyModelKind = TypeHelpers.extractKind(resolvedModel); - //Referenced enums is the only one who need custom serialization - if (propertyModelKind === ModelKind.ENUM) { - value = `${value}.GetValue()`; - } + //Special case where a referenced enum model need to be accessed + if ( + model.property instanceof ConstrainedReferenceModel && + model.property.ref instanceof ConstrainedEnumModel + ) { + value = `${model.property.type}.GetValue()`; } return `JsonSerializer.Serialize(writer, ${value}, options);`; } -function renderSerializeAdditionalProperties(model: CommonModel, renderer: CSharpRenderer, inputModel: CommonInputModel) { - const serializeAdditionalProperties = ''; - if (model.additionalProperties !== undefined) { - let additionalPropertyName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties); - additionalPropertyName = FormatHelpers.upperFirst(renderer.nameProperty(additionalPropertyName, model.additionalProperties)); - return `// Unwrap additional properties in object -if (value.AdditionalProperties != null) { - foreach (var additionalProperty in value.${additionalPropertyName}) +function renderSerializeProperties(model: ConstrainedObjectModel) { + let serializeProperties = ''; + if (model.properties !== undefined) { + for (const [propertyName, propertyModel] of Object.entries( + model.properties + )) { + const modelInstanceVariable = `value.${propertyName}`; + if ( + propertyModel.property instanceof ConstrainedDictionaryModel && + propertyModel.property.serializationType === 'unwrap' + ) { + serializeProperties += `// Unwrap dictionary properties +if (${modelInstanceVariable} != null) { + foreach (var unwrappedProperty in ${modelInstanceVariable}) { - //Ignore any additional properties which might already be part of the core properties - if (properties.Any(prop => prop.Name == additionalProperty.Key)) + // Ignore any unwrapped properties which might already be part of the core properties + if (properties.Any(prop => prop.Name == unwrappedProperty.Key)) { continue; } - // write property name and let the serializer serialize the value itself - writer.WritePropertyName(additionalProperty.Key); - ${renderSerializeProperty('additionalProperty.Value', model.additionalProperties, inputModel)} + // Write property name and let the serializer serialize the value itself + writer.WritePropertyName(unwrappedProperty.Key); + ${renderSerializeProperty('unwrappedProperty.Value', propertyModel)} } }`; - } - return serializeAdditionalProperties; -} - -function renderSerializeProperties(model: CommonModel, renderer: CSharpRenderer, inputModel: CommonInputModel) { - let serializeProperties = ''; - if (model.properties !== undefined) { - for (const [propertyName, propertyModel] of Object.entries(model.properties)) { - const formattedPropertyName = FormatHelpers.upperFirst(renderer.nameProperty(propertyName, propertyModel)); - const modelInstanceVariable = `value.${formattedPropertyName}`; + } serializeProperties += `if(${modelInstanceVariable} != null) { // write property name and let the serializer serialize the value itself - writer.WritePropertyName("${propertyName}"); - ${renderSerializeProperty(modelInstanceVariable, propertyModel, inputModel)} + writer.WritePropertyName("${propertyModel.unconstrainedPropertyName}"); + ${renderSerializeProperty(modelInstanceVariable, propertyModel)} }\n`; } } return serializeProperties; } -function renderSerializePatternProperties(model: CommonModel, renderer: CSharpRenderer, inputModel: CommonInputModel) { - let serializePatternProperties = ''; - if (model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(model.patternProperties)) { - let patternPropertyName = getUniquePropertyName(model, `${pattern}${DefaultPropertyNames.patternProperties}`); - patternPropertyName = FormatHelpers.upperFirst(renderer.nameProperty(patternPropertyName, patternModel)); - serializePatternProperties += `// Unwrap pattern properties in object -if(value.${patternPropertyName} != null) { - foreach (var patternProp in value.${patternPropertyName}) - { - //Ignore any pattern properties which might already be part of the core properties - if (properties.Any(prop => prop.Name == patternProp.Key)) - { - continue; - } - // write property name and let the serializer serialize the value itself - writer.WritePropertyName(patternProp.Key); - ${renderSerializeProperty('patternProp.Value', patternModel, inputModel)} - } -}`; - } - } - return serializePatternProperties; -} -function renderPropertiesList(model: CommonModel, renderer: CSharpRenderer) { - const propertyFilter: string[] = []; - if (model.additionalProperties !== undefined) { - let additionalPropertyName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties); - additionalPropertyName = FormatHelpers.upperFirst(renderer.nameProperty(additionalPropertyName, model.additionalProperties)); - propertyFilter.push(`prop.Name != "${additionalPropertyName}"`); - } - for (const [pattern, patternModel] of Object.entries(model.patternProperties || {})) { - let patternPropertyName = getUniquePropertyName(model, `${pattern}${DefaultPropertyNames.patternProperties}`); - patternPropertyName = FormatHelpers.upperFirst(renderer.nameProperty(patternPropertyName, patternModel)); - propertyFilter.push(`prop.Name != "${patternPropertyName}"`); - } +function renderPropertiesList( + model: ConstrainedObjectModel, + renderer: CSharpRenderer +) { + const unwrappedDictionaryProperties = Object.values(model.properties) + .filter((model) => { + return ( + model.property instanceof ConstrainedDictionaryModel && + model.property.serializationType === 'unwrap' + ); + }) + .map((value) => { + return value.propertyName; + }); + let propertiesList = 'var properties = value.GetType().GetProperties();'; - if (propertyFilter.length > 0) { - renderer.addDependency('using System.Linq;'); - propertiesList = `var properties = value.GetType().GetProperties().Where(prop => ${propertyFilter.join(' && ')});`; + if (unwrappedDictionaryProperties.length > 0) { + renderer.dependencyManager.addDependency('using System.Linq;'); + propertiesList = `var properties = value.GetType().GetProperties().Where(prop => ${unwrappedDictionaryProperties.join( + ' && ' + )});`; } return propertiesList; } /** * Render `serialize` function based on model */ -function renderSerialize({ renderer, model, inputModel }: { - renderer: CSharpRenderer, - model: CommonModel, - inputModel: CommonInputModel +function renderSerialize({ + renderer, + model +}: { + renderer: CSharpRenderer; + model: ConstrainedObjectModel; }): string { - const formattedModelName = renderer.nameType(model.$id); - const serializeProperties = renderSerializeProperties(model, renderer, inputModel); - const serializePatternProperties = renderSerializePatternProperties(model, renderer, inputModel); - const serializeAdditionalProperties = renderSerializeAdditionalProperties(model, renderer, inputModel); + const serializeProperties = renderSerializeProperties(model); const propertiesList = renderPropertiesList(model, renderer); - return `public override void Write(Utf8JsonWriter writer, ${formattedModelName} value, JsonSerializerOptions options) + return `public override void Write(Utf8JsonWriter writer, ${ + model.name + } value, JsonSerializerOptions options) { if (value == null) { @@ -125,94 +112,67 @@ function renderSerialize({ renderer, model, inputModel }: { ${renderer.indent(serializeProperties)} -${renderer.indent(serializePatternProperties)} - -${renderer.indent(serializeAdditionalProperties)} - writer.WriteEndObject(); }`; -} +} -function renderDeserializeProperty(type: string, model: CommonModel, inputModel: CommonInputModel) { - if (model.$ref) { - const resolvedModel = inputModel.models[model.$ref]; - const propertyModelKind = TypeHelpers.extractKind(resolvedModel); - //Referenced enums is the only one who need custom serialization - if (propertyModelKind === ModelKind.ENUM) { - return `${type}Extensions.To${type}(JsonSerializer.Deserialize(ref reader, options))`; - } +function renderDeserializeProperty(model: ConstrainedObjectPropertyModel) { + //Referenced enums is the only one who need custom serialization + if ( + model.property instanceof ConstrainedReferenceModel && + model.property.ref instanceof ConstrainedEnumModel + ) { + return `${model.property.name}Extension.To${model.property.name}(JsonSerializer.Deserialize(ref reader, options))`; } - return `JsonSerializer.Deserialize<${type}>(ref reader, options)`; + return `JsonSerializer.Deserialize<${model.property.type}>(ref reader, options)`; } -function renderDeserializeProperties(model: CommonModel, renderer: CSharpRenderer, inputModel: CommonInputModel) { +function renderDeserializeProperties(model: ConstrainedObjectModel) { const propertyEntries = Object.entries(model.properties || {}); const deserializeProperties = propertyEntries.map(([prop, propModel]) => { - const formattedPropertyName = FormatHelpers.upperFirst(renderer.nameProperty(prop, propModel)); - const propertyModelType = renderer.renderType(propModel); - return `if (propertyName == "${prop}") -{ - var value = ${renderDeserializeProperty(propertyModelType, propModel, inputModel)}; - instance.${formattedPropertyName} = value; - continue; -}`; + //Unwrapped dictionary properties, need to be unwrapped in JSON + if ( + propModel.property instanceof ConstrainedDictionaryModel && + propModel.property.serializationType === 'unwrap' + ) { + return `if(instance.${prop} == null) { instance.${prop} = new Dictionary<${ + propModel.property.key.type + }, ${propModel.property.key.type}>(); } + var deserializedValue = ${renderDeserializeProperty(propModel)}; + instance.${prop}.Add(propertyName, deserializedValue); + continue;`; + } + return `if (propertyName == "${propModel.unconstrainedPropertyName}") + { + var value = ${renderDeserializeProperty(propModel)}; + instance.${prop} = value; + continue; + }`; }); return deserializeProperties.join('\n'); } -function renderDeserializePatternProperties(model: CommonModel, renderer: CSharpRenderer, inputModel: CommonInputModel) { - if (model.patternProperties === undefined) { - return ''; - } - const patternProperties = Object.entries(model.patternProperties).map(([pattern, patternModel]) => { - let patternPropertyName = getUniquePropertyName(model, `${pattern}${DefaultPropertyNames.patternProperties}`); - patternPropertyName = FormatHelpers.upperFirst(renderer.nameProperty(patternPropertyName, patternModel)); - const patternPropertyType = renderer.renderType(patternModel); - return `if(instance.${patternPropertyName} == null) { instance.${patternPropertyName} = new Dictionary(); } -var match = Regex.Match(propertyName, @"${pattern}"); -if (match.Success) -{ - var deserializedValue = ${renderDeserializeProperty(patternPropertyType, patternModel, inputModel)}; - instance.${patternPropertyName}.Add(propertyName, deserializedValue); - continue; -}`; - }); - return patternProperties.join('\n'); -} - -function renderDeserializeAdditionalProperties(model: CommonModel, renderer: CSharpRenderer, inputModel: CommonInputModel) { - if (model.additionalProperties === undefined) { - return ''; - } - let additionalPropertyName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties); - additionalPropertyName = FormatHelpers.upperFirst(renderer.nameProperty(additionalPropertyName, model.additionalProperties)); - const additionalPropertyType = renderer.renderType(model.additionalProperties); - return `if(instance.${additionalPropertyName} == null) { instance.${additionalPropertyName} = new Dictionary(); } -var deserializedValue = ${renderDeserializeProperty(additionalPropertyType, model.additionalProperties, inputModel)}; -instance.${additionalPropertyName}.Add(propertyName, deserializedValue); -continue;`; -} - /** * Render `deserialize` function based on model */ -function renderDeserialize({ renderer, model, inputModel }: { - renderer: CSharpRenderer, - model: CommonModel, - inputModel: CommonInputModel +function renderDeserialize({ + renderer, + model +}: { + renderer: CSharpRenderer; + model: ConstrainedObjectModel; }): string { - const formattedModelName = renderer.nameType(model.$id); - const deserializeProperties = renderDeserializeProperties(model, renderer, inputModel); - const deserializePatternProperties = renderDeserializePatternProperties(model, renderer, inputModel); - const deserializeAdditionalProperties = renderDeserializeAdditionalProperties(model, renderer, inputModel); - return `public override ${formattedModelName} Read(ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options) + const deserializeProperties = renderDeserializeProperties(model); + return `public override ${ + model.name + } Read(ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException(); } - var instance = new ${formattedModelName}(); + var instance = new ${model.name}(); while (reader.Read()) { @@ -229,36 +189,35 @@ function renderDeserialize({ renderer, model, inputModel }: { string propertyName = reader.GetString(); ${renderer.indent(deserializeProperties, 4)} - -${renderer.indent(deserializePatternProperties, 4)} - -${renderer.indent(deserializeAdditionalProperties, 4)} } throw new JsonException(); }`; -} +} /** - * Preset which adds `serialize` and `deserialize` functions to class. - * + * Preset which adds `serialize` and `deserialize` functions to class. + * * @implements {CSharpPreset} */ -export const CSHARP_JSON_SERIALIZER_PRESET: CSharpPreset = { +export const CSHARP_JSON_SERIALIZER_PRESET: CSharpPreset = { class: { - self({ renderer, model, content, inputModel}) { - renderer.addDependency('using System.Text.Json;'); - renderer.addDependency('using System.Text.Json.Serialization;'); - renderer.addDependency('using System.Text.RegularExpressions;'); - - const formattedModelName = renderer.nameType(model.$id); - const deserialize = renderDeserialize({renderer, model, inputModel}); - const serialize = renderSerialize({renderer, model, inputModel}); - - return `[JsonConverter(typeof(${formattedModelName}Converter))] + self({ renderer, model, content }) { + renderer.dependencyManager.addDependency('using System.Text.Json;'); + renderer.dependencyManager.addDependency( + 'using System.Text.Json.Serialization;' + ); + renderer.dependencyManager.addDependency( + 'using System.Text.RegularExpressions;' + ); + + const deserialize = renderDeserialize({ renderer, model }); + const serialize = renderSerialize({ renderer, model }); + + return `[JsonConverter(typeof(${model.name}Converter))] ${content} -internal class ${formattedModelName}Converter : JsonConverter<${formattedModelName}> +internal class ${model.name}Converter : JsonConverter<${model.name}> { public override bool CanConvert(System.Type objectType) { diff --git a/src/generators/csharp/presets/NewtonsoftSerializerPreset.ts b/src/generators/csharp/presets/NewtonsoftSerializerPreset.ts new file mode 100644 index 0000000000..648e948f4c --- /dev/null +++ b/src/generators/csharp/presets/NewtonsoftSerializerPreset.ts @@ -0,0 +1,177 @@ +import { CSharpPreset } from '../CSharpPreset'; +import { + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedObjectModel, + ConstrainedReferenceModel +} from '../../../models'; +import { CSharpOptions } from '../CSharpGenerator'; +import { pascalCase } from 'change-case'; + +/** + * Render `serialize` function based on model + */ +function renderSerialize({ model }: { model: ConstrainedObjectModel }): string { + const corePropsWrite = Object.values(model.properties) + .filter( + (prop) => + !(prop.property instanceof ConstrainedDictionaryModel) || + prop.property.serializationType === 'normal' + ) + .map((prop) => { + const propertyAccessor = pascalCase(prop.propertyName); + let toJson = `jo.Add("${prop.unconstrainedPropertyName}", JToken.FromObject(value.${propertyAccessor}, serializer));`; + if ( + prop.property instanceof ConstrainedReferenceModel && + prop.property.ref instanceof ConstrainedEnumModel + ) { + toJson = `var enumValue = ${prop.property.name}Extensions.GetValue((${prop.property.name})value.${propertyAccessor}); +var stringEnumValue = enumValue.ToString(); +// C# converts booleans to uppercase True and False, which newtonsoft cannot understand +var jsonStringCompliant = stringEnumValue == "True" || stringEnumValue == "False" ? stringEnumValue.ToLower() : stringEnumValue; +var jsonToken = JToken.Parse(jsonStringCompliant); +jo.Add("${prop.unconstrainedPropertyName}", jsonToken);`; + } + return `if (value.${propertyAccessor} != null) +{ + ${toJson} +}`; + }); + const unwrapPropsWrite = Object.values(model.properties) + .filter( + (prop) => + prop.property instanceof ConstrainedDictionaryModel && + prop.property.serializationType === 'unwrap' + ) + .map((prop) => { + const propertyAccessor = pascalCase(prop.propertyName); + return `if (value.${propertyAccessor} != null) + { + foreach (var unwrapProperty in value.${propertyAccessor}) + { + var hasProp = jo[unwrapProperty.Key]; + if (hasProp != null) continue; + jo.Add(unwrapProperty.Key, JToken.FromObject(unwrapProperty.Value, serializer)); + } +}`; + }); + return `public override void WriteJson(JsonWriter writer, ${ + model.name + } value, JsonSerializer serializer) +{ + JObject jo = new JObject(); + + ${corePropsWrite.join('\n')} + ${unwrapPropsWrite.join('\n')} + + jo.WriteTo(writer); +}`; +} + +/** + * Render `deserialize` function based on model + */ +function renderDeserialize({ + model +}: { + model: ConstrainedObjectModel; +}): string { + const unwrapDictionaryProps = Object.values(model.properties).filter( + (prop) => + prop.property instanceof ConstrainedDictionaryModel && + prop.property.serializationType === 'unwrap' + ); + const coreProps = Object.values(model.properties).filter( + (prop) => + !(prop.property instanceof ConstrainedDictionaryModel) || + prop.property.serializationType === 'normal' + ); + const corePropsRead = coreProps.map((prop) => { + const propertyAccessor = pascalCase(prop.propertyName); + let toValue = `jo["${prop.unconstrainedPropertyName}"].ToObject<${prop.property.type}>(serializer)`; + if ( + prop.property instanceof ConstrainedReferenceModel && + prop.property.ref instanceof ConstrainedEnumModel + ) { + toValue = `${prop.property.name}Extensions.To${prop.property.name}(jo["${prop.unconstrainedPropertyName}"])`; + } + return `if(jo["${prop.unconstrainedPropertyName}"] != null) { + value.${propertyAccessor} = ${toValue}; +}`; + }); + const nonDictionaryPropCheck = coreProps.map((prop) => { + return `prop.Name != "${prop.unconstrainedPropertyName}"`; + }); + const dictionaryInitializers = unwrapDictionaryProps.map((prop) => { + const propertyAccessor = pascalCase(prop.propertyName); + return `value.${propertyAccessor} = new Dictionary<${ + (prop.property as ConstrainedDictionaryModel).key.type + }, ${(prop.property as ConstrainedDictionaryModel).value.type}>();`; + }); + const unwrapDictionaryRead = unwrapDictionaryProps.map((prop) => { + const propertyAccessor = pascalCase(prop.propertyName); + return `value.${propertyAccessor}[additionalProperty.Name] = additionalProperty.Value.ToObject<${ + (prop.property as ConstrainedDictionaryModel).value.type + }>(serializer);`; + }); + const additionalPropertiesCode = + unwrapDictionaryProps.length !== 0 + ? `var additionalProperties = jo.Properties().Where((prop) => ${nonDictionaryPropCheck.join( + ' || ' + )}); + ${dictionaryInitializers} + + foreach (var additionalProperty in additionalProperties) + { + ${unwrapDictionaryRead.join('\n')} + }` + : ''; + return `public override ${ + model.name + } ReadJson(JsonReader reader, System.Type objectType, ${ + model.name + } existingValue, bool hasExistingValue, JsonSerializer serializer) +{ + JObject jo = JObject.Load(reader); + ${model.name} value = new ${model.name}(); + + ${corePropsRead.join('\n')} + + ${additionalPropertiesCode} + return value; +}`; +} + +/** + * Preset which adds Newtonsoft/JSON.net converters for serializing and deserializing the data models + * + * @implements {CSharpPreset} + */ +export const CSHARP_NEWTONSOFT_SERIALIZER_PRESET: CSharpPreset = + { + class: { + self: ({ renderer, content, model }) => { + renderer.dependencyManager.addDependency('using Newtonsoft.Json;'); + renderer.dependencyManager.addDependency('using Newtonsoft.Json.Linq;'); + renderer.dependencyManager.addDependency( + 'using System.Collections.Generic;' + ); + renderer.dependencyManager.addDependency('using System.Linq;'); + + const deserialize = renderDeserialize({ model }); + const serialize = renderSerialize({ model }); + + return `[JsonConverter(typeof(${model.name}Converter))] +${content} + +public class ${model.name}Converter : JsonConverter<${model.name}> +{ + ${deserialize} + ${serialize} + + public override bool CanRead => true; + public override bool CanWrite => true; +}`; + } + } + }; diff --git a/src/generators/csharp/presets/index.ts b/src/generators/csharp/presets/index.ts index c7343600e4..04605832ae 100644 --- a/src/generators/csharp/presets/index.ts +++ b/src/generators/csharp/presets/index.ts @@ -1,2 +1,3 @@ export * from './JsonSerializerPreset'; +export * from './NewtonsoftSerializerPreset'; export * from './CommonPreset'; diff --git a/src/generators/csharp/renderers/ClassRenderer.ts b/src/generators/csharp/renderers/ClassRenderer.ts index 12c7bf0aa9..49a49a916c 100644 --- a/src/generators/csharp/renderers/ClassRenderer.ts +++ b/src/generators/csharp/renderers/ClassRenderer.ts @@ -1,31 +1,35 @@ import { CSharpRenderer } from '../CSharpRenderer'; -import { CommonModel, PropertyType } from '../../../models'; -import { DefaultPropertyNames, getUniquePropertyName } from '../../../helpers'; +import { + ConstrainedDictionaryModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel +} from '../../../models'; import { pascalCase } from 'change-case'; import { CsharpClassPreset } from '../CSharpPreset'; +import { CSharpOptions } from '../CSharpGenerator'; /** * Renderer for CSharp's `struct` type - * + * * @extends CSharpRenderer */ -export class ClassRenderer extends CSharpRenderer { +export class ClassRenderer extends CSharpRenderer { public async defaultSelf(): Promise { const content = [ await this.renderProperties(), await this.runCtorPreset(), await this.renderAccessors(), - await this.runAdditionalContentPreset(), + await this.runAdditionalContentPreset() ]; - if (this.options?.collectionType === 'List' || - this.model.additionalProperties !== undefined || - this.model.patternProperties !== undefined) { - this.addDependency('using System.Collections.Generic;'); + if ( + this.options?.collectionType === 'List' || + this.model.containsPropertyType(ConstrainedDictionaryModel) + ) { + this.dependencyManager.addDependency('using System.Collections.Generic;'); } - const formattedName = this.nameType(this.model.$id); - return `public class ${formattedName} + return `public class ${this.model.name} { ${this.indent(this.renderBlock(content, 2))} }`; @@ -35,25 +39,11 @@ ${this.indent(this.renderBlock(content, 2))} const properties = this.model.properties || {}; const content: string[] = []; - for (const [propertyName, property] of Object.entries(properties)) { - const rendererProperty = await this.runPropertyPreset(propertyName, property, this.options); + for (const property of Object.values(properties)) { + const rendererProperty = await this.runPropertyPreset(property); content.push(rendererProperty); } - if (this.model.additionalProperties !== undefined) { - const propertyName = getUniquePropertyName(this.model, DefaultPropertyNames.additionalProperties); - const additionalProperty = await this.runPropertyPreset(propertyName, this.model.additionalProperties, this.options, PropertyType.additionalProperty); - content.push(additionalProperty); - } - - if (this.model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(this.model.patternProperties)) { - const propertyName = getUniquePropertyName(this.model, `${pattern}${DefaultPropertyNames.patternProperties}`); - const renderedPatternProperty = await this.runPropertyPreset(propertyName, patternModel, this.options, PropertyType.patternProperties); - content.push(renderedPatternProperty); - } - } - return this.renderBlock(content); } @@ -61,20 +51,8 @@ ${this.indent(this.renderBlock(content, 2))} const properties = this.model.properties || {}; const content: string[] = []; - for (const [propertyName, property] of Object.entries(properties)) { - content.push(await this.runAccessorPreset(propertyName, property, this.options, PropertyType.property)); - } - - if (this.model.additionalProperties !== undefined) { - const propertyName = getUniquePropertyName(this.model, DefaultPropertyNames.additionalProperties); - content.push(await this.runAccessorPreset(propertyName, this.model.additionalProperties, this.options, PropertyType.additionalProperty)); - } - - if (this.model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(this.model.patternProperties)) { - const propertyName = getUniquePropertyName(this.model, `${pattern}${DefaultPropertyNames.patternProperties}`); - content.push(await this.runAccessorPreset(propertyName, patternModel, this.options, PropertyType.patternProperties)); - } + for (const property of Object.values(properties)) { + content.push(await this.runAccessorPreset(property)); } return this.renderBlock(content, 2); @@ -84,68 +62,75 @@ ${this.indent(this.renderBlock(content, 2))} return this.runPreset('ctor'); } - runAccessorPreset(propertyName: string, property: CommonModel, options?: any, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('accessor', { propertyName, property, options, type }); + runAccessorPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('accessor', { + property, + options: this.options, + renderer: this + }); } - runPropertyPreset(propertyName: string, property: CommonModel, options?: any, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('property', { propertyName, property, options, type }); + runPropertyPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('property', { + property, + options: this.options, + renderer: this + }); } - runGetterPreset(propertyName: string, property: CommonModel, options?: any, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('getter', { propertyName, property, options, type }); + runGetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('getter', { + property, + options: this.options, + renderer: this + }); } - runSetterPreset(propertyName: string, property: CommonModel, options?: any, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('setter', { propertyName, property, options, type }); + runSetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('setter', { + property, + options: this.options, + renderer: this + }); } } -export const CSHARP_DEFAULT_CLASS_PRESET: CsharpClassPreset = { +export const CSHARP_DEFAULT_CLASS_PRESET: CsharpClassPreset = { self({ renderer }) { return renderer.defaultSelf(); }, - async property({ renderer, propertyName, options, property, type }) { - propertyName = renderer.nameProperty(propertyName, property); - let propertyType = renderer.renderType(property, propertyName); - if (type === PropertyType.additionalProperty || type === PropertyType.patternProperties) { - propertyType = `Dictionary`; - } + async property({ renderer, property, options }) { if (options?.autoImplementedProperties) { - const getter = await renderer.runGetterPreset(propertyName, property, options, type); - const setter = await renderer.runSetterPreset(propertyName, property, options, type); - return `public ${propertyType} ${pascalCase(propertyName)} { ${getter} ${setter} }`; + const getter = await renderer.runGetterPreset(property); + const setter = await renderer.runSetterPreset(property); + return `public ${property.property.type} ${pascalCase( + property.propertyName + )} { ${getter} ${setter} }`; } - return `private ${propertyType} ${propertyName};`; + return `private ${property.property.type} ${property.propertyName};`; }, - async accessor({ renderer, propertyName, options, property, type }) { - const formattedAccessorName = pascalCase(renderer.nameProperty(propertyName, property)); - let propertyType = renderer.renderType(property, propertyName); - if (type === PropertyType.additionalProperty || type === PropertyType.patternProperties) { - propertyType = `Dictionary`; - } + async accessor({ renderer, options, property }) { + const formattedAccessorName = pascalCase(property.propertyName); if (options?.autoImplementedProperties) { return ''; } - return `public ${propertyType} ${formattedAccessorName} + return `public ${property.property.type} ${formattedAccessorName} { - ${await renderer.runGetterPreset(propertyName, property, options, type)} - ${await renderer.runSetterPreset(propertyName, property, options, type)} + ${await renderer.runGetterPreset(property)} + ${await renderer.runSetterPreset(property)} }`; }, - getter({ renderer, propertyName, options, property }) { + getter({ options, property }) { if (options?.autoImplementedProperties) { return 'get;'; } - const formattedPropertyName = renderer.nameProperty(propertyName, property); - return `get { return ${formattedPropertyName}; }`; + return `get { return ${property.propertyName}; }`; }, - setter({ renderer, propertyName, options, property }) { + setter({ options, property }) { if (options?.autoImplementedProperties) { return 'set;'; } - const formattedPropertyName = renderer.nameProperty(propertyName, property); - return `set { ${formattedPropertyName} = value; }`; + return `set { ${property.propertyName} = value; }`; } }; diff --git a/src/generators/csharp/renderers/EnumRenderer.ts b/src/generators/csharp/renderers/EnumRenderer.ts index f199ed4ea0..7d2850d9bb 100644 --- a/src/generators/csharp/renderers/EnumRenderer.ts +++ b/src/generators/csharp/renderers/EnumRenderer.ts @@ -1,19 +1,21 @@ import { CSharpRenderer } from '../CSharpRenderer'; -import { EnumPreset } from '../../../models'; -import { pascalCase } from 'change-case'; -import { FormatHelpers } from '../../../helpers'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../models'; +import { EnumPresetType } from '../CSharpPreset'; +import { CSharpOptions } from '../CSharpGenerator'; /** * Renderer for C#'s `enum` type - * + * * @extends CSharpRenderer */ -export class EnumRenderer extends CSharpRenderer { +export class EnumRenderer extends CSharpRenderer { async defaultSelf(): Promise { const enumItems = await this.renderItems(); - const formattedName = this.nameType(this.model.$id); - const getValueCaseItemValues = await this.getValueCaseItemValues(); - const toEnumCaseItemValues = await this.toEnumCaseItemValues(); + const getValueCaseItemValues = this.getValueCaseItemValues(); + const toEnumCaseItemValues = this.toEnumCaseItemValues(); const enumValueSwitch = `switch (enumValue) { ${this.indent(getValueCaseItemValues)} @@ -24,22 +26,26 @@ return null;`; ${this.indent(toEnumCaseItemValues)} } return null;`; - const classContent = `public static dynamic GetValue(this ${formattedName} enumValue) + const classContent = `public static ${this.model.type}? GetValue(this ${ + this.model.name + } enumValue) { ${this.indent(enumValueSwitch)} } -public static ${formattedName}? To${formattedName}(dynamic value) +public static ${this.model.name}? To${this.model.name}(${ + this.model.type + }? value) { ${this.indent(valueSwitch)} }`; - return `public enum ${formattedName} + return `public enum ${this.model.name} { ${this.indent(enumItems)} } -public static class ${formattedName}Extensions +public static class ${this.model.name}Extensions { ${this.indent(classContent)} } @@ -47,7 +53,7 @@ ${this.indent(classContent)} } async renderItems(): Promise { - const enums = this.model.enum || []; + const enums = this.model.values || []; const items: string[] = []; for (const value of enums) { @@ -59,70 +65,43 @@ ${this.indent(classContent)} return `${content}`; } - /** - * Some enum values require custom value conversion - */ - getEnumValue(enumValue: any): any { - switch (typeof enumValue) { - case 'number': - case 'bigint': - case 'boolean': - return enumValue; - case 'object': - return `"${JSON.stringify(enumValue).replace(/"/g, '\\"')}"`; - default: - return `"${enumValue}"`; - } - } - - async toEnumCaseItemValues(): Promise { - const enums = this.model.enum || []; + toEnumCaseItemValues(): string { + const enums = this.model.values || []; const items: string[] = []; - const formattedName = this.nameType(this.model.$id); for (const enumValue of enums) { - const renderedItem = await this.runItemPreset(enumValue); - const value = this.getEnumValue(enumValue); - items.push(`case ${value}: return ${formattedName}.${renderedItem};`); + items.push( + `case ${enumValue.value}: return ${this.model.name}.${enumValue.key};` + ); } const content = items.join('\n'); return `${content}`; } - async getValueCaseItemValues(): Promise { - const enums = this.model.enum || []; + getValueCaseItemValues(): string { + const enums = this.model.values || []; const items: string[] = []; - const formattedName = this.nameType(this.model.$id); for (const enumValue of enums) { - const renderedItem = await this.runItemPreset(enumValue); - const value = this.getEnumValue(enumValue); - items.push(`case ${formattedName}.${renderedItem}: return ${value};`); + items.push( + `case ${this.model.name}.${enumValue.key}: return ${enumValue.value};` + ); } const content = items.join('\n'); return `${content}`; } - runItemPreset(item: any): Promise { + runItemPreset(item: ConstrainedEnumValueModel): Promise { return this.runPreset('item', { item }); } } -export const CSHARP_DEFAULT_ENUM_PRESET: EnumPreset = { +export const CSHARP_DEFAULT_ENUM_PRESET: EnumPresetType = { self({ renderer }) { return renderer.defaultSelf(); }, item({ item }) { - let itemName = FormatHelpers.replaceSpecialCharacters(String(item), { exclude: [' '], separator: '_' }); - if (typeof item === 'number' || typeof item === 'bigint') { - itemName = `Number_${itemName}`; - } else if (typeof item === 'object') { - itemName = `${JSON.stringify(item)}`; - } else if (!(/^[a-zA-Z]+$/).test(itemName.charAt(0))) { - itemName = `String_${itemName}`; - } - - return pascalCase(itemName); - }, + return item.key; + } }; diff --git a/src/generators/dart/Constants.ts b/src/generators/dart/Constants.ts index 691fbe8837..decfff85d2 100644 --- a/src/generators/dart/Constants.ts +++ b/src/generators/dart/Constants.ts @@ -1,5 +1,7 @@ +import { checkForReservedKeyword } from '../../helpers'; + export const RESERVED_DART_KEYWORDS = [ - 'abstract', + 'abstract', 'as', 'assert', 'async', @@ -61,9 +63,12 @@ export const RESERVED_DART_KEYWORDS = [ 'void', 'while', 'with', - 'yield', + 'yield' ]; -export function isReservedDartKeyword(word: string): boolean { - return RESERVED_DART_KEYWORDS.includes(word); +export function isReservedDartKeyword( + word: string, + forceLowerCase = true +): boolean { + return checkForReservedKeyword(word, RESERVED_DART_KEYWORDS, forceLowerCase); } diff --git a/src/generators/dart/DartConstrainer.ts b/src/generators/dart/DartConstrainer.ts new file mode 100644 index 0000000000..ca6b22bab5 --- /dev/null +++ b/src/generators/dart/DartConstrainer.ts @@ -0,0 +1,77 @@ +import { + defaultEnumKeyConstraints, + defaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; +import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; +import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; +import { DartTypeMapping } from './DartGenerator'; + +export const DartDefaultTypeMapping: DartTypeMapping = { + Object({ constrainedModel }): string { + return constrainedModel.name; + }, + Reference({ constrainedModel }): string { + return constrainedModel.name; + }, + Any(): string { + return 'Object'; + }, + Float(): string { + return 'double'; + }, + Integer(): string { + return 'int'; + }, + String({ constrainedModel }): string { + const format = constrainedModel.originalInput?.format; + switch (format) { + case 'date': + return 'DateTime'; + case 'time': + return 'DateTime'; + case 'dateTime': + case 'date-time': + return 'DateTime'; + case 'string': + case 'password': + case 'byte': + return 'String'; + case 'binary': + return 'byte[]'; + default: + return 'String'; + } + }, + Boolean(): string { + return 'bool'; + }, + Tuple({ options }): string { + //Since Dart dont support tuples, lets use the most generic type + if (options.collectionType && options.collectionType === 'List') { + return 'List'; + } + return 'Object[]'; + }, + Array({ constrainedModel, options }): string { + if (options.collectionType && options.collectionType === 'List') { + return `List<${constrainedModel.valueModel.type}>`; + } + return `${constrainedModel.valueModel.type}[]`; + }, + Enum({ constrainedModel }): string { + return constrainedModel.name; + }, + Union(): string { + return 'Object'; + }, + Dictionary({ constrainedModel }): string { + return `Map<${constrainedModel.key.type}, ${constrainedModel.value.type}>`; + } +}; + +export const DartDefaultConstraints = { + enumKey: defaultEnumKeyConstraints(), + enumValue: defaultEnumValueConstraints(), + modelName: defaultModelNameConstraints(), + propertyKey: defaultPropertyKeyConstraints() +}; diff --git a/src/generators/dart/DartDependencyManager.ts b/src/generators/dart/DartDependencyManager.ts new file mode 100644 index 0000000000..9769738a1f --- /dev/null +++ b/src/generators/dart/DartDependencyManager.ts @@ -0,0 +1,28 @@ +import { FormatHelpers } from '../../helpers'; +import { ConstrainedMetaModel } from '../../models'; +import { AbstractDependencyManager } from '../AbstractDependencyManager'; +import { DartOptions } from './DartGenerator'; + +export class DartDependencyManager extends AbstractDependencyManager { + constructor(public options: DartOptions, dependencies: string[] = []) { + super(dependencies); + } + + renderImport(model: ConstrainedMetaModel, packageName: string): string { + return `import 'package:${packageName}/${FormatHelpers.snakeCase( + model.name + )}.dart';`; + } + + renderAllModelDependencies( + model: ConstrainedMetaModel, + packageName: string + ): string { + return model + .getNearestDependencies() + .map((dependencyModel) => { + return this.renderImport(dependencyModel, packageName); + }) + .join('\n'); + } +} diff --git a/src/generators/dart/DartFileGenerator.ts b/src/generators/dart/DartFileGenerator.ts index 594750705d..f00a800a53 100644 --- a/src/generators/dart/DartFileGenerator.ts +++ b/src/generators/dart/DartFileGenerator.ts @@ -1,23 +1,42 @@ import { DartGenerator, DartRenderCompleteModelOptions } from './'; -import { CommonInputModel, OutputModel } from '../../models'; +import { InputMetaModel, OutputModel } from '../../models'; import * as path from 'path'; import { AbstractFileGenerator } from '../AbstractFileGenerator'; import { FileHelpers } from '../../helpers'; -export class DartFileGenerator extends DartGenerator implements AbstractFileGenerator { +export class DartFileGenerator + extends DartGenerator + implements AbstractFileGenerator +{ /** - * Generates all the models to an output directory each model with their own separate files. - * + * Generates all the models to an output directory each model with their own separate files. + * * @param input * @param outputDirectory where you want the models generated to * @param options + * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ - public async generateToFiles(input: Record | CommonInputModel, outputDirectory: string, options: DartRenderCompleteModelOptions): Promise { + public async generateToFiles( + input: Record | InputMetaModel, + outputDirectory: string, + options: DartRenderCompleteModelOptions, + ensureFilesWritten = false + ): Promise { let generatedModels = await this.generateCompleteModels(input, options); - generatedModels = generatedModels.filter((outputModel) => { return outputModel.modelName !== undefined; }); + //Filter anything out that have not been successfully generated + generatedModels = generatedModels.filter((outputModel) => { + return outputModel.modelName !== ''; + }); for (const outputModel of generatedModels) { - const filePath = path.resolve(outputDirectory, `${outputModel.modelName}.dart`); - await FileHelpers.writerToFileSystem(outputModel.result, filePath); + const filePath = path.resolve( + outputDirectory, + `${outputModel.modelName}.dart` + ); + await FileHelpers.writerToFileSystem( + outputModel.result, + filePath, + ensureFilesWritten + ); } return generatedModels; } diff --git a/src/generators/dart/DartGenerator.ts b/src/generators/dart/DartGenerator.ts index 1b13b95cf8..5fdf6280c9 100644 --- a/src/generators/dart/DartGenerator.ts +++ b/src/generators/dart/DartGenerator.ts @@ -3,54 +3,147 @@ import { CommonGeneratorOptions, defaultGeneratorOptions } from '../AbstractGenerator'; -import {CommonModel, CommonInputModel, RenderOutput} from '../../models'; -import {CommonNamingConvention, CommonNamingConventionImplementation, ModelKind, TypeHelpers} from '../../helpers'; -import {DartPreset, DART_DEFAULT_PRESET} from './DartPreset'; -import {ClassRenderer} from './renderers/ClassRenderer'; -import {EnumRenderer} from './renderers/EnumRenderer'; -import {isReservedDartKeyword} from './Constants'; -import {Logger} from '../../'; -import {FormatHelpers} from '../../helpers/FormatHelpers'; - +import { + RenderOutput, + ConstrainedMetaModel, + MetaModel, + ConstrainedObjectModel, + ConstrainedEnumModel, + InputMetaModel +} from '../../models'; +import { + constrainMetaModel, + Constraints, + split, + SplitOptions, + TypeMapping +} from '../../helpers'; +import { DartPreset, DART_DEFAULT_PRESET } from './DartPreset'; +import { ClassRenderer } from './renderers/ClassRenderer'; +import { EnumRenderer } from './renderers/EnumRenderer'; +import { isReservedDartKeyword } from './Constants'; +import { Logger } from '../../'; +import { + DartDefaultConstraints, + DartDefaultTypeMapping +} from './DartConstrainer'; +import { DeepPartial, mergePartialAndDefault } from '../../utils/Partials'; +import { DartDependencyManager } from './DartDependencyManager'; export interface DartOptions extends CommonGeneratorOptions { collectionType?: 'List'; - namingConvention?: CommonNamingConvention; + typeMapping: TypeMapping; + constraints: Constraints; } +export type DartTypeMapping = TypeMapping; export interface DartRenderCompleteModelOptions { packageName: string; } -export class DartGenerator extends AbstractGenerator { +export class DartGenerator extends AbstractGenerator< + DartOptions, + DartRenderCompleteModelOptions +> { static defaultOptions: DartOptions = { ...defaultGeneratorOptions, defaultPreset: DART_DEFAULT_PRESET, collectionType: 'List', - namingConvention: CommonNamingConventionImplementation + typeMapping: DartDefaultTypeMapping, + constraints: DartDefaultConstraints }; - constructor( - options: DartOptions = DartGenerator.defaultOptions, - ) { - super('Dart', DartGenerator.defaultOptions, options); + static defaultCompleteModelOptions: DartRenderCompleteModelOptions = { + packageName: 'AsyncapiModels' + }; + + constructor(options?: DeepPartial) { + const realizedOptions = DartGenerator.getDartOptions(options); + super('Dart', realizedOptions); + } + + /** + * Returns the Dart options by merging custom options with default ones. + */ + static getDartOptions(options?: DeepPartial): DartOptions { + const optionsToUse = mergePartialAndDefault( + DartGenerator.defaultOptions, + options + ) as DartOptions; + //Always overwrite the dependency manager unless user explicitly state they want it (ignore default temporary dependency manager) + if (options?.dependencyManager === undefined) { + optionsToUse.dependencyManager = () => { + return new DartDependencyManager(optionsToUse); + }; + } + return optionsToUse; + } + + /** + * Wrapper to get an instance of the dependency manager + */ + getDependencyManager(options: DartOptions): DartDependencyManager { + return this.getDependencyManagerInstance(options) as DartDependencyManager; + } + + splitMetaModel(model: MetaModel): MetaModel[] { + //These are the models that we have separate renderers for + const metaModelsToSplit: SplitOptions = { + splitEnum: true, + splitObject: true + }; + return split(model, metaModelsToSplit); } + constrainToMetaModel( + model: MetaModel, + options: DeepPartial + ): ConstrainedMetaModel { + const optionsToUse = DartGenerator.getDartOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + return constrainMetaModel( + this.options.typeMapping, + this.options.constraints, + { + metaModel: model, + dependencyManager: dependencyManagerToUse, + options: this.options, + constrainedName: '' //This is just a placeholder, it will be constrained within the function + } + ); + } /** * Render a scattered model, where the source code and library and model dependencies are separated. * * @param model * @param inputModel */ - render(model: CommonModel, inputModel: CommonInputModel): Promise { - const kind = TypeHelpers.extractKind(model); - // We don't support union in Dart generator, however, if union is an object, we render it as a class. - if (kind === ModelKind.OBJECT || (kind === ModelKind.UNION && model.type?.includes('object'))) { - return this.renderClass(model, inputModel); - } else if (kind === ModelKind.ENUM) { - return this.renderEnum(model, inputModel); + render( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = DartGenerator.getDartOptions({ + ...this.options, + ...options + }); + if (model instanceof ConstrainedObjectModel) { + return this.renderClass(model, inputModel, optionsToUse); + } else if (model instanceof ConstrainedEnumModel) { + return this.renderEnum(model, inputModel, optionsToUse); } - Logger.warn(`Dart generator, cannot generate this type of model, ${model.$id}`); - return Promise.resolve(RenderOutput.toRenderOutput({result: '', renderedName: '', dependencies: []})); + Logger.warn( + `Dart generator, cannot generate this type of model, ${model.name}` + ); + return Promise.resolve( + RenderOutput.toRenderOutput({ + result: '', + renderedName: '', + dependencies: [] + }) + ); } /** @@ -62,21 +155,33 @@ export class DartGenerator extends AbstractGenerator { - if (isReservedDartKeyword(options.packageName)) { - throw new Error(`You cannot use reserved Dart keyword (${options.packageName}) as package name, please use another.`); + async renderCompleteModel( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + completeModelOptions: Partial, + options: DeepPartial + ): Promise { + const completeModelOptionsToUse = mergePartialAndDefault( + DartGenerator.defaultCompleteModelOptions, + completeModelOptions + ) as DartRenderCompleteModelOptions; + const optionsToUse = DartGenerator.getDartOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + if (isReservedDartKeyword(completeModelOptionsToUse.packageName)) { + throw new Error( + `You cannot use reserved Dart keyword (${completeModelOptionsToUse.packageName}) as package name, please use another.` + ); } - const outputModel = await this.render(model, inputModel); - const modelDependencies = model.getNearestDependencies().map((dependencyModelName) => { - const formattedDependencyModelName = this.options.namingConvention?.type ? this.options.namingConvention.type(dependencyModelName, { - inputModel, - model: inputModel.models[String(dependencyModelName)], - reservedKeywordCallback: isReservedDartKeyword - }) : dependencyModelName; - return `import 'package:${options.packageName}/${FormatHelpers.snakeCase(formattedDependencyModelName)}.dart';`; - }); - const outputContent = `${modelDependencies.join('\n')} + const outputModel = await this.render(model, inputModel, optionsToUse); + const modelDependencies = dependencyManagerToUse.renderAllModelDependencies( + model, + completeModelOptionsToUse.packageName + ); + const outputContent = `${modelDependencies} ${outputModel.dependencies.join('\n')} ${outputModel.result}`; return RenderOutput.toRenderOutput({ @@ -86,19 +191,57 @@ export class DartGenerator extends AbstractGenerator { + async renderClass( + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = DartGenerator.getDartOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const presets = this.getPresets('class'); - const renderer = new ClassRenderer(this.options, this, presets, model, inputModel); + const renderer = new ClassRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = FormatHelpers.snakeCase(renderer.nameType(model.$id, model)); - return RenderOutput.toRenderOutput({result, renderedName, dependencies: renderer.dependencies}); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } - async renderEnum(model: CommonModel, inputModel: CommonInputModel): Promise { + async renderEnum( + model: ConstrainedEnumModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = DartGenerator.getDartOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const presets = this.getPresets('enum'); - const renderer = new EnumRenderer(this.options, this, presets, model, inputModel); + const renderer = new EnumRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = FormatHelpers.snakeCase(renderer.nameType(model.$id, model)); - return RenderOutput.toRenderOutput({result, renderedName, dependencies: renderer.dependencies}); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } } diff --git a/src/generators/dart/DartPreset.ts b/src/generators/dart/DartPreset.ts index 6d0860927d..7b8cd4003e 100644 --- a/src/generators/dart/DartPreset.ts +++ b/src/generators/dart/DartPreset.ts @@ -1,13 +1,23 @@ import { Preset, ClassPreset, EnumPreset } from '../../models'; -import { ClassRenderer, DART_DEFAULT_CLASS_PRESET } from './renderers/ClassRenderer'; -import { EnumRenderer, DART_DEFAULT_ENUM_PRESET } from './renderers/EnumRenderer'; +import { DartOptions } from './DartGenerator'; +import { + ClassRenderer, + DART_DEFAULT_CLASS_PRESET +} from './renderers/ClassRenderer'; +import { + EnumRenderer, + DART_DEFAULT_ENUM_PRESET +} from './renderers/EnumRenderer'; -export type DartPreset = Preset<{ - class: ClassPreset; - enum: EnumPreset; +export type ClassPresetType = ClassPreset; +export type EnumPresetType = EnumPreset; + +export type DartPreset = Preset<{ + class: ClassPresetType; + enum: EnumPresetType; }>; export const DART_DEFAULT_PRESET: DartPreset = { class: DART_DEFAULT_CLASS_PRESET, - enum: DART_DEFAULT_ENUM_PRESET, + enum: DART_DEFAULT_ENUM_PRESET }; diff --git a/src/generators/dart/DartRenderer.ts b/src/generators/dart/DartRenderer.ts index c10db5be6d..294a767497 100644 --- a/src/generators/dart/DartRenderer.ts +++ b/src/generators/dart/DartRenderer.ts @@ -1,162 +1,53 @@ import { AbstractRenderer } from '../AbstractRenderer'; import { DartGenerator, DartOptions } from './DartGenerator'; -import { CommonModel, CommonInputModel, Preset } from '../../models'; -import { FormatHelpers, ModelKind, TypeHelpers } from '../../helpers'; -import { isReservedDartKeyword } from './Constants'; +import { Preset, ConstrainedMetaModel, InputMetaModel } from '../../models'; +import { FormatHelpers } from '../../helpers'; +import { DartDependencyManager } from './DartDependencyManager'; /** * Common renderer for Dart types - * + * * @extends AbstractRenderer */ -export abstract class DartRenderer extends AbstractRenderer { +export abstract class DartRenderer< + RendererModelType extends ConstrainedMetaModel +> extends AbstractRenderer { constructor( options: DartOptions, generator: DartGenerator, presets: Array<[Preset, unknown]>, - model: CommonModel, - inputModel: CommonInputModel, + model: RendererModelType, + inputModel: InputMetaModel, + public dependencyManager: DartDependencyManager ) { super(options, generator, presets, model, inputModel); } - /** - * Renders the name of a type based on provided generator option naming convention type function. - * - * This is used to render names of models and then later used if it is referenced from other models. - * - * @param name - * @param model - */ - nameType(name: string | undefined, model?: CommonModel): string { - return this.options?.namingConvention?.type - ? this.options.namingConvention.type(name, { model: model || this.model, inputModel: this.inputModel, reservedKeywordCallback: isReservedDartKeyword }) - : name || ''; - } - - /** - * Renders the name of a property based on provided generator option naming convention property function. - * - * @param propertyName - * @param property - */ - nameProperty(propertyName: string | undefined, property?: CommonModel): string { - return this.options?.namingConvention?.property - ? this.options.namingConvention.property(propertyName, { model: this.model, inputModel: this.inputModel, property, reservedKeywordCallback: isReservedDartKeyword }) - : propertyName || ''; - } - - /** - * Renders model(s) to Dart type(s). - * - * @param model - */ - renderType(model: CommonModel | CommonModel[]): string { - if (Array.isArray(model) || Array.isArray(model.type)) { - return 'Object'; // fallback - } - if (model.$ref !== undefined) { - return this.nameType(model.$ref, model); - } - const kind = TypeHelpers.extractKind(model); - if ( - kind === ModelKind.PRIMITIVE || - kind === ModelKind.ARRAY - ) { - const format = model.getFromOriginalInput('format'); - return this.toClassType(this.toDartType(format || model.type, model)); - } - return this.nameType(model.$id, model); - } - - /** - * Returns the Dart corresponding type from CommonModel type or JSON schema format - * @param type - * @param model - */ - toDartType(type: string | undefined, model: CommonModel): string { - switch (type) { - case 'integer': - case 'int32': - case 'long': - case 'int64': - return 'int'; - case 'boolean': - return 'bool'; - case 'date': - return 'DateTime'; - case 'time': - return 'DateTime'; - case 'dateTime': - case 'date-time': - return 'DateTime'; - case 'string': - case 'password': - case 'byte': - return 'String'; - case 'float': - case 'double': - case 'number': - return 'double'; - case 'binary': - return 'byte[]'; - case 'array': { - let arrayItemModel = model.items; - //Since Dart dont support tuples, lets make sure that we combine the tuple types to find the appropriate array type - if (Array.isArray(model.items)) { - arrayItemModel = model.items.reduce((prevModel, currentModel) => { - return CommonModel.mergeCommonModels(CommonModel.toCommonModel(prevModel), CommonModel.toCommonModel(currentModel), {}); - }); - //If tuples and additionalItems make sure to find the appropriate type by merging all the tuples and additionalItems model together to find the combined type. - if (model.additionalItems !== undefined) { - arrayItemModel = CommonModel.mergeCommonModels(arrayItemModel, model.additionalItems, {}); - } - } - const newType = arrayItemModel ? this.renderType(arrayItemModel) : 'Object'; - if (this.options.collectionType && this.options.collectionType === 'List') { - return `List<${newType}>`; - } - return `${newType}[]`; - } - default: - return 'Object'; - } - } - - toClassType(type: string): string { - switch (type) { - case 'int': - case 'long': - return 'int'; - case 'boolean': - return 'bool'; - case 'float': - case 'double': - return 'double'; - default: - return `${type}`; - } - } - renderComments(lines: string | string[]): string { lines = FormatHelpers.breakLines(lines); - const newLiteral = lines.map(line => ` * ${line}`).join('\n'); + const newLiteral = lines.map((line: string) => ` * ${line}`).join('\n'); return `/** ${newLiteral} */`; } - renderAnnotation(annotationName: string, value?: any | Record): string { + renderAnnotation( + annotationName: string, + value?: any | Record + ): string { const name = `@${FormatHelpers.upperFirst(annotationName)}`; let values = undefined; if (value !== undefined) { if (typeof value === 'object') { - values = Object.entries(value || {}).map(([paramName, newValue]) => { - if (paramName && newValue !== undefined) { - return `${paramName}=${newValue}`; - } - return newValue; - }).filter(v => v !== undefined).join(', '); + values = Object.entries(value || {}) + .map(([paramName, newValue]) => { + if (paramName && newValue !== undefined) { + return `${paramName}=${newValue}`; + } + return newValue; + }) + .filter((v) => v !== undefined) + .join(', '); } else { values = value; } diff --git a/src/generators/dart/constrainer/EnumConstrainer.ts b/src/generators/dart/constrainer/EnumConstrainer.ts new file mode 100644 index 0000000000..1d27cbe0f8 --- /dev/null +++ b/src/generators/dart/constrainer/EnumConstrainer.ts @@ -0,0 +1,97 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { ConstrainedEnumModel, EnumModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { + EnumKeyConstraint, + EnumValueConstraint, + FormatHelpers +} from '../../../helpers'; +import { isReservedDartKeyword } from '../Constants'; + +export type ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_KEYS: ( + constrainedEnumModel: ConstrainedEnumModel, + enumModel: EnumModel, + value: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_KEYS: NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toConstantCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedDartKeyword); + } +}; + +export function defaultEnumKeyConstraints( + customConstraints?: Partial +): EnumKeyConstraint { + const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; + + return ({ enumKey, enumModel, constrainedEnumModel }) => { + let constrainedEnumKey = enumKey; + constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey); + constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey); + //If the enum key has been manipulated, lets make sure it don't clash with existing keys + if (constrainedEnumKey !== enumKey) { + constrainedEnumKey = constraints.NO_DUPLICATE_KEYS( + constrainedEnumModel, + enumModel, + constrainedEnumKey, + constraints.NAMING_FORMATTER! + ); + } + constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey); + return constrainedEnumKey; + }; +} + +export function defaultEnumValueConstraints(): EnumValueConstraint { + return ({ enumValue }) => { + let constrainedEnumValue = enumValue; + switch (typeof enumValue) { + case 'string': + case 'boolean': + constrainedEnumValue = `"${enumValue}"`; + break; + case 'bigint': + case 'number': { + constrainedEnumValue = enumValue; + break; + } + case 'object': { + constrainedEnumValue = `"${JSON.stringify(enumValue).replace( + /"/g, + '\\"' + )}"`; + break; + } + default: { + constrainedEnumValue = `"${enumValue}"`; + } + } + return constrainedEnumValue; + }; +} diff --git a/src/generators/dart/constrainer/ModelNameConstrainer.ts b/src/generators/dart/constrainer/ModelNameConstrainer.ts new file mode 100644 index 0000000000..95acebaad6 --- /dev/null +++ b/src/generators/dart/constrainer/ModelNameConstrainer.ts @@ -0,0 +1,49 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { isReservedDartKeyword } from '../Constants'; + +export type ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultModelNameConstraints: ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NAMING_FORMATTER: (value: string) => { + return FormatHelpers.toPascalCase(value); + }, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedDartKeyword); + } +}; + +export function defaultModelNameConstraints( + customConstraints?: Partial +): ModelNameConstraint { + const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; + + return ({ modelName }) => { + let constrainedValue = modelName; + constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue); + constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue); + constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue); + constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue); + constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); + return constrainedValue; + }; +} diff --git a/src/generators/dart/constrainer/PropertyKeyConstrainer.ts b/src/generators/dart/constrainer/PropertyKeyConstrainer.ts new file mode 100644 index 0000000000..6b14ab2452 --- /dev/null +++ b/src/generators/dart/constrainer/PropertyKeyConstrainer.ts @@ -0,0 +1,78 @@ +import { ConstrainedObjectModel, ObjectModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { isReservedDartKeyword } from '../Constants'; + +export type PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_PROPERTIES: ( + constrainedObjectModel: ConstrainedObjectModel, + objectModel: ObjectModel, + propertyName: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toCamelCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedDartKeyword); + } +}; + +export function defaultPropertyKeyConstraints( + customConstraints?: Partial +): PropertyKeyConstraint { + const constraints = { + ...DefaultPropertyKeyConstraints, + ...customConstraints + }; + + return ({ objectPropertyModel, constrainedObjectModel, objectModel }) => { + let constrainedPropertyKey = objectPropertyModel.propertyName; + + constrainedPropertyKey = constraints.NO_SPECIAL_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey); + constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS( + constrainedPropertyKey + ); + //If the property name has been manipulated, lets make sure it don't clash with existing properties + if (constrainedPropertyKey !== objectPropertyModel.propertyName) { + constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES( + constrainedObjectModel, + objectModel, + constrainedPropertyKey, + constraints.NAMING_FORMATTER + ); + } + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + return constrainedPropertyKey; + }; +} diff --git a/src/generators/dart/index.ts b/src/generators/dart/index.ts index 4c10bb3c57..a94a7d34b4 100644 --- a/src/generators/dart/index.ts +++ b/src/generators/dart/index.ts @@ -3,3 +3,19 @@ export * from './DartFileGenerator'; export { DART_DEFAULT_PRESET } from './DartPreset'; export type { DartPreset } from './DartPreset'; export * from './presets'; + +export { + defaultEnumKeyConstraints as dartDefaultEnumKeyConstraints, + DefaultEnumKeyConstraints as DartDefaultEnumKeyConstraints, + defaultEnumValueConstraints as dartDefaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; + +export { + DefaultModelNameConstraints as DartDefaultModelNameConstraints, + defaultModelNameConstraints as dartDefaultModelNameConstraints +} from './constrainer/ModelNameConstrainer'; + +export { + DefaultPropertyKeyConstraints as DartDefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints as dartDefaultPropertyKeyConstraints +} from './constrainer/PropertyKeyConstrainer'; diff --git a/src/generators/dart/presets/JsonSerializablePreset.ts b/src/generators/dart/presets/JsonSerializablePreset.ts index d545982e62..2e4b4ccdd9 100644 --- a/src/generators/dart/presets/JsonSerializablePreset.ts +++ b/src/generators/dart/presets/JsonSerializablePreset.ts @@ -1,5 +1,5 @@ -import {DartPreset} from '../DartPreset'; -import {FormatHelpers} from '../../../helpers/FormatHelpers'; +import { FormatHelpers } from '../../../helpers'; +import { DartPreset } from '../DartPreset'; /** * Preset which adds `json_serializable` related annotations to class's property getters. @@ -8,27 +8,33 @@ import {FormatHelpers} from '../../../helpers/FormatHelpers'; */ export const DART_JSON_PRESET: DartPreset = { class: { - self({renderer, model, content}) { - renderer.addDependency('import \'package:json_annotation/json_annotation.dart\';'); - const formattedModelName = renderer.nameType(model.$id); - const snakeformattedModelName = FormatHelpers.snakeCase(formattedModelName); - renderer.addDependency(`part '${snakeformattedModelName}.g.dart';`); - renderer.addDependency('@JsonSerializable()'); + self({ renderer, model, content }) { + const snakeformattedModelName = FormatHelpers.snakeCase(model.name); + renderer.dependencyManager.addDependency( + `import 'package:json_annotation/json_annotation.dart';` + ); + renderer.dependencyManager.addDependency( + `part '${snakeformattedModelName}.g.dart';` + ); + renderer.dependencyManager.addDependency('@JsonSerializable()'); return content; }, - additionalContent({renderer, model}) { - const formattedModelName = renderer.nameType(model.$id); - return `factory ${formattedModelName}.fromJson(Map json) => _$${formattedModelName}FromJson(json); -Map toJson() => _$${formattedModelName}ToJson(this);`; + additionalContent({ model }) { + return `factory ${model.name}.fromJson(Map json) => _$${model.name}FromJson(json); +Map toJson() => _$${model.name}ToJson(this);`; } - }, enum: { - self({renderer, model, content}) { - renderer.addDependency('import \'package:json_annotation/json_annotation.dart\';'); - const formattedModelName = renderer.nameType(model.$id); - const snakeformattedModelName = FormatHelpers.snakeCase(formattedModelName); - renderer.addDependency(`part '${snakeformattedModelName}.g.dart';`); - renderer.addDependency('@JsonEnum(alwaysCreate:true)'); + }, + enum: { + self({ renderer, model, content }) { + const snakeformattedModelName = FormatHelpers.snakeCase(model.name); + renderer.dependencyManager.addDependency( + `import 'package:json_annotation/json_annotation.dart';` + ); + renderer.dependencyManager.addDependency( + `part '${snakeformattedModelName}.g.dart';` + ); + renderer.dependencyManager.addDependency('@JsonEnum(alwaysCreate:true)'); return content; - }, + } } }; diff --git a/src/generators/dart/presets/index.ts b/src/generators/dart/presets/index.ts index 82b477a887..18212e3649 100644 --- a/src/generators/dart/presets/index.ts +++ b/src/generators/dart/presets/index.ts @@ -1,2 +1 @@ export * from './JsonSerializablePreset'; - diff --git a/src/generators/dart/renderers/ClassRenderer.ts b/src/generators/dart/renderers/ClassRenderer.ts index 3630aa8588..f758a71119 100644 --- a/src/generators/dart/renderers/ClassRenderer.ts +++ b/src/generators/dart/renderers/ClassRenderer.ts @@ -1,23 +1,26 @@ -import {DartRenderer} from '../DartRenderer'; -import {CommonModel, ClassPreset, PropertyType} from '../../../models'; -import {DefaultPropertyNames, getUniquePropertyName} from '../../../helpers'; +import { DartRenderer } from '../DartRenderer'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel +} from '../../../models'; +import { ClassPresetType } from '../DartPreset'; +import { DartOptions } from '../DartGenerator'; /** * Renderer for Dart's `class` type * * @extends DartRenderer */ -export class ClassRenderer extends DartRenderer { +export class ClassRenderer extends DartRenderer { async defaultSelf(): Promise { const content = [ await this.renderProperties(), await this.runCtorPreset(), await this.renderAccessors(), - await this.runAdditionalContentPreset(), + await this.runAdditionalContentPreset() ]; - const formattedName = this.nameType(`${this.model.$id}`); - return `class ${formattedName} { + return `class ${this.model.name} { ${this.indent(this.renderBlock(content, 2))} }`; } @@ -33,24 +36,16 @@ ${this.indent(this.renderBlock(content, 2))} const properties = this.model.properties || {}; const content: string[] = []; - for (const [propertyName, property] of Object.entries(properties)) { - const rendererProperty = await this.runPropertyPreset(propertyName, property); + for (const property of Object.values(properties)) { + const rendererProperty = await this.runPropertyPreset(property); content.push(rendererProperty); } - if (this.model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(this.model.patternProperties)) { - const propertyName = getUniquePropertyName(this.model, `${pattern}${DefaultPropertyNames.patternProperties}`); - const renderedPatternProperty = await this.runPropertyPreset(propertyName, patternModel, PropertyType.patternProperties); - content.push(renderedPatternProperty); - } - } - return this.renderBlock(content); } - runPropertyPreset(propertyName: string, property: CommonModel, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('property', {propertyName, property, type}); + runPropertyPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('property', { property }); } /** @@ -64,19 +59,14 @@ ${this.indent(this.renderBlock(content, 2))} } } -export const DART_DEFAULT_CLASS_PRESET: ClassPreset = { - self({renderer}) { +export const DART_DEFAULT_CLASS_PRESET: ClassPresetType = { + self({ renderer }) { return renderer.defaultSelf(); }, - property({renderer, propertyName, property, type}) { - propertyName = renderer.nameProperty(propertyName, property); - let propertyType = renderer.renderType(property); - if (type === PropertyType.additionalProperty || type === PropertyType.patternProperties) { - propertyType = `Map`; - } - return `${propertyType}? ${propertyName};`; + property({ property }) { + return `${property.property.type}? ${property.propertyName};`; }, - ctor({renderer,model}) { - return `${renderer.nameType(model.$id)}();`; + ctor({ model }) { + return `${model.name}();`; } }; diff --git a/src/generators/dart/renderers/EnumRenderer.ts b/src/generators/dart/renderers/EnumRenderer.ts index f92f8a8837..774f79df7d 100644 --- a/src/generators/dart/renderers/EnumRenderer.ts +++ b/src/generators/dart/renderers/EnumRenderer.ts @@ -1,28 +1,25 @@ -import {DartRenderer} from '../DartRenderer'; -import {EnumPreset} from '../../../models'; -import {FormatHelpers} from '../../../helpers'; +import { DartRenderer } from '../DartRenderer'; +import { ConstrainedEnumModel } from '../../../models'; +import { EnumPresetType } from '../DartPreset'; +import { DartOptions } from '../DartGenerator'; /** * Renderer for Dart's `enum` type * * @extends DartRenderer */ -export class EnumRenderer extends DartRenderer { +export class EnumRenderer extends DartRenderer { async defaultSelf(): Promise { - const content = [ - await this.renderItems(), - ]; - const formattedName = this.nameType(this.model.$id); - return `enum ${formattedName} { + const content = [await this.renderItems()]; + return `enum ${this.model.name} { ${this.indent(this.renderBlock(content, 2))} }`; } async renderItems(): Promise { - const enums = this.model.enum || []; const items: string[] = []; - for (const value of enums) { + for (const value of this.model.values) { const renderedItem = await this.runItemPreset(value); items.push(renderedItem); } @@ -30,58 +27,16 @@ ${this.indent(this.renderBlock(content, 2))} const content = items.join(', '); return `${content}`; } - - normalizeKey(value: any): string { - let key; - switch (typeof value) { - case 'bigint': - case 'number': { - key = 'number_${value}'; - break; - } - case 'boolean': { - key = `boolean_${value}`; - break; - } - case 'object': { - key = JSON.stringify(value); - break; - } - default: { - key = FormatHelpers.replaceSpecialCharacters(String(value), {exclude: [' '], separator: '_'}); - //Ensure no special char can be the beginning letter - if (!(/^[a-zA-Z]+$/).test(key.charAt(0))) { - key = `string_${key}`; - } - } - } - return FormatHelpers.toConstantCase(key); - } - - normalizeValue(value: any): string { - if (typeof value === 'number') { - return `NUMBER_${value}`; - } - if (typeof value === 'string') { - return `${value}`; - } - if (typeof value === 'object') { - return `${JSON.stringify(value).replace(/"/g, '\\"')}`; - } - return String(value); - } - runItemPreset(item: any): Promise { - return this.runPreset('item', {item}); + return this.runPreset('item', { item }); } } -export const DART_DEFAULT_ENUM_PRESET: EnumPreset = { - self({renderer}) { +export const DART_DEFAULT_ENUM_PRESET: EnumPresetType = { + self({ renderer }) { return renderer.defaultSelf(); }, - item({renderer, item}) { - const value = renderer.normalizeValue(item); - return `${value}`; - }, + item({ item }) { + return `${item.value}`; + } }; diff --git a/src/generators/go/Constants.ts b/src/generators/go/Constants.ts index 3cc948a342..784bf4bef1 100644 --- a/src/generators/go/Constants.ts +++ b/src/generators/go/Constants.ts @@ -1,3 +1,5 @@ +import { checkForReservedKeyword } from '../../helpers'; + export const RESERVED_GO_KEYWORDS = [ 'break', 'case', @@ -26,6 +28,9 @@ export const RESERVED_GO_KEYWORDS = [ 'var' ]; -export function isReservedGoKeyword(word: string): boolean { - return RESERVED_GO_KEYWORDS.includes(word); +export function isReservedGoKeyword( + word: string, + forceLowerCase = true +): boolean { + return checkForReservedKeyword(word, RESERVED_GO_KEYWORDS, forceLowerCase); } diff --git a/src/generators/go/GoConstrainer.ts b/src/generators/go/GoConstrainer.ts new file mode 100644 index 0000000000..9a7c2c3b88 --- /dev/null +++ b/src/generators/go/GoConstrainer.ts @@ -0,0 +1,56 @@ +import { Constraints } from '../../helpers'; +import { + defaultEnumKeyConstraints, + defaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; +import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; +import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; +import { GoTypeMapping } from './GoGenerator'; + +export const GoDefaultTypeMapping: GoTypeMapping = { + Object({ constrainedModel }): string { + return constrainedModel.name; + }, + Reference({ constrainedModel }): string { + return `${constrainedModel.name}`; + }, + Any(): string { + return 'interface{}'; + }, + Float(): string { + return 'float64'; + }, + Integer(): string { + return 'int'; + }, + String(): string { + return 'string'; + }, + Boolean(): string { + return 'bool'; + }, + Tuple(): string { + //Because Go have no notion of tuples (and no custom implementation), we have to render it as a list of any value. + return '[]interface{}'; + }, + Array({ constrainedModel }): string { + return `[]${constrainedModel.valueModel.type}`; + }, + Enum({ constrainedModel }): string { + return constrainedModel.name; + }, + Union(): string { + //Because Go have no notion of unions (and no custom implementation), we have to render it as any value. + return 'interface{}'; + }, + Dictionary({ constrainedModel }): string { + return `map[${constrainedModel.key.type}]${constrainedModel.value.type}`; + } +}; + +export const GoDefaultConstraints: Constraints = { + enumKey: defaultEnumKeyConstraints(), + enumValue: defaultEnumValueConstraints(), + modelName: defaultModelNameConstraints(), + propertyKey: defaultPropertyKeyConstraints() +}; diff --git a/src/generators/go/GoDependencyManager.ts b/src/generators/go/GoDependencyManager.ts new file mode 100644 index 0000000000..bc4d23a61b --- /dev/null +++ b/src/generators/go/GoDependencyManager.ts @@ -0,0 +1,8 @@ +import { AbstractDependencyManager } from '../AbstractDependencyManager'; +import { GoOptions } from './GoGenerator'; + +export class GoDependencyManager extends AbstractDependencyManager { + constructor(public options: GoOptions, dependencies: string[] = []) { + super(dependencies); + } +} diff --git a/src/generators/go/GoFileGenerator.ts b/src/generators/go/GoFileGenerator.ts index e0ef44aae8..785a47a0de 100644 --- a/src/generators/go/GoFileGenerator.ts +++ b/src/generators/go/GoFileGenerator.ts @@ -1,23 +1,42 @@ import { GoGenerator, GoRenderCompleteModelOptions } from './GoGenerator'; -import { CommonInputModel, OutputModel } from '../../models'; +import { InputMetaModel, OutputModel } from '../../models'; import * as path from 'path'; import { AbstractFileGenerator } from '../AbstractFileGenerator'; import { FileHelpers } from '../../helpers'; -export class GoFileGenerator extends GoGenerator implements AbstractFileGenerator { +export class GoFileGenerator + extends GoGenerator + implements AbstractFileGenerator +{ /** - * Generates all the models to an output directory each model with their own separate files. - * + * Generates all the models to an output directory each model with their own separate files. + * * @param input * @param outputDirectory where you want the models generated to * @param options + * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ - public async generateToFiles(input: Record | CommonInputModel, outputDirectory: string, options: GoRenderCompleteModelOptions): Promise { + public async generateToFiles( + input: Record | InputMetaModel, + outputDirectory: string, + options: GoRenderCompleteModelOptions, + ensureFilesWritten = false + ): Promise { let generatedModels = await this.generateCompleteModels(input, options); - generatedModels = generatedModels.filter((outputModel) => { return outputModel.modelName !== undefined; }); + //Filter anything out that have not been successfully generated + generatedModels = generatedModels.filter((outputModel) => { + return outputModel.modelName !== ''; + }); for (const outputModel of generatedModels) { - const filePath = path.resolve(outputDirectory, `${outputModel.modelName}.go`); - await FileHelpers.writerToFileSystem(outputModel.result, filePath); + const filePath = path.resolve( + outputDirectory, + `${outputModel.modelName}.go` + ); + await FileHelpers.writerToFileSystem( + outputModel.result, + filePath, + ensureFilesWritten + ); } return generatedModels; } diff --git a/src/generators/go/GoGenerator.ts b/src/generators/go/GoGenerator.ts index 991545e736..c4c494ae2e 100644 --- a/src/generators/go/GoGenerator.ts +++ b/src/generators/go/GoGenerator.ts @@ -3,88 +3,140 @@ import { CommonGeneratorOptions, defaultGeneratorOptions } from '../AbstractGenerator'; -import { CommonModel, CommonInputModel, RenderOutput } from '../../models'; -import { TypeHelpers, ModelKind, FormatHelpers } from '../../helpers'; +import { + InputMetaModel, + RenderOutput, + ConstrainedObjectModel, + ConstrainedEnumModel, + ConstrainedMetaModel, + MetaModel +} from '../../models'; +import { + constrainMetaModel, + Constraints, + split, + SplitOptions, + TypeMapping +} from '../../helpers'; import { GoPreset, GO_DEFAULT_PRESET } from './GoPreset'; import { StructRenderer } from './renderers/StructRenderer'; import { EnumRenderer } from './renderers/EnumRenderer'; -import { pascalCaseTransformMerge } from 'change-case'; import { Logger } from '../../utils/LoggingInterface'; -import { isReservedGoKeyword } from './Constants'; -/** - * The Go naming convention type - */ -export type GoNamingConvention = { - type?: (name: string | undefined, ctx: { model: CommonModel, inputModel: CommonInputModel, reservedKeywordCallback?: (name: string) => boolean }) => string; - field?: (name: string | undefined, ctx: { model: CommonModel, inputModel: CommonInputModel, field?: CommonModel, reservedKeywordCallback?: (name: string) => boolean }) => string; -}; - -/** - * A GoNamingConvention implementation for Go - */ -export const GoNamingConventionImplementation: GoNamingConvention = { - type: (name: string | undefined, ctx) => { - if (!name) { return ''; } - let formattedName = FormatHelpers.toPascalCase(name, { transform: pascalCaseTransformMerge }); - if (ctx.reservedKeywordCallback !== undefined && ctx.reservedKeywordCallback(formattedName)) { - formattedName = FormatHelpers.toPascalCase(`reserved_${formattedName}`); - } - return formattedName; - }, - // eslint-disable-next-line sonarjs/no-identical-functions - field: (name: string | undefined, ctx) => { - if (!name) { return ''; } - let formattedName = FormatHelpers.toPascalCase(name, { transform: pascalCaseTransformMerge }); - if (ctx.reservedKeywordCallback !== undefined && ctx.reservedKeywordCallback(formattedName)) { - formattedName = FormatHelpers.toPascalCase(`reserved_${formattedName}`); - if (Object.keys(ctx.model.properties || {}).includes(formattedName)) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return GoNamingConventionImplementation.field!(`reserved_${formattedName}`, ctx); - } - } - return formattedName; - } -}; +import { GoDefaultConstraints, GoDefaultTypeMapping } from './GoConstrainer'; +import { DeepPartial, mergePartialAndDefault } from '../../utils/Partials'; +import { GoDependencyManager } from './GoDependencyManager'; export interface GoOptions extends CommonGeneratorOptions { - namingConvention?: GoNamingConvention; + typeMapping: TypeMapping; + constraints: Constraints; } +export type GoTypeMapping = TypeMapping; export interface GoRenderCompleteModelOptions { - packageName: string + packageName: string; } /** * Generator for Go */ -export class GoGenerator extends AbstractGenerator { +export class GoGenerator extends AbstractGenerator< + GoOptions, + GoRenderCompleteModelOptions +> { static defaultOptions: GoOptions = { ...defaultGeneratorOptions, defaultPreset: GO_DEFAULT_PRESET, - namingConvention: GoNamingConventionImplementation + typeMapping: GoDefaultTypeMapping, + constraints: GoDefaultConstraints }; - constructor( - options: GoOptions = GoGenerator.defaultOptions, - ) { - super('Go', GoGenerator.defaultOptions, options); + + static defaultCompleteModelOptions: GoRenderCompleteModelOptions = { + packageName: 'AsyncapiModels' + }; + + constructor(options?: DeepPartial) { + const realizedOptions = GoGenerator.getGoOptions(options); + super('Go', realizedOptions); } - reservedGoKeyword(name: string): boolean { - return isReservedGoKeyword(name); + + /** + * Returns the Go options by merging custom options with default ones. + */ + static getGoOptions(options?: DeepPartial): GoOptions { + const optionsToUse = mergePartialAndDefault( + GoGenerator.defaultOptions, + options + ) as GoOptions; + //Always overwrite the dependency manager unless user explicitly state they want it (ignore default temporary dependency manager) + if (options?.dependencyManager === undefined) { + optionsToUse.dependencyManager = () => { + return new GoDependencyManager(optionsToUse); + }; + } + return optionsToUse; + } + + /** + * Wrapper to get an instance of the dependency manager + */ + getDependencyManager(options: GoOptions): GoDependencyManager { + return this.getDependencyManagerInstance(options) as GoDependencyManager; + } + + splitMetaModel(model: MetaModel): MetaModel[] { + //These are the models that we have separate renderers for + const metaModelsToSplit: SplitOptions = { + splitEnum: true, + splitObject: true + }; + return split(model, metaModelsToSplit); } - render(model: CommonModel, inputModel: CommonInputModel): Promise { - const kind = TypeHelpers.extractKind(model); - switch (kind) { - case ModelKind.UNION: - // We don't support union in Go generator, however, if union is an object, we render it as a struct. - if (!model.type?.includes('object')) { break; } - return this.renderStruct(model, inputModel); - case ModelKind.OBJECT: - return this.renderStruct(model, inputModel); - case ModelKind.ENUM: - return this.renderEnum(model, inputModel); + + constrainToMetaModel( + model: MetaModel, + options: DeepPartial + ): ConstrainedMetaModel { + const optionsToUse = GoGenerator.getGoOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + return constrainMetaModel( + this.options.typeMapping, + this.options.constraints, + { + metaModel: model, + dependencyManager: dependencyManagerToUse, + options: this.options, + constrainedName: '' //This is just a placeholder, it will be constrained within the function + } + ); + } + + render( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = GoGenerator.getGoOptions({ + ...this.options, + ...options + }); + if (model instanceof ConstrainedObjectModel) { + return this.renderStruct(model, inputModel, optionsToUse); + } else if (model instanceof ConstrainedEnumModel) { + return this.renderEnum(model, inputModel, optionsToUse); } - Logger.warn(`Go generator, cannot generate this type of model, ${model.$id}`); - return Promise.resolve(RenderOutput.toRenderOutput({ result: '', renderedName: '', dependencies: [] })); + Logger.warn( + `Go generator, cannot generate this type of model, ${model.name}` + ); + return Promise.resolve( + RenderOutput.toRenderOutput({ + result: '', + renderedName: '', + dependencies: [] + }) + ); } /** @@ -94,35 +146,94 @@ export class GoGenerator extends AbstractGenerator { - const outputModel = await this.render(model, inputModel); + async renderCompleteModel( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + completeModelOptions: Partial, + options: DeepPartial + ): Promise { + const completeModelOptionsToUse = mergePartialAndDefault( + GoGenerator.defaultCompleteModelOptions, + completeModelOptions + ) as GoRenderCompleteModelOptions; + const optionsToUse = GoGenerator.getGoOptions({ + ...this.options, + ...options + }); + const outputModel = await this.render(model, inputModel, optionsToUse); let importCode = ''; if (outputModel.dependencies.length > 0) { - const dependencies = outputModel.dependencies.map((dependency) => { return `"${dependency}"`; }).join('\n'); + const dependencies = outputModel.dependencies + .map((dependency) => { + return `"${dependency}"`; + }) + .join('\n'); importCode = `import ( ${dependencies} )`; } const outputContent = ` -package ${options.packageName} +package ${completeModelOptionsToUse.packageName} ${importCode} ${outputModel.result}`; - return RenderOutput.toRenderOutput({ result: outputContent, renderedName: outputModel.renderedName, dependencies: outputModel.dependencies }); + return RenderOutput.toRenderOutput({ + result: outputContent, + renderedName: outputModel.renderedName, + dependencies: outputModel.dependencies + }); } - async renderEnum(model: CommonModel, inputModel: CommonInputModel): Promise { + async renderEnum( + model: ConstrainedEnumModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = GoGenerator.getGoOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const presets = this.getPresets('enum'); - const renderer = new EnumRenderer(this.options, this, presets, model, inputModel); + const renderer = new EnumRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = renderer.nameType(model.$id, model); - return RenderOutput.toRenderOutput({ result, renderedName, dependencies: renderer.dependencies }); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } - async renderStruct(model: CommonModel, inputModel: CommonInputModel): Promise { + async renderStruct( + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = GoGenerator.getGoOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const presets = this.getPresets('struct'); - const renderer = new StructRenderer(this.options, this, presets, model, inputModel); + const renderer = new StructRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = renderer.nameType(model.$id, model); - return RenderOutput.toRenderOutput({ result, renderedName, dependencies: renderer.dependencies }); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } } diff --git a/src/generators/go/GoPreset.ts b/src/generators/go/GoPreset.ts index 0b0fc6cca4..afef892a59 100644 --- a/src/generators/go/GoPreset.ts +++ b/src/generators/go/GoPreset.ts @@ -1,30 +1,38 @@ -/* eslint-disable @typescript-eslint/ban-types */ import { AbstractRenderer } from '../AbstractRenderer'; -import { Preset, CommonModel, CommonPreset, PresetArgs, EnumPreset } from '../../models'; -import { StructRenderer, GO_DEFAULT_STRUCT_PRESET } from './renderers/StructRenderer'; +import { + Preset, + CommonPreset, + PresetArgs, + EnumPreset, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel +} from '../../models'; +import { + StructRenderer, + GO_DEFAULT_STRUCT_PRESET +} from './renderers/StructRenderer'; import { EnumRenderer, GO_DEFAULT_ENUM_PRESET } from './renderers/EnumRenderer'; +import { GoOptions } from './GoGenerator'; -export enum FieldType { - field, - additionalProperty, - patternProperties -} export interface FieldArgs { - fieldName: string; - field: CommonModel; - type: FieldType; + field: ConstrainedObjectPropertyModel; } -export interface StructPreset extends CommonPreset { - field?: (args: PresetArgs & FieldArgs) => Promise | string; +export interface StructPreset + extends CommonPreset { + field?: ( + args: PresetArgs & FieldArgs + ) => Promise | string; } +export type StructPresetType = StructPreset; +export type EnumPresetType = EnumPreset; -export type GoPreset = Preset<{ - struct: StructPreset; - enum: EnumPreset +export type GoPreset = Preset<{ + struct: StructPresetType; + enum: EnumPresetType; }>; export const GO_DEFAULT_PRESET: GoPreset = { struct: GO_DEFAULT_STRUCT_PRESET, - enum: GO_DEFAULT_ENUM_PRESET, + enum: GO_DEFAULT_ENUM_PRESET }; diff --git a/src/generators/go/GoRenderer.ts b/src/generators/go/GoRenderer.ts index 1c01de1daf..142e225d3f 100644 --- a/src/generators/go/GoRenderer.ts +++ b/src/generators/go/GoRenderer.ts @@ -1,121 +1,30 @@ import { AbstractRenderer } from '../AbstractRenderer'; import { GoGenerator, GoOptions } from './GoGenerator'; -import { CommonModel, CommonInputModel, Preset } from '../../models'; +import { InputMetaModel, Preset, ConstrainedMetaModel } from '../../models'; import { FormatHelpers } from '../../helpers/FormatHelpers'; -import { DefaultPropertyNames, getUniquePropertyName } from '../../helpers'; -import { FieldType } from './GoPreset'; -import { isReservedGoKeyword } from './Constants'; +import { GoDependencyManager } from './GoDependencyManager'; /** * Common renderer for Go types - * + * * @extends AbstractRenderer */ -export abstract class GoRenderer extends AbstractRenderer { +export abstract class GoRenderer< + RendererModelType extends ConstrainedMetaModel +> extends AbstractRenderer { constructor( options: GoOptions, generator: GoGenerator, presets: Array<[Preset, unknown]>, - model: CommonModel, - inputModel: CommonInputModel, + model: RendererModelType, + inputModel: InputMetaModel, + public dependencyManager: GoDependencyManager ) { super(options, generator, presets, model, inputModel); } - async renderFields(): Promise { - const fields = this.model.properties || {}; - const content: string[] = []; - - for (const [fieldName, field] of Object.entries(fields)) { - const renderField = await this.runFieldPreset(fieldName, field); - content.push(renderField); - } - - if (this.model.additionalProperties !== undefined) { - const propertyName = getUniquePropertyName(this.model, DefaultPropertyNames.additionalProperties); - const additionalProperty = await this.runFieldPreset(propertyName, this.model.additionalProperties, FieldType.additionalProperty); - content.push(additionalProperty); - } - - if (this.model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(this.model.patternProperties)) { - const propertyName = getUniquePropertyName(this.model, `${pattern}${DefaultPropertyNames.patternProperties}`); - const renderedPatternProperty = await this.runFieldPreset(propertyName, patternModel, FieldType.patternProperties); - content.push(renderedPatternProperty); - } - } - return this.renderBlock(content); - } - - /** - * Renders the name of a type based on provided generator option naming convention type function. - * - * This is used to render names of models and then later used if that class is referenced from other models. - * - * @param name - * @param model - */ - nameType(name: string | undefined, model?: CommonModel): string { - return this.options?.namingConvention?.type - ? this.options.namingConvention.type(name, { model: model || this.model, inputModel: this.inputModel, reservedKeywordCallback: isReservedGoKeyword }) - : name || ''; - } - - /** - * Renders the name of a field based on provided generator option naming convention field function. - * - * @param fieldName - * @param field - */ - nameField(fieldName: string | undefined, field?: CommonModel): string { - return this.options?.namingConvention?.field - ? this.options.namingConvention.field(fieldName, { model: this.model, inputModel: this.inputModel, field, reservedKeywordCallback: isReservedGoKeyword }) - : fieldName || ''; - } - - runFieldPreset(fieldName: string, field: CommonModel, type: FieldType = FieldType.field): Promise { - return this.runPreset('field', { fieldName, field, type }); - } - - renderType(model: CommonModel): string { - if (model.$ref !== undefined) { - const formattedRef = this.nameType(model.$ref); - return `*${formattedRef}`; - } - - if (Array.isArray(model.type)) { - return model.type.length > 1 ? '[]interface{}' : `[]${this.toGoType(model.type[0], model)}`; - } - - return this.toGoType(model.type, model); - } - renderComments(lines: string | string[]): string { lines = FormatHelpers.breakLines(lines); - return lines.map(line => `// ${line}`).join('\n'); - } - - /* eslint-disable sonarjs/no-duplicate-string */ - toGoType(type: string | undefined, model: CommonModel): string { - switch (type) { - case 'string': - return 'string'; - case 'integer': - return 'int'; - case 'number': - return 'float64'; - case 'boolean': - return 'bool'; - case 'object': - return 'interface{}'; - case 'array': { - if (Array.isArray(model.items)) { - return model.items.length > 1 ? '[]interface{}' : `[]${this.renderType(model.items[0])}`; - } - const arrayType = model.items ? this.renderType(model.items) : 'interface{}'; - return `[]${arrayType}`; - } - default: return 'interface{}'; - } + return lines.map((line) => `// ${line}`).join('\n'); } } diff --git a/src/generators/go/constrainer/EnumConstrainer.ts b/src/generators/go/constrainer/EnumConstrainer.ts new file mode 100644 index 0000000000..6eb0384e9c --- /dev/null +++ b/src/generators/go/constrainer/EnumConstrainer.ts @@ -0,0 +1,97 @@ +import { ConstrainedEnumModel, EnumModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { + FormatHelpers, + EnumKeyConstraint, + EnumValueConstraint +} from '../../../helpers'; +import { isReservedGoKeyword } from '../Constants'; + +export type ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_KEYS: ( + constrainedEnumModel: ConstrainedEnumModel, + enumModel: EnumModel, + newEnumKey: string, + namingFormatter: (value: string) => string, + enumKeyToCheck: string, + onNameChange: () => string, + onNameChangeToCheck: () => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude '_', '$' because they are allowed as enum keys + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: ['_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_KEYS: NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toPascalCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedGoKeyword); + } +}; + +export function defaultEnumKeyConstraints( + customConstraints?: Partial +): EnumKeyConstraint { + const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; + + return ({ enumKey, enumModel, constrainedEnumModel }) => { + let constrainedEnumKey = enumKey; + constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey); + constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey); + //If the enum key has been manipulated, lets make sure it don't clash with existing keys + if (constrainedEnumKey !== enumKey) { + //Must check against the enum key with the constrained enum model name + constrainedEnumKey = constraints.NO_DUPLICATE_KEYS( + constrainedEnumModel, + enumModel, + constrainedEnumKey, + constraints.NAMING_FORMATTER, + `${constrainedEnumModel.name}_${constrainedEnumKey}`, + () => { + return `reserved_${constrainedEnumKey}`; + }, + () => { + return `${constrainedEnumModel.name}_reserved_${constrainedEnumKey}`; + } + ); + } + constrainedEnumKey = `${constrainedEnumModel.name}_${constrainedEnumKey}`; + constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey); + return constrainedEnumKey; + }; +} + +export function defaultEnumValueConstraints(): EnumValueConstraint { + return ({ enumValue }) => { + let constrainedEnumValue: any = JSON.stringify(enumValue); + switch (typeof enumValue) { + case 'string': + constrainedEnumValue = `"${enumValue}"`; + break; + case 'number': + case 'bigint': + constrainedEnumValue = enumValue; + break; + } + return constrainedEnumValue; + }; +} diff --git a/src/generators/go/constrainer/ModelNameConstrainer.ts b/src/generators/go/constrainer/ModelNameConstrainer.ts new file mode 100644 index 0000000000..890eba255f --- /dev/null +++ b/src/generators/go/constrainer/ModelNameConstrainer.ts @@ -0,0 +1,50 @@ +import { + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { isReservedGoKeyword } from '../Constants'; + +export type ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultModelNameConstraints: ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NAMING_FORMATTER: (value: string) => { + return FormatHelpers.toPascalCase(value); + }, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedGoKeyword); + } +}; + +export function defaultModelNameConstraints( + customConstraints?: Partial +): ModelNameConstraint { + const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; + + return ({ modelName }) => { + let constrainedValue = modelName; + constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue); + constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue); + constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue); + constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue); + constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); + return constrainedValue; + }; +} diff --git a/src/generators/go/constrainer/PropertyKeyConstrainer.ts b/src/generators/go/constrainer/PropertyKeyConstrainer.ts new file mode 100644 index 0000000000..bb040de0c7 --- /dev/null +++ b/src/generators/go/constrainer/PropertyKeyConstrainer.ts @@ -0,0 +1,78 @@ +import { ConstrainedObjectModel, ObjectModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { isReservedGoKeyword } from '../Constants'; + +export type PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_PROPERTIES: ( + constrainedObjectModel: ConstrainedObjectModel, + objectModel: ObjectModel, + propertyName: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toPascalCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedGoKeyword); + } +}; + +export function defaultPropertyKeyConstraints( + customConstraints?: Partial +): PropertyKeyConstraint { + const constraints = { + ...DefaultPropertyKeyConstraints, + ...customConstraints + }; + + return ({ objectPropertyModel, constrainedObjectModel, objectModel }) => { + let constrainedPropertyKey = objectPropertyModel.propertyName; + + constrainedPropertyKey = constraints.NO_SPECIAL_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey); + constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS( + constrainedPropertyKey + ); + //If the property name has been manipulated, lets make sure it don't clash with existing properties + if (constrainedPropertyKey !== objectPropertyModel.propertyName) { + constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES( + constrainedObjectModel, + objectModel, + constrainedPropertyKey, + constraints.NAMING_FORMATTER + ); + } + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + return constrainedPropertyKey; + }; +} diff --git a/src/generators/go/index.ts b/src/generators/go/index.ts index e408d8ecc4..62b65aa2a2 100644 --- a/src/generators/go/index.ts +++ b/src/generators/go/index.ts @@ -2,3 +2,19 @@ export * from './GoGenerator'; export * from './GoFileGenerator'; export { GO_DEFAULT_PRESET } from './GoPreset'; export type { GoPreset } from './GoPreset'; + +export { + defaultEnumKeyConstraints as goDefaultEnumKeyConstraints, + DefaultEnumKeyConstraints as GoDefaultEnumKeyConstraints, + defaultEnumValueConstraints as goDefaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; + +export { + DefaultModelNameConstraints as GoDefaultModelNameConstraints, + defaultModelNameConstraints as goDefaultModelNameConstraints +} from './constrainer/ModelNameConstrainer'; + +export { + DefaultPropertyKeyConstraints as GoDefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints as goDefaultPropertyKeyConstraints +} from './constrainer/PropertyKeyConstrainer'; diff --git a/src/generators/go/renderers/EnumRenderer.ts b/src/generators/go/renderers/EnumRenderer.ts index c7fc3af93f..b2b7f81a5a 100644 --- a/src/generators/go/renderers/EnumRenderer.ts +++ b/src/generators/go/renderers/EnumRenderer.ts @@ -1,39 +1,49 @@ import { GoRenderer } from '../GoRenderer'; -import { EnumPreset, CommonModel } from '../../../models'; -import { FormatHelpers } from '../../../helpers'; +import { ConstrainedEnumModel } from '../../../models'; +import { EnumPresetType } from '../GoPreset'; +import { GoOptions } from '../GoGenerator'; /** * Renderer for Go's `enum` type - * + * + * This renderer is a generic solution that works for all types of enum values. + * This is also why you wont see `type MyEnum string´ even if possible. + * * @extends GoRenderer */ -export class EnumRenderer extends GoRenderer { +export class EnumRenderer extends GoRenderer { public defaultSelf(): string { - const formattedName = this.nameType(this.model.$id); - const type = this.enumType(this.model); - const doc = formattedName && this.renderCommentForEnumType(formattedName, type); - // eslint-disable-next-line sonarjs/no-duplicate-string - if (type === 'interface{}') { - return `${doc} -type ${formattedName} ${type}`; - } - - const enumValues = this.renderConstValuesForEnumType(formattedName, type, this.model.enum); + const doc = this.renderCommentForEnumType(this.model.name, this.model.type); + const enumValues = this.renderConstValuesForEnumType(); + const temp = this.model.values.map((value) => { + return `${this.model.name}Values[${value.key}]: ${value.key},`; + }); + const values = this.model.values + .map((value) => { + return value.value; + }) + .join(','); return `${doc} -type ${formattedName} ${type} +type ${this.model.name} uint const ( ${this.indent(this.renderBlock(enumValues))} -)`; - } +) - enumType(model: CommonModel): string { - if (this.model.type === undefined || Array.isArray(this.model.type)) { - return 'interface{}'; - } +// Value returns the value of the enum. +func (op ${this.model.name}) Value() any { + if op >= ${this.model.name}(len(${this.model.name}Values)) { + return nil + } + return ${this.model.name}Values[op] +} - return this.toGoType(this.model.type, model); +var ${this.model.name}Values = []any{${values}} +var ValuesTo${this.model.name} = map[any]${this.model.name}{ +${this.indent(this.renderBlock(temp))} +} +`; } renderCommentForEnumType(name: string, type: string): string { @@ -41,28 +51,21 @@ ${this.indent(this.renderBlock(enumValues))} return this.renderComments(`${name} represents an enum of ${globalType}.`); } - renderConstValuesForEnumType(typeName: string, innerType: string, values: string[]): string[] { - const firstName = typeName.concat(FormatHelpers.upperFirst(FormatHelpers.toCamelCase(values[0].toString()))); - - let enumValues = [innerType === 'string' ? `${firstName} ${typeName} = "${values[0]}"` : `${firstName} ${typeName} = iota`]; - - for (const value of values.slice(1)) { - const name = typeName.concat(FormatHelpers.upperFirst(FormatHelpers.toCamelCase(value))); - - if (innerType === 'string') { - enumValues = enumValues.concat(`${name} = "${value}"`); + renderConstValuesForEnumType(): string[] { + return this.model.values.map((enumValue, index) => { + if (index === 0) { + return `${enumValue.key} ${this.model.name} = iota`; } - if (innerType === 'int') { - enumValues = enumValues.concat(`${name}`); + if (typeof enumValue.value === 'string') { + return enumValue.key; } - } - - return enumValues; + return enumValue.key; + }); } } -export const GO_DEFAULT_ENUM_PRESET: EnumPreset = { +export const GO_DEFAULT_ENUM_PRESET: EnumPresetType = { self({ renderer }) { return renderer.defaultSelf(); - }, + } }; diff --git a/src/generators/go/renderers/StructRenderer.ts b/src/generators/go/renderers/StructRenderer.ts index e8fb19799e..bfffdec0be 100644 --- a/src/generators/go/renderers/StructRenderer.ts +++ b/src/generators/go/renderers/StructRenderer.ts @@ -1,38 +1,59 @@ import { GoRenderer } from '../GoRenderer'; -import { FieldType, StructPreset } from '../GoPreset'; +import { StructPresetType } from '../GoPreset'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ConstrainedReferenceModel +} from '../../../models'; +import { GoOptions } from '../GoGenerator'; /** * Renderer for Go's `struct` type - * + * * @extends GoRenderer */ -export class StructRenderer extends GoRenderer { +export class StructRenderer extends GoRenderer { public async defaultSelf(): Promise { const content = [ await this.renderFields(), await this.runAdditionalContentPreset() ]; - - const formattedName = this.nameType(this.model.$id); - const doc = this.renderComments(`${formattedName} represents a ${formattedName} model.`); - + + const doc = this.renderComments( + `${this.model.name} represents a ${this.model.name} model.` + ); + return `${doc} -type ${formattedName} struct { +type ${this.model.name} struct { ${this.indent(this.renderBlock(content, 2))} }`; } + + async renderFields(): Promise { + const fields = this.model.properties || {}; + const content: string[] = []; + + for (const field of Object.values(fields)) { + const renderField = await this.runFieldPreset(field); + content.push(renderField); + } + return this.renderBlock(content); + } + + runFieldPreset(field: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('field', { field }); + } } -export const GO_DEFAULT_STRUCT_PRESET: StructPreset = { +export const GO_DEFAULT_STRUCT_PRESET: StructPresetType = { self({ renderer }) { return renderer.defaultSelf(); }, - field({ fieldName, field, renderer, type }) { - fieldName = renderer.nameField(fieldName, field); - let fieldType = renderer.renderType(field); - if (type === FieldType.additionalProperty || type === FieldType.patternProperties) { - fieldType = `map[string]${fieldType}`; + field({ field }) { + let fieldType = field.property.type; + if (field.property instanceof ConstrainedReferenceModel) { + fieldType = `*${fieldType}`; } - return `${ fieldName } ${ fieldType }`; - }, + return `${field.propertyName} ${fieldType}`; + } }; diff --git a/src/generators/index.ts b/src/generators/index.ts index 57fcdf36d8..f26efad2f1 100644 --- a/src/generators/index.ts +++ b/src/generators/index.ts @@ -5,5 +5,8 @@ export * from './dart'; export * from './csharp'; export * from './javascript'; export * from './typescript'; +export * from './python'; export * from './go'; +export * from './rust'; +export * from './kotlin'; export * from './AbstractFileGenerator'; diff --git a/src/generators/java/Constants.ts b/src/generators/java/Constants.ts index a4cc39f725..7089fbcb5c 100644 --- a/src/generators/java/Constants.ts +++ b/src/generators/java/Constants.ts @@ -1,56 +1,61 @@ +import { checkForReservedKeyword } from '../../helpers'; + export const RESERVED_JAVA_KEYWORDS = [ - 'abstract', - 'continue', - 'for', - 'new', + 'abstract', + 'continue', + 'for', + 'new', 'switch', - 'assert', - 'default', - 'goto', - 'package', - 'synchronized', - 'boolean', - 'do', - 'if', - 'private', - 'this', - 'break', - 'double', - 'implements', - 'protected', - 'throw', - 'byte', - 'else', - 'import', - 'public', - 'throws', - 'case', - 'enum', - 'instanceof', - 'return', - 'transient', - 'catch', - 'extends', - 'int', - 'short', - 'try', - 'char', - 'final', - 'interface', - 'static', - 'void', - 'class', - 'finally', - 'long', - 'strictfp', - 'volatile', - 'const', - 'float', - 'native', - 'super', + 'assert', + 'default', + 'goto', + 'package', + 'synchronized', + 'boolean', + 'do', + 'if', + 'private', + 'this', + 'break', + 'double', + 'implements', + 'protected', + 'throw', + 'byte', + 'else', + 'import', + 'public', + 'throws', + 'case', + 'enum', + 'instanceof', + 'return', + 'transient', + 'catch', + 'extends', + 'int', + 'short', + 'try', + 'char', + 'final', + 'interface', + 'static', + 'void', + 'class', + 'finally', + 'long', + 'strictfp', + 'volatile', + 'const', + 'float', + 'native', + 'super', 'while' ]; -export function isReservedJavaKeyword(word: string): boolean { - return RESERVED_JAVA_KEYWORDS.includes(word); +export function isReservedJavaKeyword( + word: string, + forceLowerCase = true +): boolean { + return checkForReservedKeyword(word, RESERVED_JAVA_KEYWORDS, forceLowerCase); } diff --git a/src/generators/java/JavaConstrainer.ts b/src/generators/java/JavaConstrainer.ts new file mode 100644 index 0000000000..f13d411a54 --- /dev/null +++ b/src/generators/java/JavaConstrainer.ts @@ -0,0 +1,188 @@ +import { ConstrainedEnumValueModel } from '../../models'; +import { + defaultEnumKeyConstraints, + defaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; +import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; +import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; +import { JavaTypeMapping } from './JavaGenerator'; + +function enumFormatToNumberType( + enumValueModel: ConstrainedEnumValueModel, + format: string +): string { + switch (format) { + case 'integer': + case 'int32': + return 'int'; + case 'long': + case 'int64': + return 'long'; + case 'float': + return 'float'; + case 'double': + return 'double'; + default: + if (Number.isInteger(enumValueModel.value)) { + return 'int'; + } + return 'double'; + } +} + +const fromEnumValueToType = ( + enumValueModel: ConstrainedEnumValueModel, + format: string +): string => { + switch (typeof enumValueModel.value) { + case 'boolean': + return 'boolean'; + case 'number': + case 'bigint': + return enumFormatToNumberType(enumValueModel, format); + case 'object': + return 'Object'; + case 'string': + return 'String'; + default: + return 'Object'; + } +}; + +/** + * Converts union of different number types to the most strict type it can be. + * + * int + double = double (long + double, float + double can never happen, otherwise this would be converted to double) + * int + float = float (long + float can never happen, otherwise this would be the case as well) + * int + long = long + */ +const interpretUnionValueType = (types: string[]): string => { + if (types.includes('double')) { + return 'double'; + } + + if (types.includes('float')) { + return 'float'; + } + + if (types.includes('long')) { + return 'long'; + } + + return 'Object'; +}; + +export const JavaDefaultTypeMapping: JavaTypeMapping = { + Object({ constrainedModel }): string { + return constrainedModel.name; + }, + Reference({ constrainedModel }): string { + return constrainedModel.name; + }, + Any(): string { + return 'Object'; + }, + Float({ constrainedModel }): string { + let type = 'Double'; + const format = + constrainedModel.originalInput && + constrainedModel.originalInput['format']; + switch (format) { + case 'float': + type = 'float'; + break; + } + return type; + }, + Integer({ constrainedModel }): string { + let type = 'Integer'; + const format = + constrainedModel.originalInput && + constrainedModel.originalInput['format']; + switch (format) { + case 'integer': + case 'int32': + type = 'int'; + break; + case 'long': + case 'int64': + type = 'long'; + break; + } + return type; + }, + String({ constrainedModel }): string { + let type = 'String'; + const format = + constrainedModel.originalInput && + constrainedModel.originalInput['format']; + switch (format) { + case 'date': + type = 'java.time.LocalDate'; + break; + case 'time': + type = 'java.time.OffsetTime'; + break; + case 'dateTime': + case 'date-time': + type = 'java.time.OffsetDateTime'; + break; + case 'binary': + type = 'byte[]'; + break; + } + return type; + }, + Boolean(): string { + return 'Boolean'; + }, + Tuple({ options }): string { + //Because Java have no notion of tuples (and no custom implementation), we have to render it as a list of any value. + const tupleType = 'Object'; + if (options.collectionType && options.collectionType === 'List') { + return `List<${tupleType}>`; + } + return `${tupleType}[]`; + }, + Array({ constrainedModel, options }): string { + if (options.collectionType && options.collectionType === 'List') { + return `List<${constrainedModel.valueModel.type}>`; + } + return `${constrainedModel.valueModel.type}[]`; + }, + Enum({ constrainedModel }): string { + const format = + constrainedModel.originalInput && + constrainedModel.originalInput['format']; + const valueTypes = constrainedModel.values.map((enumValue) => + fromEnumValueToType(enumValue, format) + ); + const uniqueTypes = valueTypes.filter((item, pos) => { + return valueTypes.indexOf(item) === pos; + }); + + //Enums cannot handle union types, default to a loose type + if (uniqueTypes.length > 1) { + return interpretUnionValueType(uniqueTypes); + } + return uniqueTypes[0]; + }, + Union(): string { + //Because Java have no notion of unions (and no custom implementation), we have to render it as any value. + return 'Object'; + }, + Dictionary({ constrainedModel }): string { + //Limitations to Java is that maps cannot have specific value types... + if (constrainedModel.value.type === 'int') { + constrainedModel.value.type = 'Integer'; + } + return `Map<${constrainedModel.key.type}, ${constrainedModel.value.type}>`; + } +}; + +export const JavaDefaultConstraints = { + enumKey: defaultEnumKeyConstraints(), + enumValue: defaultEnumValueConstraints(), + modelName: defaultModelNameConstraints(), + propertyKey: defaultPropertyKeyConstraints() +}; diff --git a/src/generators/java/JavaDependencyManager.ts b/src/generators/java/JavaDependencyManager.ts new file mode 100644 index 0000000000..d933543510 --- /dev/null +++ b/src/generators/java/JavaDependencyManager.ts @@ -0,0 +1,25 @@ +import { ConstrainedMetaModel } from '../../models'; +import { AbstractDependencyManager } from '../AbstractDependencyManager'; +import { JavaOptions } from './JavaGenerator'; + +export class JavaDependencyManager extends AbstractDependencyManager { + constructor(public options: JavaOptions, dependencies: string[] = []) { + super(dependencies); + } + + renderImport(model: ConstrainedMetaModel, packageName: string): string { + return `import ${packageName}.${model.name};`; + } + + renderAllModelDependencies( + model: ConstrainedMetaModel, + packageName: string + ): string { + return model + .getNearestDependencies() + .map((dependencyModel) => { + return this.renderImport(dependencyModel, packageName); + }) + .join('\n'); + } +} diff --git a/src/generators/java/JavaFileGenerator.ts b/src/generators/java/JavaFileGenerator.ts index e1bfb99ca1..e9c045fd0b 100644 --- a/src/generators/java/JavaFileGenerator.ts +++ b/src/generators/java/JavaFileGenerator.ts @@ -1,23 +1,42 @@ import { JavaGenerator, JavaRenderCompleteModelOptions } from './'; -import { CommonInputModel, OutputModel } from '../../models'; +import { InputMetaModel, OutputModel } from '../../models'; import * as path from 'path'; import { AbstractFileGenerator } from '../AbstractFileGenerator'; import { FileHelpers } from '../../helpers'; -export class JavaFileGenerator extends JavaGenerator implements AbstractFileGenerator { +export class JavaFileGenerator + extends JavaGenerator + implements AbstractFileGenerator +{ /** - * Generates all the models to an output directory each model with their own separate files. - * + * Generates all the models to an output directory each model with their own separate files. + * * @param input * @param outputDirectory where you want the models generated to * @param options + * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ - public async generateToFiles(input: Record | CommonInputModel, outputDirectory: string, options: JavaRenderCompleteModelOptions): Promise { + public async generateToFiles( + input: Record | InputMetaModel, + outputDirectory: string, + options: JavaRenderCompleteModelOptions, + ensureFilesWritten = false + ): Promise { let generatedModels = await this.generateCompleteModels(input, options); - generatedModels = generatedModels.filter((outputModel) => { return outputModel.modelName !== undefined; }); + //Filter anything out that have not been successfully generated + generatedModels = generatedModels.filter((outputModel) => { + return outputModel.modelName !== ''; + }); for (const outputModel of generatedModels) { - const filePath = path.resolve(outputDirectory, `${outputModel.modelName}.java`); - await FileHelpers.writerToFileSystem(outputModel.result, filePath); + const filePath = path.resolve( + outputDirectory, + `${outputModel.modelName}.java` + ); + await FileHelpers.writerToFileSystem( + outputModel.result, + filePath, + ensureFilesWritten + ); } return generatedModels; } diff --git a/src/generators/java/JavaGenerator.ts b/src/generators/java/JavaGenerator.ts index 3ade5ad2f0..ad57dc46a5 100644 --- a/src/generators/java/JavaGenerator.ts +++ b/src/generators/java/JavaGenerator.ts @@ -1,93 +1,256 @@ -import { - AbstractGenerator, +import { + AbstractGenerator, CommonGeneratorOptions, defaultGeneratorOptions } from '../AbstractGenerator'; -import { CommonModel, CommonInputModel, RenderOutput } from '../../models'; -import { CommonNamingConvention, CommonNamingConventionImplementation, ModelKind, TypeHelpers } from '../../helpers'; +import { + ConstrainedEnumModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + InputMetaModel, + MetaModel, + RenderOutput +} from '../../models'; +import { split, SplitOptions, TypeMapping } from '../../helpers'; import { JavaPreset, JAVA_DEFAULT_PRESET } from './JavaPreset'; import { ClassRenderer } from './renderers/ClassRenderer'; import { EnumRenderer } from './renderers/EnumRenderer'; import { isReservedJavaKeyword } from './Constants'; import { Logger } from '../../'; +import { constrainMetaModel, Constraints } from '../../helpers'; +import { + JavaDefaultConstraints, + JavaDefaultTypeMapping +} from './JavaConstrainer'; +import { DeepPartial, mergePartialAndDefault } from '../../utils/Partials'; +import { JavaDependencyManager } from './JavaDependencyManager'; + export interface JavaOptions extends CommonGeneratorOptions { - collectionType?: 'List' | 'Array'; - namingConvention?: CommonNamingConvention; + collectionType: 'List' | 'Array'; + typeMapping: TypeMapping; + constraints: Constraints; } +export type JavaTypeMapping = TypeMapping; export interface JavaRenderCompleteModelOptions { - packageName: string + packageName: string; } -export class JavaGenerator extends AbstractGenerator { +export class JavaGenerator extends AbstractGenerator< + JavaOptions, + JavaRenderCompleteModelOptions +> { static defaultOptions: JavaOptions = { ...defaultGeneratorOptions, - defaultPreset: JAVA_DEFAULT_PRESET, + defaultPreset: JAVA_DEFAULT_PRESET, collectionType: 'Array', - namingConvention: CommonNamingConventionImplementation + typeMapping: JavaDefaultTypeMapping, + constraints: JavaDefaultConstraints + }; + + static defaultCompleteModelOptions: JavaRenderCompleteModelOptions = { + packageName: 'Asyncapi.Models' }; - constructor( - options: JavaOptions = JavaGenerator.defaultOptions, - ) { - super('Java', JavaGenerator.defaultOptions, options); + constructor(options?: DeepPartial) { + const realizedOptions = JavaGenerator.getJavaOptions(options); + super('Java', realizedOptions); + } + + /** + * Returns the Java options by merging custom options with default ones. + */ + static getJavaOptions(options?: DeepPartial): JavaOptions { + const optionsToUse = mergePartialAndDefault( + JavaGenerator.defaultOptions, + options + ) as JavaOptions; + //Always overwrite the dependency manager unless user explicitly state they want it (ignore default temporary dependency manager) + if (options?.dependencyManager === undefined) { + optionsToUse.dependencyManager = () => { + return new JavaDependencyManager(optionsToUse); + }; + } + return optionsToUse; + } + + /** + * Wrapper to get an instance of the dependency manager + */ + getDependencyManager(options: JavaOptions): JavaDependencyManager { + return this.getDependencyManagerInstance(options) as JavaDependencyManager; + } + + splitMetaModel(model: MetaModel): MetaModel[] { + //These are the models that we have separate renderers for + const metaModelsToSplit: SplitOptions = { + splitEnum: true, + splitObject: true + }; + return split(model, metaModelsToSplit); + } + + constrainToMetaModel( + model: MetaModel, + options: DeepPartial + ): ConstrainedMetaModel { + const optionsToUse = JavaGenerator.getJavaOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + return constrainMetaModel( + this.options.typeMapping, + this.options.constraints, + { + metaModel: model, + dependencyManager: dependencyManagerToUse, + options: this.options, + constrainedName: '' //This is just a placeholder, it will be constrained within the function + } + ); } /** * Render a scattered model, where the source code and library and model dependencies are separated. - * - * @param model - * @param inputModel + * + * @param model + * @param inputModel */ - render(model: CommonModel, inputModel: CommonInputModel): Promise { - const kind = TypeHelpers.extractKind(model); - // We don't support union in Java generator, however, if union is an object, we render it as a class. - if (kind === ModelKind.OBJECT || (kind === ModelKind.UNION && model.type?.includes('object'))) { - return this.renderClass(model, inputModel); - } else if (kind === ModelKind.ENUM) { - return this.renderEnum(model, inputModel); + render( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = JavaGenerator.getJavaOptions({ + ...this.options, + ...options + }); + if (model instanceof ConstrainedObjectModel) { + return this.renderClass(model, inputModel, optionsToUse); + } else if (model instanceof ConstrainedEnumModel) { + return this.renderEnum(model, inputModel, optionsToUse); } - Logger.warn(`Java generator, cannot generate this type of model, ${model.$id}`); - return Promise.resolve(RenderOutput.toRenderOutput({ result: '', renderedName: '', dependencies: [] })); + Logger.warn( + `Java generator, cannot generate this type of model, ${model.name}` + ); + return Promise.resolve( + RenderOutput.toRenderOutput({ + result: '', + renderedName: '', + dependencies: [] + }) + ); } /** * Render a complete model result where the model code, library and model dependencies are all bundled appropriately. - * + * * For Java you need to specify which package the model is placed under. - * - * @param model - * @param inputModel + * + * @param model + * @param inputModel * @param options used to render the full output */ - async renderCompleteModel(model: CommonModel, inputModel: CommonInputModel, options: JavaRenderCompleteModelOptions): Promise { - if (isReservedJavaKeyword(options.packageName)) { - throw new Error(`You cannot use reserved Java keyword (${options.packageName}) as package name, please use another.`); - } - - const outputModel = await this.render(model, inputModel); - const modelDependencies = model.getNearestDependencies().map((dependencyModelName) => { - const formattedDependencyModelName = this.options.namingConvention?.type ? this.options.namingConvention.type(dependencyModelName, {inputModel, model: inputModel.models[String(dependencyModelName)], reservedKeywordCallback: isReservedJavaKeyword}) : dependencyModelName; - return `import ${options.packageName}.${formattedDependencyModelName};`; + async renderCompleteModel( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + completeModelOptions: Partial, + options: DeepPartial + ): Promise { + const completeModelOptionsToUse = mergePartialAndDefault( + JavaGenerator.defaultCompleteModelOptions, + completeModelOptions + ) as JavaRenderCompleteModelOptions; + const optionsToUse = JavaGenerator.getJavaOptions({ + ...this.options, + ...options }); - const outputContent = `package ${options.packageName}; -${modelDependencies.join('\n')} + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + + this.assertPackageIsValid(completeModelOptionsToUse); + + const outputModel = await this.render(model, inputModel, optionsToUse); + const modelDependencies = dependencyManagerToUse.renderAllModelDependencies( + model, + completeModelOptionsToUse.packageName + ); + const outputContent = `package ${completeModelOptionsToUse.packageName}; +${modelDependencies} ${outputModel.dependencies.join('\n')} -${outputModel.result}`; - return RenderOutput.toRenderOutput({result: outputContent, renderedName: outputModel.renderedName, dependencies: outputModel.dependencies}); +${outputModel.result}`; + return RenderOutput.toRenderOutput({ + result: outputContent, + renderedName: outputModel.renderedName, + dependencies: outputModel.dependencies + }); } - async renderClass(model: CommonModel, inputModel: CommonInputModel): Promise { + private assertPackageIsValid(options: JavaRenderCompleteModelOptions) { + const reservedWords = options.packageName + .split('.') + .filter((subpackage) => isReservedJavaKeyword(subpackage, true)); + + if (reservedWords.length > 0) { + throw new Error( + `You cannot use '${ + options.packageName + }' as a package name, contains reserved keywords: [${reservedWords.join( + ', ' + )}]` + ); + } + } + + async renderClass( + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = JavaGenerator.getJavaOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const presets = this.getPresets('class'); - const renderer = new ClassRenderer(this.options, this, presets, model, inputModel); + const renderer = new ClassRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = renderer.nameType(model.$id, model); - return RenderOutput.toRenderOutput({result, renderedName, dependencies: renderer.dependencies}); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } - async renderEnum(model: CommonModel, inputModel: CommonInputModel): Promise { - const presets = this.getPresets('enum'); - const renderer = new EnumRenderer(this.options, this, presets, model, inputModel); + async renderEnum( + model: ConstrainedEnumModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = JavaGenerator.getJavaOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('enum'); + const renderer = new EnumRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = renderer.nameType(model.$id, model); - return RenderOutput.toRenderOutput({result, renderedName, dependencies: renderer.dependencies}); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } } diff --git a/src/generators/java/JavaPreset.ts b/src/generators/java/JavaPreset.ts index b91be0918f..970c335d75 100644 --- a/src/generators/java/JavaPreset.ts +++ b/src/generators/java/JavaPreset.ts @@ -1,20 +1,40 @@ -/* eslint-disable @typescript-eslint/ban-types */ -import { Preset, ClassPreset, EnumPreset, PresetArgs, EnumArgs } from '../../models'; -import { ClassRenderer, JAVA_DEFAULT_CLASS_PRESET } from './renderers/ClassRenderer'; -import { EnumRenderer, JAVA_DEFAULT_ENUM_PRESET } from './renderers/EnumRenderer'; +import { + Preset, + ClassPreset, + EnumPreset, + PresetArgs, + EnumArgs, + ConstrainedEnumModel +} from '../../models'; +import { JavaOptions } from './JavaGenerator'; +import { + ClassRenderer, + JAVA_DEFAULT_CLASS_PRESET +} from './renderers/ClassRenderer'; +import { + EnumRenderer, + JAVA_DEFAULT_ENUM_PRESET +} from './renderers/EnumRenderer'; -export interface JavaEnumPreset extends EnumPreset { - ctor?: (args: PresetArgs & EnumArgs) => string; - getValue?: (args: PresetArgs & EnumArgs) => string; - fromValue?: (args: PresetArgs & EnumArgs) => string; +export type ClassPresetType = ClassPreset; +export interface EnumPresetType extends EnumPreset { + ctor?: ( + args: PresetArgs & EnumArgs + ) => string; + getValue?: ( + args: PresetArgs & EnumArgs + ) => string; + fromValue?: ( + args: PresetArgs & EnumArgs + ) => string; } -export type JavaPreset = Preset<{ - class: ClassPreset; - enum: JavaEnumPreset; +export type JavaPreset = Preset<{ + class: ClassPresetType; + enum: EnumPresetType; }>; -export const JAVA_DEFAULT_PRESET: JavaPreset = { +export const JAVA_DEFAULT_PRESET: JavaPreset = { class: JAVA_DEFAULT_CLASS_PRESET, - enum: JAVA_DEFAULT_ENUM_PRESET, + enum: JAVA_DEFAULT_ENUM_PRESET }; diff --git a/src/generators/java/JavaRenderer.ts b/src/generators/java/JavaRenderer.ts index 8fa866a160..cd357421ff 100644 --- a/src/generators/java/JavaRenderer.ts +++ b/src/generators/java/JavaRenderer.ts @@ -1,175 +1,53 @@ import { AbstractRenderer } from '../AbstractRenderer'; import { JavaGenerator, JavaOptions } from './JavaGenerator'; -import { CommonModel, CommonInputModel, Preset } from '../../models'; -import { FormatHelpers, ModelKind, TypeHelpers } from '../../helpers'; -import { isReservedJavaKeyword } from './Constants'; +import { ConstrainedMetaModel, InputMetaModel, Preset } from '../../models'; +import { FormatHelpers } from '../../helpers'; +import { JavaDependencyManager } from './JavaDependencyManager'; /** * Common renderer for Java types - * + * * @extends AbstractRenderer */ -export abstract class JavaRenderer extends AbstractRenderer { +export abstract class JavaRenderer< + RendererModelType extends ConstrainedMetaModel +> extends AbstractRenderer { constructor( options: JavaOptions, generator: JavaGenerator, presets: Array<[Preset, unknown]>, - model: CommonModel, - inputModel: CommonInputModel, + model: RendererModelType, + inputModel: InputMetaModel, + public dependencyManager: JavaDependencyManager ) { super(options, generator, presets, model, inputModel); } - /** - * Renders the name of a type based on provided generator option naming convention type function. - * - * This is used to render names of models and then later used if it is referenced from other models. - * - * @param name - * @param model - */ - nameType(name: string | undefined, model?: CommonModel): string { - return this.options?.namingConvention?.type - ? this.options.namingConvention.type(name, { model: model || this.model, inputModel: this.inputModel, reservedKeywordCallback: isReservedJavaKeyword }) - : name || ''; - } - - /** - * Renders the name of a property based on provided generator option naming convention property function. - * - * @param propertyName - * @param property - */ - nameProperty(propertyName: string | undefined, property?: CommonModel): string { - return this.options?.namingConvention?.property - ? this.options.namingConvention.property(propertyName, { model: this.model, inputModel: this.inputModel, property, reservedKeywordCallback: isReservedJavaKeyword }) - : propertyName || ''; - } - - /** - * Renders model(s) to Java type(s). - * - * @param model - */ - renderType(model: CommonModel | CommonModel[]): string { - if (Array.isArray(model) || Array.isArray(model.type)) { - return 'Object'; // fallback - } - if (model.$ref !== undefined) { - return this.nameType(model.$ref, model); - } - const kind = TypeHelpers.extractKind(model); - if ( - kind === ModelKind.PRIMITIVE || - kind === ModelKind.ARRAY - ) { - const format = model.getFromOriginalInput('format'); - return this.toClassType(this.toJavaTypeWithFormat(model.type, format, model)); - } - return this.nameType(model.$id, model); - } - - toJavaTypeWithFormat(type: string | undefined, format: string | undefined, model: CommonModel): string { - if (format) { - const returnType = this.toJavaType(format, model); - if (returnType !== 'Object') { - return returnType; - } - } - return this.toJavaType(type, model); - } - /** - * Returns the Java corresponding type from CommonModel type or JSON schema format - * @param type - * @param model - */ - toJavaType(type: string | undefined, model: CommonModel): string { - switch (type) { - case 'integer': - case 'int32': - return 'int'; - case 'long': - case 'int64': - return 'long'; - case 'boolean': - return 'boolean'; - case 'date': - return 'java.time.LocalDate'; - case 'time': - return 'java.time.OffsetTime'; - case 'dateTime': - case 'date-time': - return 'java.time.OffsetDateTime'; - case 'string': - case 'password': - case 'byte': - return 'String'; - case 'float': - return 'float'; - case 'double': - case 'number': - return 'double'; - case 'binary': - return 'byte[]'; - case 'array': { - let arrayItemModel = model.items; - //Since Java dont support tuples, lets make sure that we combine the tuple types to find the appropriate array type - if (Array.isArray(model.items)) { - arrayItemModel = model.items.reduce((prevModel, currentModel) => { - return CommonModel.mergeCommonModels(CommonModel.toCommonModel(prevModel), CommonModel.toCommonModel(currentModel), {}); - }); - //If tuples and additionalItems make sure to find the appropriate type by merging all the tuples and additionalItems model together to find the combined type. - if (model.additionalItems !== undefined) { - arrayItemModel = CommonModel.mergeCommonModels(arrayItemModel, model.additionalItems, {}); - } - } - const newType = arrayItemModel ? this.renderType(arrayItemModel) : 'Object'; - if (this.options.collectionType && this.options.collectionType === 'List') { - return `List<${newType}>`; - } - return `${newType}[]`; - } - default: - return 'Object'; - } - } - - toClassType(type: string): string { - switch (type) { - case 'int': - return 'Integer'; - case 'long': - return 'Long'; - case 'boolean': - return 'Boolean'; - case 'float': - return 'Float'; - case 'double': - return 'Double'; - default: - return type; - } - } - renderComments(lines: string | string[]): string { lines = FormatHelpers.breakLines(lines); - const newLiteral = lines.map(line => ` * ${line}`).join('\n'); + const newLiteral = lines.map((line) => ` * ${line}`).join('\n'); return `/** ${newLiteral} */`; } - renderAnnotation(annotationName: string, value?: any | Record): string { + renderAnnotation( + annotationName: string, + value?: any | Record + ): string { const name = `@${FormatHelpers.upperFirst(annotationName)}`; let values = undefined; if (value !== undefined) { if (typeof value === 'object') { - values = Object.entries(value || {}).map(([paramName, newValue]) => { - if (paramName && newValue !== undefined) { - return `${paramName}=${newValue}`; - } - return newValue; - }).filter(v => v !== undefined).join(', '); + values = Object.entries(value || {}) + .map(([paramName, newValue]) => { + if (paramName && newValue !== undefined) { + return `${paramName}=${newValue}`; + } + return newValue; + }) + .filter((v) => v !== undefined) + .join(', '); } else { values = value; } diff --git a/src/generators/java/constrainer/EnumConstrainer.ts b/src/generators/java/constrainer/EnumConstrainer.ts new file mode 100644 index 0000000000..b427be75cd --- /dev/null +++ b/src/generators/java/constrainer/EnumConstrainer.ts @@ -0,0 +1,98 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { ConstrainedEnumModel, EnumModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { + FormatHelpers, + EnumKeyConstraint, + EnumValueConstraint +} from '../../../helpers'; +import { isReservedJavaKeyword } from '../Constants'; + +export type ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_KEYS: ( + constrainedEnumModel: ConstrainedEnumModel, + enumModel: EnumModel, + value: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude '_', '$' because they are allowed as enum keys + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_KEYS: NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toConstantCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedJavaKeyword); + } +}; + +export function defaultEnumKeyConstraints( + customConstraints?: Partial +): EnumKeyConstraint { + const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; + + return ({ enumKey, enumModel, constrainedEnumModel }) => { + let constrainedEnumKey = enumKey; + constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey); + constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey); + //If the enum key has been manipulated, lets make sure it don't clash with existing keys + if (constrainedEnumKey !== enumKey) { + constrainedEnumKey = constraints.NO_DUPLICATE_KEYS( + constrainedEnumModel, + enumModel, + constrainedEnumKey, + constraints.NAMING_FORMATTER! + ); + } + constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey); + return constrainedEnumKey; + }; +} + +export function defaultEnumValueConstraints(): EnumValueConstraint { + return ({ enumValue }) => { + let constrainedEnumValue = enumValue; + switch (typeof enumValue) { + case 'string': + case 'boolean': + constrainedEnumValue = `"${enumValue}"`; + break; + case 'bigint': + case 'number': { + constrainedEnumValue = enumValue; + break; + } + case 'object': { + constrainedEnumValue = `"${JSON.stringify(enumValue).replace( + /"/g, + '\\"' + )}"`; + break; + } + default: { + constrainedEnumValue = `"${JSON.stringify(enumValue)}"`; + } + } + return constrainedEnumValue; + }; +} diff --git a/src/generators/java/constrainer/ModelNameConstrainer.ts b/src/generators/java/constrainer/ModelNameConstrainer.ts new file mode 100644 index 0000000000..2411c1d15d --- /dev/null +++ b/src/generators/java/constrainer/ModelNameConstrainer.ts @@ -0,0 +1,49 @@ +import { + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { isReservedJavaKeyword } from '../Constants'; + +export type ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultModelNameConstraints: ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NAMING_FORMATTER: (value: string) => { + return FormatHelpers.toPascalCase(value); + }, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedJavaKeyword); + } +}; +export function defaultModelNameConstraints( + customConstraints?: Partial +): ModelNameConstraint { + const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; + + return ({ modelName }) => { + let constrainedValue = modelName; + constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue); + constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue); + constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue); + constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue); + constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); + return constrainedValue; + }; +} diff --git a/src/generators/java/constrainer/PropertyKeyConstrainer.ts b/src/generators/java/constrainer/PropertyKeyConstrainer.ts new file mode 100644 index 0000000000..2301051992 --- /dev/null +++ b/src/generators/java/constrainer/PropertyKeyConstrainer.ts @@ -0,0 +1,78 @@ +import { ConstrainedObjectModel, ObjectModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { isReservedJavaKeyword } from '../Constants'; + +export type PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_PROPERTIES: ( + constrainedObjectModel: ConstrainedObjectModel, + objectModel: ObjectModel, + propertyName: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toCamelCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedJavaKeyword); + } +}; + +export function defaultPropertyKeyConstraints( + customConstraints?: Partial +): PropertyKeyConstraint { + const constraints = { + ...DefaultPropertyKeyConstraints, + ...customConstraints + }; + + return ({ objectPropertyModel, constrainedObjectModel, objectModel }) => { + let constrainedPropertyKey = objectPropertyModel.propertyName; + + constrainedPropertyKey = constraints.NO_SPECIAL_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey); + constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS( + constrainedPropertyKey + ); + //If the property name has been manipulated, lets make sure it don't clash with existing properties + if (constrainedPropertyKey !== objectPropertyModel.propertyName) { + constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES( + constrainedObjectModel, + objectModel, + constrainedPropertyKey, + constraints.NAMING_FORMATTER + ); + } + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + return constrainedPropertyKey; + }; +} diff --git a/src/generators/java/index.ts b/src/generators/java/index.ts index 7a5d42fb10..5be332bace 100644 --- a/src/generators/java/index.ts +++ b/src/generators/java/index.ts @@ -3,3 +3,19 @@ export * from './JavaFileGenerator'; export { JAVA_DEFAULT_PRESET } from './JavaPreset'; export type { JavaPreset } from './JavaPreset'; export * from './presets'; + +export { + defaultEnumKeyConstraints as javaDefaultEnumKeyConstraints, + DefaultEnumKeyConstraints as JavaDefaultEnumKeyConstraints, + defaultEnumValueConstraints as javaDefaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; + +export { + DefaultModelNameConstraints as JavaDefaultModelNameConstraints, + defaultModelNameConstraints as javaDefaultModelNameConstraints +} from './constrainer/ModelNameConstrainer'; + +export { + DefaultPropertyKeyConstraints as JavaDefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints as javaDefaultPropertyKeyConstraints +} from './constrainer/PropertyKeyConstrainer'; diff --git a/src/generators/java/presets/CommonPreset.ts b/src/generators/java/presets/CommonPreset.ts index 4b6691e6ae..936aaf7703 100644 --- a/src/generators/java/presets/CommonPreset.ts +++ b/src/generators/java/presets/CommonPreset.ts @@ -1,10 +1,7 @@ import { JavaRenderer } from '../JavaRenderer'; import { JavaPreset } from '../JavaPreset'; - -import { getUniquePropertyName, DefaultPropertyNames, FormatHelpers } from '../../../helpers'; -import { CommonModel } from '../../../models'; -import { Logger } from '../../../utils/LoggingInterface'; - +import { FormatHelpers } from '../../../helpers'; +import { ConstrainedArrayModel, ConstrainedObjectModel } from '../../../models'; export interface JavaCommonPresetOptions { equal: boolean; hashCode: boolean; @@ -15,20 +12,21 @@ export interface JavaCommonPresetOptions { /** * Render `equal` function based on model's properties */ -function renderEqual({ renderer, model }: { - renderer: JavaRenderer, - model: CommonModel, +function renderEqual({ + renderer, + model +}: { + renderer: JavaRenderer; + model: ConstrainedObjectModel; }): string { - const formattedModelName = renderer.nameType(model.$id); const properties = model.properties || {}; const propertyKeys = [...Object.keys(properties)]; - if (model.additionalProperties !== undefined) { - propertyKeys.push(getUniquePropertyName(model, DefaultPropertyNames.additionalProperties)); - } - const equalProperties = propertyKeys.map(prop => { - const camelCasedProp = renderer.nameProperty(prop); - return `Objects.equals(this.${camelCasedProp}, self.${camelCasedProp})`; - }).join(' &&\n'); + + const equalProperties = propertyKeys + .map((propertyName) => { + return `Objects.equals(this.${propertyName}, self.${propertyName})`; + }) + .join(' &&\n'); return `${renderer.renderAnnotation('Override')} public boolean equals(Object o) { @@ -38,7 +36,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - ${formattedModelName} self = (${formattedModelName}) o; + ${model.name} self = (${model.name}) o; return ${equalProperties.length > 0 ? renderer.indent(equalProperties, 6) : 'true'}; }`; @@ -47,17 +45,20 @@ ${equalProperties.length > 0 ? renderer.indent(equalProperties, 6) : 'true'}; /** * Render `hashCode` function based on model's properties */ -function renderHashCode({ renderer, model }: { - renderer: JavaRenderer, - model: CommonModel, +function renderHashCode({ + renderer, + model +}: { + renderer: JavaRenderer; + model: ConstrainedObjectModel; }): string { const properties = model.properties || {}; const propertyKeys = [...Object.keys(properties)]; - if (model.additionalProperties !== undefined) { - propertyKeys.push(getUniquePropertyName(model, DefaultPropertyNames.additionalProperties)); - } + //Object casting needed because otherwise properties with arrays fails to be compiled. - const hashProperties = propertyKeys.map(prop => `(Object)${renderer.nameProperty(prop)}`).join(', '); + const hashProperties = propertyKeys + .map((propertyName) => `(Object)${propertyName}`) + .join(', '); return `${renderer.renderAnnotation('Override')} public int hashCode() { @@ -68,29 +69,34 @@ public int hashCode() { /** * Render `toString` function based on model's properties */ -function renderToString({ renderer, model }: { - renderer: JavaRenderer, - model: CommonModel, +function renderToString({ + renderer, + model +}: { + renderer: JavaRenderer; + model: ConstrainedObjectModel; }): string { - const formattedModelName = renderer.nameType(model.$id); const properties = model.properties || {}; const propertyKeys = [...Object.keys(properties)]; - if (model.additionalProperties !== undefined) { - propertyKeys.push(getUniquePropertyName(model, DefaultPropertyNames.additionalProperties)); - } - const toStringProperties = propertyKeys.map(prop => { - const renderedPropertyName = renderer.nameProperty(prop); - return `" ${renderedPropertyName}: " + toIndentedString(${renderedPropertyName}) + "\\n" +`; + const toStringProperties = propertyKeys.map((propertyName) => { + return `" ${propertyName}: " + toIndentedString(${propertyName}) + "\\n" +`; }); return `${renderer.renderAnnotation('Override')} public String toString() { - return "class ${formattedModelName} {\\n" + -${toStringProperties.length > 0 ? renderer.indent(renderer.renderBlock(toStringProperties), 4) : ''} + return "class ${model.name} {\\n" + +${ + toStringProperties.length > 0 + ? renderer.indent(renderer.renderBlock(toStringProperties), 4) + : '' +} "}"; } -${renderer.renderComments(['Convert the given object to string with each line indented by 4 spaces', '(except the first line).'])} +${renderer.renderComments([ + 'Convert the given object to string with each line indented by 4 spaces', + '(except the first line).' +])} private String toIndentedString(Object o) { if (o == null) { return "null"; @@ -99,14 +105,17 @@ private String toIndentedString(Object o) { }`; } -function renderMarshalProperties(renderer: JavaRenderer, model: CommonModel) { +function renderMarshalProperties({ + model +}: { + model: ConstrainedObjectModel; +}): string { const properties = model.properties || {}; const propertyKeys = [...Object.keys(properties)]; - const marshalProperties = propertyKeys.map(prop => { - const formattedPropertyName = renderer.nameProperty(prop, model); - const modelInstanceVariable = `this.${formattedPropertyName}`; + const marshalProperties = propertyKeys.map((propertyName) => { + const modelInstanceVariable = `this.${propertyName}`; return `if(${modelInstanceVariable} != null) { - propList.add("${formattedPropertyName}:"+${modelInstanceVariable}.toString()); + propList.add("${propertyName}:"+${modelInstanceVariable}.toString()); }`; }); return marshalProperties.join('\n'); @@ -114,64 +123,72 @@ function renderMarshalProperties(renderer: JavaRenderer, model: CommonModel) { /** * Render `marshal` function based on model's properties */ -function renderMarshalling({ renderer, model }: { - renderer: JavaRenderer, - model: CommonModel, +function renderMarshalling({ + renderer, + model +}: { + renderer: JavaRenderer; + model: ConstrainedObjectModel; }): string { return `public String marshal() { List propList = new ArrayList(); - ${renderer.indent(renderMarshalProperties(renderer, model))} + ${renderer.indent(renderMarshalProperties({ model }))} return propList.stream().collect(Collectors.joining(",")); }`; } -function renderUnmarshalProperties(renderer: JavaRenderer, model: CommonModel) { +function renderUnmarshalProperties({ + model +}: { + model: ConstrainedObjectModel; +}): string { const properties = model.properties || {}; - const propertyKeys = [...Object.keys(properties)]; - const unmarshalProperties = propertyKeys.map(prop => { - const formattedPropertyName = renderer.nameProperty(prop, model); - const setterFunction = `set${formattedPropertyName.charAt(0).toUpperCase()}${formattedPropertyName.slice(1)}`; - const propModel = properties[String(prop)]; - if (propModel.type === 'undefined') { - Logger.error(`Could not render unmarshal for property ${prop}`); - return; - } - if (propModel.type === 'array') { - return `if(jsonObject.has("${formattedPropertyName}")) { - JSONArray jsonArray = jsonObject.getJSONArray("${formattedPropertyName}"); - String[] ${formattedPropertyName} = new String[jsonArray.length()]; + const unmarshalProperties = Object.entries(properties).map( + ([propertyName, property]) => { + const setterFunction = `set${propertyName + .charAt(0) + .toUpperCase()}${propertyName.slice(1)}`; + if (property instanceof ConstrainedArrayModel) { + return `if(jsonObject.has("${propertyName}")) { + JSONArray jsonArray = jsonObject.getJSONArray("${propertyName}"); + String[] ${propertyName} = new String[jsonArray.length()]; for(int i = 0; i < jsonArray.length(); i++) { - ${formattedPropertyName}[i] = jsonArray.getString(i); + ${propertyName}[i] = jsonArray.getString(i); } - result.${setterFunction}(${formattedPropertyName}); + result.${setterFunction}(${propertyName}); }`; - } - const getType = `jsonObject.get${FormatHelpers.upperFirst(propModel.type?.toString() || '')}`; - return `if(jsonObject.has("${formattedPropertyName}")) { - result.${setterFunction}(${getType}("${formattedPropertyName}")); + } + const getType = `jsonObject.get${FormatHelpers.upperFirst( + property.property.type + )}`; + return `if(jsonObject.has("${propertyName}")) { + result.${setterFunction}(${getType}("${propertyName}")); }`; - }); + } + ); return unmarshalProperties.join('\n'); } /** * Render `unmarshal` function based on model's properties */ -function renderUnmarshalling({ renderer, model }: { - renderer: JavaRenderer, - model: CommonModel, +function renderUnmarshalling({ + renderer, + model +}: { + renderer: JavaRenderer; + model: ConstrainedObjectModel; }): string { - const formattedModelName = renderer.nameType(model.$id); - return `public static ${formattedModelName} unmarshal(String json) { - ${formattedModelName} result = new ${formattedModelName}(); + return `public static ${model.name} unmarshal(String json) { + ${model.name} result = new ${model.name}(); JSONObject jsonObject = new JSONObject(json); - ${renderer.indent(renderUnmarshalProperties(renderer, model))} + ${renderer.indent(renderUnmarshalProperties({ model }))} return result; }`; } /** - * Preset which adds `equal`, `hashCode`, `toString` functions to class. - * + * Preset which adds `equal`, `hashCode`, `toString` functions to class. + * * @implements {JavaPreset} */ export const JAVA_COMMON_PRESET: JavaPreset = { @@ -179,30 +196,39 @@ export const JAVA_COMMON_PRESET: JavaPreset = { additionalContent({ renderer, model, content, options }) { options = options || {}; const blocks: string[] = []; - const shouldContainEqual = options.equal === undefined || options.equal === true; - const shouldContainHashCode = options.hashCode === undefined || options.hashCode === true; - const shouldContainToString = options.classToString === undefined || options.classToString === true; + const shouldContainEqual = + options.equal === undefined || options.equal === true; + const shouldContainHashCode = + options.hashCode === undefined || options.hashCode === true; + const shouldContainToString = + options.classToString === undefined || options.classToString === true; const shouldContainMarshal = options.marshalling === true; if (shouldContainEqual === true || shouldContainHashCode === true) { - renderer.addDependency('import java.util.Objects;'); + renderer.dependencyManager.addDependency('import java.util.Objects;'); } if (shouldContainMarshal === true) { - renderer.addDependency('import java.util.stream;'); - renderer.addDependency('import org.json.JSONObject;'); - renderer.addDependency('import java.util.Map;'); + renderer.dependencyManager.addDependency('import java.util.stream;'); + renderer.dependencyManager.addDependency('import org.json.JSONObject;'); + renderer.dependencyManager.addDependency('import java.util.Map;'); } - if (shouldContainEqual) { blocks.push(renderEqual({ renderer, model })); } - if (shouldContainHashCode) { blocks.push(renderHashCode({ renderer, model })); } - if (shouldContainToString) { blocks.push(renderToString({ renderer, model })); } + if (shouldContainEqual) { + blocks.push(renderEqual({ renderer, model })); + } + if (shouldContainHashCode) { + blocks.push(renderHashCode({ renderer, model })); + } + if (shouldContainToString) { + blocks.push(renderToString({ renderer, model })); + } if (shouldContainMarshal === true) { blocks.push(renderMarshalling({ renderer, model })); blocks.push(renderUnmarshalling({ renderer, model })); } return renderer.renderBlock([content, ...blocks], 2); - }, + } } }; diff --git a/src/generators/java/presets/ConstraintsPreset.ts b/src/generators/java/presets/ConstraintsPreset.ts index ad1818995a..9e067d3533 100644 --- a/src/generators/java/presets/ConstraintsPreset.ts +++ b/src/generators/java/presets/ConstraintsPreset.ts @@ -1,60 +1,92 @@ +import { + ConstrainedArrayModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedStringModel +} from '../../../models'; import { JavaPreset } from '../JavaPreset'; + /** * Preset which extends class's getters with annotations from `javax.validation.constraints` package - * + * * @implements {JavaPreset} */ export const JAVA_CONSTRAINTS_PRESET: JavaPreset = { class: { - self({renderer, content}) { - renderer.addDependency('import javax.validation.constraints.*;'); + self({ renderer, content }) { + renderer.dependencyManager.addDependency( + 'import javax.validation.constraints.*;' + ); return content; }, - getter({ renderer, model, propertyName, property, content }) { + // eslint-disable-next-line sonarjs/cognitive-complexity + property({ renderer, property, content }) { const annotations: string[] = []; - - const isRequired = model.isRequired(propertyName); - if (isRequired) { + + if (property.required) { annotations.push(renderer.renderAnnotation('NotNull')); } - + const originalInput = property.property.originalInput; + // string - const pattern = property.getFromOriginalInput('pattern'); - if (pattern !== undefined) { - annotations.push(renderer.renderAnnotation('Pattern', { regexp: `"${pattern}"` })); + if (property.property instanceof ConstrainedStringModel) { + const pattern = originalInput['pattern']; + if (pattern !== undefined) { + annotations.push( + renderer.renderAnnotation('Pattern', { regexp: `"${pattern}"` }) + ); + } + const minLength = originalInput['minLength']; + const maxLength = originalInput['maxLength']; + if (minLength !== undefined || maxLength !== undefined) { + annotations.push( + renderer.renderAnnotation('Size', { + min: minLength, + max: maxLength + }) + ); + } } - const minLength = property.getFromOriginalInput('minLength'); - const maxLength = property.getFromOriginalInput('maxLength'); - if (minLength !== undefined || maxLength !== undefined) { - annotations.push(renderer.renderAnnotation('Size', { min: minLength, max: maxLength })); - } - + // number/integer - const minimum = property.getFromOriginalInput('minimum'); - if (minimum !== undefined) { - annotations.push(renderer.renderAnnotation('Min', minimum)); - } - const exclusiveMinimum = property.getFromOriginalInput('exclusiveMinimum'); - if (exclusiveMinimum !== undefined) { - annotations.push(renderer.renderAnnotation('Min', exclusiveMinimum + 1)); - } - const maximum = property.getFromOriginalInput('maximum'); - if (maximum !== undefined) { - annotations.push(renderer.renderAnnotation('Max', maximum)); + if ( + property.property instanceof ConstrainedFloatModel || + property.property instanceof ConstrainedIntegerModel + ) { + const minimum = originalInput['minimum']; + if (minimum !== undefined) { + annotations.push(renderer.renderAnnotation('Min', minimum)); + } + const exclusiveMinimum = originalInput['exclusiveMinimum']; + if (exclusiveMinimum !== undefined) { + annotations.push( + renderer.renderAnnotation('Min', exclusiveMinimum + 1) + ); + } + const maximum = originalInput['maximum']; + if (maximum !== undefined) { + annotations.push(renderer.renderAnnotation('Max', maximum)); + } + const exclusiveMaximum = originalInput['exclusiveMaximum']; + if (exclusiveMaximum !== undefined) { + annotations.push( + renderer.renderAnnotation('Max', exclusiveMaximum - 1) + ); + } } - const exclusiveMaximum = property.getFromOriginalInput('exclusiveMaximum'); - if (exclusiveMaximum !== undefined) { - annotations.push(renderer.renderAnnotation('Max', exclusiveMaximum - 1)); - } - + // array - const minItems = property.getFromOriginalInput('minItems'); - const maxItems = property.getFromOriginalInput('maxItems'); - if (minItems !== undefined || maxItems !== undefined) { - annotations.push(renderer.renderAnnotation('Size', { min: minItems, max: maxItems })); + if (property.property instanceof ConstrainedArrayModel) { + const minItems = originalInput['minItems']; + const maxItems = originalInput['maxItems']; + if (minItems !== undefined || maxItems !== undefined) { + annotations.push( + renderer.renderAnnotation('Size', { min: minItems, max: maxItems }) + ); + } } - + return renderer.renderBlock([...annotations, content]); - }, + } } }; diff --git a/src/generators/java/presets/DescriptionPreset.ts b/src/generators/java/presets/DescriptionPreset.ts index acbb7df5b8..81553237df 100644 --- a/src/generators/java/presets/DescriptionPreset.ts +++ b/src/generators/java/presets/DescriptionPreset.ts @@ -1,15 +1,19 @@ import { JavaRenderer } from '../JavaRenderer'; import { JavaPreset } from '../JavaPreset'; import { FormatHelpers } from '../../../helpers'; -import { CommonModel } from '../../../models'; +import { ConstrainedMetaModel } from '../../../models'; -function renderDescription({ renderer, content, item }: { - renderer: JavaRenderer, - content: string, - item: CommonModel, +function renderDescription({ + renderer, + content, + item +}: { + renderer: JavaRenderer; + content: string; + item: ConstrainedMetaModel; }): string { - let desc = item.getFromOriginalInput('description'); - const examples = item.getFromOriginalInput('examples'); + let desc = item.originalInput['description']; + const examples = item.originalInput['examples']; if (Array.isArray(examples)) { const renderedExamples = FormatHelpers.renderJSONExamples(examples); @@ -25,8 +29,8 @@ function renderDescription({ renderer, content, item }: { } /** - * Preset which adds description to rendered model. - * + * Preset which adds description to rendered model. + * * @implements {JavaPreset} */ export const JAVA_DESCRIPTION_PRESET: JavaPreset = { @@ -35,12 +39,12 @@ export const JAVA_DESCRIPTION_PRESET: JavaPreset = { return renderDescription({ renderer, content, item: model }); }, getter({ renderer, property, content }) { - return renderDescription({ renderer, content, item: property }); + return renderDescription({ renderer, content, item: property.property }); } }, enum: { self({ renderer, model, content }) { return renderDescription({ renderer, content, item: model }); - }, + } } }; diff --git a/src/generators/java/presets/JacksonPreset.ts b/src/generators/java/presets/JacksonPreset.ts index 1e480e4029..939e1c1b99 100644 --- a/src/generators/java/presets/JacksonPreset.ts +++ b/src/generators/java/presets/JacksonPreset.ts @@ -1,4 +1,4 @@ -import { PropertyType } from '../../../models'; +import { ConstrainedDictionaryModel } from '../../../models'; import { JavaPreset } from '../JavaPreset'; /** @@ -8,30 +8,44 @@ import { JavaPreset } from '../JavaPreset'; */ export const JAVA_JACKSON_PRESET: JavaPreset = { class: { - self({renderer, content}) { - renderer.addDependency('import com.fasterxml.jackson.annotation.*;'); + self({ renderer, content }) { + renderer.dependencyManager.addDependency( + 'import com.fasterxml.jackson.annotation.*;' + ); return content; }, - getter({renderer, propertyName, content, type}) { - if (type === PropertyType.property) { - const annotation = renderer.renderAnnotation('JsonProperty', `"${propertyName}"`); + property({ renderer, property, content }) { + //Properties that are dictionaries with unwrapped options, cannot get the annotation because it cannot be accurately unwrapped by the jackson library. + const isDictionary = + property.property instanceof ConstrainedDictionaryModel; + const hasUnwrappedOptions = + isDictionary && + (property.property as ConstrainedDictionaryModel).serializationType === + 'unwrap'; + if (!hasUnwrappedOptions) { + const annotation = renderer.renderAnnotation( + 'JsonProperty', + `"${property.unconstrainedPropertyName}"` + ); return renderer.renderBlock([annotation, content]); } return renderer.renderBlock([content]); - }, + } }, enum: { - self({renderer, content}) { - renderer.addDependency('import com.fasterxml.jackson.annotation.*;'); + self({ renderer, content }) { + renderer.dependencyManager.addDependency( + 'import com.fasterxml.jackson.annotation.*;' + ); return content; }, - getValue({content}) { + getValue({ content }) { return `@JsonValue ${content}`; }, - fromValue({content}) { + fromValue({ content }) { return `@JsonCreator ${content}`; - }, + } } }; diff --git a/src/generators/java/renderers/ClassRenderer.ts b/src/generators/java/renderers/ClassRenderer.ts index f006a55ef3..d3fd211997 100644 --- a/src/generators/java/renderers/ClassRenderer.ts +++ b/src/generators/java/renderers/ClassRenderer.ts @@ -1,30 +1,35 @@ import { JavaRenderer } from '../JavaRenderer'; -import { CommonModel, ClassPreset, PropertyType } from '../../../models'; -import { DefaultPropertyNames, FormatHelpers, getUniquePropertyName } from '../../../helpers'; +import { + ConstrainedDictionaryModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel +} from '../../../models'; +import { FormatHelpers } from '../../../helpers'; +import { JavaOptions } from '../JavaGenerator'; +import { ClassPresetType } from '../JavaPreset'; /** * Renderer for Java's `class` type - * + * * @extends JavaRenderer */ -export class ClassRenderer extends JavaRenderer { +export class ClassRenderer extends JavaRenderer { async defaultSelf(): Promise { const content = [ await this.renderProperties(), await this.runCtorPreset(), await this.renderAccessors(), - await this.runAdditionalContentPreset(), + await this.runAdditionalContentPreset() ]; if (this.options?.collectionType === 'List') { - this.addDependency('import java.util.List;'); + this.dependencyManager.addDependency('import java.util.List;'); } - if (this.model.additionalProperties !== undefined || this.model.patternProperties !== undefined) { - this.addDependency('import java.util.Map;'); + if (this.model.containsPropertyType(ConstrainedDictionaryModel)) { + this.dependencyManager.addDependency('import java.util.Map;'); } - - const formattedName = this.nameType(`${this.model.$id}`); - return `public class ${formattedName} { + + return `public class ${this.model.name} { ${this.indent(this.renderBlock(content, 2))} }`; } @@ -40,30 +45,16 @@ ${this.indent(this.renderBlock(content, 2))} const properties = this.model.properties || {}; const content: string[] = []; - for (const [propertyName, property] of Object.entries(properties)) { - const rendererProperty = await this.runPropertyPreset(propertyName, property); + for (const property of Object.values(properties)) { + const rendererProperty = await this.runPropertyPreset(property); content.push(rendererProperty); } - - if (this.model.additionalProperties !== undefined) { - const propertyName = getUniquePropertyName(this.model, DefaultPropertyNames.additionalProperties); - const additionalProperty = await this.runPropertyPreset(propertyName, this.model.additionalProperties, PropertyType.additionalProperty); - content.push(additionalProperty); - } - - if (this.model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(this.model.patternProperties)) { - const propertyName = getUniquePropertyName(this.model, `${pattern}${DefaultPropertyNames.patternProperties}`); - const renderedPatternProperty = await this.runPropertyPreset(propertyName, patternModel, PropertyType.patternProperties); - content.push(renderedPatternProperty); - } - } return this.renderBlock(content); } - runPropertyPreset(propertyName: string, property: CommonModel, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('property', { propertyName, property, type}); + runPropertyPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('property', { property }); } /** @@ -73,68 +64,39 @@ ${this.indent(this.renderBlock(content, 2))} const properties = this.model.properties || {}; const content: string[] = []; - for (const [propertyName, property] of Object.entries(properties)) { - const getter = await this.runGetterPreset(propertyName, property); - const setter = await this.runSetterPreset(propertyName, property); - content.push(this.renderBlock([getter, setter])); - } - - if (this.model.additionalProperties !== undefined) { - const propertyName = getUniquePropertyName(this.model, DefaultPropertyNames.additionalProperties); - const getter = await this.runGetterPreset(propertyName, this.model.additionalProperties, PropertyType.additionalProperty); - const setter = await this.runSetterPreset(propertyName, this.model.additionalProperties, PropertyType.additionalProperty); + for (const property of Object.values(properties)) { + const getter = await this.runGetterPreset(property); + const setter = await this.runSetterPreset(property); content.push(this.renderBlock([getter, setter])); } - if (this.model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(this.model.patternProperties)) { - const propertyName = getUniquePropertyName(this.model, `${pattern}${DefaultPropertyNames.patternProperties}`); - const getter = await this.runGetterPreset(propertyName, patternModel, PropertyType.patternProperties); - const setter = await this.runSetterPreset(propertyName, patternModel, PropertyType.patternProperties); - content.push(this.renderBlock([getter, setter])); - } - } - return this.renderBlock(content, 2); } - runGetterPreset(propertyName: string, property: CommonModel, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('getter', { propertyName, property, type}); + runGetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('getter', { property }); } - runSetterPreset(propertyName: string, property: CommonModel, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('setter', { propertyName, property, type}); + runSetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('setter', { property }); } } -export const JAVA_DEFAULT_CLASS_PRESET: ClassPreset = { +export const JAVA_DEFAULT_CLASS_PRESET: ClassPresetType = { self({ renderer }) { return renderer.defaultSelf(); }, - property({ renderer, propertyName, property, type }) { - propertyName = renderer.nameProperty(propertyName, property); - let propertyType = renderer.renderType(property); - if (type === PropertyType.additionalProperty || type === PropertyType.patternProperties) { - propertyType = `Map`; - } - return `private ${propertyType} ${propertyName};`; + property({ property }) { + return `private ${property.property.type} ${property.propertyName};`; }, - getter({ renderer, propertyName, property, type }) { - const formattedPropertyName = renderer.nameProperty(propertyName, property); - const getterName = `get${FormatHelpers.toPascalCase(propertyName)}`; - let getterType = renderer.renderType(property); - if (type === PropertyType.additionalProperty || type === PropertyType.patternProperties) { - getterType = `Map`; - } - return `public ${getterType} ${getterName}() { return this.${formattedPropertyName}; }`; + getter({ property }) { + const getterName = `get${FormatHelpers.toPascalCase( + property.propertyName + )}`; + return `public ${property.property.type} ${getterName}() { return this.${property.propertyName}; }`; }, - setter({ renderer, propertyName, property, type }) { - const formattedPropertyName = renderer.nameProperty(propertyName, property); - const setterName = FormatHelpers.toPascalCase(propertyName); - let setterType = renderer.renderType(property); - if (type === PropertyType.additionalProperty || type === PropertyType.patternProperties) { - setterType = `Map`; - } - return `public void set${setterName}(${setterType} ${formattedPropertyName}) { this.${formattedPropertyName} = ${formattedPropertyName}; }`; + setter({ property }) { + const setterName = FormatHelpers.toPascalCase(property.propertyName); + return `public void set${setterName}(${property.property.type} ${property.propertyName}) { this.${property.propertyName} = ${property.propertyName}; }`; } }; diff --git a/src/generators/java/renderers/EnumRenderer.ts b/src/generators/java/renderers/EnumRenderer.ts index 20b59e66c3..a83cdfc5ac 100644 --- a/src/generators/java/renderers/EnumRenderer.ts +++ b/src/generators/java/renderers/EnumRenderer.ts @@ -1,13 +1,14 @@ import { JavaRenderer } from '../JavaRenderer'; -import { FormatHelpers } from '../../../helpers'; -import { JavaEnumPreset } from '../JavaPreset'; +import { ConstrainedEnumModel } from '../../../models'; +import { EnumPresetType } from '../JavaPreset'; +import { JavaOptions } from '../JavaGenerator'; /** * Renderer for Java's `enum` type * * @extends JavaRenderer */ -export class EnumRenderer extends JavaRenderer { +export class EnumRenderer extends JavaRenderer { async defaultSelf(): Promise { const content = [ await this.renderItems(), @@ -16,14 +17,13 @@ export class EnumRenderer extends JavaRenderer { await this.runFromValuePreset(), await this.runAdditionalContentPreset() ]; - const formattedName = this.nameType(this.model.$id); - return `public enum ${formattedName} { + return `public enum ${this.model.name} { ${this.indent(this.renderBlock(content, 2))} }`; } async renderItems(): Promise { - const enums = this.model.enum || []; + const enums = this.model.values || []; const items: string[] = []; for (const value of enums) { @@ -35,45 +35,8 @@ ${this.indent(this.renderBlock(content, 2))} return `${content};`; } - normalizeKey(value: any): string { - let key; - switch (typeof value) { - case 'bigint': - case 'number': { - key = `number_${value}`; - break; - } - case 'boolean': { - key = `boolean_${value}`; - break; - } - case 'object': { - key = JSON.stringify(value); - break; - } - default: { - key = FormatHelpers.replaceSpecialCharacters(String(value), {exclude: [' ', '_'], separator: '_'}); - //Ensure no special char can be the beginning letter - if (!(/^[a-zA-Z]+$/).test(key.charAt(0))) { - key = `string_${key}`; - } - } - } - return FormatHelpers.toConstantCase(key); - } - - normalizeValue(value: any): string { - if (typeof value === 'string') { - return `"${value}"`; - } - if (typeof value === 'object') { - return `"${JSON.stringify(value).replace(/"/g, '\\"')}"`; - } - return String(value); - } - runItemPreset(item: any): Promise { - return this.runPreset('item', {item}); + return this.runPreset('item', { item }); } runCtorPreset(): Promise { @@ -89,39 +52,34 @@ ${this.indent(this.renderBlock(content, 2))} } } -export const JAVA_DEFAULT_ENUM_PRESET: JavaEnumPreset = { - self({renderer}) { +export const JAVA_DEFAULT_ENUM_PRESET: EnumPresetType = { + self({ renderer }) { return renderer.defaultSelf(); }, - item({renderer, item}) { - const key = renderer.normalizeKey(item); - const value = renderer.normalizeValue(item); - return `${key}(${value})`; + item({ item, model }) { + //Cast the enum type just to be sure, as some cases can be `int` type with floating value. + return `${item.key}((${model.type})${item.value})`; }, - ctor({renderer, model}) { - const enumName = renderer.nameType(model.$id); - const type = Array.isArray(model.type) ? 'Object' : model.type; - const classType = renderer.toClassType(renderer.toJavaType(type, model)); - return `private ${classType} value; + ctor({ model }) { + return `private ${model.type} value; -${enumName}(${classType} value) { +${model.name}(${model.type} value) { this.value = value; }`; }, - getValue({renderer, model}) { - const type = Array.isArray(model.type) ? 'Object' : model.type; - const classType = renderer.toClassType(renderer.toJavaType(type, model)); - return `public ${classType} getValue() { + getValue({ model }) { + return `public ${model.type} getValue() { return value; }`; }, - fromValue({renderer, model}) { - const enumName = renderer.nameType(model.$id); - const type = Array.isArray(model.type) ? 'Object' : model.type; - const classType = renderer.toClassType(renderer.toJavaType(type, model)); - return `public static ${enumName} fromValue(${classType} value) { - for (${enumName} e : ${enumName}.values()) { - if (e.value.equals(value)) { + fromValue({ model }) { + const valueComparitor = + model.type.charAt(0) === model.type.charAt(0).toUpperCase() + ? 'e.value.equals(value)' + : 'e.value == value'; + return `public static ${model.name} fromValue(${model.type} value) { + for (${model.name} e : ${model.name}.values()) { + if (${valueComparitor}) { return e; } } diff --git a/src/generators/javascript/Constants.ts b/src/generators/javascript/Constants.ts index 3eb67c0b45..2c6eca7c5c 100644 --- a/src/generators/javascript/Constants.ts +++ b/src/generators/javascript/Constants.ts @@ -1,3 +1,5 @@ +import { checkForReservedKeyword } from '../../helpers'; + //Based on https://www.w3schools.com/js/js_reserved.asp export const RESERVED_JAVASCRIPT_KEYWORDS = [ //Standard reserved keywords @@ -66,134 +68,141 @@ export const RESERVED_JAVASCRIPT_KEYWORDS = [ 'with', 'yield', // Reserved for > ECMAScript 5/6 - 'abstract', - 'boolean', - 'byte', + 'abstract', + 'boolean', + 'byte', 'char', - 'double', - 'final', - 'float', + 'double', + 'final', + 'float', 'goto', - 'int', - 'long', - 'native', + 'int', + 'long', + 'native', 'short', - 'synchronized', - 'throws', - 'transient', + 'synchronized', + 'throws', + 'transient', 'volatile', //Reserved built-in objects, properties and methods - 'hasOwnProperty', - 'Infinity', - 'isFinite', + 'hasOwnProperty', + 'Infinity', + 'isFinite', 'isNaN', - 'isPrototypeOf', - 'length', - 'Math', + 'isPrototypeOf', + 'length', + 'Math', 'NaN', - 'name', - 'Number', - 'Object', + 'name', + 'Number', + 'Object', 'prototype', - 'String', - 'toString', - 'undefined', + 'String', + 'toString', + 'undefined', 'valueOf', //Java reserved words - 'getClass', - 'java', - 'JavaArray', + 'getClass', + 'java', + 'JavaArray', 'javaClass', - 'JavaObject', + 'JavaObject', 'JavaPackage', //Other reserved words - 'alert', - 'all', - 'anchor', + 'alert', + 'all', + 'anchor', 'anchors', - 'area', - 'assign', - 'blur', + 'area', + 'assign', + 'blur', 'button', - 'checkbox', - 'clearInterval', - 'clearTimeout', + 'checkbox', + 'clearInterval', + 'clearTimeout', 'clientInformation', - 'close', - 'closed', - 'confirm', + 'close', + 'closed', + 'confirm', 'constructor', - 'crypto', - 'decodeURI', - 'decodeURIComponent', + 'crypto', + 'decodeURI', + 'decodeURIComponent', 'defaultStatus', - 'document', - 'element', - 'elements', + 'document', + 'element', + 'elements', 'embed', - 'embeds', - 'encodeURI', - 'encodeURIComponent', + 'embeds', + 'encodeURI', + 'encodeURIComponent', 'escape', - 'event', - 'fileUpload', - 'focus', + 'event', + 'fileUpload', + 'focus', 'form', - 'forms', - 'frame', - 'innerHeight', + 'forms', + 'frame', + 'innerHeight', 'innerWidth', - 'layer', - 'layers', - 'link', + 'layer', + 'layers', + 'link', 'location', - 'mimeTypes', - 'navigate', - 'navigator', + 'mimeTypes', + 'navigate', + 'navigator', 'frames', - 'frameRate', - 'hidden', - 'history', + 'frameRate', + 'hidden', + 'history', 'image', - 'images', - 'offscreenBuffering', - 'open', + 'images', + 'offscreenBuffering', + 'open', 'opener', - 'option', - 'outerHeight', - 'outerWidth', + 'option', + 'outerHeight', + 'outerWidth', 'packages', - 'pageXOffset', - 'pageYOffset', - 'parent', + 'pageXOffset', + 'pageYOffset', + 'parent', 'parseFloat', - 'parseInt', - 'password', - 'pkcs11', + 'parseInt', + 'password', + 'pkcs11', 'plugin', - 'prompt', - 'propertyIsEnum', - 'radio', + 'prompt', + 'propertyIsEnum', + 'radio', 'reset', - 'screenX', - 'screenY', - 'scroll', + 'screenX', + 'screenY', + 'scroll', 'secure', - 'select', - 'self', - 'setInterval', + 'select', + 'self', + 'setInterval', 'setTimeout', - 'status', - 'submit', - 'taint', + 'status', + 'submit', + 'taint', 'text', - 'textarea', - 'top', - 'unescape', + 'textarea', + 'top', + 'unescape', 'untaint', 'window' ]; -export function isReservedJavaScriptKeyword(word: string): boolean { - return RESERVED_JAVASCRIPT_KEYWORDS.includes(word); +export function isReservedJavaScriptKeyword( + word: string, + forceLowerCase = true +): boolean { + return checkForReservedKeyword( + word, + RESERVED_JAVASCRIPT_KEYWORDS, + forceLowerCase + ); } diff --git a/src/generators/javascript/JavaScriptConstrainer.ts b/src/generators/javascript/JavaScriptConstrainer.ts new file mode 100644 index 0000000000..0ef63fbdda --- /dev/null +++ b/src/generators/javascript/JavaScriptConstrainer.ts @@ -0,0 +1,54 @@ +import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; +import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; +import { + defaultEnumKeyConstraints, + defaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; +import { Constraints } from '../../helpers'; +import { JavaScriptTypeMapping } from './JavaScriptGenerator'; + +export const JavaScriptDefaultTypeMapping: JavaScriptTypeMapping = { + Object(): string { + return ''; + }, + Reference(): string { + return ''; + }, + Any(): string { + return ''; + }, + Float(): string { + return ''; + }, + Integer(): string { + return ''; + }, + String(): string { + return ''; + }, + Boolean(): string { + return ''; + }, + Tuple(): string { + return ''; + }, + Array(): string { + return ''; + }, + Enum(): string { + return ''; + }, + Union(): string { + return ''; + }, + Dictionary(): string { + return ''; + } +}; + +export const JavaScriptDefaultConstraints: Constraints = { + enumKey: defaultEnumKeyConstraints(), + enumValue: defaultEnumValueConstraints(), + modelName: defaultModelNameConstraints(), + propertyKey: defaultPropertyKeyConstraints() +}; diff --git a/src/generators/javascript/JavaScriptDependencyManager.ts b/src/generators/javascript/JavaScriptDependencyManager.ts new file mode 100644 index 0000000000..767ec087c8 --- /dev/null +++ b/src/generators/javascript/JavaScriptDependencyManager.ts @@ -0,0 +1,20 @@ +import { renderJavaScriptDependency } from '../../helpers'; +import { AbstractDependencyManager } from '../AbstractDependencyManager'; +import { JavaScriptOptions } from './JavaScriptGenerator'; + +export class JavaScriptDependencyManager extends AbstractDependencyManager { + constructor(public options: JavaScriptOptions, dependencies: string[] = []) { + super(dependencies); + } + + /** + * Simple helper function to render a dependency based on the module system that the user defines. + */ + renderDependency(toImport: string, fromModule: string): string { + return renderJavaScriptDependency( + toImport, + fromModule, + this.options.moduleSystem + ); + } +} diff --git a/src/generators/javascript/JavaScriptFileGenerator.ts b/src/generators/javascript/JavaScriptFileGenerator.ts index b102a4fcd2..ddebdaae5d 100644 --- a/src/generators/javascript/JavaScriptFileGenerator.ts +++ b/src/generators/javascript/JavaScriptFileGenerator.ts @@ -1,24 +1,45 @@ import { JavaScriptGenerator, JavaScriptRenderCompleteModelOptions } from './'; -import { CommonInputModel, OutputModel } from '../../models'; +import { InputMetaModel, OutputModel } from '../../models'; import * as path from 'path'; import { AbstractFileGenerator } from '../AbstractFileGenerator'; import { FileHelpers } from '../../helpers'; -export class JavaScriptFileGenerator extends JavaScriptGenerator implements AbstractFileGenerator { +export class JavaScriptFileGenerator + extends JavaScriptGenerator + implements AbstractFileGenerator +{ /** - * Generates all the models to an output directory each model with their own separate files. - * + * Generates all the models to an output directory each model with their own separate files. + * * @param input * @param outputDirectory where you want the models generated to * @param options + * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ - public async generateToFiles(input: Record | CommonInputModel, outputDirectory: string, options?: JavaScriptRenderCompleteModelOptions): Promise { - let generatedModels = await this.generateCompleteModels(input, options || {}); + public async generateToFiles( + input: Record | InputMetaModel, + outputDirectory: string, + options?: JavaScriptRenderCompleteModelOptions, + ensureFilesWritten = false + ): Promise { + let generatedModels = await this.generateCompleteModels( + input, + options || {} + ); //Filter anything out that have not been successfully generated - generatedModels = generatedModels.filter((outputModel) => { return outputModel.modelName !== ''; }); + generatedModels = generatedModels.filter((outputModel) => { + return outputModel.modelName !== ''; + }); for (const outputModel of generatedModels) { - const filePath = path.resolve(outputDirectory, `${outputModel.modelName}.js`); - await FileHelpers.writerToFileSystem(outputModel.result, filePath); + const filePath = path.resolve( + outputDirectory, + `${outputModel.modelName}.js` + ); + await FileHelpers.writerToFileSystem( + outputModel.result, + filePath, + ensureFilesWritten + ); } return generatedModels; } diff --git a/src/generators/javascript/JavaScriptGenerator.ts b/src/generators/javascript/JavaScriptGenerator.ts index 397e214912..d164696a14 100644 --- a/src/generators/javascript/JavaScriptGenerator.ts +++ b/src/generators/javascript/JavaScriptGenerator.ts @@ -1,37 +1,96 @@ -import { - AbstractGenerator, +import { + AbstractGenerator, CommonGeneratorOptions, defaultGeneratorOptions } from '../AbstractGenerator'; -import { CommonModel, CommonInputModel, RenderOutput } from '../../models'; -import { TypeHelpers, ModelKind, CommonNamingConvention, CommonNamingConventionImplementation } from '../../helpers'; +import { + ConstrainedMetaModel, + ConstrainedObjectModel, + InputMetaModel, + MetaModel, + RenderOutput +} from '../../models'; +import { + TypeMapping, + Constraints, + split, + constrainMetaModel, + SplitOptions +} from '../../helpers'; import { JavaScriptPreset, JS_DEFAULT_PRESET } from './JavaScriptPreset'; import { ClassRenderer } from './renderers/ClassRenderer'; import { Logger } from '../../'; -export interface JavaScriptOptions extends CommonGeneratorOptions { - namingConvention?: CommonNamingConvention -} - -export interface JavaScriptRenderCompleteModelOptions { - moduleSystem?: 'ESM' | 'CJS'; +import { + JavaScriptDefaultConstraints, + JavaScriptDefaultTypeMapping +} from './JavaScriptConstrainer'; +import { DeepPartial, mergePartialAndDefault } from '../../utils'; +import { JavaScriptDependencyManager } from './JavaScriptDependencyManager'; +export interface JavaScriptOptions + extends CommonGeneratorOptions { + typeMapping: TypeMapping; + constraints: Constraints; + moduleSystem: 'ESM' | 'CJS'; } +export type JavaScriptTypeMapping = TypeMapping< + JavaScriptOptions, + JavaScriptDependencyManager +>; +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface JavaScriptRenderCompleteModelOptions {} /** * Generator for JavaScript */ -export class JavaScriptGenerator extends AbstractGenerator { +export class JavaScriptGenerator extends AbstractGenerator< + JavaScriptOptions, + JavaScriptRenderCompleteModelOptions +> { static defaultOptions: JavaScriptOptions = { ...defaultGeneratorOptions, defaultPreset: JS_DEFAULT_PRESET, - namingConvention: CommonNamingConventionImplementation + typeMapping: JavaScriptDefaultTypeMapping, + constraints: JavaScriptDefaultConstraints, + moduleSystem: 'ESM' }; - constructor( - options: JavaScriptOptions = JavaScriptGenerator.defaultOptions, - ) { - super('JavaScript', JavaScriptGenerator.defaultOptions, options); + static defaultCompleteModelOptions: JavaScriptRenderCompleteModelOptions = {}; + + constructor(options?: DeepPartial) { + const realizedOptions = JavaScriptGenerator.getJavaScriptOptions(options); + super('JavaScript', realizedOptions); + } + + /** + * Returns the JavaScript options by merging custom options with default ones. + */ + static getJavaScriptOptions( + options?: DeepPartial + ): JavaScriptOptions { + const optionsToUse = mergePartialAndDefault( + JavaScriptGenerator.defaultOptions, + options + ) as JavaScriptOptions; + //Always overwrite the dependency manager unless user explicitly state they want it (ignore default temporary dependency manager) + if (options?.dependencyManager === undefined) { + optionsToUse.dependencyManager = () => { + return new JavaScriptDependencyManager(optionsToUse); + }; + } + return optionsToUse; + } + + /** + * Wrapper to get an instance of the dependency manager + */ + getDependencyManager( + options: JavaScriptOptions + ): JavaScriptDependencyManager { + return this.getDependencyManagerInstance( + options + ) as JavaScriptDependencyManager; } - + /** * Render a complete model result where the model code, library and model dependencies are all bundled appropriately. * @@ -40,51 +99,122 @@ export class JavaScriptGenerator extends AbstractGenerator { + async renderCompleteModel( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + completeModelOptions: Partial, + options: DeepPartial + ): Promise { + //const completeModelOptionsToUse = mergePartialAndDefault(JavaScriptGenerator.defaultCompleteModelOptions, completeModelOptions) as JavaScriptRenderCompleteModelOptions; + const optionsToUse = JavaScriptGenerator.getJavaScriptOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const outputModel = await this.render(model, inputModel); - let modelDependencies = model.getNearestDependencies(); + const modelDependencies = model.getNearestDependencies(); //Ensure model dependencies have their rendered name - modelDependencies = modelDependencies.map((dependencyModelName) => { - return this.options.namingConvention?.type ? this.options.namingConvention.type(dependencyModelName, { inputModel, model: inputModel.models[String(dependencyModelName)] }) : dependencyModelName; - }); - //Filter out any dependencies that is recursive to it'self - modelDependencies = modelDependencies.filter((dependencyModelName) => { - return dependencyModelName !== outputModel.renderedName; - }); - //Create the correct dependency imports - modelDependencies = modelDependencies.map((formattedDependencyModelName) => { - if (options.moduleSystem === 'CJS') { - return `const ${formattedDependencyModelName} = require('./${formattedDependencyModelName}');`; - } - return `import ${formattedDependencyModelName} from './${formattedDependencyModelName}';`; + const modelDependencyImports = modelDependencies.map((dependencyModel) => { + return dependencyManagerToUse.renderDependency( + dependencyModel.name, + `./${dependencyModel.name}` + ); }); let modelCode = `${outputModel.result} export default ${outputModel.renderedName}; `; - if (options.moduleSystem === 'CJS') { + if (optionsToUse.moduleSystem === 'CJS') { modelCode = `${outputModel.result} module.exports = ${outputModel.renderedName};`; } - const outputContent = `${[...modelDependencies, ...outputModel.dependencies].join('\n')} + const outputContent = `${[ + ...modelDependencyImports, + ...outputModel.dependencies + ].join('\n')} ${modelCode}`; - return RenderOutput.toRenderOutput({ result: outputContent, renderedName: outputModel.renderedName, dependencies: outputModel.dependencies }); + return RenderOutput.toRenderOutput({ + result: outputContent, + renderedName: outputModel.renderedName, + dependencies: outputModel.dependencies + }); } - render(model: CommonModel, inputModel: CommonInputModel): Promise { - const kind = TypeHelpers.extractKind(model); - if (kind === ModelKind.OBJECT) { - return this.renderClass(model, inputModel); + render( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = JavaScriptGenerator.getJavaScriptOptions({ + ...this.options, + ...options + }); + if (model instanceof ConstrainedObjectModel) { + return this.renderClass(model, inputModel, optionsToUse); } - Logger.warn(`JS generator, cannot generate model for '${model.$id}'`); - return Promise.resolve(RenderOutput.toRenderOutput({result: '', renderedName: '', dependencies: []})); + Logger.warn(`JS generator, cannot generate model for '${model.name}'`); + return Promise.resolve( + RenderOutput.toRenderOutput({ + result: '', + renderedName: '', + dependencies: [] + }) + ); + } + + splitMetaModel(model: MetaModel): MetaModel[] { + //These are the models that we have separate renderers for + const metaModelsToSplit: SplitOptions = { + splitObject: true + }; + return split(model, metaModelsToSplit); } - async renderClass(model: CommonModel, inputModel: CommonInputModel): Promise { - const presets = this.getPresets('class'); - const renderer = new ClassRenderer(this.options, this, presets, model, inputModel); + constrainToMetaModel( + model: MetaModel, + options: DeepPartial + ): ConstrainedMetaModel { + const optionsToUse = JavaScriptGenerator.getJavaScriptOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + return constrainMetaModel( + this.options.typeMapping, + this.options.constraints, + { + metaModel: model, + dependencyManager: dependencyManagerToUse, + options: this.options, + constrainedName: '' //This is just a placeholder, it will be constrained within the function + } + ); + } + + async renderClass( + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = JavaScriptGenerator.getJavaScriptOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('class'); + const renderer = new ClassRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = renderer.nameType(model.$id, model); - return RenderOutput.toRenderOutput({result, renderedName, dependencies: renderer.dependencies}); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } } diff --git a/src/generators/javascript/JavaScriptPreset.ts b/src/generators/javascript/JavaScriptPreset.ts index 56c48b593c..b11c8c098e 100644 --- a/src/generators/javascript/JavaScriptPreset.ts +++ b/src/generators/javascript/JavaScriptPreset.ts @@ -1,12 +1,15 @@ -/* eslint-disable @typescript-eslint/ban-types */ import { Preset, ClassPreset } from '../../models'; +import { + ClassRenderer, + JS_DEFAULT_CLASS_PRESET +} from './renderers/ClassRenderer'; -import { ClassRenderer, JS_DEFAULT_CLASS_PRESET } from './renderers/ClassRenderer'; +export type ClassPresetType = ClassPreset; -export type JavaScriptPreset = Preset<{ - class: ClassPreset; +export type JavaScriptPreset = Preset<{ + class: ClassPresetType; }>; export const JS_DEFAULT_PRESET: JavaScriptPreset = { - class: JS_DEFAULT_CLASS_PRESET, + class: JS_DEFAULT_CLASS_PRESET }; diff --git a/src/generators/javascript/JavaScriptRenderer.ts b/src/generators/javascript/JavaScriptRenderer.ts index 2c6de544e4..cf88b5be0a 100644 --- a/src/generators/javascript/JavaScriptRenderer.ts +++ b/src/generators/javascript/JavaScriptRenderer.ts @@ -1,86 +1,37 @@ import { AbstractRenderer } from '../AbstractRenderer'; import { JavaScriptGenerator, JavaScriptOptions } from './JavaScriptGenerator'; -import { getUniquePropertyName, FormatHelpers, DefaultPropertyNames } from '../../helpers'; -import { CommonModel, CommonInputModel, Preset, PropertyType } from '../../models'; -import { isReservedJavaScriptKeyword } from './Constants'; +import { FormatHelpers } from '../../helpers'; +import { Preset, ConstrainedMetaModel, InputMetaModel } from '../../models'; +import { JavaScriptDependencyManager } from './JavaScriptDependencyManager'; /** * Common renderer for JavaScript types - * + * * @extends AbstractRenderer */ -export abstract class JavaScriptRenderer extends AbstractRenderer { +export abstract class JavaScriptRenderer< + RendererModelType extends ConstrainedMetaModel +> extends AbstractRenderer< + JavaScriptOptions, + JavaScriptGenerator, + RendererModelType +> { constructor( options: JavaScriptOptions, generator: JavaScriptGenerator, presets: Array<[Preset, unknown]>, - model: CommonModel, - inputModel: CommonInputModel, + model: RendererModelType, + inputModel: InputMetaModel, + public dependencyManager: JavaScriptDependencyManager ) { super(options, generator, presets, model, inputModel); } - /** - * Renders the name of a type based on provided generator option naming convention type function. - * - * This is used to render names of models (example TS class) and then later used if that class is referenced from other models. - * - * @param name - * @param model - */ - nameType(name: string | undefined, model?: CommonModel): string { - return this.options?.namingConvention?.type - ? this.options.namingConvention.type(name, { model: model || this.model, inputModel: this.inputModel, reservedKeywordCallback: isReservedJavaScriptKeyword }) - : name || ''; - } - - /** - * Renders the name of a property based on provided generator option naming convention property function. - * - * @param propertyName - * @param property - */ - nameProperty(propertyName: string | undefined, property?: CommonModel): string { - return this.options?.namingConvention?.property - ? this.options.namingConvention.property(propertyName, { model: this.model, inputModel: this.inputModel, property, reservedKeywordCallback: isReservedJavaScriptKeyword }) - : propertyName || ''; - } - renderComments(lines: string | string[]): string { lines = FormatHelpers.breakLines(lines); - const content = lines.map(line => ` * ${line}`).join('\n'); + const content = lines.map((line) => ` * ${line}`).join('\n'); return `/** ${content} */`; } - - async renderProperties(): Promise { - const properties = this.model.properties || {}; - const content: string[] = []; - - for (const [propertyName, property] of Object.entries(properties)) { - const rendererProperty = await this.runPropertyPreset(propertyName, property); - content.push(rendererProperty); - } - - if (this.model.additionalProperties !== undefined) { - const propertyName = getUniquePropertyName(this.model, DefaultPropertyNames.additionalProperties); - const additionalProperty = await this.runPropertyPreset(propertyName, this.model.additionalProperties, PropertyType.additionalProperty); - content.push(additionalProperty); - } - - if (this.model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(this.model.patternProperties)) { - const propertyName = getUniquePropertyName(this.model, `${pattern}${DefaultPropertyNames.patternProperties}`); - const renderedPatternProperty = await this.runPropertyPreset(propertyName, patternModel, PropertyType.patternProperties); - content.push(renderedPatternProperty); - } - } - - return this.renderBlock(content); - } - - runPropertyPreset(propertyName: string, property: CommonModel, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('property', { propertyName, property, type }); - } } diff --git a/src/generators/javascript/constrainer/EnumConstrainer.ts b/src/generators/javascript/constrainer/EnumConstrainer.ts new file mode 100644 index 0000000000..46e230ea69 --- /dev/null +++ b/src/generators/javascript/constrainer/EnumConstrainer.ts @@ -0,0 +1,15 @@ +import { EnumKeyConstraint, EnumValueConstraint } from '../../../helpers'; +/** + * Enums for JS do not have any constraints because we never render anything specific for enums. + **/ +export function defaultEnumKeyConstraints(): EnumKeyConstraint { + return ({ enumKey }) => { + return enumKey; + }; +} + +export function defaultEnumValueConstraints(): EnumValueConstraint { + return ({ enumValue }) => { + return enumValue; + }; +} diff --git a/src/generators/javascript/constrainer/ModelNameConstrainer.ts b/src/generators/javascript/constrainer/ModelNameConstrainer.ts new file mode 100644 index 0000000000..b016689ae9 --- /dev/null +++ b/src/generators/javascript/constrainer/ModelNameConstrainer.ts @@ -0,0 +1,49 @@ +import { + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { isReservedJavaScriptKeyword } from '../Constants'; + +export type ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultModelNameConstraints: ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NAMING_FORMATTER: (value: string) => { + return FormatHelpers.toPascalCase(value); + }, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedJavaScriptKeyword); + } +}; +export function defaultModelNameConstraints( + customConstraints?: Partial +): ModelNameConstraint { + const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; + + return ({ modelName }) => { + let constrainedValue = modelName; + constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue); + constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue); + constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue); + constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue); + constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); + return constrainedValue; + }; +} diff --git a/src/generators/javascript/constrainer/PropertyKeyConstrainer.ts b/src/generators/javascript/constrainer/PropertyKeyConstrainer.ts new file mode 100644 index 0000000000..5299519ea5 --- /dev/null +++ b/src/generators/javascript/constrainer/PropertyKeyConstrainer.ts @@ -0,0 +1,78 @@ +import { ConstrainedObjectModel, ObjectModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { isReservedJavaScriptKeyword } from '../Constants'; + +export type PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_PROPERTIES: ( + constrainedObjectModel: ConstrainedObjectModel, + objectModel: ObjectModel, + propertyName: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toCamelCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedJavaScriptKeyword); + } +}; + +export function defaultPropertyKeyConstraints( + customConstraints?: Partial +): PropertyKeyConstraint { + const constraints = { + ...DefaultPropertyKeyConstraints, + ...customConstraints + }; + + return ({ objectPropertyModel, constrainedObjectModel, objectModel }) => { + let constrainedPropertyKey = objectPropertyModel.propertyName; + + constrainedPropertyKey = constraints.NO_SPECIAL_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey); + constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS( + constrainedPropertyKey + ); + //If the property name has been manipulated, lets make sure it don't clash with existing properties + if (constrainedPropertyKey !== objectPropertyModel.propertyName) { + constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES( + constrainedObjectModel, + objectModel, + constrainedPropertyKey, + constraints.NAMING_FORMATTER + ); + } + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + return constrainedPropertyKey; + }; +} diff --git a/src/generators/javascript/index.ts b/src/generators/javascript/index.ts index 586eb2b0c0..f857a319ed 100644 --- a/src/generators/javascript/index.ts +++ b/src/generators/javascript/index.ts @@ -3,3 +3,18 @@ export * from './JavaScriptFileGenerator'; export { JS_DEFAULT_PRESET } from './JavaScriptPreset'; export type { JavaScriptPreset } from './JavaScriptPreset'; export * from './presets'; + +export { + defaultEnumKeyConstraints as javaScriptDefaultEnumKeyConstraints, + defaultEnumValueConstraints as javaScriptDefaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; + +export { + DefaultModelNameConstraints as JavaScriptDefaultModelNameConstraints, + defaultModelNameConstraints as javaScriptDefaultModelNameConstraints +} from './constrainer/ModelNameConstrainer'; + +export { + DefaultPropertyKeyConstraints as JavaScriptDefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints as javaScriptDefaultPropertyKeyConstraints +} from './constrainer/PropertyKeyConstrainer'; diff --git a/src/generators/javascript/presets/CommonPreset.ts b/src/generators/javascript/presets/CommonPreset.ts index a6aa52ed50..007f13f5fc 100644 --- a/src/generators/javascript/presets/CommonPreset.ts +++ b/src/generators/javascript/presets/CommonPreset.ts @@ -1,7 +1,12 @@ import { JavaScriptRenderer } from '../JavaScriptRenderer'; import { JavaScriptPreset } from '../JavaScriptPreset'; -import { getUniquePropertyName, DefaultPropertyNames, TypeHelpers, ModelKind } from '../../../helpers'; -import { CommonInputModel, CommonModel } from '../../../models'; +import { + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + ConstrainedReferenceModel +} from '../../../models'; import renderExampleFunction from './utils/ExampleFunction'; export interface JavaScriptCommonPresetOptions { @@ -12,25 +17,29 @@ export interface JavaScriptCommonPresetOptions { function realizePropertyFactory(prop: string) { return `$\{typeof ${prop} === 'number' || typeof ${prop} === 'boolean' ? ${prop} : JSON.stringify(${prop})}`; } -function renderMarshalProperty(modelInstanceVariable: string, model: CommonModel, inputModel: CommonInputModel) { - if (model.$ref) { - const resolvedModel = inputModel.models[model.$ref]; - const propertyModelKind = TypeHelpers.extractKind(resolvedModel); +function renderMarshalProperty( + modelInstanceVariable: string, + model: ConstrainedMetaModel +) { + if ( + model instanceof ConstrainedReferenceModel && + !(model.ref instanceof ConstrainedEnumModel) + ) { //Referenced enums only need standard marshalling, so lets filter those away - if (propertyModelKind !== ModelKind.ENUM) { - return `$\{${modelInstanceVariable}.marshal()}`; - } + return `$\{${modelInstanceVariable}.marshal()}`; } return realizePropertyFactory(modelInstanceVariable); } -function renderMarshalProperties(model: CommonModel, renderer: JavaScriptRenderer, inputModel: CommonInputModel) { +function renderMarshalProperties(model: ConstrainedObjectModel) { const properties = model.properties || {}; const propertyKeys = [...Object.entries(properties)]; const marshalProperties = propertyKeys.map(([prop, propModel]) => { - const formattedPropertyName = renderer.nameProperty(prop, propModel); - const modelInstanceVariable = `this.${formattedPropertyName}`; - const propMarshalCode = renderMarshalProperty(modelInstanceVariable, propModel, inputModel); - const marshalCode = `json += \`"${prop}": ${propMarshalCode},\`;`; + const modelInstanceVariable = `this.${prop}`; + const propMarshalCode = renderMarshalProperty( + modelInstanceVariable, + propModel.property + ); + const marshalCode = `json += \`"${propModel.unconstrainedPropertyName}": ${propMarshalCode},\`;`; return `if(${modelInstanceVariable} !== undefined) { ${marshalCode} }`; @@ -38,174 +47,145 @@ function renderMarshalProperties(model: CommonModel, renderer: JavaScriptRendere return marshalProperties.join('\n'); } -function renderMarshalPatternProperties(model: CommonModel, renderer: JavaScriptRenderer, inputModel: CommonInputModel) { - let marshalPatternProperties = ''; - if (model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(model.patternProperties)) { - let patternPropertyName = getUniquePropertyName(model, `${pattern}${DefaultPropertyNames.patternProperties}`); - patternPropertyName = renderer.nameProperty(patternPropertyName, patternModel); - const modelInstanceVariable = 'value'; - const patternPropertyMarshalCode = renderMarshalProperty(modelInstanceVariable, patternModel, inputModel); - const marshalCode = `json += \`"$\{key}": ${patternPropertyMarshalCode},\`;`; - marshalPatternProperties += `if(this.${patternPropertyName} !== undefined) { - for (const [key, value] of this.${patternPropertyName}.entries()) { - //Only render pattern properties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - ${marshalCode} - } -}`; - } - } - return marshalPatternProperties; -} - -function renderMarshalAdditionalProperties(model: CommonModel, renderer: JavaScriptRenderer, inputModel: CommonInputModel) { - let marshalAdditionalProperties = ''; - if (model.additionalProperties !== undefined) { - let additionalPropertyName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties); - additionalPropertyName = renderer.nameProperty(additionalPropertyName, model.additionalProperties); - const modelInstanceVariable = 'value'; - const patternPropertyMarshalCode = renderMarshalProperty(modelInstanceVariable, model.additionalProperties, inputModel); - const marshalCode = `json += \`"$\{key}": ${patternPropertyMarshalCode},\`;`; - marshalAdditionalProperties = `if(this.${additionalPropertyName} !== undefined) { - for (const [key, value] of this.${additionalPropertyName}.entries()) { - //Only render additionalProperties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - ${marshalCode} - } -}`; - } - return marshalAdditionalProperties; -} - /** * Render `marshal` function based on model */ -function renderMarshal({ renderer, model, inputModel }: { - renderer: JavaScriptRenderer, - model: CommonModel, - inputModel: CommonInputModel +function renderMarshal({ + renderer, + model +}: { + renderer: JavaScriptRenderer; + model: ConstrainedObjectModel; }): string { return `marshal(){ let json = '{' -${renderer.indent(renderMarshalProperties(model, renderer, inputModel))} -${renderer.indent(renderMarshalPatternProperties(model, renderer, inputModel))} -${renderer.indent(renderMarshalAdditionalProperties(model, renderer, inputModel))} +${renderer.indent(renderMarshalProperties(model))} //Remove potential last comma return \`$\{json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; }`; } -function renderUnmarshalProperty(modelInstanceVariable: string, model: CommonModel, inputModel: CommonInputModel, renderer: JavaScriptRenderer) { - if (model.$ref) { - const resolvedModel = inputModel.models[model.$ref]; - const propertyModelKind = TypeHelpers.extractKind(resolvedModel); - //Referenced enums only need standard marshalling, so lets filter those away - if (propertyModelKind !== ModelKind.ENUM) { - return `${renderer.nameType(model.$ref)}.unmarshal(${modelInstanceVariable})`; - } +function renderUnmarshalProperty( + modelInstanceVariable: string, + model: ConstrainedMetaModel +) { + //Referenced enums only need standard marshalling, so lets filter those away + if ( + model instanceof ConstrainedReferenceModel && + model.ref instanceof ConstrainedEnumModel + ) { + return `${model.type}.unmarshal(${modelInstanceVariable})`; } return `${modelInstanceVariable}`; } -function renderUnmarshalProperties(model: CommonModel, renderer: JavaScriptRenderer, inputModel: CommonInputModel) { +function renderUnmarshalProperties(model: ConstrainedObjectModel) { const properties = model.properties || {}; const propertyKeys = [...Object.entries(properties)]; - const unmarshalProperties = propertyKeys.map(([prop, propModel]) => { - const formattedPropertyName = renderer.nameProperty(prop, propModel); - const modelInstanceVariable = `obj["${prop}"]`; - const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, propModel, inputModel, renderer); - return `if (${modelInstanceVariable} !== undefined) { - instance.${formattedPropertyName} = ${unmarshalCode}; -}`; - }); - return unmarshalProperties.join('\n'); -} - -function renderUnmarshalPatternProperties(model: CommonModel, renderer: JavaScriptRenderer, inputModel: CommonInputModel) { - let unmarshalPatternProperties = ''; - let setPatternPropertiesMap = ''; - if (model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(model.patternProperties)) { - let patternPropertyName = getUniquePropertyName(model, `${pattern}${DefaultPropertyNames.patternProperties}`); - patternPropertyName = renderer.nameProperty(patternPropertyName, patternModel); - const modelInstanceVariable = 'value'; - const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, patternModel, inputModel, renderer); - setPatternPropertiesMap += `if (instance.${patternPropertyName} === undefined) {instance.${patternPropertyName} = new Map();}\n`; - unmarshalPatternProperties += `//Check all pattern properties -if (key.match(new RegExp('${pattern}'))) { - instance.${patternPropertyName}.set(key, ${unmarshalCode}); - continue; + const normalProperties = propertyKeys.filter( + ([, propModel]) => + !(propModel instanceof ConstrainedDictionaryModel) || + propModel.serializationType === 'normal' + ); + const unmarshalNormalProperties = normalProperties.map( + ([prop, propModel]) => { + const modelInstanceVariable = `obj["${propModel.unconstrainedPropertyName}"]`; + const unmarshalCode = renderUnmarshalProperty( + modelInstanceVariable, + propModel.property + ); + return `if (${modelInstanceVariable} !== undefined) { + instance.${prop} = ${unmarshalCode}; }`; } - } - return { unmarshalPatternProperties, setPatternPropertiesMap }; + ); + + return ` +${unmarshalNormalProperties.join('\n')} + +`; } -function renderUnmarshalAdditionalProperties(model: CommonModel, renderer: JavaScriptRenderer, inputModel: CommonInputModel) { - let unmarshalAdditionalProperties = ''; - let setAdditionalPropertiesMap = ''; - if (model.additionalProperties !== undefined) { - let additionalPropertyName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties); - additionalPropertyName = renderer.nameProperty(additionalPropertyName, model.additionalProperties); +function renderUnmarshalUnwrapProperties( + model: ConstrainedObjectModel, + renderer: JavaScriptRenderer +) { + const unmarshalAdditionalProperties = []; + const setAdditionalPropertiesMap = []; + const unwrappedDictionaryProperties = Object.entries(model.properties).filter( + ([, propModel]) => + propModel instanceof ConstrainedDictionaryModel && + propModel.serializationType === 'unwrap' + ); + for (const [prop] of unwrappedDictionaryProperties) { const modelInstanceVariable = 'value'; - const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, model.additionalProperties, inputModel, renderer); - setAdditionalPropertiesMap = `if (instance.${additionalPropertyName} === undefined) {instance.${additionalPropertyName} = new Map();}`; - unmarshalAdditionalProperties = `instance.${additionalPropertyName}.set(key, ${unmarshalCode});`; + const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, model); + setAdditionalPropertiesMap.push( + `if (instance.${prop} === undefined) {instance.${prop} = new Map();}` + ); + unmarshalAdditionalProperties.push( + `instance.${prop}.set(key, ${unmarshalCode});` + ); } - return { unmarshalAdditionalProperties, setAdditionalPropertiesMap }; + const propertyNames = Object.keys(model.properties).map( + (prop) => `"${prop}"` + ); + return ` +//Not part of core properties +${setAdditionalPropertiesMap.join('\n')} +//Only go over remaining. properties +for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![${propertyNames}].includes(key);}))) { +${renderer.indent(unmarshalAdditionalProperties.join('\n'), 2)} +}`; } /** * Render `unmarshal` function based on model */ -function renderUnmarshal({ renderer, model, inputModel }: { - renderer: JavaScriptRenderer, - model: CommonModel, - inputModel: CommonInputModel +function renderUnmarshal({ + renderer, + model +}: { + renderer: JavaScriptRenderer; + model: ConstrainedObjectModel; }): string { - const properties = model.properties || {}; - const { unmarshalPatternProperties, setPatternPropertiesMap } = renderUnmarshalPatternProperties(model, renderer, inputModel); - const { unmarshalAdditionalProperties, setAdditionalPropertiesMap } = renderUnmarshalAdditionalProperties(model, renderer, inputModel); - const unmarshalProperties = renderUnmarshalProperties(model, renderer, inputModel); - const formattedModelName = renderer.nameType(model.$id); - const propertyNames = Object.keys(properties).map((prop => `"${prop}"`)); + const unmarshalProperties = renderUnmarshalProperties(model); + const unmarshalUnwrapProperties = renderUnmarshalUnwrapProperties( + model, + renderer + ); return `unmarshal(json){ const obj = typeof json === "object" ? json : JSON.parse(json); - const instance = new ${formattedModelName}({}); + const instance = new ${model.name}({}); ${renderer.indent(unmarshalProperties)} - //Not part of core properties - ${setPatternPropertiesMap} - ${setAdditionalPropertiesMap} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![${propertyNames}].includes(key);}))) { -${renderer.indent(unmarshalPatternProperties, 4)} -${renderer.indent(unmarshalAdditionalProperties, 4)} - } +${renderer.indent(unmarshalUnwrapProperties)} + return instance; }`; } /** - * Preset which adds `marshal`, `unmarshal` functions to class. - * + * Preset which adds `marshal`, `unmarshal` functions to class. + * * @implements {JavaScriptPreset} */ -export const JS_COMMON_PRESET: JavaScriptPreset = { - class: { - additionalContent({ renderer, model, content, options, inputModel }) { - options = options || {}; - const blocks: string[] = []; +export const JS_COMMON_PRESET: JavaScriptPreset = + { + class: { + additionalContent({ renderer, model, content, options }) { + options = options || {}; + const blocks: string[] = []; - if (options.marshalling === true) { - blocks.push(renderMarshal({ renderer, model, inputModel })); - blocks.push(renderUnmarshal({ renderer, model, inputModel })); + if (options.marshalling === true) { + blocks.push(renderMarshal({ renderer, model })); + blocks.push(renderUnmarshal({ renderer, model })); + } + if (options.example === true) { + blocks.push(renderExampleFunction({ model })); + } + return renderer.renderBlock([content, ...blocks], 2); } - if (options.example === true) { - blocks.push(renderExampleFunction({ renderer, model })); - } - return renderer.renderBlock([content, ...blocks], 2); - }, - } -}; + } + }; diff --git a/src/generators/javascript/presets/utils/ExampleFunction.ts b/src/generators/javascript/presets/utils/ExampleFunction.ts index 9510ce7bb0..776aee58a3 100644 --- a/src/generators/javascript/presets/utils/ExampleFunction.ts +++ b/src/generators/javascript/presets/utils/ExampleFunction.ts @@ -1,66 +1,68 @@ -import { JavaScriptRenderer } from '../../JavaScriptRenderer'; -import { CommonModel } from '../../../../models'; +import { + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedUnionModel +} from '../../../../models'; -export function renderValueFromModel(model: CommonModel, renderer: JavaScriptRenderer): string | undefined { - if (model.$ref !== undefined) { - return `${renderer.nameType(model.$ref)}.example()`; - } - if (Array.isArray(model.type)) { - if (model.type.length > 0) { - return renderValueFromType(model.type[0], model, renderer); - } - return undefined; - } - return renderValueFromType(model.type, model, renderer); -} - -export function renderValueFromType(modelType: string | undefined, model: CommonModel, renderer: JavaScriptRenderer): string | undefined { - if (modelType === undefined) { - return undefined; - } - switch (modelType) { - case 'string': +/** + * Render specific example values + * @param model + */ +export function renderValueFromModel( + model: ConstrainedMetaModel +): string | undefined { + if (model instanceof ConstrainedReferenceModel) { + return `${model.ref.type}.example()`; + } else if (model instanceof ConstrainedUnionModel) { + //Greedy example, where we just use the first type of the union models + return renderValueFromModel(model.union[0]); + } else if (model instanceof ConstrainedStringModel) { return '"string"'; - case 'integer': - case 'number': + } else if ( + model instanceof ConstrainedFloatModel || + model instanceof ConstrainedIntegerModel + ) { return '0'; - case 'boolean': + } else if (model instanceof ConstrainedBooleanModel) { return 'true'; - case 'array': { - if (model.items === undefined) { - return '[]'; - } - if (Array.isArray(model.items)) { - const arrayValues = model.items.map((item) => { - return renderValueFromModel(item, renderer); - }); - return `[${arrayValues.join(', ')}]`; - } - const arrayType = renderValueFromModel(model.items, renderer); - return `[${arrayType}]`; - } + } else if (model instanceof ConstrainedArrayModel) { + const value = renderValueFromModel(model.valueModel); + return `[${value}]`; + } else if (model instanceof ConstrainedTupleModel) { + const values = model.tuple.map((tupleModel) => + renderValueFromModel(tupleModel.value) + ); + return `[${values.join(', ')}]`; } return undefined; } -export default function renderExampleFunction({ renderer, model }: { - renderer: JavaScriptRenderer, - model: CommonModel +export default function renderExampleFunction({ + model +}: { + model: ConstrainedObjectModel; }): string { const properties = model.properties || {}; const setProperties = []; for (const [propertyName, property] of Object.entries(properties)) { - const formattedPropertyName = renderer.nameProperty(propertyName, property); - const potentialRenderedValue = renderValueFromModel(property, renderer); + const potentialRenderedValue = renderValueFromModel(property.property); if (potentialRenderedValue === undefined) { continue; } - setProperties.push(` instance.${formattedPropertyName} = ${potentialRenderedValue};`); + setProperties.push( + ` instance.${propertyName} = ${potentialRenderedValue};` + ); } - const formattedModelName = renderer.nameType(model.$id); return `example(){ - const instance = new ${formattedModelName}({}); -${(setProperties.join('\n'))} + const instance = new ${model.name}({}); +${setProperties.join('\n')} return instance; }`; } diff --git a/src/generators/javascript/renderers/ClassRenderer.ts b/src/generators/javascript/renderers/ClassRenderer.ts index e3a7e6249a..616f392492 100644 --- a/src/generators/javascript/renderers/ClassRenderer.ts +++ b/src/generators/javascript/renderers/ClassRenderer.ts @@ -1,14 +1,17 @@ import { JavaScriptRenderer } from '../JavaScriptRenderer'; - -import { CommonModel, ClassPreset, PropertyType } from '../../../models'; -import { DefaultPropertyNames, FormatHelpers, getUniquePropertyName } from '../../../helpers'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel +} from '../../../models'; +import { JavaScriptOptions } from '../JavaScriptGenerator'; +import { ClassPresetType } from '../JavaScriptPreset'; /** * Renderer for JavaScript's `class` type - * + * * @extends JavaScriptRenderer */ -export class ClassRenderer extends JavaScriptRenderer { +export class ClassRenderer extends JavaScriptRenderer { public async defaultSelf(): Promise { const content = [ await this.renderProperties(), @@ -16,9 +19,8 @@ export class ClassRenderer extends JavaScriptRenderer { await this.renderAccessors(), await this.runAdditionalContentPreset() ]; - - const formattedName = this.model.$id && FormatHelpers.toPascalCase(this.model.$id); - return `class ${formattedName} { + + return `class ${this.model.name} { ${this.indent(this.renderBlock(content, 2))} }`; } @@ -31,72 +33,69 @@ ${this.indent(this.renderBlock(content, 2))} const properties = this.model.properties || {}; const content: string[] = []; - for (const [propertyName, property] of Object.entries(properties)) { - const getter = await this.runGetterPreset(propertyName, property); - const setter = await this.runSetterPreset(propertyName, property); + for (const property of Object.values(properties)) { + const getter = await this.runGetterPreset(property); + const setter = await this.runSetterPreset(property); content.push(this.renderBlock([getter, setter])); } - if (this.model.additionalProperties !== undefined) { - const propertyName = getUniquePropertyName(this.model, DefaultPropertyNames.additionalProperties); - const getter = await this.runGetterPreset(propertyName, this.model.additionalProperties, PropertyType.additionalProperty); - const setter = await this.runSetterPreset(propertyName, this.model.additionalProperties, PropertyType.additionalProperty); - content.push(this.renderBlock([getter, setter])); - } + return this.renderBlock(content, 2); + } - if (this.model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(this.model.patternProperties)) { - const propertyName = getUniquePropertyName(this.model, `${pattern}${DefaultPropertyNames.patternProperties}`); - const getter = await this.runGetterPreset(propertyName, patternModel, PropertyType.patternProperties); - const setter = await this.runSetterPreset(propertyName, patternModel, PropertyType.patternProperties); - content.push(this.renderBlock([getter, setter])); - } - } + runGetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('getter', { property }); + } - return this.renderBlock(content, 2); + runSetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('setter', { property }); } - runGetterPreset(propertyName: string, property: CommonModel, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('getter', { propertyName, property, type }); + async renderProperties(): Promise { + const properties = this.model.properties || {}; + const content: string[] = []; + + for (const property of Object.values(properties)) { + const rendererProperty = await this.runPropertyPreset(property); + content.push(rendererProperty); + } + + return this.renderBlock(content); } - runSetterPreset(propertyName: string, property: CommonModel, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('setter', { propertyName, property, type }); + runPropertyPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('property', { property }); } } -export const JS_DEFAULT_CLASS_PRESET: ClassPreset = { +export const JS_DEFAULT_CLASS_PRESET: ClassPresetType = { self({ renderer }) { return renderer.defaultSelf(); }, ctor({ renderer, model }) { const properties = model.properties || {}; - const assigments = Object.entries(properties).map(([propertyName, property]) => { - if (!model.isRequired(propertyName)) { - propertyName = renderer.nameProperty(propertyName, property); - return `if (input.hasOwnProperty('${propertyName}')) { + const assignments = Object.entries(properties).map( + ([propertyName, property]) => { + if (!property.required) { + return `if (input.hasOwnProperty('${propertyName}')) { this.${propertyName} = input.${propertyName}; }`; + } + return `this.${propertyName} = input.${propertyName};`; } - propertyName = renderer.nameProperty(propertyName, property); - return `this.${propertyName} = input.${propertyName};`; - }); - const body = renderer.renderBlock(assigments); + ); + const body = renderer.renderBlock(assignments); return `constructor(input) { ${renderer.indent(body)} }`; }, - property({ renderer, propertyName, property }) { - propertyName = renderer.nameProperty(propertyName, property); - return `${propertyName};`; + property({ property }) { + return `${property.propertyName};`; }, - getter({ renderer, propertyName, property }) { - propertyName = renderer.nameProperty(propertyName, property); - return `get ${propertyName}() { return this.${propertyName}; }`; - }, - setter({ renderer, propertyName, property }) { - propertyName = renderer.nameProperty(propertyName, property); - return `set ${propertyName}(${propertyName}) { this.${propertyName} = ${propertyName}; }`; + getter({ property }) { + return `get ${property.propertyName}() { return this.${property.propertyName}; }`; }, + setter({ property }) { + return `set ${property.propertyName}(${property.propertyName}) { this.${property.propertyName} = ${property.propertyName}; }`; + } }; diff --git a/src/generators/kotlin/Constants.ts b/src/generators/kotlin/Constants.ts new file mode 100644 index 0000000000..31f0f34cb7 --- /dev/null +++ b/src/generators/kotlin/Constants.ts @@ -0,0 +1,57 @@ +/** + * For the full list of Kotlin keywords, refer to the reference documentation. + * https://kotlinlang.org/docs/keyword-reference.html + */ + +import { checkForReservedKeyword } from '../../helpers'; + +export const RESERVED_KEYWORDS_ILLEGAL_AS_PARAMETER = [ + 'as', + 'as?', + 'break', + 'class', + 'continue', + 'do', + 'else', + 'false', + 'for', + 'fun', + 'if', + 'in', + '!in', + 'interface', + 'is', + '!is', + 'null', + 'object', + 'package', + 'return', + 'super', + 'this', + 'throw', + 'true', + 'try', + 'typealias', + 'typeof', + 'val', + 'var', + 'when', + 'while' +]; + +export const ILLEGAL_ENUM_FIELDS = ['as?', '!in', '!is']; + +export function isInvalidKotlinEnumKey(word: string): boolean { + return checkForReservedKeyword(word, ILLEGAL_ENUM_FIELDS, true); +} + +export function isReservedKotlinKeyword( + word: string, + forceLowerCase = true +): boolean { + return checkForReservedKeyword( + word, + RESERVED_KEYWORDS_ILLEGAL_AS_PARAMETER, + forceLowerCase + ); +} diff --git a/src/generators/kotlin/KotlinConstrainer.ts b/src/generators/kotlin/KotlinConstrainer.ts new file mode 100644 index 0000000000..b666471f55 --- /dev/null +++ b/src/generators/kotlin/KotlinConstrainer.ts @@ -0,0 +1,162 @@ +import { ConstrainedEnumValueModel } from '../../models'; +import { + defaultEnumKeyConstraints, + defaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; +import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; +import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; +import { KotlinTypeMapping } from './KotlinGenerator'; + +function enumFormatToNumberType( + enumValueModel: ConstrainedEnumValueModel, + format: string +): string { + switch (format) { + case 'integer': + case 'int32': + return 'Int'; + case 'long': + case 'int64': + return 'Long'; + case 'float': + return 'Float'; + case 'double': + return 'Double'; + default: + return Number.isInteger(enumValueModel.value) ? 'Int' : 'Double'; + } +} + +function fromEnumValueToKotlinType( + enumValueModel: ConstrainedEnumValueModel, + format: string +): string { + switch (typeof enumValueModel.value) { + case 'boolean': + return 'Boolean'; + case 'number': + case 'bigint': + return enumFormatToNumberType(enumValueModel, format); + case 'object': + return 'Any'; + case 'string': + return 'String'; + default: + return 'Any'; + } +} + +/** + * Converts union of different number types to the most strict type it can be. + * + * int + double = double (long + double, float + double can never happen, otherwise this would be converted to double) + * int + float = float (long + float can never happen, otherwise this would be the case as well) + * int + long = long + * + * Basically a copy from JavaConstrainer.ts + */ +function interpretUnionValueType(types: string[]): string { + if (types.includes('Double')) { + return 'Double'; + } + + if (types.includes('Float')) { + return 'Float'; + } + + if (types.includes('Long')) { + return 'Long'; + } + + return 'Any'; +} + +export const KotlinDefaultTypeMapping: KotlinTypeMapping = { + Object({ constrainedModel }): string { + return constrainedModel.name; + }, + Reference({ constrainedModel }): string { + return constrainedModel.name; + }, + Any(): string { + return 'Any'; + }, + Float({ constrainedModel }): string { + const format = + constrainedModel.originalInput && + constrainedModel.originalInput['format']; + return format === 'float' ? 'Float' : 'Double'; + }, + Integer({ constrainedModel }): string { + const format = + constrainedModel.originalInput && + constrainedModel.originalInput['format']; + return format === 'long' || format === 'int64' ? 'Long' : 'Int'; + }, + String({ constrainedModel }): string { + const format = + constrainedModel.originalInput && + constrainedModel.originalInput['format']; + switch (format) { + case 'date': { + return 'java.time.LocalDate'; + } + case 'time': { + return 'java.time.OffsetTime'; + } + case 'dateTime': + case 'date-time': { + return 'java.time.OffsetDateTime'; + } + case 'binary': { + return 'ByteArray'; + } + default: { + return 'String'; + } + } + }, + Boolean(): string { + return 'Boolean'; + }, + // Since there are not tuples in Kotlin, we have to return a collection of `Any` + Tuple({ options }): string { + const isList = options.collectionType && options.collectionType === 'List'; + + return isList ? 'List' : 'Array'; + }, + Array({ constrainedModel, options }): string { + const isList = options.collectionType && options.collectionType === 'List'; + const type = constrainedModel.valueModel.type; + + return isList ? `List<${type}>` : `Array<${type}>`; + }, + Enum({ constrainedModel }): string { + const format = + constrainedModel.originalInput && + constrainedModel.originalInput['format']; + const valueTypes = constrainedModel.values.map((enumValue) => + fromEnumValueToKotlinType(enumValue, format) + ); + const uniqueTypes = [...new Set(valueTypes)]; + + // Enums cannot handle union types, default to a loose type + return uniqueTypes.length > 1 + ? interpretUnionValueType(uniqueTypes) + : uniqueTypes[0]; + }, + Union(): string { + // No Unions in Kotlin, use Any for now. + return 'Any'; + }, + Dictionary({ constrainedModel }): string { + return `Map<${constrainedModel.key.type}, ${constrainedModel.value.type}>`; + } +}; + +export const KotlinDefaultConstraints = { + enumKey: defaultEnumKeyConstraints(), + enumValue: defaultEnumValueConstraints(), + modelName: defaultModelNameConstraints(), + propertyKey: defaultPropertyKeyConstraints() +}; diff --git a/src/generators/kotlin/KotlinDependencyManager.ts b/src/generators/kotlin/KotlinDependencyManager.ts new file mode 100644 index 0000000000..b7a516281f --- /dev/null +++ b/src/generators/kotlin/KotlinDependencyManager.ts @@ -0,0 +1,17 @@ +import { AbstractDependencyManager } from '../AbstractDependencyManager'; +import { KotlinOptions } from './KotlinGenerator'; + +export class KotlinDependencyManager extends AbstractDependencyManager { + constructor(public options: KotlinOptions, dependencies: string[] = []) { + super(dependencies); + } + + /** + * Adds a dependency package ensuring correct syntax. + * + * @param dependencyPackage package to import, for example `javax.validation.constraints.*` + */ + addDependency(dependencyPackage: string): void { + super.addDependency(`import ${dependencyPackage}`); + } +} diff --git a/src/generators/kotlin/KotlinFileGenerator.ts b/src/generators/kotlin/KotlinFileGenerator.ts new file mode 100644 index 0000000000..e0686ecae9 --- /dev/null +++ b/src/generators/kotlin/KotlinFileGenerator.ts @@ -0,0 +1,43 @@ +import { KotlinGenerator, KotlinRenderCompleteModelOptions } from '.'; +import { InputMetaModel, OutputModel } from '../../models'; +import * as path from 'path'; +import { AbstractFileGenerator } from '../AbstractFileGenerator'; +import { FileHelpers } from '../../helpers'; + +export class KotlinFileGenerator + extends KotlinGenerator + implements AbstractFileGenerator +{ + /** + * Generates all the models to an output directory each model with their own separate files. + * + * @param input + * @param outputDirectory where you want the models generated to + * @param options + * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. + */ + public async generateToFiles( + input: Record | InputMetaModel, + outputDirectory: string, + options: KotlinRenderCompleteModelOptions, + ensureFilesWritten = false + ): Promise { + let generatedModels = await this.generateCompleteModels(input, options); + //Filter anything out that have not been successfully generated + generatedModels = generatedModels.filter((outputModel) => { + return outputModel.modelName !== ''; + }); + for (const outputModel of generatedModels) { + const filePath = path.resolve( + outputDirectory, + `${outputModel.modelName}.kt` + ); + await FileHelpers.writerToFileSystem( + outputModel.result, + filePath, + ensureFilesWritten + ); + } + return generatedModels; + } +} diff --git a/src/generators/kotlin/KotlinGenerator.ts b/src/generators/kotlin/KotlinGenerator.ts new file mode 100644 index 0000000000..1f647bffd9 --- /dev/null +++ b/src/generators/kotlin/KotlinGenerator.ts @@ -0,0 +1,249 @@ +import { + AbstractGenerator, + CommonGeneratorOptions, + defaultGeneratorOptions +} from '../AbstractGenerator'; +import { + ConstrainedEnumModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + InputMetaModel, + MetaModel, + RenderOutput +} from '../../models'; +import { IndentationTypes, split, TypeMapping } from '../../helpers'; +import { KotlinPreset, KOTLIN_DEFAULT_PRESET } from './KotlinPreset'; +import { ClassRenderer } from './renderers/ClassRenderer'; +import { EnumRenderer } from './renderers/EnumRenderer'; +import { isReservedKotlinKeyword } from './Constants'; +import { Logger } from '../..'; +import { + constrainMetaModel, + Constraints +} from '../../helpers/ConstrainHelpers'; +import { + KotlinDefaultConstraints, + KotlinDefaultTypeMapping +} from './KotlinConstrainer'; +import { DeepPartial, mergePartialAndDefault } from '../../utils/Partials'; +import { KotlinDependencyManager } from './KotlinDependencyManager'; + +export interface KotlinOptions extends CommonGeneratorOptions { + typeMapping: TypeMapping; + constraints: Constraints; + collectionType: 'List' | 'Array'; +} +export type KotlinTypeMapping = TypeMapping< + KotlinOptions, + KotlinDependencyManager +>; +export interface KotlinRenderCompleteModelOptions { + packageName: string; +} +export class KotlinGenerator extends AbstractGenerator< + KotlinOptions, + KotlinRenderCompleteModelOptions +> { + static defaultOptions: KotlinOptions = { + ...defaultGeneratorOptions, + indentation: { + type: IndentationTypes.SPACES, + size: 4 + }, + defaultPreset: KOTLIN_DEFAULT_PRESET, + collectionType: 'List', + typeMapping: KotlinDefaultTypeMapping, + constraints: KotlinDefaultConstraints + }; + + constructor(options?: DeepPartial) { + const realizedOptions = KotlinGenerator.getKotlinOptions(options); + super('Kotlin', realizedOptions); + } + + /** + * Returns the Kotlin options by merging custom options with default ones. + */ + static getKotlinOptions(options?: DeepPartial): KotlinOptions { + const optionsToUse = mergePartialAndDefault( + KotlinGenerator.defaultOptions, + options + ) as KotlinOptions; + //Always overwrite the dependency manager unless user explicitly state they want it (ignore default temporary dependency manager) + if (options?.dependencyManager === undefined) { + optionsToUse.dependencyManager = () => { + return new KotlinDependencyManager(optionsToUse); + }; + } + return optionsToUse; + } + + /** + * Wrapper to get an instance of the dependency manager + */ + getDependencyManager(options: KotlinOptions): KotlinDependencyManager { + return this.getDependencyManagerInstance( + options + ) as KotlinDependencyManager; + } + + /** + * This function makes sure we split up the MetaModels accordingly to what we want to render as models. + */ + splitMetaModel(model: MetaModel): MetaModel[] { + const metaModelsToSplit = { + splitEnum: true, + splitObject: true + }; + return split(model, metaModelsToSplit); + } + + constrainToMetaModel( + model: MetaModel, + options: DeepPartial + ): ConstrainedMetaModel { + const optionsToUse = KotlinGenerator.getKotlinOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + return constrainMetaModel( + optionsToUse.typeMapping, + optionsToUse.constraints, + { + metaModel: model, + dependencyManager: dependencyManagerToUse, + options: optionsToUse, + constrainedName: '' //This is just a placeholder, it will be constrained within the function + } + ); + } + + /** + * Render a scattered model, where the source code and library and model dependencies are separated. + * + * @param model + * @param inputModel + */ + render( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = KotlinGenerator.getKotlinOptions({ + ...this.options, + ...options + }); + if (model instanceof ConstrainedObjectModel) { + return this.renderClass(model, inputModel, optionsToUse); + } else if (model instanceof ConstrainedEnumModel) { + return this.renderEnum(model, inputModel, optionsToUse); + } + Logger.warn( + `Kotlin generator, cannot generate this type of model, ${model.name}` + ); + return Promise.resolve( + RenderOutput.toRenderOutput({ + result: '', + renderedName: '', + dependencies: [] + }) + ); + } + + /** + * Render a complete model result where the model code, library and model dependencies are all bundled appropriately. + * + * For Kotlin you need to specify which package the model is placed under. + * + * @param model + * @param inputModel + * @param options used to render the full output + */ + async renderCompleteModel( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options: KotlinRenderCompleteModelOptions + ): Promise { + const optionsToUse = KotlinGenerator.getKotlinOptions({ + ...this.options, + ...options + }); + const outputModel = await this.render(model, inputModel, optionsToUse); + const packageName = this.sanitizePackageName(options.packageName); + const outputContent = `package ${packageName} +${outputModel.dependencies.join('\n')} + +${outputModel.result}`; + return RenderOutput.toRenderOutput({ + result: outputContent, + renderedName: outputModel.renderedName, + dependencies: outputModel.dependencies + }); + } + + private sanitizePackageName(packageName: string): string { + return packageName + .split('.') + .map((subpackage) => + isReservedKotlinKeyword(subpackage, true) + ? `\`${subpackage}\`` + : subpackage + ) + .join('.'); + } + + async renderClass( + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = KotlinGenerator.getKotlinOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('class'); + const renderer = new ClassRenderer( + this.options, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runSelfPreset(); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } + + async renderEnum( + model: ConstrainedEnumModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = KotlinGenerator.getKotlinOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('enum'); + const renderer = new EnumRenderer( + this.options, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runSelfPreset(); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } +} diff --git a/src/generators/kotlin/KotlinPreset.ts b/src/generators/kotlin/KotlinPreset.ts new file mode 100644 index 0000000000..7d6c3c2e97 --- /dev/null +++ b/src/generators/kotlin/KotlinPreset.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/ban-types */ +import { Preset, ClassPreset, EnumPreset } from '../../models'; +import { KotlinOptions } from './KotlinGenerator'; +import { + ClassRenderer, + KOTLIN_DEFAULT_CLASS_PRESET +} from './renderers/ClassRenderer'; +import { + EnumRenderer, + KOTLIN_DEFAULT_ENUM_PRESET +} from './renderers/EnumRenderer'; + +export type ClassPresetType = ClassPreset; +export type EnumPresetType = EnumPreset; + +export type KotlinPreset = Preset<{ + class: ClassPresetType; + enum: EnumPresetType; +}>; + +export const KOTLIN_DEFAULT_PRESET: KotlinPreset = { + class: KOTLIN_DEFAULT_CLASS_PRESET, + enum: KOTLIN_DEFAULT_ENUM_PRESET +}; diff --git a/src/generators/kotlin/KotlinRenderer.ts b/src/generators/kotlin/KotlinRenderer.ts new file mode 100644 index 0000000000..f829941c4a --- /dev/null +++ b/src/generators/kotlin/KotlinRenderer.ts @@ -0,0 +1,72 @@ +import { AbstractRenderer } from '../AbstractRenderer'; +import { KotlinGenerator, KotlinOptions } from './KotlinGenerator'; +import { ConstrainedMetaModel, InputMetaModel, Preset } from '../../models'; +import { FormatHelpers } from '../../helpers'; +import { KotlinDependencyManager } from './KotlinDependencyManager'; + +/** + * Common renderer for Kotlin + * + * @extends AbstractRenderer + */ +export abstract class KotlinRenderer< + RendererModelType extends ConstrainedMetaModel +> extends AbstractRenderer { + constructor( + options: KotlinOptions, + generator: KotlinGenerator, + presets: Array<[Preset, unknown]>, + model: RendererModelType, + inputModel: InputMetaModel, + public dependencyManager: KotlinDependencyManager + ) { + super(options, generator, presets, model, inputModel); + } + + renderComments(lines: string | string[]): string { + lines = FormatHelpers.breakLines(lines); + const newLiteral = lines.map((line) => ` * ${line}`).join('\n'); + return `/** +${newLiteral} + */`; + } + + renderAnnotation( + annotationName: string, + value?: any | Record, + prefix?: 'field:' | 'get:' | 'param:' + ): string { + const name = `@${!prefix ? '' : prefix}${FormatHelpers.upperFirst( + annotationName + )}`; + + if (value === undefined || value === null) { + return name; + } + + if (typeof value !== 'object') { + return `${name}(${value})`; + } + + const entries = Object.entries(value || {}); + + if (entries.length === 0) { + return name; + } + + const values = concatenateEntries(entries); + return `${name}(${values})`; + } +} + +function concatenateEntries(entries: [string, unknown][] = []): string { + return entries + .map(([paramName, newValue]) => { + if (paramName && newValue !== undefined) { + return `${paramName}=${newValue}`; + } + return newValue; + }) + .filter((v) => v !== undefined) + .join(', '); +} diff --git a/src/generators/kotlin/constrainer/EnumConstrainer.ts b/src/generators/kotlin/constrainer/EnumConstrainer.ts new file mode 100644 index 0000000000..62bad7b4b3 --- /dev/null +++ b/src/generators/kotlin/constrainer/EnumConstrainer.ts @@ -0,0 +1,85 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { ConstrainedEnumModel, EnumModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS, + FormatHelpers, + EnumKeyConstraint, + EnumValueConstraint +} from '../../../helpers'; +import { isInvalidKotlinEnumKey } from '../Constants'; + +export type ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_KEYS: ( + constrainedEnumModel: ConstrainedEnumModel, + enumModel: EnumModel, + value: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude '_' because they are allowed as enum keys + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_KEYS: NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toConstantCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isInvalidKotlinEnumKey); + } +}; + +export function defaultEnumKeyConstraints( + customConstraints?: Partial +): EnumKeyConstraint { + const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; + + return ({ enumKey, enumModel, constrainedEnumModel }) => { + let constrainedEnumKey = enumKey; + constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey); + constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey); + //If the enum key has been manipulated, lets make sure it don't clash with existing keys + if (constrainedEnumKey !== enumKey) { + constrainedEnumKey = constraints.NO_DUPLICATE_KEYS( + constrainedEnumModel, + enumModel, + constrainedEnumKey, + constraints.NAMING_FORMATTER + ); + } + constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey); + return constrainedEnumKey; + }; +} + +export function defaultEnumValueConstraints(): EnumValueConstraint { + return ({ enumValue }) => { + switch (typeof enumValue) { + case 'string': + return `"${enumValue}"`; + case 'boolean': + case 'bigint': + case 'number': + return enumValue; + case 'object': + return `"${JSON.stringify(enumValue).replace(/"/g, '\\"')}"`; + default: + return `"${JSON.stringify(enumValue)}"`; + } + }; +} diff --git a/src/generators/kotlin/constrainer/ModelNameConstrainer.ts b/src/generators/kotlin/constrainer/ModelNameConstrainer.ts new file mode 100644 index 0000000000..6477940ae5 --- /dev/null +++ b/src/generators/kotlin/constrainer/ModelNameConstrainer.ts @@ -0,0 +1,50 @@ +import { + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { isReservedKotlinKeyword } from '../Constants'; + +export type ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultModelNameConstraints: ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NAMING_FORMATTER: (value: string) => { + return FormatHelpers.toPascalCase(value); + }, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedKotlinKeyword); + } +}; + +export function defaultModelNameConstraints( + customConstraints?: Partial +): ModelNameConstraint { + const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; + + return ({ modelName }) => { + let constrainedValue = modelName; + constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue); + constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue); + constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue); + constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue); + constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); + return constrainedValue; + }; +} diff --git a/src/generators/kotlin/constrainer/PropertyKeyConstrainer.ts b/src/generators/kotlin/constrainer/PropertyKeyConstrainer.ts new file mode 100644 index 0000000000..6ae658ff4c --- /dev/null +++ b/src/generators/kotlin/constrainer/PropertyKeyConstrainer.ts @@ -0,0 +1,78 @@ +import { ConstrainedObjectModel, ObjectModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { isReservedKotlinKeyword } from '../Constants'; + +export type PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_PROPERTIES: ( + constrainedObjectModel: ConstrainedObjectModel, + objectModel: ObjectModel, + propertyName: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toCamelCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedKotlinKeyword); + } +}; + +export function defaultPropertyKeyConstraints( + customConstraints?: Partial +): PropertyKeyConstraint { + const constraints = { + ...DefaultPropertyKeyConstraints, + ...customConstraints + }; + + return ({ objectPropertyModel, constrainedObjectModel, objectModel }) => { + let constrainedPropertyKey = objectPropertyModel.propertyName; + + constrainedPropertyKey = constraints.NO_SPECIAL_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey); + constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS( + constrainedPropertyKey + ); + //If the property name has been manipulated, lets make sure it don't clash with existing properties + if (constrainedPropertyKey !== objectPropertyModel.propertyName) { + constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES( + constrainedObjectModel, + objectModel, + constrainedPropertyKey, + constraints.NAMING_FORMATTER + ); + } + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + return constrainedPropertyKey; + }; +} diff --git a/src/generators/kotlin/index.ts b/src/generators/kotlin/index.ts new file mode 100644 index 0000000000..71f671d488 --- /dev/null +++ b/src/generators/kotlin/index.ts @@ -0,0 +1,21 @@ +export * from './KotlinGenerator'; +export * from './KotlinFileGenerator'; +export { KOTLIN_DEFAULT_PRESET } from './KotlinPreset'; +export type { KotlinPreset } from './KotlinPreset'; +export * from './presets'; + +export { + defaultEnumKeyConstraints as kotlinDefaultEnumKeyConstraints, + DefaultEnumKeyConstraints as KotlinDefaultEnumKeyConstraints, + defaultEnumValueConstraints as kotlinDefaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; + +export { + DefaultModelNameConstraints as KotlinDefaultModelNameConstraints, + defaultModelNameConstraints as kotlinDefaultModelNameConstraints +} from './constrainer/ModelNameConstrainer'; + +export { + DefaultPropertyKeyConstraints as KotlinDefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints as kotlinDefaultPropertyKeyConstraints +} from './constrainer/PropertyKeyConstrainer'; diff --git a/src/generators/kotlin/presets/ConstraintsPreset.ts b/src/generators/kotlin/presets/ConstraintsPreset.ts new file mode 100644 index 0000000000..3ba7508cc3 --- /dev/null +++ b/src/generators/kotlin/presets/ConstraintsPreset.ts @@ -0,0 +1,149 @@ +import { + ConstrainedArrayModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedMetaModel, + ConstrainedStringModel +} from '../../../models'; +import { KotlinPreset } from '../KotlinPreset'; +import { ClassRenderer } from '../renderers/ClassRenderer'; + +export const KOTLIN_CONSTRAINTS_PRESET: KotlinPreset = { + class: { + self({ renderer, content }) { + renderer.dependencyManager.addDependency( + 'javax.validation.constraints.*' + ); + return content; + }, + property({ renderer, property, content }) { + const annotations: string[] = []; + + if (property.required) { + annotations.push(renderer.renderAnnotation('NotNull', null, 'get:')); + } + + annotations.push( + ...getTypeSpecificAnnotations(property.property, renderer) + ); + + return renderer.renderBlock([...annotations, content]); + } + } +}; + +function getTypeSpecificAnnotations( + property: ConstrainedMetaModel, + renderer: ClassRenderer +): string[] { + if (property instanceof ConstrainedStringModel) { + return getStringAnnotations(property, renderer); + } else if ( + property instanceof ConstrainedFloatModel || + property instanceof ConstrainedIntegerModel + ) { + return getNumericAnnotations(property, renderer); + } else if (property instanceof ConstrainedArrayModel) { + return getArrayAnnotations(property, renderer); + } + + return []; +} + +function getStringAnnotations( + property: ConstrainedStringModel, + renderer: ClassRenderer +): string[] { + const annotations: string[] = []; + const originalInput = property.originalInput; + + if (originalInput['pattern'] !== undefined) { + annotations.push( + renderer.renderAnnotation( + 'Pattern', + { regexp: `"${originalInput['pattern']}"` }, + 'get:' + ) + ); + } + + if ( + originalInput['minLength'] !== undefined || + originalInput['maxLength'] !== undefined + ) { + annotations.push( + renderer.renderAnnotation( + 'Size', + { min: originalInput['minLength'], max: originalInput['maxLength'] }, + 'get:' + ) + ); + } + + return annotations; +} + +function getNumericAnnotations( + property: ConstrainedIntegerModel | ConstrainedFloatModel, + renderer: ClassRenderer +): string[] { + const annotations: string[] = []; + const originalInput = property.originalInput; + + if (originalInput['minimum'] !== undefined) { + annotations.push( + renderer.renderAnnotation('Min', originalInput['minimum'], 'get:') + ); + } + + if (originalInput['exclusiveMinimum'] !== undefined) { + annotations.push( + renderer.renderAnnotation( + 'Min', + originalInput['exclusiveMinimum'] + 1, + 'get:' + ) + ); + } + + if (originalInput['maximum'] !== undefined) { + annotations.push( + renderer.renderAnnotation('Max', originalInput['maximum'], 'get:') + ); + } + + if (originalInput['exclusiveMaximum'] !== undefined) { + annotations.push( + renderer.renderAnnotation( + 'Max', + originalInput['exclusiveMaximum'] - 1, + 'get:' + ) + ); + } + + return annotations; +} + +function getArrayAnnotations( + property: ConstrainedArrayModel, + renderer: ClassRenderer +): string[] { + const annotations: string[] = []; + const originalInput = property.originalInput; + + if ( + originalInput['minItems'] !== undefined || + originalInput['maxItems'] !== undefined + ) { + annotations.push( + renderer.renderAnnotation( + 'Size', + { min: originalInput['minItems'], max: originalInput['maxItems'] }, + 'get:' + ) + ); + } + + return annotations; +} diff --git a/src/generators/kotlin/presets/DescriptionPreset.ts b/src/generators/kotlin/presets/DescriptionPreset.ts new file mode 100644 index 0000000000..8c4652fac7 --- /dev/null +++ b/src/generators/kotlin/presets/DescriptionPreset.ts @@ -0,0 +1,63 @@ +import { KotlinRenderer } from '../KotlinRenderer'; +import { KotlinPreset } from '../KotlinPreset'; +import { FormatHelpers } from '../../../helpers'; +import { ConstrainedEnumModel, ConstrainedObjectModel } from '../../../models'; +function renderDescription({ + renderer, + content, + item +}: { + renderer: KotlinRenderer; + content: string; + item: ConstrainedObjectModel | ConstrainedEnumModel; +}): string { + if (!item.originalInput['description']) { + return content; + } + + let comment = `${item.originalInput['description']}`; + + if (item instanceof ConstrainedObjectModel) { + const properties = Object.keys(item.properties) + .map((key) => item.properties[`${key}`]) + .map((model) => { + const property = `@property ${model.propertyName}`; + const desc = model.property.originalInput['description']; + + return desc !== undefined ? `${property} ${desc}` : property; + }) + .join('\n'); + + comment += `\n\n${properties}`; + } + + const examples = Array.isArray(item.originalInput['examples']) + ? `Examples: \n${FormatHelpers.renderJSONExamples( + item.originalInput['examples'] + )}` + : null; + + if (examples !== null) { + comment += `\n\n${examples}`; + } + + return `${renderer.renderComments(comment)}\n${content}`; +} + +/** + * Preset which adds description to rendered model. + * + * @implements {KotlinPreset} + */ +export const KOTLIN_DESCRIPTION_PRESET: KotlinPreset = { + class: { + self({ renderer, model, content }) { + return renderDescription({ renderer, content, item: model }); + } + }, + enum: { + self({ renderer, model, content }) { + return renderDescription({ renderer, content, item: model }); + } + } +}; diff --git a/src/generators/kotlin/presets/index.ts b/src/generators/kotlin/presets/index.ts new file mode 100644 index 0000000000..083899b1d8 --- /dev/null +++ b/src/generators/kotlin/presets/index.ts @@ -0,0 +1,2 @@ +export * from './DescriptionPreset'; +export * from './ConstraintsPreset'; diff --git a/src/generators/kotlin/renderers/ClassRenderer.ts b/src/generators/kotlin/renderers/ClassRenderer.ts new file mode 100644 index 0000000000..b05cbbadfe --- /dev/null +++ b/src/generators/kotlin/renderers/ClassRenderer.ts @@ -0,0 +1,58 @@ +import { KotlinRenderer } from '../KotlinRenderer'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel +} from '../../../models'; +import { KotlinOptions } from '../KotlinGenerator'; +import { ClassPresetType } from '../KotlinPreset'; + +/** + * Renderer for Kotlin's `class` type + * + * @extends KotlinRenderer + */ +export class ClassRenderer extends KotlinRenderer { + async defaultSelf(hasProperties: boolean): Promise { + return hasProperties + ? await this.defaultWithProperties() + : `class ${this.model.name} {}`; + } + + private async defaultWithProperties(): Promise { + const content = [ + await this.renderProperties(), + await this.runAdditionalContentPreset() + ]; + + return `data class ${this.model.name}( +${this.indent(this.renderBlock(content, 2))} +)`; + } + + async renderProperties(): Promise { + const properties = this.model.properties || {}; + const content: string[] = []; + + for (const property of Object.values(properties)) { + const rendererProperty = await this.runPropertyPreset(property); + content.push(rendererProperty); + } + + return this.renderBlock(content); + } + + runPropertyPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('property', { property }); + } +} + +export const KOTLIN_DEFAULT_CLASS_PRESET: ClassPresetType = { + self({ renderer, model }) { + const hasProperties = Object.keys(model.properties).length > 0; + + return renderer.defaultSelf(hasProperties); + }, + property({ property }) { + return `val ${property.propertyName}: ${property.property.type},`; + } +}; diff --git a/src/generators/kotlin/renderers/EnumRenderer.ts b/src/generators/kotlin/renderers/EnumRenderer.ts new file mode 100644 index 0000000000..6f8965ac16 --- /dev/null +++ b/src/generators/kotlin/renderers/EnumRenderer.ts @@ -0,0 +1,55 @@ +import { KotlinRenderer } from '../KotlinRenderer'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../models'; +import { EnumPresetType } from '../KotlinPreset'; +import { KotlinOptions } from '../KotlinGenerator'; + +/** + * Renderer for Kotlin's `enum` type + * + * @extends KotlinRenderer + */ +export class EnumRenderer extends KotlinRenderer { + async defaultSelf(valueType: string): Promise { + const content = [ + await this.renderItems(), + await this.runFromValuePreset(), + await this.runAdditionalContentPreset() + ]; + return `enum class ${this.model.name}(val value: ${valueType}) { +${this.indent(this.renderBlock(content, 2))} +}`; + } + + async renderItems(): Promise { + const enums = this.model.values || []; + const items: string[] = []; + + for (const value of enums) { + const renderedItem = await this.runItemPreset(value); + items.push(renderedItem); + } + + const content = items.join(', \n'); + return `${content};`; + } + + runItemPreset(item: ConstrainedEnumValueModel): Promise { + return this.runPreset('item', { item }); + } + + runFromValuePreset(): Promise { + return this.runPreset('fromValue'); + } +} + +export const KOTLIN_DEFAULT_ENUM_PRESET: EnumPresetType = { + self({ renderer, model }) { + return renderer.defaultSelf(model.type); + }, + item({ item }) { + return `${item.key}(${item.value})`; + } +}; diff --git a/src/generators/python/Constants.ts b/src/generators/python/Constants.ts new file mode 100644 index 0000000000..5ed2602982 --- /dev/null +++ b/src/generators/python/Constants.ts @@ -0,0 +1,49 @@ +import { checkForReservedKeyword } from '../../helpers'; + +export const RESERVED_PYTHON_KEYWORDS = [ + 'False', + 'def', + 'if', + 'raise', + 'None', + 'del', + 'import', + 'return', + 'True', + 'elif', + 'in', + 'try', + 'and', + 'else', + 'is', + 'while', + 'as', + 'except', + 'lambda', + 'with', + 'assert', + 'finally', + 'nonlocal', + 'yield', + 'break', + 'for', + 'not', + 'class', + 'from', + 'or', + 'continue', + 'global', + 'pass', + 'exec' +]; + +export function isReservedPythonKeyword( + word: string, + forceLowerCase = true +): boolean { + return checkForReservedKeyword( + word, + RESERVED_PYTHON_KEYWORDS, + forceLowerCase + ); +} diff --git a/src/generators/python/PythonConstrainer.ts b/src/generators/python/PythonConstrainer.ts new file mode 100644 index 0000000000..d492261221 --- /dev/null +++ b/src/generators/python/PythonConstrainer.ts @@ -0,0 +1,61 @@ +import { + defaultEnumKeyConstraints, + defaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; +import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; +import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; +import { PythonTypeMapping } from './PythonGenerator'; + +export const PythonDefaultTypeMapping: PythonTypeMapping = { + Object({ constrainedModel }): string { + //Returning name here because all object models have been split out + return constrainedModel.name; + }, + Reference({ constrainedModel }): string { + return constrainedModel.name; + }, + Any(): string { + return 'Any'; + }, + Float(): string { + return 'float'; + }, + Integer(): string { + return 'int'; + }, + String(): string { + return 'str'; + }, + Boolean(): string { + return 'bool'; + }, + Tuple({ constrainedModel }): string { + const tupleTypes = constrainedModel.tuple.map((unionModel) => { + return unionModel.value.type; + }); + return `tuple[${tupleTypes.join(', ')}]`; + }, + Array({ constrainedModel }): string { + return `list[${constrainedModel.valueModel.type}]`; + }, + Enum({ constrainedModel }): string { + //Returning name here because all enum models have been split out + return constrainedModel.name; + }, + Union({ constrainedModel }): string { + const unionTypes = constrainedModel.union.map((unionModel) => { + return unionModel.type; + }); + return unionTypes.join(' | '); + }, + Dictionary({ constrainedModel }): string { + return `dict[${constrainedModel.value.type}, ${constrainedModel.value.type}]`; + } +}; + +export const PythonDefaultConstraints = { + enumKey: defaultEnumKeyConstraints(), + enumValue: defaultEnumValueConstraints(), + modelName: defaultModelNameConstraints(), + propertyKey: defaultPropertyKeyConstraints() +}; diff --git a/src/generators/python/PythonDependencyManager.ts b/src/generators/python/PythonDependencyManager.ts new file mode 100644 index 0000000000..f121a8d63b --- /dev/null +++ b/src/generators/python/PythonDependencyManager.ts @@ -0,0 +1,19 @@ +import { ConstrainedMetaModel } from '../../models'; +import { AbstractDependencyManager } from '../AbstractDependencyManager'; +import { PythonOptions } from './PythonGenerator'; + +export class PythonDependencyManager extends AbstractDependencyManager { + constructor(public options: PythonOptions, dependencies: string[] = []) { + super(dependencies); + } + + /** + * Simple helper function to render a dependency based on the module system that the user defines. + */ + renderDependency(model: ConstrainedMetaModel): string { + const useExplicitImports = this.options.importsStyle === 'explicit'; + return `from ${useExplicitImports ? '.' : ''}${model.name} import ${ + model.name + }`; + } +} diff --git a/src/generators/python/PythonFileGenerator.ts b/src/generators/python/PythonFileGenerator.ts new file mode 100644 index 0000000000..79854bec8b --- /dev/null +++ b/src/generators/python/PythonFileGenerator.ts @@ -0,0 +1,43 @@ +import { PythonGenerator, PythonRenderCompleteModelOptions } from '.'; +import { InputMetaModel, OutputModel } from '../../models'; +import * as path from 'path'; +import { AbstractFileGenerator } from '../AbstractFileGenerator'; +import { FileHelpers } from '../../helpers'; + +export class PythonFileGenerator + extends PythonGenerator + implements AbstractFileGenerator +{ + /** + * Generates all the models to an output directory each model with their own separate files. + * + * @param input + * @param outputDirectory where you want the models generated to + * @param options + * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. + */ + public async generateToFiles( + input: Record | InputMetaModel, + outputDirectory: string, + options: PythonRenderCompleteModelOptions, + ensureFilesWritten = false + ): Promise { + let generatedModels = await this.generateCompleteModels(input, options); + //Filter anything out that have not been successfully generated + generatedModels = generatedModels.filter((outputModel) => { + return outputModel.modelName !== ''; + }); + for (const outputModel of generatedModels) { + const filePath = path.resolve( + outputDirectory, + `${outputModel.modelName}.py` + ); + await FileHelpers.writerToFileSystem( + outputModel.result, + filePath, + ensureFilesWritten + ); + } + return generatedModels; + } +} diff --git a/src/generators/python/PythonGenerator.ts b/src/generators/python/PythonGenerator.ts new file mode 100644 index 0000000000..a4d1401d01 --- /dev/null +++ b/src/generators/python/PythonGenerator.ts @@ -0,0 +1,247 @@ +/* eslint-disable no-console */ +import { + AbstractGenerator, + CommonGeneratorOptions, + defaultGeneratorOptions +} from '../AbstractGenerator'; +import { + ConstrainedEnumModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + InputMetaModel, + MetaModel, + RenderOutput +} from '../../models'; +import { split, SplitOptions, TypeMapping } from '../../helpers'; +import { PythonPreset, PYTHON_DEFAULT_PRESET } from './PythonPreset'; +import { ClassRenderer } from './renderers/ClassRenderer'; +import { EnumRenderer } from './renderers/EnumRenderer'; +import { Logger } from '../..'; +import { + constrainMetaModel, + Constraints +} from '../../helpers/ConstrainHelpers'; +import { + PythonDefaultConstraints, + PythonDefaultTypeMapping +} from './PythonConstrainer'; +import { DeepPartial, mergePartialAndDefault } from '../../utils/Partials'; +import { PythonDependencyManager } from './PythonDependencyManager'; + +export interface PythonOptions extends CommonGeneratorOptions { + typeMapping: TypeMapping; + constraints: Constraints; + importsStyle: 'explicit' | 'implicit'; +} +export type PythonTypeMapping = TypeMapping< + PythonOptions, + PythonDependencyManager +>; +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PythonRenderCompleteModelOptions {} + +export class PythonGenerator extends AbstractGenerator< + PythonOptions, + PythonRenderCompleteModelOptions +> { + static defaultOptions: PythonOptions = { + ...defaultGeneratorOptions, + defaultPreset: PYTHON_DEFAULT_PRESET, + typeMapping: PythonDefaultTypeMapping, + constraints: PythonDefaultConstraints, + importsStyle: 'implicit', + // Temporarily set + dependencyManager: () => { + return {} as PythonDependencyManager; + } + }; + + static defaultCompleteModelOptions: PythonRenderCompleteModelOptions = {}; + + constructor(options?: DeepPartial) { + const realizedOptions = PythonGenerator.getPythonOptions(options); + super('Python', realizedOptions); + } + + /** + * Returns the Python options by merging custom options with default ones. + */ + static getPythonOptions(options?: DeepPartial): PythonOptions { + const optionsToUse = mergePartialAndDefault( + PythonGenerator.defaultOptions, + options + ) as PythonOptions; + //Always overwrite the dependency manager unless user explicitly state they want it (ignore default temporary dependency manager) + if (options?.dependencyManager === undefined) { + optionsToUse.dependencyManager = () => { + return new PythonDependencyManager(optionsToUse); + }; + } + return optionsToUse; + } + + /** + * Wrapper to get an instance of the dependency manager + */ + getDependencyManager(options: PythonOptions): PythonDependencyManager { + return this.getDependencyManagerInstance( + options + ) as PythonDependencyManager; + } + + /** + * This function makes sure we split up the MetaModels accordingly to what we want to render as models. + */ + splitMetaModel(model: MetaModel): MetaModel[] { + //These are the models that we have separate renderers for + const metaModelsToSplit: SplitOptions = { + splitEnum: true, + splitObject: true + }; + return split(model, metaModelsToSplit); + } + + constrainToMetaModel( + model: MetaModel, + options: DeepPartial + ): ConstrainedMetaModel { + const optionsToUse = PythonGenerator.getPythonOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + return constrainMetaModel( + this.options.typeMapping, + this.options.constraints, + { + metaModel: model, + dependencyManager: dependencyManagerToUse, + options: { ...this.options }, + constrainedName: '' //This is just a placeholder, it will be constrained within the function + } + ); + } + + /** + * Render a scattered model, where the source code and library and model dependencies are separated. + * + * @param model + * @param inputModel + */ + render( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = PythonGenerator.getPythonOptions({ + ...this.options, + ...options + }); + if (model instanceof ConstrainedObjectModel) { + return this.renderClass(model, inputModel, optionsToUse); + } else if (model instanceof ConstrainedEnumModel) { + return this.renderEnum(model, inputModel, optionsToUse); + } + Logger.warn( + `Python generator, cannot generate this type of model, ${model.name}` + ); + return Promise.resolve( + RenderOutput.toRenderOutput({ + result: '', + renderedName: '', + dependencies: [] + }) + ); + } + + /** + * Render a complete model result where the model code, library and model dependencies are all bundled appropriately. + * + * For Python you need to specify which package the model is placed under. + * + * @param model + * @param inputModel + * @param options used to render the full output + */ + async renderCompleteModel( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + completeModelOptions: Partial, + options: DeepPartial + ): Promise { + //const completeModelOptionsToUse = mergePartialAndDefault(PythonGenerator.defaultCompleteModelOptions, completeModelOptions) as PythonRenderCompleteModelOptions; + const optionsToUse = PythonGenerator.getPythonOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const outputModel = await this.render(model, inputModel, { + dependencyManager: dependencyManagerToUse + }); + const modelDependencies = model.getNearestDependencies().map((model) => { + return dependencyManagerToUse.renderDependency(model); + }); + const outputContent = `${modelDependencies.join('\n')} +${outputModel.dependencies.join('\n')} +${outputModel.result}`; + return RenderOutput.toRenderOutput({ + result: outputContent, + renderedName: outputModel.renderedName, + dependencies: outputModel.dependencies + }); + } + + async renderClass( + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = PythonGenerator.getPythonOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('class'); + const renderer = new ClassRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runSelfPreset(); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } + + async renderEnum( + model: ConstrainedEnumModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = PythonGenerator.getPythonOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('enum'); + const renderer = new EnumRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runSelfPreset(); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } +} diff --git a/src/generators/python/PythonPreset.ts b/src/generators/python/PythonPreset.ts new file mode 100644 index 0000000000..9eaaaf4689 --- /dev/null +++ b/src/generators/python/PythonPreset.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/ban-types */ +import { Preset, ClassPreset, EnumPreset } from '../../models'; +import { PythonOptions } from './PythonGenerator'; +import { + ClassRenderer, + PYTHON_DEFAULT_CLASS_PRESET +} from './renderers/ClassRenderer'; +import { + EnumRenderer, + PYTHON_DEFAULT_ENUM_PRESET +} from './renderers/EnumRenderer'; + +export type ClassPresetType = ClassPreset; +export type EnumPresetType = EnumPreset; + +export type PythonPreset = Preset<{ + class: ClassPresetType; + enum: EnumPresetType; +}>; + +export const PYTHON_DEFAULT_PRESET: PythonPreset = { + class: PYTHON_DEFAULT_CLASS_PRESET, + enum: PYTHON_DEFAULT_ENUM_PRESET +}; diff --git a/src/generators/python/PythonRenderer.ts b/src/generators/python/PythonRenderer.ts new file mode 100644 index 0000000000..1d4a9f7be6 --- /dev/null +++ b/src/generators/python/PythonRenderer.ts @@ -0,0 +1,33 @@ +import { AbstractRenderer } from '../AbstractRenderer'; +import { PythonGenerator, PythonOptions } from './PythonGenerator'; +import { ConstrainedMetaModel, InputMetaModel, Preset } from '../../models'; +import { FormatHelpers } from '../../helpers'; +import { PythonDependencyManager } from './PythonDependencyManager'; + +/** + * Common renderer for Python + * + * @extends AbstractRenderer + */ +export abstract class PythonRenderer< + RendererModelType extends ConstrainedMetaModel +> extends AbstractRenderer { + constructor( + options: PythonOptions, + generator: PythonGenerator, + presets: Array<[Preset, unknown]>, + model: RendererModelType, + inputModel: InputMetaModel, + public dependencyManager: PythonDependencyManager + ) { + super(options, generator, presets, model, inputModel); + } + + renderComments(lines: string | string[]): string { + lines = FormatHelpers.breakLines(lines); + const content = lines.join('\n'); + return `""" +${content} +"""`; + } +} diff --git a/src/generators/python/constrainer/EnumConstrainer.ts b/src/generators/python/constrainer/EnumConstrainer.ts new file mode 100644 index 0000000000..c6ed1b4ef2 --- /dev/null +++ b/src/generators/python/constrainer/EnumConstrainer.ts @@ -0,0 +1,105 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { ConstrainedEnumModel, EnumModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { + FormatHelpers, + EnumKeyConstraint, + EnumValueConstraint +} from '../../../helpers'; +import { isReservedPythonKeyword } from '../Constants'; + +export type ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_KEYS: ( + constrainedEnumModel: ConstrainedEnumModel, + enumModel: EnumModel, + value: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_' because they are allowed as enum keys + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_KEYS: NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toConstantCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedPythonKeyword); + } +}; + +/** + * Default constraint logic for Python, which converts the enum key into a key that is compatible with Python + */ +export function defaultEnumKeyConstraints( + customConstraints?: Partial +): EnumKeyConstraint { + const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; + + return ({ enumKey, enumModel, constrainedEnumModel }) => { + let constrainedEnumKey = enumKey; + constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey); + constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey); + //If the enum key has been manipulated, lets make sure it don't clash with existing keys + if (constrainedEnumKey !== enumKey) { + constrainedEnumKey = constraints.NO_DUPLICATE_KEYS( + constrainedEnumModel, + enumModel, + constrainedEnumKey, + constraints.NAMING_FORMATTER! + ); + } + constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey); + return constrainedEnumKey; + }; +} + +/** + * Convert the enum value to a value that is compatible with Python + */ +export function defaultEnumValueConstraints(): EnumValueConstraint { + return ({ enumValue }) => { + let constrainedEnumValue = enumValue; + switch (typeof enumValue) { + case 'string': + case 'boolean': + constrainedEnumValue = `"${enumValue}"`; + break; + case 'bigint': + case 'number': { + constrainedEnumValue = enumValue; + break; + } + case 'object': { + constrainedEnumValue = `"${JSON.stringify(enumValue).replace( + /"/g, + '\\"' + )}"`; + break; + } + default: { + constrainedEnumValue = `"${JSON.stringify(enumValue)}"`; + } + } + return constrainedEnumValue; + }; +} diff --git a/src/generators/python/constrainer/ModelNameConstrainer.ts b/src/generators/python/constrainer/ModelNameConstrainer.ts new file mode 100644 index 0000000000..42e9ac994f --- /dev/null +++ b/src/generators/python/constrainer/ModelNameConstrainer.ts @@ -0,0 +1,53 @@ +import { + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { isReservedPythonKeyword } from '../Constants'; + +export type ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultModelNameConstraints: ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NAMING_FORMATTER: (value: string) => { + return FormatHelpers.toPascalCase(value); + }, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedPythonKeyword); + } +}; + +/** + * Default constraint logic for Python, which converts the model name into something that is compatible with Python + */ +export function defaultModelNameConstraints( + customConstraints?: Partial +): ModelNameConstraint { + const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; + + return ({ modelName }) => { + let constrainedValue = modelName; + constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue); + constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue); + constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue); + constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue); + constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); + return constrainedValue; + }; +} diff --git a/src/generators/python/constrainer/PropertyKeyConstrainer.ts b/src/generators/python/constrainer/PropertyKeyConstrainer.ts new file mode 100644 index 0000000000..93e01576a6 --- /dev/null +++ b/src/generators/python/constrainer/PropertyKeyConstrainer.ts @@ -0,0 +1,80 @@ +import { ConstrainedObjectModel, ObjectModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { isReservedPythonKeyword } from '../Constants'; + +export type PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_PROPERTIES: ( + constrainedObjectModel: ConstrainedObjectModel, + objectModel: ObjectModel, + propertyName: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toCamelCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedPythonKeyword); + } +}; +/** + * Default constraint logic for Python, which converts the object property key into something that is compatible with Python + */ +export function defaultPropertyKeyConstraints( + customConstraints?: Partial +): PropertyKeyConstraint { + const constraints = { + ...DefaultPropertyKeyConstraints, + ...customConstraints + }; + + return ({ objectPropertyModel, constrainedObjectModel, objectModel }) => { + let constrainedPropertyKey = objectPropertyModel.propertyName; + + constrainedPropertyKey = constraints.NO_SPECIAL_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey); + constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS( + constrainedPropertyKey + ); + //If the property name has been manipulated, lets make sure it don't clash with existing properties + if (constrainedPropertyKey !== objectPropertyModel.propertyName) { + constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES( + constrainedObjectModel, + objectModel, + constrainedPropertyKey, + constraints.NAMING_FORMATTER + ); + } + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + return constrainedPropertyKey; + }; +} diff --git a/src/generators/python/index.ts b/src/generators/python/index.ts new file mode 100644 index 0000000000..43da4a8a94 --- /dev/null +++ b/src/generators/python/index.ts @@ -0,0 +1,21 @@ +export * from './PythonGenerator'; +export * from './PythonFileGenerator'; +export * from './presets'; +export { PYTHON_DEFAULT_PRESET } from './PythonPreset'; +export type { PythonPreset } from './PythonPreset'; + +export { + defaultEnumKeyConstraints as pythonDefaultEnumKeyConstraints, + DefaultEnumKeyConstraints as PythonDefaultEnumKeyConstraints, + defaultEnumValueConstraints as pythonDefaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; + +export { + DefaultModelNameConstraints as PythonDefaultModelNameConstraints, + defaultModelNameConstraints as pythonDefaultModelNameConstraints +} from './constrainer/ModelNameConstrainer'; + +export { + DefaultPropertyKeyConstraints as PythonDefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints as pythonDefaultPropertyKeyConstraints +} from './constrainer/PropertyKeyConstrainer'; diff --git a/src/generators/python/presets/Pydantic.ts b/src/generators/python/presets/Pydantic.ts new file mode 100644 index 0000000000..0b50785549 --- /dev/null +++ b/src/generators/python/presets/Pydantic.ts @@ -0,0 +1,37 @@ +import { PythonOptions } from '../PythonGenerator'; +import { ClassPresetType, PythonPreset } from '../PythonPreset'; + +const PYTHON_PYDANTIC_CLASS_PRESET: ClassPresetType = { + async self({ renderer, model }) { + renderer.dependencyManager.addDependency( + 'from typing import Optional, Any' + ); + renderer.dependencyManager.addDependency( + 'from pydantic import BaseModel, Field' + ); + + const defaultClassString = await renderer.defaultSelf(); + + return defaultClassString.replace( + `class ${model.name}:`, + `class ${model.name}(BaseModel):` + ); + }, + property(params) { + const type = params.property.required + ? params.property.property.type + : `Optional[${params.property.property.type}]`; + const alias = params.property.property.originalInput['description'] + ? `alias='${params.property.property.originalInput['description']}'` + : ''; + + return `${params.property.propertyName}: ${type} = Field(${alias})`; + }, + ctor: () => '', + getter: () => '', + setter: () => '' +}; + +export const PYTHON_PYDANTIC_PRESET: PythonPreset = { + class: PYTHON_PYDANTIC_CLASS_PRESET +}; diff --git a/src/generators/python/presets/index.ts b/src/generators/python/presets/index.ts new file mode 100644 index 0000000000..d701e09bf2 --- /dev/null +++ b/src/generators/python/presets/index.ts @@ -0,0 +1 @@ +export { PYTHON_PYDANTIC_PRESET } from './Pydantic'; diff --git a/src/generators/python/renderers/ClassRenderer.ts b/src/generators/python/renderers/ClassRenderer.ts new file mode 100644 index 0000000000..378c5e74f4 --- /dev/null +++ b/src/generators/python/renderers/ClassRenderer.ts @@ -0,0 +1,107 @@ +import { PythonRenderer } from '../PythonRenderer'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel +} from '../../../models'; +import { PythonOptions } from '../PythonGenerator'; +import { ClassPresetType } from '../PythonPreset'; + +/** + * Renderer for Python's `class` type + * + * @extends PythonRenderer + */ +export class ClassRenderer extends PythonRenderer { + async defaultSelf(): Promise { + const content = [ + await this.renderProperties(), + await this.runCtorPreset(), + await this.renderAccessors(), + await this.runAdditionalContentPreset() + ]; + + return `class ${this.model.name}: +${this.indent(this.renderBlock(content, 2))} +`; + } + + runCtorPreset(): Promise { + return this.runPreset('ctor'); + } + + /** + * Render all the properties for the class. + */ + async renderProperties(): Promise { + const properties = this.model.properties || {}; + const content: string[] = []; + + for (const property of Object.values(properties)) { + const rendererProperty = await this.runPropertyPreset(property); + content.push(rendererProperty); + } + + return this.renderBlock(content); + } + + runPropertyPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('property', { property }); + } + + /** + * Render all the accessors for the properties + */ + async renderAccessors(): Promise { + const properties = this.model.properties || {}; + const content: string[] = []; + + for (const property of Object.values(properties)) { + const getter = await this.runGetterPreset(property); + const setter = await this.runSetterPreset(property); + content.push(this.renderBlock([getter, setter])); + } + + return this.renderBlock(content, 2); + } + + runGetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('getter', { property }); + } + + runSetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('setter', { property }); + } +} + +export const PYTHON_DEFAULT_CLASS_PRESET: ClassPresetType = { + self({ renderer }) { + return renderer.defaultSelf(); + }, + ctor({ renderer, model }) { + const properties = model.properties || {}; + let body = ''; + if (Object.keys(properties).length > 0) { + const assigments = Object.values(properties).map((property) => { + if (!property.required) { + return `if hasattr(input, '${property.propertyName}'):\n\tself._${property.propertyName} = input.${property.propertyName}`; + } + return `self._${property.propertyName} = input.${property.propertyName}`; + }); + body = renderer.renderBlock(assigments); + } else { + body = `""" +No properties +"""`; + } + return `def __init__(self, input): +${renderer.indent(body)}`; + }, + getter({ property }) { + return `@property +def ${property.propertyName}(self):\n\treturn self._${property.propertyName}`; + }, + setter({ property }) { + return `@${property.propertyName}.setter +def ${property.propertyName}(self, ${property.propertyName}):\n\tself._${property.propertyName} = ${property.propertyName}`; + } +}; diff --git a/src/generators/python/renderers/EnumRenderer.ts b/src/generators/python/renderers/EnumRenderer.ts new file mode 100644 index 0000000000..44f7a19993 --- /dev/null +++ b/src/generators/python/renderers/EnumRenderer.ts @@ -0,0 +1,51 @@ +import { PythonRenderer } from '../PythonRenderer'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../models'; +import { EnumPresetType } from '../PythonPreset'; +import { PythonOptions } from '../PythonGenerator'; + +/** + * Renderer for Python's `enum` type + * + * @extends PythonRenderer + */ +export class EnumRenderer extends PythonRenderer { + async defaultSelf(): Promise { + const content = [ + await this.renderItems(), + await this.runAdditionalContentPreset() + ]; + + return `class ${this.model.name}(Enum): +${this.indent(this.renderBlock(content, 2))}`; + } + + async renderItems(): Promise { + const enums = this.model.values || []; + const items: string[] = []; + + for (const value of enums) { + const renderedItem = await this.runItemPreset(value); + items.push(renderedItem); + } + + const content = items.join('\n'); + return `${content}`; + } + + runItemPreset(item: ConstrainedEnumValueModel): Promise { + return this.runPreset('item', { item }); + } +} + +export const PYTHON_DEFAULT_ENUM_PRESET: EnumPresetType = { + self({ renderer }) { + renderer.dependencyManager.addDependency('from enum import Enum'); + return renderer.defaultSelf(); + }, + item({ item }) { + return `${item.key} = ${item.value}`; + } +}; diff --git a/src/generators/rust/Constants.ts b/src/generators/rust/Constants.ts new file mode 100644 index 0000000000..291fad2acd --- /dev/null +++ b/src/generators/rust/Constants.ts @@ -0,0 +1,68 @@ +import { checkForReservedKeyword } from '../../helpers'; + +export const RESERVED_RUST_KEYWORDS = [ + // strict keywords can only be used in correct context, and are therefore invalid as: + // Items, variables and function parameters, fields and vairants, type parameters, lifetime parameters, loop labels, macros or attributes, macro placeholders + // https://doc.rust-lang.org/reference/keywords.html#strict-keywords + 'as', + 'async', + 'await', + 'break', + 'const', + 'continue', + 'crate', + 'dyn', + 'else', + 'enum', + 'extern', + 'false', + 'fn', + 'for', + 'if', + 'impl', + 'in', + 'let', + 'loop', + 'match', + 'mod', + 'move', + 'mut', + 'pub', + 'ref', + 'return', + 'self', + 'Self', + 'static', + 'struct', + 'super', + 'trait', + 'true', + 'try', + 'type', + 'unsafe', + 'use', + 'where', + 'while', + // weak keywrods + // these keywords have special meaning only in certain contexts, but are included as reserved keywords here for simplicity + 'union', + "'static", + 'macro_rules', + // keywords reserved for future use + // https://doc.rust-lang.org/reference/keywords.html#reserved-keywords + 'abstract', + 'become', + 'box', + 'do', + 'final', + 'macro', + 'override', + 'priv', + 'typeof', + 'unsized', + 'yield' +]; + +export function isReservedRustKeyword(word: string): boolean { + return checkForReservedKeyword(word, RESERVED_RUST_KEYWORDS, false); +} diff --git a/src/generators/rust/RustConstrainer.ts b/src/generators/rust/RustConstrainer.ts new file mode 100644 index 0000000000..612d064b31 --- /dev/null +++ b/src/generators/rust/RustConstrainer.ts @@ -0,0 +1,363 @@ +import { + defaultEnumKeyConstraints, + defaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; +import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; +import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; +import { RustTypeMapping } from './RustGenerator'; +import { FormatHelpers, Constraints } from '../../helpers'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedFloatModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedUnionModel +} from '../../models'; + +export function deriveHash(model: ConstrainedMetaModel): boolean { + // float primitives and std::collection::HashMap do not implement Hash trait + if ( + model instanceof ConstrainedDictionaryModel || + model instanceof ConstrainedFloatModel || + model instanceof ConstrainedAnyModel + ) { + return false; + } else if ( + // all contents implement Hash trait + model instanceof ConstrainedUnionModel || + model instanceof ConstrainedTupleModel || + model instanceof ConstrainedEnumModel || + model instanceof ConstrainedArrayModel || + model instanceof ConstrainedObjectModel + ) { + return allHashable(model); + } + return true; +} + +export function deriveCopy(model: ConstrainedMetaModel): boolean { + if ( + // serde_json::Value, HashMap, Box, and String do not implement copy + model instanceof ConstrainedAnyModel || + model instanceof ConstrainedDictionaryModel || + model instanceof ConstrainedReferenceModel || + model instanceof ConstrainedStringModel + ) { + return false; + } else if ( + // all contents implement Copy trait + model instanceof ConstrainedArrayModel || + model instanceof ConstrainedEnumModel || + model instanceof ConstrainedObjectModel || + model instanceof ConstrainedTupleModel || + model instanceof ConstrainedUnionModel + ) { + return allCopyable(model); + } + return true; +} + +export function derivePartialEq(model: ConstrainedMetaModel): boolean { + if (model instanceof ConstrainedAnyModel) { + return false; + } + + if ( + // all contents implement PartialEq trait + model instanceof ConstrainedArrayModel || + model instanceof ConstrainedEnumModel || + model instanceof ConstrainedObjectModel || + model instanceof ConstrainedTupleModel || + model instanceof ConstrainedUnionModel || + model instanceof ConstrainedDictionaryModel + ) { + return allPartialEq(model); + } + return true; +} + +export function deriveEq(model: ConstrainedMetaModel): boolean { + if (!derivePartialEq(model)) { + return false; + } + + if ( + model instanceof ConstrainedFloatModel || + model instanceof ConstrainedAnyModel + ) { + return false; + } else if ( + // all contents implement Eq trait + model instanceof ConstrainedArrayModel || + model instanceof ConstrainedEnumModel || + model instanceof ConstrainedObjectModel || + model instanceof ConstrainedTupleModel || + model instanceof ConstrainedUnionModel || + model instanceof ConstrainedDictionaryModel + ) { + return allEq(model); + } + return true; +} + +export function derivePartialOrd(model: ConstrainedMetaModel): boolean { + if ( + model instanceof ConstrainedAnyModel || + model instanceof ConstrainedDictionaryModel + ) { + return false; + } else if ( + // all contents implement PartialOrd trait + model instanceof ConstrainedArrayModel || + model instanceof ConstrainedEnumModel || + model instanceof ConstrainedObjectModel || + model instanceof ConstrainedTupleModel || + model instanceof ConstrainedUnionModel + ) { + return allPartialOrd(model); + } + return true; +} + +export function deriveOrd(model: ConstrainedMetaModel): boolean { + if (!derivePartialOrd(model)) { + return false; + } + + if ( + model instanceof ConstrainedFloatModel || + model instanceof ConstrainedAnyModel || + model instanceof ConstrainedDictionaryModel + ) { + return false; + } else if ( + // all contents implement Ord trait + model instanceof ConstrainedArrayModel || + model instanceof ConstrainedEnumModel || + model instanceof ConstrainedObjectModel || + model instanceof ConstrainedTupleModel || + model instanceof ConstrainedUnionModel + ) { + return allOrd(model); + } + return true; +} + +export function allPartialEq(model: ConstrainedMetaModel): boolean { + if (model instanceof ConstrainedUnionModel) { + return model.union.map(derivePartialEq).every((v) => v === true); + } else if (model instanceof ConstrainedTupleModel) { + return model.tuple + .map((v) => derivePartialEq(v.value)) + .every((v) => v === true); + } else if (model instanceof ConstrainedObjectModel) { + return Object.values(model.properties) + .map((p) => derivePartialEq(p.property)) + .every((v) => v === true); + } else if (model instanceof ConstrainedArrayModel) { + return derivePartialEq(model.valueModel); + } else if (model instanceof ConstrainedEnumModel) { + return model.values + .map((v) => derivePartialEq(v.value)) + .every((v) => v === true); + } else if (model instanceof ConstrainedDictionaryModel) { + return derivePartialEq(model.value); + } + return false; +} + +export function allEq(model: ConstrainedMetaModel): boolean { + if (model instanceof ConstrainedUnionModel) { + return model.union.map(deriveEq).every((v) => v === true); + } else if (model instanceof ConstrainedTupleModel) { + return model.tuple.map((v) => deriveEq(v.value)).every((v) => v === true); + } else if (model instanceof ConstrainedObjectModel) { + return Object.values(model.properties) + .map((p) => deriveEq(p.property)) + .every((v) => v === true); + } else if (model instanceof ConstrainedArrayModel) { + return deriveEq(model.valueModel); + } else if (model instanceof ConstrainedEnumModel) { + return model.values.map((v) => deriveEq(v.value)).every((v) => v === true); + } else if (model instanceof ConstrainedDictionaryModel) { + return deriveEq(model.value); + } + return false; +} + +export function allPartialOrd(model: ConstrainedMetaModel): boolean { + if (model instanceof ConstrainedUnionModel) { + return model.union.map(derivePartialOrd).every((v) => v === true); + } else if (model instanceof ConstrainedTupleModel) { + return model.tuple + .map((v) => derivePartialOrd(v.value)) + .every((v) => v === true); + } else if (model instanceof ConstrainedObjectModel) { + return Object.values(model.properties) + .map((p) => derivePartialOrd(p.property)) + .every((v) => v === true); + } else if (model instanceof ConstrainedArrayModel) { + return derivePartialOrd(model.valueModel); + } else if (model instanceof ConstrainedEnumModel) { + return model.values + .map((v) => derivePartialOrd(v.value)) + .every((v) => v === true); + } + return false; +} + +export function allOrd(model: ConstrainedMetaModel): boolean { + if (model instanceof ConstrainedUnionModel) { + return model.union.map(deriveOrd).every((v) => v === true); + } else if (model instanceof ConstrainedTupleModel) { + return model.tuple.map((v) => deriveOrd(v.value)).every((v) => v === true); + } else if (model instanceof ConstrainedObjectModel) { + return Object.values(model.properties) + .map((p) => deriveOrd(p.property)) + .every((v) => v === true); + } else if (model instanceof ConstrainedArrayModel) { + return deriveOrd(model.valueModel); + } else if (model instanceof ConstrainedEnumModel) { + return model.values.map((v) => deriveOrd(v.value)).every((v) => v === true); + } + return false; +} + +export function allHashable(model: ConstrainedMetaModel): boolean { + if (model instanceof ConstrainedUnionModel) { + return model.union.map(deriveHash).every((v) => v === true); + } else if (model instanceof ConstrainedTupleModel) { + return model.tuple.map((v) => deriveHash(v.value)).every((v) => v === true); + } else if (model instanceof ConstrainedObjectModel) { + return Object.values(model.properties) + .map((p) => deriveHash(p.property)) + .every((v) => v === true); + } else if (model instanceof ConstrainedArrayModel) { + return deriveHash(model.valueModel); + } else if (model instanceof ConstrainedEnumModel) { + return model.values + .map((v) => deriveHash(v.value)) + .every((v) => v === true); + } + return false; +} + +export function allCopyable(model: ConstrainedMetaModel): boolean { + if (model instanceof ConstrainedUnionModel) { + return model.union.map(deriveCopy).every((v) => v === true); + } else if (model instanceof ConstrainedTupleModel) { + return model.tuple.map((v) => deriveCopy(v.value)).every((v) => v === true); + } else if (model instanceof ConstrainedObjectModel) { + return Object.values(model.properties) + .map((p) => deriveCopy(p.property)) + .every((v) => v === true); + } else if (model instanceof ConstrainedArrayModel) { + return deriveCopy(model.valueModel); + } else if (model instanceof ConstrainedEnumModel) { + return model.values + .map((v) => deriveCopy(v.value)) + .every((v) => v === true); + } + return false; +} + +export const RustDefaultTypeMapping: RustTypeMapping = { + Object({ constrainedModel }): string { + return constrainedModel.name; + }, + Reference({ constrainedModel }): string { + return `${constrainedModel.name}`; + }, + Any(): string { + return 'serde_json::Value'; + }, + Float({ constrainedModel }): string { + let type = 'f64'; + const format = + constrainedModel.originalInput && + constrainedModel.originalInput['format']; + switch (format) { + case 'fp32': + case 'f32': + case 'float32': + type = 'f32'; + break; + } + return type; + }, + Integer({ constrainedModel }): string { + let type = 'i32'; + const format = + constrainedModel.originalInput && + constrainedModel.originalInput['format']; + switch (format) { + case 'integer': + case 'int32': + break; + case 'long': + case 'int64': + type = 'i64'; + break; + } + return type; + }, + String({ constrainedModel }): string { + let type = 'String'; + const format = + constrainedModel.originalInput && + constrainedModel.originalInput['format']; + switch (format) { + case 'bytes': + case 'bytes[]': + case 'binary': + type = 'Vec'; + break; + } + return type; + }, + Boolean(): string { + return 'bool'; + }, + Tuple({ constrainedModel }): string { + return `${constrainedModel.name}`; + }, + Array({ constrainedModel }): string { + const prefix = + constrainedModel.valueModel instanceof ConstrainedReferenceModel + ? 'crate::' + : ''; + return `Vec<${prefix}${FormatHelpers.upperFirst( + constrainedModel.valueModel.type + )}>`; + }, + Enum({ constrainedModel }): string { + return constrainedModel.name; + }, + Union({ constrainedModel }): string { + return constrainedModel.name; + }, + Dictionary({ constrainedModel }): string { + const key_prefix = + constrainedModel.key instanceof ConstrainedReferenceModel + ? 'crate::' + : ''; + const value_prefix = + constrainedModel.value instanceof ConstrainedReferenceModel + ? 'crate::' + : ''; + return `std::collections::HashMap<${key_prefix}${constrainedModel.key.type}, ${value_prefix}${constrainedModel.value.type}>`; + } +}; + +export const RustDefaultConstraints: Constraints = { + enumKey: defaultEnumKeyConstraints(), + enumValue: defaultEnumValueConstraints(), + modelName: defaultModelNameConstraints(), + propertyKey: defaultPropertyKeyConstraints() +}; diff --git a/src/generators/rust/RustDependencyManager.ts b/src/generators/rust/RustDependencyManager.ts new file mode 100644 index 0000000000..1a492262b8 --- /dev/null +++ b/src/generators/rust/RustDependencyManager.ts @@ -0,0 +1,8 @@ +import { AbstractDependencyManager } from '../AbstractDependencyManager'; +import { RustOptions } from './RustGenerator'; + +export class RustDependencyManager extends AbstractDependencyManager { + constructor(public options: RustOptions, dependencies: string[] = []) { + super(dependencies); + } +} diff --git a/src/generators/rust/RustFileGenerator.ts b/src/generators/rust/RustFileGenerator.ts new file mode 100644 index 0000000000..5264aad16b --- /dev/null +++ b/src/generators/rust/RustFileGenerator.ts @@ -0,0 +1,81 @@ +import { RustGenerator, RustRenderCompleteModelOptions } from './RustGenerator'; +import { InputMetaModel, OutputModel } from '../../models'; +import * as path from 'path'; +import { AbstractFileGenerator } from '../AbstractFileGenerator'; +import { FileHelpers, FormatHelpers } from '../../helpers'; + +export class RustFileGenerator + extends RustGenerator + implements AbstractFileGenerator +{ + /** + * Generates all the models to an output directory each model with their own separate files. + * + * @param input + * @param outputDirectory where you want the models generated to + * @param options + * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. + */ + public async generateToFiles( + input: Record | InputMetaModel, + outputDirectory: string, + options: RustRenderCompleteModelOptions, + ensureFilesWritten = false + ): Promise { + let generatedModels = await this.generateCompleteModels(input, options); + //Filter anything out that have not been successfully generated + generatedModels = generatedModels.filter((outputModel) => { + return ( + outputModel.modelName !== '' && outputModel.modelName !== undefined + ); + }); + for (const outputModel of generatedModels) { + const fileName = FormatHelpers.snakeCase(outputModel.modelName); + const filePath = path.resolve(outputDirectory, `${fileName}.rs`); + await FileHelpers.writerToFileSystem( + outputModel.result, + filePath, + ensureFilesWritten + ); + } + return generatedModels; + } + + /** + * Generates a compile-able package (crate), + * + * @param input + * @param outputDirectory where you want the models generated to + * @param options + * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. + */ + public async generateToPackage( + input: Record | InputMetaModel, + outputDirectory: string, + options: RustRenderCompleteModelOptions, + ensureFilesWritten = false + ): Promise { + // Crate package expects source code to be written to src/.rs + const sourceOutputDirectory = `${outputDirectory}/src`; + let generatedModels = await this.generateToFiles( + input, + sourceOutputDirectory, + options, + ensureFilesWritten + ); + // render lib.rs and Cargo.toml + if (options.supportFiles) { + const supportOutput = await this.generateCompleteSupport(input, options); + generatedModels = generatedModels.concat(supportOutput); + for (const outputModel of supportOutput) { + const filePath = path.resolve(outputDirectory, outputModel.model.name); + await FileHelpers.writerToFileSystem( + outputModel.result, + filePath, + ensureFilesWritten + ); + } + } + return generatedModels; + } +} diff --git a/src/generators/rust/RustGenerator.ts b/src/generators/rust/RustGenerator.ts new file mode 100644 index 0000000000..41f22a1ab8 --- /dev/null +++ b/src/generators/rust/RustGenerator.ts @@ -0,0 +1,450 @@ +import { + AbstractGenerator, + CommonGeneratorOptions, + defaultGeneratorOptions +} from '../AbstractGenerator'; +import { + OutputModel, + InputMetaModel, + RenderOutput, + ConstrainedObjectModel, + ConstrainedEnumModel, + ConstrainedMetaModel, + MetaModel, + ConstrainedTupleModel, + ConstrainedUnionModel +} from '../../models'; +import { + constrainMetaModel, + Constraints, + IndentationTypes, + split, + SplitOptions, + TypeMapping +} from '../../helpers'; +import { RustPreset, RUST_DEFAULT_PRESET } from './RustPreset'; +import { StructRenderer } from './renderers/StructRenderer'; +import { EnumRenderer } from './renderers/EnumRenderer'; +import { Logger } from '../../utils/LoggingInterface'; +import { + RustDefaultConstraints, + RustDefaultTypeMapping +} from './RustConstrainer'; +import { TupleRenderer } from './renderers/TupleRenderer'; +import { UnionRenderer } from './renderers/UnionRenderer'; +import { PackageRenderer } from './renderers/PackageRenderer'; +import { DeepPartial, mergePartialAndDefault } from '../../utils/Partials'; +import { RustDependencyManager } from './RustDependencyManager'; +export interface RustOptions extends CommonGeneratorOptions { + typeMapping: TypeMapping; + constraints: Constraints; +} +export type RustTypeMapping = TypeMapping; + +export enum RustPackageFeatures { + json +} + +export interface RustPackageOptions { + packageName: string; + packageVersion: string; + authors: string[]; + homepage: string; + repository: string; + license: string; + description: string; + edition: string; + packageFeatures: RustPackageFeatures[]; +} + +export interface RustRenderCompleteModelOptions { + supportFiles: boolean; + package: RustPackageOptions; +} + +export const defaultRustRenderCompleteModelOptions = { + supportFiles: true, + package: { + // Many of these options can be parsed from schema? + packageName: 'asyncapi-rs-models', + packageVersion: '0.0.0', + authors: ['AsyncAPI Rust Champions'], + homepage: 'https://www.asyncapi.com/tools/modelina', + repository: 'https://github.com/asyncapi/modelina', + license: 'Apache-2.0', + description: 'Rust models generated by AsyncAPI Modelina', + edition: '2018', + packageFeatures: [RustPackageFeatures.json] as RustPackageFeatures[] + } as RustPackageOptions +}; + +export class ConstrainedSupportFileModel extends ConstrainedMetaModel {} + +/** + * Generator for Rust + */ +export class RustGenerator extends AbstractGenerator< + RustOptions, + RustRenderCompleteModelOptions +> { + static defaultOptions: RustOptions = { + ...defaultGeneratorOptions, + defaultPreset: RUST_DEFAULT_PRESET, + typeMapping: RustDefaultTypeMapping, + constraints: RustDefaultConstraints, + indentation: { + type: IndentationTypes.SPACES, + size: 4 + }, + // Temporarily set + dependencyManager: () => { + return {} as RustDependencyManager; + } + }; + + constructor(options?: DeepPartial) { + const realizedOptions = RustGenerator.getRustOptions(options); + super('Rust', realizedOptions); + } + + /** + * Returns the Rust options by merging custom options with default ones. + */ + static getRustOptions(options?: DeepPartial): RustOptions { + const optionsToUse = mergePartialAndDefault( + RustGenerator.defaultOptions, + options + ) as RustOptions; + //Always overwrite the dependency manager unless user explicitly state they want it (ignore default temporary dependency manager) + if (options?.dependencyManager === undefined) { + optionsToUse.dependencyManager = () => { + return new RustDependencyManager(optionsToUse); + }; + } + return optionsToUse; + } + + /** + * Returns the Rust options by merging custom options with default ones. + */ + static getRustCompleteOptions( + options?: DeepPartial + ): RustRenderCompleteModelOptions { + return mergePartialAndDefault( + defaultRustRenderCompleteModelOptions, + options + ) as RustRenderCompleteModelOptions; + } + + /** + * Wrapper to get an instance of the dependency manager + */ + getDependencyManager(options: RustOptions): RustDependencyManager { + return this.getDependencyManagerInstance(options) as RustDependencyManager; + } + + splitMetaModel(model: MetaModel): MetaModel[] { + //These are the models that we have separate renderers for + const metaModelsToSplit: SplitOptions = { + splitEnum: true, + splitObject: true, + splitTuple: true, + splitArray: false, + splitUnion: true + }; + return split(model, metaModelsToSplit); + } + + constrainToMetaModel( + model: MetaModel, + options: DeepPartial + ): ConstrainedMetaModel { + const optionsToUse = RustGenerator.getRustOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + return constrainMetaModel( + this.options.typeMapping, + this.options.constraints, + { + metaModel: model, + dependencyManager: dependencyManagerToUse, + options: this.options, + constrainedName: '' //This is just a placeholder, it will be constrained within the function + } + ); + } + + render( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = RustGenerator.getRustOptions({ + ...this.options, + ...options + }); + if (model instanceof ConstrainedObjectModel) { + return this.renderStruct(model, inputModel, optionsToUse); + } else if (model instanceof ConstrainedEnumModel) { + return this.renderEnum(model, inputModel, optionsToUse); + } else if (model instanceof ConstrainedTupleModel) { + return this.renderTuple(model, inputModel, optionsToUse); + } else if (model instanceof ConstrainedUnionModel) { + return this.renderUnion(model, inputModel, optionsToUse); + } + Logger.warn( + `Rust generator, cannot generate this type of model, ${model.name}` + ); + return Promise.resolve( + RenderOutput.toRenderOutput({ + result: '', + renderedName: '', + dependencies: [] + }) + ); + } + + /** + * Render a complete model result where the model code, library and model dependencies are all bundled appropriately. + * + * @param model + * @param inputModel + * @param options + */ + async renderCompleteModel( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + _completeModelOptions: Partial, + options: DeepPartial + ): Promise { + const optionsToUse = RustGenerator.getRustOptions({ + ...this.options, + ...options + }); + Logger.debug('Generating complete models with options: ', optionsToUse); + const outputModel = await this.render(model, inputModel); + const outputContent = outputModel.result; + return RenderOutput.toRenderOutput({ + result: outputContent, + renderedName: outputModel.renderedName, + dependencies: outputModel.dependencies + }); + } + + async renderEnum( + model: ConstrainedEnumModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = RustGenerator.getRustOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('enum'); + const renderer = new EnumRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runSelfPreset(); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } + + async renderStruct( + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = RustGenerator.getRustOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('struct'); + const renderer = new StructRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runSelfPreset(); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } + + async renderTuple( + model: ConstrainedTupleModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = RustGenerator.getRustOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('tuple'); + const renderer = new TupleRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runSelfPreset(); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } + + async renderUnion( + model: ConstrainedUnionModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = RustGenerator.getRustOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('union'); + const renderer = new UnionRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runSelfPreset(); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } + async renderManifest( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + completeModelOptions: Partial, + options?: DeepPartial + ): Promise { + const optionsToUse = RustGenerator.getRustOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('package'); + const renderer = new PackageRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runPreset('manifest', { + packageOptions: completeModelOptions.package, + inputModel + }); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } + async renderLib( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + completeModelOptions: Partial, + options?: DeepPartial + ): Promise { + const optionsToUse = RustGenerator.getRustOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('package'); + const renderer = new PackageRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runPreset('lib', { + packageOptions: completeModelOptions.package, + inputModel + }); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } + + async generateCompleteSupport( + input: Record | InputMetaModel, + completeModelOptions: Partial, + options?: DeepPartial + ): Promise { + const inputModel = await this.processInput(input); + const manifestMetaModel = new ConstrainedSupportFileModel( + 'Cargo.toml', + inputModel, + 'supportFile' + ); + const libMetaModel = new ConstrainedSupportFileModel( + 'src/lib.rs', + inputModel, + 'supportFile' + ); + const manifestOutput = await this.renderManifest( + manifestMetaModel, + inputModel, + completeModelOptions, + options + ); + const libOutput = await this.renderLib( + libMetaModel, + inputModel, + completeModelOptions, + options + ); + + return [ + OutputModel.toOutputModel({ + result: manifestOutput.result, + modelName: 'CargoToml', + dependencies: manifestOutput.dependencies, + model: manifestMetaModel, + inputModel + }), + OutputModel.toOutputModel({ + result: libOutput.result, + modelName: 'lib', + dependencies: libOutput.dependencies, + model: libMetaModel, + inputModel + }) + ]; + } +} diff --git a/src/generators/rust/RustPreset.ts b/src/generators/rust/RustPreset.ts new file mode 100644 index 0000000000..f2a9b39cf7 --- /dev/null +++ b/src/generators/rust/RustPreset.ts @@ -0,0 +1,134 @@ +/* eslint-disable @typescript-eslint/ban-types */ +import { AbstractRenderer } from '../AbstractRenderer'; +import { + Preset, + CommonPreset, + PresetArgs, + ConstrainedEnumValueModel, + ConstrainedUnionModel, + ConstrainedMetaModel, + ConstrainedTupleModel, + ConstrainedEnumModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ConstrainedTupleValueModel, + InputMetaModel +} from '../../models'; +import { RustOptions, RustPackageOptions } from './RustGenerator'; +import { + StructRenderer, + RUST_DEFAULT_STRUCT_PRESET +} from './renderers/StructRenderer'; +import { + EnumRenderer, + RUST_DEFAULT_ENUM_PRESET +} from './renderers/EnumRenderer'; +import { + TupleRenderer, + RUST_DEFAULT_TUPLE_PRESET +} from './renderers/TupleRenderer'; +import { + UnionRenderer, + RUST_DEFAULT_UNION_PRESET +} from './renderers/UnionRenderer'; +import { + PackageRenderer, + RUST_DEFAULT_PACKAGE_PRESET +} from './renderers/PackageRenderer'; + +export interface StructFieldArgs { + field: ConstrainedObjectPropertyModel; +} + +export interface TupleFieldArgs { + field: ConstrainedTupleValueModel; + fieldIndex: number; +} + +export interface UnionArgs { + item: ConstrainedMetaModel; +} + +export interface EnumArgs { + item: ConstrainedEnumValueModel; + itemIndex: number; +} + +export interface RustStructPreset + extends CommonPreset { + field?: ( + args: PresetArgs & StructFieldArgs + ) => Promise | string; + fieldMacro?: ( + args: PresetArgs & StructFieldArgs + ) => Promise | string; + structMacro?: ( + args: PresetArgs & StructFieldArgs + ) => Promise | string; +} + +export interface RustTuplePreset + extends CommonPreset { + field?: ( + args: PresetArgs & TupleFieldArgs + ) => Promise | string; + structMacro?: ( + args: PresetArgs + ) => Promise | string; +} + +export interface RustEnumPreset + extends CommonPreset { + item?: (args: PresetArgs & EnumArgs) => string; + itemMacro?: ( + args: PresetArgs & EnumArgs + ) => string; + structMacro?: ( + args: PresetArgs + ) => Promise | string; +} + +export interface RustUnionPreset + extends CommonPreset { + item?: (args: PresetArgs & UnionArgs) => string; + itemMacro?: ( + args: PresetArgs & UnionArgs + ) => string; + structMacro?: ( + args: PresetArgs + ) => Promise | string; +} + +export interface PackageArgs { + packageOptions: RustPackageOptions; + inputModel: InputMetaModel; +} +export interface RustPackagePreset + extends CommonPreset { + manifest?: ( + args: PresetArgs & PackageArgs + ) => string; + lib?: (args: PresetArgs & PackageArgs) => string; +} + +export type EnumPresetType = RustEnumPreset; +export type StructPresetType = RustStructPreset; +export type TuplePresetType = RustTuplePreset; +export type UnionPresetType = RustUnionPreset; +export type PackagePresetType = RustPackagePreset; + +export type RustPreset = Preset<{ + struct: StructPresetType; + enum: EnumPresetType; + tuple: TuplePresetType; + union: UnionPresetType; + package: PackagePresetType; +}>; + +export const RUST_DEFAULT_PRESET: RustPreset = { + struct: RUST_DEFAULT_STRUCT_PRESET, + enum: RUST_DEFAULT_ENUM_PRESET, + tuple: RUST_DEFAULT_TUPLE_PRESET, + union: RUST_DEFAULT_UNION_PRESET, + package: RUST_DEFAULT_PACKAGE_PRESET +}; diff --git a/src/generators/rust/RustRenderer.ts b/src/generators/rust/RustRenderer.ts new file mode 100644 index 0000000000..cb387663aa --- /dev/null +++ b/src/generators/rust/RustRenderer.ts @@ -0,0 +1,62 @@ +import { AbstractRenderer } from '../AbstractRenderer'; +import { RustGenerator, RustOptions } from './RustGenerator'; +import { InputMetaModel, Preset, ConstrainedMetaModel } from '../../models'; +import { FormatHelpers } from '../../helpers/FormatHelpers'; +import { + deriveCopy, + deriveHash, + derivePartialEq, + deriveEq, + derivePartialOrd, + deriveOrd +} from './RustConstrainer'; +import { RustDependencyManager } from './RustDependencyManager'; + +/** + * Common renderer for Rust types + * + * @extends AbstractRenderer + */ +export abstract class RustRenderer< + RendererModelType extends ConstrainedMetaModel +> extends AbstractRenderer { + constructor( + options: RustOptions, + generator: RustGenerator, + presets: Array<[Preset, unknown]>, + model: RendererModelType, + inputModel: InputMetaModel, + public dependencyManager: RustDependencyManager + ) { + super(options, generator, presets, model, inputModel); + } + + renderComments(lines: string | string[]): string { + lines = FormatHelpers.breakLines(lines); + return lines.map((line) => `// ${line}`).join('\n'); + } + + renderMacro(model: ConstrainedMetaModel): string { + const derive: string[] = ['Serialize', 'Deserialize', 'Clone', 'Debug']; + if (deriveHash(model)) { + derive.push('Hash'); + } + if (deriveCopy(model)) { + derive.push('Copy'); + } + if (derivePartialEq(model)) { + derive.push('PartialEq'); + } + if (deriveEq(model)) { + derive.push('Eq'); + } + if (derivePartialOrd(model)) { + derive.push('PartialOrd'); + } + if (deriveOrd(model)) { + derive.push('Ord'); + } + derive.sort(); + return `#[derive(${derive.join(', ')})]`; + } +} diff --git a/src/generators/rust/constrainer/EnumConstrainer.ts b/src/generators/rust/constrainer/EnumConstrainer.ts new file mode 100644 index 0000000000..7915d3f893 --- /dev/null +++ b/src/generators/rust/constrainer/EnumConstrainer.ts @@ -0,0 +1,79 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { ConstrainedEnumModel, EnumModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { + FormatHelpers, + EnumKeyConstraint, + EnumValueConstraint +} from '../../../helpers'; +import { isReservedRustKeyword } from '../Constants'; + +export type ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_KEYS: ( + constrainedEnumModel: ConstrainedEnumModel, + enumModel: EnumModel, + value: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude '_', '$' because they are allowed as enum keys + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_KEYS: NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toPascalCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedRustKeyword); + } +}; + +export function defaultEnumKeyConstraints( + customConstraints?: Partial +): EnumKeyConstraint { + const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; + + return ({ enumKey, enumModel, constrainedEnumModel }) => { + let constrainedEnumKey = enumKey; + constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey); + //If the enum key has been manipulated, lets make sure it don't clash with existing keys + if (constrainedEnumKey !== enumKey) { + constrainedEnumKey = constraints.NO_DUPLICATE_KEYS( + constrainedEnumModel, + enumModel, + constrainedEnumKey, + constraints.NAMING_FORMATTER + ); + } + constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey); + + constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey); + // If the name is a reserved keyword, make sure to format it afterwards + constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey); + return constrainedEnumKey; + }; +} + +export function defaultEnumValueConstraints(): EnumValueConstraint { + return ({ enumValue }) => { + return enumValue; + }; +} diff --git a/src/generators/rust/constrainer/ModelNameConstrainer.ts b/src/generators/rust/constrainer/ModelNameConstrainer.ts new file mode 100644 index 0000000000..599c69ab11 --- /dev/null +++ b/src/generators/rust/constrainer/ModelNameConstrainer.ts @@ -0,0 +1,53 @@ +import { + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { isReservedRustKeyword } from '../Constants'; + +export type ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultModelNameConstraints: ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NAMING_FORMATTER: (value: string) => { + return FormatHelpers.toPascalCaseMergingNumbers(value); + }, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedRustKeyword); + } +}; +export function defaultModelNameConstraints( + customConstraints?: Partial +): ModelNameConstraint { + const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; + + return ({ modelName }) => { + let constrainedValue = modelName; + constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue); + constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue); + constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue); + constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); + + constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue); + // If the name is a reserved keyword, make sure to format it afterwards + constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); + + return constrainedValue; + }; +} diff --git a/src/generators/rust/constrainer/PropertyKeyConstrainer.ts b/src/generators/rust/constrainer/PropertyKeyConstrainer.ts new file mode 100644 index 0000000000..656c7a4618 --- /dev/null +++ b/src/generators/rust/constrainer/PropertyKeyConstrainer.ts @@ -0,0 +1,85 @@ +import { ConstrainedObjectModel, ObjectModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { isReservedRustKeyword } from '../Constants'; + +export type PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_PROPERTIES: ( + constrainedObjectModel: ConstrainedObjectModel, + objectModel: ObjectModel, + propertyName: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toSnakeCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedRustKeyword); + } +}; + +export function defaultPropertyKeyConstraints( + customConstraints?: Partial +): PropertyKeyConstraint { + const constraints = { + ...DefaultPropertyKeyConstraints, + ...customConstraints + }; + + return ({ objectPropertyModel, constrainedObjectModel, objectModel }) => { + let constrainedPropertyKey = objectPropertyModel.propertyName; + + constrainedPropertyKey = constraints.NO_SPECIAL_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey); + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + + constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS( + constrainedPropertyKey + ); + // If the property key is a reserved keyword, make sure to format it afterwards + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + + //If the property name has been manipulated, lets make sure it don't clash with existing properties + if (constrainedPropertyKey !== objectPropertyModel.propertyName) { + constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES( + constrainedObjectModel, + objectModel, + constrainedPropertyKey, + constraints.NAMING_FORMATTER + ); + } + + return constrainedPropertyKey; + }; +} diff --git a/src/generators/rust/index.ts b/src/generators/rust/index.ts new file mode 100644 index 0000000000..4c24936e1d --- /dev/null +++ b/src/generators/rust/index.ts @@ -0,0 +1,5 @@ +export * from './RustGenerator'; +export * from './RustFileGenerator'; +export { RUST_DEFAULT_PRESET } from './RustPreset'; +export type { RustPreset } from './RustPreset'; +export * from './presets'; diff --git a/src/generators/rust/presets/CommonPreset.ts b/src/generators/rust/presets/CommonPreset.ts new file mode 100644 index 0000000000..e5be9f8c32 --- /dev/null +++ b/src/generators/rust/presets/CommonPreset.ts @@ -0,0 +1,167 @@ +import { + ConstrainedEnumModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedUnionModel, + ConstrainedEnumValueModel +} from '../../../models'; +import { EnumRenderer } from '../renderers/EnumRenderer'; +import { RustPreset } from '../RustPreset'; +import { Logger } from '../../../utils'; +import { StructRenderer } from '../renderers/StructRenderer'; + +export interface RustCommonPresetOptions { + implementDefault: boolean; + deriveDefault: boolean; + implementNew: boolean; +} + +export const defaultRustCommonPresetOptions: RustCommonPresetOptions = { + implementDefault: true, + deriveDefault: true, + implementNew: true +}; + +/** + * Get default first member of enum + */ +function getEnumDefaultOrFirst( + model: ConstrainedEnumModel +): ConstrainedEnumValueModel { + const maybeDefault = model.values.find((v, idx) => { + const originalValue = model.originalInput.enum[Number(idx)]; + if ( + model.originalInput.default && + model.originalInput.default === originalValue + ) { + return v; + } + }); + if (maybeDefault === undefined) { + Logger.warn( + `Schema does not provide default value for field ${model.originalInput} - this is incompatible with RustGenerator's #[derive Default] implementation. Please re-run with { deriveDefault: false } option` + ); + return model.values[0]; + } + return maybeDefault; +} + +/** + * Render `impl Default` for enum model + */ +function renderImplementDefault({ + model +}: { + renderer: EnumRenderer; + model: ConstrainedEnumModel; +}): string { + const defaultConstrainedValue = getEnumDefaultOrFirst(model); + + const defaultValue = defaultConstrainedValue.key; + return `impl Default for ${model.name} { + fn default() -> ${model.name} { + ${model.name}::${defaultValue} + } +}`; +} + +/** + * Render `new` constructor function in struct impl + */ +function renderImplementNew({ + model, + renderer +}: { + renderer: StructRenderer; + model: ConstrainedObjectModel; +}) { + const args: string[] = []; + const fields: string[] = []; + const properties = model.properties || {}; + for (const v of Object.values(properties)) { + const prefix = + v.property instanceof ConstrainedReferenceModel ? 'crate::' : ''; + const fieldType = prefix + v.property.type; + + if (v.required) { + args.push(`${v.propertyName}: ${fieldType}`); + if (v.property instanceof ConstrainedReferenceModel) { + fields.push(`${v.propertyName}: Box::new(${v.propertyName}),`); + } else { + fields.push(`${v.propertyName},`); + } + // use map to box reference if field is optional + } else if ( + !v.required && + (v.property instanceof ConstrainedReferenceModel || + v.property instanceof ConstrainedUnionModel) + ) { + args.push(`${v.propertyName}: Option`); + fields.push(`${v.propertyName}: ${v.propertyName}.map(Box::new),`); + } else { + args.push(`${v.propertyName}: Option<${fieldType}>`); + fields.push(`${v.propertyName},`); + } + } + const fieldsBlock = renderer.renderBlock(fields); + return `pub fn new(${args.join(', ')}) -> ${model.name} { + ${model.name} { +${renderer.indent(fieldsBlock, 8)} + } +}`; +} + +export const RUST_COMMON_PRESET: RustPreset = { + enum: { + additionalContent({ renderer, model, content, options }) { + options = options || defaultRustCommonPresetOptions; + const blocks: string[] = []; + if (options.implementDefault) { + blocks.push(renderImplementDefault({ renderer, model })); + } + return renderer.renderBlock([content, ...blocks], 2); + } + }, + struct: { + additionalContent({ renderer, model, content, options }) { + options = options || defaultRustCommonPresetOptions; + const fnBlocks = []; + if (options.implementNew) { + fnBlocks.push(renderImplementNew({ model, renderer })); + } + + const fnBlock = renderer.renderBlock(fnBlocks); + const contentBlock = ` +impl ${model.name} { +${renderer.indent(fnBlock, 4)} +} +`; + content = renderer.renderBlock([content, contentBlock]); + return content; + } + }, + tuple: { + additionalContent({ renderer, model, content, options }) { + options = options || defaultRustCommonPresetOptions; + + if (options.implementNew) { + const properties = model.tuple; + const args: string[] = Object.values(properties).map( + (v, idx) => `value_${idx}: ${v.value.type}` + ); + const fields: string[] = Object.values(properties).map( + (_v, idx) => `value_${idx}` + ); + const implementNew = ` +impl ${model.name} { + pub fn new(${args.join(', ')}) -> ${model.name} { + ${model.name}(${fields.join(', ')}) + } +} +`; + content = renderer.renderBlock([content, implementNew]); + } + return content; + } + } +}; diff --git a/src/generators/rust/presets/index.ts b/src/generators/rust/presets/index.ts new file mode 100644 index 0000000000..58c93bfea2 --- /dev/null +++ b/src/generators/rust/presets/index.ts @@ -0,0 +1 @@ +export * from './CommonPreset'; diff --git a/src/generators/rust/renderers/EnumRenderer.ts b/src/generators/rust/renderers/EnumRenderer.ts new file mode 100644 index 0000000000..1fe976d72c --- /dev/null +++ b/src/generators/rust/renderers/EnumRenderer.ts @@ -0,0 +1,106 @@ +import { RustRenderer } from '../RustRenderer'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../models'; +import { EnumPresetType } from '../RustPreset'; +import { RustOptions } from '../RustGenerator'; + +/** + * Renderer for Rust's `enum` type + * + * @extends EnumRenderer + */ +export class EnumRenderer extends RustRenderer { + public async defaultSelf(): Promise { + const doc = this.renderComments( + `${this.model.name} represents a ${this.model.name} model.` + ); + const structMacro = await this.runStructMacroPreset(); + const items = await this.renderItems(); + const additionalContent = await this.runAdditionalContentPreset(); + return `${doc} +${structMacro} +pub enum ${this.model.name} { +${this.indent(items)} +} +${additionalContent}`; + } + + async renderItems(): Promise { + const enums = this.model.values || []; + const items = await Promise.all( + enums.map(async (value, index) => { + const macro = await this.runItemMacroPreset(value, index); + const renderedItem = `${await this.runItemPreset(value, index)},`; + return [macro, renderedItem]; + }) + ); + return this.renderBlock(items.flat()); + } + + runItemPreset( + item: ConstrainedEnumValueModel, + itemIndex: number + ): Promise { + return this.runPreset('item', { item, itemIndex }); + } + + runItemMacroPreset( + item: ConstrainedEnumValueModel, + itemIndex: number + ): Promise { + return this.runPreset('itemMacro', { item, itemIndex }); + } + + runStructMacroPreset(): Promise { + return this.runPreset('structMacro'); + } + + /** + * Returns the type for the JSON value + */ + renderEnumValueType(value: any): string { + switch (typeof value) { + case 'boolean': + return 'bool'; + case 'bigint': + return 'i64'; + case 'number': { + return 'f64'; + } + case 'object': { + return 'HashMap'; + } + case 'string': + default: { + return 'String'; + } + } + } +} + +export const RUST_DEFAULT_ENUM_PRESET: EnumPresetType = { + self({ renderer }) { + return renderer.defaultSelf(); + }, + item({ item, renderer }) { + const typeOfEnumValue = renderer.renderEnumValueType(item.value); + if (typeOfEnumValue === 'HashMap') { + return `${item.key}(${typeOfEnumValue})`; + } + return `${item.key}`; + }, + itemMacro({ item }) { + const serdeArgs = []; + if (typeof item.value === 'object') { + serdeArgs.push('flatten'); + } else { + serdeArgs.push(`rename="${item.value}"`); + } + return `#[serde(${serdeArgs.join(', ')})]`; + }, + structMacro({ model, renderer }) { + return renderer.renderMacro(model); + } +}; diff --git a/src/generators/rust/renderers/PackageRenderer.ts b/src/generators/rust/renderers/PackageRenderer.ts new file mode 100644 index 0000000000..9b4a2461c1 --- /dev/null +++ b/src/generators/rust/renderers/PackageRenderer.ts @@ -0,0 +1,77 @@ +import { RustRenderer } from '../RustRenderer'; +import { PackagePresetType } from '../RustPreset'; +import { ConstrainedMetaModel, MetaModel } from '../../../models'; +import { RustOptions } from '../RustGenerator'; +import { FormatHelpers } from '../../../helpers'; +import { defaultModelNameConstraints } from '../constrainer/ModelNameConstrainer'; +/** + * Renderer for Rust's supporting files + * + * @extends RustRenderer + */ +export class PackageRenderer extends RustRenderer { + public defaultSelf(): string { + return ''; + } +} + +export const RUST_DEFAULT_PACKAGE_PRESET: PackagePresetType = { + self({ renderer }) { + return renderer.defaultSelf(); + }, + + lib({ inputModel, renderer }) { + const modelNames: string[] = Object.values(inputModel.models).map( + (m: MetaModel) => m.name + ); + const imports = renderer.renderBlock( + modelNames + .map((modelName) => { + let mod = defaultModelNameConstraints()({ modelName }); + mod = FormatHelpers.snakeCase(mod); + return ` +pub mod ${mod}; +pub use self::${mod}::*;`; + }) + .flat() + ); + + return `#[macro_use] +extern crate serde; +extern crate serde_json; +${imports}`; + }, + + manifest({ packageOptions }) { + const { + packageName, + packageVersion, + homepage, + authors, + repository, + description, + license, + edition + } = packageOptions; + const authorsString = authors.map((a: string) => `"${a}"`).join(','); + return `[package] +name = "${packageName}" +version = "${packageVersion}" +authors = [${authorsString}] +homepage = "${homepage}" +repository = "${repository}" +license = "${license}" +description = "${description}" +edition = "${edition}" + +[dependencies] +serde = { version = "1", features = ["derive"] } +serde_json = { version="1", optional = true } + +[dev-dependencies] + +[features] +default = ["json"] +json = ["dep:serde_json"]`; + } +}; diff --git a/src/generators/rust/renderers/StructRenderer.ts b/src/generators/rust/renderers/StructRenderer.ts new file mode 100644 index 0000000000..f7f0124fa9 --- /dev/null +++ b/src/generators/rust/renderers/StructRenderer.ts @@ -0,0 +1,84 @@ +import { RustRenderer } from '../RustRenderer'; +import { StructPresetType } from '../RustPreset'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ConstrainedReferenceModel +} from '../../../models'; +import { RustOptions } from '../RustGenerator'; + +/** + * Renderer for Rust `struct` type + * + * @extends RustRenderer + */ +export class StructRenderer extends RustRenderer { + public async defaultSelf(): Promise { + const content = [await this.renderFields()]; + + const structMacro = await this.runStructMacroPreset(); + + const doc = this.renderComments( + `${this.model.name} represents a ${this.model.name} model.` + ); + const additionalContent = await this.runAdditionalContentPreset(); + return `${doc} +${structMacro} +pub struct ${this.model.name} { +${this.indent(this.renderBlock(content))} +} +${additionalContent}`; + } + + async renderFields(): Promise { + const fields = this.model.properties; + const content: string[] = []; + + for (const field of Object.values(fields)) { + const renderFieldMacro = await this.runFieldMacroPreset(field); + content.push(renderFieldMacro); + const renderField = await this.runFieldPreset(field); + content.push(renderField); + } + return this.renderBlock(content); + } + + runStructMacroPreset(): Promise { + return this.runPreset('structMacro'); + } + runFieldMacroPreset(field: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('fieldMacro', { field }); + } + + runFieldPreset(field: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('field', { field }); + } +} + +export const RUST_DEFAULT_STRUCT_PRESET: StructPresetType = { + self({ renderer }) { + return renderer.defaultSelf(); + }, + structMacro({ model, renderer }) { + return renderer.renderMacro(model); + }, + + fieldMacro({ field }) { + const serdeArgs: string[] = []; + serdeArgs.push(`rename="${field.unconstrainedPropertyName}"`); + if (!field.required) { + serdeArgs.push('skip_serializing_if = "Option::is_none"'); + } + return `#[serde(${serdeArgs.join(', ')})]`; + }, + field({ field }) { + let fieldType = field.property.type; + if (field.property instanceof ConstrainedReferenceModel) { + fieldType = `Box`; + } + if (!field.required) { + fieldType = `Option<${fieldType}>`; + } + return `pub ${field.propertyName}: ${fieldType},`; + } +}; diff --git a/src/generators/rust/renderers/TupleRenderer.ts b/src/generators/rust/renderers/TupleRenderer.ts new file mode 100644 index 0000000000..ab7756384a --- /dev/null +++ b/src/generators/rust/renderers/TupleRenderer.ts @@ -0,0 +1,58 @@ +import { RustRenderer } from '../RustRenderer'; +import { + ConstrainedTupleModel, + ConstrainedTupleValueModel +} from '../../../models'; +import { TuplePresetType } from '../RustPreset'; +import { RustOptions } from '../RustGenerator'; + +/** + * Renderer for Rust's `Tuple` type + * + * @extends TupleRenderer + */ +export class TupleRenderer extends RustRenderer { + public async defaultSelf(): Promise { + const doc = this.renderComments( + `${this.model.name} represents a ${this.model.name} model.` + ); + const structMacro = await this.runStructMacroPreset(); + const additionalContent = await this.runAdditionalContentPreset(); + const fields = await this.renderFields(); + return `${doc} +${structMacro} +pub struct ${this.model.name}(${fields}); +${additionalContent} +`; + } + + async renderFields(): Promise { + const fields = this.model.tuple; + const content: string[] = []; + + for (const field of Object.values(fields)) { + const renderField = await this.runFieldPreset(field); + content.push(renderField); + } + return content.join(', '); + } + + runStructMacroPreset(): Promise { + return this.runPreset('structMacro'); + } + runFieldPreset(field: ConstrainedTupleValueModel): Promise { + return this.runPreset('field', { field }); + } +} + +export const RUST_DEFAULT_TUPLE_PRESET: TuplePresetType = { + self({ renderer }) { + return renderer.defaultSelf(); + }, + structMacro({ model, renderer }) { + return renderer.renderMacro(model); + }, + field({ field }) { + return field.value.type; + } +}; diff --git a/src/generators/rust/renderers/UnionRenderer.ts b/src/generators/rust/renderers/UnionRenderer.ts new file mode 100644 index 0000000000..6f7ff9e53d --- /dev/null +++ b/src/generators/rust/renderers/UnionRenderer.ts @@ -0,0 +1,89 @@ +import { RustRenderer } from '../RustRenderer'; +import { + ConstrainedUnionModel, + ConstrainedMetaModel, + ConstrainedReferenceModel +} from '../../../models'; +import { UnionPresetType } from '../RustPreset'; +import { RustOptions } from '../RustGenerator'; +import { Logger } from '../../../index'; + +/** + * Renderer for Rust's `Union` type + * + * @extends UnionRenderer + */ +export class UnionRenderer extends RustRenderer { + public async defaultSelf(): Promise { + const doc = this.renderComments( + `${this.model.name} represents a union of types: ${this.model.union + .map((m) => m.type) + .join(', ')}` + ); + const structMacro = await this.runStructMacroPreset(); + const variants = await Promise.all( + this.model.union.map(async (v) => { + const macro = await this.runItemMacroPreset(v); + const variant = `${await this.runItemPreset(v)},`; + return [macro, variant]; + }) + ); + const additionalContent = await this.runAdditionalContentPreset(); + return `${doc} +${structMacro} +pub enum ${this.model.name} { +${this.indent(this.renderBlock(variants.flat()))} +} +${additionalContent} +`; + } + + runStructMacroPreset(): Promise { + return this.runPreset('structMacro'); + } + + runItemMacroPreset(item: ConstrainedMetaModel): Promise { + return this.runPreset('itemMacro', { item }); + } + + runItemPreset(item: ConstrainedMetaModel): Promise { + return this.runPreset('item', { item }); + } +} + +export const RUST_DEFAULT_UNION_PRESET: UnionPresetType = { + self({ renderer }) { + return renderer.defaultSelf(); + }, + + structMacro({ model, renderer }) { + const blocks = [renderer.renderMacro(model)]; + if (model.originalInput.discriminator !== undefined) { + // when discriminator is provided, polymorphic type is internally-tagged: + // https://serde.rs/enum-representations.html#internally-tagged + const discriminatorBlock = `#[serde(tag = "${model.originalInput.discriminator}")]`; + blocks.push(discriminatorBlock); + } else { + // otherwise if a discriminator field is not provided, use untagged representation + // https://serde.rs/enum-representations.html#untagged + // a warning is logged because this is a highly inefficient deserialization method + Logger.warn( + `${model.name} is a polymorphic schema, but no discriminator field was found. RustGenerator will use serde's untagged data representation, which attempts to match against the first valid data representation. This is significantly slower than deserializing a tagged data representation. You should provide a discriminator field if possible. See serde tagged/untagged docs for more info: https://serde.rs/enum-representations.html#untagged` + ); + const discriminatorBlock = '#[serde(untagged)]'; + blocks.push(discriminatorBlock); + } + return renderer.renderBlock(blocks); + }, + itemMacro({ item }) { + const serdeArgs: string[] = []; + serdeArgs.push(`rename="${item.name}"`); + return `#[serde(${serdeArgs.join(', ')})]`; + }, + item({ item }) { + if (item instanceof ConstrainedReferenceModel) { + return `${item.name}(crate::${item.ref.type})`; + } + return `${item.name}(${item.type})`; + } +}; diff --git a/src/generators/template/Constants.ts b/src/generators/template/Constants.ts new file mode 100644 index 0000000000..cb538ac072 --- /dev/null +++ b/src/generators/template/Constants.ts @@ -0,0 +1,14 @@ +import { checkForReservedKeyword } from '../../helpers'; + +export const RESERVED_TEMPLATE_KEYWORDS = ['abstract', 'continue']; + +export function isReservedTemplateKeyword( + word: string, + forceLowerCase = true +): boolean { + return checkForReservedKeyword( + word, + RESERVED_TEMPLATE_KEYWORDS, + forceLowerCase + ); +} diff --git a/src/generators/template/TemplateConstrainer.ts b/src/generators/template/TemplateConstrainer.ts new file mode 100644 index 0000000000..0c9c9d5c0c --- /dev/null +++ b/src/generators/template/TemplateConstrainer.ts @@ -0,0 +1,60 @@ +import { TypeMapping } from '../../helpers'; +import { + defaultEnumKeyConstraints, + defaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; +import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; +import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; +import { TemplateDependencyManager } from './TemplateDependencyManager'; +import { TemplateOptions } from './TemplateGenerator'; + +export const TemplateDefaultTypeMapping: TypeMapping< + TemplateOptions, + TemplateDependencyManager +> = { + Object({ constrainedModel }): string { + //Returning name here because all object models have been split out + return constrainedModel.name; + }, + Reference({ constrainedModel }): string { + return constrainedModel.name; + }, + Any(): string { + return ''; + }, + Float(): string { + return ''; + }, + Integer(): string { + return ''; + }, + String(): string { + return ''; + }, + Boolean(): string { + return ''; + }, + Tuple(): string { + return ''; + }, + Array(): string { + return ''; + }, + Enum({ constrainedModel }): string { + //Returning name here because all enum models have been split out + return constrainedModel.name; + }, + Union(): string { + return ''; + }, + Dictionary(): string { + return ''; + } +}; + +export const TemplateDefaultConstraints = { + enumKey: defaultEnumKeyConstraints(), + enumValue: defaultEnumValueConstraints(), + modelName: defaultModelNameConstraints(), + propertyKey: defaultPropertyKeyConstraints() +}; diff --git a/src/generators/template/TemplateDependencyManager.ts b/src/generators/template/TemplateDependencyManager.ts new file mode 100644 index 0000000000..7243eb89a6 --- /dev/null +++ b/src/generators/template/TemplateDependencyManager.ts @@ -0,0 +1,8 @@ +import { AbstractDependencyManager } from '../AbstractDependencyManager'; +import { TemplateOptions } from './TemplateGenerator'; + +export class TemplateDependencyManager extends AbstractDependencyManager { + constructor(public options: TemplateOptions, dependencies: string[] = []) { + super(dependencies); + } +} diff --git a/src/generators/template/TemplateFileGenerator.ts b/src/generators/template/TemplateFileGenerator.ts new file mode 100644 index 0000000000..495a3851ad --- /dev/null +++ b/src/generators/template/TemplateFileGenerator.ts @@ -0,0 +1,45 @@ +import { TemplateGenerator, TemplateRenderCompleteModelOptions } from '.'; +import { InputMetaModel, OutputModel } from '../../models'; +import * as path from 'path'; +import { AbstractFileGenerator } from '../AbstractFileGenerator'; +import { FileHelpers } from '../../helpers'; + +export class TemplateFileGenerator + extends TemplateGenerator + implements AbstractFileGenerator +{ + /** + * Generates all the models to an output directory each model with their own separate files. + * + * @param input + * @param outputDirectory where you want the models generated to + * @param options + */ + public async generateToFiles( + input: Record | InputMetaModel, + outputDirectory: string, + options?: TemplateRenderCompleteModelOptions, + ensureFilesWritten = false + ): Promise { + let generatedModels = await this.generateCompleteModels( + input, + options || {} + ); + //Filter anything out that have not been successfully generated + generatedModels = generatedModels.filter((outputModel) => { + return outputModel.modelName !== ''; + }); + for (const outputModel of generatedModels) { + const filePath = path.resolve( + outputDirectory, + `${outputModel.modelName}.MYEXTENSION` + ); + await FileHelpers.writerToFileSystem( + outputModel.result, + filePath, + ensureFilesWritten + ); + } + return generatedModels; + } +} diff --git a/src/generators/template/TemplateGenerator.ts b/src/generators/template/TemplateGenerator.ts new file mode 100644 index 0000000000..3cfeba8343 --- /dev/null +++ b/src/generators/template/TemplateGenerator.ts @@ -0,0 +1,233 @@ +import { + AbstractGenerator, + CommonGeneratorOptions, + defaultGeneratorOptions +} from '../AbstractGenerator'; +import { + ConstrainedEnumModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + InputMetaModel, + MetaModel, + RenderOutput +} from '../../models'; +import { split, TypeMapping } from '../../helpers'; +import { TemplatePreset, TEMPLATE_DEFAULT_PRESET } from './TemplatePreset'; +import { ClassRenderer } from './renderers/ClassRenderer'; +import { EnumRenderer } from './renderers/EnumRenderer'; +import { isReservedTemplateKeyword } from './Constants'; +import { Logger } from '../..'; +import { + constrainMetaModel, + Constraints +} from '../../helpers/ConstrainHelpers'; +import { + TemplateDefaultConstraints, + TemplateDefaultTypeMapping +} from './TemplateConstrainer'; +import { DeepPartial, mergePartialAndDefault } from '../../utils/Partials'; +import { TemplateDependencyManager } from './TemplateDependencyManager'; + +export interface TemplateOptions + extends CommonGeneratorOptions { + typeMapping: TypeMapping; + constraints: Constraints; +} +export interface TemplateRenderCompleteModelOptions { + packageName: string; +} +export class TemplateGenerator extends AbstractGenerator< + TemplateOptions, + TemplateRenderCompleteModelOptions +> { + static defaultOptions: TemplateOptions = { + ...defaultGeneratorOptions, + defaultPreset: TEMPLATE_DEFAULT_PRESET, + typeMapping: TemplateDefaultTypeMapping, + constraints: TemplateDefaultConstraints + }; + + constructor(options?: DeepPartial) { + const realizedOptions = TemplateGenerator.getTemplateOptions(options); + super('Template', realizedOptions); + } + + /** + * Returns the Template options by merging custom options with default ones. + */ + static getTemplateOptions( + options?: DeepPartial + ): TemplateOptions { + const optionsToUse = mergePartialAndDefault( + TemplateGenerator.defaultOptions, + options + ) as TemplateOptions; + //Always overwrite the dependency manager unless user explicitly state they want it (ignore default temporary dependency manager) + if (options?.dependencyManager === undefined) { + optionsToUse.dependencyManager = () => { + return new TemplateDependencyManager(optionsToUse); + }; + } + return optionsToUse; + } + + /** + * Wrapper to get an instance of the dependency manager + */ + getDependencyManager(options: TemplateOptions): TemplateDependencyManager { + return this.getDependencyManagerInstance( + options + ) as TemplateDependencyManager; + } + + /** + * This function makes sure we split up the MetaModels accordingly to what we want to render as models. + */ + splitMetaModel(model: MetaModel): MetaModel[] { + //These are the models that we have separate renderers for + const metaModelsToSplit = { + splitEnum: true, + splitObject: true + }; + return split(model, metaModelsToSplit); + } + + constrainToMetaModel( + model: MetaModel, + options: DeepPartial + ): ConstrainedMetaModel { + const optionsToUse = TemplateGenerator.getTemplateOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + return constrainMetaModel( + this.options.typeMapping, + this.options.constraints, + { + metaModel: model, + dependencyManager: dependencyManagerToUse, + options: this.options, + constrainedName: '' //This is just a placeholder, it will be constrained within the function + } + ); + } + + /** + * Render a scattered model, where the source code and library and model dependencies are separated. + * + * @param model + * @param inputModel + */ + render( + model: ConstrainedMetaModel, + inputModel: InputMetaModel + ): Promise { + if (model instanceof ConstrainedObjectModel) { + return this.renderClass(model, inputModel); + } else if (model instanceof ConstrainedEnumModel) { + return this.renderEnum(model, inputModel); + } + Logger.warn( + `Template generator, cannot generate this type of model, ${model.name}` + ); + return Promise.resolve( + RenderOutput.toRenderOutput({ + result: '', + renderedName: '', + dependencies: [] + }) + ); + } + + /** + * Render a complete model result where the model code, library and model dependencies are all bundled appropriately. + * + * For Template you need to specify which package the model is placed under. + * + * @param model + * @param inputModel + * @param options used to render the full output + */ + async renderCompleteModel( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options: TemplateRenderCompleteModelOptions + ): Promise { + if (isReservedTemplateKeyword(options.packageName)) { + throw new Error( + `You cannot use reserved Template keyword (${options.packageName}) as package name, please use another.` + ); + } + + const outputModel = await this.render(model, inputModel); + const modelDependencies = model + .getNearestDependencies() + .map((dependencyModel) => { + return `import ${options.packageName}.${dependencyModel.name};`; + }); + const outputContent = `package ${options.packageName}; +${modelDependencies.join('\n')} +${outputModel.dependencies.join('\n')} +${outputModel.result}`; + return RenderOutput.toRenderOutput({ + result: outputContent, + renderedName: outputModel.renderedName, + dependencies: outputModel.dependencies + }); + } + + async renderClass( + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + options?: Partial + ): Promise { + const optionsToUse = TemplateGenerator.getTemplateOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('class'); + const renderer = new ClassRenderer( + this.options, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runSelfPreset(); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } + + async renderEnum( + model: ConstrainedEnumModel, + inputModel: InputMetaModel, + options?: Partial + ): Promise { + const optionsToUse = TemplateGenerator.getTemplateOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const presets = this.getPresets('enum'); + const renderer = new EnumRenderer( + this.options, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); + const result = await renderer.runSelfPreset(); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); + } +} diff --git a/src/generators/template/TemplatePreset.ts b/src/generators/template/TemplatePreset.ts new file mode 100644 index 0000000000..3beaf6aed4 --- /dev/null +++ b/src/generators/template/TemplatePreset.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/ban-types */ +import { Preset, ClassPreset, EnumPreset } from '../../models'; +import { TemplateOptions } from './TemplateGenerator'; +import { + ClassRenderer, + TEMPLATE_DEFAULT_CLASS_PRESET +} from './renderers/ClassRenderer'; +import { + EnumRenderer, + TEMPLATE_DEFAULT_ENUM_PRESET +} from './renderers/EnumRenderer'; + +export type ClassPresetType = ClassPreset; +export type EnumPresetType = EnumPreset; + +export type TemplatePreset = Preset<{ + class: ClassPresetType; + enum: EnumPresetType; +}>; + +export const TEMPLATE_DEFAULT_PRESET: TemplatePreset = { + class: TEMPLATE_DEFAULT_CLASS_PRESET, + enum: TEMPLATE_DEFAULT_ENUM_PRESET +}; diff --git a/src/generators/template/TemplateRenderer.ts b/src/generators/template/TemplateRenderer.ts new file mode 100644 index 0000000000..8a93013cb0 --- /dev/null +++ b/src/generators/template/TemplateRenderer.ts @@ -0,0 +1,28 @@ +import { AbstractRenderer } from '../AbstractRenderer'; +import { TemplateGenerator, TemplateOptions } from './TemplateGenerator'; +import { ConstrainedMetaModel, InputMetaModel, Preset } from '../../models'; +import { TemplateDependencyManager } from './TemplateDependencyManager'; + +/** + * Common renderer for Template + * + * @extends AbstractRenderer + */ +export abstract class TemplateRenderer< + RendererModelType extends ConstrainedMetaModel +> extends AbstractRenderer< + TemplateOptions, + TemplateGenerator, + RendererModelType +> { + constructor( + options: TemplateOptions, + generator: TemplateGenerator, + presets: Array<[Preset, unknown]>, + model: RendererModelType, + inputModel: InputMetaModel, + public dependencyManager: TemplateDependencyManager + ) { + super(options, generator, presets, model, inputModel); + } +} diff --git a/src/generators/template/constrainer/EnumConstrainer.ts b/src/generators/template/constrainer/EnumConstrainer.ts new file mode 100644 index 0000000000..eec1a43306 --- /dev/null +++ b/src/generators/template/constrainer/EnumConstrainer.ts @@ -0,0 +1,105 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { ConstrainedEnumModel, EnumModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { + FormatHelpers, + EnumKeyConstraint, + EnumValueConstraint +} from '../../../helpers'; +import { isReservedTemplateKeyword } from '../Constants'; + +export type ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_KEYS: ( + constrainedEnumModel: ConstrainedEnumModel, + enumModel: EnumModel, + value: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_' because they are allowed as enum keys + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_KEYS: NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toConstantCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedTemplateKeyword); + } +}; + +/** + * Default constraint logic for Template, which converts the enum key into a key that is compatible with Template + */ +export function defaultEnumKeyConstraints( + customConstraints?: Partial +): EnumKeyConstraint { + const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; + + return ({ enumKey, enumModel, constrainedEnumModel }) => { + let constrainedEnumKey = enumKey; + constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey); + constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey); + //If the enum key has been manipulated, lets make sure it don't clash with existing keys + if (constrainedEnumKey !== enumKey) { + constrainedEnumKey = constraints.NO_DUPLICATE_KEYS( + constrainedEnumModel, + enumModel, + constrainedEnumKey, + constraints.NAMING_FORMATTER! + ); + } + constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey); + return constrainedEnumKey; + }; +} + +/** + * Convert the enum value to a value that is compatible with Template + */ +export function defaultEnumValueConstraints(): EnumValueConstraint { + return ({ enumValue }) => { + let constrainedEnumValue = enumValue; + switch (typeof enumValue) { + case 'string': + case 'boolean': + constrainedEnumValue = `"${enumValue}"`; + break; + case 'bigint': + case 'number': { + constrainedEnumValue = enumValue; + break; + } + case 'object': { + constrainedEnumValue = `"${JSON.stringify(enumValue).replace( + /"/g, + '\\"' + )}"`; + break; + } + default: { + constrainedEnumValue = `"${JSON.stringify(enumValue)}"`; + } + } + return constrainedEnumValue; + }; +} diff --git a/src/generators/template/constrainer/ModelNameConstrainer.ts b/src/generators/template/constrainer/ModelNameConstrainer.ts new file mode 100644 index 0000000000..8f96bc2ef0 --- /dev/null +++ b/src/generators/template/constrainer/ModelNameConstrainer.ts @@ -0,0 +1,53 @@ +import { + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { isReservedTemplateKeyword } from '../Constants'; + +export type ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultModelNameConstraints: ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NAMING_FORMATTER: (value: string) => { + return FormatHelpers.toPascalCase(value); + }, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedTemplateKeyword); + } +}; + +/** + * Default constraint logic for Template, which converts the model name into something that is compatible with Template + */ +export function defaultModelNameConstraints( + customConstraints?: Partial +): ModelNameConstraint { + const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; + + return ({ modelName }) => { + let constrainedValue = modelName; + constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue); + constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue); + constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue); + constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue); + constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); + return constrainedValue; + }; +} diff --git a/src/generators/template/constrainer/PropertyKeyConstrainer.ts b/src/generators/template/constrainer/PropertyKeyConstrainer.ts new file mode 100644 index 0000000000..19ee7546b6 --- /dev/null +++ b/src/generators/template/constrainer/PropertyKeyConstrainer.ts @@ -0,0 +1,80 @@ +import { ConstrainedObjectModel, ObjectModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { isReservedTemplateKeyword } from '../Constants'; + +export type PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_PROPERTIES: ( + constrainedObjectModel: ConstrainedObjectModel, + objectModel: ObjectModel, + propertyName: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toCamelCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedTemplateKeyword); + } +}; +/** + * Default constraint logic for Template, which converts the object property key into something that is compatible with Template + */ +export function defaultPropertyKeyConstraints( + customConstraints?: Partial +): PropertyKeyConstraint { + const constraints = { + ...DefaultPropertyKeyConstraints, + ...customConstraints + }; + + return ({ objectPropertyModel, constrainedObjectModel, objectModel }) => { + let constrainedPropertyKey = objectPropertyModel.propertyName; + + constrainedPropertyKey = constraints.NO_SPECIAL_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey); + constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS( + constrainedPropertyKey + ); + //If the property name has been manipulated, lets make sure it don't clash with existing properties + if (constrainedPropertyKey !== objectPropertyModel.propertyName) { + constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES( + constrainedObjectModel, + objectModel, + constrainedPropertyKey, + constraints.NAMING_FORMATTER + ); + } + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + return constrainedPropertyKey; + }; +} diff --git a/src/generators/template/index.ts b/src/generators/template/index.ts new file mode 100644 index 0000000000..668dd402bc --- /dev/null +++ b/src/generators/template/index.ts @@ -0,0 +1,21 @@ +export * from './TemplateGenerator'; +export * from './TemplateFileGenerator'; +export { TEMPLATE_DEFAULT_PRESET } from './TemplatePreset'; +export type { TemplatePreset } from './TemplatePreset'; +export * from './presets'; + +export { + defaultEnumKeyConstraints as templateDefaultEnumKeyConstraints, + DefaultEnumKeyConstraints as TemplateDefaultEnumKeyConstraints, + defaultEnumValueConstraints as templateDefaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; + +export { + DefaultModelNameConstraints as TemplateDefaultModelNameConstraints, + defaultModelNameConstraints as templateDefaultModelNameConstraints +} from './constrainer/ModelNameConstrainer'; + +export { + DefaultPropertyKeyConstraints as TemplateDefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints as templateDefaultPropertyKeyConstraints +} from './constrainer/PropertyKeyConstrainer'; diff --git a/src/generators/template/presets/DescriptionPreset.ts b/src/generators/template/presets/DescriptionPreset.ts new file mode 100644 index 0000000000..7dca3b0a12 --- /dev/null +++ b/src/generators/template/presets/DescriptionPreset.ts @@ -0,0 +1,25 @@ +import { TemplatePreset } from '../TemplatePreset'; + +/** + * Preset which adds description to rendered model. + * + * @implements {TemplatePreset} + */ +export const TEMPLATE_DESCRIPTION_PRESET: TemplatePreset = { + class: { + self({ content }) { + const renderedDesc = 'my description'; + return `${renderedDesc}\n${content}`; + }, + getter({ content }) { + const renderedDesc = 'my description'; + return `${renderedDesc}\n${content}`; + } + }, + enum: { + self({ content }) { + const renderedDesc = 'my description'; + return `${renderedDesc}\n${content}`; + } + } +}; diff --git a/src/generators/template/presets/index.ts b/src/generators/template/presets/index.ts new file mode 100644 index 0000000000..e74c57b288 --- /dev/null +++ b/src/generators/template/presets/index.ts @@ -0,0 +1 @@ +export * from './DescriptionPreset'; diff --git a/src/generators/template/renderers/ClassRenderer.ts b/src/generators/template/renderers/ClassRenderer.ts new file mode 100644 index 0000000000..1fe3188b46 --- /dev/null +++ b/src/generators/template/renderers/ClassRenderer.ts @@ -0,0 +1,95 @@ +import { TemplateRenderer } from '../TemplateRenderer'; +import { + ConstrainedDictionaryModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel +} from '../../../models'; +import { FormatHelpers } from '../../../helpers'; +import { TemplateOptions } from '../TemplateGenerator'; +import { ClassPresetType } from '../TemplatePreset'; + +/** + * Renderer for Template's `class` type + * + * @extends TemplateRenderer + */ +export class ClassRenderer extends TemplateRenderer { + async defaultSelf(): Promise { + const content = [ + await this.renderProperties(), + await this.runCtorPreset(), + await this.renderAccessors(), + await this.runAdditionalContentPreset() + ]; + + return `public class ${this.model.name} { +${this.indent(this.renderBlock(content, 2))} +}`; + } + + runCtorPreset(): Promise { + return this.runPreset('ctor'); + } + + /** + * Render all the properties for the class. + */ + async renderProperties(): Promise { + const properties = this.model.properties || {}; + const content: string[] = []; + + for (const property of Object.values(properties)) { + const rendererProperty = await this.runPropertyPreset(property); + content.push(rendererProperty); + } + + return this.renderBlock(content); + } + + runPropertyPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('property', { property }); + } + + /** + * Render all the accessors for the properties + */ + async renderAccessors(): Promise { + const properties = this.model.properties || {}; + const content: string[] = []; + + for (const property of Object.values(properties)) { + const getter = await this.runGetterPreset(property); + const setter = await this.runSetterPreset(property); + content.push(this.renderBlock([getter, setter])); + } + + return this.renderBlock(content, 2); + } + + runGetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('getter', { property }); + } + + runSetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('setter', { property }); + } +} + +export const TEMPLATE_DEFAULT_CLASS_PRESET: ClassPresetType = { + self({ renderer }) { + return renderer.defaultSelf(); + }, + property({ property }) { + return `private ${property.property.type} ${property.propertyName};`; + }, + getter({ property }) { + const getterName = `get${FormatHelpers.toPascalCase( + property.propertyName + )}`; + return `public ${property.property.type} ${getterName}() { return this.${property.propertyName}; }`; + }, + setter({ property }) { + const setterName = FormatHelpers.toPascalCase(property.propertyName); + return `public void set${setterName}(${property.property.type} ${property.propertyName}) { this.${property.propertyName} = ${property.propertyName}; }`; + } +}; diff --git a/src/generators/template/renderers/EnumRenderer.ts b/src/generators/template/renderers/EnumRenderer.ts new file mode 100644 index 0000000000..85f3525ecf --- /dev/null +++ b/src/generators/template/renderers/EnumRenderer.ts @@ -0,0 +1,82 @@ +import { TemplateRenderer } from '../TemplateRenderer'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../models'; +import { EnumPresetType } from '../TemplatePreset'; +import { TemplateOptions } from '../TemplateGenerator'; + +/** + * Renderer for Template's `enum` type + * + * @extends TemplateRenderer + */ +export class EnumRenderer extends TemplateRenderer { + async defaultSelf(): Promise { + const content = [ + await this.renderItems(), + await this.runAdditionalContentPreset() + ]; + return `public enum ${this.model.name} { +${this.indent(this.renderBlock(content, 2))} +}`; + } + + async renderItems(): Promise { + const enums = this.model.values || []; + const items: string[] = []; + + for (const value of enums) { + const renderedItem = await this.runItemPreset(value); + items.push(renderedItem); + } + + const content = items.join(', '); + return `${content};`; + } + + runItemPreset(item: ConstrainedEnumValueModel): Promise { + return this.runPreset('item', { item }); + } +} + +export const TEMPLATE_DEFAULT_ENUM_PRESET: EnumPresetType = { + self({ renderer }) { + renderer.dependencyManager.addDependency( + 'import com.fasterxml.jackson.annotation.*;' + ); + return renderer.defaultSelf(); + }, + item({ item }) { + return `${item.key}(${item.value})`; + }, + additionalContent({ model }) { + const enumValueType = 'Object'; + + return `private ${enumValueType} value; + +${model.type}(${enumValueType} value) { + this.value = value; +} + +@JsonValue +public ${enumValueType} getValue() { + return value; +} + +@Override +public String toString() { + return String.valueOf(value); +} + +@JsonCreator +public static ${model.type} fromValue(${enumValueType} value) { + for (${model.type} e : ${model.type}.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); +}`; + } +}; diff --git a/src/generators/typescript/Constants.ts b/src/generators/typescript/Constants.ts index 98b62d6f80..a4c81d4bda 100644 --- a/src/generators/typescript/Constants.ts +++ b/src/generators/typescript/Constants.ts @@ -1,3 +1,4 @@ +import { checkForReservedKeyword } from '../../helpers'; import { isReservedJavaScriptKeyword } from '../javascript/Constants'; export const RESERVED_TYPESCRIPT_KEYWORDS = [ 'break', @@ -65,9 +66,18 @@ export const RESERVED_TYPESCRIPT_KEYWORDS = [ ]; /** - * Not only do we need to check reserved TS keywords, but we have a transitive dependency + * Not only do we need to check reserved TS keywords, but we have a transitive dependency * on JS keywords as well because of potential transpilation process. */ -export function isReservedTypeScriptKeyword(word: string): boolean { - return RESERVED_TYPESCRIPT_KEYWORDS.includes(word) && isReservedJavaScriptKeyword(word); +export function isReservedTypeScriptKeyword( + word: string, + forceLowerCase = true +): boolean { + return ( + checkForReservedKeyword( + word, + RESERVED_TYPESCRIPT_KEYWORDS, + forceLowerCase + ) || isReservedJavaScriptKeyword(word) + ); } diff --git a/src/generators/typescript/TypeScriptConstrainer.ts b/src/generators/typescript/TypeScriptConstrainer.ts new file mode 100644 index 0000000000..2f169a5578 --- /dev/null +++ b/src/generators/typescript/TypeScriptConstrainer.ts @@ -0,0 +1,83 @@ +import { ConstrainedUnionModel } from '../../models'; +import { Logger } from '../../utils'; +import { + defaultEnumKeyConstraints, + defaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; +import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; +import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; +import { TypeScriptTypeMapping } from './TypeScriptGenerator'; + +export const TypeScriptDefaultTypeMapping: TypeScriptTypeMapping = { + Object({ constrainedModel }): string { + return constrainedModel.name; + }, + Reference({ constrainedModel }): string { + return constrainedModel.name; + }, + Any(): string { + return 'any'; + }, + Float(): string { + return 'number'; + }, + Integer(): string { + return 'number'; + }, + String(): string { + return 'string'; + }, + Boolean(): string { + return 'boolean'; + }, + Tuple({ constrainedModel }): string { + const tupleTypes = constrainedModel.tuple.map((constrainedType) => { + return constrainedType.value.type; + }); + return `[${tupleTypes.join(', ')}]`; + }, + Array({ constrainedModel }): string { + let arrayType = constrainedModel.valueModel.type; + if (constrainedModel.valueModel instanceof ConstrainedUnionModel) { + arrayType = `(${arrayType})`; + } + return `${arrayType}[]`; + }, + Enum({ constrainedModel }): string { + return constrainedModel.name; + }, + Union({ constrainedModel }): string { + const unionTypes = constrainedModel.union.map((unionModel) => { + return unionModel.type; + }); + return unionTypes.join(' | '); + }, + Dictionary({ constrainedModel, options }): string { + let keyType; + //There is some restrictions on what can be used as keys for dictionaries. + if (constrainedModel.key instanceof ConstrainedUnionModel) { + Logger.error( + 'Key for dictionary is not allowed to be union type, falling back to any model.' + ); + keyType = 'any'; + } else { + keyType = constrainedModel.key.type; + } + switch (options.mapType) { + case 'indexedObject': + return `{ [name: ${keyType}]: ${constrainedModel.value.type} }`; + case 'record': + return `Record<${keyType}, ${constrainedModel.value.type}>`; + default: + case 'map': + return `Map<${keyType}, ${constrainedModel.value.type}>`; + } + } +}; + +export const TypeScriptDefaultConstraints = { + enumKey: defaultEnumKeyConstraints(), + enumValue: defaultEnumValueConstraints(), + modelName: defaultModelNameConstraints(), + propertyKey: defaultPropertyKeyConstraints() +}; diff --git a/src/generators/typescript/TypeScriptDependencyManager.ts b/src/generators/typescript/TypeScriptDependencyManager.ts new file mode 100644 index 0000000000..d88617be2e --- /dev/null +++ b/src/generators/typescript/TypeScriptDependencyManager.ts @@ -0,0 +1,59 @@ +import { AbstractDependencyManager } from '../AbstractDependencyManager'; +import { renderJavaScriptDependency } from '../../helpers'; +import { ConstrainedMetaModel } from '../../models'; +import { TypeScriptExportType, TypeScriptOptions } from './TypeScriptGenerator'; + +export class TypeScriptDependencyManager extends AbstractDependencyManager { + constructor(public options: TypeScriptOptions, dependencies: string[] = []) { + super(dependencies); + } + + /** + * Simple helper function to add a dependency correct based on the module system that the user defines. + */ + addTypeScriptDependency(toImport: string, fromModule: string): void { + const dependencyImport = this.renderDependency(toImport, fromModule); + this.addDependency(dependencyImport); + } + + /** + * Simple helper function to render a dependency based on the module system that the user defines. + */ + renderDependency(toImport: string, fromModule: string): string { + return renderJavaScriptDependency( + toImport, + fromModule, + this.options.moduleSystem + ); + } + + /** + * Render the model dependencies based on the option + */ + renderCompleteModelDependencies( + model: ConstrainedMetaModel, + exportType: TypeScriptExportType + ): string { + const dependencyObject = + exportType === 'named' ? `{${model.name}}` : model.name; + return this.renderDependency(dependencyObject, `./${model.name}`); + } + + /** + * Render the exported statement for the model based on the options + */ + renderExport( + model: ConstrainedMetaModel, + exportType: TypeScriptExportType + ): string { + const cjsExport = + exportType === 'default' + ? `module.exports = ${model.name};` + : `exports.${model.name} = ${model.name};`; + const esmExport = + exportType === 'default' + ? `export default ${model.name};\n` + : `export { ${model.name} };`; + return this.options.moduleSystem === 'CJS' ? cjsExport : esmExport; + } +} diff --git a/src/generators/typescript/TypeScriptFileGenerator.ts b/src/generators/typescript/TypeScriptFileGenerator.ts index c594f3f511..345937d4e4 100644 --- a/src/generators/typescript/TypeScriptFileGenerator.ts +++ b/src/generators/typescript/TypeScriptFileGenerator.ts @@ -1,24 +1,41 @@ import { TypeScriptGenerator, TypeScriptRenderCompleteModelOptions } from './'; -import { CommonInputModel, OutputModel } from '../../models'; +import { InputMetaModel, OutputModel } from '../../models'; import * as path from 'path'; -import { AbstractFileGenerator } from '../AbstractFileGenerator'; import { FileHelpers } from '../../helpers'; -export class TypeScriptFileGenerator extends TypeScriptGenerator implements AbstractFileGenerator { +export class TypeScriptFileGenerator extends TypeScriptGenerator { /** - * Generates all the models to an output directory each model with their own separate files. - * + * Generates all the models to an output directory each model with their own separate files. + * * @param input * @param outputDirectory where you want the models generated to * @param options + * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ - public async generateToFiles(input: Record | CommonInputModel, outputDirectory: string, options?: TypeScriptRenderCompleteModelOptions): Promise { - let generatedModels = await this.generateCompleteModels(input, options || {}); + public async generateToFiles( + input: Record | InputMetaModel, + outputDirectory: string, + options?: TypeScriptRenderCompleteModelOptions, + ensureFilesWritten = false + ): Promise { + let generatedModels = await this.generateCompleteModels( + input, + options || {} + ); //Filter anything out that have not been successfully generated - generatedModels = generatedModels.filter((outputModel) => { return outputModel.modelName !== ''; }); + generatedModels = generatedModels.filter((outputModel) => { + return outputModel.modelName !== ''; + }); for (const outputModel of generatedModels) { - const filePath = path.resolve(outputDirectory, `${outputModel.modelName}.ts`); - await FileHelpers.writerToFileSystem(outputModel.result, filePath); + const filePath = path.resolve( + outputDirectory, + `${outputModel.modelName}.ts` + ); + await FileHelpers.writerToFileSystem( + outputModel.result, + filePath, + ensureFilesWritten + ); } return generatedModels; } diff --git a/src/generators/typescript/TypeScriptGenerator.ts b/src/generators/typescript/TypeScriptGenerator.ts index 6a74b1e819..251d6ee9e4 100644 --- a/src/generators/typescript/TypeScriptGenerator.ts +++ b/src/generators/typescript/TypeScriptGenerator.ts @@ -1,46 +1,151 @@ -import { hasPreset } from '../../helpers/PresetHelpers'; import { AbstractGenerator, CommonGeneratorOptions, defaultGeneratorOptions } from '../AbstractGenerator'; -import { CommonModel, CommonInputModel, RenderOutput } from '../../models'; -import { TypeHelpers, ModelKind, CommonNamingConvention, CommonNamingConventionImplementation } from '../../helpers'; -import { TS_EXPORT_KEYWORD_PRESET } from './presets'; +import { + ConstrainedEnumModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + InputMetaModel, + MetaModel, + RenderOutput +} from '../../models'; +import { + constrainMetaModel, + Constraints, + split, + SplitOptions, + TypeMapping +} from '../../helpers'; import { TypeScriptPreset, TS_DEFAULT_PRESET } from './TypeScriptPreset'; import { ClassRenderer } from './renderers/ClassRenderer'; import { InterfaceRenderer } from './renderers/InterfaceRenderer'; import { EnumRenderer } from './renderers/EnumRenderer'; import { TypeRenderer } from './renderers/TypeRenderer'; +import { + TypeScriptDefaultConstraints, + TypeScriptDefaultTypeMapping +} from './TypeScriptConstrainer'; +import { DeepPartial, mergePartialAndDefault } from '../../utils'; +import { TypeScriptDependencyManager } from './TypeScriptDependencyManager'; -export interface TypeScriptOptions extends CommonGeneratorOptions { - renderTypes?: boolean; - modelType?: 'class' | 'interface'; - enumType?: 'enum' | 'union'; - namingConvention?: CommonNamingConvention; +export type TypeScriptModuleSystemType = 'ESM' | 'CJS'; +export type TypeScriptExportType = 'named' | 'default'; +export interface TypeScriptOptions + extends CommonGeneratorOptions< + TypeScriptPreset, + TypeScriptDependencyManager + > { + renderTypes: boolean; + modelType: 'class' | 'interface'; + enumType: 'enum' | 'union'; + mapType: 'indexedObject' | 'map' | 'record'; + typeMapping: TypeMapping; + constraints: Constraints; + moduleSystem: TypeScriptModuleSystemType; } +export type TypeScriptTypeMapping = TypeMapping< + TypeScriptOptions, + TypeScriptDependencyManager +>; export interface TypeScriptRenderCompleteModelOptions { - moduleSystem?: 'ESM' | 'CJS'; - exportType?: 'default' | 'named'; + exportType: TypeScriptExportType; } /** * Generator for TypeScript */ -export class TypeScriptGenerator extends AbstractGenerator { +export class TypeScriptGenerator extends AbstractGenerator< + TypeScriptOptions, + TypeScriptRenderCompleteModelOptions +> { static defaultOptions: TypeScriptOptions = { ...defaultGeneratorOptions, renderTypes: true, modelType: 'class', enumType: 'enum', + mapType: 'map', defaultPreset: TS_DEFAULT_PRESET, - namingConvention: CommonNamingConventionImplementation + typeMapping: TypeScriptDefaultTypeMapping, + constraints: TypeScriptDefaultConstraints, + moduleSystem: 'ESM', + // Temporarily set + dependencyManager: () => { + return {} as TypeScriptDependencyManager; + } }; - constructor( - options: TypeScriptOptions = TypeScriptGenerator.defaultOptions, - ) { - super('TypeScript', TypeScriptGenerator.defaultOptions, options); + static defaultCompleteModelOptions: TypeScriptRenderCompleteModelOptions = { + exportType: 'default' + }; + + constructor(options?: DeepPartial) { + const realizedOptions = TypeScriptGenerator.getOptions(options); + super('TypeScript', realizedOptions); + } + + /** + * Returns the TypeScript options by merging custom options with default ones. + */ + static getOptions( + options?: DeepPartial + ): TypeScriptOptions { + const optionsToUse = mergePartialAndDefault( + TypeScriptGenerator.defaultOptions, + options + ) as TypeScriptOptions; + //Always overwrite the dependency manager unless user explicitly state they want it (ignore default temporary dependency manager) + const dependencyManagerOverwritten = + optionsToUse.dependencyManager !== + TypeScriptGenerator.defaultOptions.dependencyManager; + if (!dependencyManagerOverwritten) { + optionsToUse.dependencyManager = () => { + return new TypeScriptDependencyManager(optionsToUse); + }; + } + return optionsToUse; + } + + /** + * Wrapper to get an instance of the dependency manager + */ + getDependencyManager( + options: TypeScriptOptions + ): TypeScriptDependencyManager { + return this.getDependencyManagerInstance( + options + ) as TypeScriptDependencyManager; + } + + splitMetaModel(model: MetaModel): MetaModel[] { + //These are the models that we have separate renderers for + const metaModelsToSplit: SplitOptions = { + splitEnum: true, + splitObject: true + }; + return split(model, metaModelsToSplit); + } + + constrainToMetaModel( + model: MetaModel, + options: DeepPartial + ): ConstrainedMetaModel { + const optionsToUse = TypeScriptGenerator.getOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + return constrainMetaModel( + this.options.typeMapping, + this.options.constraints, + { + metaModel: model, + dependencyManager: dependencyManagerToUse, + options: { ...this.options }, + constrainedName: '' //This is just a placeholder, it will be constrained within the function + } + ); } /** @@ -50,116 +155,182 @@ export class TypeScriptGenerator extends AbstractGenerator { - // Shallow copy presets so that we can restore it once we are done - const originalPresets = [...(this.options.presets ? this.options.presets : [])]; - - // Add preset that adds the `export` keyword if it hasn't already been added - if ( - moduleSystem === 'ESM' && - exportType === 'named' && - !hasPreset(originalPresets, TS_EXPORT_KEYWORD_PRESET) - ) { - this.options.presets = [TS_EXPORT_KEYWORD_PRESET, ...originalPresets]; - } - - const outputModel = await this.render(model, inputModel); - let modelDependencies = model.getNearestDependencies(); - //Ensure model dependencies have their rendered name - modelDependencies = modelDependencies.map((dependencyModelName) => { - return this.options.namingConvention?.type ? this.options.namingConvention.type(dependencyModelName, { inputModel, model: inputModel.models[String(dependencyModelName)] }) : dependencyModelName; + async renderCompleteModel( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + completeModelOptions: Partial, + options: DeepPartial + ): Promise { + const completeModelOptionsToUse = mergePartialAndDefault( + TypeScriptGenerator.defaultCompleteModelOptions, + completeModelOptions + ) as TypeScriptRenderCompleteModelOptions; + const optionsToUse = TypeScriptGenerator.getOptions({ + ...this.options, + ...options }); - //Filter out any dependencies that is recursive to itself - modelDependencies = modelDependencies.filter((dependencyModelName) => { - return dependencyModelName !== outputModel.renderedName; + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); + const outputModel = await this.render(model, inputModel, { + ...optionsToUse, + dependencyManager: dependencyManagerToUse }); + const modelDependencies = model.getNearestDependencies(); - //Create the correct dependency imports - modelDependencies = modelDependencies.map( - (dependencyName) => { - const dependencyObject = - exportType === 'named' ? `{${dependencyName}}` : dependencyName; - - return moduleSystem === 'CJS' - ? `const ${dependencyObject} = require('./${dependencyName}');` - : `import ${dependencyObject} from './${dependencyName}';`; - } + //Create the correct model dependency imports + const modelDependencyImports = modelDependencies.map((model) => { + return dependencyManagerToUse.renderCompleteModelDependencies( + model, + completeModelOptionsToUse.exportType + ); + }); + const modelExport = dependencyManagerToUse.renderExport( + model, + completeModelOptionsToUse.exportType ); - //Ensure we expose the model correctly, based on the module system and export type - const cjsExport = - exportType === 'default' - ? `module.exports = ${outputModel.renderedName};` - : `exports.${outputModel.renderedName} = ${outputModel.renderedName};`; - const esmExport = - exportType === 'default' - ? `export default ${outputModel.renderedName};\n` - : ''; - const modelCode = `${outputModel.result}\n${moduleSystem === 'CJS' ? cjsExport : esmExport}`; - - const outputContent = `${[...modelDependencies, ...outputModel.dependencies].join('\n')} + const modelCode = `${outputModel.result}\n${modelExport}`; + const outputContent = `${[ + ...modelDependencyImports, + ...outputModel.dependencies + ].join('\n')} ${modelCode}`; - // Restore presets array from original copy - this.options.presets = originalPresets; - - return RenderOutput.toRenderOutput({ result: outputContent, renderedName: outputModel.renderedName, dependencies: outputModel.dependencies }); + return RenderOutput.toRenderOutput({ + result: outputContent, + renderedName: outputModel.renderedName, + dependencies: outputModel.dependencies + }); } - render(model: CommonModel, inputModel: CommonInputModel): Promise { - const kind = TypeHelpers.extractKind(model); - switch (kind) { - case ModelKind.OBJECT: { - return this.renderModelType(model, inputModel); - } - case ModelKind.ENUM: { - if (this.options.enumType === 'union') { - return this.renderType(model, inputModel); + /** + * Render any ConstrainedMetaModel to code based on the type + */ + render( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = TypeScriptGenerator.getOptions({ + ...this.options, + ...options + }); + if (model instanceof ConstrainedObjectModel) { + if (this.options.modelType === 'interface') { + return this.renderInterface(model, inputModel, optionsToUse); } - return this.renderEnum(model, inputModel); - } - default: return this.renderType(model, inputModel); + return this.renderClass(model, inputModel, optionsToUse); + } else if (model instanceof ConstrainedEnumModel) { + return this.renderEnum(model, inputModel, optionsToUse); } + return this.renderType(model, inputModel, optionsToUse); } - async renderClass(model: CommonModel, inputModel: CommonInputModel): Promise { + async renderClass( + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = TypeScriptGenerator.getOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const presets = this.getPresets('class'); - const renderer = new ClassRenderer(this.options, this, presets, model, inputModel); + const renderer = new ClassRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = renderer.nameType(model.$id, model); - return RenderOutput.toRenderOutput({result, renderedName, dependencies: renderer.dependencies}); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } - async renderInterface(model: CommonModel, inputModel: CommonInputModel): Promise { + async renderInterface( + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + options?: Partial + ): Promise { + const optionsToUse = TypeScriptGenerator.getOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const presets = this.getPresets('interface'); - const renderer = new InterfaceRenderer(this.options, this, presets, model, inputModel); + const renderer = new InterfaceRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = renderer.nameType(model.$id, model); - return RenderOutput.toRenderOutput({result, renderedName, dependencies: renderer.dependencies}); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } - async renderEnum(model: CommonModel, inputModel: CommonInputModel): Promise { + async renderEnum( + model: ConstrainedEnumModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = TypeScriptGenerator.getOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const presets = this.getPresets('enum'); - const renderer = new EnumRenderer(this.options, this, presets, model, inputModel); + const renderer = new EnumRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = renderer.nameType(model.$id, model); - return RenderOutput.toRenderOutput({result, renderedName, dependencies: renderer.dependencies}); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } - async renderType(model: CommonModel, inputModel: CommonInputModel): Promise { + async renderType( + model: ConstrainedMetaModel, + inputModel: InputMetaModel, + options?: DeepPartial + ): Promise { + const optionsToUse = TypeScriptGenerator.getOptions({ + ...this.options, + ...options + }); + const dependencyManagerToUse = this.getDependencyManager(optionsToUse); const presets = this.getPresets('type'); - const renderer = new TypeRenderer(this.options, this, presets, model, inputModel); + const renderer = new TypeRenderer( + optionsToUse, + this, + presets, + model, + inputModel, + dependencyManagerToUse + ); const result = await renderer.runSelfPreset(); - const renderedName = renderer.nameType(model.$id, model); - return RenderOutput.toRenderOutput({result, renderedName, dependencies: renderer.dependencies}); - } - - private renderModelType(model: CommonModel, inputModel: CommonInputModel): Promise { - const modelType = this.options.modelType; - if (modelType === 'interface') { - return this.renderInterface(model, inputModel); - } - return this.renderClass(model, inputModel); + return RenderOutput.toRenderOutput({ + result, + renderedName: model.name, + dependencies: dependencyManagerToUse.dependencies + }); } } diff --git a/src/generators/typescript/TypeScriptObjectRenderer.ts b/src/generators/typescript/TypeScriptObjectRenderer.ts new file mode 100644 index 0000000000..3a0693c972 --- /dev/null +++ b/src/generators/typescript/TypeScriptObjectRenderer.ts @@ -0,0 +1,52 @@ +import { TypeScriptGenerator, TypeScriptOptions } from './TypeScriptGenerator'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + InputMetaModel, + Preset +} from '../../models'; +import { TypeScriptRenderer } from './TypeScriptRenderer'; +import { TypeScriptDependencyManager } from './TypeScriptDependencyManager'; + +/** + * Common renderer for TypeScript types + * + * @extends AbstractRenderer + */ +export abstract class TypeScriptObjectRenderer extends TypeScriptRenderer { + constructor( + options: TypeScriptOptions, + generator: TypeScriptGenerator, + presets: Array<[Preset, unknown]>, + model: ConstrainedObjectModel, + inputModel: InputMetaModel, + dependencyManager: TypeScriptDependencyManager + ) { + super(options, generator, presets, model, inputModel, dependencyManager); + } + + /** + * Render all the properties for the model by calling the property preset per property. + */ + async renderProperties(): Promise { + const properties = this.model.properties || {}; + const content: string[] = []; + + for (const property of Object.values(properties)) { + const rendererProperty = await this.runPropertyPreset(property); + content.push(rendererProperty); + } + + return this.renderBlock(content); + } + + renderProperty(property: ConstrainedObjectPropertyModel): string { + return `${property.propertyName}${ + property.required === false ? '?' : '' + }: ${property.property.type};`; + } + + runPropertyPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('property', { property }); + } +} diff --git a/src/generators/typescript/TypeScriptPreset.ts b/src/generators/typescript/TypeScriptPreset.ts index 0c7bff54c8..65d3365a11 100644 --- a/src/generators/typescript/TypeScriptPreset.ts +++ b/src/generators/typescript/TypeScriptPreset.ts @@ -1,23 +1,42 @@ -/* eslint-disable @typescript-eslint/ban-types */ -import { Preset, ClassPreset, InterfacePreset, EnumPreset, CommonPreset } from '../../models'; - -import { ClassRenderer, TS_DEFAULT_CLASS_PRESET } from './renderers/ClassRenderer'; -import { InterfaceRenderer, TS_DEFAULT_INTERFACE_PRESET } from './renderers/InterfaceRenderer'; +import { + Preset, + ClassPreset, + InterfacePreset, + EnumPreset, + CommonPreset, + ConstrainedMetaModel +} from '../../models'; +import { + ClassRenderer, + TS_DEFAULT_CLASS_PRESET +} from './renderers/ClassRenderer'; +import { + InterfaceRenderer, + TS_DEFAULT_INTERFACE_PRESET +} from './renderers/InterfaceRenderer'; import { EnumRenderer, TS_DEFAULT_ENUM_PRESET } from './renderers/EnumRenderer'; import { TypeRenderer, TS_DEFAULT_TYPE_PRESET } from './renderers/TypeRenderer'; +import { TypeScriptOptions } from './TypeScriptGenerator'; -export type TypePreset = CommonPreset +export type ClassPresetType = ClassPreset; +export type InterfacePresetType = InterfacePreset; +export type EnumPresetType = EnumPreset; +export type TypePresetType = CommonPreset< + TypeRenderer, + O, + ConstrainedMetaModel +>; -export type TypeScriptPreset = Preset<{ - class: ClassPreset; - interface: InterfacePreset; - enum: EnumPreset; - type: TypePreset; +export type TypeScriptPreset = Preset<{ + class: ClassPresetType; + interface: InterfacePresetType; + enum: EnumPresetType; + type: TypePresetType; }>; -export const TS_DEFAULT_PRESET: TypeScriptPreset = { +export const TS_DEFAULT_PRESET: TypeScriptPreset = { class: TS_DEFAULT_CLASS_PRESET, interface: TS_DEFAULT_INTERFACE_PRESET, enum: TS_DEFAULT_ENUM_PRESET, - type: TS_DEFAULT_TYPE_PRESET, + type: TS_DEFAULT_TYPE_PRESET }; diff --git a/src/generators/typescript/TypeScriptRenderer.ts b/src/generators/typescript/TypeScriptRenderer.ts index 33344ecdd6..bd4bd5a31c 100644 --- a/src/generators/typescript/TypeScriptRenderer.ts +++ b/src/generators/typescript/TypeScriptRenderer.ts @@ -1,176 +1,37 @@ import { AbstractRenderer } from '../AbstractRenderer'; import { TypeScriptGenerator, TypeScriptOptions } from './TypeScriptGenerator'; - import { FormatHelpers } from '../../helpers'; -import { CommonModel, CommonInputModel, Preset, PropertyType } from '../../models'; -import { DefaultPropertyNames, getUniquePropertyName } from '../../helpers/NameHelpers'; -import { isReservedTypeScriptKeyword } from './Constants'; +import { ConstrainedMetaModel, InputMetaModel, Preset } from '../../models'; +import { TypeScriptDependencyManager } from './TypeScriptDependencyManager'; /** * Common renderer for TypeScript types - * + * * @extends AbstractRenderer */ -export abstract class TypeScriptRenderer extends AbstractRenderer { +export abstract class TypeScriptRenderer< + RendererModelType extends ConstrainedMetaModel = ConstrainedMetaModel +> extends AbstractRenderer< + TypeScriptOptions, + TypeScriptGenerator, + RendererModelType +> { constructor( options: TypeScriptOptions, generator: TypeScriptGenerator, presets: Array<[Preset, unknown]>, - model: CommonModel, - inputModel: CommonInputModel, + model: RendererModelType, + inputModel: InputMetaModel, + public dependencyManager: TypeScriptDependencyManager ) { super(options, generator, presets, model, inputModel); } - /** - * Renders the name of a type based on provided generator option naming convention type function. - * - * This is used to render names of models (example TS class) and then later used if that class is referenced from other models. - * - * @param name - * @param model - */ - nameType(name: string | undefined, model?: CommonModel): string { - return this.options?.namingConvention?.type - ? this.options.namingConvention.type(name, { model: model || this.model, inputModel: this.inputModel, reservedKeywordCallback: isReservedTypeScriptKeyword }) - : name || ''; - } - - /** - * Renders the name of a property based on provided generator option naming convention property function. - * - * @param propertyName - * @param property - */ - nameProperty(propertyName: string | undefined, property?: CommonModel): string { - return this.options?.namingConvention?.property - ? this.options.namingConvention.property(propertyName, { model: this.model, inputModel: this.inputModel, property, reservedKeywordCallback: isReservedTypeScriptKeyword }) - : propertyName || ''; - } - - renderType(model: CommonModel | CommonModel[]): string { - if (Array.isArray(model)) { - return model.map(t => this.renderType(t)).join(' | '); - } - if (model.enum !== undefined) { - return model.enum.map(value => typeof value === 'string' ? `"${value}"` : value).join(' | '); - } - if (model.$ref !== undefined) { - return this.nameType(model.$ref); - } - if (Array.isArray(model.type)) { - return [... new Set(model.type.map(t => this.toTsType(t, model)))].join(' | '); - } - return this.toTsType(model.type, model); - } - - /** - * JSON Schema types to TS - * - * @param type - * @param model - */ - toTsType(type: string | undefined, model: CommonModel): string { - switch (type) { - case 'null': - return 'null'; - case 'object': - return 'object'; - case 'string': - return 'string'; - case 'integer': - case 'number': - return 'number'; - case 'boolean': - return 'boolean'; - case 'array': { - //Check and see if it should be rendered as tuples or array - if (Array.isArray(model.items)) { - const types = model.items.map((item) => { - return this.renderType(item); - }); - const additionalTypes = model.additionalItems ? `, ...(${this.renderType(model.additionalItems)})[]` : ''; - return `[${types.join(', ')}${additionalTypes}]`; - } - const arrayType = model.items ? this.renderType(model.items) : 'unknown'; - return `Array<${arrayType}>`; - } - default: return 'any'; - } - } - - renderTypeSignature(type: CommonModel | CommonModel[], { - isRequired = true, - orUndefined = false, - }: { - isRequired?: boolean; - orUndefined?: boolean; - } = {}): string { - if (this.options.renderTypes === false) { - return ''; - } - - const annotation = isRequired ? ':' : '?:'; - let t = this.renderType(type); - t = orUndefined ? `${t} | undefined` : t; - - return `${annotation} ${t}`; - } - renderComments(lines: string | string[]): string { lines = FormatHelpers.breakLines(lines); - const renderedLines = lines.map(line => ` * ${line}`).join('\n'); + const renderedLines = lines.map((line) => ` * ${line}`).join('\n'); return `/** ${renderedLines} */`; } - - /** - * Render all the properties for the model by calling the property preset per property. - */ - async renderProperties(): Promise { - const properties = this.model.properties || {}; - const content: string[] = []; - - for (const [propertyName, property] of Object.entries(properties)) { - const rendererProperty = await this.runPropertyPreset(propertyName, property); - content.push(rendererProperty); - } - - if (this.model.additionalProperties !== undefined) { - const propertyName = getUniquePropertyName(this.model, DefaultPropertyNames.additionalProperties); - const additionalProperty = await this.runPropertyPreset(propertyName, this.model.additionalProperties, PropertyType.additionalProperty); - content.push(additionalProperty); - } - - if (this.model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(this.model.patternProperties)) { - const propertyName = getUniquePropertyName(this.model, `${pattern}${DefaultPropertyNames.patternProperties}`); - const renderedPatternProperty = await this.runPropertyPreset(propertyName, patternModel, PropertyType.patternProperties); - content.push(renderedPatternProperty); - } - } - - return this.renderBlock(content); - } - - renderProperty(propertyName: string, property: CommonModel, type: PropertyType = PropertyType.property): string { - const formattedPropertyName = this.nameProperty(propertyName, property); - let signature: string; - switch (type) { - case PropertyType.property: - signature = this.renderTypeSignature(property, { isRequired: this.model.isRequired(propertyName) }); - return `${formattedPropertyName}${signature};`; - case PropertyType.additionalProperty: - case PropertyType.patternProperties: - signature = this.renderType(property); - return `${formattedPropertyName}?: Map;`; - default: - return ''; - } - } - - runPropertyPreset(propertyName: string, property: CommonModel, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('property', { propertyName, property, type }); - } } diff --git a/src/generators/typescript/constrainer/EnumConstrainer.ts b/src/generators/typescript/constrainer/EnumConstrainer.ts new file mode 100644 index 0000000000..06485c62c3 --- /dev/null +++ b/src/generators/typescript/constrainer/EnumConstrainer.ts @@ -0,0 +1,94 @@ +import { ConstrainedEnumModel, EnumModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { + EnumKeyConstraint, + EnumValueConstraint, + FormatHelpers +} from '../../../helpers'; +import { isReservedTypeScriptKeyword } from '../Constants'; + +export type ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_KEYS: ( + constrainedEnumModel: ConstrainedEnumModel, + enumModel: EnumModel, + value: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude '_', '$' because they are allowed as enum keys + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: ['_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_KEYS: NO_DUPLICATE_ENUM_KEYS, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toConstantCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedTypeScriptKeyword); + } +}; + +export function defaultEnumKeyConstraints( + customConstraints?: Partial +): EnumKeyConstraint { + const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; + + return ({ enumKey, enumModel, constrainedEnumModel }) => { + let constrainedEnumKey = enumKey; + constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey); + constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey); + constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey); + //If the enum key has been manipulated, lets make sure it don't clash with existing keys + if (constrainedEnumKey !== enumKey) { + constrainedEnumKey = constraints.NO_DUPLICATE_KEYS( + constrainedEnumModel, + enumModel, + constrainedEnumKey, + constraints.NAMING_FORMATTER + ); + } + constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey); + return constrainedEnumKey; + }; +} + +export function defaultEnumValueConstraints(): EnumValueConstraint { + return ({ enumValue }) => { + let normalizedEnumValue; + switch (typeof enumValue) { + case 'string': + case 'boolean': + normalizedEnumValue = `"${enumValue}"`; + break; + case 'bigint': + case 'number': { + normalizedEnumValue = `${enumValue}`; + break; + } + case 'object': { + normalizedEnumValue = `'${JSON.stringify(enumValue)}'`; + break; + } + default: { + normalizedEnumValue = String(enumValue); + } + } + return normalizedEnumValue; + }; +} diff --git a/src/generators/typescript/constrainer/ModelNameConstrainer.ts b/src/generators/typescript/constrainer/ModelNameConstrainer.ts new file mode 100644 index 0000000000..935b745db6 --- /dev/null +++ b/src/generators/typescript/constrainer/ModelNameConstrainer.ts @@ -0,0 +1,49 @@ +import { + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { isReservedTypeScriptKeyword } from '../Constants'; + +export type ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultModelNameConstraints: ModelNameConstraints = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NAMING_FORMATTER: (value: string) => { + return FormatHelpers.toPascalCase(value); + }, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedTypeScriptKeyword); + } +}; +export function defaultModelNameConstraints( + customConstraints?: Partial +): ModelNameConstraint { + const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; + + return ({ modelName }) => { + let constrainedValue = modelName; + constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue); + constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue); + constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue); + constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue); + constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); + return constrainedValue; + }; +} diff --git a/src/generators/typescript/constrainer/PropertyKeyConstrainer.ts b/src/generators/typescript/constrainer/PropertyKeyConstrainer.ts new file mode 100644 index 0000000000..ca130ad858 --- /dev/null +++ b/src/generators/typescript/constrainer/PropertyKeyConstrainer.ts @@ -0,0 +1,77 @@ +import { ConstrainedObjectModel, ObjectModel } from '../../../models'; +import { + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NO_RESERVED_KEYWORDS +} from '../../../helpers/Constraints'; +import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { isReservedTypeScriptKeyword } from '../Constants'; + +export type PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => string; + NO_NUMBER_START_CHAR: (value: string) => string; + NO_DUPLICATE_PROPERTIES: ( + constrainedObjectModel: ConstrainedObjectModel, + objectModel: ObjectModel, + propertyName: string, + namingFormatter: (value: string) => string + ) => string; + NO_EMPTY_VALUE: (value: string) => string; + NAMING_FORMATTER: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string) => string; +}; + +export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { + NO_SPECIAL_CHAR: (value: string) => { + //Exclude ` ` because it gets formatted by NAMING_FORMATTER + //Exclude '_', '$' because they are allowed + return FormatHelpers.replaceSpecialCharacters(value, { + exclude: [' ', '_', '$'], + separator: '_' + }); + }, + NO_NUMBER_START_CHAR, + NO_DUPLICATE_PROPERTIES, + NO_EMPTY_VALUE, + NAMING_FORMATTER: FormatHelpers.toCamelCase, + NO_RESERVED_KEYWORDS: (value: string) => { + return NO_RESERVED_KEYWORDS(value, isReservedTypeScriptKeyword); + } +}; +export function defaultPropertyKeyConstraints( + customConstraints?: Partial +): PropertyKeyConstraint { + const constraints = { + ...DefaultPropertyKeyConstraints, + ...customConstraints + }; + + return ({ objectPropertyModel, constrainedObjectModel, objectModel }) => { + let constrainedPropertyKey = objectPropertyModel.propertyName; + + constrainedPropertyKey = constraints.NO_SPECIAL_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR( + constrainedPropertyKey + ); + constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey); + constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS( + constrainedPropertyKey + ); + //If the property name has been manipulated, lets make sure it don't clash with existing properties + if (constrainedPropertyKey !== objectPropertyModel.propertyName) { + constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES( + constrainedObjectModel, + objectModel, + constrainedPropertyKey, + constraints.NAMING_FORMATTER + ); + } + constrainedPropertyKey = constraints.NAMING_FORMATTER( + constrainedPropertyKey + ); + return constrainedPropertyKey; + }; +} diff --git a/src/generators/typescript/index.ts b/src/generators/typescript/index.ts index 99b8ed1cf9..2861978502 100644 --- a/src/generators/typescript/index.ts +++ b/src/generators/typescript/index.ts @@ -3,3 +3,19 @@ export * from './TypeScriptFileGenerator'; export { TS_DEFAULT_PRESET } from './TypeScriptPreset'; export type { TypeScriptPreset } from './TypeScriptPreset'; export * from './presets'; + +export { + defaultEnumKeyConstraints as typeScriptDefaultEnumKeyConstraints, + DefaultEnumKeyConstraints as TypeScriptDefaultEnumKeyConstraints, + defaultEnumValueConstraints as typeScriptDefaultEnumValueConstraints +} from './constrainer/EnumConstrainer'; + +export { + DefaultModelNameConstraints as TypeScriptDefaultModelNameConstraints, + defaultModelNameConstraints as typeScriptDefaultModelNameConstraints +} from './constrainer/ModelNameConstrainer'; + +export { + DefaultPropertyKeyConstraints as TypeScriptDefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints as typeScriptDefaultPropertyKeyConstraints +} from './constrainer/PropertyKeyConstrainer'; diff --git a/src/generators/typescript/presets/CommonPreset.ts b/src/generators/typescript/presets/CommonPreset.ts index 3512eba5bf..df91581020 100644 --- a/src/generators/typescript/presets/CommonPreset.ts +++ b/src/generators/typescript/presets/CommonPreset.ts @@ -1,8 +1,13 @@ -import { TypeScriptRenderer } from '../TypeScriptRenderer'; import { TypeScriptPreset } from '../TypeScriptPreset'; -import { getUniquePropertyName, DefaultPropertyNames, TypeHelpers, ModelKind } from '../../../helpers'; -import { CommonInputModel, CommonModel } from '../../../models'; +import { + ConstrainedObjectModel, + ConstrainedDictionaryModel, + ConstrainedReferenceModel, + ConstrainedMetaModel, + ConstrainedEnumModel +} from '../../../models'; import renderExampleFunction from './utils/ExampleFunction'; +import { ClassRenderer } from '../renderers/ClassRenderer'; export interface TypeScriptCommonPresetOptions { marshalling: boolean; @@ -12,202 +17,211 @@ export interface TypeScriptCommonPresetOptions { function realizePropertyFactory(prop: string) { return `$\{typeof ${prop} === 'number' || typeof ${prop} === 'boolean' ? ${prop} : JSON.stringify(${prop})}`; } -function renderMarshalProperty(modelInstanceVariable: string, model: CommonModel, inputModel: CommonInputModel) { - if (model.$ref) { - const resolvedModel = inputModel.models[model.$ref]; - const propertyModelKind = TypeHelpers.extractKind(resolvedModel); - //Referenced enums only need standard marshalling, so lets filter those away - if (propertyModelKind !== ModelKind.ENUM) { - return `$\{${modelInstanceVariable}.marshal()}`; - } + +function renderMarshalProperty( + modelInstanceVariable: string, + model: ConstrainedMetaModel +) { + if ( + model instanceof ConstrainedReferenceModel && + !(model.ref instanceof ConstrainedEnumModel) + ) { + return `$\{${modelInstanceVariable}.marshal()}`; } return realizePropertyFactory(modelInstanceVariable); } -function renderMarshalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { +function renderMarshalProperties(model: ConstrainedObjectModel) { const properties = model.properties || {}; const propertyKeys = [...Object.entries(properties)]; - const marshalProperties = propertyKeys.map(([prop, propModel]) => { - const formattedPropertyName = renderer.nameProperty(prop, propModel); - const modelInstanceVariable = `this.${formattedPropertyName}`; - const propMarshalCode = renderMarshalProperty(modelInstanceVariable, propModel, inputModel); - const marshalCode = `json += \`"${prop}": ${propMarshalCode},\`;`; + + //These are a bit special as 'unwrap' dictionary models means they have to be unwrapped within the JSON object. + const unwrapDictionaryProperties = []; + const normalProperties = []; + for (const entry of propertyKeys) { + if ( + entry[1].property instanceof ConstrainedDictionaryModel && + entry[1].property.serializationType === 'unwrap' + ) { + unwrapDictionaryProperties.push(entry); + } else { + normalProperties.push(entry); + } + } + + const marshalNormalProperties = normalProperties.map(([prop, propModel]) => { + const modelInstanceVariable = `this.${prop}`; + const propMarshalCode = renderMarshalProperty( + modelInstanceVariable, + propModel.property + ); + const marshalCode = `json += \`"${propModel.unconstrainedPropertyName}": ${propMarshalCode},\`;`; return `if(${modelInstanceVariable} !== undefined) { ${marshalCode} }`; }); - return marshalProperties.join('\n'); -} -function renderMarshalPatternProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { - let marshalPatternProperties = ''; - if (model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(model.patternProperties)) { - let patternPropertyName = getUniquePropertyName(model, `${pattern}${DefaultPropertyNames.patternProperties}`); - patternPropertyName = renderer.nameProperty(patternPropertyName, patternModel); + const marshalUnwrapDictionaryProperties = unwrapDictionaryProperties.map( + ([prop, propModel]) => { const modelInstanceVariable = 'value'; - const patternPropertyMarshalCode = renderMarshalProperty(modelInstanceVariable, patternModel, inputModel); + const patternPropertyMarshalCode = renderMarshalProperty( + modelInstanceVariable, + (propModel.property as ConstrainedDictionaryModel).value + ); const marshalCode = `json += \`"$\{key}": ${patternPropertyMarshalCode},\`;`; - marshalPatternProperties += `if(this.${patternPropertyName} !== undefined) { - for (const [key, value] of this.${patternPropertyName}.entries()) { - //Only render pattern properties which are not already a property - if(Object.keys(this).includes(String(key))) continue; + return `if(this.${prop} !== undefined) { +for (const [key, value] of this.${prop}.entries()) { + //Only unwrap those who are not already a property in the JSON object + if(Object.keys(this).includes(String(key))) continue; ${marshalCode} } }`; } - } - return marshalPatternProperties; -} + ); -function renderMarshalAdditionalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { - let marshalAdditionalProperties = ''; - if (model.additionalProperties !== undefined) { - let additionalPropertyName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties); - additionalPropertyName = renderer.nameProperty(additionalPropertyName, model.additionalProperties); - const modelInstanceVariable = 'value'; - const patternPropertyMarshalCode = renderMarshalProperty(modelInstanceVariable, model.additionalProperties, inputModel); - const marshalCode = `json += \`"$\{key}": ${patternPropertyMarshalCode},\`;`; - marshalAdditionalProperties = `if(this.${additionalPropertyName} !== undefined) { - for (const [key, value] of this.${additionalPropertyName}.entries()) { - //Only render additionalProperties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - ${marshalCode} - } -}`; - } - return marshalAdditionalProperties; + return ` +${marshalNormalProperties.join('\n')} +${marshalUnwrapDictionaryProperties.join('\n')} +`; } /** * Render `marshal` function based on model */ -function renderMarshal({ renderer, model, inputModel }: { - renderer: TypeScriptRenderer, - model: CommonModel, - inputModel: CommonInputModel +function renderMarshal({ + renderer, + model +}: { + renderer: ClassRenderer; + model: ConstrainedObjectModel; }): string { return `public marshal() : string { let json = '{' -${renderer.indent(renderMarshalProperties(model, renderer, inputModel))} -${renderer.indent(renderMarshalPatternProperties(model, renderer, inputModel))} -${renderer.indent(renderMarshalAdditionalProperties(model, renderer, inputModel))} - +${renderer.indent(renderMarshalProperties(model))} //Remove potential last comma return \`$\{json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; }`; } -function renderUnmarshalProperty(modelInstanceVariable: string, model: CommonModel, inputModel: CommonInputModel, renderer: TypeScriptRenderer) { - if (model.$ref) { - const resolvedModel = inputModel.models[model.$ref]; - const propertyModelKind = TypeHelpers.extractKind(resolvedModel); - //Referenced enums only need standard marshalling, so lets filter those away - if (propertyModelKind !== ModelKind.ENUM) { - return `${renderer.nameType(model.$ref)}.unmarshal(${modelInstanceVariable})`; - } +function renderUnmarshalProperty( + modelInstanceVariable: string, + model: ConstrainedMetaModel +) { + if ( + model instanceof ConstrainedReferenceModel && + !(model.ref instanceof ConstrainedEnumModel) + ) { + return `${model.type}.unmarshal(${modelInstanceVariable})`; } return `${modelInstanceVariable}`; } -function renderUnmarshalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { +function renderUnmarshalProperties(model: ConstrainedObjectModel) { const properties = model.properties || {}; const propertyKeys = [...Object.entries(properties)]; - const unmarshalProperties = propertyKeys.map(([prop, propModel]) => { - const formattedPropertyName = renderer.nameProperty(prop, propModel); - const modelInstanceVariable = `obj["${prop}"]`; - const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, propModel, inputModel, renderer); - return `if (${modelInstanceVariable} !== undefined) { - instance.${formattedPropertyName} = ${unmarshalCode}; -}`; + const propertyNames = propertyKeys.map(([name]) => { + return name; }); - return unmarshalProperties.join('\n'); -} + //These are a bit special as 'unwrap' dictionary models means they have to be unwrapped within the JSON object. + const unwrapDictionaryProperties = []; + const normalProperties = []; + for (const entry of propertyKeys) { + if ( + entry[1].property instanceof ConstrainedDictionaryModel && + entry[1].property.serializationType === 'unwrap' + ) { + unwrapDictionaryProperties.push(entry); + } else { + normalProperties.push(entry); + } + } -function renderUnmarshalPatternProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { - let unmarshalPatternProperties = ''; - let setPatternPropertiesMap = ''; - if (model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(model.patternProperties)) { - let patternPropertyName = getUniquePropertyName(model, `${pattern}${DefaultPropertyNames.patternProperties}`); - patternPropertyName = renderer.nameProperty(patternPropertyName, patternModel); - const modelInstanceVariable = 'value as any'; - const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, patternModel, inputModel, renderer); - setPatternPropertiesMap += `if (instance.${patternPropertyName} === undefined) {instance.${patternPropertyName} = new Map();}\n`; - unmarshalPatternProperties += `//Check all pattern properties -if (key.match(new RegExp('${pattern}'))) { - instance.${patternPropertyName}.set(key, ${unmarshalCode}); - continue; + const unmarshalNormalProperties = normalProperties.map( + ([prop, propModel]) => { + const modelInstanceVariable = `obj["${propModel.unconstrainedPropertyName}"]`; + const unmarshalCode = renderUnmarshalProperty( + modelInstanceVariable, + propModel.property + ); + return `if (${modelInstanceVariable} !== undefined) { + instance.${prop} = ${unmarshalCode}; }`; } - } - return { unmarshalPatternProperties, setPatternPropertiesMap }; -} + ); -function renderUnmarshalAdditionalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { - let unmarshalAdditionalProperties = ''; - let setAdditionalPropertiesMap = ''; - if (model.additionalProperties !== undefined) { - let additionalPropertyName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties); - additionalPropertyName = renderer.nameProperty(additionalPropertyName, model.additionalProperties); + const setDictionaryProperties = []; + const unmarshalDictionaryProperties = []; + for (const [prop, propModel] of unwrapDictionaryProperties) { const modelInstanceVariable = 'value as any'; - const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, model.additionalProperties, inputModel, renderer); - setAdditionalPropertiesMap = `if (instance.${additionalPropertyName} === undefined) {instance.${additionalPropertyName} = new Map();}`; - unmarshalAdditionalProperties = `instance.${additionalPropertyName}.set(key, ${unmarshalCode});`; + const unmarshalCode = renderUnmarshalProperty( + modelInstanceVariable, + (propModel.property as ConstrainedDictionaryModel).value + ); + setDictionaryProperties.push( + `if (instance.${prop} === undefined) {instance.${prop} = new Map();}` + ); + unmarshalDictionaryProperties.push( + `instance.${prop}.set(key, ${unmarshalCode});` + ); } - return { unmarshalAdditionalProperties, setAdditionalPropertiesMap }; + const corePropertyKeys = propertyNames + .map((propertyKey) => `"${propertyKey}"`) + .join(','); + const unwrappedDictionaryCode = + setDictionaryProperties.length > 0 + ? `${setDictionaryProperties.join('\n')} + for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![${corePropertyKeys}].includes(key);}))) { + ${unmarshalDictionaryProperties.join('\n')} + }` + : ''; + + return ` +${unmarshalNormalProperties.join('\n')} + +${unwrappedDictionaryCode} +`; } /** * Render `unmarshal` function based on model */ -function renderUnmarshal({ renderer, model, inputModel }: { - renderer: TypeScriptRenderer, - model: CommonModel, - inputModel: CommonInputModel +function renderUnmarshal({ + renderer, + model +}: { + renderer: ClassRenderer; + model: ConstrainedObjectModel; }): string { - const properties = model.properties || {}; - const { unmarshalPatternProperties, setPatternPropertiesMap } = renderUnmarshalPatternProperties(model, renderer, inputModel); - const { unmarshalAdditionalProperties, setAdditionalPropertiesMap } = renderUnmarshalAdditionalProperties(model, renderer, inputModel); - const unmarshalProperties = renderUnmarshalProperties(model, renderer, inputModel); - const formattedModelName = renderer.nameType(model.$id); - const propertyNames = Object.keys(properties).map((prop => `"${prop}"`)); - return `public static unmarshal(json: string | object): ${formattedModelName} { + const unmarshalProperties = renderUnmarshalProperties(model); + return `public static unmarshal(json: string | object): ${model.type} { const obj = typeof json === "object" ? json : JSON.parse(json); - const instance = new ${formattedModelName}({} as any); + const instance = new ${model.type}({} as any); ${renderer.indent(unmarshalProperties)} - - //Not part of core properties - ${setPatternPropertiesMap} - ${setAdditionalPropertiesMap} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![${propertyNames}].includes(key);}))) { -${renderer.indent(unmarshalPatternProperties, 4)} -${renderer.indent(unmarshalAdditionalProperties, 4)} - } return instance; }`; } /** - * Preset which adds `marshal`, `unmarshal`, `example` functions to class. - * + * Preset which adds `marshal`, `unmarshal`, `example` functions to class. + * * @implements {TypeScriptPreset} */ -export const TS_COMMON_PRESET: TypeScriptPreset = { - class: { - additionalContent({ renderer, model, content, options, inputModel }) { - options = options || {}; - const blocks: string[] = []; - - if (options.marshalling === true) { - blocks.push(renderMarshal({ renderer, model, inputModel })); - blocks.push(renderUnmarshal({ renderer, model, inputModel })); - } - - if (options.example === true) { - blocks.push(renderExampleFunction({ renderer, model })); +export const TS_COMMON_PRESET: TypeScriptPreset = + { + class: { + additionalContent({ renderer, model, content, options }) { + options = options || {}; + const blocks: string[] = []; + + if (options.marshalling === true) { + blocks.push(renderMarshal({ renderer, model })); + blocks.push(renderUnmarshal({ renderer, model })); + } + + if (options.example === true) { + blocks.push(renderExampleFunction({ model })); + } + + return renderer.renderBlock([content, ...blocks], 2); } - - return renderer.renderBlock([content, ...blocks], 2); - }, - } -}; + } + }; diff --git a/src/generators/typescript/presets/DescriptionPreset.ts b/src/generators/typescript/presets/DescriptionPreset.ts index d3c112036f..a11612c030 100644 --- a/src/generators/typescript/presets/DescriptionPreset.ts +++ b/src/generators/typescript/presets/DescriptionPreset.ts @@ -1,18 +1,18 @@ -import { CommonModel } from '../../../models'; +import { ConstrainedMetaModel } from '../../../models'; import { TypeScriptPreset } from '../TypeScriptPreset'; import { TypeScriptRenderer } from '../TypeScriptRenderer'; const renderDescription = ({ renderer, content, - item, + item }: { - renderer: TypeScriptRenderer; + renderer: TypeScriptRenderer; content: string; - item: CommonModel; + item: ConstrainedMetaModel; }): string => { - const desc = item.getFromOriginalInput('description')?.trim(); - const examples = item.getFromOriginalInput('examples'); + const desc = item.originalInput.description?.trim(); + const examples = item.originalInput.examples; const formattedExamples = `@example ${ examples?.join ? examples.join(', ') : examples }`; @@ -38,7 +38,7 @@ export const TS_DESCRIPTION_PRESET: TypeScriptPreset = { return renderDescription({ renderer, content, item: model }); }, property({ renderer, property, content }) { - return renderDescription({ renderer, content, item: property }); + return renderDescription({ renderer, content, item: property.property }); } }, interface: { @@ -46,17 +46,17 @@ export const TS_DESCRIPTION_PRESET: TypeScriptPreset = { return renderDescription({ renderer, content, item: model }); }, property({ renderer, property, content }) { - return renderDescription({ renderer, content, item: property }); + return renderDescription({ renderer, content, item: property.property }); } }, type: { self({ renderer, model, content }) { return renderDescription({ renderer, content, item: model }); - }, + } }, enum: { self({ renderer, model, content }) { return renderDescription({ renderer, content, item: model }); - }, - }, + } + } }; diff --git a/src/generators/typescript/presets/ExportKeywordPreset.ts b/src/generators/typescript/presets/ExportKeywordPreset.ts deleted file mode 100644 index bfe227ea42..0000000000 --- a/src/generators/typescript/presets/ExportKeywordPreset.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { CommonModel } from '../../../models'; -import { TypeScriptPreset } from '../TypeScriptPreset'; -import { TypeScriptRenderer } from '../TypeScriptRenderer'; - -const renderWithExportKeyword = ({ - content, -}: { - renderer: TypeScriptRenderer; - content: string; - item: CommonModel; -}): string => `export ${content}`; - -/** - * Preset which adds export keyword wherever applicable (named exports) - * - * @implements {TypeScriptPreset} - */ -export const TS_EXPORT_KEYWORD_PRESET: TypeScriptPreset = { - class: { - self({ renderer, model, content }) { - return renderWithExportKeyword({ renderer, content, item: model }); - }, - }, - interface: { - self({ renderer, model, content }) { - return renderWithExportKeyword({ renderer, content, item: model }); - }, - }, - type: { - self({ renderer, model, content }) { - return renderWithExportKeyword({ renderer, content, item: model }); - }, - }, - enum: { - self({ renderer, model, content }) { - return renderWithExportKeyword({ renderer, content, item: model }); - }, - }, -}; diff --git a/src/generators/typescript/presets/JsonBinPackPreset.ts b/src/generators/typescript/presets/JsonBinPackPreset.ts new file mode 100644 index 0000000000..21452e1c5a --- /dev/null +++ b/src/generators/typescript/presets/JsonBinPackPreset.ts @@ -0,0 +1,55 @@ +import { TypeScriptPreset } from '../TypeScriptPreset'; +// eslint-disable-next-line @typescript-eslint/no-var-requires,no-undef +const alterschema = require('alterschema'); + +function getInputSchema(originalInput: any): string { + if (originalInput.$schema !== undefined) { + if ( + originalInput.$schema.includes('http://json-schema.org/draft-04/schema') + ) { + return 'draft4'; + } + if ( + originalInput.$schema.includes('http://json-schema.org/draft-06/schema') + ) { + return 'draft6'; + } + } + return 'draft7'; +} + +/** + * Preset which adds jsonbinpack marshalling/unmarshalling methods + * + * @implements {TypeScriptPreset} + */ +export const TS_JSONBINPACK_PRESET: TypeScriptPreset = { + class: { + async additionalContent({ renderer, content, model }) { + renderer.dependencyManager.addTypeScriptDependency( + 'jsonbinpack', + 'jsonbinpack' + ); + + const jsonSchema = await alterschema( + model.originalInput, + getInputSchema(model.originalInput), + '2020-12' + ); + const json = JSON.stringify(jsonSchema); + const packContent = ` +public async jsonbinSerialize(): Promise{ + const jsonData = JSON.parse(this.marshal()); + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema(${json}); + return jsonbinpack.serialize(jsonbinpackEncodedSchema, jsonData); +} + +public static async jsonbinDeserialize(buffer: Buffer): Promise<${model.name}> { + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema(${json}); + const json = jsonbinpack.deserialize(jsonbinpackEncodedSchema, buffer); + return ${model.name}.unmarshal(json); +}`; + return `${content}\n${packContent}`; + } + } +}; diff --git a/src/generators/typescript/presets/index.ts b/src/generators/typescript/presets/index.ts index 5d9603b4fa..db80ff18e0 100644 --- a/src/generators/typescript/presets/index.ts +++ b/src/generators/typescript/presets/index.ts @@ -1,3 +1,3 @@ export * from './CommonPreset'; -export * from './ExportKeywordPreset'; export * from './DescriptionPreset'; +export * from './JsonBinPackPreset'; diff --git a/src/generators/typescript/presets/utils/ExampleFunction.ts b/src/generators/typescript/presets/utils/ExampleFunction.ts index 0cf6d0f2a4..fd9a0437c9 100644 --- a/src/generators/typescript/presets/utils/ExampleFunction.ts +++ b/src/generators/typescript/presets/utils/ExampleFunction.ts @@ -1,79 +1,76 @@ -import { TypeScriptRenderer } from '../../TypeScriptRenderer'; -import { CommonModel } from '../../../../models'; +import { + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedEnumModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedUnionModel +} from '../../../../models'; /** * Inferring first acceptable value from the model. - * - * @param model - * @param renderer + * + * @param model */ -export function renderValueFromModel(model: CommonModel, renderer: TypeScriptRenderer): string | undefined { - if (Array.isArray(model.enum) && model.enum.length > 0) { - return JSON.stringify(model.enum[0]); - } - if (model.$ref !== undefined) { - return `${renderer.nameType(model.$ref)}.example()`; - } - if (Array.isArray(model.type)) { - if (model.type.length > 0) { - return renderValueFromType(model.type[0], model, renderer); - } - return undefined; - } - return renderValueFromType(model.type, model, renderer); -} - -export function renderValueFromType(modelType: string | undefined, model: CommonModel, renderer: TypeScriptRenderer): string | undefined { - if (modelType === undefined) { - return undefined; - } - switch (modelType) { - case 'string': +export function renderValueFromModel( + model: ConstrainedMetaModel +): string | undefined { + if (model instanceof ConstrainedEnumModel && model.values.length > 0) { + //Greedy example + return model.values[0].value; + } else if (model instanceof ConstrainedReferenceModel) { + return `${model.name}.example()`; + } else if (model instanceof ConstrainedUnionModel && model.union.length > 0) { + //Greedy example + return renderValueFromModel(model.union[0]); + } else if (model instanceof ConstrainedArrayModel) { + const arrayType = renderValueFromModel(model.valueModel); + return `[${arrayType}]`; + } else if (model instanceof ConstrainedTupleModel && model.tuple.length > 0) { + const values = model.tuple.map((tupleModel) => { + return renderValueFromModel(tupleModel.value); + }); + return `[${values.join(',')}]`; + } else if (model instanceof ConstrainedStringModel) { return '"string"'; - case 'integer': - case 'number': + } else if (model instanceof ConstrainedIntegerModel) { return '0'; - case 'boolean': + } else if (model instanceof ConstrainedFloatModel) { + return '0.0'; + } else if (model instanceof ConstrainedBooleanModel) { return 'true'; - case 'array': { - if (model.items === undefined) { - return '[]'; - } - //Check and see if it should be rendered as tuples - if (Array.isArray(model.items)) { - const arrayValues = model.items.map((item) => { - return renderValueFromModel(item, renderer); - }); - return `[${arrayValues.join(', ')}]`; - } - const arrayType = renderValueFromModel(model.items, renderer); - return `[${arrayType}]`; - } } return undefined; } + /** * Render `example` function based on model properties. */ -export default function renderExampleFunction({ renderer, model }: { - renderer: TypeScriptRenderer, - model: CommonModel, +export default function renderExampleFunction({ + model +}: { + model: ConstrainedObjectModel; }): string { const properties = model.properties || {}; const setProperties = []; for (const [propertyName, property] of Object.entries(properties)) { - const formattedPropertyName = renderer.nameProperty(propertyName, property); - const potentialRenderedValue = renderValueFromModel(property, renderer); + const potentialRenderedValue = renderValueFromModel(property.property); if (potentialRenderedValue === undefined) { //Unable to determine example value, skip property. continue; } - setProperties.push(` instance.${formattedPropertyName} = ${potentialRenderedValue};`); + setProperties.push( + ` instance.${propertyName} = ${potentialRenderedValue};` + ); } - const formattedModelName = renderer.nameType(model.$id); - return `public static example(): ${formattedModelName} { - const instance = new ${formattedModelName}({} as any); -${(setProperties.join('\n'))} + return `public static example(): ${model.type} { + const instance = new ${model.type}({} as any); +${setProperties.join('\n')} return instance; }`; } diff --git a/src/generators/typescript/renderers/ClassRenderer.ts b/src/generators/typescript/renderers/ClassRenderer.ts index a3f992007a..2d65c944f0 100644 --- a/src/generators/typescript/renderers/ClassRenderer.ts +++ b/src/generators/typescript/renderers/ClassRenderer.ts @@ -1,13 +1,14 @@ -import { TypeScriptRenderer } from '../TypeScriptRenderer'; -import { CommonModel, ClassPreset, PropertyType } from '../../../models'; -import { getUniquePropertyName, DefaultPropertyNames } from '../../../helpers'; +import { ConstrainedObjectPropertyModel } from '../../../models'; +import { TypeScriptOptions } from '../TypeScriptGenerator'; +import { TypeScriptObjectRenderer } from '../TypeScriptObjectRenderer'; +import { ClassPresetType } from '../TypeScriptPreset'; /** * Renderer for TypeScript's `class` type - * + * * @extends TypeScriptRenderer */ -export class ClassRenderer extends TypeScriptRenderer { +export class ClassRenderer extends TypeScriptObjectRenderer { public async defaultSelf(): Promise { const content = [ await this.renderProperties(), @@ -16,8 +17,7 @@ export class ClassRenderer extends TypeScriptRenderer { await this.runAdditionalContentPreset() ]; - const formattedName = this.nameType(this.model.$id); - return `class ${formattedName} { + return `class ${this.model.name} { ${this.indent(this.renderBlock(content, 2))} }`; } @@ -27,54 +27,38 @@ ${this.indent(this.renderBlock(content, 2))} } async renderAccessors(): Promise { - const properties = this.model.properties || {}; + const properties = this.model.properties; const content: string[] = []; - for (const [propertyName, property] of Object.entries(properties)) { - const getter = await this.runGetterPreset(propertyName, property); - const setter = await this.runSetterPreset(propertyName, property); + for (const property of Object.values(properties)) { + const getter = await this.runGetterPreset(property); + const setter = await this.runSetterPreset(property); content.push(this.renderBlock([getter, setter])); } - if (this.model.additionalProperties !== undefined) { - const propertyName = getUniquePropertyName(this.model, DefaultPropertyNames.additionalProperties); - const getter = await this.runGetterPreset(propertyName, this.model.additionalProperties, PropertyType.additionalProperty); - const setter = await this.runSetterPreset(propertyName, this.model.additionalProperties, PropertyType.additionalProperty); - content.push(this.renderBlock([getter, setter])); - } - - if (this.model.patternProperties !== undefined) { - for (const [pattern, patternModel] of Object.entries(this.model.patternProperties)) { - const propertyName = getUniquePropertyName(this.model, `${pattern}${DefaultPropertyNames.patternProperties}`); - const getter = await this.runGetterPreset(propertyName, patternModel, PropertyType.patternProperties); - const setter = await this.runSetterPreset(propertyName, patternModel, PropertyType.patternProperties); - content.push(this.renderBlock([getter, setter])); - } - } return this.renderBlock(content, 2); } - runGetterPreset(propertyName: string, property: CommonModel, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('getter', { propertyName, property, type }); + runGetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('getter', { property }); } - runSetterPreset(propertyName: string, property: CommonModel, type: PropertyType = PropertyType.property): Promise { - return this.runPreset('setter', { propertyName, property, type }); + runSetterPreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('setter', { property }); } } -export const TS_DEFAULT_CLASS_PRESET: ClassPreset = { +export const TS_DEFAULT_CLASS_PRESET: ClassPresetType = { self({ renderer }) { return renderer.defaultSelf(); }, - ctor({ renderer, model }) : string { + ctor({ renderer, model }): string { const properties = model.properties || {}; - const assignments = Object.entries(properties).map(([propertyName, property]) => { - propertyName = renderer.nameProperty(propertyName, property); + const assignments = Object.keys(properties).map((propertyName) => { return `this._${propertyName} = input.${propertyName};`; }); - const ctorProperties = Object.entries(properties).map(([propertyName, property]) => { - return renderer.renderProperty(propertyName, property).replace(';', ','); + const ctorProperties = Object.values(properties).map((property) => { + return renderer.renderProperty(property).replace(';', ','); }); return `constructor(input: { @@ -83,31 +67,19 @@ ${renderer.indent(renderer.renderBlock(ctorProperties))} ${renderer.indent(renderer.renderBlock(assignments))} }`; }, - property({ renderer, propertyName, property, type }): string { - return `private _${renderer.renderProperty(propertyName, property, type)}`; + property({ renderer, property }): string { + return `private _${renderer.renderProperty(property)}`; }, - getter({ renderer, model, propertyName, property, type }): string { - const isRequired = model.isRequired(propertyName); - propertyName = renderer.nameProperty(propertyName, property); - let signature = ''; - if (type === PropertyType.property) { - signature = renderer.renderTypeSignature(property, { orUndefined: !isRequired }); - } else if (type === PropertyType.additionalProperty || type === PropertyType.patternProperties) { - const mapType = renderer.renderType(property); - signature = `: Map | undefined`; - } - return `get ${propertyName}()${signature} { return this._${propertyName}; }`; - }, - setter({ renderer, model, propertyName, property, type }): string { - const isRequired = model.isRequired(propertyName); - propertyName = renderer.nameProperty(propertyName, property); - let signature = ''; - if (type === PropertyType.property) { - signature = renderer.renderTypeSignature(property, { orUndefined: !isRequired }); - } else if (type === PropertyType.additionalProperty || type === PropertyType.patternProperties) { - const mapType = renderer.renderType(property); - signature = `: Map | undefined`; - } - return `set ${propertyName}(${propertyName}${signature}) { this._${propertyName} = ${propertyName}; }`; + getter({ property }): string { + return `get ${property.propertyName}(): ${property.property.type}${ + property.required === false ? ' | undefined' : '' + } { return this._${property.propertyName}; }`; }, + setter({ property }): string { + return `set ${property.propertyName}(${property.propertyName}: ${ + property.property.type + }${property.required === false ? ' | undefined' : ''}) { this._${ + property.propertyName + } = ${property.propertyName}; }`; + } }; diff --git a/src/generators/typescript/renderers/EnumRenderer.ts b/src/generators/typescript/renderers/EnumRenderer.ts index 024cc41a57..35eb656362 100644 --- a/src/generators/typescript/renderers/EnumRenderer.ts +++ b/src/generators/typescript/renderers/EnumRenderer.ts @@ -1,28 +1,36 @@ import { TypeScriptRenderer } from '../TypeScriptRenderer'; - -import { EnumPreset } from '../../../models'; -import { FormatHelpers } from '../../../helpers'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../models'; +import { EnumPresetType } from '../TypeScriptPreset'; +import { TypeScriptOptions } from '../TypeScriptGenerator'; /** * Renderer for TypeScript's `enum` type - * + * * @extends TypeScriptRenderer */ -export class EnumRenderer extends TypeScriptRenderer { +export class EnumRenderer extends TypeScriptRenderer { async defaultSelf(): Promise { const content = [ await this.renderItems(), await this.runAdditionalContentPreset() ]; - const formattedName = this.nameType(this.model.$id); - return `enum ${formattedName} { + return `enum ${this.model.name} { ${this.indent(this.renderBlock(content, 2))} }`; } + renderUnionEnum(model: ConstrainedEnumModel): string { + const enums = model.values || []; + const enumTypes = enums.map((t) => t.value).join(' | '); + return `type ${model.name} = ${enumTypes};`; + } + async renderItems(): Promise { - const enums = this.model.enum || []; + const enums = this.model.values || []; const items: string[] = []; for (const item of enums) { @@ -33,64 +41,19 @@ ${this.indent(this.renderBlock(content, 2))} return this.renderBlock(items); } - runItemPreset(item: any): Promise { + runItemPreset(item: ConstrainedEnumValueModel): Promise { return this.runPreset('item', { item }); } - - normalizeKey(value: any): any { - let key; - switch (typeof value) { - case 'bigint': - case 'number': { - key = `number_${value}`; - break; - } - case 'object': { - key = JSON.stringify(value); - break; - } - default: { - key = FormatHelpers.replaceSpecialCharacters(String(value), { exclude: [' ','_'], separator: '_' }); - //Ensure no special char can be the beginning letter - if (!(/^[a-zA-Z]+$/).test(key.charAt(0))) { - key = `String_${key}`; - } - } - } - return FormatHelpers.toConstantCase(key); - } - - normalizeValue(value: any): any { - let normalizedValue; - switch (typeof value) { - case 'string': - case 'boolean': - normalizedValue = `"${value}"`; - break; - case 'bigint': - case 'number': { - normalizedValue = value; - break; - } - case 'object': { - normalizedValue = `'${JSON.stringify(value)}'`; - break; - } - default: { - normalizedValue = String(value); - } - } - return normalizedValue; - } } -export const TS_DEFAULT_ENUM_PRESET: EnumPreset = { - self({ renderer }) { +export const TS_DEFAULT_ENUM_PRESET: EnumPresetType = { + self({ renderer, options, model }) { + if (options.enumType === 'union' && model instanceof ConstrainedEnumModel) { + return renderer.renderUnionEnum(model); + } return renderer.defaultSelf(); }, - item({ item, renderer }): string { - const key = renderer.normalizeKey(item); - const value = renderer.normalizeValue(item); - return `${key} = ${value},`; + item({ item }): string { + return `${item.key} = ${item.value},`; } }; diff --git a/src/generators/typescript/renderers/InterfaceRenderer.ts b/src/generators/typescript/renderers/InterfaceRenderer.ts index 53099afdf3..3a14bb495e 100644 --- a/src/generators/typescript/renderers/InterfaceRenderer.ts +++ b/src/generators/typescript/renderers/InterfaceRenderer.ts @@ -1,30 +1,31 @@ -import { TypeScriptRenderer } from '../TypeScriptRenderer'; -import { InterfacePreset } from '../../../models'; +import { TypeScriptOptions } from '../TypeScriptGenerator'; +import { TypeScriptObjectRenderer } from '../TypeScriptObjectRenderer'; +import { InterfacePresetType } from '../TypeScriptPreset'; /** * Renderer for TypeScript's `interface` type - * + * * @extends TypeScriptRenderer */ -export class InterfaceRenderer extends TypeScriptRenderer { +export class InterfaceRenderer extends TypeScriptObjectRenderer { async defaultSelf(): Promise { const content = [ await this.renderProperties(), await this.runAdditionalContentPreset() ]; - const formattedName = this.nameType(this.model.$id); - return `interface ${formattedName} { + return `interface ${this.model.name} { ${this.indent(this.renderBlock(content, 2))} }`; } } -export const TS_DEFAULT_INTERFACE_PRESET: InterfacePreset = { - self({ renderer }) { - return renderer.defaultSelf(); - }, - property({ renderer, propertyName, property, type }) { - return renderer.renderProperty(propertyName, property, type); - } -}; +export const TS_DEFAULT_INTERFACE_PRESET: InterfacePresetType = + { + self({ renderer }) { + return renderer.defaultSelf(); + }, + property({ renderer, property }) { + return renderer.renderProperty(property); + } + }; diff --git a/src/generators/typescript/renderers/TypeRenderer.ts b/src/generators/typescript/renderers/TypeRenderer.ts index e0f81df7d5..d80a67f441 100644 --- a/src/generators/typescript/renderers/TypeRenderer.ts +++ b/src/generators/typescript/renderers/TypeRenderer.ts @@ -1,35 +1,21 @@ import { TypeScriptRenderer } from '../TypeScriptRenderer'; -import { TypePreset } from '../TypeScriptPreset'; -import { TypeHelpers, ModelKind } from '../../../helpers'; +import { TypePresetType } from '../TypeScriptPreset'; +import { ConstrainedMetaModel } from '../../../models'; +import { TypeScriptOptions } from '../TypeScriptGenerator'; /** * Renderer for TypeScript's `type` type - * + * * @extends TypeScriptRenderer */ -export class TypeRenderer extends TypeScriptRenderer { - async defaultSelf(): Promise { - const body = await this.renderTypeBody(); - const formattedName = this.nameType(this.model.$id); - return `type ${formattedName} = ${body};`; - } - - renderTypeBody(): Promise { - const kind = TypeHelpers.extractKind(this.model); - if (kind === ModelKind.ENUM) { - return Promise.resolve(this.renderEnum()); - } - return Promise.resolve(this.renderType(this.model)); - } - - renderEnum(): string { - const enums = this.model.enum || []; - return enums.map(t => typeof t === 'string' ? `"${t}"` : t).join(' | '); +export class TypeRenderer extends TypeScriptRenderer { + defaultSelf(): string { + return `type ${this.model.name} = ${this.model.type};`; } } -export const TS_DEFAULT_TYPE_PRESET: TypePreset = { +export const TS_DEFAULT_TYPE_PRESET: TypePresetType = { self({ renderer }) { return renderer.defaultSelf(); - }, + } }; diff --git a/src/helpers/CommonModelToMetaModel.ts b/src/helpers/CommonModelToMetaModel.ts new file mode 100644 index 0000000000..9b08baa446 --- /dev/null +++ b/src/helpers/CommonModelToMetaModel.ts @@ -0,0 +1,522 @@ +import { Logger } from '../utils'; +import { + CommonModel, + MetaModel, + UnionModel, + ObjectModel, + DictionaryModel, + StringModel, + TupleModel, + TupleValueModel, + ArrayModel, + BooleanModel, + IntegerModel, + FloatModel, + EnumModel, + EnumValueModel, + ObjectPropertyModel, + AnyModel +} from '../models'; + +export function convertToMetaModel( + jsonSchemaModel: CommonModel, + alreadySeenModels: Map = new Map() +): MetaModel { + const hasModel = alreadySeenModels.has(jsonSchemaModel); + if (hasModel) { + return alreadySeenModels.get(jsonSchemaModel) as MetaModel; + } + const modelName = jsonSchemaModel.$id || 'undefined'; + + const unionModel = convertToUnionModel( + jsonSchemaModel, + modelName, + alreadySeenModels + ); + if (unionModel !== undefined) { + return unionModel; + } + const anyModel = convertToAnyModel(jsonSchemaModel, modelName); + if (anyModel !== undefined) { + return anyModel; + } + const enumModel = convertToEnumModel(jsonSchemaModel, modelName); + if (enumModel !== undefined) { + return enumModel; + } + const objectModel = convertToObjectModel( + jsonSchemaModel, + modelName, + alreadySeenModels + ); + if (objectModel !== undefined) { + return objectModel; + } + const dictionaryModel = convertToDictionaryModel( + jsonSchemaModel, + modelName, + alreadySeenModels + ); + if (dictionaryModel !== undefined) { + return dictionaryModel; + } + const tupleModel = convertToTupleModel( + jsonSchemaModel, + modelName, + alreadySeenModels + ); + if (tupleModel !== undefined) { + return tupleModel; + } + const arrayModel = convertToArrayModel( + jsonSchemaModel, + modelName, + alreadySeenModels + ); + if (arrayModel !== undefined) { + return arrayModel; + } + const stringModel = convertToStringModel(jsonSchemaModel, modelName); + if (stringModel !== undefined) { + return stringModel; + } + const floatModel = convertToFloatModel(jsonSchemaModel, modelName); + if (floatModel !== undefined) { + return floatModel; + } + const integerModel = convertToIntegerModel(jsonSchemaModel, modelName); + if (integerModel !== undefined) { + return integerModel; + } + const booleanModel = convertToBooleanModel(jsonSchemaModel, modelName); + if (booleanModel !== undefined) { + return booleanModel; + } + Logger.error('Failed to convert to MetaModel, defaulting to AnyModel'); + return new AnyModel(modelName, jsonSchemaModel.originalInput); +} +function isEnumModel(jsonSchemaModel: CommonModel): boolean { + if (!Array.isArray(jsonSchemaModel.enum)) { + return false; + } + return true; +} + +/** + * Converts a CommonModel into multiple models wrapped in a union model. + * + * Because a CommonModel might contain multiple models, it's name for each of those models would be the same, instead we slightly change the model name. + * Each model has it's type as a name prepended to the union name. + * + * If the CommonModel has multiple types + */ +// eslint-disable-next-line sonarjs/cognitive-complexity +export function convertToUnionModel( + jsonSchemaModel: CommonModel, + name: string, + alreadySeenModels: Map +): UnionModel | undefined { + const containsUnions = Array.isArray(jsonSchemaModel.union); + const containsSimpleTypeUnion = + Array.isArray(jsonSchemaModel.type) && jsonSchemaModel.type.length > 1; + const containsAllTypes = + Array.isArray(jsonSchemaModel.type) && jsonSchemaModel.type.length === 7; + if ( + (!containsSimpleTypeUnion && !containsUnions) || + isEnumModel(jsonSchemaModel) || + containsAllTypes + ) { + return undefined; + } + const unionModel = new UnionModel(name, jsonSchemaModel.originalInput, []); + //cache model before continuing + if (!alreadySeenModels.has(jsonSchemaModel)) { + alreadySeenModels.set(jsonSchemaModel, unionModel); + } + + // Has multiple types, so convert to union + if (containsUnions && jsonSchemaModel.union) { + for (const unionCommonModel of jsonSchemaModel.union) { + const unionMetaModel = convertToMetaModel( + unionCommonModel, + alreadySeenModels + ); + unionModel.union.push(unionMetaModel); + } + return unionModel; + } + + // Has simple union types + // Each must have a different name then the root union model, as it otherwise clashes when code is generated + const enumModel = convertToEnumModel(jsonSchemaModel, `${name}_enum`); + if (enumModel !== undefined) { + unionModel.union.push(enumModel); + } + const objectModel = convertToObjectModel( + jsonSchemaModel, + `${name}_object`, + alreadySeenModels + ); + if (objectModel !== undefined) { + unionModel.union.push(objectModel); + } + const dictionaryModel = convertToDictionaryModel( + jsonSchemaModel, + `${name}_dictionary`, + alreadySeenModels + ); + if (dictionaryModel !== undefined) { + unionModel.union.push(dictionaryModel); + } + const tupleModel = convertToTupleModel( + jsonSchemaModel, + `${name}_tuple`, + alreadySeenModels + ); + if (tupleModel !== undefined) { + unionModel.union.push(tupleModel); + } + const arrayModel = convertToArrayModel( + jsonSchemaModel, + `${name}_array`, + alreadySeenModels + ); + if (arrayModel !== undefined) { + unionModel.union.push(arrayModel); + } + const stringModel = convertToStringModel(jsonSchemaModel, `${name}_string`); + if (stringModel !== undefined) { + unionModel.union.push(stringModel); + } + const floatModel = convertToFloatModel(jsonSchemaModel, `${name}_float`); + if (floatModel !== undefined) { + unionModel.union.push(floatModel); + } + const integerModel = convertToIntegerModel( + jsonSchemaModel, + `${name}_integer` + ); + if (integerModel !== undefined) { + unionModel.union.push(integerModel); + } + const booleanModel = convertToBooleanModel( + jsonSchemaModel, + `${name}_boolean` + ); + if (booleanModel !== undefined) { + unionModel.union.push(booleanModel); + } + return unionModel; +} +export function convertToStringModel( + jsonSchemaModel: CommonModel, + name: string +): StringModel | undefined { + if (!jsonSchemaModel.type?.includes('string')) { + return undefined; + } + return new StringModel(name, jsonSchemaModel.originalInput); +} +export function convertToAnyModel( + jsonSchemaModel: CommonModel, + name: string +): AnyModel | undefined { + if ( + !Array.isArray(jsonSchemaModel.type) || + jsonSchemaModel.type.length !== 7 + ) { + return undefined; + } + return new AnyModel(name, jsonSchemaModel.originalInput); +} +export function convertToIntegerModel( + jsonSchemaModel: CommonModel, + name: string +): IntegerModel | undefined { + if (!jsonSchemaModel.type?.includes('integer')) { + return undefined; + } + return new IntegerModel(name, jsonSchemaModel.originalInput); +} +export function convertToFloatModel( + jsonSchemaModel: CommonModel, + name: string +): FloatModel | undefined { + if (!jsonSchemaModel.type?.includes('number')) { + return undefined; + } + return new FloatModel(name, jsonSchemaModel.originalInput); +} +export function convertToEnumModel( + jsonSchemaModel: CommonModel, + name: string +): EnumModel | undefined { + if (!isEnumModel(jsonSchemaModel)) { + return undefined; + } + const metaModel = new EnumModel(name, jsonSchemaModel.originalInput, []); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + for (const enumValue of jsonSchemaModel.enum!) { + let enumKey = enumValue; + if (typeof enumValue !== 'string') { + enumKey = JSON.stringify(enumValue); + } + const enumValueModel = new EnumValueModel(enumKey, enumValue); + metaModel.values.push(enumValueModel); + } + return metaModel; +} +export function convertToBooleanModel( + jsonSchemaModel: CommonModel, + name: string +): BooleanModel | undefined { + if (!jsonSchemaModel.type?.includes('boolean')) { + return undefined; + } + return new BooleanModel(name, jsonSchemaModel.originalInput); +} + +/** + * Determine whether we have a dictionary or an object. because in some cases inputs might be: + * { "type": "object", "additionalProperties": { "$ref": "#" } } which is to be interpreted as a dictionary not an object model. + */ +function isDictionary(jsonSchemaModel: CommonModel): boolean { + if ( + Object.keys(jsonSchemaModel.properties || {}).length > 0 || + jsonSchemaModel.additionalProperties === undefined + ) { + return false; + } + return true; +} + +/** + * Return the original input based on additionalProperties and patternProperties. + */ +function getOriginalInputFromAdditionalAndPatterns( + jsonSchemaModel: CommonModel +) { + const originalInputs = []; + if (jsonSchemaModel.additionalProperties !== undefined) { + originalInputs.push(jsonSchemaModel.additionalProperties.originalInput); + } + + if (jsonSchemaModel.patternProperties !== undefined) { + for (const patternModel of Object.values( + jsonSchemaModel.patternProperties + )) { + originalInputs.push(patternModel.originalInput); + } + } + return originalInputs; +} + +/** + * Function creating the right meta model based on additionalProperties and patternProperties. + */ +function convertAdditionalAndPatterns( + jsonSchemaModel: CommonModel, + name: string, + alreadySeenModels: Map +) { + const modelsAsValue = new Map(); + if (jsonSchemaModel.additionalProperties !== undefined) { + const additionalPropertyModel = convertToMetaModel( + jsonSchemaModel.additionalProperties, + alreadySeenModels + ); + modelsAsValue.set(additionalPropertyModel.name, additionalPropertyModel); + } + + if (jsonSchemaModel.patternProperties !== undefined) { + for (const patternModel of Object.values( + jsonSchemaModel.patternProperties + )) { + const patternPropertyModel = convertToMetaModel(patternModel); + modelsAsValue.set(patternPropertyModel.name, patternPropertyModel); + } + } + if (modelsAsValue.size === 1) { + return Array.from(modelsAsValue.values())[0]; + } + return new UnionModel( + name, + getOriginalInputFromAdditionalAndPatterns(jsonSchemaModel), + Array.from(modelsAsValue.values()) + ); +} + +// eslint-disable-next-line @typescript-eslint/no-non-null-assertion +export function convertToDictionaryModel( + jsonSchemaModel: CommonModel, + name: string, + alreadySeenModels: Map +): DictionaryModel | undefined { + if (!isDictionary(jsonSchemaModel)) { + return undefined; + } + const originalInput = + getOriginalInputFromAdditionalAndPatterns(jsonSchemaModel); + const keyModel = new StringModel(name, originalInput); + const valueModel = convertAdditionalAndPatterns( + jsonSchemaModel, + name, + alreadySeenModels + ); + return new DictionaryModel( + name, + originalInput, + keyModel, + valueModel, + 'normal' + ); +} + +export function convertToObjectModel( + jsonSchemaModel: CommonModel, + name: string, + alreadySeenModels: Map +): ObjectModel | undefined { + if ( + !jsonSchemaModel.type?.includes('object') || + isDictionary(jsonSchemaModel) + ) { + return undefined; + } + + const metaModel = new ObjectModel(name, jsonSchemaModel.originalInput, {}); + //cache model before continuing + if (!alreadySeenModels.has(jsonSchemaModel)) { + alreadySeenModels.set(jsonSchemaModel, metaModel); + } + + for (const [propertyName, prop] of Object.entries( + jsonSchemaModel.properties || {} + )) { + const isRequired = jsonSchemaModel.isRequired(propertyName); + const propertyModel = new ObjectPropertyModel( + propertyName, + isRequired, + convertToMetaModel(prop, alreadySeenModels) + ); + metaModel.properties[String(propertyName)] = propertyModel; + } + + if ( + jsonSchemaModel.additionalProperties !== undefined || + jsonSchemaModel.patternProperties !== undefined + ) { + let propertyName = 'additionalProperties'; + while (metaModel.properties[String(propertyName)] !== undefined) { + propertyName = `reserved_${propertyName}`; + } + const originalInput = + getOriginalInputFromAdditionalAndPatterns(jsonSchemaModel); + const keyModel = new StringModel(propertyName, originalInput); + const valueModel = convertAdditionalAndPatterns( + jsonSchemaModel, + propertyName, + alreadySeenModels + ); + const dictionaryModel = new DictionaryModel( + propertyName, + originalInput, + keyModel, + valueModel, + 'unwrap' + ); + const propertyModel = new ObjectPropertyModel( + propertyName, + false, + dictionaryModel + ); + metaModel.properties[String(propertyName)] = propertyModel; + } + return metaModel; +} + +export function convertToArrayModel( + jsonSchemaModel: CommonModel, + name: string, + alreadySeenModels: Map +): ArrayModel | undefined { + if (!jsonSchemaModel.type?.includes('array')) { + return undefined; + } + + const isNormalArray = + !Array.isArray(jsonSchemaModel.items) && + jsonSchemaModel.additionalItems === undefined && + jsonSchemaModel.items !== undefined; + //item multiple types + additionalItems sat = both count, as normal array + //item single type + additionalItems sat = contradicting, only items count, as normal array + //item not sat + additionalItems sat = anything is allowed, as normal array + //item single type + additionalItems not sat = normal array + //item not sat + additionalItems not sat = normal array, any type + if (isNormalArray) { + const placeholderModel = new AnyModel('', undefined); + const metaModel = new ArrayModel( + name, + jsonSchemaModel.originalInput, + placeholderModel + ); + alreadySeenModels.set(jsonSchemaModel, metaModel); + + const valueModel = convertToMetaModel( + jsonSchemaModel.items as CommonModel, + alreadySeenModels + ); + metaModel.valueModel = valueModel; + return metaModel; + } + + const valueModel = new UnionModel('union', jsonSchemaModel.originalInput, []); + const metaModel = new ArrayModel( + name, + jsonSchemaModel.originalInput, + valueModel + ); + alreadySeenModels.set(jsonSchemaModel, metaModel); + if (jsonSchemaModel.items !== undefined) { + for (const itemModel of Array.isArray(jsonSchemaModel.items) + ? jsonSchemaModel.items + : [jsonSchemaModel.items]) { + const itemsModel = convertToMetaModel(itemModel, alreadySeenModels); + valueModel.union.push(itemsModel); + } + } + if (jsonSchemaModel.additionalItems !== undefined) { + const itemsModel = convertToMetaModel( + jsonSchemaModel.additionalItems, + alreadySeenModels + ); + valueModel.union.push(itemsModel); + } + return metaModel; +} +export function convertToTupleModel( + jsonSchemaModel: CommonModel, + name: string, + alreadySeenModels: Map +): TupleModel | undefined { + const isTuple = + jsonSchemaModel.type?.includes('array') && + Array.isArray(jsonSchemaModel.items) && + jsonSchemaModel.additionalItems === undefined; + if (!isTuple) { + return undefined; + } + + const items = jsonSchemaModel.items as CommonModel[]; + //item multiple types + additionalItems not sat = tuple of item type + const tupleModel = new TupleModel(name, jsonSchemaModel.originalInput, []); + alreadySeenModels.set(jsonSchemaModel, tupleModel); + for (let i = 0; i < items.length; i++) { + const item = items[Number(i)]; + const valueModel = convertToMetaModel(item, alreadySeenModels); + const tupleValueModel = new TupleValueModel(i, valueModel); + tupleModel.tuple[Number(i)] = tupleValueModel; + } + return tupleModel; +} diff --git a/src/helpers/ConstrainHelpers.ts b/src/helpers/ConstrainHelpers.ts new file mode 100644 index 0000000000..ab9c47451e --- /dev/null +++ b/src/helpers/ConstrainHelpers.ts @@ -0,0 +1,563 @@ +import { AbstractDependencyManager } from '../generators/AbstractDependencyManager'; +import { + ConstrainedAnyModel, + ConstrainedBooleanModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedTupleValueModel, + ConstrainedArrayModel, + ConstrainedUnionModel, + ConstrainedEnumModel, + ConstrainedDictionaryModel, + ConstrainedEnumValueModel, + ConstrainedObjectPropertyModel +} from '../models/ConstrainedMetaModel'; +import { + AnyModel, + BooleanModel, + FloatModel, + IntegerModel, + ObjectModel, + ReferenceModel, + StringModel, + TupleModel, + ArrayModel, + UnionModel, + EnumModel, + DictionaryModel, + MetaModel, + ObjectPropertyModel +} from '../models/MetaModel'; +import { getTypeFromMapping, TypeMapping } from './TypeHelpers'; + +export type ConstrainContext< + Options, + M extends MetaModel, + DependencyManager extends AbstractDependencyManager +> = { + partOfProperty?: ConstrainedObjectPropertyModel; + metaModel: M; + constrainedName: string; + options: Options; + dependencyManager: DependencyManager; +}; + +export type EnumKeyContext = { + enumKey: string; + constrainedEnumModel: ConstrainedEnumModel; + enumModel: EnumModel; +}; +export type EnumKeyConstraint = (context: EnumKeyContext) => string; + +export type EnumValueContext = { + enumValue: any; + constrainedEnumModel: ConstrainedEnumModel; + enumModel: EnumModel; +}; +export type EnumValueConstraint = (context: EnumValueContext) => any; + +export type ModelNameContext = { + modelName: string; +}; +export type ModelNameConstraint = (context: ModelNameContext) => string; + +export type PropertyKeyContext = { + constrainedObjectPropertyModel: ConstrainedObjectPropertyModel; + objectPropertyModel: ObjectPropertyModel; + constrainedObjectModel: ConstrainedObjectModel; + objectModel: ObjectModel; +}; + +export type PropertyKeyConstraint = (context: PropertyKeyContext) => string; + +export interface Constraints { + enumKey: EnumKeyConstraint; + enumValue: EnumValueConstraint; + modelName: ModelNameConstraint; + propertyKey: PropertyKeyConstraint; +} + +const placeHolderConstrainedObject = new ConstrainedAnyModel('', undefined, ''); + +function constrainReferenceModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + constrainRules: Constraints, + context: ConstrainContext, + alreadySeenModels: Map +): ConstrainedReferenceModel { + const constrainedModel = new ConstrainedReferenceModel( + context.constrainedName, + context.metaModel.originalInput, + '', + placeHolderConstrainedObject + ); + alreadySeenModels.set(context.metaModel, constrainedModel); + + const constrainedRefModel = constrainMetaModel( + typeMapping, + constrainRules, + { ...context, metaModel: context.metaModel.ref, partOfProperty: undefined }, + alreadySeenModels + ); + constrainedModel.ref = constrainedRefModel; + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} +function constrainAnyModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + context: ConstrainContext +): ConstrainedAnyModel { + const constrainedModel = new ConstrainedAnyModel( + context.constrainedName, + context.metaModel.originalInput, + '' + ); + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} +function constrainFloatModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + context: ConstrainContext +): ConstrainedFloatModel { + const constrainedModel = new ConstrainedFloatModel( + context.constrainedName, + context.metaModel.originalInput, + '' + ); + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} +function constrainIntegerModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + context: ConstrainContext +): ConstrainedIntegerModel { + const constrainedModel = new ConstrainedIntegerModel( + context.constrainedName, + context.metaModel.originalInput, + '' + ); + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} +function constrainStringModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + context: ConstrainContext +): ConstrainedStringModel { + const constrainedModel = new ConstrainedStringModel( + context.constrainedName, + context.metaModel.originalInput, + '' + ); + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} +function constrainBooleanModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + context: ConstrainContext +): ConstrainedBooleanModel { + const constrainedModel = new ConstrainedBooleanModel( + context.constrainedName, + context.metaModel.originalInput, + '' + ); + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} +function constrainTupleModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + constrainRules: Constraints, + context: ConstrainContext, + alreadySeenModels: Map +): ConstrainedTupleModel { + const constrainedModel = new ConstrainedTupleModel( + context.constrainedName, + context.metaModel.originalInput, + '', + [] + ); + alreadySeenModels.set(context.metaModel, constrainedModel); + + const constrainedTupleModels = context.metaModel.tuple.map((tupleValue) => { + const tupleType = constrainMetaModel( + typeMapping, + constrainRules, + { ...context, metaModel: tupleValue.value, partOfProperty: undefined }, + alreadySeenModels + ); + return new ConstrainedTupleValueModel(tupleValue.index, tupleType); + }); + constrainedModel.tuple = constrainedTupleModels; + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} +function constrainArrayModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + constrainRules: Constraints, + context: ConstrainContext, + alreadySeenModels: Map +): ConstrainedArrayModel { + const constrainedModel = new ConstrainedArrayModel( + context.constrainedName, + context.metaModel.originalInput, + '', + placeHolderConstrainedObject + ); + alreadySeenModels.set(context.metaModel, constrainedModel); + const constrainedValueModel = constrainMetaModel( + typeMapping, + constrainRules, + { + ...context, + metaModel: context.metaModel.valueModel, + partOfProperty: undefined + }, + alreadySeenModels + ); + constrainedModel.valueModel = constrainedValueModel; + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} +function constrainUnionModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + constrainRules: Constraints, + context: ConstrainContext, + alreadySeenModels: Map +): ConstrainedUnionModel { + const constrainedModel = new ConstrainedUnionModel( + context.constrainedName, + context.metaModel.originalInput, + '', + [] + ); + alreadySeenModels.set(context.metaModel, constrainedModel); + + const constrainedUnionModels = context.metaModel.union.map((unionValue) => { + return constrainMetaModel( + typeMapping, + constrainRules, + { ...context, metaModel: unionValue, partOfProperty: undefined }, + alreadySeenModels + ); + }); + constrainedModel.union = constrainedUnionModels; + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} +function constrainDictionaryModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + constrainRules: Constraints, + context: ConstrainContext, + alreadySeenModels: Map +): ConstrainedDictionaryModel { + const constrainedModel = new ConstrainedDictionaryModel( + context.constrainedName, + context.metaModel.originalInput, + '', + placeHolderConstrainedObject, + placeHolderConstrainedObject, + context.metaModel.serializationType + ); + alreadySeenModels.set(context.metaModel, constrainedModel); + + const keyModel = constrainMetaModel( + typeMapping, + constrainRules, + { ...context, metaModel: context.metaModel.key, partOfProperty: undefined }, + alreadySeenModels + ); + constrainedModel.key = keyModel; + const valueModel = constrainMetaModel( + typeMapping, + constrainRules, + { + ...context, + metaModel: context.metaModel.value, + partOfProperty: undefined + }, + alreadySeenModels + ); + constrainedModel.value = valueModel; + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} + +function constrainObjectModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + constrainRules: Constraints, + context: ConstrainContext, + alreadySeenModels: Map +): ConstrainedObjectModel { + const constrainedModel = new ConstrainedObjectModel( + context.constrainedName, + context.metaModel.originalInput, + '', + {} + ); + alreadySeenModels.set(context.metaModel, constrainedModel); + + for (const propertyMetaModel of Object.values(context.metaModel.properties)) { + const constrainedPropertyModel = new ConstrainedObjectPropertyModel( + '', + propertyMetaModel.propertyName, + propertyMetaModel.required, + constrainedModel + ); + const constrainedPropertyName = constrainRules.propertyKey({ + objectPropertyModel: propertyMetaModel, + constrainedObjectPropertyModel: constrainedPropertyModel, + constrainedObjectModel: constrainedModel, + objectModel: context.metaModel + }); + constrainedPropertyModel.propertyName = constrainedPropertyName; + const constrainedProperty = constrainMetaModel( + typeMapping, + constrainRules, + { + ...context, + metaModel: propertyMetaModel.property, + partOfProperty: constrainedPropertyModel + }, + alreadySeenModels + ); + constrainedPropertyModel.property = constrainedProperty; + constrainedModel.properties[String(constrainedPropertyName)] = + constrainedPropertyModel; + } + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} + +function ConstrainEnumModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + constrainRules: Constraints, + context: ConstrainContext +): ConstrainedEnumModel { + const constrainedModel = new ConstrainedEnumModel( + context.constrainedName, + context.metaModel.originalInput, + '', + [] + ); + + for (const enumValue of context.metaModel.values) { + const constrainedEnumKey = constrainRules.enumKey({ + enumKey: String(enumValue.key), + enumModel: context.metaModel, + constrainedEnumModel: constrainedModel + }); + const constrainedEnumValue = constrainRules.enumValue({ + enumValue: enumValue.value, + enumModel: context.metaModel, + constrainedEnumModel: constrainedModel + }); + + const constrainedEnumValueModel = new ConstrainedEnumValueModel( + constrainedEnumKey, + constrainedEnumValue + ); + constrainedModel.values.push(constrainedEnumValueModel); + } + constrainedModel.type = getTypeFromMapping(typeMapping, { + constrainedModel, + options: context.options, + partOfProperty: context.partOfProperty, + dependencyManager: context.dependencyManager + }); + return constrainedModel; +} + +export function constrainMetaModel< + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + constrainRules: Constraints, + context: ConstrainContext, + alreadySeenModels: Map = new Map() +): ConstrainedMetaModel { + if (alreadySeenModels.has(context.metaModel)) { + return alreadySeenModels.get(context.metaModel) as ConstrainedMetaModel; + } + const constrainedName = constrainRules.modelName({ + modelName: context.metaModel.name + }); + const newContext = { ...context, constrainedName }; + if (newContext.metaModel instanceof ObjectModel) { + return constrainObjectModel( + typeMapping, + constrainRules, + { ...newContext, metaModel: newContext.metaModel }, + alreadySeenModels + ); + } else if (newContext.metaModel instanceof ReferenceModel) { + return constrainReferenceModel( + typeMapping, + constrainRules, + { ...newContext, metaModel: newContext.metaModel }, + alreadySeenModels + ); + } else if (newContext.metaModel instanceof DictionaryModel) { + return constrainDictionaryModel( + typeMapping, + constrainRules, + { ...newContext, metaModel: newContext.metaModel }, + alreadySeenModels + ); + } else if (newContext.metaModel instanceof TupleModel) { + return constrainTupleModel( + typeMapping, + constrainRules, + { ...newContext, metaModel: newContext.metaModel }, + alreadySeenModels + ); + } else if (newContext.metaModel instanceof ArrayModel) { + return constrainArrayModel( + typeMapping, + constrainRules, + { ...newContext, metaModel: newContext.metaModel }, + alreadySeenModels + ); + } else if (newContext.metaModel instanceof UnionModel) { + return constrainUnionModel( + typeMapping, + constrainRules, + { ...newContext, metaModel: newContext.metaModel }, + alreadySeenModels + ); + } + // Simple models are those who does not have properties that contain other MetaModels. + let simpleModel: ConstrainedMetaModel | undefined; + if (newContext.metaModel instanceof EnumModel) { + simpleModel = ConstrainEnumModel(typeMapping, constrainRules, { + ...newContext, + metaModel: newContext.metaModel + }); + } else if (newContext.metaModel instanceof BooleanModel) { + simpleModel = constrainBooleanModel(typeMapping, { + ...newContext, + metaModel: newContext.metaModel + }); + } else if (newContext.metaModel instanceof AnyModel) { + simpleModel = constrainAnyModel(typeMapping, { + ...newContext, + metaModel: newContext.metaModel + }); + } else if (newContext.metaModel instanceof FloatModel) { + simpleModel = constrainFloatModel(typeMapping, { + ...newContext, + metaModel: newContext.metaModel + }); + } else if (newContext.metaModel instanceof IntegerModel) { + simpleModel = constrainIntegerModel(typeMapping, { + ...newContext, + metaModel: newContext.metaModel + }); + } else if (newContext.metaModel instanceof StringModel) { + simpleModel = constrainStringModel(typeMapping, { + ...newContext, + metaModel: newContext.metaModel + }); + } + if (simpleModel !== undefined) { + alreadySeenModels.set(context.metaModel, simpleModel); + return simpleModel; + } + + throw new Error('Could not constrain model'); +} diff --git a/src/helpers/Constraints.ts b/src/helpers/Constraints.ts new file mode 100644 index 0000000000..79beee443d --- /dev/null +++ b/src/helpers/Constraints.ts @@ -0,0 +1,133 @@ +import { + ConstrainedObjectModel, + ConstrainedEnumModel +} from '../models/ConstrainedMetaModel'; +import { ObjectModel, EnumModel } from '../models/MetaModel'; + +export function NO_NUMBER_START_CHAR(value: string): string { + const firstChar = value.charAt(0); + if (firstChar !== '' && !isNaN(+firstChar)) { + return `number_${value}`; + } + return value; +} + +/** + * Because a lot of the other constrain functions (such as NO_NUMBER_START_CHAR, NO_EMPTY_VALUE, etc) they might manipulate the property names by append, prepend, or manipulate it any other way. + * We then need to make sure that they don't clash with any existing properties, this is what this function handles. + * If so, prepend `reserved_` to the property name and recheck. + * + * @param constrainedObjectModel the current constrained object model, which contains already existing constrained properties + * @param objectModel the raw object model which is non-constrained to the output language. + * @param propertyName one of the properties in objectModel which might have been manipulated + * @param namingFormatter the name formatter which are used to format the property key + */ +export function NO_DUPLICATE_PROPERTIES( + constrainedObjectModel: ConstrainedObjectModel, + objectModel: ObjectModel, + propertyName: string, + namingFormatter: (value: string) => string +): string { + // Make sure that the given property name is formatted correctly for further comparisons + const formattedPropertyName = namingFormatter(propertyName); + let newPropertyName = propertyName; + const alreadyPartOfMetaModel = Object.keys(objectModel.properties) + .filter((key) => propertyName !== key) // Filter out the potential same property name that we can safely ignore for this check. + .includes(formattedPropertyName); + const alreadyPartOfConstrainedModel = Object.keys( + constrainedObjectModel.properties + ).includes(formattedPropertyName); + if (alreadyPartOfMetaModel || alreadyPartOfConstrainedModel) { + newPropertyName = `reserved_${propertyName}`; + newPropertyName = NO_DUPLICATE_PROPERTIES( + constrainedObjectModel, + objectModel, + newPropertyName, + namingFormatter + ); + } + return newPropertyName; +} + +/** + * Because a lot of the other constrain functions (such as NO_NUMBER_START_CHAR, NO_EMPTY_VALUE, etc) they might manipulate the enum keys by append, prepend, or manipulate it any other way. + * We then need to make sure that they don't clash with any existing enum keys, this is what this function handles. + * If so, prepend `reserved_` to the enum key and recheck. + * + * @param constrainedEnumModel the current constrained enum model, which contains already existing constrained enum keys + * @param enumModel the raw enum model which is non-constrained to the output language. + * @param enumKey one of the enum keys in enumModel which might have been manipulated. + * @param namingFormatter the name formatter which are used to format the enum key. + * @param enumKeyToCheck the enum key to use for checking if it already exist, defaults to enumKey. + * @param onNameChange callback to change the name of the enum key that needs to be returned. + * @param onNameChangeToCheck callback to change the enum key which is being checked as part of the existing models. + * @returns {string} the potential new enum key that does not clash with existing enum keys. + */ +export function NO_DUPLICATE_ENUM_KEYS( + constrainedEnumModel: ConstrainedEnumModel, + enumModel: EnumModel, + enumKey: string, + namingFormatter: (value: string) => string, + enumKeyToCheck: string = enumKey, + onNameChange: (currentEnumKey: string) => string = (currentEnumKey) => { + return `reserved_${currentEnumKey}`; + }, + onNameChangeToCheck: (currentEnumKey: string) => string = onNameChange +): string { + const formattedEnumKey = namingFormatter(enumKeyToCheck); + let newEnumKey = enumKey; + + const alreadyPartOfMetaModel = enumModel.values + .map((model) => model.key) + .filter((key) => enumKeyToCheck !== key) + .includes(formattedEnumKey); + const alreadyPartOfConstrainedModel = constrainedEnumModel.values + .map((model) => model.key) + .includes(formattedEnumKey); + + if (alreadyPartOfMetaModel || alreadyPartOfConstrainedModel) { + newEnumKey = onNameChange(newEnumKey); + enumKeyToCheck = onNameChangeToCheck(enumKeyToCheck); + newEnumKey = NO_DUPLICATE_ENUM_KEYS( + constrainedEnumModel, + enumModel, + newEnumKey, + namingFormatter, + enumKeyToCheck, + onNameChange, + onNameChangeToCheck + ); + } + return newEnumKey; +} + +export function NO_EMPTY_VALUE(value: string): string { + if (value === '') { + return 'empty'; + } + return value; +} + +export function NO_RESERVED_KEYWORDS( + propertyName: string, + reservedKeywordCallback: (value: string) => boolean +): string { + if (reservedKeywordCallback(propertyName)) { + return `reserved_${propertyName}`; + } + return propertyName; +} + +export function checkForReservedKeyword( + word: string, + wordList: string[], + forceLowerCase = true +): boolean { + let wordListToCheck = [...wordList]; + let wordToCheck = word; + if (forceLowerCase) { + wordListToCheck = wordListToCheck.map((value) => value.toLowerCase()); + wordToCheck = wordToCheck.toLowerCase(); + } + return wordListToCheck.includes(wordToCheck); +} diff --git a/src/helpers/DependencyHelpers.ts b/src/helpers/DependencyHelpers.ts new file mode 100644 index 0000000000..472101a64a --- /dev/null +++ b/src/helpers/DependencyHelpers.ts @@ -0,0 +1,34 @@ +import { ConstrainedMetaModel } from '../models'; + +/** + * Function to make it easier to render JS/TS dependencies based on module system + * + * @param toImport + * @param fromModule + * @param moduleSystem + */ +export function renderJavaScriptDependency( + toImport: string, + fromModule: string, + moduleSystem: 'CJS' | 'ESM' +): string { + return moduleSystem === 'CJS' + ? `const ${toImport} = require('${fromModule}');` + : `import ${toImport} from '${fromModule}';`; +} + +/** + * Function to make an array of ConstrainedMetaModels only contain unique values (ignores different in memory instances) + * + * @param array to make unique + */ +export function makeUnique( + array: ConstrainedMetaModel[] +): ConstrainedMetaModel[] { + const seen: Set = new Set(); + + return array.filter((item: ConstrainedMetaModel) => { + const naiveIdentifier = item.name + item.type; + return seen.has(naiveIdentifier) ? false : seen.add(naiveIdentifier); + }); +} diff --git a/src/helpers/FileHelpers.ts b/src/helpers/FileHelpers.ts index c8997d6244..981c2f5ce7 100644 --- a/src/helpers/FileHelpers.ts +++ b/src/helpers/FileHelpers.ts @@ -1,19 +1,67 @@ -import * as fs from 'fs'; +import { promises as fs } from 'fs'; import * as path from 'path'; + +/** + * Convert a string into utf-8 encoding and return the byte size. + */ +function lengthInUtf8Bytes(str: string): number { + // Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence. + const m = encodeURIComponent(str).match(/%[89ABab]/g); + return str.length + (m ? m.length : 0); +} + export class FileHelpers { /** * Node specific file writer, which writes the content to the specified filepath. - * + * * This function is invasive, as it overwrite any existing files with the same name as the model. - * + * * @param content to write - * @param filePath to write to + * @param filePath to write to, + * @param ensureFilesWritten veryify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ - static async writerToFileSystem(content: string, filePath: string): Promise { - const outputFilePath = path.resolve(filePath); - // eslint-disable-next-line security/detect-non-literal-fs-filename - await fs.promises.mkdir(path.dirname(outputFilePath), { recursive: true }); - // eslint-disable-next-line security/detect-non-literal-fs-filename - await fs.promises.writeFile(outputFilePath, content); + static writerToFileSystem( + content: string, + filePath: string, + ensureFilesWritten = false + ): Promise { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + try { + const outputFilePath = path.resolve(filePath); + // eslint-disable-next-line security/detect-non-literal-fs-filename + await fs.mkdir(path.dirname(outputFilePath), { recursive: true }); + // eslint-disable-next-line security/detect-non-literal-fs-filename + await fs.writeFile(outputFilePath, content); + + /** + * It happens that the promise is resolved before the file is actually written to. + * + * This often happen if the file system is swamped with write requests in either benchmarks or in our blackbox tests. + * + * To avoid this we dont resolve until we are sure the file is written and exists. + */ + if (ensureFilesWritten) { + // eslint-disable-next-line no-undef + const timerId = setInterval(async () => { + try { + // eslint-disable-next-line security/detect-non-literal-fs-filename + const isExists = await fs.stat(outputFilePath); + if (isExists && isExists.size === lengthInUtf8Bytes(content)) { + // eslint-disable-next-line no-undef + clearInterval(timerId); + resolve(); + } + } catch (e) { + // Ignore errors here as the file might not have been written yet + } + }, 10); + } else { + resolve(); + } + } catch (e) { + reject(e); + } + }); } } diff --git a/src/helpers/FormatHelpers.ts b/src/helpers/FormatHelpers.ts index c2370c97ba..d073df624a 100644 --- a/src/helpers/FormatHelpers.ts +++ b/src/helpers/FormatHelpers.ts @@ -2,12 +2,14 @@ import { camelCase, pascalCase, paramCase, - constantCase + constantCase, + snakeCase, + pascalCaseTransformMerge } from 'change-case'; export enum IndentationTypes { TABS = 'tabs', - SPACES = 'spaces', + SPACES = 'spaces' } const specialCharacterReplacements = new Map([ @@ -18,7 +20,7 @@ const specialCharacterReplacements = new Map([ ['$', 'dollar'], ['%', 'percent'], ['&', 'ampersand'], - ['\'', 'apostrophe'], + ["'", 'apostrophe'], ['(', 'roundleft'], [')', 'roundright'], ['*', 'asterisk'], @@ -43,10 +45,13 @@ const specialCharacterReplacements = new Map([ ['{', 'curlyleft'], ['|', 'vertical'], ['}', 'curlyright'], - ['~', 'tilde'], + ['~', 'tilde'] ]); -interface ReplaceSpecialCharactersOptions { separator?: string, exclude?: string[] } +interface ReplaceSpecialCharactersOptions { + separator?: string; + exclude?: string[]; +} export class FormatHelpers { /** @@ -58,6 +63,15 @@ export class FormatHelpers { return value.charAt(0).toUpperCase() + value.slice(1); } + /** + * Lower first char in given string value. + * @param {string} value to change + * @returns {string} + */ + static lowerFirst(value: string): string { + return value.charAt(0).toLowerCase() + value.slice(1); + } + /** * Transform into a string with the separator denoted by the next word capitalized. * @param {string} value to transform @@ -72,6 +86,16 @@ export class FormatHelpers { */ static toPascalCase = pascalCase; + /** + * Transform into a string of capitalized words without separators + * merging numbers. + * @param {string} value to transform + * @returns {string} + */ + static toPascalCaseMergingNumbers(value: string): string { + return pascalCase(value, { transform: pascalCaseTransformMerge }); + } + /** * Transform into a lower cased string with dashes between words. * @param {string} value to transform @@ -87,18 +111,37 @@ export class FormatHelpers { static toConstantCase = constantCase; /** - * Replace special characters (Not 0-9,a-z,A-Z) with character names - * @param {string} value to transform - * @param {ReplaceSpecialCharactersOptions} options - * @returns {string} - */ - static replaceSpecialCharacters(string: string, options?: ReplaceSpecialCharactersOptions): string { + * Transform into lower case string with an underscore between words. + * @param {string} value to transform + * @returns {string} + */ + static toSnakeCase = snakeCase; + + /** + * Replace special characters (Not 0-9,a-z,A-Z) with character names + * @param {string} value to transform + * @param {ReplaceSpecialCharactersOptions} options + * @returns {string} + */ + static replaceSpecialCharacters( + string: string, + options?: ReplaceSpecialCharactersOptions + ): string { const separator = options?.separator ?? ''; return [...string].reduce((sum: string, c: string, i: number) => { - if (options?.exclude?.includes(c)) { return sum + c; } + if (options?.exclude?.includes(c)) { + return sum + c; + } const replacement = specialCharacterReplacements.get(c); - if (replacement === undefined) { return sum + c; } - return sum + (sum.endsWith(separator) || sum.length === 0 ? '' : separator) + replacement + (i === string.length - 1 ? '' : separator); + if (replacement === undefined) { + return sum + c; + } + return ( + sum + + (sum.endsWith(separator) || sum.length === 0 ? '' : separator) + + replacement + + (i === string.length - 1 ? '' : separator) + ); }, ''); } @@ -109,7 +152,7 @@ export class FormatHelpers { */ static breakLines(lines: string | string[]): string[] { lines = Array.isArray(lines) ? lines : [lines]; - return lines.map(line => line.split('\n')).flatMap(line => line); + return lines.map((line) => line.split('\n')).flatMap((line) => line); } /** @@ -119,7 +162,11 @@ export class FormatHelpers { * @param {IndentationTypes} type the type of indendations to use. SPACES by default. * @returns {string} */ - static indent(content = '', size = 1, type: IndentationTypes = IndentationTypes.SPACES): string { + static indent( + content = '', + size = 1, + type: IndentationTypes = IndentationTypes.SPACES + ): string { if (size < 1) { return content; } @@ -128,7 +175,10 @@ export class FormatHelpers { if (content.includes('\n')) { const newLineArray = content.split('\n'); return newLineArray.reduce((accumulator, value) => { - const newValue = value.trim() === '' ? value : `${this.getIndentation(size, type)}${value}`; + const newValue = + value.trim() === '' + ? value + : `${this.getIndentation(size, type)}${value}`; return accumulator === '' ? newValue : `${accumulator}\n${newValue}`; }, ''); } @@ -142,14 +192,17 @@ export class FormatHelpers { * @param {IndentationTypes} type the type of indendations to use. SPACES by default. * @returns {string} */ - private static getIndentation(size = 0, type: IndentationTypes = IndentationTypes.SPACES): string { + private static getIndentation( + size = 0, + type: IndentationTypes = IndentationTypes.SPACES + ): string { const whitespaceChar = type === IndentationTypes.SPACES ? ' ' : '\t'; return Array(size).fill(whitespaceChar).join(''); } /** * Render given JSON Schema example to string - * + * * @param {Array} examples to render * @returns {string} */ @@ -157,7 +210,9 @@ export class FormatHelpers { let renderedExamples = ''; if (Array.isArray(examples)) { for (const example of examples) { - if (renderedExamples !== '') { renderedExamples += ', '; } + if (renderedExamples !== '') { + renderedExamples += ', '; + } if (typeof example === 'object') { try { renderedExamples += JSON.stringify(example); @@ -173,9 +228,10 @@ export class FormatHelpers { } static snakeCase(renderName: string): string { - return renderName.replace(/\W+/g, ' ') + return renderName + .replace(/\W+/g, ' ') .split(/ |\B(?=[A-Z])/) - .map(word => word.toLowerCase()) + .map((word) => word.toLowerCase()) .join('_'); } } diff --git a/src/helpers/NameHelpers.ts b/src/helpers/NameHelpers.ts deleted file mode 100644 index 32f0fbbbd3..0000000000 --- a/src/helpers/NameHelpers.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { FormatHelpers } from '../helpers'; -import { CommonInputModel, CommonModel } from '../models'; - -/** - * Default property names for different aspects of the common model - */ -export enum DefaultPropertyNames { - additionalProperties = 'additionalProperties', - patternProperties = 'PatternProperties' -} - -/** - * Recursively find the proper property name. - * - * This function ensures that the property name is unique for the model - * - * @param rootModel - * @param propertyName - */ -export function getUniquePropertyName(rootModel: CommonModel, propertyName: string): string { - if (Object.keys(rootModel.properties || {}).includes(propertyName)) { - return getUniquePropertyName(rootModel, `reserved_${propertyName}`); - } - return propertyName; -} - -/** - * The common naming convention context type. - */ -export type CommonTypeNamingConventionCtx = { model: CommonModel, inputModel: CommonInputModel, reservedKeywordCallback?: (name: string) => boolean}; -export type CommonPropertyNamingConventionCtx = { model: CommonModel, inputModel: CommonInputModel, property?: CommonModel, reservedKeywordCallback?: (name: string) => boolean}; - -/** - * The common naming convention type shared between generators for different languages. - */ -export type CommonNamingConvention = { - type?: (name: string | undefined, ctx: CommonTypeNamingConventionCtx) => string; - property?: (name: string | undefined, ctx: CommonPropertyNamingConventionCtx) => string; -}; - -/** - * A CommonNamingConvention implementation shared between generators for different languages. - */ -export const CommonNamingConventionImplementation: CommonNamingConvention = { - type: (name, ctx) => { - if (!name) {return '';} - let formattedName = FormatHelpers.toPascalCase(name); - if (ctx.reservedKeywordCallback !== undefined && ctx.reservedKeywordCallback(formattedName)) { - formattedName = FormatHelpers.toPascalCase(`reserved_${formattedName}`); - } - return formattedName; - }, - property: (name, ctx) => { - if (!name) {return '';} - let formattedName = FormatHelpers.toCamelCase(name); - if (ctx.reservedKeywordCallback !== undefined && ctx.reservedKeywordCallback(formattedName)) { - // If name is considered reserved, make sure we rename it appropriately - // and make sure no clashes occur. - formattedName = FormatHelpers.toCamelCase(`reserved_${formattedName}`); - if (Object.keys(ctx.model.properties || {}).includes(formattedName)) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return CommonNamingConventionImplementation.property!(`reserved_${formattedName}`, ctx); - } - } - return formattedName; - } -}; diff --git a/src/helpers/PresetHelpers.ts b/src/helpers/PresetHelpers.ts index 6114a40eda..87ba31141b 100644 --- a/src/helpers/PresetHelpers.ts +++ b/src/helpers/PresetHelpers.ts @@ -10,11 +10,11 @@ export const hasPreset =

( presets: Presets

, preset: P ): boolean => - presets.some( - (presetListItem) => + presets.some( + (presetListItem) => // Check regular preset equality - preset === presetListItem || + preset === presetListItem || // Check PresetWithOptions equality (Object.prototype.hasOwnProperty.call(preset, 'preset') && preset.preset === presetListItem) - ); + ); diff --git a/src/helpers/Splitter.ts b/src/helpers/Splitter.ts new file mode 100644 index 0000000000..f5c6145dcf --- /dev/null +++ b/src/helpers/Splitter.ts @@ -0,0 +1,118 @@ +/* eslint-disable sonarjs/cognitive-complexity */ + +import { + MetaModel, + ReferenceModel, + EnumModel, + UnionModel, + ArrayModel, + TupleModel, + StringModel, + IntegerModel, + FloatModel, + BooleanModel, + ObjectModel, + DictionaryModel +} from '../models'; + +export type SplitOptions = { + splitEnum?: boolean; + splitUnion?: boolean; + splitArray?: boolean; + splitTuple?: boolean; + splitString?: boolean; + splitInteger?: boolean; + splitFloat?: boolean; + splitBoolean?: boolean; + splitObject?: boolean; + splitDictionary?: boolean; +}; + +/** + * Try split the model + * @param model + * @param options + * @param models + * @returns whether the new or old MetaModel to use. + */ +const trySplitModel = ( + model: MetaModel, + options: SplitOptions, + models: MetaModel[] +): MetaModel => { + const shouldSplit = + (options.splitEnum === true && model instanceof EnumModel) || + (options.splitUnion === true && model instanceof UnionModel) || + (options.splitArray === true && model instanceof ArrayModel) || + (options.splitTuple === true && model instanceof TupleModel) || + (options.splitString === true && model instanceof StringModel) || + (options.splitInteger === true && model instanceof IntegerModel) || + (options.splitFloat === true && model instanceof FloatModel) || + (options.splitBoolean === true && model instanceof BooleanModel) || + (options.splitObject === true && model instanceof ObjectModel) || + (options.splitDictionary === true && model instanceof DictionaryModel); + + if (shouldSplit) { + if (!models.includes(model)) { + models.push(model); + } + return new ReferenceModel(model.name, model.originalInput, model); + } + return model; +}; + +/** + * Overwrite the nested models with references where required. + * + * @param model + * @param options + * @param models + * @returns an array of all the split models + */ +export const split = ( + model: MetaModel, + options: SplitOptions, + models: MetaModel[] = [model], + alreadySeenModels: MetaModel[] = [] +): MetaModel[] => { + if (!alreadySeenModels.includes(model)) { + alreadySeenModels.push(model); + } else { + return models; + } + if (model instanceof ObjectModel) { + for (const [prop, propModel] of Object.entries(model.properties)) { + const propertyModel = propModel.property; + model.properties[String(prop)].property = trySplitModel( + propModel.property, + options, + models + ); + split(propertyModel, options, models, alreadySeenModels); + } + } else if (model instanceof UnionModel) { + for (let index = 0; index < model.union.length; index++) { + const unionModel = model.union[Number(index)]; + model.union[Number(index)] = trySplitModel(unionModel, options, models); + split(unionModel, options, models, alreadySeenModels); + } + } else if (model instanceof ArrayModel) { + const valueModel = model.valueModel; + model.valueModel = trySplitModel(valueModel, options, models); + split(valueModel, options, models, alreadySeenModels); + } else if (model instanceof TupleModel) { + for (const tuple of model.tuple) { + const tupleModel = tuple.value; + tuple.value = trySplitModel(tupleModel, options, models); + split(tupleModel, options, models, alreadySeenModels); + } + } else if (model instanceof DictionaryModel) { + const keyModel = model.key; + const valueModel = model.value; + model.key = trySplitModel(keyModel, options, models); + model.value = trySplitModel(valueModel, options, models); + split(keyModel, options, models, alreadySeenModels); + split(valueModel, options, models, alreadySeenModels); + } + return models; +}; diff --git a/src/helpers/TypeHelpers.ts b/src/helpers/TypeHelpers.ts index fd49970fa2..364cffb0b5 100644 --- a/src/helpers/TypeHelpers.ts +++ b/src/helpers/TypeHelpers.ts @@ -1,25 +1,145 @@ -/* eslint-disable no-unused-vars */ -import { CommonModel } from '../models'; +import { AbstractDependencyManager } from '../generators/AbstractDependencyManager'; +import { + ConstrainedAnyModel, + ConstrainedBooleanModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedArrayModel, + ConstrainedUnionModel, + ConstrainedEnumModel, + ConstrainedDictionaryModel, + ConstrainedObjectPropertyModel +} from '../models/ConstrainedMetaModel'; -export enum ModelKind { - OBJECT = 'object', - ARRAY = 'array', - ENUM = 'enum', - UNION = 'union', - PRIMITIVE = 'primitive', -} - -export class TypeHelpers { +export type TypeContext< + T extends ConstrainedMetaModel, + Options, + DependencyManager extends AbstractDependencyManager +> = { + /** + * If the model is a property in an object model this can be used to conditionally change types based on property information. + */ + partOfProperty?: ConstrainedObjectPropertyModel; + /** + * The underlying options provided to the generator + */ + options: Options; /** - * Returns the type (object | array | union | enum | primitive) of the model - * @param model to check - * @returns {ModelKind} + * The specific constrained model that we are trying to find the type for */ - static extractKind(model: CommonModel): ModelKind { - if (model.type === 'object') {return ModelKind.OBJECT;} - if (model.type === 'array') {return ModelKind.ARRAY;} - if (Array.isArray(model.enum)) {return ModelKind.ENUM;} - if (Array.isArray(model.type)) {return ModelKind.UNION;} - return ModelKind.PRIMITIVE; + constrainedModel: T; + /** + * Dependency manager that can be used to add custom dependencies to the rendering of the model, such as when using external types. + */ + dependencyManager: DependencyManager; +}; + +export type TypeMappingFunction< + T extends ConstrainedMetaModel, + Options, + DependencyManager extends AbstractDependencyManager +> = (context: TypeContext) => string; + +export type TypeMapping< + Options, + DependencyManager extends AbstractDependencyManager +> = { + Object: TypeMappingFunction< + ConstrainedObjectModel, + Options, + DependencyManager + >; + Reference: TypeMappingFunction< + ConstrainedReferenceModel, + Options, + DependencyManager + >; + Any: TypeMappingFunction; + Float: TypeMappingFunction; + Integer: TypeMappingFunction< + ConstrainedIntegerModel, + Options, + DependencyManager + >; + String: TypeMappingFunction< + ConstrainedStringModel, + Options, + DependencyManager + >; + Boolean: TypeMappingFunction< + ConstrainedBooleanModel, + Options, + DependencyManager + >; + Tuple: TypeMappingFunction; + Array: TypeMappingFunction; + Enum: TypeMappingFunction; + Union: TypeMappingFunction; + Dictionary: TypeMappingFunction< + ConstrainedDictionaryModel, + Options, + DependencyManager + >; +}; + +export function getTypeFromMapping< + T extends ConstrainedMetaModel, + Options, + DependencyManager extends AbstractDependencyManager +>( + typeMapping: TypeMapping, + context: TypeContext +): string { + if (context.constrainedModel instanceof ConstrainedObjectModel) { + return typeMapping.Object({ + ...context, + constrainedModel: context.constrainedModel + }); + } else if (context.constrainedModel instanceof ConstrainedReferenceModel) { + return typeMapping.Reference({ + ...context, + constrainedModel: context.constrainedModel + }); + } else if (context.constrainedModel instanceof ConstrainedAnyModel) { + return typeMapping.Any(context); + } else if (context.constrainedModel instanceof ConstrainedFloatModel) { + return typeMapping.Float(context); + } else if (context.constrainedModel instanceof ConstrainedIntegerModel) { + return typeMapping.Integer(context); + } else if (context.constrainedModel instanceof ConstrainedStringModel) { + return typeMapping.String(context); + } else if (context.constrainedModel instanceof ConstrainedBooleanModel) { + return typeMapping.Boolean(context); + } else if (context.constrainedModel instanceof ConstrainedTupleModel) { + return typeMapping.Tuple({ + ...context, + constrainedModel: context.constrainedModel + }); + } else if (context.constrainedModel instanceof ConstrainedArrayModel) { + return typeMapping.Array({ + ...context, + constrainedModel: context.constrainedModel + }); + } else if (context.constrainedModel instanceof ConstrainedEnumModel) { + return typeMapping.Enum({ + ...context, + constrainedModel: context.constrainedModel + }); + } else if (context.constrainedModel instanceof ConstrainedUnionModel) { + return typeMapping.Union({ + ...context, + constrainedModel: context.constrainedModel + }); + } else if (context.constrainedModel instanceof ConstrainedDictionaryModel) { + return typeMapping.Dictionary({ + ...context, + constrainedModel: context.constrainedModel + }); } + throw new Error('Could not find type for model'); } diff --git a/src/helpers/index.ts b/src/helpers/index.ts index f7d84c8e06..4624b77036 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -1,4 +1,9 @@ export * from './FormatHelpers'; export * from './TypeHelpers'; -export * from './NameHelpers'; export * from './FileHelpers'; +export * from './CommonModelToMetaModel'; +export * from './Splitter'; +export * from './Constraints'; +export * from './ConstrainHelpers'; +export * from './PresetHelpers'; +export * from './DependencyHelpers'; diff --git a/src/index.ts b/src/index.ts index 3d231ce81c..cf1dcbbd3c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,5 +2,5 @@ export * from './generators'; export * from './helpers'; export * from './models'; export * from './processors'; -export type {ModelLoggingInterface} from './utils'; -export {Logger} from './utils'; +export type { ModelLoggingInterface } from './utils'; +export { Logger } from './utils'; diff --git a/src/interpreter/InterpretAdditionalItems.ts b/src/interpreter/InterpretAdditionalItems.ts index 308b977675..b08121187f 100644 --- a/src/interpreter/InterpretAdditionalItems.ts +++ b/src/interpreter/InterpretAdditionalItems.ts @@ -1,17 +1,31 @@ import { CommonModel } from '../models'; -import { Interpreter, InterpreterOptions, InterpreterSchemaType } from './Interpreter'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; /** * Interpreter function for additionalItems keyword. - * + * * @param schema * @param model * @param interpreter * @param interpreterOptions to control the interpret process */ -export default function interpretAdditionalItems(schema: InterpreterSchemaType, model: CommonModel, interpreter : Interpreter, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions): void { - if (typeof schema === 'boolean' || model.type?.includes('array') === false) {return;} - const additionalItemsModel = interpreter.interpret(schema.additionalItems === undefined ? true : schema.additionalItems, interpreterOptions); +export default function interpretAdditionalItems( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if (typeof schema === 'boolean' || model.type?.includes('array') === false) { + return; + } + const additionalItemsModel = interpreter.interpret( + schema.additionalItems === undefined ? true : schema.additionalItems, + interpreterOptions + ); if (additionalItemsModel !== undefined) { model.addAdditionalItems(additionalItemsModel, schema); } diff --git a/src/interpreter/InterpretAdditionalProperties.ts b/src/interpreter/InterpretAdditionalProperties.ts index d4fcaf2683..614ff5a6d6 100644 --- a/src/interpreter/InterpretAdditionalProperties.ts +++ b/src/interpreter/InterpretAdditionalProperties.ts @@ -1,18 +1,34 @@ import { CommonModel } from '../models'; -import { Interpreter, InterpreterOptions, InterpreterSchemaType } from './Interpreter'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; import { isModelObject } from './Utils'; /** * Interpreter function for additionalProperties keyword. - * + * * @param schema * @param model * @param interpreter * @param interpreterOptions to control the interpret process */ -export default function interpretAdditionalProperties(schema: InterpreterSchemaType, model: CommonModel, interpreter : Interpreter, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions): void { - if (typeof schema === 'boolean' || isModelObject(model) === false) {return;} - const additionalPropertiesModel = interpreter.interpret(schema.additionalProperties === undefined ? true : schema.additionalProperties, interpreterOptions); +export default function interpretAdditionalProperties( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if (typeof schema === 'boolean' || isModelObject(model) === false) { + return; + } + const additionalPropertiesModel = interpreter.interpret( + schema.additionalProperties === undefined + ? true + : schema.additionalProperties, + interpreterOptions + ); if (additionalPropertiesModel !== undefined) { model.addAdditionalProperty(additionalPropertiesModel, schema); } diff --git a/src/interpreter/InterpretAllOf.ts b/src/interpreter/InterpretAllOf.ts index 438b4bc189..ef763a5127 100644 --- a/src/interpreter/InterpretAllOf.ts +++ b/src/interpreter/InterpretAllOf.ts @@ -1,29 +1,62 @@ import { Logger } from '../utils'; import { CommonModel } from '../models/CommonModel'; -import { Interpreter, InterpreterOptions, InterpreterSchemaType } from './Interpreter'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; import { isModelObject } from './Utils'; /** * Interpreter function for allOf keyword. - * + * * It either merges allOf schemas into existing model or if allowed, create inheritance. - * - * @param schema - * @param model - * @param interpreter + * + * @param schema + * @param model + * @param interpreter * @param interpreterOptions to control the interpret process */ -export default function interpretAllOf(schema: InterpreterSchemaType, model: CommonModel, interpreter : Interpreter, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions): void { - if (typeof schema === 'boolean' || schema.allOf === undefined) {return;} - for (const allOfSchema of schema.allOf) { +export default function interpretAllOf( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if ( + typeof schema === 'boolean' || + schema.allOf === undefined || + schema.oneOf + ) { + return; + } + for (const allOfSchema of schema.allOf) { const allOfModel = interpreter.interpret(allOfSchema, interpreterOptions); - if (allOfModel === undefined) {continue;} - if (isModelObject(allOfModel) === true && interpreterOptions.allowInheritance === true) { - Logger.info(`Processing allOf, inheritance is enabled, ${model.$id} inherits from ${allOfModel.$id}`, model, allOfModel); + if (allOfModel === undefined) { + continue; + } + if ( + isModelObject(allOfModel) === true && + interpreterOptions.allowInheritance === true + ) { + Logger.info( + `Processing allOf, inheritance is enabled, ${model.$id} inherits from ${allOfModel.$id}`, + model, + allOfModel + ); model.addExtendedModel(allOfModel); } else { - Logger.info('Processing allOf, inheritance is not enabled. AllOf model is merged together with already interpreted model', model, allOfModel); - interpreter.interpretAndCombineSchema(allOfSchema, model, schema, interpreterOptions); + Logger.info( + 'Processing allOf, inheritance is not enabled. AllOf model is merged together with already interpreted model', + model, + allOfModel + ); + interpreter.interpretAndCombineSchema( + allOfSchema, + model, + schema, + interpreterOptions + ); } } } diff --git a/src/interpreter/InterpretAnyOf.ts b/src/interpreter/InterpretAnyOf.ts new file mode 100644 index 0000000000..9191cef43a --- /dev/null +++ b/src/interpreter/InterpretAnyOf.ts @@ -0,0 +1,34 @@ +import { CommonModel } from '../models/CommonModel'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; + +/** + * Interpreter function for anyOf keyword. + * + * It puts the schema reference into the items field. + * + * @param schema + * @param model + * @param interpreter + * @param interpreterOptions to control the interpret process + */ +export default function interpretAnyOf( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if (typeof schema === 'boolean' || schema.anyOf === undefined) { + return; + } + for (const anyOfSchema of schema.anyOf) { + const anyOfModel = interpreter.interpret(anyOfSchema, interpreterOptions); + if (anyOfModel === undefined) { + continue; + } + model.addItemUnion(anyOfModel); + } +} diff --git a/src/interpreter/InterpretConst.ts b/src/interpreter/InterpretConst.ts index 76991a51dd..34ec4784d9 100644 --- a/src/interpreter/InterpretConst.ts +++ b/src/interpreter/InterpretConst.ts @@ -4,13 +4,22 @@ import { inferTypeFromValue } from './Utils'; /** * Interpreter function for const keyword for draft version > 4 - * - * @param schema + * + * @param schema * @param model */ -export default function interpretConst(schema: InterpreterSchemaType, model: CommonModel): void { - if (schema instanceof Draft4Schema || typeof schema === 'boolean' || schema.const === undefined) {return;} - +export default function interpretConst( + schema: InterpreterSchemaType, + model: CommonModel +): void { + if ( + schema instanceof Draft4Schema || + typeof schema === 'boolean' || + schema.const === undefined + ) { + return; + } + const schemaConst = schema.const; model.enum = [schemaConst]; diff --git a/src/interpreter/InterpretDependencies.ts b/src/interpreter/InterpretDependencies.ts index 7e5a53fc3a..dbb6210768 100644 --- a/src/interpreter/InterpretDependencies.ts +++ b/src/interpreter/InterpretDependencies.ts @@ -1,18 +1,34 @@ import { CommonModel } from '../models/CommonModel'; -import { Interpreter, InterpreterOptions, InterpreterSchemaType } from './Interpreter'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; /** * Interpreter function for dependencies keyword. - * - * @param schema + * + * @param schema * @param model */ -export default function interpretDependencies(schema: InterpreterSchemaType, model: CommonModel, interpreter: Interpreter, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions): void { - if (typeof schema === 'boolean' || schema.dependencies === undefined) {return;} +export default function interpretDependencies( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if (typeof schema === 'boolean' || schema.dependencies === undefined) { + return; + } for (const dependency of Object.values(schema.dependencies)) { // Only handle schema dependency and skip property dependencies if (!Array.isArray(dependency)) { - interpreter.interpretAndCombineSchema(dependency as any, model, schema, interpreterOptions); + interpreter.interpretAndCombineSchema( + dependency as any, + model, + schema, + interpreterOptions + ); } } } diff --git a/src/interpreter/InterpretEnum.ts b/src/interpreter/InterpretEnum.ts index 63fb553af4..bf29b23b79 100644 --- a/src/interpreter/InterpretEnum.ts +++ b/src/interpreter/InterpretEnum.ts @@ -1,16 +1,20 @@ - import { CommonModel } from '../models/CommonModel'; import { InterpreterSchemaType } from './Interpreter'; import { inferTypeFromValue } from './Utils'; /** * Interpreter function for enum keyword - * - * @param schema + * + * @param schema * @param model */ -export default function interpretEnum(schema: InterpreterSchemaType, model: CommonModel): void { - if (typeof schema === 'boolean' || schema.enum === undefined) {return;} +export default function interpretEnum( + schema: InterpreterSchemaType, + model: CommonModel +): void { + if (typeof schema === 'boolean' || schema.enum === undefined) { + return; + } for (const enumValue of schema.enum) { if (schema.type === undefined) { const inferredType = inferTypeFromValue(enumValue); diff --git a/src/interpreter/InterpretItems.ts b/src/interpreter/InterpretItems.ts index 4b33b1f3be..2becb5a9f1 100644 --- a/src/interpreter/InterpretItems.ts +++ b/src/interpreter/InterpretItems.ts @@ -1,31 +1,53 @@ - import { CommonModel } from '../models/CommonModel'; -import { Interpreter, InterpreterOptions, InterpreterSchemaType } from './Interpreter'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; /** * Interpreter function for items keyword. - * - * @param schema - * @param model - * @param interpreter + * + * @param schema + * @param model + * @param interpreter * @param interpreterOptions to control the interpret process */ -export default function interpretItems(schema: InterpreterSchemaType, model: CommonModel, interpreter : Interpreter, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions): void { - if (typeof schema === 'boolean' || schema.items === undefined) {return;} +export default function interpretItems( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if (typeof schema === 'boolean' || schema.items === undefined) { + return; + } model.addTypes('array'); - interpretArrayItems(schema, schema.items, model, interpreter, interpreterOptions); + interpretArrayItems( + schema, + schema.items, + model, + interpreter, + interpreterOptions + ); } /** * Internal function to process all item schemas - * - * @param rootSchema - * @param itemSchemas - * @param model - * @param interpreter + * + * @param rootSchema + * @param itemSchemas + * @param model + * @param interpreter * @param interpreterOptions to control the interpret process */ -function interpretArrayItems(rootSchema: InterpreterSchemaType, itemSchemas: InterpreterSchemaType[] | InterpreterSchemaType, model: CommonModel, interpreter : Interpreter, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions): void { +function interpretArrayItems( + rootSchema: InterpreterSchemaType, + itemSchemas: InterpreterSchemaType[] | InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { if (Array.isArray(itemSchemas)) { for (const [index, itemSchema] of itemSchemas.entries()) { const itemModel = interpreter.interpret(itemSchema, interpreterOptions); diff --git a/src/interpreter/InterpretNot.ts b/src/interpreter/InterpretNot.ts index 706d496fe2..d6b29165bf 100644 --- a/src/interpreter/InterpretNot.ts +++ b/src/interpreter/InterpretNot.ts @@ -1,19 +1,31 @@ - import { Logger } from '../utils'; import { CommonModel } from '../models/CommonModel'; -import { Interpreter, InterpreterOptions, InterpreterSchemaType } from './Interpreter'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; /** * Interpreter function for not keyword. - * + * * @param schema * @param model * @param interpreter * @param interpreterOptions to control the interpret process */ -export default function interpretNot(schema: InterpreterSchemaType, model: CommonModel, interpreter: Interpreter, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions): void { - if (typeof schema === 'boolean') {return;} - if (schema.not === undefined) {return;} +export default function interpretNot( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if (typeof schema === 'boolean') { + return; + } + if (schema.not === undefined) { + return; + } if (typeof schema.not === 'object') { const notSchema = schema.not; const newInterpreterOptions: InterpreterOptions = { @@ -22,10 +34,17 @@ export default function interpretNot(schema: InterpreterSchemaType, model: Commo }; const notModel = interpreter.interpret(notSchema, newInterpreterOptions); if (notModel !== undefined) { - if (notModel.type !== undefined) {model.removeType(notModel.type);} - if (notModel.enum !== undefined) {model.removeEnum(notModel.enum);} + if (notModel.type !== undefined) { + model.removeType(notModel.type); + } + if (notModel.enum !== undefined) { + model.removeEnum(notModel.enum); + } } } else if (typeof schema.not === 'boolean') { - Logger.warn(`Encountered boolean not schema for model ${model.$id}. This schema are not applied!`, schema); + Logger.warn( + `Encountered boolean not schema for model ${model.$id}. This schema are not applied!`, + schema + ); } } diff --git a/src/interpreter/InterpretOneOf.ts b/src/interpreter/InterpretOneOf.ts new file mode 100644 index 0000000000..b7fd92c571 --- /dev/null +++ b/src/interpreter/InterpretOneOf.ts @@ -0,0 +1,39 @@ +import { CommonModel } from '../models/CommonModel'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; + +/** + * Interpreter function for oneOf keyword. + * + * It puts the schema reference into the items field. + * + * @param schema + * @param model + * @param interpreter + * @param interpreterOptions to control the interpret process + */ +export default function interpretOneOf( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if ( + typeof schema === 'boolean' || + schema.oneOf === undefined || + schema.allOf || + schema.properties + ) { + return; + } + for (const oneOfSchema of schema.oneOf) { + const oneOfModel = interpreter.interpret(oneOfSchema, interpreterOptions); + if (oneOfModel === undefined) { + continue; + } + model.addItemUnion(oneOfModel); + } +} diff --git a/src/interpreter/InterpretOneOfWithAllOf.ts b/src/interpreter/InterpretOneOfWithAllOf.ts new file mode 100644 index 0000000000..45a9377dee --- /dev/null +++ b/src/interpreter/InterpretOneOfWithAllOf.ts @@ -0,0 +1,75 @@ +import { CommonModel } from '../models/CommonModel'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; + +/** + * Interpreter function for oneOf keyword combined with the allOf keyword. + * + * It merges the allOf schemas into all of the oneOf schemas. Shared properties are merged. The oneOf schemas are then added as union to the model. + * + * @param schema + * @param model + * @param interpreter + * @param interpreterOptions to control the interpret process + */ +export default function interpretOneOfWithAllOf( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if ( + typeof schema === 'boolean' || + !schema.oneOf || + !schema.allOf || + schema.properties || + interpreterOptions.allowInheritance + ) { + return; + } + + for (const oneOfSchema of schema.oneOf) { + const oneOfModel = interpreter.interpret(oneOfSchema, interpreterOptions); + + if (!oneOfModel) { + continue; + } + + const [firstAllOfSchema, ...allOfSchemas] = schema.allOf; + + if (typeof firstAllOfSchema === 'boolean') { + continue; + } + + const allOfModel = interpreter.interpret( + { ...firstAllOfSchema }, + interpreterOptions + ); + + if (!allOfModel) { + continue; + } + + for (const allOfSchema of allOfSchemas) { + interpreter.interpretAndCombineSchema( + allOfSchema, + allOfModel, + firstAllOfSchema, + interpreterOptions + ); + } + + interpreter.interpretAndCombineSchema( + oneOfSchema, + allOfModel, + firstAllOfSchema, + interpreterOptions + ); + allOfModel.$id = oneOfModel.$id; + + model.addItemUnion(allOfModel); + } +} diff --git a/src/interpreter/InterpretOneOfWithProperties.ts b/src/interpreter/InterpretOneOfWithProperties.ts new file mode 100644 index 0000000000..ddb046e238 --- /dev/null +++ b/src/interpreter/InterpretOneOfWithProperties.ts @@ -0,0 +1,63 @@ +import { CommonModel } from '../models/CommonModel'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; + +/** + * Interpreter function for oneOf keyword combined with properties. + * + * It merges the properties of the schema into the oneOf schemas. Shared properties are merged. The oneOf schemas are then added as union to the model. + * + * @param schema + * @param model + * @param interpreter + * @param interpreterOptions to control the interpret process + */ +export default function interpretOneOfWithProperties( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if ( + typeof schema === 'boolean' || + !schema.oneOf || + !schema.properties || + schema.allOf + ) { + return; + } + + for (const oneOfSchema of schema.oneOf) { + const oneOfModel = interpreter.interpret(oneOfSchema, interpreterOptions); + + if (!oneOfModel) { + continue; + } + + const schemaModel = interpreter.interpret( + { + ...schema, + oneOf: undefined + }, + interpreterOptions + ); + + if (!schemaModel) { + continue; + } + + interpreter.interpretAndCombineSchema( + oneOfSchema, + schemaModel, + schema, + interpreterOptions + ); + model.setType(undefined); + schemaModel.$id = oneOfModel.$id; + + model.addItemUnion(schemaModel); + } +} diff --git a/src/interpreter/InterpretPatternProperties.ts b/src/interpreter/InterpretPatternProperties.ts index fe93079ade..ef6eedad07 100644 --- a/src/interpreter/InterpretPatternProperties.ts +++ b/src/interpreter/InterpretPatternProperties.ts @@ -1,18 +1,34 @@ import { CommonModel } from '../models'; -import { Interpreter, InterpreterOptions, InterpreterSchemaType } from './Interpreter'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; /** * Interpreter function for patternProperties keyword. - * + * * @param schema * @param model * @param interpreter * @param interpreterOptions to control the interpret process */ -export default function interpretPatternProperties(schema: InterpreterSchemaType, model: CommonModel, interpreter : Interpreter, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions): void { - if (typeof schema === 'boolean') {return;} - for (const [pattern, patternSchema] of Object.entries(schema.patternProperties || {})) { - const patternModel = interpreter.interpret(patternSchema as any, interpreterOptions); +export default function interpretPatternProperties( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if (typeof schema === 'boolean') { + return; + } + for (const [pattern, patternSchema] of Object.entries( + schema.patternProperties || {} + )) { + const patternModel = interpreter.interpret( + patternSchema as any, + interpreterOptions + ); if (patternModel !== undefined) { model.addPatternProperty(pattern, patternModel, schema); } diff --git a/src/interpreter/InterpretProperties.ts b/src/interpreter/InterpretProperties.ts index 037ca85838..f89d0541d5 100644 --- a/src/interpreter/InterpretProperties.ts +++ b/src/interpreter/InterpretProperties.ts @@ -1,23 +1,42 @@ import { CommonModel } from '../models/CommonModel'; -import { Interpreter, InterpreterOptions, InterpreterSchemaType } from './Interpreter'; +import { + Interpreter, + InterpreterOptions, + InterpreterSchemaType +} from './Interpreter'; /** * Interpreter function for interpreting properties keyword. - * + * * @param schema * @param model * @param interpreter * @param interpreterOptions to control the interpret process */ -export default function interpretProperties(schema: InterpreterSchemaType, model: CommonModel, interpreter : Interpreter, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions): void { - if (typeof schema === 'boolean' || schema.properties === undefined) {return;} +export default function interpretProperties( + schema: InterpreterSchemaType, + model: CommonModel, + interpreter: Interpreter, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions +): void { + if ( + typeof schema === 'boolean' || + schema.properties === undefined || + schema.oneOf + ) { + return; + } model.addTypes('object'); - - for (const [propertyName, propertySchema] of Object.entries(schema.properties)) { - const propertyModel = interpreter.interpret(propertySchema, interpreterOptions); + + for (const [propertyName, propertySchema] of Object.entries( + schema.properties + )) { + const propertyModel = interpreter.interpret( + propertySchema, + interpreterOptions + ); if (propertyModel !== undefined) { model.addProperty(propertyName, propertyModel, schema); } } } - diff --git a/src/interpreter/Interpreter.ts b/src/interpreter/Interpreter.ts index 3a706efdf1..b1283df907 100644 --- a/src/interpreter/Interpreter.ts +++ b/src/interpreter/Interpreter.ts @@ -1,5 +1,12 @@ -import { CommonModel, Draft6Schema, Draft4Schema, SwaggerV2Schema, AsyncapiV2Schema, Draft7Schema } from '../models'; -import { interpretName, isEnum, isModelObject } from './Utils'; +import { + CommonModel, + Draft6Schema, + Draft4Schema, + SwaggerV2Schema, + AsyncapiV2Schema, + Draft7Schema +} from '../models'; +import { interpretName } from './Utils'; import interpretProperties from './InterpretProperties'; import interpretAllOf from './InterpretAllOf'; import interpretConst from './InterpretConst'; @@ -10,30 +17,42 @@ import interpretPatternProperties from './InterpretPatternProperties'; import interpretNot from './InterpretNot'; import interpretDependencies from './InterpretDependencies'; import interpretAdditionalItems from './InterpretAdditionalItems'; +import interpretOneOf from './InterpretOneOf'; +import interpretAnyOf from './InterpretAnyOf'; +import interpretOneOfWithAllOf from './InterpretOneOfWithAllOf'; +import interpretOneOfWithProperties from './InterpretOneOfWithProperties'; export type InterpreterOptions = { - allowInheritance?: boolean -} -export type InterpreterSchemas = Draft6Schema | Draft4Schema | Draft7Schema | SwaggerV2Schema | AsyncapiV2Schema; + allowInheritance?: boolean; +}; +export type InterpreterSchemas = + | Draft6Schema + | Draft4Schema + | Draft7Schema + | SwaggerV2Schema + | AsyncapiV2Schema; export type InterpreterSchemaType = InterpreterSchemas | boolean; export class Interpreter { static defaultInterpreterOptions: InterpreterOptions = { allowInheritance: false - } + }; private anonymCounter = 1; private seenSchemas: Map = new Map(); - + /** * Transforms a schema into instances of CommonModel by processing all keywords from schema documents and infers the model definition. - * + * * @param schema * @param interpreterOptions to control the interpret process */ - interpret(schema: InterpreterSchemaType, options: InterpreterOptions = Interpreter.defaultInterpreterOptions): CommonModel | undefined { + interpret( + schema: InterpreterSchemaType, + options: InterpreterOptions = Interpreter.defaultInterpreterOptions + ): CommonModel | undefined { if (this.seenSchemas.has(schema)) { - const cachedModel = this.seenSchemas.get(schema); + const cachedModel = this.seenSchemas.get(schema); if (cachedModel !== undefined) { return cachedModel; } @@ -41,7 +60,7 @@ export class Interpreter { //If it is a false validation schema return no CommonModel if (schema === false) { return undefined; - } + } const model = new CommonModel(); model.originalInput = schema; this.seenSchemas.set(schema, model); @@ -51,20 +70,36 @@ export class Interpreter { /** * Function to interpret a schema into a CommonModel. - * - * @param model - * @param schema + * + * @param model + * @param schema * @param interpreterOptions to control the interpret process */ - private interpretSchema(model: CommonModel, schema: InterpreterSchemaType, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions) { + private interpretSchema( + model: CommonModel, + schema: InterpreterSchemaType, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions + ) { if (schema === true) { - model.setType(['object', 'string', 'number', 'array', 'boolean', 'null', 'integer']); + model.setType([ + 'object', + 'string', + 'number', + 'array', + 'boolean', + 'null', + 'integer' + ]); } else if (typeof schema === 'object') { this.interpretSchemaObject(model, schema, interpreterOptions); } } - private interpretSchemaObject(model: CommonModel, schema: InterpreterSchemas, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions) { + private interpretSchemaObject( + model: CommonModel, + schema: InterpreterSchemas, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions + ) { if (schema.type !== undefined) { model.addTypes(schema.type); } @@ -78,55 +113,58 @@ export class Interpreter { interpretItems(schema, model, this, interpreterOptions); interpretProperties(schema, model, this, interpreterOptions); interpretAllOf(schema, model, this, interpreterOptions); + interpretOneOf(schema, model, this, interpreterOptions); + interpretOneOfWithAllOf(schema, model, this, interpreterOptions); + interpretOneOfWithProperties(schema, model, this, interpreterOptions); + interpretAnyOf(schema, model, this, interpreterOptions); interpretDependencies(schema, model, this, interpreterOptions); interpretConst(schema, model); interpretEnum(schema, model); - this.interpretAndCombineMultipleSchemas(schema.oneOf, model, schema, interpreterOptions); - this.interpretAndCombineMultipleSchemas(schema.anyOf, model, schema, interpreterOptions); - if (!(schema instanceof Draft4Schema) && !(schema instanceof Draft6Schema)) { - this.interpretAndCombineSchema(schema.then, model, schema, interpreterOptions); - this.interpretAndCombineSchema(schema.else, model, schema, interpreterOptions); + if ( + !(schema instanceof Draft4Schema) && + !(schema instanceof Draft6Schema) + ) { + this.interpretAndCombineSchema( + schema.then, + model, + schema, + interpreterOptions + ); + this.interpretAndCombineSchema( + schema.else, + model, + schema, + interpreterOptions + ); } interpretNot(schema, model, this, interpreterOptions); - //All schemas of type model object or enum MUST have ids - if (isModelObject(model) === true || isEnum(model) === true) { - model.$id = interpretName(schema) || `anonymSchema${this.anonymCounter++}`; - } else if ((!(schema instanceof Draft4Schema) && schema.$id !== undefined) || (schema instanceof Draft4Schema && schema.id !== undefined)) { - model.$id = interpretName(schema); - } + //All schemas MUST have ids as we do not know how it will be generated and when it will be needed + model.$id = interpretName(schema) || `anonymSchema${this.anonymCounter++}`; } /** * Go through a schema and combine the interpreted models together. - * + * * @param schema to go through * @param currentModel the current output * @param rootSchema the root schema to use as original schema when merged * @param interpreterOptions to control the interpret process */ - interpretAndCombineSchema(schema: InterpreterSchemaType | undefined, currentModel: CommonModel, rootSchema: any, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions): void { - if (typeof schema !== 'object') {return;} + interpretAndCombineSchema( + schema: InterpreterSchemaType | undefined, + currentModel: CommonModel, + rootSchema: any, + interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions + ): void { + if (typeof schema !== 'object') { + return; + } const model = this.interpret(schema, interpreterOptions); if (model !== undefined) { CommonModel.mergeCommonModels(currentModel, model, rootSchema); } } - - /** - * Go through multiple schemas and combine the interpreted models together. - * - * @param schema to go through - * @param currentModel the current output - * @param rootSchema the root schema to use as original schema when merged - * @param interpreterOptions to control the interpret process - */ - interpretAndCombineMultipleSchemas(schema: InterpreterSchemaType[] | undefined, currentModel: CommonModel, rootSchema: any, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions): void { - if (!Array.isArray(schema)) { return; } - for (const forEachSchema of schema) { - this.interpretAndCombineSchema(forEachSchema, currentModel, rootSchema, interpreterOptions); - } - } } diff --git a/src/interpreter/PostInterpreter.ts b/src/interpreter/PostInterpreter.ts deleted file mode 100644 index 81965394c9..0000000000 --- a/src/interpreter/PostInterpreter.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { CommonModel } from '../models'; -import { Logger } from '../utils'; -import { isEnum, isModelObject } from './Utils'; -/** - * Post process the interpreted model. By applying the following: - * - Ensure models are split as required - * - * @param model - */ -export function postInterpretModel(model: CommonModel): CommonModel[] { - const splitModels: CommonModel[] = [model]; - ensureModelsAreSplit(model, splitModels); - return splitModels; -} - -/** - * This function splits up a model if needed and add the new model to the list of models. - * - * @param model check if it should be split up - * @param iteratedModels which have already been split up - */ -function trySplitModels(model: CommonModel, splitModels: CommonModel[], iteratedModels: CommonModel[]): CommonModel { - let modelToReturn: CommonModel = model; - if (isModelObject(model) === true || isEnum(model) === true) { - Logger.info(`Splitting model ${model.$id || 'any'} since it should be on its own`); - const switchRootModel = new CommonModel(); - switchRootModel.$ref = model.$id; - modelToReturn = switchRootModel; - if (!splitModels.includes(model)) { - splitModels.push(model); - } - } - ensureModelsAreSplit(model, splitModels, iteratedModels); - return modelToReturn; -} - -/** - * Split up all models which should and use ref instead. - * - * @param model to ensure are split - * @param iteratedModels which are already split - */ -function ensureModelsAreSplit(model: CommonModel, splitModels: CommonModel[], iteratedModels: CommonModel[] = []): void { - // eslint-disable-next-line sonarjs/no-collapsible-if - if (iteratedModels.includes(model)) { return; } - iteratedModels.push(model); - if (model.properties) { - const existingProperties = model.properties; - for (const [prop, propSchema] of Object.entries(existingProperties)) { - model.properties[String(prop)] = trySplitModels(propSchema, splitModels, iteratedModels); - } - } - if (model.patternProperties) { - const existingPatternProperties = model.patternProperties; - for (const [pattern, patternModel] of Object.entries(existingPatternProperties)) { - model.patternProperties[String(pattern)] = trySplitModels(patternModel, splitModels, iteratedModels); - } - } - if (model.additionalProperties) { - model.additionalProperties = trySplitModels(model.additionalProperties, splitModels, iteratedModels); - } - if (model.items) { - let existingItems = model.items; - if (Array.isArray(existingItems)) { - for (const [itemIndex, itemModel] of existingItems.entries()) { - existingItems[Number(itemIndex)] = trySplitModels(itemModel, splitModels, iteratedModels); - } - } else { - existingItems = trySplitModels(existingItems, splitModels, iteratedModels); - } - model.items = existingItems; - } -} diff --git a/src/interpreter/Utils.ts b/src/interpreter/Utils.ts index 46084f2056..3e23ffd378 100644 --- a/src/interpreter/Utils.ts +++ b/src/interpreter/Utils.ts @@ -2,10 +2,10 @@ import { CommonModel } from '../models/CommonModel'; /** * Check if CommonModel is an enum - * - * @param model + * + * @param model */ -export function isEnum(model: CommonModel) : boolean { +export function isEnum(model: CommonModel): boolean { if (model.enum !== undefined) { return true; } @@ -14,9 +14,9 @@ export function isEnum(model: CommonModel) : boolean { /** * Check if CommonModel is a separate model or a simple model. - * @param model + * @param model */ -export function isModelObject(model: CommonModel) : boolean { +export function isModelObject(model: CommonModel): boolean { if (model.type !== undefined) { // If all possible JSON types are defined, don't split it even if it does contain object. if (Array.isArray(model.type) && model.type.length === 7) { @@ -29,7 +29,7 @@ export function isModelObject(model: CommonModel) : boolean { /** * Infers the JSON Schema type from value - * + * * @param value to infer type of */ export function inferTypeFromValue(value: any): string { @@ -42,13 +42,13 @@ export function inferTypeFromValue(value: any): string { const typeOfEnum = typeof value; if (typeOfEnum === 'bigint') { return 'integer'; - } + } return typeOfEnum; } /** * Find the name for simplified version of schema - * + * * @param schema to find the name */ export function interpretName(schema: any | boolean): string | undefined { diff --git a/src/models/AsyncapiV2Schema.ts b/src/models/AsyncapiV2Schema.ts index fc97d9811b..df2db03970 100644 --- a/src/models/AsyncapiV2Schema.ts +++ b/src/models/AsyncapiV2Schema.ts @@ -14,9 +14,9 @@ export class AsyncapiV2ExternalDocumentation { /** * AsyncAPI schema model - * + * * Based on Draft 7 with additions - * + * * https://www.asyncapi.com/docs/specifications/v2.0.0#schemaObject * https://www.asyncapi.com/docs/specifications/v2.1.0#schemaObject * https://www.asyncapi.com/docs/specifications/v2.2.0#schemaObject @@ -39,18 +39,18 @@ export class AsyncapiV2Schema { allOf?: (AsyncapiV2Schema | boolean)[]; oneOf?: (AsyncapiV2Schema | boolean)[]; anyOf?: (AsyncapiV2Schema | boolean)[]; - not?: (AsyncapiV2Schema | boolean); - dependencies?: { [key: string]: AsyncapiV2Schema | boolean | string[]; }; + not?: AsyncapiV2Schema | boolean; + dependencies?: { [key: string]: AsyncapiV2Schema | boolean | string[] }; format?: string; - definitions?: { [key: string]: AsyncapiV2Schema | boolean; }; + definitions?: { [key: string]: AsyncapiV2Schema | boolean }; description?: string; default?: any; type?: string | string[]; enum?: any[]; items?: AsyncapiV2Schema | AsyncapiV2Schema[] | boolean; - properties?: { [key: string]: AsyncapiV2Schema | boolean; }; + properties?: { [key: string]: AsyncapiV2Schema | boolean }; additionalProperties?: AsyncapiV2Schema | boolean; - patternProperties?: { [key: string]: AsyncapiV2Schema | boolean; }; + patternProperties?: { [key: string]: AsyncapiV2Schema | boolean }; $ref?: string; required?: string[]; additionalItems?: AsyncapiV2Schema | boolean; @@ -85,17 +85,22 @@ export class AsyncapiV2Schema { /** * Takes a deep copy of the input object and converts it to an instance of AsyncapiV2Schema. - * - * @param object + * + * @param object */ static toSchema(object: Record): AsyncapiV2Schema { const convertedSchema = AsyncapiV2Schema.internalToSchema(object); if (convertedSchema instanceof AsyncapiV2Schema) { return convertedSchema; } - throw new Error('Could not convert input to expected copy of AsyncapiV2Schema'); + throw new Error( + 'Could not convert input to expected copy of AsyncapiV2Schema' + ); } - private static internalToSchema(object: any, seenSchemas: Map = new Map()): any { + private static internalToSchema( + object: any, + seenSchemas: Map = new Map() + ): any { // if primitive types return as is if (null === object || 'object' !== typeof object) { return object; @@ -108,7 +113,10 @@ export class AsyncapiV2Schema { if (object instanceof Array) { const copy: any = []; for (let i = 0, len = object.length; i < len; i++) { - copy[Number(i)] = AsyncapiV2Schema.internalToSchema(object[Number(i)], seenSchemas); + copy[Number(i)] = AsyncapiV2Schema.internalToSchema( + object[Number(i)], + seenSchemas + ); } return copy; } @@ -119,13 +127,16 @@ export class AsyncapiV2Schema { let copyProp = prop; // Ignore value properties (those with `any` type) as they should be saved as is regardless of value - if (propName !== 'default' && + if ( + propName !== 'default' && propName !== 'examples' && propName !== 'const' && - propName !== 'enum') { + propName !== 'enum' + ) { // Custom convert to External documentation instance if (propName === 'externalDocs') { - schema.externalDocs = AsyncapiV2ExternalDocumentation.toExternalDocumentation(prop); + schema.externalDocs = + AsyncapiV2ExternalDocumentation.toExternalDocumentation(prop); continue; } copyProp = AsyncapiV2Schema.internalToSchema(prop, seenSchemas); diff --git a/src/models/CommonInputModel.ts b/src/models/CommonInputModel.ts deleted file mode 100644 index 035644bd11..0000000000 --- a/src/models/CommonInputModel.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { CommonModel } from './CommonModel'; - -/** - * This class is the wrapper for simplified models and the rest of the context needed for further generate typed models. - */ -export class CommonInputModel { - models: {[key: string]: CommonModel} = {}; - originalInput: any = {}; -} diff --git a/src/models/CommonModel.ts b/src/models/CommonModel.ts index 58521bde7f..c5d9c18616 100644 --- a/src/models/CommonModel.ts +++ b/src/models/CommonModel.ts @@ -10,27 +10,32 @@ export class CommonModel { type?: string | string[]; enum?: any[]; items?: CommonModel | CommonModel[]; - properties?: { [key: string]: CommonModel; }; + properties?: { [key: string]: CommonModel }; additionalProperties?: CommonModel; - patternProperties?: { [key: string]: CommonModel; }; - $ref?: string; + patternProperties?: { [key: string]: CommonModel }; required?: string[]; additionalItems?: CommonModel; + union?: CommonModel[]; /** * Takes a deep copy of the input object and converts it to an instance of CommonModel. - * + * * @param object to transform * @returns CommonModel instance of the object */ - static toCommonModel(object: Record | CommonModel): CommonModel { + static toCommonModel( + object: Record | CommonModel + ): CommonModel { const convertedSchema = CommonModel.internalToSchema(object); if (convertedSchema instanceof CommonModel) { return convertedSchema; } throw new Error('Could not convert input to expected copy of CommonModel'); } - private static internalToSchema(object: any, seenSchemas: Map = new Map()): any { + private static internalToSchema( + object: any, + seenSchemas: Map = new Map() + ): any { // if primitive types return as is if (null === object || 'object' !== typeof object) { return object; @@ -43,7 +48,10 @@ export class CommonModel { if (object instanceof Array) { const copy: any = []; for (let i = 0, len = object.length; i < len; i++) { - copy[Number(i)] = CommonModel.internalToSchema(object[Number(i)], seenSchemas); + copy[Number(i)] = CommonModel.internalToSchema( + object[Number(i)], + seenSchemas + ); } return copy; } @@ -54,8 +62,7 @@ export class CommonModel { let copyProp = prop; // Ignore value properties (those with `any` type) as they should be saved as is regardless of value - if (propName !== 'originalInput' && - propName !== 'enum') { + if (propName !== 'originalInput' && propName !== 'enum') { copyProp = CommonModel.internalToSchema(prop, seenSchemas); } (schema as any)[String(propName)] = copyProp; @@ -65,22 +72,24 @@ export class CommonModel { /** * Retrieves data from originalInput by given key - * + * * @param key given key * @returns {any} */ getFromOriginalInput(key: K): any { const input = this.originalInput || {}; - if (typeof input === 'boolean') {return undefined;} + if (typeof input === 'boolean') { + return undefined; + } return input[String(key)]; } /** * Set the types of the model - * + * * @param type */ - setType(type : string | string[] | undefined): void { + setType(type: string | string[] | undefined): void { if (Array.isArray(type)) { if (type.length === 0) { this.type = undefined; @@ -89,25 +98,27 @@ export class CommonModel { this.type = type[0]; return; } - } + } this.type = type; } - + /** * Removes type(s) from model type - * - * @param types + * + * @param types */ - removeType(typesToRemove : string | string[]): void { + removeType(typesToRemove: string | string[]): void { if (Array.isArray(typesToRemove)) { for (const type of typesToRemove) { this.removeType(type); } } else if (this.type !== undefined && this.type.includes(typesToRemove)) { if (Array.isArray(this.type)) { - this.setType(this.type.filter((el) => { - return el !== typesToRemove; - })); + this.setType( + this.type.filter((el) => { + return el !== typesToRemove; + }) + ); } else { this.setType(undefined); } @@ -116,9 +127,9 @@ export class CommonModel { /** * Adds types to the existing model types. - * + * * Makes sure to only keep a single type incase of duplicates. - * + * * @param types which types we should try and add to the existing output */ addTypes(types: string[] | string): void { @@ -137,7 +148,7 @@ export class CommonModel { /** * Checks if given property name is required in object - * + * * @param propertyName given property name * @returns {boolean} */ @@ -150,16 +161,25 @@ export class CommonModel { /** * Adds an item to the model. - * + * * If items already exist the two are merged. - * - * @param itemModel + * + * @param itemModel * @param originalInput corresponding input that got interpreted to this model */ addItem(itemModel: CommonModel, originalInput: any): void { if (this.items !== undefined) { - Logger.warn(`While trying to add item to model ${this.$id}, duplicate items found. Merging models together to form a unified item model.`, itemModel, originalInput, this); - this.items = CommonModel.mergeCommonModels(this.items as CommonModel, itemModel, originalInput); + Logger.warn( + `While trying to add item to model ${this.$id}, duplicate items found. Merging models together to form a unified item model.`, + itemModel, + originalInput, + this + ); + this.items = CommonModel.mergeCommonModels( + this.items as CommonModel, + itemModel, + originalInput + ); } else { this.items = itemModel; } @@ -167,38 +187,71 @@ export class CommonModel { /** * Adds a tuple to the model. - * + * * If a item already exist it will be merged. - * - * @param tupleModel + * + * @param tupleModel * @param originalInput corresponding input that got interpreted to this model - * @param index + * @param index */ - addItemTuple(tupleModel: CommonModel, originalInput: any, index: number): void { + addItemTuple( + tupleModel: CommonModel, + originalInput: any, + index: number + ): void { let modelItems = this.items; if (!Array.isArray(modelItems)) { - Logger.warn('Trying to add item tuple to a non-tuple item, will drop existing item model', tupleModel, originalInput, index); + Logger.warn( + 'Trying to add item tuple to a non-tuple item, will drop existing item model', + tupleModel, + originalInput, + index + ); modelItems = []; } const existingModelAtIndex = modelItems[Number(index)]; if (existingModelAtIndex !== undefined) { - Logger.warn('Trying to add item tuple at index ${index} but it was already occupied, merging models', tupleModel, originalInput, index); - modelItems[Number(index)] = CommonModel.mergeCommonModels(existingModelAtIndex, tupleModel, originalInput); + Logger.warn( + 'Trying to add item tuple at index ${index} but it was already occupied, merging models', + tupleModel, + originalInput, + index + ); + modelItems[Number(index)] = CommonModel.mergeCommonModels( + existingModelAtIndex, + tupleModel, + originalInput + ); } else { modelItems[Number(index)] = tupleModel; } this.items = modelItems; } + /** + * Adds a union model to the model. + * + * @param unionModel + */ + addItemUnion(unionModel: CommonModel): void { + if (Array.isArray(this.union)) { + this.union.push(unionModel); + } else { + this.union = [unionModel]; + } + } + /** * Add enum value to the model. - * + * * Ensures no duplicates are added. - * - * @param enumValue + * + * @param enumValue */ addEnum(enumValue: any): void { - if (this.enum === undefined) {this.enum = [];} + if (this.enum === undefined) { + this.enum = []; + } if (!this.enum.includes(enumValue)) { this.enum.push(enumValue); } @@ -206,11 +259,13 @@ export class CommonModel { /** * Remove enum from model. - * - * @param enumValue + * + * @param enumValue */ removeEnum(enumsToRemove: any | any[]): void { - if (this.enum === undefined || enumsToRemove === undefined) {return;} + if (this.enum === undefined || enumsToRemove === undefined) { + return; + } if (Array.isArray(enumsToRemove)) { for (const enumToRemove of enumsToRemove) { this.removeEnum(enumToRemove); @@ -230,16 +285,31 @@ export class CommonModel { /** * Adds a property to the model. * If the property already exist the two are merged. - * - * @param propertyName - * @param propertyModel + * + * @param propertyName + * @param propertyModel * @param originalInput corresponding input that got interpreted to this model */ - addProperty(propertyName: string, propertyModel: CommonModel, originalInput: any): void { - if (this.properties === undefined) {this.properties = {};} + addProperty( + propertyName: string, + propertyModel: CommonModel, + originalInput: any + ): void { + if (this.properties === undefined) { + this.properties = {}; + } if (this.properties[`${propertyName}`] !== undefined) { - Logger.warn(`While trying to add property to model, duplicate properties found. Merging models together for property ${propertyName}`, propertyModel, originalInput, this); - this.properties[String(propertyName)] = CommonModel.mergeCommonModels(this.properties[String(propertyName)], propertyModel, originalInput); + Logger.warn( + `While trying to add property to model, duplicate properties found. Merging models together for property ${propertyName}`, + propertyModel, + originalInput, + this + ); + this.properties[String(propertyName)] = CommonModel.mergeCommonModels( + this.properties[String(propertyName)], + propertyModel, + originalInput + ); } else { this.properties[String(propertyName)] = propertyModel; } @@ -248,217 +318,265 @@ export class CommonModel { /** * Adds additionalProperty to the model. * If another model already exist the two are merged. - * - * @param additionalPropertiesModel + * + * @param additionalPropertiesModel * @param originalInput corresponding input that got interpreted to this model corresponding input that got interpreted to this model */ - addAdditionalProperty(additionalPropertiesModel: CommonModel, originalInput: any): void { + addAdditionalProperty( + additionalPropertiesModel: CommonModel, + originalInput: any + ): void { if (this.additionalProperties !== undefined) { - Logger.warn('While trying to add additionalProperties to model, but it is already present, merging models together', additionalPropertiesModel, originalInput, this); - this.additionalProperties = CommonModel.mergeCommonModels(this.additionalProperties, additionalPropertiesModel, originalInput); + Logger.warn( + 'While trying to add additionalProperties to model, but it is already present, merging models together', + additionalPropertiesModel, + originalInput, + this + ); + this.additionalProperties = CommonModel.mergeCommonModels( + this.additionalProperties, + additionalPropertiesModel, + originalInput + ); } else { this.additionalProperties = additionalPropertiesModel; } } /** - * Adds additionalItems to the model. - * If another model already exist the two are merged. - * - * @param additionalItemsModel - * @param originalInput corresponding input that got interpreted to this model + * Adds a patternProperty to the model. + * If the pattern already exist the two models are merged. + * + * @param pattern + * @param patternModel + * @param originalInput corresponding input that got interpreted to this model */ - addAdditionalItems(additionalItemsModel: CommonModel, originalInput: any): void { - if (this.additionalItems !== undefined) { - Logger.warn('While trying to add additionalItems to model, but it is already present, merging models together', additionalItemsModel, originalInput, this); - this.additionalItems = CommonModel.mergeCommonModels(this.additionalItems, additionalItemsModel, originalInput); + addPatternProperty( + pattern: string, + patternModel: CommonModel, + originalInput: any + ): void { + if (this.patternProperties === undefined) { + this.patternProperties = {}; + } + if (this.patternProperties[`${pattern}`] !== undefined) { + Logger.warn( + `While trying to add patternProperty to model, duplicate patterns found. Merging pattern models together for pattern ${pattern}`, + patternModel, + originalInput, + this + ); + this.patternProperties[String(pattern)] = CommonModel.mergeCommonModels( + this.patternProperties[String(pattern)], + patternModel, + originalInput + ); } else { - this.additionalItems = additionalItemsModel; + this.patternProperties[String(pattern)] = patternModel; } } - + /** - * Adds a patternProperty to the model. - * If the pattern already exist the two models are merged. - * - * @param pattern - * @param patternModel - * @param originalInput corresponding input that got interpreted to this model + * Adds additionalItems to the model. + * If another model already exist the two are merged. + * + * @param additionalItemsModel + * @param originalInput corresponding input that got interpreted to this model */ - addPatternProperty(pattern: string, patternModel: CommonModel, originalInput: any): void { - if (this.patternProperties === undefined) {this.patternProperties = {};} - if (this.patternProperties[`${pattern}`] !== undefined) { - Logger.warn(`While trying to add patternProperty to model, duplicate patterns found. Merging pattern models together for pattern ${pattern}`, patternModel, originalInput, this); - this.patternProperties[String(pattern)] = CommonModel.mergeCommonModels(this.patternProperties[String(pattern)], patternModel, originalInput); + addAdditionalItems( + additionalItemsModel: CommonModel, + originalInput: any + ): void { + if (this.additionalItems !== undefined) { + Logger.warn( + 'While trying to add additionalItems to model, but it is already present, merging models together', + additionalItemsModel, + originalInput, + this + ); + this.additionalItems = CommonModel.mergeCommonModels( + this.additionalItems, + additionalItemsModel, + originalInput + ); } else { - this.patternProperties[String(pattern)] = patternModel; + this.additionalItems = additionalItemsModel; } } - + /** * Adds another model this model should extend. - * + * * It is only allowed to extend if the other model have $id and is not already being extended. - * - * @param extendedModel + * + * @param extendedModel */ addExtendedModel(extendedModel: CommonModel): void { if (extendedModel.$id === undefined) { - Logger.error('Found no $id for allOf model and cannot extend the existing model, this should never happen.', this, extendedModel); + Logger.error( + 'Found no $id for allOf model and cannot extend the existing model, this should never happen.', + this, + extendedModel + ); return; } this.extend = this.extend || []; - if (this.extend.includes(extendedModel.$id)) { - Logger.info(`${this.$id} model already extends model ${extendedModel.$id}.`, this, extendedModel); + if (this.extend.includes(extendedModel.$id)) { + Logger.info( + `${this.$id} model already extends model ${extendedModel.$id}.`, + this, + extendedModel + ); return; } this.extend.push(extendedModel.$id); } /** - * Returns an array of unique `$id`s from all the CommonModel's this model depends on. + * Merge two common model properties together + * + * @param mergeTo + * @param mergeFrom + * @param originalInput corresponding input that got interpreted to this model + * @param alreadyIteratedModels */ - // eslint-disable-next-line sonarjs/cognitive-complexity - getNearestDependencies(): string[] { - const dependsOn = []; - - if (this.$ref !== undefined) { - return [this.$ref]; + private static mergeProperties( + mergeTo: CommonModel, + mergeFrom: CommonModel, + originalInput: any, + alreadyIteratedModels: Map = new Map() + ) { + if (!mergeTo.properties) { + mergeTo.properties = mergeFrom.properties; + return; } - if (this.additionalProperties !== undefined) { - dependsOn.push(...this.additionalProperties.getNearestDependencies()); - } - if (this.extend !== undefined) { - dependsOn.push(...this.extend); - } - if (this.items !== undefined) { - const items = Array.isArray(this.items) ? this.items : [this.items]; - for (const item of items) { - dependsOn.push(...item.getNearestDependencies()); - } - } - if (this.properties !== undefined && Object.keys(this.properties).length) { - for (const property of Object.values(this.properties)) { - dependsOn.push(...property.getNearestDependencies()); - } - } - if (this.patternProperties !== undefined && Object.keys(this.patternProperties).length) { - for (const patternProperty of Object.values(this.patternProperties)) { - dependsOn.push(...patternProperty.getNearestDependencies()); - } - } - if (this.additionalItems !== undefined) { - dependsOn.push(...this.additionalItems.getNearestDependencies()); + if (!mergeFrom.properties) { + return; } - return [...new Set(dependsOn)]; - } - /** - * Merge two common model properties together - * - * @param mergeTo - * @param mergeFrom - * @param originalInput corresponding input that got interpreted to this model - * @param alreadyIteratedModels - */ - private static mergeProperties(mergeTo: CommonModel, mergeFrom: CommonModel, originalInput: any, alreadyIteratedModels: Map = new Map()) { - const mergeToProperties = mergeTo.properties; - const mergeFromProperties = mergeFrom.properties; - if (mergeFromProperties !== undefined) { - if (mergeToProperties === undefined) { - mergeTo.properties = mergeFromProperties; + mergeTo.properties = { + ...mergeTo.properties + }; + + for (const [propName, propValue] of Object.entries(mergeFrom.properties)) { + if (!mergeTo.properties[String(propName)]) { + mergeTo.properties[String(propName)] = propValue; } else { - for (const [propName, prop] of Object.entries(mergeFromProperties)) { - if (mergeToProperties[String(propName)] !== undefined) { - Logger.warn(`Found duplicate properties ${propName} for model. Model property from ${mergeFrom.$id || 'unknown'} merged into ${mergeTo.$id || 'unknown'}`, mergeTo, mergeFrom, originalInput); - mergeToProperties[String(propName)] = CommonModel.mergeCommonModels(mergeToProperties[String(propName)], prop, originalInput, alreadyIteratedModels); - } else { - mergeToProperties[String(propName)] = prop; - } - } + Logger.warn( + `Found duplicate properties ${propName} for model. Model property from ${ + mergeFrom.$id || 'unknown' + } merged into ${mergeTo.$id || 'unknown'}`, + mergeTo, + mergeFrom, + originalInput + ); + mergeTo.properties[String(propName)] = CommonModel.mergeCommonModels( + mergeTo.properties[String(propName)], + propValue, + originalInput, + alreadyIteratedModels + ); } } } /** - * Merge two common model additionalProperties together - * - * @param mergeTo - * @param mergeFrom - * @param originalInput corresponding input that got interpreted to this model + * Merge two common model additionalProperties together + * + * @param mergeTo + * @param mergeFrom + * @param originalInput corresponding input that got interpreted to this model * @param alreadyIteratedModels */ - private static mergeAdditionalProperties(mergeTo: CommonModel, mergeFrom: CommonModel, originalInput: any, alreadyIteratedModels: Map = new Map()) { + private static mergeAdditionalProperties( + mergeTo: CommonModel, + mergeFrom: CommonModel, + originalInput: any, + alreadyIteratedModels: Map = new Map() + ) { const mergeToAdditionalProperties = mergeTo.additionalProperties; const mergeFromAdditionalProperties = mergeFrom.additionalProperties; if (mergeFromAdditionalProperties !== undefined) { if (mergeToAdditionalProperties === undefined) { mergeTo.additionalProperties = mergeFromAdditionalProperties; } else { - Logger.warn(`Found duplicate additionalProperties for model. additionalProperties from ${mergeFrom.$id || 'unknown'} merged into ${mergeTo.$id || 'unknown'}`, mergeTo, mergeFrom, originalInput); - mergeTo.additionalProperties = CommonModel.mergeCommonModels(mergeToAdditionalProperties, mergeFromAdditionalProperties, originalInput, alreadyIteratedModels); + Logger.warn( + `Found duplicate additionalProperties for model. additionalProperties from ${ + mergeFrom.$id || 'unknown' + } merged into ${mergeTo.$id || 'unknown'}`, + mergeTo, + mergeFrom, + originalInput + ); + mergeTo.additionalProperties = CommonModel.mergeCommonModels( + mergeToAdditionalProperties, + mergeFromAdditionalProperties, + originalInput, + alreadyIteratedModels + ); } } } + /** - * Merge two common model additionalItems together - * - * @param mergeTo - * @param mergeFrom - * @param originalInput corresponding input that got interpreted to this model + * Merge two common model additionalItems together + * + * @param mergeTo + * @param mergeFrom + * @param originalInput corresponding input that got interpreted to this model * @param alreadyIteratedModels */ - private static mergeAdditionalItems(mergeTo: CommonModel, mergeFrom: CommonModel, originalInput: any, alreadyIteratedModels: Map = new Map()) { + private static mergeAdditionalItems( + mergeTo: CommonModel, + mergeFrom: CommonModel, + originalInput: any, + alreadyIteratedModels: Map = new Map() + ) { const mergeToAdditionalItems = mergeTo.additionalItems; - const mergeFromAdditionalItems= mergeFrom.additionalItems; + const mergeFromAdditionalItems = mergeFrom.additionalItems; if (mergeFromAdditionalItems !== undefined) { if (mergeToAdditionalItems === undefined) { mergeTo.additionalItems = mergeFromAdditionalItems; } else { - Logger.warn(`Found duplicate additionalItems for model. additionalItems from ${mergeFrom.$id || 'unknown'} merged into ${mergeTo.$id || 'unknown'}`, mergeTo, mergeFrom, originalInput); - mergeTo.additionalItems = CommonModel.mergeCommonModels(mergeToAdditionalItems, mergeFromAdditionalItems, originalInput, alreadyIteratedModels); - } - } - } - /** - * Merge two common model pattern properties together - * - * @param mergeTo - * @param mergeFrom - * @param originalInput corresponding input that got interpreted to this model - * @param alreadyIteratedModels - */ - private static mergePatternProperties(mergeTo: CommonModel, mergeFrom: CommonModel, originalInput: any, alreadyIteratedModels: Map = new Map()) { - const mergeToPatternProperties = mergeTo.patternProperties; - const mergeFromPatternProperties = mergeFrom.patternProperties; - if (mergeFromPatternProperties !== undefined) { - if (mergeToPatternProperties === undefined) { - mergeTo.patternProperties = mergeFromPatternProperties; - } else { - for (const [pattern, patternModel] of Object.entries(mergeFromPatternProperties)) { - if (mergeToPatternProperties[String(pattern)] !== undefined) { - Logger.warn(`Found duplicate pattern ${pattern} for model. Model pattern for ${mergeFrom.$id || 'unknown'} merged into ${mergeTo.$id || 'unknown'}`, mergeTo, mergeFrom, originalInput); - mergeToPatternProperties[String(pattern)] = CommonModel.mergeCommonModels(mergeToPatternProperties[String(pattern)], patternModel, originalInput, alreadyIteratedModels); - } else { - mergeToPatternProperties[String(pattern)] = patternModel; - } - } + Logger.warn( + `Found duplicate additionalItems for model. additionalItems from ${ + mergeFrom.$id || 'unknown' + } merged into ${mergeTo.$id || 'unknown'}`, + mergeTo, + mergeFrom, + originalInput + ); + mergeTo.additionalItems = CommonModel.mergeCommonModels( + mergeToAdditionalItems, + mergeFromAdditionalItems, + originalInput, + alreadyIteratedModels + ); } } } /** * Merge items together, prefer tuples over simple array since it is more strict. - * - * @param mergeTo - * @param mergeFrom - * @param originalInput corresponding input that got interpreted to this model + * + * @param mergeTo + * @param mergeFrom + * @param originalInput corresponding input that got interpreted to this model * @param alreadyIteratedModels */ // eslint-disable-next-line sonarjs/cognitive-complexity - private static mergeItems(mergeTo: CommonModel, mergeFrom: CommonModel, originalInput: any, alreadyIteratedModels: Map = new Map()) { - if (mergeFrom.items === undefined) { return; } - if (Array.isArray(mergeFrom.items) && mergeFrom.items.length === 0) { return; } + private static mergeItems( + mergeTo: CommonModel, + mergeFrom: CommonModel, + originalInput: any, + alreadyIteratedModels: Map = new Map() + ) { + if (mergeFrom.items === undefined) { + return; + } + if (Array.isArray(mergeFrom.items) && mergeFrom.items.length === 0) { + return; + } if (mergeTo.items === undefined) { mergeTo.items = mergeFrom.items; return; @@ -467,13 +585,24 @@ export class CommonModel { //mergeFrom and mergeTo is not tuple if (!Array.isArray(mergeFrom.items) && !Array.isArray(mergeToItems)) { - mergeTo.items = CommonModel.mergeCommonModels(mergeToItems, mergeFrom.items, originalInput, alreadyIteratedModels); + mergeTo.items = CommonModel.mergeCommonModels( + mergeToItems, + mergeFrom.items, + originalInput, + alreadyIteratedModels + ); } //mergeFrom and mergeTo is tuple if (Array.isArray(mergeFrom.items) && Array.isArray(mergeToItems)) { for (const [index, mergeFromTupleModel] of mergeFrom.items.entries()) { - (mergeTo.items as CommonModel[])[Number(index)] = CommonModel.mergeCommonModels(mergeToItems[Number(index)], mergeFromTupleModel, originalInput, alreadyIteratedModels); + (mergeTo.items as CommonModel[])[Number(index)] = + CommonModel.mergeCommonModels( + mergeToItems[Number(index)], + mergeFromTupleModel, + originalInput, + alreadyIteratedModels + ); } } @@ -484,11 +613,58 @@ export class CommonModel { //mergeFrom is not tuple && mergeTo is, do nothing } + /** + * Merge two common model pattern properties together + * + * @param mergeTo + * @param mergeFrom + * @param originalInput corresponding input that got interpreted to this model + * @param alreadyIteratedModels + */ + private static mergePatternProperties( + mergeTo: CommonModel, + mergeFrom: CommonModel, + originalInput: any, + alreadyIteratedModels: Map = new Map() + ) { + const mergeToPatternProperties = mergeTo.patternProperties; + const mergeFromPatternProperties = mergeFrom.patternProperties; + if (mergeFromPatternProperties !== undefined) { + if (mergeToPatternProperties === undefined) { + mergeTo.patternProperties = mergeFromPatternProperties; + } else { + for (const [pattern, patternModel] of Object.entries( + mergeFromPatternProperties + )) { + if (mergeToPatternProperties[String(pattern)] !== undefined) { + Logger.warn( + `Found duplicate pattern ${pattern} for model. Model pattern for ${ + mergeFrom.$id || 'unknown' + } merged into ${mergeTo.$id || 'unknown'}`, + mergeTo, + mergeFrom, + originalInput + ); + mergeToPatternProperties[String(pattern)] = + CommonModel.mergeCommonModels( + mergeToPatternProperties[String(pattern)], + patternModel, + originalInput, + alreadyIteratedModels + ); + } else { + mergeToPatternProperties[String(pattern)] = patternModel; + } + } + } + } + } + /** * Merge types together - * - * @param mergeTo - * @param mergeFrom + * + * @param mergeTo + * @param mergeFrom */ private static mergeTypes(mergeTo: CommonModel, mergeFrom: CommonModel) { //Only add the types that do not already exist @@ -516,33 +692,75 @@ export class CommonModel { /** * Only merge if left side is undefined and right side is sat OR both sides are defined - * - * @param mergeTo - * @param mergeFrom - * @param originalInput corresponding input that got interpreted to this model + * + * @param mergeTo + * @param mergeFrom + * @param originalInput corresponding input that got interpreted to this model * @param alreadyIteratedModels */ - static mergeCommonModels(mergeTo: CommonModel | undefined, mergeFrom: CommonModel, originalInput: any, alreadyIteratedModels: Map = new Map()): CommonModel { - if (mergeTo === undefined) {return mergeFrom;} - Logger.debug(`Merging model ${mergeFrom.$id || 'unknown'} into ${mergeTo.$id || 'unknown'}`, mergeTo, mergeFrom, originalInput); - if (alreadyIteratedModels.has(mergeFrom)) {return alreadyIteratedModels.get(mergeFrom) as CommonModel;} + static mergeCommonModels( + mergeTo: CommonModel | undefined, + mergeFrom: CommonModel, + originalInput: any, + alreadyIteratedModels: Map = new Map() + ): CommonModel { + if (mergeTo === undefined) { + return mergeFrom; + } + Logger.debug( + `Merging model ${mergeFrom.$id || 'unknown'} into ${ + mergeTo.$id || 'unknown' + }`, + mergeTo, + mergeFrom, + originalInput + ); + if (alreadyIteratedModels.has(mergeFrom)) { + return alreadyIteratedModels.get(mergeFrom) as CommonModel; + } alreadyIteratedModels.set(mergeFrom, mergeTo); - CommonModel.mergeAdditionalProperties(mergeTo, mergeFrom, originalInput, alreadyIteratedModels); - CommonModel.mergeAdditionalItems(mergeTo, mergeFrom, originalInput, alreadyIteratedModels); - CommonModel.mergePatternProperties(mergeTo, mergeFrom, originalInput, alreadyIteratedModels); - CommonModel.mergeProperties(mergeTo, mergeFrom, originalInput, alreadyIteratedModels); - CommonModel.mergeItems(mergeTo, mergeFrom, originalInput, alreadyIteratedModels); + CommonModel.mergeAdditionalProperties( + mergeTo, + mergeFrom, + originalInput, + alreadyIteratedModels + ); + CommonModel.mergePatternProperties( + mergeTo, + mergeFrom, + originalInput, + alreadyIteratedModels + ); + CommonModel.mergeAdditionalItems( + mergeTo, + mergeFrom, + originalInput, + alreadyIteratedModels + ); + CommonModel.mergeProperties( + mergeTo, + mergeFrom, + originalInput, + alreadyIteratedModels + ); + CommonModel.mergeItems( + mergeTo, + mergeFrom, + originalInput, + alreadyIteratedModels + ); CommonModel.mergeTypes(mergeTo, mergeFrom); if (mergeFrom.enum !== undefined) { - mergeTo.enum = [... new Set([...(mergeTo.enum || []), ...mergeFrom.enum])]; + mergeTo.enum = [...new Set([...(mergeTo.enum || []), ...mergeFrom.enum])]; } if (mergeFrom.required !== undefined) { - mergeTo.required = [... new Set([...(mergeTo.required || []), ...mergeFrom.required])]; + mergeTo.required = [ + ...new Set([...(mergeTo.required || []), ...mergeFrom.required]) + ]; } mergeTo.$id = mergeTo.$id || mergeFrom.$id; - mergeTo.$ref = mergeTo.$ref || mergeFrom.$ref; mergeTo.extend = mergeTo.extend || mergeFrom.extend; mergeTo.originalInput = originalInput; return mergeTo; diff --git a/src/models/ConstrainedMetaModel.ts b/src/models/ConstrainedMetaModel.ts new file mode 100644 index 0000000000..6eb7f994a0 --- /dev/null +++ b/src/models/ConstrainedMetaModel.ts @@ -0,0 +1,220 @@ +import { makeUnique } from '../helpers/DependencyHelpers'; +import { MetaModel } from './MetaModel'; + +export abstract class ConstrainedMetaModel extends MetaModel { + constructor(name: string, originalInput: any, public type: string) { + super(name, originalInput); + } + + /** + * Get the nearest constrained meta models for the constrained model. + * + * This is often used when you want to know which other models you are referencing. + */ + getNearestDependencies(): ConstrainedMetaModel[] { + return []; + } +} + +export class ConstrainedReferenceModel extends ConstrainedMetaModel { + constructor( + name: string, + originalInput: any, + type: string, + public ref: ConstrainedMetaModel + ) { + super(name, originalInput, type); + } +} +export class ConstrainedAnyModel extends ConstrainedMetaModel {} +export class ConstrainedFloatModel extends ConstrainedMetaModel {} +export class ConstrainedIntegerModel extends ConstrainedMetaModel {} +export class ConstrainedStringModel extends ConstrainedMetaModel {} +export class ConstrainedBooleanModel extends ConstrainedMetaModel {} +export class ConstrainedTupleValueModel { + constructor(public index: number, public value: ConstrainedMetaModel) {} +} +export class ConstrainedTupleModel extends ConstrainedMetaModel { + constructor( + name: string, + originalInput: any, + type: string, + public tuple: ConstrainedTupleValueModel[] + ) { + super(name, originalInput, type); + } + + getNearestDependencies(): ConstrainedMetaModel[] { + let dependencyModels: ConstrainedMetaModel[] = []; + for (const tupleModel of Object.values(this.tuple)) { + if (tupleModel.value instanceof ConstrainedReferenceModel) { + dependencyModels.push(tupleModel.value); + } else { + //Lets check the non-reference model for dependencies + dependencyModels = [ + ...dependencyModels, + ...tupleModel.value.getNearestDependencies() + ]; + } + } + + //Ensure no duplicate references + dependencyModels = makeUnique(dependencyModels); + + return dependencyModels; + } +} +export class ConstrainedObjectPropertyModel { + constructor( + public propertyName: string, + public unconstrainedPropertyName: string, + public required: boolean, + public property: ConstrainedMetaModel + ) {} +} +export class ConstrainedArrayModel extends ConstrainedMetaModel { + constructor( + name: string, + originalInput: any, + type: string, + public valueModel: ConstrainedMetaModel + ) { + super(name, originalInput, type); + } + + getNearestDependencies(): ConstrainedMetaModel[] { + if (this.valueModel.name !== this.name) { + if (this.valueModel instanceof ConstrainedReferenceModel) { + return [this.valueModel]; + } + return this.valueModel.getNearestDependencies(); + } + return []; + } +} +export class ConstrainedUnionModel extends ConstrainedMetaModel { + constructor( + name: string, + originalInput: any, + type: string, + public union: ConstrainedMetaModel[] + ) { + super(name, originalInput, type); + } + + getNearestDependencies(): ConstrainedMetaModel[] { + let dependencyModels: ConstrainedMetaModel[] = []; + for (const unionModel of Object.values(this.union)) { + if (unionModel instanceof ConstrainedReferenceModel) { + dependencyModels.push(unionModel); + } else { + //Lets check the non-reference model for dependencies + dependencyModels = [ + ...dependencyModels, + ...unionModel.getNearestDependencies() + ]; + } + } + + //Ensure no duplicate references + dependencyModels = makeUnique(dependencyModels); + + return dependencyModels; + } +} +export class ConstrainedEnumValueModel { + constructor(public key: string, public value: any) {} +} +export class ConstrainedEnumModel extends ConstrainedMetaModel { + constructor( + name: string, + originalInput: any, + type: string, + public values: ConstrainedEnumValueModel[] + ) { + super(name, originalInput, type); + } +} +export class ConstrainedDictionaryModel extends ConstrainedMetaModel { + constructor( + name: string, + originalInput: any, + type: string, + public key: ConstrainedMetaModel, + public value: ConstrainedMetaModel, + public serializationType: 'unwrap' | 'normal' = 'normal' + ) { + super(name, originalInput, type); + } + + getNearestDependencies(): ConstrainedMetaModel[] { + const dependencies = [this.key, this.value]; + let dependencyModels: ConstrainedMetaModel[] = []; + for (const model of dependencies) { + if (model instanceof ConstrainedReferenceModel) { + dependencyModels.push(model); + } else { + //Lets check the non-reference model for dependencies + dependencyModels = [ + ...dependencyModels, + ...model.getNearestDependencies() + ]; + } + } + + //Ensure no duplicate references + dependencyModels = makeUnique(dependencyModels); + + return dependencyModels; + } +} + +export class ConstrainedObjectModel extends ConstrainedMetaModel { + constructor( + name: string, + originalInput: any, + type: string, + public properties: { [key: string]: ConstrainedObjectPropertyModel } + ) { + super(name, originalInput, type); + } + + getNearestDependencies(): ConstrainedMetaModel[] { + let dependencyModels: ConstrainedMetaModel[] = []; + for (const modelProperty of Object.values(this.properties)) { + if (modelProperty.property instanceof ConstrainedReferenceModel) { + dependencyModels.push(modelProperty.property); + } else { + //Lets check the non-reference model for dependencies + dependencyModels = [ + ...dependencyModels, + ...modelProperty.property.getNearestDependencies() + ]; + } + } + + //Ensure no self references + dependencyModels = dependencyModels.filter((referenceModel) => { + return referenceModel.name !== this.name; + }); + + //Ensure no duplicate references + dependencyModels = makeUnique(dependencyModels); + + return dependencyModels; + } + + /** + * More specifics on the type setup here: https://github.com/Microsoft/TypeScript/wiki/FAQ#why-cant-i-write-typeof-t-new-t-or-instanceof-t-in-my-generic-function + * + * @param propertyType + */ + containsPropertyType(propertyType: { new (...args: any[]): T }): boolean { + const foundPropertiesWithType = Object.values(this.properties).filter( + (property) => { + return property.property instanceof propertyType; + } + ); + return foundPropertiesWithType.length !== 0; + } +} diff --git a/src/models/Draft4Schema.ts b/src/models/Draft4Schema.ts index 58826e00be..40e3346b54 100644 --- a/src/models/Draft4Schema.ts +++ b/src/models/Draft4Schema.ts @@ -21,17 +21,17 @@ export class Draft4Schema { oneOf?: Draft4Schema[]; anyOf?: Draft4Schema[]; not?: Draft4Schema; - dependencies?: { [key: string]: Draft4Schema | string[]; }; + dependencies?: { [key: string]: Draft4Schema | string[] }; format?: string; - definitions?: { [key: string]: Draft4Schema; }; + definitions?: { [key: string]: Draft4Schema }; description?: string; default?: any; type?: string | string[]; enum?: any[]; items?: Draft4Schema | Draft4Schema[]; - properties?: { [key: string]: Draft4Schema; }; + properties?: { [key: string]: Draft4Schema }; additionalProperties?: Draft4Schema | boolean; - patternProperties?: { [key: string]: Draft4Schema; }; + patternProperties?: { [key: string]: Draft4Schema }; $ref?: string; required?: string[]; additionalItems?: Draft4Schema | boolean; @@ -41,8 +41,8 @@ export class Draft4Schema { /** * Takes a deep copy of the input object and converts it to an instance of Draft4Schema. - * - * @param object + * + * @param object */ static toSchema(object: Record): Draft4Schema { const convertedSchema = Draft4Schema.internalToSchema(object); @@ -51,7 +51,10 @@ export class Draft4Schema { } throw new Error('Could not convert input to expected copy of Draft4Schema'); } - private static internalToSchema(object: any, seenSchemas: Map = new Map()): any { + private static internalToSchema( + object: any, + seenSchemas: Map = new Map() + ): any { // if primitive types return as is if (null === object || 'object' !== typeof object) { return object; @@ -64,7 +67,10 @@ export class Draft4Schema { if (object instanceof Array) { const copy: any = []; for (let i = 0, len = object.length; i < len; i++) { - copy[Number(i)] = Draft4Schema.internalToSchema(object[Number(i)], seenSchemas); + copy[Number(i)] = Draft4Schema.internalToSchema( + object[Number(i)], + seenSchemas + ); } return copy; } @@ -75,8 +81,7 @@ export class Draft4Schema { let copyProp = prop; // Ignore value properties (those with `any` type) as they should be saved as is regardless of value - if (propName !== 'default' && - propName !== 'enum') { + if (propName !== 'default' && propName !== 'enum') { copyProp = Draft4Schema.internalToSchema(prop, seenSchemas); } (schema as any)[String(propName)] = copyProp; diff --git a/src/models/Draft6Schema.ts b/src/models/Draft6Schema.ts index da997b5ec4..f799151619 100644 --- a/src/models/Draft6Schema.ts +++ b/src/models/Draft6Schema.ts @@ -19,17 +19,17 @@ export class Draft6Schema { oneOf?: (Draft6Schema | boolean)[]; anyOf?: (Draft6Schema | boolean)[]; not?: Draft6Schema | boolean; - dependencies?: { [key: string]: Draft6Schema | boolean | string[]; }; + dependencies?: { [key: string]: Draft6Schema | boolean | string[] }; format?: string; - definitions?: { [key: string]: Draft6Schema | boolean; }; + definitions?: { [key: string]: Draft6Schema | boolean }; description?: string; default?: any; type?: string | string[]; enum?: any[]; items?: Draft6Schema | Draft6Schema[] | boolean; - properties?: { [key: string]: Draft6Schema | boolean; }; + properties?: { [key: string]: Draft6Schema | boolean }; additionalProperties?: Draft6Schema | boolean; - patternProperties?: { [key: string]: Draft6Schema | boolean; }; + patternProperties?: { [key: string]: Draft6Schema | boolean }; $ref?: string; required?: string[]; additionalItems?: Draft6Schema | boolean; @@ -47,8 +47,8 @@ export class Draft6Schema { /** * Takes a deep copy of the input object and converts it to an instance of Draft6Schema. - * - * @param object + * + * @param object */ static toSchema(object: Record): Draft6Schema { const convertedSchema = Draft6Schema.internalToSchema(object); @@ -57,7 +57,10 @@ export class Draft6Schema { } throw new Error('Could not convert input to expected copy of Draft6Schema'); } - private static internalToSchema(object: any, seenSchemas: Map = new Map()): any { + private static internalToSchema( + object: any, + seenSchemas: Map = new Map() + ): any { // if primitive types return as is if (null === object || 'object' !== typeof object) { return object; @@ -70,7 +73,10 @@ export class Draft6Schema { if (object instanceof Array) { const copy: any = []; for (let i = 0, len = object.length; i < len; i++) { - copy[Number(i)] = Draft6Schema.internalToSchema(object[Number(i)], seenSchemas); + copy[Number(i)] = Draft6Schema.internalToSchema( + object[Number(i)], + seenSchemas + ); } return copy; } @@ -81,10 +87,12 @@ export class Draft6Schema { let copyProp = prop; // Ignore value properties (those with `any` type) as they should be saved as is regardless of value - if (propName !== 'default' && + if ( + propName !== 'default' && propName !== 'examples' && propName !== 'const' && - propName !== 'enum') { + propName !== 'enum' + ) { copyProp = Draft6Schema.internalToSchema(prop, seenSchemas); } (schema as any)[String(propName)] = copyProp; diff --git a/src/models/Draft7Schema.ts b/src/models/Draft7Schema.ts index 5358dde67a..49fafc0027 100644 --- a/src/models/Draft7Schema.ts +++ b/src/models/Draft7Schema.ts @@ -19,17 +19,17 @@ export class Draft7Schema { oneOf?: (Draft7Schema | boolean)[]; anyOf?: (Draft7Schema | boolean)[]; not?: Draft7Schema | boolean; - dependencies?: { [key: string]: Draft7Schema | boolean | string[]; }; + dependencies?: { [key: string]: Draft7Schema | boolean | string[] }; format?: string; - definitions?: { [key: string]: Draft7Schema | boolean; }; + definitions?: { [key: string]: Draft7Schema | boolean }; description?: string; default?: any; type?: string | string[]; enum?: any[]; items?: Draft7Schema | Draft7Schema[] | boolean; - properties?: { [key: string]: Draft7Schema | boolean; }; + properties?: { [key: string]: Draft7Schema | boolean }; additionalProperties?: Draft7Schema | boolean; - patternProperties?: { [key: string]: Draft7Schema | boolean; }; + patternProperties?: { [key: string]: Draft7Schema | boolean }; $ref?: string; required?: string[]; additionalItems?: Draft7Schema | boolean; @@ -57,8 +57,8 @@ export class Draft7Schema { /** * Takes a deep copy of the input object and converts it to an instance of Draft7Schema. - * - * @param object + * + * @param object */ static toSchema(object: Record): Draft7Schema { const convertedSchema = Draft7Schema.internalToSchema(object); @@ -67,7 +67,10 @@ export class Draft7Schema { } throw new Error('Could not convert input to expected copy of Draft7Schema'); } - private static internalToSchema(object: any, seenSchemas: Map = new Map()): any { + private static internalToSchema( + object: any, + seenSchemas: Map = new Map() + ): any { // if primitive types return as is if (null === object || 'object' !== typeof object) { return object; @@ -80,7 +83,10 @@ export class Draft7Schema { if (object instanceof Array) { const copy: any = []; for (let i = 0, len = object.length; i < len; i++) { - copy[Number(i)] = Draft7Schema.internalToSchema(object[Number(i)], seenSchemas); + copy[Number(i)] = Draft7Schema.internalToSchema( + object[Number(i)], + seenSchemas + ); } return copy; } @@ -91,10 +97,12 @@ export class Draft7Schema { let copyProp = prop; // Ignore value properties (those with `any` type) as they should be saved as is regardless of value - if (propName !== 'default' && + if ( + propName !== 'default' && propName !== 'examples' && propName !== 'const' && - propName !== 'enum') { + propName !== 'enum' + ) { copyProp = Draft7Schema.internalToSchema(prop, seenSchemas); } (schema as any)[String(propName)] = copyProp; diff --git a/src/models/InputMetaModel.ts b/src/models/InputMetaModel.ts new file mode 100644 index 0000000000..d24131605c --- /dev/null +++ b/src/models/InputMetaModel.ts @@ -0,0 +1,9 @@ +import { MetaModel } from './MetaModel'; + +/** + * Since each input processor can create multiple meta models this is a wrapper to a MetaModel to make that possible. + */ +export class InputMetaModel { + models: { [key: string]: MetaModel } = {}; + originalInput: any = {}; +} diff --git a/src/models/MetaModel.ts b/src/models/MetaModel.ts new file mode 100644 index 0000000000..66988560b4 --- /dev/null +++ b/src/models/MetaModel.ts @@ -0,0 +1,75 @@ +export class MetaModel { + constructor(public name: string, public originalInput: any) {} +} + +export class ReferenceModel extends MetaModel { + constructor(name: string, originalInput: any, public ref: MetaModel) { + super(name, originalInput); + } +} +export class AnyModel extends MetaModel {} +export class FloatModel extends MetaModel {} +export class IntegerModel extends MetaModel {} +export class StringModel extends MetaModel {} +export class BooleanModel extends MetaModel {} +export class TupleValueModel { + constructor(public index: number, public value: MetaModel) {} +} +export class TupleModel extends MetaModel { + constructor( + name: string, + originalInput: any, + public tuple: TupleValueModel[] + ) { + super(name, originalInput); + } +} +export class ObjectPropertyModel { + constructor( + public propertyName: string, + public required: boolean, + public property: MetaModel + ) {} +} +export class ObjectModel extends MetaModel { + constructor( + name: string, + originalInput: any, + public properties: { [key: string]: ObjectPropertyModel } + ) { + super(name, originalInput); + } +} +export class ArrayModel extends MetaModel { + constructor(name: string, originalInput: any, public valueModel: MetaModel) { + super(name, originalInput); + } +} +export class UnionModel extends MetaModel { + constructor(name: string, originalInput: any, public union: MetaModel[]) { + super(name, originalInput); + } +} +export class EnumValueModel { + constructor(public key: string, public value: any) {} +} +export class EnumModel extends MetaModel { + constructor( + name: string, + originalInput: any, + public values: EnumValueModel[] + ) { + super(name, originalInput); + } +} +export class DictionaryModel extends MetaModel { + constructor( + name: string, + originalInput: any, + public key: MetaModel, + public value: MetaModel, + public serializationType: 'unwrap' | 'normal' = 'normal' + ) { + super(name, originalInput); + } +} diff --git a/src/models/OpenapiV3Schema.ts b/src/models/OpenapiV3Schema.ts index c4da3f27c7..85fb3d694f 100644 --- a/src/models/OpenapiV3Schema.ts +++ b/src/models/OpenapiV3Schema.ts @@ -28,7 +28,7 @@ export class OpenAPIV3ExternalDocumentation { } export class OpenapiV3Discriminator { propertyName?: string; - mapping?: {[k: string]: string}; + mapping?: { [k: string]: string }; static toDiscriminator(object: any): OpenapiV3Discriminator { const doc = new OpenapiV3Discriminator(); for (const [propName, prop] of Object.entries(object)) { @@ -40,15 +40,15 @@ export class OpenapiV3Discriminator { /** * OpenAPI 3.0 -> 3.0.4 schema model - * + * * Based on Draft 6, but with restricted keywords and definitions * Modifications * - type, cannot be an array nor contain 'null' - * + * * Restrictions (keywords not allowed) * - patternProperties * - not - * + * * https://swagger.io/specification/#schema-object */ export class OpenapiV3Schema { @@ -69,15 +69,15 @@ export class OpenapiV3Schema { allOf?: (OpenapiV3Schema | boolean)[]; oneOf?: (OpenapiV3Schema | boolean)[]; anyOf?: (OpenapiV3Schema | boolean)[]; - dependencies?: { [key: string]: OpenapiV3Schema | boolean | string[]; }; + dependencies?: { [key: string]: OpenapiV3Schema | boolean | string[] }; format?: string; - definitions?: { [key: string]: OpenapiV3Schema | boolean; }; + definitions?: { [key: string]: OpenapiV3Schema | boolean }; description?: string; default?: any; type?: string | string[]; enum?: any[]; items?: OpenapiV3Schema | OpenapiV3Schema[] | boolean; - properties?: { [key: string]: OpenapiV3Schema | boolean; }; + properties?: { [key: string]: OpenapiV3Schema | boolean }; additionalProperties?: OpenapiV3Schema | boolean; $ref?: string; required?: string[]; @@ -89,7 +89,7 @@ export class OpenapiV3Schema { //Draft 6 replacements $id?: string; //Replaces 'id' //Draft 6 additions - contains?: (OpenapiV3Schema | boolean); + contains?: OpenapiV3Schema | boolean; const?: any; propertyNames?: OpenapiV3Schema | boolean; examples?: any; @@ -108,17 +108,22 @@ export class OpenapiV3Schema { /** * Takes a deep copy of the input object and converts it to an instance of OpenapiV3Schema. - * - * @param object + * + * @param object */ static toSchema(object: Record): OpenapiV3Schema { const convertedSchema = OpenapiV3Schema.internalToSchema(object); if (convertedSchema instanceof OpenapiV3Schema) { return convertedSchema; } - throw new Error('Could not convert input to expected copy of OpenapiV3Schema'); + throw new Error( + 'Could not convert input to expected copy of OpenapiV3Schema' + ); } - private static internalToSchema(object: any, seenSchemas: Map = new Map()): any { + private static internalToSchema( + object: any, + seenSchemas: Map = new Map() + ): any { // if primitive types return as is if (null === object || 'object' !== typeof object) { return object; @@ -131,7 +136,10 @@ export class OpenapiV3Schema { if (object instanceof Array) { const copy: any = []; for (let i = 0, len = object.length; i < len; i++) { - copy[Number(i)] = OpenapiV3Schema.internalToSchema(object[Number(i)], seenSchemas); + copy[Number(i)] = OpenapiV3Schema.internalToSchema( + object[Number(i)], + seenSchemas + ); } return copy; } @@ -142,11 +150,11 @@ export class OpenapiV3Schema { let copyProp = prop; // Ignore value properties (those with `any` type) as they should be saved as is regardless of value - if (propName !== 'default' && - propName !== 'enum') { + if (propName !== 'default' && propName !== 'enum') { // Custom convert to External documentation instance if (propName === 'externalDocs') { - schema.externalDocs = OpenAPIV3ExternalDocumentation.toExternalDocumentation(prop); + schema.externalDocs = + OpenAPIV3ExternalDocumentation.toExternalDocumentation(prop); continue; } else if (propName === 'xml') { schema.xml = OpenapiV3Xml.toXml(prop); diff --git a/src/models/OutputModel.ts b/src/models/OutputModel.ts index 748e9197d5..5f2383ecf2 100644 --- a/src/models/OutputModel.ts +++ b/src/models/OutputModel.ts @@ -1,11 +1,11 @@ -import { CommonInputModel } from './CommonInputModel'; -import { CommonModel } from './CommonModel'; +import { InputMetaModel } from './InputMetaModel'; +import { ConstrainedMetaModel } from './ConstrainedMetaModel'; export interface ToOutputModelArg { result: string; - model: CommonModel; + model: ConstrainedMetaModel; modelName: string; - inputModel: CommonInputModel; + inputModel: InputMetaModel; dependencies: string[]; } @@ -15,18 +15,35 @@ export interface ToOutputModelArg { export class OutputModel { constructor( public readonly result: string, - public readonly model: CommonModel, + public readonly model: ConstrainedMetaModel, public readonly modelName: string, - public readonly inputModel: CommonInputModel, + public readonly inputModel: InputMetaModel, public readonly dependencies: string[] ) {} static toOutputModel(args: ToOutputModelArg): OutputModel; static toOutputModel(args: Array): Array; - static toOutputModel(args: ToOutputModelArg | Array): OutputModel | Array { + static toOutputModel( + args: ToOutputModelArg | Array + ): OutputModel | Array { if (Array.isArray(args)) { - return args.map(arg => new this(arg.result, arg.model, arg.modelName, arg.inputModel, arg.dependencies)); + return args.map( + (arg) => + new this( + arg.result, + arg.model, + arg.modelName, + arg.inputModel, + arg.dependencies + ) + ); } - return new this(args.result, args.model, args.modelName, args.inputModel, args.dependencies); + return new this( + args.result, + args.model, + args.modelName, + args.inputModel, + args.dependencies + ); } } diff --git a/src/models/Preset.ts b/src/models/Preset.ts index f50969668e..d8d69ef6d9 100644 --- a/src/models/Preset.ts +++ b/src/models/Preset.ts @@ -1,19 +1,33 @@ /* eslint-disable @typescript-eslint/ban-types */ import { AbstractRenderer } from '../generators/AbstractRenderer'; -import { CommonInputModel } from './CommonInputModel'; -import { CommonModel } from './CommonModel'; +import { InputMetaModel } from './InputMetaModel'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel +} from './ConstrainedMetaModel'; -export interface PresetArgs { - model: CommonModel; - inputModel: CommonInputModel; +export interface PresetArgs< + R extends AbstractRenderer, + O, + M extends ConstrainedMetaModel +> { + model: M; + inputModel: InputMetaModel; renderer: R; options: O; content: string; } -export interface CommonPreset { - self?: (args: PresetArgs) => Promise | string; - additionalContent?: (args: PresetArgs) => Promise | string; +export interface CommonPreset< + R extends AbstractRenderer, + O, + M extends ConstrainedMetaModel +> { + self?: (args: PresetArgs) => Promise | string; + additionalContent?: (args: PresetArgs) => Promise | string; } export enum PropertyType { @@ -22,33 +36,48 @@ export enum PropertyType { patternProperties } export interface PropertyArgs { - propertyName: string; - property: CommonModel; - type: PropertyType; + property: ConstrainedObjectPropertyModel; } -export interface ClassPreset extends CommonPreset { - ctor?: (args: PresetArgs) => Promise | string; - property?: (args: PresetArgs & PropertyArgs) => Promise | string; - getter?: (args: PresetArgs & PropertyArgs) => Promise | string; - setter?: (args: PresetArgs & PropertyArgs) => Promise | string; +export interface ClassPreset + extends CommonPreset { + ctor?: ( + args: PresetArgs + ) => Promise | string; + property?: ( + args: PresetArgs & PropertyArgs + ) => Promise | string; + getter?: ( + args: PresetArgs & PropertyArgs + ) => Promise | string; + setter?: ( + args: PresetArgs & PropertyArgs + ) => Promise | string; } -export interface InterfacePreset extends CommonPreset { - property?: (args: PresetArgs & PropertyArgs) => Promise | string; +export interface InterfacePreset + extends CommonPreset { + property?: ( + args: PresetArgs & PropertyArgs + ) => Promise | string; } export interface EnumArgs { - item: any; + item: ConstrainedEnumValueModel; } -export interface EnumPreset extends CommonPreset { - item?: (args: PresetArgs & EnumArgs) => string; +export interface EnumPreset + extends CommonPreset { + item?: (args: PresetArgs & EnumArgs) => string; } -export type Preset> = any> = Partial; +export type Preset< + C extends Record> = any +> = Partial; export type PresetWithOptions

= { - preset: P, - options: O, -} -export type Presets

= Array

>; + preset: P; + options: O; +}; +export type Presets

= Array< + P | PresetWithOptions

+>; diff --git a/src/models/ProcessorOptions.ts b/src/models/ProcessorOptions.ts index 28efd1cf71..7f5d237048 100644 --- a/src/models/ProcessorOptions.ts +++ b/src/models/ProcessorOptions.ts @@ -1,7 +1,9 @@ -import { ParserOptions } from '@asyncapi/parser'; +import { ParseOptions } from '@asyncapi/parser'; +import { InterpreterOptions } from '../interpreter/Interpreter'; import { TypeScriptInputProcessorOptions } from '../processors/index'; export interface ProcessorOptions { - asyncapi?: ParserOptions, - typescript?: TypeScriptInputProcessorOptions, + asyncapi?: ParseOptions; + typescript?: TypeScriptInputProcessorOptions; + interpreter?: InterpreterOptions; } diff --git a/src/models/SwaggerV2Schema.ts b/src/models/SwaggerV2Schema.ts index 7bf8ce3ecc..d0ea91aeda 100644 --- a/src/models/SwaggerV2Schema.ts +++ b/src/models/SwaggerV2Schema.ts @@ -25,15 +25,15 @@ export class SwaggerV2ExternalDocumentation { /** * OpenAPI 2.0 (Swagger 2.0) schema model - * + * * Based on Draft 4, but with restricted keywords and definitions - * + * * Restrictions (keywords not allowed) * - oneOf * - anyOf * - patternProperties * - not - * + * * https://swagger.io/specification/v2/#schemaObject */ export class SwaggerV2Schema { @@ -54,13 +54,13 @@ export class SwaggerV2Schema { minProperties?: number; allOf?: SwaggerV2Schema[]; format?: string; - definitions?: { [key: string]: SwaggerV2Schema; }; + definitions?: { [key: string]: SwaggerV2Schema }; description?: string; default?: any; type?: string | string[]; enum?: any[]; items?: SwaggerV2Schema | SwaggerV2Schema[]; - properties?: { [key: string]: SwaggerV2Schema; }; + properties?: { [key: string]: SwaggerV2Schema }; additionalProperties?: SwaggerV2Schema | boolean; $ref?: string; required?: string[]; @@ -79,17 +79,22 @@ export class SwaggerV2Schema { /** * Takes a deep copy of the input object and converts it to an instance of SwaggerV2Schema. - * - * @param object + * + * @param object */ static toSchema(object: Record): SwaggerV2Schema { const convertedSchema = SwaggerV2Schema.internalToSchema(object); if (convertedSchema instanceof SwaggerV2Schema) { return convertedSchema; } - throw new Error('Could not convert input to expected copy of SwaggerV2Schema'); + throw new Error( + 'Could not convert input to expected copy of SwaggerV2Schema' + ); } - private static internalToSchema(object: any, seenSchemas: Map = new Map()): any { + private static internalToSchema( + object: any, + seenSchemas: Map = new Map() + ): any { // if primitive types return as is if (null === object || 'object' !== typeof object) { return object; @@ -102,7 +107,10 @@ export class SwaggerV2Schema { if (object instanceof Array) { const copy: any = []; for (let i = 0, len = object.length; i < len; i++) { - copy[Number(i)] = SwaggerV2Schema.internalToSchema(object[Number(i)], seenSchemas); + copy[Number(i)] = SwaggerV2Schema.internalToSchema( + object[Number(i)], + seenSchemas + ); } return copy; } @@ -113,11 +121,11 @@ export class SwaggerV2Schema { let copyProp = prop; // Ignore value properties (those with `any` type) as they should be saved as is regardless of value - if (propName !== 'default' && - propName !== 'enum') { + if (propName !== 'default' && propName !== 'enum') { // Custom convert to External documentation instance if (propName === 'externalDocs') { - schema.externalDocs = SwaggerV2ExternalDocumentation.toExternalDocumentation(prop); + schema.externalDocs = + SwaggerV2ExternalDocumentation.toExternalDocumentation(prop); continue; } else if (propName === 'xml') { schema.xml = SwaggerV2Xml.toXml(prop); diff --git a/src/models/index.ts b/src/models/index.ts index 46cafa0afa..555d5d5352 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1,4 +1,3 @@ -export * from './CommonInputModel'; export * from './CommonModel'; export * from './RenderOutput'; export * from './OutputModel'; @@ -10,3 +9,6 @@ export * from './Draft6Schema'; export * from './Draft4Schema'; export * from './SwaggerV2Schema'; export * from './OpenapiV3Schema'; +export * from './MetaModel'; +export * from './ConstrainedMetaModel'; +export * from './InputMetaModel'; diff --git a/src/processors/AbstractInputProcessor.ts b/src/processors/AbstractInputProcessor.ts index 27031a2fa6..9617676a3d 100644 --- a/src/processors/AbstractInputProcessor.ts +++ b/src/processors/AbstractInputProcessor.ts @@ -1,6 +1,10 @@ -import { ProcessorOptions, CommonInputModel } from '../models'; +import { ProcessorOptions, InputMetaModel } from '../models'; + export abstract class AbstractInputProcessor { public static MODELGEN_INFFERED_NAME = 'x-modelgen-inferred-name'; - abstract process(input: Record, options?: ProcessorOptions): Promise; - abstract shouldProcess(input: Record): boolean; + abstract process( + input: any, + options?: ProcessorOptions + ): Promise; + abstract shouldProcess(input: any): boolean; } diff --git a/src/processors/AsyncAPIInputProcessor.ts b/src/processors/AsyncAPIInputProcessor.ts index b64342a9cd..147029f437 100644 --- a/src/processors/AsyncAPIInputProcessor.ts +++ b/src/processors/AsyncAPIInputProcessor.ts @@ -1,164 +1,328 @@ -import {parse, AsyncAPIDocument, Schema as AsyncAPISchema, ParserOptions} from '@asyncapi/parser'; +import { + createAsyncAPIDocument, + isAsyncAPIDocument, + isOldAsyncAPIDocument, + Parser +} from '@asyncapi/parser'; +import { createDetailedAsyncAPI } from '@asyncapi/parser/cjs/utils'; import { AbstractInputProcessor } from './AbstractInputProcessor'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; -import { CommonInputModel, ProcessorOptions } from '../models'; +import { InputMetaModel, ProcessorOptions } from '../models'; import { Logger } from '../utils'; import { AsyncapiV2Schema } from '../models/AsyncapiV2Schema'; +import { convertToMetaModel } from '../helpers'; +import type { + AsyncAPIDocumentInterface, + SchemaInterface as AsyncAPISchema +} from '@asyncapi/parser'; /** * Class for processing AsyncAPI inputs */ export class AsyncAPIInputProcessor extends AbstractInputProcessor { - static supportedVersions = ['2.0.0', '2.1.0', '2.2.0', '2.3.0','2.4.0']; + private parser = new Parser(); + + static supportedVersions = [ + '2.0.0', + '2.1.0', + '2.2.0', + '2.3.0', + '2.4.0', + '2.5.0' + ]; /** * Process the input as an AsyncAPI document - * - * @param input + * + * @param input */ - async process(input: Record, options?: ProcessorOptions): Promise { - if (!this.shouldProcess(input)) {throw new Error('Input is not an AsyncAPI document so it cannot be processed.');} + // eslint-disable-next-line sonarjs/cognitive-complexity + async process( + input?: any, + options?: ProcessorOptions + ): Promise { + if (!this.shouldProcess(input)) { + throw new Error( + 'Input is not an AsyncAPI document so it cannot be processed.' + ); + } Logger.debug('Processing input as an AsyncAPI document'); - let doc: AsyncAPIDocument; - const common = new CommonInputModel(); + let doc: AsyncAPIDocumentInterface; + const inputModel = new InputMetaModel(); if (!AsyncAPIInputProcessor.isFromParser(input)) { - doc = await parse(input as any, options?.asyncapi || {} as ParserOptions); + const { document, diagnostics } = await this.parser.parse( + input as any, + options?.asyncapi || {} + ); + if (document) { + doc = document; + } else { + const err = new Error( + 'Input is not an correct AsyncAPI document so it cannot be processed.' + ); + (err as any).diagnostics = diagnostics; + throw err; + } + } else if (AsyncAPIInputProcessor.isFromNewParser(input)) { + doc = input as AsyncAPIDocumentInterface; } else { - doc = input as AsyncAPIDocument; + // Is from old parser + const parsedJSON = input.json(); + const detailed = createDetailedAsyncAPI(parsedJSON, parsedJSON); + doc = createAsyncAPIDocument(detailed); } - common.originalInput = doc; - - for (const [, message] of doc.allMessages()) { - const schema = AsyncAPIInputProcessor.convertToInternalSchema(message.payload()); - const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(schema); - common.models = {...common.models, ...commonModels}; + + inputModel.originalInput = doc; + // Go over all the message payloads and convert them to models + for (const message of doc.allMessages()) { + const payload = message.payload(); + if (payload) { + const schema = AsyncAPIInputProcessor.convertToInternalSchema(payload); + const newCommonModel = + JsonSchemaInputProcessor.convertSchemaToCommonModel(schema, options); + if (newCommonModel.$id !== undefined) { + if (inputModel.models[newCommonModel.$id] !== undefined) { + Logger.warn( + `Overwriting existing model with $id ${newCommonModel.$id}, are there two models with the same id present?`, + newCommonModel + ); + } + const metaModel = convertToMetaModel(newCommonModel); + inputModel.models[metaModel.name] = metaModel; + } else { + Logger.warn( + 'Model did not have $id which is required, ignoring.', + newCommonModel + ); + } + } } - return common; + return inputModel; } /** - * - * Reflect the name of the schema and save it to `x-modelgen-inferred-name` extension. - * + * + * Reflect the name of the schema and save it to `x-modelgen-inferred-name` extension. + * * This keeps the the id of the model deterministic if used in conjunction with other AsyncAPI tools such as the generator. - * + * * @param schema to reflect name for */ + /* eslint-disable @typescript-eslint/no-non-null-assertion */ // eslint-disable-next-line sonarjs/cognitive-complexity static convertToInternalSchema( schema: AsyncAPISchema | boolean, alreadyIteratedSchemas: Map = new Map() ): AsyncapiV2Schema | boolean { - if (typeof schema === 'boolean') {return schema;} - const schemaUid = schema.uid(); + if (typeof schema === 'boolean') { + return schema; + } + + let schemaUid = schema.id(); + //Because the constraint functionality of generators cannot handle -, <, >, we remove them from the id if it's an anonymous schema. + if ( + typeof schemaUid !== 'undefined' && + schemaUid.includes('', ''); + } + if (alreadyIteratedSchemas.has(schemaUid)) { - return alreadyIteratedSchemas.get(schemaUid) as AsyncapiV2Schema; + return alreadyIteratedSchemas.get(schemaUid) as AsyncapiV2Schema; } - const convertedSchema = Object.assign(new AsyncapiV2Schema(), schema.json()); + const convertedSchema = Object.assign( + new AsyncapiV2Schema(), + schema.json() + ); convertedSchema[this.MODELGEN_INFFERED_NAME] = schemaUid; alreadyIteratedSchemas.set(schemaUid, convertedSchema); - if (schema.allOf() !== null) { - convertedSchema.allOf = schema.allOf().map((item) => this.convertToInternalSchema(item, alreadyIteratedSchemas)); + if (schema.allOf()) { + convertedSchema.allOf = schema + .allOf()! + .map((item: any) => + this.convertToInternalSchema(item, alreadyIteratedSchemas) + ); } - if (schema.oneOf() !== null) { - convertedSchema.oneOf = schema.oneOf().map((item) => this.convertToInternalSchema(item, alreadyIteratedSchemas)); + if (schema.oneOf()) { + convertedSchema.oneOf = schema + .oneOf()! + .map((item: any) => + this.convertToInternalSchema(item, alreadyIteratedSchemas) + ); } - if (schema.anyOf() !== null) { - convertedSchema.anyOf = schema.anyOf().map((item) => this.convertToInternalSchema(item, alreadyIteratedSchemas)); + if (schema.anyOf()) { + convertedSchema.anyOf = schema + .anyOf()! + .map((item: any) => + this.convertToInternalSchema(item, alreadyIteratedSchemas) + ); } - if (schema.not() !== null) { - convertedSchema.not = this.convertToInternalSchema(schema.not(), alreadyIteratedSchemas); + if (schema.not()) { + convertedSchema.not = this.convertToInternalSchema( + schema.not()!, + alreadyIteratedSchemas + ); } if ( typeof schema.additionalItems() === 'object' && schema.additionalItems() !== null ) { - convertedSchema.additionalItems = this.convertToInternalSchema(schema.additionalItems(), alreadyIteratedSchemas); + convertedSchema.additionalItems = this.convertToInternalSchema( + schema.additionalItems(), + alreadyIteratedSchemas + ); } - if (schema.contains() !== null) { - convertedSchema.contains = this.convertToInternalSchema(schema.contains(), alreadyIteratedSchemas); + if (schema.contains()) { + convertedSchema.contains = this.convertToInternalSchema( + schema.contains()!, + alreadyIteratedSchemas + ); } - if (schema.propertyNames() !== null) { - convertedSchema.propertyNames = this.convertToInternalSchema(schema.propertyNames(), alreadyIteratedSchemas); + if (schema.propertyNames()) { + convertedSchema.propertyNames = this.convertToInternalSchema( + schema.propertyNames()!, + alreadyIteratedSchemas + ); } - if (schema.if() !== null) { - convertedSchema.if = this.convertToInternalSchema(schema.if(), alreadyIteratedSchemas); + if (schema.if()) { + convertedSchema.if = this.convertToInternalSchema( + schema.if()!, + alreadyIteratedSchemas + ); } - if (schema.then() !== null) { - convertedSchema.then = this.convertToInternalSchema(schema.then(), alreadyIteratedSchemas); + if (schema.then()) { + convertedSchema.then = this.convertToInternalSchema( + schema.then()!, + alreadyIteratedSchemas + ); } - if (schema.else() !== null) { - convertedSchema.else = this.convertToInternalSchema(schema.else(), alreadyIteratedSchemas); + if (schema.else()) { + convertedSchema.else = this.convertToInternalSchema( + schema.else()!, + alreadyIteratedSchemas + ); } if ( - typeof schema.additionalProperties() === 'object' && + typeof schema.additionalProperties() === 'object' && schema.additionalProperties() !== null ) { - convertedSchema.additionalProperties = this.convertToInternalSchema(schema.additionalProperties(), alreadyIteratedSchemas); + convertedSchema.additionalProperties = this.convertToInternalSchema( + schema.additionalProperties(), + alreadyIteratedSchemas + ); } - if (schema.items() !== null) { + if (schema.items()) { if (Array.isArray(schema.items())) { - convertedSchema.items = (schema.items() as AsyncAPISchema[]).map((item) => this.convertToInternalSchema(item), alreadyIteratedSchemas); + convertedSchema.items = (schema.items() as AsyncAPISchema[]).map( + (item) => this.convertToInternalSchema(item), + alreadyIteratedSchemas + ); } else { - convertedSchema.items = this.convertToInternalSchema(schema.items() as AsyncAPISchema, alreadyIteratedSchemas); + convertedSchema.items = this.convertToInternalSchema( + schema.items() as AsyncAPISchema, + alreadyIteratedSchemas + ); } } - if (schema.properties() !== null && Object.keys(schema.properties()).length) { - const properties : {[key: string]: AsyncapiV2Schema | boolean} = {}; - for (const [propertyName, propertySchema] of Object.entries(schema.properties())) { - properties[String(propertyName)] = this.convertToInternalSchema(propertySchema, alreadyIteratedSchemas); + const schemaProperties = schema.properties(); + if (schemaProperties && Object.keys(schemaProperties).length) { + const properties: { [key: string]: AsyncapiV2Schema | boolean } = {}; + for (const [propertyName, propertySchema] of Object.entries( + schemaProperties + )) { + properties[String(propertyName)] = this.convertToInternalSchema( + propertySchema, + alreadyIteratedSchemas + ); } convertedSchema.properties = properties; } - if (schema.dependencies() !== null && Object.keys(schema.dependencies()).length) { - const dependencies: { [key: string]: AsyncapiV2Schema | boolean | string[] } = {}; - for (const [dependencyName, dependency] of Object.entries(schema.dependencies())) { + + const schemaDependencies = schema.dependencies(); + if (schemaDependencies && Object.keys(schemaDependencies).length) { + const dependencies: { + [key: string]: AsyncapiV2Schema | boolean | string[]; + } = {}; + for (const [dependencyName, dependency] of Object.entries( + schemaDependencies + )) { if (typeof dependency === 'object' && !Array.isArray(dependency)) { - dependencies[String(dependencyName)] = this.convertToInternalSchema(dependency, alreadyIteratedSchemas); + dependencies[String(dependencyName)] = this.convertToInternalSchema( + dependency, + alreadyIteratedSchemas + ); } else { dependencies[String(dependencyName)] = dependency as string[]; } } convertedSchema.dependencies = dependencies; } - if (schema.patternProperties() !== null && Object.keys(schema.patternProperties()).length) { - const patternProperties: { [key: string]: AsyncapiV2Schema | boolean } = {}; - for (const [patternPropertyName, patternProperty] of Object.entries(schema.patternProperties())) { - patternProperties[String(patternPropertyName)] = this.convertToInternalSchema(patternProperty, alreadyIteratedSchemas); + + const schemaPatternProperties = schema.patternProperties(); + if ( + schemaPatternProperties && + Object.keys(schemaPatternProperties).length + ) { + const patternProperties: { [key: string]: AsyncapiV2Schema | boolean } = + {}; + for (const [patternPropertyName, patternProperty] of Object.entries( + schemaPatternProperties + )) { + patternProperties[String(patternPropertyName)] = + this.convertToInternalSchema(patternProperty, alreadyIteratedSchemas); } convertedSchema.patternProperties = patternProperties; } - if (schema.definitions() !== null && Object.keys(schema.definitions()).length) { + + const schemaDefinitions = schema.definitions(); + if (schemaDefinitions && Object.keys(schemaDefinitions).length) { const definitions: { [key: string]: AsyncapiV2Schema | boolean } = {}; - for (const [definitionName, definition] of Object.entries(schema.definitions())) { - definitions[String(definitionName)] = this.convertToInternalSchema(definition, alreadyIteratedSchemas); + for (const [definitionName, definition] of Object.entries( + schemaDefinitions + )) { + definitions[String(definitionName)] = this.convertToInternalSchema( + definition, + alreadyIteratedSchemas + ); } convertedSchema.definitions = definitions; } return convertedSchema; } + /** - * Figures out if an object is of type AsyncAPI document - * - * @param input - */ - shouldProcess(input: Record) : boolean { + * Figures out if an object is of type AsyncAPI document + * + * @param input + */ + shouldProcess(input?: any): boolean { + if (!input) { + return false; + } const version = this.tryGetVersionOfDocument(input); - if (!version) {return false;} + if (!version) { + return false; + } return AsyncAPIInputProcessor.supportedVersions.includes(version); } /** * Try to find the AsyncAPI version from the input. If it cannot undefined are returned, if it can, the version is returned. - * - * @param input + * + * @param input */ - tryGetVersionOfDocument(input: Record) : string | undefined { + tryGetVersionOfDocument(input?: any): string | undefined { + if (!input) { + return; + } if (AsyncAPIInputProcessor.isFromParser(input)) { return input.version(); } @@ -166,15 +330,20 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { } /** - * Figure out if input is from the AsyncAPI js parser. - * - * @param input + * Figure out if input is from the AsyncAPI parser. + * + * @param input */ - static isFromParser(input: Record): boolean { - if (input['_json'] !== undefined && input['_json'].asyncapi !== undefined && - typeof input.version === 'function') { - return true; - } - return false; + static isFromParser(input?: any): boolean { + return isOldAsyncAPIDocument(input) || this.isFromNewParser(input); + } + + /** + * Figure out if input is from the new AsyncAPI parser. + * + * @param input + */ + static isFromNewParser(input?: any): boolean { + return isAsyncAPIDocument(input); } } diff --git a/src/processors/InputProcessor.ts b/src/processors/InputProcessor.ts index a4dd57cfec..6dc3529007 100644 --- a/src/processors/InputProcessor.ts +++ b/src/processors/InputProcessor.ts @@ -1,7 +1,7 @@ import { AbstractInputProcessor } from './AbstractInputProcessor'; import { AsyncAPIInputProcessor } from './AsyncAPIInputProcessor'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; -import { ProcessorOptions, CommonInputModel } from '../models'; +import { ProcessorOptions, InputMetaModel } from '../models'; import { SwaggerInputProcessor } from './SwaggerInputProcessor'; import { OpenAPIInputProcessor } from './OpenAPIInputProcessor'; import { TypeScriptInputProcessor } from './TypeScriptInputProcessor'; @@ -14,16 +14,16 @@ export class InputProcessor { private processors: Map = new Map(); constructor() { - this.setProcessor('asyncapi', new AsyncAPIInputProcessor()); - this.setProcessor('swagger', new SwaggerInputProcessor()); - this.setProcessor('openapi', new OpenAPIInputProcessor()); + this.setProcessor('asyncapi', new AsyncAPIInputProcessor()); + this.setProcessor('swagger', new SwaggerInputProcessor()); + this.setProcessor('openapi', new OpenAPIInputProcessor()); this.setProcessor('default', new JsonSchemaInputProcessor()); this.setProcessor('typescript', new TypeScriptInputProcessor()); } - + /** * Set a processor. - * + * * @param type of processor * @param processor */ @@ -32,22 +32,24 @@ export class InputProcessor { } /** - * + * * @returns all processors */ - getProcessors() : Map { + getProcessors(): Map { return this.processors; } /** * The processor code which delegates the processing to the correct implementation. - * + * * @param input to process * @param options passed to the processors */ - process(input: Record, options?: ProcessorOptions): Promise { + process(input: any, options?: ProcessorOptions): Promise { for (const [type, processor] of this.processors) { - if (type === 'default') {continue;} + if (type === 'default') { + continue; + } if (processor.shouldProcess(input)) { return processor.process(input, options); } @@ -59,4 +61,3 @@ export class InputProcessor { return Promise.reject(new Error('No default processor found')); } } - diff --git a/src/processors/JsonSchemaInputProcessor.ts b/src/processors/JsonSchemaInputProcessor.ts index 66ee53229c..d63bf5f024 100644 --- a/src/processors/JsonSchemaInputProcessor.ts +++ b/src/processors/JsonSchemaInputProcessor.ts @@ -1,10 +1,20 @@ import { AbstractInputProcessor } from './AbstractInputProcessor'; import $RefParser from '@apidevtools/json-schema-ref-parser'; import path from 'path'; -import { CommonModel, CommonInputModel, Draft4Schema, Draft7Schema, Draft6Schema, SwaggerV2Schema, OpenapiV3Schema, AsyncapiV2Schema } from '../models'; +import { + CommonModel, + InputMetaModel, + Draft4Schema, + Draft7Schema, + Draft6Schema, + SwaggerV2Schema, + OpenapiV3Schema, + AsyncapiV2Schema, + ProcessorOptions +} from '../models'; import { Logger } from '../utils'; -import { postInterpretModel } from '../interpreter/PostInterpreter'; import { Interpreter } from '../interpreter/Interpreter'; +import { convertToMetaModel } from '../helpers'; /** * Class for processing JSON Schema @@ -12,40 +22,44 @@ import { Interpreter } from '../interpreter/Interpreter'; export class JsonSchemaInputProcessor extends AbstractInputProcessor { /** * Function for processing a JSON Schema input. - * - * @param input + * + * @param input */ - process(input: Record): Promise { + process(input: any, options?: ProcessorOptions): Promise { if (this.shouldProcess(input)) { switch (input.$schema) { - case 'http://json-schema.org/draft-04/schema': - case 'http://json-schema.org/draft-04/schema#': - return this.processDraft4(input); - case 'http://json-schema.org/draft-06/schema': - case 'http://json-schema.org/draft-06/schema#': - return this.processDraft6(input); - case 'http://json-schema.org/draft-07/schema#': - case 'http://json-schema.org/draft-07/schema': - default: - return this.processDraft7(input); + case 'http://json-schema.org/draft-04/schema': + case 'http://json-schema.org/draft-04/schema#': + return this.processDraft4(input, options); + case 'http://json-schema.org/draft-06/schema': + case 'http://json-schema.org/draft-06/schema#': + return this.processDraft6(input, options); + case 'http://json-schema.org/draft-07/schema#': + case 'http://json-schema.org/draft-07/schema': + default: + return this.processDraft7(input, options); } } - return Promise.reject(new Error('Input is not a JSON Schema, so it cannot be processed.')); + return Promise.reject( + new Error('Input is not a JSON Schema, so it cannot be processed.') + ); } /** * Unless the schema states one that is not supported we assume its of type JSON Schema - * - * @param input + * + * @param input */ - shouldProcess(input: Record): boolean { + shouldProcess(input: any): boolean { if (input.$schema !== undefined) { - if (input.$schema === 'http://json-schema.org/draft-04/schema#' || - input.$schema === 'http://json-schema.org/draft-04/schema' || - input.$schema === 'http://json-schema.org/draft-06/schema#' || - input.$schema === 'http://json-schema.org/draft-06/schema' || - input.$schema === 'http://json-schema.org/draft-07/schema#' || - input.$schema === 'http://json-schema.org/draft-07/schema') { + if ( + input.$schema === 'http://json-schema.org/draft-04/schema#' || + input.$schema === 'http://json-schema.org/draft-04/schema' || + input.$schema === 'http://json-schema.org/draft-06/schema#' || + input.$schema === 'http://json-schema.org/draft-06/schema' || + input.$schema === 'http://json-schema.org/draft-07/schema#' || + input.$schema === 'http://json-schema.org/draft-07/schema' + ) { return true; } return false; @@ -55,65 +69,146 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { /** * Process a draft-7 schema - * + * * @param input to process as draft 7 */ - private async processDraft7(input: Record) : Promise { + private async processDraft7( + input: any, + options?: ProcessorOptions + ): Promise { Logger.debug('Processing input as a JSON Schema Draft 7 document'); - const commonInputModel = new CommonInputModel(); - commonInputModel.originalInput = input; - input = JsonSchemaInputProcessor.reflectSchemaNames(input, {}, 'root', true) as Record; - await this.dereferenceInputs(input); + const inputModel = new InputMetaModel(); + inputModel.originalInput = input; + input = JsonSchemaInputProcessor.reflectSchemaNames( + input, + {}, + 'root', + true + ) as any; + input = await this.dereferenceInputs(input); const parsedSchema = Draft7Schema.toSchema(input); - commonInputModel.models = JsonSchemaInputProcessor.convertSchemaToCommonModel(parsedSchema); + const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( + parsedSchema, + options + ); + const metaModel = convertToMetaModel(newCommonModel); + inputModel.models[metaModel.name] = metaModel; Logger.debug('Completed processing input as JSON Schema draft 7 document'); - return commonInputModel; + return inputModel; } /** * Process a draft-4 schema - * + * * @param input to process as draft 4 */ - private async processDraft4(input: Record) : Promise { + private async processDraft4( + input: any, + options?: ProcessorOptions + ): Promise { Logger.debug('Processing input as JSON Schema Draft 4 document'); - const commonInputModel = new CommonInputModel(); - commonInputModel.originalInput = input; - input = JsonSchemaInputProcessor.reflectSchemaNames(input, {}, 'root', true) as Record; - await this.dereferenceInputs(input); + const inputModel = new InputMetaModel(); + inputModel.originalInput = input; + input = JsonSchemaInputProcessor.reflectSchemaNames( + input, + {}, + 'root', + true + ) as any; + input = await this.dereferenceInputs(input); const parsedSchema = Draft4Schema.toSchema(input); - commonInputModel.models = JsonSchemaInputProcessor.convertSchemaToCommonModel(parsedSchema); + const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( + parsedSchema, + options + ); + const metaModel = convertToMetaModel(newCommonModel); + inputModel.models[metaModel.name] = metaModel; Logger.debug('Completed processing input as JSON Schema draft 4 document'); - return commonInputModel; + return inputModel; } - + /** * Process a draft-6 schema - * + * * @param input to process as draft-6 */ - private async processDraft6(input: Record) : Promise { + private async processDraft6( + input: any, + options?: ProcessorOptions + ): Promise { Logger.debug('Processing input as a JSON Schema Draft 6 document'); - const commonInputModel = new CommonInputModel(); - commonInputModel.originalInput = input; - input = JsonSchemaInputProcessor.reflectSchemaNames(input, {}, 'root', true) as Record; - await this.dereferenceInputs(input); + const inputModel = new InputMetaModel(); + inputModel.originalInput = input; + input = JsonSchemaInputProcessor.reflectSchemaNames( + input, + {}, + 'root', + true + ) as any; + input = await this.dereferenceInputs(input); const parsedSchema = Draft6Schema.toSchema(input); - commonInputModel.models = JsonSchemaInputProcessor.convertSchemaToCommonModel(parsedSchema); + const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( + parsedSchema, + options + ); + const metaModel = convertToMetaModel(newCommonModel); + inputModel.models[metaModel.name] = metaModel; Logger.debug('Completed processing input as JSON Schema draft 6 document'); - return commonInputModel; + return inputModel; } - private async dereferenceInputs(input: Record) { + /** + * This is a hotfix and really only a partial solution as it does not cover all cases. + * + * But it's the best we can do until we find or build a better library to handle references. + */ + public handleRootReference(input: any): any { + //Because of https://github.com/APIDevTools/json-schema-ref-parser/issues/201 the tool cannot handle root references. + //This really is a bad patch to fix an underlying problem, but until a full library is available, this is best we can do. + const hasRootRef = input.$ref !== undefined; + if (hasRootRef) { + Logger.warn( + 'Found a root $ref, which is not fully supported in Modelina, trying to do what I can with it...' + ); + //When we encounter it, manually try to resolve the reference in the definitions section + const hasDefinitionSection = input.definitions !== undefined; + if (hasDefinitionSection) { + const definitionLink = '#/definitions/'; + const referenceLink = input.$ref.slice(0, definitionLink.length); + const referenceIsLocal = referenceLink === definitionLink; + if (referenceIsLocal) { + const definitionName = input.$ref.slice(definitionLink.length); + const definition = input.definitions[String(definitionName)]; + const definitionExist = definition !== undefined; + if (definitionExist) { + delete input.$ref; + return { ...definition, ...input }; + } + } + } + //All other unhandled cases, means we cannot handle this input + throw new Error( + 'Cannot handle input, because it has a root `$ref`, please manually resolve the first reference.' + ); + } + return input; + } + + public async dereferenceInputs(input: any): Promise { + input = this.handleRootReference(input); Logger.debug('Dereferencing all $ref instances'); - const refParser = new $RefParser; + const refParser = new $RefParser(); // eslint-disable-next-line no-undef const localPath = `${process.cwd()}${path.sep}`; const deRefOption: $RefParser.Options = { continueOnError: true, - dereference: { circular: true }, + dereference: { circular: true } }; - Logger.debug(`Trying to dereference all $ref instances from input, using option ${JSON.stringify(deRefOption)}.`); + Logger.debug( + `Trying to dereference all $ref instances from input, using option ${JSON.stringify( + deRefOption + )}.` + ); try { await refParser.dereference(localPath, input, deRefOption); } catch (e: any) { @@ -121,16 +216,20 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { Logger.error(errorMessage, e); throw new Error(errorMessage); } - Logger.debug('Successfully dereferenced all $ref instances from input.', input); + Logger.debug( + 'Successfully dereferenced all $ref instances from input.', + input + ); + return input; } /** * Each schema must have a name, so when later interpreted, the model have the most accurate model name. - * + * * Reflect name from given schema and save it to `x-modelgen-inferred-name` extension. - * + * * This reflects all the common keywords that are shared between draft-4, draft-7 and Swagger 2.0 Schema - * + * * @param schema to process * @param namesStack is a aggegator of previous used names * @param name to infer @@ -138,19 +237,31 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { */ // eslint-disable-next-line sonarjs/cognitive-complexity static reflectSchemaNames( - schema: Draft4Schema | Draft6Schema | Draft7Schema | SwaggerV2Schema | OpenapiV3Schema | boolean, + schema: + | Draft4Schema + | Draft6Schema + | Draft7Schema + | SwaggerV2Schema + | OpenapiV3Schema + | boolean, namesStack: Record, name?: string, - isRoot?: boolean, + isRoot?: boolean ): any { - if (typeof schema === 'boolean') {return schema;} + if (typeof schema === 'boolean') { + return schema; + } schema = Object.assign({}, schema); if (isRoot) { namesStack[String(name)] = 0; (schema as any)[this.MODELGEN_INFFERED_NAME] = name; name = ''; - } else if (name && !(schema as any)[this.MODELGEN_INFFERED_NAME]) { + } else if ( + name && + !(schema as any)[this.MODELGEN_INFFERED_NAME] && + schema.$ref === undefined + ) { let occurrence = namesStack[String(name)]; if (occurrence === undefined) { namesStack[String(name)] = 0; @@ -162,49 +273,102 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { } if (schema.allOf !== undefined) { - schema.allOf = (schema.allOf as any[]).map((item: any, idx: number) => this.reflectSchemaNames(item, namesStack, this.ensureNamePattern(name, 'allOf', idx))); + schema.allOf = (schema.allOf as any[]).map((item: any, idx: number) => + this.reflectSchemaNames( + item, + namesStack, + this.ensureNamePattern(name, 'allOf', idx) + ) + ); } if (schema.oneOf !== undefined) { - schema.oneOf = (schema.oneOf as any[]).map((item: any, idx: number) => this.reflectSchemaNames(item, namesStack, this.ensureNamePattern(name, 'oneOf', idx))); + schema.oneOf = (schema.oneOf as any[]).map((item: any, idx: number) => + this.reflectSchemaNames( + item, + namesStack, + this.ensureNamePattern(name, 'oneOf', idx) + ) + ); } if (schema.anyOf !== undefined) { - schema.anyOf = (schema.anyOf as any[]).map((item: any, idx: number) => this.reflectSchemaNames(item, namesStack, this.ensureNamePattern(name, 'anyOf', idx))); + schema.anyOf = (schema.anyOf as any[]).map((item: any, idx: number) => + this.reflectSchemaNames( + item, + namesStack, + this.ensureNamePattern(name, 'anyOf', idx) + ) + ); } if (schema.not !== undefined) { - schema.not = this.reflectSchemaNames(schema.not, namesStack, this.ensureNamePattern(name, 'not')); + schema.not = this.reflectSchemaNames( + schema.not, + namesStack, + this.ensureNamePattern(name, 'not') + ); } if ( typeof schema.additionalItems === 'object' && schema.additionalItems !== undefined ) { - schema.additionalItems = this.reflectSchemaNames(schema.additionalItems, namesStack, this.ensureNamePattern(name, 'additionalItem')); + schema.additionalItems = this.reflectSchemaNames( + schema.additionalItems, + namesStack, + this.ensureNamePattern(name, 'additionalItem') + ); } if ( - typeof schema.additionalProperties === 'object' && + typeof schema.additionalProperties === 'object' && schema.additionalProperties !== undefined ) { - schema.additionalProperties = this.reflectSchemaNames(schema.additionalProperties, namesStack, this.ensureNamePattern(name, 'additionalProperty')); + schema.additionalProperties = this.reflectSchemaNames( + schema.additionalProperties, + namesStack, + this.ensureNamePattern(name, 'additionalProperty') + ); } if (schema.items !== undefined) { if (Array.isArray(schema.items)) { - schema.items = (schema.items as any[]).map((item: Draft7Schema | boolean, idx: number) => this.reflectSchemaNames(item, namesStack, this.ensureNamePattern(name, 'item', idx))); + schema.items = (schema.items as any[]).map( + (item: Draft7Schema | boolean, idx: number) => + this.reflectSchemaNames( + item, + namesStack, + this.ensureNamePattern(name, 'item', idx) + ) + ); } else { - schema.items = this.reflectSchemaNames(schema.items, namesStack, this.ensureNamePattern(name, 'item')); + schema.items = this.reflectSchemaNames( + schema.items, + namesStack, + this.ensureNamePattern(name, 'item') + ); } } if (schema.properties !== undefined) { - const properties : any = {}; - for (const [propertyName, propertySchema] of Object.entries(schema.properties)) { - properties[String(propertyName)] = this.reflectSchemaNames(propertySchema, namesStack, this.ensureNamePattern(name, propertyName)); + const properties: any = {}; + for (const [propertyName, propertySchema] of Object.entries( + schema.properties + )) { + properties[String(propertyName)] = this.reflectSchemaNames( + propertySchema, + namesStack, + this.ensureNamePattern(name, propertyName) + ); } schema.properties = properties; } if (schema.dependencies !== undefined) { const dependencies: any = {}; - for (const [dependencyName, dependency] of Object.entries(schema.dependencies)) { + for (const [dependencyName, dependency] of Object.entries( + schema.dependencies + )) { if (typeof dependency === 'object' && !Array.isArray(dependency)) { - dependencies[String(dependencyName)] = this.reflectSchemaNames(dependency as any, namesStack, this.ensureNamePattern(name, dependencyName)); + dependencies[String(dependencyName)] = this.reflectSchemaNames( + dependency as any, + namesStack, + this.ensureNamePattern(name, dependencyName) + ); } else { dependencies[String(dependencyName)] = dependency as string[]; } @@ -213,15 +377,29 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { } if (schema.patternProperties !== undefined) { const patternProperties: any = {}; - for (const [idx, [patternPropertyName, patternProperty]] of Object.entries(Object.entries(schema.patternProperties))) { - patternProperties[String(patternPropertyName)] = this.reflectSchemaNames(patternProperty as any, namesStack, this.ensureNamePattern(name, 'pattern_property', idx)); + for (const [ + idx, + [patternPropertyName, patternProperty] + ] of Object.entries(Object.entries(schema.patternProperties))) { + patternProperties[String(patternPropertyName)] = + this.reflectSchemaNames( + patternProperty as any, + namesStack, + this.ensureNamePattern(name, 'pattern_property', idx) + ); } schema.patternProperties = patternProperties; } if (schema.definitions !== undefined) { const definitions: { [key: string]: any } = {}; - for (const [definitionName, definition] of Object.entries(schema.definitions)) { - definitions[String(definitionName)] = this.reflectSchemaNames(definition, namesStack, this.ensureNamePattern(name, definitionName)); + for (const [definitionName, definition] of Object.entries( + schema.definitions + )) { + definitions[String(definitionName)] = this.reflectSchemaNames( + definition, + namesStack, + this.ensureNamePattern(name, definitionName) + ); } schema.definitions = definitions; } @@ -229,21 +407,41 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { if (!(schema instanceof Draft4Schema)) { //Keywords introduced in draft 6 if (schema.contains !== undefined) { - schema.contains = this.reflectSchemaNames(schema.contains, namesStack, this.ensureNamePattern(name, 'contain')); + schema.contains = this.reflectSchemaNames( + schema.contains, + namesStack, + this.ensureNamePattern(name, 'contain') + ); } if (schema.propertyNames !== undefined) { - schema.propertyNames = this.reflectSchemaNames(schema.propertyNames, namesStack, this.ensureNamePattern(name, 'propertyName')); + schema.propertyNames = this.reflectSchemaNames( + schema.propertyNames, + namesStack, + this.ensureNamePattern(name, 'propertyName') + ); } if (!(schema instanceof Draft6Schema)) { //Keywords introduced in Draft 7 if (schema.if !== undefined) { - schema.if = this.reflectSchemaNames(schema.if, namesStack, this.ensureNamePattern(name, 'if')); + schema.if = this.reflectSchemaNames( + schema.if, + namesStack, + this.ensureNamePattern(name, 'if') + ); } if (schema.then !== undefined) { - schema.then = this.reflectSchemaNames(schema.then, namesStack, this.ensureNamePattern(name, 'then')); + schema.then = this.reflectSchemaNames( + schema.then, + namesStack, + this.ensureNamePattern(name, 'then') + ); } if (schema.else !== undefined) { - schema.else = this.reflectSchemaNames(schema.else, namesStack, this.ensureNamePattern(name, 'else')); + schema.else = this.reflectSchemaNames( + schema.else, + namesStack, + this.ensureNamePattern(name, 'else') + ); } } } @@ -252,12 +450,15 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { /** * Ensure schema name using previous name and new part - * + * * @param previousName to concatenate with * @param newParts */ - private static ensureNamePattern(previousName: string | undefined, ...newParts: any[]): string { - const pattern = newParts.map(part => `${part}`).join('_'); + private static ensureNamePattern( + previousName: string | undefined, + ...newParts: any[] + ): string { + const pattern = newParts.map((part) => `${part}`).join('_'); if (!previousName) { return pattern; } @@ -266,26 +467,24 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { /** * Simplifies a JSON Schema into a common models - * + * * @param schema to simplify to common model */ - static convertSchemaToCommonModel(schema: Draft4Schema | Draft6Schema | Draft7Schema | SwaggerV2Schema| AsyncapiV2Schema | boolean): Record { - const commonModelsMap: Record = {}; + static convertSchemaToCommonModel( + schema: + | Draft4Schema + | Draft6Schema + | Draft7Schema + | SwaggerV2Schema + | AsyncapiV2Schema + | boolean, + options?: ProcessorOptions + ): CommonModel { const interpreter = new Interpreter(); - const model = interpreter.interpret(schema); - if (model !== undefined) { - const commonModels = postInterpretModel(model); - for (const commonModel of commonModels) { - if (commonModel.$id) { - if (commonModelsMap[commonModel.$id] !== undefined) { - Logger.warn(`Overwriting existing model with $id ${commonModel.$id}, are there two models with the same id present?`, commonModel); - } - commonModelsMap[commonModel.$id] = commonModel; - } else { - Logger.warn('Model did not have $id, ignoring.', commonModel); - } - } + const model = interpreter.interpret(schema, options?.interpreter); + if (model === undefined) { + throw new Error('Could not interpret schema to internal model'); } - return commonModelsMap; + return model; } } diff --git a/src/processors/OpenAPIInputProcessor.ts b/src/processors/OpenAPIInputProcessor.ts index 59e4274a40..c792588dde 100644 --- a/src/processors/OpenAPIInputProcessor.ts +++ b/src/processors/OpenAPIInputProcessor.ts @@ -1,9 +1,10 @@ import { AbstractInputProcessor } from './AbstractInputProcessor'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; -import { CommonInputModel, OpenapiV3Schema } from '../models'; +import { InputMetaModel, OpenapiV3Schema, ProcessorOptions } from '../models'; import { Logger } from '../utils'; import SwaggerParser from '@apidevtools/swagger-parser'; import { OpenAPIV3 } from 'openapi-types'; +import { convertToMetaModel } from '../helpers'; /** * Class for processing OpenAPI V3.0 inputs @@ -13,24 +14,38 @@ export class OpenAPIInputProcessor extends AbstractInputProcessor { /** * Process the input as a OpenAPI V3.0 document - * - * @param input + * + * @param input */ - async process(input: Record): Promise { - if (!this.shouldProcess(input)) {throw new Error('Input is not a OpenAPI document so it cannot be processed.');} + async process( + input: any, + options?: ProcessorOptions + ): Promise { + if (!this.shouldProcess(input)) { + throw new Error( + 'Input is not a OpenAPI document so it cannot be processed.' + ); + } Logger.debug('Processing input as an OpenAPI document'); - const inputModel = new CommonInputModel(); + const inputModel = new InputMetaModel(); inputModel.originalInput = input; - const api = (await SwaggerParser.dereference(input as any) as unknown) as OpenAPIV3.Document; + const api = (await SwaggerParser.dereference( + input as any + )) as unknown as OpenAPIV3.Document; for (const [path, pathObject] of Object.entries(api.paths)) { - this.processPath(pathObject, path, inputModel); + this.processPath(pathObject, path, inputModel, options); } return inputModel; } - private processPath(pathObject: OpenAPIV3.PathItemObject | undefined, path: string, inputModel: CommonInputModel) { + private processPath( + pathObject: OpenAPIV3.PathItemObject | undefined, + path: string, + inputModel: InputMetaModel, + options?: ProcessorOptions + ) { if (pathObject) { //Remove all special chars from path let formattedPathName = path.replace(/[^\w\s*]+/g, ''); @@ -38,92 +53,204 @@ export class OpenAPIInputProcessor extends AbstractInputProcessor { formattedPathName = formattedPathName.replace(/\//, ''); //Replace all segment separators '/' formattedPathName = formattedPathName.replace(/\//gm, '_'); - this.processOperation(pathObject.get, `${formattedPathName}_get`, inputModel); - this.processOperation(pathObject.put, `${formattedPathName}_put`, inputModel); - this.processOperation(pathObject.post, `${formattedPathName}_post`, inputModel); - this.processOperation(pathObject.delete, `${formattedPathName}_delete`, inputModel); - this.processOperation(pathObject.options, `${formattedPathName}_options`, inputModel); - this.processOperation(pathObject.head, `${formattedPathName}_head`, inputModel); - this.processOperation(pathObject.patch, `${formattedPathName}_patch`, inputModel); - this.processOperation(pathObject.trace, `${formattedPathName}_trace`, inputModel); - } + this.processOperation( + pathObject.get, + `${formattedPathName}_get`, + inputModel, + options + ); + this.processOperation( + pathObject.put, + `${formattedPathName}_put`, + inputModel, + options + ); + this.processOperation( + pathObject.post, + `${formattedPathName}_post`, + inputModel, + options + ); + this.processOperation( + pathObject.delete, + `${formattedPathName}_delete`, + inputModel, + options + ); + this.processOperation( + pathObject.options, + `${formattedPathName}_options`, + inputModel, + options + ); + this.processOperation( + pathObject.head, + `${formattedPathName}_head`, + inputModel, + options + ); + this.processOperation( + pathObject.patch, + `${formattedPathName}_patch`, + inputModel, + options + ); + this.processOperation( + pathObject.trace, + `${formattedPathName}_trace`, + inputModel, + options + ); + } } - private processOperation(operation: OpenAPIV3.OperationObject | undefined, path: string, inputModel: CommonInputModel) { + private processOperation( + operation: OpenAPIV3.OperationObject | undefined, + path: string, + inputModel: InputMetaModel, + options?: ProcessorOptions + ) { if (operation) { - this.iterateResponses(operation.responses, path, inputModel); + this.iterateResponses(operation.responses, path, inputModel, options); if (operation.requestBody) { - this.iterateMediaType((operation.requestBody as OpenAPIV3.RequestBodyObject).content || {}, path, inputModel); + this.iterateMediaType( + (operation.requestBody as OpenAPIV3.RequestBodyObject).content || {}, + path, + inputModel, + options + ); } - + if (operation.callbacks) { - for (const [callbackName, callback] of Object.entries(operation.callbacks)) { + for (const [callbackName, callback] of Object.entries( + operation.callbacks + )) { const callbackObject = callback as OpenAPIV3.CallbackObject; - for (const [callbackPath, callbackPathObject] of Object.entries(callbackObject)) { - this.processPath(callbackPathObject, `${path}_callback_${callbackName}_${callbackPath}`, inputModel); + for (const [callbackPath, callbackPathObject] of Object.entries( + callbackObject + )) { + this.processPath( + callbackPathObject, + `${path}_callback_${callbackName}_${callbackPath}`, + inputModel, + options + ); } } } } } - private iterateResponses(responses: OpenAPIV3.ResponsesObject, path: string, inputModel: CommonInputModel) { + private iterateResponses( + responses: OpenAPIV3.ResponsesObject, + path: string, + inputModel: InputMetaModel, + options?: ProcessorOptions + ) { for (const [responseName, response] of Object.entries(responses)) { //Replace any '/' with '_' const formattedResponseName = responseName.replace(/\//, '_'); - this.iterateMediaType((response as OpenAPIV3.ResponseObject).content || {}, `${path}_${formattedResponseName}`, inputModel); + this.iterateMediaType( + (response as OpenAPIV3.ResponseObject).content || {}, + `${path}_${formattedResponseName}`, + inputModel, + options + ); } } - private iterateMediaType(mediaTypes: {[media: string]: OpenAPIV3.MediaTypeObject}, path: string, inputModel: CommonInputModel) { + private iterateMediaType( + mediaTypes: { [media: string]: OpenAPIV3.MediaTypeObject }, + path: string, + inputModel: InputMetaModel, + options?: ProcessorOptions + ) { for (const [mediaContent, mediaTypeObject] of Object.entries(mediaTypes)) { const mediaType = mediaTypeObject; - if (mediaType.schema === undefined) { continue; } - const mediaTypeSchema = (mediaType.schema as unknown) as OpenAPIV3.SchemaObject; + if (mediaType.schema === undefined) { + continue; + } + const mediaTypeSchema = + mediaType.schema as unknown as OpenAPIV3.SchemaObject; //Replace any '/' with '_' const formattedMediaContent = mediaContent.replace(/\//, '_'); - this.includeSchema(mediaTypeSchema, `${path}_${formattedMediaContent}`, inputModel); + this.includeSchema( + mediaTypeSchema, + `${path}_${formattedMediaContent}`, + inputModel, + options + ); } } - private includeSchema(schema: OpenAPIV3.SchemaObject, name: string, inputModel: CommonInputModel) { - const internalSchema = OpenAPIInputProcessor.convertToInternalSchema(schema, name); - const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(internalSchema); - inputModel.models = {...inputModel.models, ...commonModels}; + private includeSchema( + schema: OpenAPIV3.SchemaObject, + name: string, + inputModel: InputMetaModel, + options?: ProcessorOptions + ) { + const internalSchema = OpenAPIInputProcessor.convertToInternalSchema( + schema, + name + ); + const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( + internalSchema, + options + ); + if (newCommonModel.$id !== undefined) { + if (inputModel.models[newCommonModel.$id] !== undefined) { + Logger.warn( + `Overwriting existing model with $id ${newCommonModel.$id}, are there two models with the same id present?`, + newCommonModel + ); + } + const metaModel = convertToMetaModel(newCommonModel); + inputModel.models[metaModel.name] = metaModel; + } else { + Logger.warn('Model did not have $id, ignoring.', newCommonModel); + } } /** * Converts a schema to the internal schema format. - * + * * @param schema to convert * @param name of the schema */ static convertToInternalSchema( schema: OpenAPIV3.SchemaObject, - name: string): OpenapiV3Schema { + name: string + ): OpenapiV3Schema { let internalSchema = OpenapiV3Schema.toSchema(schema as any); - internalSchema = JsonSchemaInputProcessor.reflectSchemaNames(internalSchema, {}, name, true); + internalSchema = JsonSchemaInputProcessor.reflectSchemaNames( + internalSchema, + {}, + name, + true + ); return internalSchema; } /** - * Figures out if an object is of type OpenAPI V3.0.x document and supported - * - * @param input - */ - shouldProcess(input: Record) : boolean { + * Figures out if an object is of type OpenAPI V3.0.x document and supported + * + * @param input + */ + shouldProcess(input: any): boolean { const version = this.tryGetVersionOfDocument(input); - if (!version) {return false;} + if (!version) { + return false; + } return OpenAPIInputProcessor.supportedVersions.includes(version); } /** * Try to find the AsyncAPI version from the input. If it cannot undefined are returned, if it can, the version is returned. - * - * @param input + * + * @param input */ - tryGetVersionOfDocument(input: Record) : string | undefined { + tryGetVersionOfDocument(input: any): string | undefined { return input && input.openapi; } } diff --git a/src/processors/SwaggerInputProcessor.ts b/src/processors/SwaggerInputProcessor.ts index 91b7faa664..2605b24334 100644 --- a/src/processors/SwaggerInputProcessor.ts +++ b/src/processors/SwaggerInputProcessor.ts @@ -1,9 +1,10 @@ import { AbstractInputProcessor } from './AbstractInputProcessor'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; -import { CommonInputModel, SwaggerV2Schema } from '../models'; +import { InputMetaModel, SwaggerV2Schema, ProcessorOptions } from '../models'; import { Logger } from '../utils'; import SwaggerParser from '@apidevtools/swagger-parser'; import { OpenAPIV2 } from 'openapi-types'; +import { convertToMetaModel } from '../helpers'; /** * Class for processing Swagger inputs @@ -13,18 +14,27 @@ export class SwaggerInputProcessor extends AbstractInputProcessor { /** * Process the input as a Swagger document - * - * @param input + * + * @param input */ - async process(input: Record): Promise { - if (!this.shouldProcess(input)) {throw new Error('Input is not a Swagger document so it cannot be processed.');} + async process( + input: any, + options?: ProcessorOptions + ): Promise { + if (!this.shouldProcess(input)) { + throw new Error( + 'Input is not a Swagger document so it cannot be processed.' + ); + } Logger.debug('Processing input as a Swagger document'); - const common = new CommonInputModel(); + const common = new InputMetaModel(); common.originalInput = input; - + //Since we require that all references have been dereferenced, we cannot "simply" support already parsed inputs. - const api = (await SwaggerParser.dereference(input as any) as unknown) as OpenAPIV2.Document; + const api = (await SwaggerParser.dereference( + input as any + )) as unknown as OpenAPIV2.Document; for (const [path, pathObject] of Object.entries(api.paths)) { //Remove all special chars from path let formattedPathName = path.replace(/[^\w\s*]+/g, ''); @@ -32,78 +42,167 @@ export class SwaggerInputProcessor extends AbstractInputProcessor { formattedPathName = formattedPathName.replace(/\//, ''); //Replace all segment separators '/' formattedPathName = formattedPathName.replace(/\//gm, '_'); - this.processOperation(pathObject.get, `${formattedPathName}_get`, common); - this.processOperation(pathObject.put, `${formattedPathName}_put`, common); - this.processOperation(pathObject.post, `${formattedPathName}_post`, common); - this.processOperation(pathObject.options, `${formattedPathName}_options`, common); - this.processOperation(pathObject.head, `${formattedPathName}_head`, common); - this.processOperation(pathObject.patch, `${formattedPathName}_patch`, common); + this.processOperation( + pathObject.get, + `${formattedPathName}_get`, + common, + options + ); + this.processOperation( + pathObject.put, + `${formattedPathName}_put`, + common, + options + ); + this.processOperation( + pathObject.post, + `${formattedPathName}_post`, + common, + options + ); + this.processOperation( + pathObject.options, + `${formattedPathName}_options`, + common, + options + ); + this.processOperation( + pathObject.head, + `${formattedPathName}_head`, + common, + options + ); + this.processOperation( + pathObject.patch, + `${formattedPathName}_patch`, + common, + options + ); } return common; } - private processOperation(operation: OpenAPIV2.OperationObject | undefined, path: string, inputModel: CommonInputModel) { + private processOperation( + operation: OpenAPIV2.OperationObject | undefined, + path: string, + inputModel: InputMetaModel, + options?: ProcessorOptions + ) { if (operation) { - this.includeResponses(operation.responses, path, inputModel); - this.includeParameters(operation.parameters, path, inputModel); + this.includeResponses(operation.responses, path, inputModel, options); + this.includeParameters(operation.parameters, path, inputModel, options); } } - private includeResponses(responses: OpenAPIV2.ResponsesObject, path: string, inputModel: CommonInputModel) { + private includeResponses( + responses: OpenAPIV2.ResponsesObject, + path: string, + inputModel: InputMetaModel, + options?: ProcessorOptions + ) { for (const [responseName, response] of Object.entries(responses)) { if (response !== undefined) { - const getOperationResponseSchema = (response as OpenAPIV2.ResponseObject).schema; - if (getOperationResponseSchema !== undefined) { - const swaggerSchema = SwaggerInputProcessor.convertToInternalSchema(getOperationResponseSchema, `${path}_${responseName}`); - const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(swaggerSchema); - inputModel.models = {...inputModel.models, ...commonModels}; + const getOperationResponseSchema = ( + response as OpenAPIV2.ResponseObject + ).schema; + if (getOperationResponseSchema !== undefined) { + this.includeSchema( + getOperationResponseSchema, + `${path}_${responseName}`, + inputModel, + options + ); } } } } - private includeParameters(parameters: OpenAPIV2.Parameters | undefined, path: string, inputModel: CommonInputModel) { + private includeParameters( + parameters: OpenAPIV2.Parameters | undefined, + path: string, + inputModel: InputMetaModel, + options?: ProcessorOptions + ) { for (const parameterObject of parameters || []) { const parameter = parameterObject as OpenAPIV2.Parameter; if (parameter.in === 'body') { const bodyParameterSchema = parameter.schema; - const swaggerSchema = SwaggerInputProcessor.convertToInternalSchema(bodyParameterSchema, `${path}_body`); - const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(swaggerSchema); - inputModel.models = {...inputModel.models, ...commonModels}; + this.includeSchema( + bodyParameterSchema, + `${path}_body`, + inputModel, + options + ); + } + } + } + + private includeSchema( + schema: OpenAPIV2.SchemaObject, + name: string, + inputModel: InputMetaModel, + options?: ProcessorOptions + ) { + const internalSchema = SwaggerInputProcessor.convertToInternalSchema( + schema, + name + ); + const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( + internalSchema, + options + ); + if (newCommonModel.$id !== undefined) { + if (inputModel.models[newCommonModel.$id] !== undefined) { + Logger.warn( + `Overwriting existing model with $id ${newCommonModel.$id}, are there two models with the same id present?`, + newCommonModel + ); } + const metaModel = convertToMetaModel(newCommonModel); + inputModel.models[metaModel.name] = metaModel; + } else { + Logger.warn('Model did not have $id, ignoring.', newCommonModel); } } - + /** * Converts a Swagger 2.0 Schema to the internal schema format. - * + * * @param schema to convert * @param name of the schema */ static convertToInternalSchema( schema: OpenAPIV2.SchemaObject, - name: string): SwaggerV2Schema { - schema = JsonSchemaInputProcessor.reflectSchemaNames(schema, {}, name, true); + name: string + ): SwaggerV2Schema { + schema = JsonSchemaInputProcessor.reflectSchemaNames( + schema, + {}, + name, + true + ); return SwaggerV2Schema.toSchema(schema); } /** - * Figures out if an object is of type Swagger document and supported - * - * @param input - */ - shouldProcess(input: Record) : boolean { + * Figures out if an object is of type Swagger document and supported + * + * @param input + */ + shouldProcess(input: any): boolean { const version = this.tryGetVersionOfDocument(input); - if (!version) { return false; } + if (!version) { + return false; + } return SwaggerInputProcessor.supportedVersions.includes(version); } /** * Try to find the swagger version from the input. If it cannot, undefined are returned, if it can, the version is returned. - * - * @param input + * + * @param input */ - tryGetVersionOfDocument(input: Record) : string | undefined { + tryGetVersionOfDocument(input: any): string | undefined { return input && input.swagger; } } diff --git a/src/processors/TemplateInputProcessor.ts b/src/processors/TemplateInputProcessor.ts new file mode 100644 index 0000000000..ee39c78958 --- /dev/null +++ b/src/processors/TemplateInputProcessor.ts @@ -0,0 +1,32 @@ +import { AbstractInputProcessor } from './AbstractInputProcessor'; +import { InputMetaModel, ProcessorOptions } from '../models'; + +/** + * Class for processing X input + */ +export class TemplateInputProcessor extends AbstractInputProcessor { + shouldProcess(input?: any): boolean { + if (!input) { + return false; + } + + return false; + } + + async process( + input?: any, + options?: ProcessorOptions + ): Promise { + if (!this.shouldProcess(input)) { + throw new Error( + 'Input is not X and cannot be processed by this input processor.' + ); + } + + const inputModel = new InputMetaModel(); + + // Add processing code here + + return inputModel; + } +} diff --git a/src/processors/TypeScriptInputProcessor.ts b/src/processors/TypeScriptInputProcessor.ts index c85d2e1c91..71dcc71412 100644 --- a/src/processors/TypeScriptInputProcessor.ts +++ b/src/processors/TypeScriptInputProcessor.ts @@ -1,43 +1,53 @@ -import { CommonInputModel, ProcessorOptions } from '../models'; +import { InputMetaModel, ProcessorOptions } from '../models'; import { resolve } from 'path'; import ts from 'typescript'; import * as TJS from 'typescript-json-schema'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; import { AbstractInputProcessor } from './AbstractInputProcessor'; +import { convertToMetaModel } from '../helpers'; +import { Logger } from '../utils'; /** Class for processing Typescript code inputs to Common module*/ - -export interface TypeScriptInputProcessorOptions extends TJS.PartialArgs{ - uniqueNames? : boolean; - required? : boolean; - compilerOptions? : TJS.CompilerOptions; +export interface TypeScriptInputProcessorOptions extends TJS.PartialArgs { + uniqueNames?: boolean; + required?: boolean; + compilerOptions?: TJS.CompilerOptions; } export class TypeScriptInputProcessor extends AbstractInputProcessor { static settings: TypeScriptInputProcessorOptions = { uniqueNames: false, required: true, compilerOptions: { - strictNullChecks: false, + strictNullChecks: false } - }; + }; private generateProgram(file: string): ts.Program { - return TJS.getProgramFromFiles([resolve(file)], TypeScriptInputProcessor.settings.compilerOptions); + return TJS.getProgramFromFiles( + [resolve(file)], + TypeScriptInputProcessor.settings.compilerOptions + ); } - private generateJSONSchema(file: string, typeRequired: string, options?: TypeScriptInputProcessorOptions): Array | null { + private generateJSONSchema( + file: string, + typeRequired: string, + options?: TypeScriptInputProcessorOptions + ): Array | null { const mergedOptions = { ...TypeScriptInputProcessor.settings, - ...options, + ...options }; - + const program: ts.Program = this.generateProgram(file); if (typeRequired === '*') { const generator = TJS.buildGenerator(program, mergedOptions); - if (!generator) {throw new Error('Cound not generate all types automatically');} - + if (!generator) { + throw new Error('Cound not generate all types automatically'); + } + const symbols = generator.getMainFileSymbols(program); - return symbols.map(symbol => { + return symbols.map((symbol) => { const schemaFromGenerator = generator.getSchemaForSymbol(symbol); schemaFromGenerator.$id = symbol; return schemaFromGenerator; @@ -45,47 +55,69 @@ export class TypeScriptInputProcessor extends AbstractInputProcessor { } const schema = TJS.generateSchema(program, typeRequired, mergedOptions); - if (!schema) { return null; } + if (!schema) { + return null; + } schema.$id = typeRequired; return [schema]; } - shouldProcess(input: Record): boolean { + shouldProcess(input: any): boolean { // checking if input is null - if ((input === null || undefined) || (input.baseFile === null || undefined)) { + if (input === null || undefined || input.baseFile === null || undefined) { return false; } // checking the empty string - if (Object.keys(input).length === 0 && input.constructor === Object) { return false; } + if (Object.keys(input).length === 0 && input.constructor === Object) { + return false; + } //checking if input structure is correct - if (typeof input !== 'object' || typeof input.baseFile !== 'string') { - return false; + if (typeof input !== 'object' || typeof input.baseFile !== 'string') { + return false; } return true; } - process(input: Record, options?: ProcessorOptions): Promise { - const common = new CommonInputModel(); + process(input: any, options?: ProcessorOptions): Promise { + const inputModel = new InputMetaModel(); if (!this.shouldProcess(input)) { return Promise.reject(new Error('Input is not of the valid file format')); } const { fileContents, baseFile } = input; - common.originalInput = fileContents; + inputModel.originalInput = fileContents; // obtain generated schema - const generatedSchemas = this.generateJSONSchema(baseFile, '*', options?.typescript); + const generatedSchemas = this.generateJSONSchema( + baseFile, + '*', + options?.typescript + ); if (generatedSchemas) { for (const schema of generatedSchemas) { - const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(schema as Record); - common.models = {...common.models, ...commonModels }; + const newCommonModel = + JsonSchemaInputProcessor.convertSchemaToCommonModel( + schema as any, + options + ); + if (newCommonModel.$id !== undefined) { + if (inputModel.models[newCommonModel.$id] !== undefined) { + Logger.warn( + `Overwriting existing model with $id ${newCommonModel.$id}, are there two models with the same id present?`, + newCommonModel + ); + } + const metaModel = convertToMetaModel(newCommonModel); + inputModel.models[metaModel.name] = metaModel; + } else { + Logger.warn('Model did not have $id, ignoring.', newCommonModel); + } } } - return Promise.resolve(common); + return Promise.resolve(inputModel); } } - diff --git a/src/utils/LoggingInterface.ts b/src/utils/LoggingInterface.ts index 13ebacd910..27f16f76c2 100644 --- a/src/utils/LoggingInterface.ts +++ b/src/utils/LoggingInterface.ts @@ -1,4 +1,3 @@ - /** * Logging interface for the model generation library */ @@ -11,7 +10,7 @@ export interface ModelLoggingInterface { /** * Logger class for the model generation library - * + * * This class acts as a forefront for any external loggers which is why it also implements the interface itself. */ export class LoggerClass implements ModelLoggingInterface { @@ -40,7 +39,7 @@ export class LoggerClass implements ModelLoggingInterface { /** * Sets the logger to use for the model generation library - * + * * @param logger to add */ setLogger(logger?: ModelLoggingInterface): void { diff --git a/src/utils/Partials.ts b/src/utils/Partials.ts new file mode 100644 index 0000000000..5375686f44 --- /dev/null +++ b/src/utils/Partials.ts @@ -0,0 +1,57 @@ +/* eslint-disable security/detect-object-injection, @typescript-eslint/ban-types */ + +/** + * Deep partial type that does NOT partial function arguments. + */ +export type DeepPartial = T extends Function + ? T + : T extends object + ? { [P in keyof T]?: DeepPartial } + : T; + +/** + * Return true or false based on whether the input object is a regular object or a class + * + * Taken from: https://stackoverflow.com/a/43197340/6803886 + * @param obj + */ +function isClass(obj: any): boolean { + const isCtorClass = + obj.constructor && obj.constructor.toString().substring(0, 5) === 'class'; + if (obj.prototype === undefined) { + return isCtorClass; + } + const isPrototypeCtorClass = + obj.prototype.constructor && + obj.prototype.constructor.toString && + obj.prototype.constructor.toString().substring(0, 5) === 'class'; + return isCtorClass || isPrototypeCtorClass; +} + +/** + * Merge a non optional value with custom optional values to form a full value that has all properties sat. + */ +export function mergePartialAndDefault>( + defaultNonOptional: T, + customOptional?: DeepPartial +): T { + if (customOptional === undefined) { + return defaultNonOptional; + } + // create a new object + const target = { ...defaultNonOptional } as Record; + + // deep merge the object into the target object + for (const [propName, prop] of Object.entries(customOptional)) { + const isObjectOrClass = + typeof prop === 'object' && target[propName] !== undefined; + const isRegularObject = !isClass(prop); + if (isObjectOrClass && isRegularObject) { + target[propName] = mergePartialAndDefault(target[propName], prop); + } else if (prop) { + target[propName] = prop; + } + } + + return target as T; +} diff --git a/src/utils/guards.ts b/src/utils/guards.ts index a7144bb345..bb5555fa63 100644 --- a/src/utils/guards.ts +++ b/src/utils/guards.ts @@ -1,5 +1,7 @@ import { Preset, PresetWithOptions } from '../models'; -export function isPresetWithOptions(preset: Preset | PresetWithOptions): preset is PresetWithOptions { +export function isPresetWithOptions( + preset: Preset | PresetWithOptions +): preset is PresetWithOptions { return Object.prototype.hasOwnProperty.call(preset, 'preset'); } diff --git a/src/utils/index.ts b/src/utils/index.ts index 6d1099cf01..d966cee439 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,2 +1,3 @@ export * from './guards'; export * from './LoggingInterface'; +export * from './Partials'; diff --git a/test/TestUtils/TestConstrainer.ts b/test/TestUtils/TestConstrainer.ts new file mode 100644 index 0000000000..856e921923 --- /dev/null +++ b/test/TestUtils/TestConstrainer.ts @@ -0,0 +1,28 @@ +/* eslint-disable no-undef */ +import { Constraints, TypeMapping } from '../../src/helpers'; + +export const mockedTypeMapping: TypeMapping = { + Object: jest.fn().mockReturnValue('test'), + Reference: jest.fn().mockReturnValue('test'), + Any: jest.fn().mockReturnValue('test'), + Float: jest.fn().mockReturnValue('test'), + Integer: jest.fn().mockReturnValue('test'), + String: jest.fn().mockReturnValue('test'), + Boolean: jest.fn().mockReturnValue('test'), + Tuple: jest.fn().mockReturnValue('test'), + Array: jest.fn().mockReturnValue('test'), + Enum: jest.fn().mockReturnValue('test'), + Union: jest.fn().mockReturnValue('test'), + Dictionary: jest.fn().mockReturnValue('test') +}; + +export const mockedConstraints: Constraints = { + enumKey: jest.fn().mockImplementation(({ enumKey }) => enumKey), + enumValue: jest.fn().mockImplementation(({ enumValue }) => enumValue), + modelName: jest.fn().mockImplementation(({ modelName }) => modelName), + propertyKey: jest + .fn() + .mockImplementation( + ({ objectPropertyModel }) => objectPropertyModel.propertyName + ) +}; diff --git a/test/TestUtils/TestGenerator.ts b/test/TestUtils/TestGenerator.ts new file mode 100644 index 0000000000..90ec0a4c95 --- /dev/null +++ b/test/TestUtils/TestGenerator.ts @@ -0,0 +1,60 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { + AbstractGenerator, + InputMetaModel, + IndentationTypes, + RenderOutput, + ConstrainedMetaModel, + MetaModel, + ConstrainedAnyModel, + Preset +} from '../../src'; +import { AbstractDependencyManager } from '../../src/generators/AbstractDependencyManager'; + +export const testOptions = { + indentation: { + type: IndentationTypes.SPACES, + size: 2 + } +}; +export class TestGenerator extends AbstractGenerator { + constructor(options: any = testOptions) { + super('TestGenerator', options); + } + + public constrainToMetaModel(model: MetaModel): ConstrainedMetaModel { + return new ConstrainedAnyModel(model.name, undefined, ''); + } + public splitMetaModel(model: MetaModel): MetaModel[] { + return [model]; + } + public render( + model: MetaModel, + inputModel: InputMetaModel + ): Promise { + return Promise.resolve( + RenderOutput.toRenderOutput({ + result: model.name || 'rendered content', + renderedName: 'TestName' + }) + ); + } + public renderCompleteModel( + model: MetaModel, + inputModel: InputMetaModel, + options: any + ): Promise { + return Promise.resolve( + RenderOutput.toRenderOutput({ + result: model.name || 'rendered complete content', + renderedName: 'TestName' + }) + ); + } + public testGetPresets(string: string): Array<[Preset, unknown]> { + return this.getPresets(string); + } + public getDependencyManager(options: any): AbstractDependencyManager { + return new AbstractDependencyManager(); + } +} diff --git a/test/TestUtils/TestRenderers.ts b/test/TestUtils/TestRenderers.ts new file mode 100644 index 0000000000..61d31af147 --- /dev/null +++ b/test/TestUtils/TestRenderers.ts @@ -0,0 +1,44 @@ +import { + AbstractRenderer, + InputMetaModel, + RenderOutput, + ConstrainedAnyModel +} from '../../src'; +import { GoRenderer } from '../../src/generators/go/GoRenderer'; +import { CSharpRenderer } from '../../src/generators/csharp/CSharpRenderer'; +import { JavaRenderer } from '../../src/generators/java/JavaRenderer'; +import { JavaScriptRenderer } from '../../src/generators/javascript/JavaScriptRenderer'; +import { TypeScriptRenderer } from '../../src/generators/typescript/TypeScriptRenderer'; +import { testOptions, TestGenerator } from './TestGenerator'; +import { DartRenderer } from '../../src/generators/dart/DartRenderer'; +import { RustRenderer } from '../../src/generators/rust/RustRenderer'; +import { PythonRenderer } from '../../src/generators/python/PythonRenderer'; +import { KotlinRenderer } from '../../src/generators/kotlin/KotlinRenderer'; + +export class TestRenderer extends AbstractRenderer { + constructor(presets = []) { + super( + testOptions, + new TestGenerator(), + presets, + new ConstrainedAnyModel('', undefined, ''), + new InputMetaModel() + ); + } + render(): Promise { + return Promise.resolve( + RenderOutput.toRenderOutput({ result: '', renderedName: '' }) + ); + } +} + +export class MockJavaRenderer extends JavaRenderer {} +export class MockTypeScriptRenderer extends TypeScriptRenderer {} +export class MockGoRenderer extends GoRenderer {} +export class MockCSharpRenderer extends CSharpRenderer {} +export class MockJavaScriptRenderer extends JavaScriptRenderer {} +export class MockDartRenderer extends DartRenderer {} +export class MockRustRenderer extends RustRenderer {} +export class MockPythonRenderer extends PythonRenderer {} + +export class MockKotlinRenderer extends KotlinRenderer {} diff --git a/test/blackbox/BlackBoxTestFiles.ts b/test/blackbox/BlackBoxTestFiles.ts new file mode 100644 index 0000000000..42aee8b5ca --- /dev/null +++ b/test/blackbox/BlackBoxTestFiles.ts @@ -0,0 +1,81 @@ +import * as path from 'path'; +import * as fs from 'fs'; + +/** + * Read all the files in the folder, and return the appropriate Jest `each` entries. + * @param folder + */ +function readFilesInFolder(folder: string) { + // eslint-disable-next-line no-undef + const fullPath = path.resolve(__dirname, `./docs/${folder}`); + // eslint-disable-next-line security/detect-non-literal-fs-filename + return fs.readdirSync(fullPath).map((file) => { + return { + file: `./docs/${folder}/${file}`, + outputDirectory: `./output/${folder}/${path.parse(file).name}` + }; + }); +} + +const OpenAPI3_0Files = readFilesInFolder('OpenAPI-3_0'); +const jsonSchemaDraft7Files = readFilesInFolder('JsonSchemaDraft-7'); +const jsonSchemaDraft6Files = readFilesInFolder('JsonSchemaDraft-6'); +const jsonSchemaDraft4Files = readFilesInFolder('JsonSchemaDraft-4'); +const AsyncAPIV2_0Files = readFilesInFolder('AsyncAPI-2_0'); +const AsyncAPIV2_1Files = readFilesInFolder('AsyncAPI-2_1'); +const AsyncAPIV2_2Files = readFilesInFolder('AsyncAPI-2_2'); +const AsyncAPIV2_3Files = readFilesInFolder('AsyncAPI-2_3'); +const AsyncAPIV2_4Files = readFilesInFolder('AsyncAPI-2_4'); +const AsyncAPIV2_5Files = readFilesInFolder('AsyncAPI-2_5'); + +const filesToTest = [ + ...OpenAPI3_0Files.filter(({ file }) => { + // Too large to process in normal blackbox testing, can be used to locally test stuff. + return !file.includes('postman-api.json'); + }).filter(({ file }) => { + // Too large to process in normal blackbox testing, can be used to locally test stuff. + return !file.includes('twilio-1_13.json'); + }), + ...AsyncAPIV2_0Files.filter(({ file }) => { + // Too large to process in normal blackbox testing, can be used to locally test stuff. + return !file.includes('zbos_mqtt-all-asyncapi.json'); + }), + ...AsyncAPIV2_1Files, + ...AsyncAPIV2_2Files, + ...AsyncAPIV2_3Files, + ...AsyncAPIV2_4Files, + ...AsyncAPIV2_5Files, + ...jsonSchemaDraft4Files.filter(({ file }) => { + // Too large to process in normal blackbox testing, can be used lto ocally test stuff. + return !file.includes('aws-cloudformation.json'); + }), + ...jsonSchemaDraft7Files.filter(({ file }) => { + // Too large to process in normal blackbox testing, can be used to locally test stuff. + return !file.includes('graphql-code-generator.json'); + }), + ...jsonSchemaDraft6Files.filter(({ file }) => { + // Too large to process in normal blackbox testing, can be used to locally test stuff. + return !file.includes('fhir-full.json'); + }) +]; + +/** + * Officially only use one specific file for each input type, and the rest is for local testing. + * + * Otherwise the CI system will take far too long. + */ +export default filesToTest.filter(({ file }) => { + return ( + file.includes('AsyncAPI-2_0/dummy.json') || + file.includes('AsyncAPI-2_1/dummy.json') || + file.includes('AsyncAPI-2_2/dummy.json') || + file.includes('AsyncAPI-2_3/dummy.json') || + file.includes('AsyncAPI-2_4/dummy.json') || + file.includes('AsyncAPI-2_5/streetlight_kafka.json') || + file.includes('JsonSchemaDraft-4/draft-4-core.json') || + file.includes('JsonSchemaDraft-6/draft-6-core.json') || + file.includes('JsonSchemaDraft-7/draft-7-core.json') || + file.includes('OpenAPI-3_0/petstore.json') || + file.includes('Swagger-2_0/petstore.json') + ); +}); diff --git a/test/blackbox/README.md b/test/blackbox/README.md index 8fb06b4510..fa3dce39bb 100644 --- a/test/blackbox/README.md +++ b/test/blackbox/README.md @@ -1,29 +1,34 @@ # BlackBox tests -As we start to integrate more and more inputs, it is important that we test actual and real inputs from users to ensure the library behaves as expected. For now, the best way is to add them as BlackBox tests. The BlackBox tests are focused on catching syntax errors and not logical errors, although they can be manually used for this. +As we start to integrate more and more inputs, we must test actual and real inputs from users to ensure the library behaves as expected. For now, the best way is to add them as BlackBox tests. The BlackBox tests are focused on catching syntax errors and not logical errors, although they can be manually used for this. The documents being tested can be found under [docs](./docs), which contain documents for the following input types: - [AsyncAPI 2.0](./docs/AsyncAPI-2_0) - [AsyncAPI 2.1](./docs/AsyncAPI-2_1) - [AsyncAPI 2.2](./docs/AsyncAPI-2_2) - [AsyncAPI 2.3](./docs/AsyncAPI-2_3) +- [AsyncAPI 2.4](./docs/AsyncAPI-2_4) +- [AsyncAPI 2.5](./docs/AsyncAPI-2_5) - [JSON Schema draft 4](./docs/JsonSchemaDraft-4) - [JSON Schema draft 6](./docs/JsonSchemaDraft-6) - [JSON Schema draft 7](./docs/JsonSchemaDraft-7) - [Swagger 2.0](./docs/Swagger-2_0) - [OpenAPI 3.0](./docs/OpenAPI-3_0) -Each document is tested across all output languages and output will be written to `./output` folder in appropriate sub folders, for easier access. +Each document is tested across all output languages and output will be written to `./output` folder in appropriate sub-folders, for easier access. ## Running the tests -The tests can either be run by installing all dependencies locally, or running it through docker. - -To run the BlackBox tests through Docker, run the command `npm run docker:test:blackbox`. +The tests can either be run by installing all dependencies locally or running it through docker. If you want to run the BlackBox tests locally, you have to install a couple of dependencies: -- To to run the `Java` BlackBox tests, you need to have JDK installed. -- To to run the `TypeScript` BlackBox tests, you need to have TypeScript installed globally - `npm install -g typescript`. -- To to run the `C#` BlackBox tests, you need to have C# compiler installed globally. - https://www.mono-project.com/download/stable/ -- To to run the `Go` BlackBox tests, you need to have GoLang installed - https://golang.org/doc/install +- To run the `Java` BlackBox tests, you need to have JDK installed. +- To run the `TypeScript` BlackBox tests, you need to have TypeScript installed globally - `npm install -g typescript`. +- To run the `C#` BlackBox tests, you need to have C# compiler installed globally. - https://www.mono-project.com/download/stable/ +- To run the `Go` BlackBox tests, you need to have GoLang installed - https://golang.org/doc/install +- To run the `Python` BlackBox tests, you need to have python installed - https://www.python.org/downloads/ +- To run the `Rust` BlackBox tests, you need to have rust installed - https://www.rust-lang.org/tools/install (if you are on mac you might also need to install xcode `xcode-select --install`) +- To run the `Kotlin` BlackBox tests, you need to have a JDK >= 8 as well as kotlinc installed - https://kotlinlang.org/docs/command-line.html -By default, the BlackBox tests are not run with the regular `npm run test`, but can be run with `npm run test:blackbox`. +By default, the BlackBox tests are not run with the regular `npm run test`, but can be run with `npm run test:blackbox`. Or run individual BlackBox tests you can run the commands `npm run test:blackbox:${language}` where language is one of `csharp`, `go`, `java`, `javascript`, `python`, `rust`, `typescript`, etc. + +To run the BlackBox tests through Docker, run the command `npm run docker:test:blackbox`. diff --git a/test/blackbox/blackbox-csharp.spec.ts b/test/blackbox/blackbox-csharp.spec.ts new file mode 100644 index 0000000000..e1664dfaa6 --- /dev/null +++ b/test/blackbox/blackbox-csharp.spec.ts @@ -0,0 +1,54 @@ +/** + * Blackbox tests are the final line of defence, that takes different real-life example documents and generate their corresponding models in all supported languages. + * + * For those languages where it is possible, the models are compiled/transpiled to ensure there are no syntax errors in generated models. + * + */ + +import * as path from 'path'; +import * as fs from 'fs'; +import { CSharpFileGenerator, InputMetaModel } from '../../src'; +import { execCommand } from './utils/Utils'; +import filesToTest from './BlackBoxTestFiles'; +describe.each(filesToTest)( + 'Should be able to generate with inputs', + ({ file, outputDirectory }) => { + jest.setTimeout(1000000); + const fileToGenerateFor = path.resolve(__dirname, file); + const outputDirectoryPath = path.resolve( + __dirname, + outputDirectory, + 'csharp' + ); + let models: InputMetaModel; + beforeAll(async () => { + if (fs.existsSync(outputDirectoryPath)) { + fs.rmSync(outputDirectoryPath, { recursive: true }); + } + const inputFileContent = await fs.promises.readFile(fileToGenerateFor); + const generator = new CSharpFileGenerator(); + const input = JSON.parse(String(inputFileContent)); + models = await generator.process(input); + }); + describe(file, () => { + describe('should be able to generate and compile C#', () => { + test('class and enums', async () => { + const generator = new CSharpFileGenerator(); + + const generatedModels = await generator.generateToFiles( + models, + outputDirectoryPath, + { namespace: 'TestNamespace' } + ); + expect(generatedModels).not.toHaveLength(0); + + const compileCommand = `csc /target:library /out:${path.resolve( + outputDirectoryPath, + './compiled.dll' + )} ${path.resolve(outputDirectoryPath, '*.cs')}`; + await execCommand(compileCommand); + }); + }); + }); + } +); diff --git a/test/blackbox/blackbox-go.spec.ts b/test/blackbox/blackbox-go.spec.ts new file mode 100644 index 0000000000..a5d8b92ea9 --- /dev/null +++ b/test/blackbox/blackbox-go.spec.ts @@ -0,0 +1,52 @@ +/** + * Blackbox tests are the final line of defence, that takes different real-life example documents and generate their corresponding models in all supported languages. + * + * For those languages where it is possible, the models are compiled/transpiled to ensure there are no syntax errors in generated models. + * + */ + +import * as path from 'path'; +import * as fs from 'fs'; +import { GoFileGenerator, InputMetaModel, InputProcessor } from '../../src'; +import { execCommand } from './utils/Utils'; +import filesToTest from './BlackBoxTestFiles'; + +describe.each(filesToTest)( + 'Should be able to generate with inputs', + ({ file, outputDirectory }) => { + jest.setTimeout(1000000); + const fileToGenerateFor = path.resolve(__dirname, file); + const outputDirectoryPath = path.resolve(__dirname, outputDirectory, 'go'); + let models: InputMetaModel; + beforeAll(async () => { + if (fs.existsSync(outputDirectoryPath)) { + fs.rmSync(outputDirectoryPath, { recursive: true }); + } + const inputFileContent = await fs.promises.readFile(fileToGenerateFor); + const processor = new InputProcessor(); + const input = JSON.parse(String(inputFileContent)); + models = await processor.process(input); + }); + describe(file, () => { + describe('should be able to generate Go', () => { + test('struct', async () => { + const generator = new GoFileGenerator(); + const renderOutputPath = path.resolve( + outputDirectoryPath, + './struct/' + ); + + const generatedModels = await generator.generateToFiles( + models, + renderOutputPath, + { packageName: 'test_package_name' } + ); + expect(generatedModels).not.toHaveLength(0); + + const compileCommand = `gofmt ${renderOutputPath}`; + await execCommand(compileCommand); + }); + }); + }); + } +); diff --git a/test/blackbox/blackbox-java.spec.ts b/test/blackbox/blackbox-java.spec.ts new file mode 100644 index 0000000000..4410c95e30 --- /dev/null +++ b/test/blackbox/blackbox-java.spec.ts @@ -0,0 +1,84 @@ +/** + * Blackbox tests are the final line of defence, that takes different real-life example documents and generate their corresponding models in all supported languages. + * + * For those languages where it is possible, the models are compiled/transpiled to ensure there are no syntax errors in generated models. + * + */ + +import * as path from 'path'; +import * as fs from 'fs'; +import { + InputMetaModel, + InputProcessor, + JavaFileGenerator, + JAVA_COMMON_PRESET +} from '../../src'; +import { execCommand } from './utils/Utils'; +import filesToTest from './BlackBoxTestFiles'; + +describe.each(filesToTest)( + 'Should be able to generate with inputs', + ({ file, outputDirectory }) => { + jest.setTimeout(1000000); + const fileToGenerateFor = path.resolve(__dirname, file); + const outputDirectoryPath = path.resolve( + __dirname, + outputDirectory, + 'java' + ); + let models: InputMetaModel; + beforeAll(async () => { + if (fs.existsSync(outputDirectoryPath)) { + fs.rmSync(outputDirectoryPath, { recursive: true }); + } + const inputFileContent = await fs.promises.readFile(fileToGenerateFor); + const processor = new InputProcessor(); + const input = JSON.parse(String(inputFileContent)); + models = await processor.process(input); + }); + describe(file, () => { + const javaGeneratorOptions = [ + { + generatorOption: {}, + description: 'default generator', + renderOutputPath: path.resolve(outputDirectoryPath, './class/default') + }, + { + generatorOption: { + presets: [JAVA_COMMON_PRESET] + }, + description: 'all common presets', + renderOutputPath: path.resolve( + outputDirectoryPath, + './class/commonpreset' + ) + } + ]; + describe.each(javaGeneratorOptions)( + 'should be able to generate and compile Java', + ({ generatorOption, renderOutputPath }) => { + test('class and enums', async () => { + const generator = new JavaFileGenerator(generatorOption); + const dependencyPath = path.resolve( + __dirname, + './dependencies/java/*' + ); + + const generatedModels = await generator.generateToFiles( + models, + renderOutputPath, + { packageName: 'TestPackageName' } + ); + expect(generatedModels).not.toHaveLength(0); + + const compileCommand = `javac -cp ${dependencyPath} ${path.resolve( + renderOutputPath, + '*.java' + )}`; + await execCommand(compileCommand); + }); + } + ); + }); + } +); diff --git a/test/blackbox/blackbox-javascript.spec.ts b/test/blackbox/blackbox-javascript.spec.ts new file mode 100644 index 0000000000..bd001bafd5 --- /dev/null +++ b/test/blackbox/blackbox-javascript.spec.ts @@ -0,0 +1,62 @@ +/** + * Blackbox tests are the final line of defence, that takes different real-life example documents and generate their corresponding models in all supported languages. + * + * For those languages where it is possible, the models are compiled/transpiled to ensure there are no syntax errors in generated models. + * + */ + +import * as path from 'path'; +import * as fs from 'fs'; +import { + InputMetaModel, + InputProcessor, + JavaScriptFileGenerator +} from '../../src'; +import { execCommand } from './utils/Utils'; +import filesToTest from './BlackBoxTestFiles'; + +describe.each(filesToTest)( + 'Should be able to generate with inputs', + ({ file, outputDirectory }) => { + jest.setTimeout(1000000); + const fileToGenerateFor = path.resolve(__dirname, file); + const outputDirectoryPath = path.resolve(__dirname, outputDirectory, 'js'); + let models: InputMetaModel; + beforeAll(async () => { + if (fs.existsSync(outputDirectoryPath)) { + fs.rmSync(outputDirectoryPath, { recursive: true }); + } + const inputFileContent = await fs.promises.readFile(fileToGenerateFor); + const processor = new InputProcessor(); + const input = JSON.parse(String(inputFileContent)); + models = await processor.process(input); + }); + describe(file, () => { + describe('should be able to generate JS', () => { + test('class', async () => { + const generator = new JavaScriptFileGenerator({ + moduleSystem: 'CJS' + }); + const renderOutputPath = path.resolve(outputDirectoryPath, './class'); + + const generatedModels = await generator.generateToFiles( + models, + renderOutputPath, + {}, + true + ); + expect(generatedModels).not.toHaveLength(0); + + const files = fs.readdirSync(renderOutputPath); + for (const file of files) { + const transpileAndRunCommand = `node --check ${path.resolve( + renderOutputPath, + file + )}`; + await execCommand(transpileAndRunCommand); + } + }); + }); + }); + } +); diff --git a/test/blackbox/blackbox-kotlin.spec.ts b/test/blackbox/blackbox-kotlin.spec.ts new file mode 100644 index 0000000000..92a37a5ca3 --- /dev/null +++ b/test/blackbox/blackbox-kotlin.spec.ts @@ -0,0 +1,103 @@ +/** + * Blackbox tests are the final line of defence, that takes different real-life example documents and generate their corresponding models in all supported languages. + * + * For those languages where it is possible, the models are compiled/transpiled to ensure there are no syntax errors in generated models. + * + */ + +import * as path from 'path'; +import * as fs from 'fs'; +import { + InputMetaModel, + InputProcessor, + KotlinFileGenerator, + KOTLIN_DEFAULT_PRESET, + JAVA_COMMON_PRESET, + KOTLIN_CONSTRAINTS_PRESET +} from '../../src'; +import { execCommand } from './utils/Utils'; +import filesToTest from './BlackBoxTestFiles'; + +async function generate(fileToGenerateFor: string): Promise { + const inputFileContent = await fs.promises.readFile(fileToGenerateFor); + const processor = new InputProcessor(); + const input = JSON.parse(String(inputFileContent)); + return processor.process(input); +} + +function deleteDirectoryIfExists(directory: string) { + if (fs.existsSync(directory)) { + fs.rmSync(directory, { recursive: true }); + } +} + +const isWindows = process.platform === 'win32'; +const describeIf = (condition: boolean) => + condition ? describe : describe.skip; + +// Windows environment has a weird setup, where it is using Kotlin Native instead of Kotlin JVM as it's compiler +// (See https://github.com/asyncapi/modelina/issues/1080) +describeIf(!isWindows).each(filesToTest)( + 'Should be able to generate with inputs', + ({ file, outputDirectory }) => { + jest.setTimeout(1000000); + const fileToGenerateFor = path.resolve(__dirname, file); + const outputDirectoryPath = path.resolve( + __dirname, + outputDirectory, + 'kotlin' + ); + + let models: InputMetaModel; + beforeAll(async () => { + deleteDirectoryIfExists(outputDirectoryPath); + models = await generate(fileToGenerateFor); + }); + + describe(file, () => { + const kotlinGeneratorOptions = [ + { + generatorOption: {}, + description: 'default generator', + renderOutputPath: path.resolve(outputDirectoryPath, './class/default') + }, + { + generatorOption: { + presets: [KOTLIN_CONSTRAINTS_PRESET] + }, + description: 'constraints preset', + renderOutputPath: path.resolve( + outputDirectoryPath, + './class/constraints' + ) + } + ]; + + describe.each(kotlinGeneratorOptions)( + 'should be able to generate and compile Kotlin', + ({ generatorOption, renderOutputPath }) => { + test('class and enums', async () => { + const generator = new KotlinFileGenerator(generatorOption); + const dependencyPath = path.resolve( + __dirname, + './dependencies/kotlin/*' + ); + + const generatedModels = await generator.generateToFiles( + models, + renderOutputPath, + { packageName: 'main' } + ); + expect(generatedModels).not.toHaveLength(0); + + const compileCommand = `kotlinc ${path.resolve( + renderOutputPath, + '*.kt' + )} -cp ${dependencyPath} -d ${renderOutputPath}`; + await execCommand(compileCommand); + }); + } + ); + }); + } +); diff --git a/test/blackbox/blackbox-python.spec.ts b/test/blackbox/blackbox-python.spec.ts new file mode 100644 index 0000000000..054288fa80 --- /dev/null +++ b/test/blackbox/blackbox-python.spec.ts @@ -0,0 +1,62 @@ +/** + * Blackbox tests are the final line of defence, that takes different real-life example documents and generate their corresponding models in all supported languages. + * + * For those languages where it is possible, the models are compiled/transpiled to ensure there are no syntax errors in generated models. + * + */ + +import * as path from 'path'; +import * as fs from 'fs'; +import { + InputMetaModel, + InputProcessor, + PythonFileGenerator, + PythonRenderCompleteModelOptions +} from '../../src'; +import { execCommand } from './utils/Utils'; +import filesToTest from './BlackBoxTestFiles'; + +describe.each(filesToTest)( + 'Should be able to generate with inputs', + ({ file, outputDirectory }) => { + jest.setTimeout(1000000); + const fileToGenerateFor = path.resolve(__dirname, file); + const outputDirectoryPath = path.resolve( + __dirname, + outputDirectory, + 'python' + ); + let models: InputMetaModel; + beforeAll(async () => { + if (fs.existsSync(outputDirectoryPath)) { + fs.rmSync(outputDirectoryPath, { recursive: true }); + } + const inputFileContent = await fs.promises.readFile(fileToGenerateFor); + const processor = new InputProcessor(); + const input = JSON.parse(String(inputFileContent)); + models = await processor.process(input); + }); + describe(file, () => { + describe('should be able to generate Python', () => { + test('class and enums', async () => { + const generator = new PythonFileGenerator(); + const renderOutputPath = path.resolve( + outputDirectoryPath, + './class/' + ); + const options = {} as PythonRenderCompleteModelOptions; + const generatedModels = await generator.generateToFiles( + models, + renderOutputPath, + options + ); + expect(generatedModels).not.toHaveLength(0); + + const compileCommand = `python -m compileall -f ${renderOutputPath}`; + await execCommand(compileCommand); + expect(generatedModels).not.toHaveLength(0); + }); + }); + }); + } +); diff --git a/test/blackbox/blackbox-rust.spec.ts b/test/blackbox/blackbox-rust.spec.ts new file mode 100644 index 0000000000..af32364f6d --- /dev/null +++ b/test/blackbox/blackbox-rust.spec.ts @@ -0,0 +1,86 @@ +/** + * Blackbox tests are the final line of defence, that takes different real-life example documents and generate their corresponding models in all supported languages. + * + * For those languages where it is possible, the models are compiled/transpiled to ensure there are no syntax errors in generated models. + * + */ + +import * as path from 'path'; +import * as fs from 'fs'; +import { + defaultRustRenderCompleteModelOptions, + InputMetaModel, + InputProcessor, + RustFileGenerator, + RustPackageFeatures, + RustRenderCompleteModelOptions +} from '../../src'; +import { execCommand } from './utils/Utils'; +import filesToTest from './BlackBoxTestFiles'; + +describe.each(filesToTest)( + 'Should be able to generate with inputs', + ({ file, outputDirectory }) => { + jest.setTimeout(1000000); + const fileToGenerateFor = path.resolve(__dirname, file); + const outputDirectoryPath = path.resolve( + __dirname, + outputDirectory, + 'rust' + ); + let models: InputMetaModel; + beforeAll(async () => { + if (fs.existsSync(outputDirectoryPath)) { + fs.rmSync(outputDirectoryPath, { recursive: true }); + } + const inputFileContent = await fs.promises.readFile(fileToGenerateFor); + const processor = new InputProcessor(); + const input = JSON.parse(String(inputFileContent)); + models = await processor.process(input); + }); + describe(file, () => { + describe.skip('should be able to generate Rust', () => { + test('struct with serde_json', async () => { + const generator = new RustFileGenerator(); + const renderOutputPath = path.resolve( + outputDirectoryPath, + './struct' + ); + const cargoFile = path.resolve( + outputDirectoryPath, + './struct/Cargo.toml' + ); + const options = { + ...defaultRustRenderCompleteModelOptions, + supportFiles: true, // generate Cargo.toml and lib.rs + package: { + packageName: 'asyncapi-rs-example', + packageVersion: '1.0.0', + // set authors, homepage, repository, and license + authors: ['AsyncAPI Rust Champions'], + homepage: 'https://www.asyncapi.com/tools/modelina', + repository: 'https://github.com/asyncapi/modelina', + license: 'Apache-2.0', + description: 'Rust models generated by AsyncAPI Modelina', + // support 2018 editions and up + edition: '2018', + // enable serde_json + packageFeatures: [ + RustPackageFeatures.json + ] as RustPackageFeatures[] + } + } as RustRenderCompleteModelOptions; + const generatedModels = await generator.generateToPackage( + models, + renderOutputPath, + options + ); + expect(generatedModels).not.toHaveLength(0); + + const compileCommand = `cargo build --manifest-path=${cargoFile}`; + await execCommand(compileCommand, true); + }); + }); + }); + } +); diff --git a/test/blackbox/blackbox-typescript.spec.ts b/test/blackbox/blackbox-typescript.spec.ts new file mode 100644 index 0000000000..c50eeb1250 --- /dev/null +++ b/test/blackbox/blackbox-typescript.spec.ts @@ -0,0 +1,72 @@ +/** + * Blackbox tests are the final line of defence, that takes different real-life example documents and generate their corresponding models in all supported languages. + * + * For those languages where it is possible, the models are compiled/transpiled to ensure there are no syntax errors in generated models. + * + */ +import * as path from 'path'; +import * as fs from 'fs'; +import { + InputMetaModel, + InputProcessor, + TypeScriptFileGenerator +} from '../../src'; +import { execCommand } from './utils/Utils'; +import filesToTest from './BlackBoxTestFiles'; + +describe.each(filesToTest)( + 'Should be able to generate with inputs', + ({ file, outputDirectory }) => { + jest.setTimeout(1000000); + const fileToGenerateFor = path.resolve(__dirname, file); + const outputDirectoryPath = path.resolve(__dirname, outputDirectory, 'ts'); + let models: InputMetaModel; + beforeAll(async () => { + if (fs.existsSync(outputDirectoryPath)) { + fs.rmSync(outputDirectoryPath, { recursive: true }); + } + const inputFileContent = await fs.promises.readFile(fileToGenerateFor); + const processor = new InputProcessor(); + const input = JSON.parse(String(inputFileContent)); + models = await processor.process(input); + }); + describe(file, () => { + describe('should be able to generate and transpile TS', () => { + test('class and enums', async () => { + const generator = new TypeScriptFileGenerator({ modelType: 'class' }); + const renderOutputPath = path.resolve(outputDirectoryPath, './class'); + + const generatedModels = await generator.generateToFiles( + models, + renderOutputPath, + {} + ); + expect(generatedModels).not.toHaveLength(0); + + const transpileCommand = `tsc --downlevelIteration -t es5 --baseUrl ${renderOutputPath}`; + await execCommand(transpileCommand); + }); + + test('interface and enums', async () => { + const generator = new TypeScriptFileGenerator({ + modelType: 'interface' + }); + const renderOutputPath = path.resolve( + outputDirectoryPath, + './interface' + ); + + const generatedModels = await generator.generateToFiles( + models, + renderOutputPath, + {} + ); + expect(generatedModels).not.toHaveLength(0); + + const transpileCommand = `tsc --downlevelIteration -t es5 --baseUrl ${renderOutputPath}`; + await execCommand(transpileCommand); + }); + }); + }); + } +); diff --git a/test/blackbox/blackbox.spec.ts b/test/blackbox/blackbox.spec.ts deleted file mode 100644 index 505f910afd..0000000000 --- a/test/blackbox/blackbox.spec.ts +++ /dev/null @@ -1,215 +0,0 @@ -/** - * Blackbox tests are the final line of defence, that takes different real-life example documents and generate their corresponding models in all supported languages. - * - * For those languages where it is possible, the models are compiled/transpiled to ensure there are no syntax errors in generated models. - * - */ - -import * as path from 'path'; -import * as fs from 'fs'; -import { GoFileGenerator, CSharpFileGenerator, JavaFileGenerator, JAVA_COMMON_PRESET, TypeScriptFileGenerator, JavaScriptFileGenerator } from '../../src'; -import { execCommand, generateModels, renderModels, renderModelsToSeparateFiles } from './utils/Utils'; - -/** - * Read all the files in the folder, and return the appropriate Jest `each` entries. - * @param folder - */ -function readFilesInFolder(folder: string) { - const fullPath = path.resolve(__dirname, `./docs/${folder}`); - return fs.readdirSync(fullPath).map( - (file) => { - return { file: `./docs/${folder}/${file}`, outputDirectory: `./output/${folder}/${path.parse(file).name}`}; - } - ); -} -const OpenAPI3_0Files = readFilesInFolder('OpenAPI-3_0'); -const jsonSchemaDraft7Files = readFilesInFolder('JsonSchemaDraft-7'); -const jsonSchemaDraft6Files = readFilesInFolder('JsonSchemaDraft-6'); -const jsonSchemaDraft4Files = readFilesInFolder('JsonSchemaDraft-4'); -const AsyncAPIV2_0Files = readFilesInFolder('AsyncAPI-2_0'); -const AsyncAPIV2_1Files = readFilesInFolder('AsyncAPI-2_1'); -const AsyncAPIV2_2Files = readFilesInFolder('AsyncAPI-2_2'); -const AsyncAPIV2_3Files = readFilesInFolder('AsyncAPI-2_3'); -const AsyncAPIV2_4Files = readFilesInFolder('AsyncAPI-2_4'); - -const filesToTest = [ - ...OpenAPI3_0Files.filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/456 - return file !== './docs/OpenAPI-3_0/twilio-1_13.json'; - }).filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/452 - return file !== './docs/OpenAPI-3_0/postman-api.json'; - }), - ...AsyncAPIV2_0Files.filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/504 - return file !== './docs/AsyncAPI-2_0/dummy.json'; - }), - ...AsyncAPIV2_1Files.filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/504 - return file !== './docs/AsyncAPI-2_1/dummy.json'; - }), - ...AsyncAPIV2_2Files.filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/504 - return file !== './docs/AsyncAPI-2_2/dummy.json'; - }), - ...AsyncAPIV2_3Files.filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/504 - return file !== './docs/AsyncAPI-2_3/dummy.json'; - }), - ...AsyncAPIV2_4Files.filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/504 - return file !== './docs/AsyncAPI-2_4/dummy.json'; - }), - ...jsonSchemaDraft7Files.filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/388 - return file !== './docs/JsonSchemaDraft-7/draft-7-core.json'; - }).filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/390 - return file !== './docs/JsonSchemaDraft-7/graphql-code-generator.json'; - }), - ...jsonSchemaDraft4Files.filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/449 - return file !== './docs/JsonSchemaDraft-4/openapi-3.json'; - }).filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/389 - return file !== './docs/JsonSchemaDraft-4/jenkins-config.json'; - }).filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/450 - return file !== './docs/JsonSchemaDraft-4/circleci-config.json'; - }).filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/390 - return file !== './docs/JsonSchemaDraft-4/circleci-config.json'; - }).filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/452 - return file !== './docs/JsonSchemaDraft-4/chrome-manifest.json'; - }).filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/367 - return file !== './docs/JsonSchemaDraft-4/aws-cloudformation.json'; - }).filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/388 - return file !== './docs/JsonSchemaDraft-4/draft-4-core.json'; - }), - ...jsonSchemaDraft6Files.filter(({file}) => { - //Blocked by https://github.com/asyncapi/modelina/issues/453 - return file !== './docs/JsonSchemaDraft-6/fhir-full.json'; - }) -]; - -// eslint-disable-next-line no-console -console.log('This is gonna take some time, Stay Awhile and Listen'); -describe.each(filesToTest)('Should be able to generate with inputs', ({file, outputDirectory}) => { - jest.setTimeout(1000000); - const fileToGenerateFor = path.resolve(__dirname, file); - const outputDirectoryPath = path.resolve(__dirname, outputDirectory); - beforeAll(async () => { - if (fs.existsSync(outputDirectoryPath)) { - await fs.rmSync(outputDirectoryPath, {recursive: true}); - } - }); - describe(file, () => { - const javaGeneratorOptions = [ - { - generatorOption: { }, - description: 'default generator' - }, - { - generatorOption: { - presets: [ - JAVA_COMMON_PRESET - ] - }, - description: 'all common presets' - } - ]; - describe.each(javaGeneratorOptions)('should be able to generate and compile Java', ({generatorOption, description}) => { - test('class', async () => { - const generator = new JavaFileGenerator(generatorOption); - const inputFileContent = await fs.promises.readFile(fileToGenerateFor); - const input = JSON.parse(String(inputFileContent)); - const renderOutputPath = path.resolve(outputDirectoryPath, './java/class'); - const dependencyPath = path.resolve(__dirname, './dependencies/java/*'); - - const generatedModels = await generator.generateToFiles(input, renderOutputPath, {packageName: 'TestPackageName'}); - expect(generatedModels).not.toHaveLength(0); - - const compileCommand = `javac -cp ${dependencyPath} ${path.resolve(renderOutputPath, '*.java')}`; - await execCommand(compileCommand); - }); - }); - describe('should be able to generate and compile C#', () => { - test('class', async () => { - const generator = new CSharpFileGenerator(); - const inputFileContent = await fs.promises.readFile(fileToGenerateFor); - const input = JSON.parse(String(inputFileContent)); - const renderOutputPath = path.resolve(outputDirectoryPath, './csharp'); - - const generatedModels = await generator.generateToFiles(input, renderOutputPath, {namespace: 'TestNamespace'}); - expect(generatedModels).not.toHaveLength(0); - - const compileCommand = `csc /target:library /out:${path.resolve(renderOutputPath, './compiled.dll')} ${path.resolve(renderOutputPath, '*.cs')}`; - await execCommand(compileCommand); - }); - }); - - describe('should be able to generate and transpile TS', () => { - test('class', async () => { - const generator = new TypeScriptFileGenerator({modelType: 'class'}); - const inputFileContent = await fs.promises.readFile(fileToGenerateFor); - const input = JSON.parse(String(inputFileContent)); - const renderOutputPath = path.resolve(outputDirectoryPath, './ts/class'); - - const generatedModels = await generator.generateToFiles(input, renderOutputPath); - expect(generatedModels).not.toHaveLength(0); - - const transpileCommand = `tsc --downlevelIteration -t es5 --baseUrl ${renderOutputPath}`; - await execCommand(transpileCommand); - }); - - test('interface', async () => { - const generator = new TypeScriptFileGenerator({modelType: 'interface'}); - const inputFileContent = await fs.promises.readFile(fileToGenerateFor); - const input = JSON.parse(String(inputFileContent)); - const renderOutputPath = path.resolve(outputDirectoryPath, './ts/interface'); - - const generatedModels = await generator.generateToFiles(input, renderOutputPath); - expect(generatedModels).not.toHaveLength(0); - - const transpileCommand = `tsc --downlevelIteration -t es5 --baseUrl ${renderOutputPath}`; - await execCommand(transpileCommand); - }); - }); - - describe('should be able to generate JS', () => { - test('class', async () => { - const generator = new JavaScriptFileGenerator(); - const inputFileContent = await fs.promises.readFile(fileToGenerateFor); - const input = JSON.parse(String(inputFileContent)); - const renderOutputPath = path.resolve(outputDirectoryPath, './js/class'); - - const generatedModels = await generator.generateToFiles(input, renderOutputPath, {moduleSystem: 'CJS'}); - expect(generatedModels).not.toHaveLength(0); - - const files = fs.readdirSync(renderOutputPath); - for (const file of files) { - const transpileAndRunCommand = `node --check ${path.resolve(renderOutputPath, file)}`; - await execCommand(transpileAndRunCommand); - } - }); - }); - - describe('should be able to generate Go', () => { - test('struct', async () => { - const generator = new GoFileGenerator(); - const inputFileContent = await fs.promises.readFile(fileToGenerateFor); - const input = JSON.parse(String(inputFileContent)); - const renderOutputPath = path.resolve(outputDirectoryPath, './go/struct/'); - - const generatedModels = await generator.generateToFiles(input, renderOutputPath, {packageName: 'test_package_name'}); - expect(generatedModels).not.toHaveLength(0); - - const compileCommand = `gofmt ${renderOutputPath}`; - await execCommand(compileCommand); - }); - }); - }); -}); diff --git a/test/blackbox/dependencies/kotlin/validation-api-2.0.1.Final.jar b/test/blackbox/dependencies/kotlin/validation-api-2.0.1.Final.jar new file mode 100644 index 0000000000..2368e10a53 Binary files /dev/null and b/test/blackbox/dependencies/kotlin/validation-api-2.0.1.Final.jar differ diff --git a/test/blackbox/docs/AsyncAPI-2_5/streetlight_kafka.json b/test/blackbox/docs/AsyncAPI-2_5/streetlight_kafka.json new file mode 100644 index 0000000000..1b6b7fc719 --- /dev/null +++ b/test/blackbox/docs/AsyncAPI-2_5/streetlight_kafka.json @@ -0,0 +1,279 @@ +{ + "asyncapi": "2.5.0", + "info": { + "title": "Streetlights Kafka API", + "version": "1.0.0", + "description": "The Smartylighting Streetlights API allows you to remotely manage the city lights.\n\n### Check out its awesome features:\n\n* Turn a specific streetlight on/off 🌃\n* Dim a specific streetlight 😎\n* Receive real-time information about environmental lighting conditions 📈\n", + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0" + } + }, + "servers": { + "scram-connections": { + "url": "test.mykafkacluster.org:18092", + "protocol": "kafka-secure", + "description": "Test broker secured with scramSha256", + "security": [ + { + "saslScram": [] + } + ], + "tags": [ + { + "name": "env:test-scram", + "description": "This environment is meant for running internal tests through scramSha256" + }, + { + "name": "kind:remote", + "description": "This server is a remote server. Not exposed by the application" + }, + { + "name": "visibility:private", + "description": "This resource is private and only available to certain users" + } + ] + }, + "mtls-connections": { + "url": "test.mykafkacluster.org:28092", + "protocol": "kafka-secure", + "description": "Test broker secured with X509", + "security": [ + { + "certs": [] + } + ], + "tags": [ + { + "name": "env:test-mtls", + "description": "This environment is meant for running internal tests through mtls" + }, + { + "name": "kind:remote", + "description": "This server is a remote server. Not exposed by the application" + }, + { + "name": "visibility:private", + "description": "This resource is private and only available to certain users" + } + ] + } + }, + "defaultContentType": "application/json", + "channels": { + "smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured": { + "description": "The topic on which measured values may be produced and consumed.", + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "publish": { + "summary": "Inform about environmental lighting conditions of a particular streetlight.", + "operationId": "receiveLightMeasurement", + "traits": [ + { + "$ref": "#/components/operationTraits/kafka" + } + ], + "message": { + "$ref": "#/components/messages/lightMeasured" + } + } + }, + "smartylighting.streetlights.1.0.action.{streetlightId}.turn.on": { + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "subscribe": { + "operationId": "turnOn", + "traits": [ + { + "$ref": "#/components/operationTraits/kafka" + } + ], + "message": { + "$ref": "#/components/messages/turnOnOff" + } + } + }, + "smartylighting.streetlights.1.0.action.{streetlightId}.turn.off": { + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "subscribe": { + "operationId": "turnOff", + "traits": [ + { + "$ref": "#/components/operationTraits/kafka" + } + ], + "message": { + "$ref": "#/components/messages/turnOnOff" + } + } + }, + "smartylighting.streetlights.1.0.action.{streetlightId}.dim": { + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "subscribe": { + "operationId": "dimLight", + "traits": [ + { + "$ref": "#/components/operationTraits/kafka" + } + ], + "message": { + "$ref": "#/components/messages/dimLight" + } + } + } + }, + "components": { + "messages": { + "lightMeasured": { + "name": "lightMeasured", + "title": "Light measured", + "summary": "Inform about environmental lighting conditions of a particular streetlight.", + "contentType": "application/json", + "traits": [ + { + "$ref": "#/components/messageTraits/commonHeaders" + } + ], + "payload": { + "$ref": "#/components/schemas/lightMeasuredPayload" + } + }, + "turnOnOff": { + "name": "turnOnOff", + "title": "Turn on/off", + "summary": "Command a particular streetlight to turn the lights on or off.", + "traits": [ + { + "$ref": "#/components/messageTraits/commonHeaders" + } + ], + "payload": { + "$ref": "#/components/schemas/turnOnOffPayload" + } + }, + "dimLight": { + "name": "dimLight", + "title": "Dim light", + "summary": "Command a particular streetlight to dim the lights.", + "traits": [ + { + "$ref": "#/components/messageTraits/commonHeaders" + } + ], + "payload": { + "$ref": "#/components/schemas/dimLightPayload" + } + } + }, + "schemas": { + "lightMeasuredPayload": { + "type": "object", + "properties": { + "lumens": { + "type": "integer", + "minimum": 0, + "description": "Light intensity measured in lumens." + }, + "sentAt": { + "$ref": "#/components/schemas/sentAt" + } + } + }, + "turnOnOffPayload": { + "type": "object", + "properties": { + "command": { + "type": "string", + "enum": [ + "on", + "off" + ], + "description": "Whether to turn on or off the light." + }, + "sentAt": { + "$ref": "#/components/schemas/sentAt" + } + } + }, + "dimLightPayload": { + "type": "object", + "properties": { + "percentage": { + "type": "integer", + "description": "Percentage to which the light should be dimmed to.", + "minimum": 0, + "maximum": 100 + }, + "sentAt": { + "$ref": "#/components/schemas/sentAt" + } + } + }, + "sentAt": { + "type": "string", + "format": "date-time", + "description": "Date and time when the message was sent." + } + }, + "securitySchemes": { + "saslScram": { + "type": "scramSha256", + "description": "Provide your username and password for SASL/SCRAM authentication" + }, + "certs": { + "type": "X509", + "description": "Download the certificate files from service provider" + } + }, + "parameters": { + "streetlightId": { + "description": "The ID of the streetlight.", + "schema": { + "type": "string" + } + } + }, + "messageTraits": { + "commonHeaders": { + "headers": { + "type": "object", + "properties": { + "my-app-header": { + "type": "integer", + "minimum": 0, + "maximum": 100 + } + } + } + } + }, + "operationTraits": { + "kafka": { + "bindings": { + "kafka": { + "clientId": { + "type": "string", + "enum": [ + "my-app-id" + ] + } + } + } + } + } + } +} \ No newline at end of file diff --git a/test/blackbox/docs/JsonSchemaDraft-6/draft-6-core.json b/test/blackbox/docs/JsonSchemaDraft-6/draft-6-core.json new file mode 100644 index 0000000000..bd3e763bc2 --- /dev/null +++ b/test/blackbox/docs/JsonSchemaDraft-6/draft-6-core.json @@ -0,0 +1,155 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "http://json-schema.org/draft-06/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "examples": { + "type": "array", + "items": {} + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": {} + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": {}, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": {} +} diff --git a/test/blackbox/utils/Utils.ts b/test/blackbox/utils/Utils.ts index b3ee69cd78..5a77f2d7cf 100644 --- a/test/blackbox/utils/Utils.ts +++ b/test/blackbox/utils/Utils.ts @@ -1,52 +1,70 @@ /* eslint-disable no-undef */ /* eslint-disable security/detect-non-literal-fs-filename */ -import {promises as fs} from 'fs'; +import { promises as fs } from 'fs'; import * as path from 'path'; import { AbstractGenerator, FormatHelpers, OutputModel } from '../../../src'; -import {promisify} from 'util'; -import {exec} from 'child_process'; +import { promisify } from 'util'; +import { exec } from 'child_process'; const promiseExec = promisify(exec); /** * Exec a command and if any errors occur reject the promise. - * - * @param absolutePathToFile - * @param generator + * + * @param absolutePathToFile + * @param generator */ -export async function generateModels(absolutePathToFile: string, generator: AbstractGenerator): Promise { +export async function generateModels( + absolutePathToFile: string, + generator: AbstractGenerator +): Promise { const inputFileContent = await fs.readFile(absolutePathToFile); const input = JSON.parse(String(inputFileContent)); return generator.generate(input); } /** * Execute a command and if any errors occur reject the promise. - * - * @param command + * + * @param command */ -export async function execCommand(command: string) : Promise { +export async function execCommand( + command: string, + allowStdError = false +): Promise { try { const { stderr } = await promiseExec(command); if (stderr !== '') { - return Promise.reject(stderr); + if (!allowStdError) { + return Promise.reject(stderr); + } + // eslint-disable-next-line no-console + console.error(stderr); } return Promise.resolve(); } catch (e: any) { - const wrapperError = new Error(`Error: ${e.stack}; Stdout: ${e.stdout}`); - return Promise.reject(wrapperError); + return Promise.reject(`${e.stack}; Stdout: ${e.stdout}`); } } /** * Render all models to separate files in the same directory - * + * * @param generatedModels to write to file * @param outputPath absolute path to output directory */ -export async function renderModelsToSeparateFiles(generatedModels: OutputModel[], outputPath: string, extension: string): Promise { +export async function renderModelsToSeparateFiles( + generatedModels: OutputModel[], + outputPath: string, + extension: string +): Promise { await fs.rm(outputPath, { recursive: true, force: true }); await fs.mkdir(outputPath, { recursive: true }); for (const outputModel of generatedModels) { - const outputFilePath = path.resolve(outputPath, `${FormatHelpers.toPascalCase(outputModel.model.$id || 'undefined')}.${extension}`); + const outputFilePath = path.resolve( + outputPath, + `${FormatHelpers.toPascalCase( + outputModel.model.name || 'undefined' + )}.${extension}` + ); const outputContent = ` ${outputModel.dependencies.join('\n')} ${outputModel.result} @@ -57,11 +75,15 @@ ${outputModel.result} /** * Render all models to a single file - * + * * @param models to write to file * @param outputPath path to output */ -export async function renderModels(generatedModels: OutputModel[], outputPath: string, headers?: string[]): Promise { +export async function renderModels( + generatedModels: OutputModel[], + outputPath: string, + headers?: string[] +): Promise { const outputDir = path.resolve(__dirname, path.dirname(outputPath)); await fs.rm(outputDir, { recursive: true, force: true }); await fs.mkdir(outputDir, { recursive: true }); @@ -69,7 +91,9 @@ export async function renderModels(generatedModels: OutputModel[], outputPath: s return model.result; }); - const stringOutput = headers ? `${headers.join('\n')}\n\n${output.join('\n')}` : output.join('\n'); + const stringOutput = headers + ? `${headers.join('\n')}\n\n${output.join('\n')}` + : output.join('\n'); const outputFilePath = path.resolve(__dirname, outputPath); await fs.writeFile(outputFilePath, stringOutput); } diff --git a/test/generators/AbstractGenerator.spec.ts b/test/generators/AbstractGenerator.spec.ts index 9aa97c3f17..379139e342 100644 --- a/test/generators/AbstractGenerator.spec.ts +++ b/test/generators/AbstractGenerator.spec.ts @@ -1,26 +1,5 @@ -import { AbstractGenerator, FileGenerator } from '../../src/generators'; -import { IndentationTypes } from '../../src/helpers'; -import { CommonInputModel, CommonModel, OutputModel, RenderOutput } from '../../src/models'; - -export const testOptions = { - indentation: { - type: IndentationTypes.SPACES, - size: 2, - } -}; -export class TestGenerator extends AbstractGenerator { - constructor() { - super('TestGenerator', testOptions); - } - - render(model: CommonModel, inputModel: CommonInputModel): Promise { - return Promise.resolve(RenderOutput.toRenderOutput({result: model.$id || 'rendered content', renderedName: 'TestName'})); - } - - renderCompleteModel(model: CommonModel, inputModel: CommonInputModel, options: any): Promise { - return Promise.resolve(RenderOutput.toRenderOutput({result: 'rendered complete content', renderedName: 'TestName'})); - } -} +import { InputMetaModel, CommonModel, AnyModel } from '../../src/models'; +import { TestGenerator } from '../TestUtils/TestGenerator'; describe('AbstractGenerator', () => { let generator: TestGenerator; beforeEach(() => { @@ -28,61 +7,67 @@ describe('AbstractGenerator', () => { }); test('generate() should return OutputModels', async () => { - const doc: any = { $id: 'test' }; - const outputModels = await generator.generate(doc); + const cim = new InputMetaModel(); + const model = new AnyModel('test', undefined); + cim.models[model.name] = model; + const outputModels = await generator.generate(cim); expect(outputModels[0].result).toEqual('test'); expect(outputModels[0].modelName).toEqual('TestName'); }); test('generateCompleteModels() should return OutputModels', async () => { - const doc: any = { $id: 'test' }; - const outputModels = await generator.generateCompleteModels(doc, {}); + const cim = new InputMetaModel(); + const model = new AnyModel('test', undefined); + cim.models[model.name] = model; + const outputModels = await generator.generateCompleteModels(cim, {}); - expect(outputModels[0].result).toEqual('rendered complete content'); + expect(outputModels[0].result).toEqual('test'); expect(outputModels[0].modelName).toEqual('TestName'); }); - test('generate() should process CommonInputModel instance', async () => { - const cim = new CommonInputModel(); - const model = new CommonModel(); - model.$id = 'test'; - cim.models[model.$id] = model; + test('generate() should process InputMetaModel instance', async () => { + const cim = new InputMetaModel(); + const model = new AnyModel('test', undefined); + cim.models[model.name] = model; const outputModels = await generator.generate(cim); expect(outputModels[0].result).toEqual('test'); expect(outputModels[0].modelName).toEqual('TestName'); }); - test('generateCompleteModels() should process CommonInputModel instance', async () => { - const cim = new CommonInputModel(); - const model = new CommonModel(); - model.$id = 'test'; - cim.models[model.$id] = model; + test('generateCompleteModels() should process InputMetaModel instance', async () => { + const cim = new InputMetaModel(); + const model = new AnyModel('test', undefined); + cim.models[model.name] = model; const outputModels = await generator.generateCompleteModels(cim, {}); - expect(outputModels[0].result).toEqual('rendered complete content'); + expect(outputModels[0].result).toEqual('test'); expect(outputModels[0].modelName).toEqual('TestName'); }); - test('should `process` function return CommonInputModel', async () => { - const doc: any = { $id: 'test' }; + test('should `process` function return InputMetaModel', async () => { + const doc: any = { type: 'string', $id: 'test' }; const commonInputModel = await generator.process(doc); const keys = Object.keys(commonInputModel.models); - expect(commonInputModel).toBeInstanceOf(CommonInputModel); + expect(commonInputModel).toBeInstanceOf(InputMetaModel); expect(commonInputModel.models).toBeDefined(); expect(keys).toHaveLength(1); expect(commonInputModel.models[keys[0]].originalInput).toEqual({ $id: 'test', - 'x-modelgen-inferred-name': 'root', + type: 'string', + 'x-modelgen-inferred-name': 'root' }); }); test('should `render` function return renderer model', async () => { - const doc: any = { $id: 'SomeModel' }; + const doc: any = { type: 'string', $id: 'SomeModel' }; const commonInputModel = await generator.process(doc); const keys = Object.keys(commonInputModel.models); - const renderedContent = await generator.render(commonInputModel.models[keys[0]], commonInputModel); + const renderedContent = await generator.render( + commonInputModel.models[keys[0]], + commonInputModel + ); expect(renderedContent.result).toEqual('SomeModel'); expect(renderedContent.renderedName).toEqual('TestName'); @@ -91,22 +76,9 @@ describe('AbstractGenerator', () => { describe('getPresets()', () => { test('getPresets()', () => { - class GeneratorWithPresets extends AbstractGenerator { - constructor() { - super('TestGenerator', {presets: [{preset: {test: 'test2'}, options: {}}]}); - } - - render(model: CommonModel, inputModel: CommonInputModel): any { return; } - - testGetPresets(string: string) { - return this.getPresets(string); - } - - renderCompleteModel(model: CommonModel, inputModel: CommonInputModel, options: any): Promise { - throw new Error('Method not implemented.'); - } - } - const newGenerator = new GeneratorWithPresets(); + const newGenerator = new TestGenerator({ + presets: [{ preset: { test: 'test2' }, options: {} }] + }); expect(newGenerator.testGetPresets('test')).toEqual([['test2', {}]]); }); }); diff --git a/test/generators/AbstractRenderer.spec.ts b/test/generators/AbstractRenderer.spec.ts index 07478336ce..8421cfbbfc 100644 --- a/test/generators/AbstractRenderer.spec.ts +++ b/test/generators/AbstractRenderer.spec.ts @@ -1,18 +1,7 @@ -import { AbstractRenderer } from '../../src/generators'; import { IndentationTypes } from '../../src/helpers'; -import { CommonInputModel, CommonModel, RenderOutput } from '../../src/models'; -import { testOptions, TestGenerator } from './AbstractGenerator.spec'; +import { TestRenderer } from '../TestUtils/TestRenderers'; describe('AbstractRenderer', () => { - class TestRenderer extends AbstractRenderer { - constructor(presets = []) { - super(testOptions, new TestGenerator(), presets, new CommonModel(), new CommonInputModel()); - } - render(): Promise { - return Promise.resolve(RenderOutput.toRenderOutput({result: '', renderedName: ''})); - } - } - let renderer: TestRenderer; beforeEach(() => { renderer = new TestRenderer(); @@ -28,26 +17,6 @@ describe('AbstractRenderer', () => { expect(content).toEqual('Test1\nTest2'); }); - test('can use generator inside renderer', async () => { - const generator = renderer.generator; - const doc: any = { $id: 'test' }; - const outputModels = await generator.generate(doc); - - expect(outputModels[0].result).toEqual('test'); - }); - - describe('addDependency()', () => { - test('should add dependency', () => { - renderer.addDependency('test'); - expect(renderer.dependencies).toEqual(['test']); - }); - test('should not add duplicate dependency', () => { - renderer.addDependency('test'); - renderer.addDependency('test'); - expect(renderer.dependencies).toEqual(['test']); - }); - }); - describe('indent()', () => { test('should render text with indentation', () => { const content = renderer.indent('Test'); @@ -63,20 +32,26 @@ describe('AbstractRenderer', () => { test('should call correct preset', async () => { const presetCallback = jest.fn(); const tempRenderer = new TestRenderer([ - [{ - self: presetCallback as never - } as never, {} as never] as never + [ + { + self: presetCallback as never + } as never, + {} as never + ] as never ]); await tempRenderer.runSelfPreset(); expect(presetCallback).toHaveBeenCalled(); }); test('should not call incorrect preset', async () => { const presetCallback = jest.fn(); - + const tempRenderer = new TestRenderer([ - [{ - none_self: presetCallback as never - } as never, {} as never] as never + [ + { + none_self: presetCallback as never + } as never, + {} as never + ] as never ]); await tempRenderer.runSelfPreset(); expect(presetCallback).not.toHaveBeenCalled(); @@ -86,32 +61,42 @@ describe('AbstractRenderer', () => { test('should call correct preset', async () => { const presetCallback = jest.fn(); const tempRenderer = new TestRenderer([ - [{ - additionalContent: presetCallback as never - } as never, {} as never] as never + [ + { + additionalContent: presetCallback as never + } as never, + {} as never + ] as never ]); await tempRenderer.runAdditionalContentPreset(); expect(presetCallback).toHaveBeenCalled(); }); test('should not call incorrect preset', async () => { const presetCallback = jest.fn(); - + const tempRenderer = new TestRenderer([ - [{ - none_additionalContent: presetCallback as never - } as never, {} as never] as never + [ + { + none_additionalContent: presetCallback as never + } as never, + {} as never + ] as never ]); await tempRenderer.runAdditionalContentPreset(); expect(presetCallback).not.toHaveBeenCalled(); }); }); + describe('runPreset()', () => { test('should use string', async () => { const preset1Callback = jest.fn(); const tempRenderer = new TestRenderer([ - [{ - test: preset1Callback.mockReturnValue('value') as never - } as never, {} as never] as never, + [ + { + test: preset1Callback.mockReturnValue('value') as never + } as never, + {} as never + ] as never ]); const content = await tempRenderer.runPreset('test'); expect(content).toEqual('value'); @@ -120,9 +105,12 @@ describe('AbstractRenderer', () => { test('should not render non-string values', async () => { const preset1Callback = jest.fn(); const tempRenderer = new TestRenderer([ - [{ - test: preset1Callback.mockReturnValue(213) as never - } as never, {} as never] as never, + [ + { + test: preset1Callback.mockReturnValue(213) as never + } as never, + {} as never + ] as never ]); const content = await tempRenderer.runPreset('test'); expect(content).toEqual(''); @@ -132,28 +120,41 @@ describe('AbstractRenderer', () => { const preset1Callback = jest.fn(); const preset2Callback = jest.fn(); const tempRenderer = new TestRenderer([ - [{ - test: preset1Callback.mockReturnValue('value') as never - } as never, {} as never] as never, - [{ - test: preset2Callback.mockReturnValue('value2') as never - } as never, {} as never] as never, + [ + { + test: preset1Callback.mockReturnValue('value') as never + } as never, + {} as never + ] as never, + [ + { + test: preset2Callback.mockReturnValue('value2') as never + } as never, + {} as never + ] as never ]); const content = await tempRenderer.runPreset('test'); expect(content).toEqual('value2'); expect(preset1Callback).toHaveBeenCalled(); expect(preset2Callback).toHaveBeenCalled(); }); + test('should not use previous preset if undefined returned', async () => { const preset1Callback = jest.fn(); const preset2Callback = jest.fn(); const tempRenderer = new TestRenderer([ - [{ - test: preset1Callback.mockReturnValue('value') as never - } as never, {} as never] as never, - [{ - test: preset2Callback.mockReturnValue(undefined) as never - } as never, {} as never] as never + [ + { + test: preset1Callback.mockReturnValue('value') as never + } as never, + {} as never + ] as never, + [ + { + test: preset2Callback.mockReturnValue(undefined) as never + } as never, + {} as never + ] as never ]); const content = await tempRenderer.runPreset('test'); expect(content).toEqual(''); @@ -164,12 +165,18 @@ describe('AbstractRenderer', () => { const preset1Callback = jest.fn(); const preset2Callback = jest.fn(); const tempRenderer = new TestRenderer([ - [{ - test: preset1Callback.mockReturnValue('value') as never - } as never, {} as never] as never, - [{ - test: preset2Callback.mockReturnValue(null) as never - } as never, {} as never] as never + [ + { + test: preset1Callback.mockReturnValue('value') as never + } as never, + {} as never + ] as never, + [ + { + test: preset2Callback.mockReturnValue(null) as never + } as never, + {} as never + ] as never ]); const content = await tempRenderer.runPreset('test'); expect(content).toEqual(''); @@ -180,12 +187,18 @@ describe('AbstractRenderer', () => { const preset1Callback = jest.fn(); const preset2Callback = jest.fn(); const tempRenderer = new TestRenderer([ - [{ - test: preset1Callback.mockReturnValue('value') as never - } as never, {} as never] as never, - [{ - test: preset2Callback.mockReturnValue('') as never - } as never, {} as never] as never + [ + { + test: preset1Callback.mockReturnValue('value') as never + } as never, + {} as never + ] as never, + [ + { + test: preset2Callback.mockReturnValue('') as never + } as never, + {} as never + ] as never ]); const content = await tempRenderer.runPreset('test'); expect(content).toEqual(''); diff --git a/test/generators/FileGenerators.spec.ts b/test/generators/FileGenerators.spec.ts new file mode 100644 index 0000000000..21df234104 --- /dev/null +++ b/test/generators/FileGenerators.spec.ts @@ -0,0 +1,175 @@ +import { + FileHelpers, + DartFileGenerator, + OutputModel, + ConstrainedAnyModel, + InputMetaModel, + GoFileGenerator, + JavaFileGenerator, + JavaScriptFileGenerator, + TypeScriptFileGenerator, + CSharpFileGenerator, + RustFileGenerator, + PythonFileGenerator, + KotlinFileGenerator +} from '../../src'; +import * as path from 'path'; + +const generatorsToTest = [ + { + generator: new GoFileGenerator(), + generatorOptions: { packageName: 'some_package' }, + fileExtension: 'go' + }, + { + generator: new DartFileGenerator(), + generatorOptions: { packageName: 'SomePackage' }, + fileExtension: 'dart' + }, + { + generator: new JavaFileGenerator(), + generatorOptions: { packageName: 'SomePackage' }, + fileExtension: 'java' + }, + { + generator: new JavaScriptFileGenerator(), + generatorOptions: {}, + fileExtension: 'js' + }, + { + generator: new TypeScriptFileGenerator(), + generatorOptions: {}, + fileExtension: 'ts' + }, + { + generator: new CSharpFileGenerator(), + generatorOptions: { namespace: 'SomeNamespace' }, + fileExtension: 'cs' + }, + { + generator: new RustFileGenerator(), + generatorOptions: { namespace: 'SomeNamespace' }, + fileExtension: 'rs' + }, + { + generator: new PythonFileGenerator(), + generatorOptions: {}, + fileExtension: 'py' + }, + { + generator: new KotlinFileGenerator(), + generatorOptions: { packageName: 'SomePackage' }, + fileExtension: 'kt' + } +]; + +describe.each(generatorsToTest)( + 'generateToFiles', + ({ generator, generatorOptions, fileExtension }) => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + const doc = { + $id: 'CustomClass', + type: 'object', + additionalProperties: true, + properties: { + someProp: { type: 'string' }, + someEnum: { + $id: 'CustomEnum', + type: 'string', + enum: ['Texas', 'Alabama', 'California'] + } + } + }; + + test('should throw accurate error if file cannot be written', async () => { + const expectedError = new Error('write error'); + jest + .spyOn(FileHelpers, 'writerToFileSystem') + .mockRejectedValue(expectedError); + jest + .spyOn(generator, 'generateCompleteModels') + .mockResolvedValue([ + new OutputModel( + 'content', + new ConstrainedAnyModel('', undefined, ''), + 'test', + new InputMetaModel(), + [] + ) + ]); + + await expect( + generator.generateToFiles(doc, '/test/', generatorOptions as any) + ).rejects.toEqual(expectedError); + expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); + expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); + }); + test('should try and generate models to files', async () => { + const outputDir = './test'; + const expectedOutputDirPath = path.resolve(outputDir); + const expectedOutputFilePath = path.resolve( + `${outputDir}/test.${fileExtension}` + ); + const expectedWriteToFileParameters = [ + 'content', + expectedOutputFilePath, + false + ]; + jest + .spyOn(FileHelpers, 'writerToFileSystem') + .mockResolvedValue(undefined); + jest + .spyOn(generator, 'generateCompleteModels') + .mockResolvedValue([ + new OutputModel( + 'content', + new ConstrainedAnyModel('', undefined, ''), + 'test', + new InputMetaModel(), + [] + ) + ]); + + await generator.generateToFiles( + doc, + expectedOutputDirPath, + generatorOptions as any + ); + expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); + expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); + expect( + (FileHelpers.writerToFileSystem as jest.Mock).mock.calls[0] + ).toEqual(expectedWriteToFileParameters); + }); + test('should ignore models that have not been rendered', async () => { + const outputDir = './test'; + const expectedOutputDirPath = path.resolve(outputDir); + jest + .spyOn(FileHelpers, 'writerToFileSystem') + .mockResolvedValue(undefined); + jest + .spyOn(generator, 'generateCompleteModels') + .mockResolvedValue([ + new OutputModel( + '', + new ConstrainedAnyModel('', undefined, ''), + '', + new InputMetaModel(), + [] + ) + ]); + + const models = await generator.generateToFiles( + doc, + expectedOutputDirPath, + generatorOptions as any + ); + expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); + expect(models).toHaveLength(0); + expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(0); + }); + } +); diff --git a/test/generators/csharp/CSharpConstrainer.spec.ts b/test/generators/csharp/CSharpConstrainer.spec.ts new file mode 100644 index 0000000000..8c6dc7c9ba --- /dev/null +++ b/test/generators/csharp/CSharpConstrainer.spec.ts @@ -0,0 +1,299 @@ +import { CSharpDefaultTypeMapping } from '../../../src/generators/csharp/CSharpConstrainer'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedEnumValueModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedTupleValueModel, + ConstrainedUnionModel, + CSharpGenerator, + CSharpOptions +} from '../../../src'; +import { CSharpDependencyManager } from '../../../src/generators/csharp/CSharpDependencyManager'; + +describe('CSharpConstrainer', () => { + const dependencyManagerInstance = new CSharpDependencyManager( + CSharpGenerator.defaultOptions + ); + const defaultOptions = { + options: CSharpGenerator.defaultOptions, + dependencyManager: dependencyManagerInstance + }; + describe('ObjectModel', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedObjectModel('test', undefined, '', {}); + const type = CSharpDefaultTypeMapping.Object({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Reference', () => { + test('should render the constrained name as type', () => { + const refModel = new ConstrainedAnyModel('test', undefined, ''); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + '', + refModel + ); + const type = CSharpDefaultTypeMapping.Reference({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Any', () => { + test('should render type', () => { + const model = new ConstrainedAnyModel('test', undefined, ''); + const type = CSharpDefaultTypeMapping.Any({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('dynamic'); + }); + }); + describe('Float', () => { + test('should render type', () => { + const model = new ConstrainedFloatModel('test', undefined, ''); + const type = CSharpDefaultTypeMapping.Float({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('double'); + }); + }); + describe('Integer', () => { + test('should render type', () => { + const model = new ConstrainedIntegerModel('test', undefined, ''); + const type = CSharpDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('int'); + }); + }); + describe('String', () => { + test('should render type', () => { + const model = new ConstrainedStringModel('test', undefined, ''); + const type = CSharpDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('string'); + }); + }); + describe('Boolean', () => { + test('should render type', () => { + const model = new ConstrainedBooleanModel('test', undefined, ''); + const type = CSharpDefaultTypeMapping.Boolean({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('bool'); + }); + }); + + describe('Tuple', () => { + test('should render type', () => { + const tupleModel = new ConstrainedBooleanModel( + 'test', + undefined, + 'string' + ); + const tupleValueModel = new ConstrainedTupleValueModel(0, tupleModel); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel + ]); + const type = CSharpDefaultTypeMapping.Tuple({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('(string)'); + }); + test('should render multiple types', () => { + const tupleModel = new ConstrainedBooleanModel( + 'test', + undefined, + 'string' + ); + const tupleValueModel0 = new ConstrainedTupleValueModel(0, tupleModel); + const tupleValueModel1 = new ConstrainedTupleValueModel(1, tupleModel); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel0, + tupleValueModel1 + ]); + const type = CSharpDefaultTypeMapping.Tuple({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('(string, string)'); + }); + }); + + describe('Array', () => { + test('should render type', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const options: CSharpOptions = { + ...CSharpGenerator.defaultOptions, + collectionType: 'Array' + }; + const type = CSharpDefaultTypeMapping.Array({ + constrainedModel: model, + options, + dependencyManager: dependencyManagerInstance + }); + expect(type).toEqual('String[]'); + }); + test('should render array as a list', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const options: CSharpOptions = { + ...CSharpGenerator.defaultOptions, + collectionType: 'List' + }; + const type = CSharpDefaultTypeMapping.Array({ + constrainedModel: model, + options, + dependencyManager: dependencyManagerInstance + }); + expect(type).toEqual('IEnumerable'); + }); + }); + + describe('Enum', () => { + test('should render string enum values as String type', () => { + const enumValue = new ConstrainedEnumValueModel('test', 'string type'); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = CSharpDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('string'); + }); + test('should render boolean enum values as boolean type', () => { + const enumValue = new ConstrainedEnumValueModel('test', true); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = CSharpDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('bool'); + }); + test('should render generic number enum value with format ', () => { + const enumValue = new ConstrainedEnumValueModel('test', 123); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = CSharpDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('int'); + }); + test('should render object enum value as generic Object', () => { + const enumValue = new ConstrainedEnumValueModel('test', {}); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = CSharpDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('dynamic'); + }); + test('should render multiple value types as generic Object', () => { + const enumValue2 = new ConstrainedEnumValueModel('test', true); + const enumValue1 = new ConstrainedEnumValueModel('test', 'string type'); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue1, + enumValue2 + ]); + const type = CSharpDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('dynamic'); + }); + test('should render double and integer as dynamic type', () => { + const enumValue2 = new ConstrainedEnumValueModel('test', 123); + const enumValue1 = new ConstrainedEnumValueModel('test', 123.12); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue1, + enumValue2 + ]); + const type = CSharpDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('dynamic'); + }); + }); + + describe('Union', () => { + test('should render type', () => { + const model = new ConstrainedUnionModel('test', undefined, '', []); + const type = CSharpDefaultTypeMapping.Union({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('dynamic'); + }); + }); + + describe('Dictionary', () => { + test('should render type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'String'); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = CSharpDefaultTypeMapping.Dictionary({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Dictionary'); + }); + }); +}); diff --git a/test/generators/csharp/CSharpFileGenerator.spec.ts b/test/generators/csharp/CSharpFileGenerator.spec.ts deleted file mode 100644 index 181f3cb300..0000000000 --- a/test/generators/csharp/CSharpFileGenerator.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { CommonInputModel, CommonModel, FileHelpers, CSharpFileGenerator, OutputModel } from '../../../src'; -import * as path from 'path'; - -describe('CSharpFileGenerator', () => { - afterEach(() => { - jest.restoreAllMocks(); - }); - - describe('generateToFile()', () => { - const doc = { - $id: 'CustomClass', - type: 'object', - additionalProperties: true, - properties: { - someProp: { type: 'string' }, - someEnum: { - $id: 'CustomEnum', - type: 'string', - enum: ['Texas', 'Alabama', 'California'], - } - } - }; - test('should throw accurate error if file cannot be written', async () => { - const generator = new CSharpFileGenerator(); - const expectedError = new Error('write error'); - jest.spyOn(FileHelpers, 'writerToFileSystem').mockRejectedValue(expectedError); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), '', new CommonInputModel(), [])]); - - await expect(generator.generateToFiles(doc, '/test/', {namespace: 'SomeNamespace'})).rejects.toEqual(expectedError); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); - }); - test('should try and generate models to files', async () => { - const generator = new CSharpFileGenerator(); - const outputDir = './test'; - const expectedOutputDirPath = path.resolve(outputDir); - const expectedOutputFilePath = path.resolve(`${outputDir}/Test.cs`); - const expectedWriteToFileParameters = [ - 'content', - expectedOutputFilePath, - ]; - jest.spyOn(FileHelpers, 'writerToFileSystem').mockResolvedValue(undefined); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), 'Test', new CommonInputModel(), [])]); - - await generator.generateToFiles(doc, expectedOutputDirPath, {namespace: 'SomeNamespace'}); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); - expect((FileHelpers.writerToFileSystem as jest.Mock).mock.calls[0]).toEqual(expectedWriteToFileParameters); - }); - }); -}); diff --git a/test/generators/csharp/CSharpGenerator.spec.ts b/test/generators/csharp/CSharpGenerator.spec.ts index dbb59112ac..7d6a555cf3 100644 --- a/test/generators/csharp/CSharpGenerator.spec.ts +++ b/test/generators/csharp/CSharpGenerator.spec.ts @@ -6,26 +6,6 @@ describe('CSharpGenerator', () => { generator = new CSharpGenerator(); }); - test('should not render reserved keyword', async () => { - const doc = { - $id: 'Address', - type: 'object', - properties: { - enum: { type: 'string' }, - reservedEnum: { type: 'string' } - }, - additionalProperties: false - }; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Address']; - - let classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toMatchSnapshot(); - - classModel = await generator.render(model, inputModel); - expect(classModel.result).toMatchSnapshot(); - }); test('should render `class` type', async () => { const doc = { $id: '_address', @@ -35,10 +15,18 @@ describe('CSharpGenerator', () => { city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - tuple_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, - array_type: { type: 'array', items: { type: 'string' } }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + tuple_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + array_type: { type: 'array', items: { type: 'string' } } }, required: ['street_name', 'city', 'state', 'house_number', 'array_type'], additionalProperties: { @@ -48,67 +36,26 @@ describe('CSharpGenerator', () => { '^S(.?*)test&': { type: 'string' } - }, - }; - - const inputModel = await generator.process(doc); - const model = inputModel.models['_address']; - - let classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toMatchSnapshot(); - expect(classModel.dependencies).toEqual(['using System.Collections.Generic;']); - - classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toMatchSnapshot(); - expect(classModel.dependencies).toEqual(['using System.Collections.Generic;']); - }); - - test.each([ - { - name: 'with enums sharing same type', - doc: { - $id: 'States', - type: 'string', - enum: ['Texas', 'Alabama', 'California'], - } - }, - { - name: 'with enums of mixed types', - doc: { - $id: 'Things', - enum: ['Texas', '1', 1, false, {test: 'test'}], } - }, - ])('should render `enum` type $name', async ({doc}) => { - const inputModel = await generator.process(doc); - const model = inputModel.models[doc.$id]; + }; - let enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toMatchSnapshot(); - expect(enumModel.dependencies).toEqual([]); - - enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toMatchSnapshot(); - expect(enumModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([ + 'using System.Collections.Generic;' + ]); }); - test('should work custom preset for `enum` type', async () => { + test('should render `enum` type', async () => { const doc = { - $id: 'CustomEnum', - type: 'string', - enum: ['Texas', 'Alabama', 'California'], + $id: 'Things', + enum: ['Texas', '1', 1, false, { test: 'test' }] }; - - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomEnum']; - - let enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toMatchSnapshot(); - expect(enumModel.dependencies).toEqual([]); - - enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toMatchSnapshot(); - expect(enumModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render enums with translated special characters', async () => { @@ -117,19 +64,13 @@ describe('CSharpGenerator', () => { type: 'string', enum: ['test+', 'test', 'test-', 'test?!', '*test'] }; - - generator = new CSharpGenerator(); - - const inputModel = await generator.process(doc); - const model = inputModel.models['States']; - let enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toMatchSnapshot(); - expect(enumModel.dependencies).toEqual([]); + generator = new CSharpGenerator(); - enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toMatchSnapshot(); - expect(enumModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render models and their dependencies', async () => { @@ -141,19 +82,31 @@ describe('CSharpGenerator', () => { city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, - other_model: { type: 'object', $id: 'OtherModel', properties: {street_name: { type: 'string' }} }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + other_model: { + type: 'object', + $id: 'OtherModel', + properties: { street_name: { type: 'string' } } + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const config = {namespace: 'Test.Namespace'}; + const config = { namespace: 'Test.Namespace' }; const models = await generator.generateCompleteModels(doc, config); expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); @@ -169,20 +122,32 @@ describe('CSharpGenerator', () => { city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const config = {namespace: 'true'}; - const expectedError = new Error('You cannot use reserved CSharp keyword (true) as namespace, please use another.'); - await expect(generator.generateCompleteModels(doc, config)).rejects.toEqual(expectedError); + const config = { namespace: 'true' }; + const expectedError = new Error( + 'You cannot use reserved CSharp keyword (true) as namespace, please use another.' + ); + await expect(generator.generateCompleteModels(doc, config)).rejects.toEqual( + expectedError + ); }); describe('class renderer', () => { @@ -190,7 +155,7 @@ describe('CSharpGenerator', () => { $id: 'CustomClass', type: 'object', properties: { - property: { type: 'string' }, + property: { type: 'string' } }, additionalProperties: { type: 'string' @@ -198,39 +163,45 @@ describe('CSharpGenerator', () => { }; test('should be able to overwrite accessor preset hook', async () => { - generator = new CSharpGenerator({ presets: [ - { - class: { - accessor() { - return 'my own custom factory'; + generator = new CSharpGenerator({ + presets: [ + { + class: { + accessor() { + return 'my own custom factory'; + } } } - } - ] }); - - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomClass']; - - const classModel = await generator.render(model, inputModel); - expect(classModel.result).toMatchSnapshot(); + ] + }); + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([ + 'using System.Collections.Generic;' + ]); }); test('should be able to overwrite property preset hook', async () => { - generator = new CSharpGenerator({ presets: [ - { - class: { - property() { - return 'my own property'; - }, + generator = new CSharpGenerator({ + presets: [ + { + class: { + property() { + return 'my own property'; + } + } } - } - ] }); - - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomClass']; - - const classModel = await generator.render(model, inputModel); - expect(classModel.result).toMatchSnapshot(); + ] + }); + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([ + 'using System.Collections.Generic;' + ]); }); }); }); diff --git a/test/generators/csharp/CSharpRenderer.spec.ts b/test/generators/csharp/CSharpRenderer.spec.ts index b0d96df423..849956c2ce 100644 --- a/test/generators/csharp/CSharpRenderer.spec.ts +++ b/test/generators/csharp/CSharpRenderer.spec.ts @@ -1,144 +1,36 @@ import { CSharpGenerator } from '../../../src'; +import { CSharpDependencyManager } from '../../../src/generators/csharp/CSharpDependencyManager'; import { CSharpRenderer } from '../../../src/generators/csharp/CSharpRenderer'; -import { CommonInputModel, CommonModel } from '../../../src/models'; -class MockCSharpRenderer extends CSharpRenderer { - -} +import { ConstrainedObjectModel, InputMetaModel } from '../../../src/models'; +import { MockCSharpRenderer } from '../../TestUtils/TestRenderers'; describe('CSharpRenderer', () => { - let renderer: MockCSharpRenderer; + let renderer: CSharpRenderer; beforeEach(() => { - renderer = new MockCSharpRenderer(CSharpGenerator.defaultOptions, new CSharpGenerator(), [], new CommonModel(), new CommonInputModel()); + renderer = new MockCSharpRenderer( + CSharpGenerator.defaultOptions, + new CSharpGenerator(), + [], + new ConstrainedObjectModel('', undefined, '', {}), + new InputMetaModel(), + new CSharpDependencyManager(CSharpGenerator.defaultOptions) + ); }); - describe('toCSharpType()', () => { - test('Should render undefined as dynamic type', () => { - expect(renderer.toCSharpType(undefined, new CommonModel())).toEqual('dynamic'); - }); - test('Should render a string type', () => { - expect(renderer.toCSharpType('string', new CommonModel())).toEqual('string'); - expect(renderer.toCSharpType('password', new CommonModel())).toEqual('string'); - expect(renderer.toCSharpType('byte', new CommonModel())).toEqual('string'); - }); - test('Should render boolean as nullable bool type', () => { - expect(renderer.toCSharpType('boolean', new CommonModel())).toEqual('bool?'); - }); - test('Should render integer as nullable int type', () => { - expect(renderer.toCSharpType('integer', new CommonModel())).toEqual('int?'); - }); - test('Should render a nullable double type', () => { - expect(renderer.toCSharpType('number', new CommonModel())).toEqual('double?'); - expect(renderer.toCSharpType('double', new CommonModel())).toEqual('double?'); - }); - test('Should render array as slice of the type', () => { - const model = new CommonModel(); - model.items = CommonModel.toCommonModel({ type: 'number' }); - expect(renderer.toCSharpType('array', model)).toEqual('double?[]'); - }); - test('Should render tuple with multiple types as dynamic', () => { - const model = new CommonModel(); - model.items = [CommonModel.toCommonModel({ type: 'number' }), CommonModel.toCommonModel({ type: 'string' })]; - expect(renderer.toCSharpType('array', model)).toEqual('dynamic[]'); - }); - test('Should render object as object type', () => { - expect(renderer.toCSharpType('object', new CommonModel())).toEqual('object'); - }); - test('Should be able to return a nullable long', () => { - expect(renderer.toCSharpType('long', new CommonModel())).toEqual('long?'); - expect(renderer.toCSharpType('int64', new CommonModel())).toEqual('long?'); - }); - test('Should be able to return date time', () => { - expect(renderer.toCSharpType('time', new CommonModel())).toEqual('System.DateTime?'); - expect(renderer.toCSharpType('date', new CommonModel())).toEqual('System.DateTime?'); - expect(renderer.toCSharpType('dateTime', new CommonModel())).toEqual('System.DateTime?'); - expect(renderer.toCSharpType('date-time', new CommonModel())).toEqual('System.DateTime?'); - }); - test('Should be able to return float', () => { - expect(renderer.toCSharpType('float', new CommonModel())).toEqual('float?'); - }); - test('Should be able to return byte array', () => { - expect(renderer.toCSharpType('binary', new CommonModel())).toEqual('byte[]'); - }); - test('Should render matching tuple types as is', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - }, - { - type: 'string' - } - ] - }); - expect(renderer.toCSharpType('array', model)).toEqual('string[]'); - }); - test('Should render mismatching tuple types as dynamic', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - }, - { - type: 'number' - } - ] - }); - expect(renderer.toCSharpType('array', model)).toEqual('dynamic[]'); - }); - test('Should render matching tuple and additionalItem types', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - } - ], - additionalItems: { - type: 'string' - } - }); - expect(renderer.toCSharpType('array', model)).toEqual('string[]'); - }); - test('Should render dynamic for tuple and additionalItem type mismatch', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - } - ], - additionalItems: { - type: 'number' - } - }); - expect(renderer.toCSharpType('array', model)).toEqual('dynamic[]'); - }); - }); - describe('nameType()', () => { - test('Should call naming convention', () => { - const name = renderer.nameType('type__someType'); - expect(name).toEqual('TypeSomeType'); - }); - }); - describe('nameProperty()', () => { - test('Should call naming convention', () => { - const name = renderer.nameProperty('property__someProperty'); - expect(name).toEqual('propertySomeProperty'); - }); - }); - describe('renderType()', () => { - test('Should render refs using type naming convention', () => { - const model = new CommonModel(); - model.$ref = ''; - const renderedType = renderer.renderType(model); - expect(renderedType).toEqual('AnonymousSchema_1'); - }); - test('Should render single union type', () => { - const model = CommonModel.toCommonModel({ type: ['number'] }); - const renderedType = renderer.renderType(model); - expect(renderedType).toEqual('double?'); - }); - test('Should render union types with multiple types as slice of interface', () => { - const model = CommonModel.toCommonModel({ type: ['number', 'string'] }); - const renderedType = renderer.renderType(model); - expect(renderedType).toEqual('dynamic'); + describe('renderComments()', () => { + test('should render single lines correctly', () => { + const lines = 'test'; + const renderedLines = renderer.renderComments(lines); + expect(renderedLines).toMatchSnapshot(); + }); + test('should render multiple lines correctly', () => { + const lines = ['test', 'test2']; + const renderedLines = renderer.renderComments(lines); + expect(renderedLines).toMatchSnapshot(); + }); + test('should render no lines', () => { + const lines: string[] = []; + const renderedLines = renderer.renderComments(lines); + expect(renderedLines).toMatchSnapshot(); }); }); }); diff --git a/test/generators/csharp/__snapshots__/CSharpGenerator.spec.ts.snap b/test/generators/csharp/__snapshots__/CSharpGenerator.spec.ts.snap index bf4ceb89b8..c24555fe87 100644 --- a/test/generators/csharp/__snapshots__/CSharpGenerator.spec.ts.snap +++ b/test/generators/csharp/__snapshots__/CSharpGenerator.spec.ts.snap @@ -3,8 +3,8 @@ exports[`CSharpGenerator class renderer should be able to overwrite accessor preset hook 1`] = ` "public class CustomClass { - private string property; - private Dictionary additionalProperties; + private string? property; + private Dictionary? additionalProperties; my own custom factory @@ -18,13 +18,13 @@ exports[`CSharpGenerator class renderer should be able to overwrite property pre my own property my own property - public string Property + public string? Property { get { return property; } set { property = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } @@ -32,46 +32,6 @@ exports[`CSharpGenerator class renderer should be able to overwrite property pre }" `; -exports[`CSharpGenerator should not render reserved keyword 1`] = ` -"public class Address -{ - private string reservedReservedEnum; - private string reservedEnum; - - public string ReservedReservedEnum - { - get { return reservedReservedEnum; } - set { reservedReservedEnum = value; } - } - - public string ReservedEnum - { - get { return reservedEnum; } - set { reservedEnum = value; } - } -}" -`; - -exports[`CSharpGenerator should not render reserved keyword 2`] = ` -"public class Address -{ - private string reservedReservedEnum; - private string reservedEnum; - - public string ReservedReservedEnum - { - get { return reservedReservedEnum; } - set { reservedReservedEnum = value; } - } - - public string ReservedEnum - { - get { return reservedEnum; } - set { reservedEnum = value; } - } -}" -`; - exports[`CSharpGenerator should render \`class\` type 1`] = ` "public class Address { @@ -80,87 +40,10 @@ exports[`CSharpGenerator should render \`class\` type 1`] = ` private string state; private double houseNumber; private bool? marriage; - private dynamic members; - private dynamic[] tupleType; - private string[] arrayType; - private Dictionary additionalProperties; - private Dictionary sTestPatternProperties; - - public string StreetName - { - get { return streetName; } - set { streetName = value; } - } - - public string City - { - get { return city; } - set { city = value; } - } - - public string State - { - get { return state; } - set { state = value; } - } - - public double HouseNumber - { - get { return houseNumber; } - set { houseNumber = value; } - } - - public bool? Marriage - { - get { return marriage; } - set { marriage = value; } - } - - public dynamic Members - { - get { return members; } - set { members = value; } - } - - public dynamic[] TupleType - { - get { return tupleType; } - set { tupleType = value; } - } - - public string[] ArrayType - { - get { return arrayType; } - set { arrayType = value; } - } - - public Dictionary AdditionalProperties - { - get { return additionalProperties; } - set { additionalProperties = value; } - } - - public Dictionary STestPatternProperties - { - get { return sTestPatternProperties; } - set { sTestPatternProperties = value; } - } -}" -`; - -exports[`CSharpGenerator should render \`class\` type 2`] = ` -"public class Address -{ - private string streetName; - private string city; - private string state; - private double houseNumber; - private bool? marriage; - private dynamic members; - private dynamic[] tupleType; - private string[] arrayType; - private Dictionary additionalProperties; - private Dictionary sTestPatternProperties; + private dynamic? members; + private dynamic[]? tupleType; + private dynamic[] arrayType; + private Dictionary? additionalProperties; public string StreetName { @@ -192,183 +75,66 @@ exports[`CSharpGenerator should render \`class\` type 2`] = ` set { marriage = value; } } - public dynamic Members + public dynamic? Members { get { return members; } set { members = value; } } - public dynamic[] TupleType + public dynamic[]? TupleType { get { return tupleType; } set { tupleType = value; } } - public string[] ArrayType + public dynamic[] ArrayType { get { return arrayType; } set { arrayType = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } } - - public Dictionary STestPatternProperties - { - get { return sTestPatternProperties; } - set { sTestPatternProperties = value; } - } }" `; -exports[`CSharpGenerator should render \`enum\` type with enums of mixed types 1`] = ` -"public enum Things -{ - Texas, - String_1, - Number_1, - False, - TestTest -} - -public static class ThingsExtensions -{ - public static dynamic GetValue(this Things enumValue) - { - switch (enumValue) - { - case Things.Texas: return \\"Texas\\"; - case Things.String_1: return \\"1\\"; - case Things.Number_1: return 1; - case Things.False: return false; - case Things.TestTest: return \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\"; - } - return null; - } - - public static Things? ToThings(dynamic value) - { - switch (value) - { - case \\"Texas\\": return Things.Texas; - case \\"1\\": return Things.String_1; - case 1: return Things.Number_1; - case false: return Things.False; - case \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\": return Things.TestTest; - } - return null; - } -} -" -`; - -exports[`CSharpGenerator should render \`enum\` type with enums of mixed types 2`] = ` +exports[`CSharpGenerator should render \`enum\` type 1`] = ` "public enum Things { - Texas, - String_1, - Number_1, - False, - TestTest + TEXAS, + NUMBER_1, + RESERVED_NUMBER_1, + RESERVED_FALSE, + CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT } public static class ThingsExtensions { - public static dynamic GetValue(this Things enumValue) + public static dynamic? GetValue(this Things enumValue) { switch (enumValue) { - case Things.Texas: return \\"Texas\\"; - case Things.String_1: return \\"1\\"; - case Things.Number_1: return 1; - case Things.False: return false; - case Things.TestTest: return \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\"; + case Things.TEXAS: return \\"Texas\\"; + case Things.NUMBER_1: return \\"1\\"; + case Things.RESERVED_NUMBER_1: return 1; + case Things.RESERVED_FALSE: return false; + case Things.CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT: return \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\"; } return null; } - public static Things? ToThings(dynamic value) + public static Things? ToThings(dynamic? value) { switch (value) { - case \\"Texas\\": return Things.Texas; - case \\"1\\": return Things.String_1; - case 1: return Things.Number_1; - case false: return Things.False; - case \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\": return Things.TestTest; - } - return null; - } -} -" -`; - -exports[`CSharpGenerator should render \`enum\` type with enums sharing same type 1`] = ` -"public enum States -{ - Texas, - Alabama, - California -} - -public static class StatesExtensions -{ - public static dynamic GetValue(this States enumValue) - { - switch (enumValue) - { - case States.Texas: return \\"Texas\\"; - case States.Alabama: return \\"Alabama\\"; - case States.California: return \\"California\\"; - } - return null; - } - - public static States? ToStates(dynamic value) - { - switch (value) - { - case \\"Texas\\": return States.Texas; - case \\"Alabama\\": return States.Alabama; - case \\"California\\": return States.California; - } - return null; - } -} -" -`; - -exports[`CSharpGenerator should render \`enum\` type with enums sharing same type 2`] = ` -"public enum States -{ - Texas, - Alabama, - California -} - -public static class StatesExtensions -{ - public static dynamic GetValue(this States enumValue) - { - switch (enumValue) - { - case States.Texas: return \\"Texas\\"; - case States.Alabama: return \\"Alabama\\"; - case States.California: return \\"California\\"; - } - return null; - } - - public static States? ToStates(dynamic value) - { - switch (value) - { - case \\"Texas\\": return States.Texas; - case \\"Alabama\\": return States.Alabama; - case \\"California\\": return States.California; + case \\"Texas\\": return Things.TEXAS; + case \\"1\\": return Things.NUMBER_1; + case 1: return Things.RESERVED_NUMBER_1; + case false: return Things.RESERVED_FALSE; + case \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\": return Things.CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT; } return null; } @@ -379,78 +145,37 @@ public static class StatesExtensions exports[`CSharpGenerator should render enums with translated special characters 1`] = ` "public enum States { - TestPlus, - Test, - TestMinus, - TestQuestionExclamation, - AsteriskTest + TEST_PLUS, + TEST, + TEST_MINUS, + TEST_QUESTION_EXCLAMATION, + ASTERISK_TEST } public static class StatesExtensions { - public static dynamic GetValue(this States enumValue) + public static string? GetValue(this States enumValue) { switch (enumValue) { - case States.TestPlus: return \\"test+\\"; - case States.Test: return \\"test\\"; - case States.TestMinus: return \\"test-\\"; - case States.TestQuestionExclamation: return \\"test?!\\"; - case States.AsteriskTest: return \\"*test\\"; + case States.TEST_PLUS: return \\"test+\\"; + case States.TEST: return \\"test\\"; + case States.TEST_MINUS: return \\"test-\\"; + case States.TEST_QUESTION_EXCLAMATION: return \\"test?!\\"; + case States.ASTERISK_TEST: return \\"*test\\"; } return null; } - public static States? ToStates(dynamic value) + public static States? ToStates(string? value) { switch (value) { - case \\"test+\\": return States.TestPlus; - case \\"test\\": return States.Test; - case \\"test-\\": return States.TestMinus; - case \\"test?!\\": return States.TestQuestionExclamation; - case \\"*test\\": return States.AsteriskTest; - } - return null; - } -} -" -`; - -exports[`CSharpGenerator should render enums with translated special characters 2`] = ` -"public enum States -{ - TestPlus, - Test, - TestMinus, - TestQuestionExclamation, - AsteriskTest -} - -public static class StatesExtensions -{ - public static dynamic GetValue(this States enumValue) - { - switch (enumValue) - { - case States.TestPlus: return \\"test+\\"; - case States.Test: return \\"test\\"; - case States.TestMinus: return \\"test-\\"; - case States.TestQuestionExclamation: return \\"test?!\\"; - case States.AsteriskTest: return \\"*test\\"; - } - return null; - } - - public static States? ToStates(dynamic value) - { - switch (value) - { - case \\"test+\\": return States.TestPlus; - case \\"test\\": return States.Test; - case \\"test-\\": return States.TestMinus; - case \\"test?!\\": return States.TestQuestionExclamation; - case \\"*test\\": return States.AsteriskTest; + case \\"test+\\": return States.TEST_PLUS; + case \\"test\\": return States.TEST; + case \\"test-\\": return States.TEST_MINUS; + case \\"test?!\\": return States.TEST_QUESTION_EXCLAMATION; + case \\"*test\\": return States.ASTERISK_TEST; } return null; } @@ -470,11 +195,10 @@ exports[`CSharpGenerator should render models and their dependencies 1`] = ` private string state; private double houseNumber; private bool? marriage; - private dynamic members; + private dynamic? members; private dynamic[] arrayType; - private OtherModel otherModel; - private Dictionary additionalProperties; - private Dictionary sTestPatternProperties; + private OtherModel? otherModel; + private Dictionary? additionalProperties; public string StreetName { @@ -506,7 +230,7 @@ exports[`CSharpGenerator should render models and their dependencies 1`] = ` set { marriage = value; } } - public dynamic Members + public dynamic? Members { get { return members; } set { members = value; } @@ -518,23 +242,17 @@ exports[`CSharpGenerator should render models and their dependencies 1`] = ` set { arrayType = value; } } - public OtherModel OtherModel + public OtherModel? OtherModel { get { return otherModel; } set { otherModel = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } } - - public Dictionary STestPatternProperties - { - get { return sTestPatternProperties; } - set { sTestPatternProperties = value; } - } } }" `; @@ -546,16 +264,16 @@ exports[`CSharpGenerator should render models and their dependencies 2`] = ` public class OtherModel { - private string streetName; - private Dictionary additionalProperties; + private string? streetName; + private Dictionary? additionalProperties; - public string StreetName + public string? StreetName { get { return streetName; } set { streetName = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } @@ -563,73 +281,3 @@ exports[`CSharpGenerator should render models and their dependencies 2`] = ` } }" `; - -exports[`CSharpGenerator should work custom preset for \`enum\` type 1`] = ` -"public enum CustomEnum -{ - Texas, - Alabama, - California -} - -public static class CustomEnumExtensions -{ - public static dynamic GetValue(this CustomEnum enumValue) - { - switch (enumValue) - { - case CustomEnum.Texas: return \\"Texas\\"; - case CustomEnum.Alabama: return \\"Alabama\\"; - case CustomEnum.California: return \\"California\\"; - } - return null; - } - - public static CustomEnum? ToCustomEnum(dynamic value) - { - switch (value) - { - case \\"Texas\\": return CustomEnum.Texas; - case \\"Alabama\\": return CustomEnum.Alabama; - case \\"California\\": return CustomEnum.California; - } - return null; - } -} -" -`; - -exports[`CSharpGenerator should work custom preset for \`enum\` type 2`] = ` -"public enum CustomEnum -{ - Texas, - Alabama, - California -} - -public static class CustomEnumExtensions -{ - public static dynamic GetValue(this CustomEnum enumValue) - { - switch (enumValue) - { - case CustomEnum.Texas: return \\"Texas\\"; - case CustomEnum.Alabama: return \\"Alabama\\"; - case CustomEnum.California: return \\"California\\"; - } - return null; - } - - public static CustomEnum? ToCustomEnum(dynamic value) - { - switch (value) - { - case \\"Texas\\": return CustomEnum.Texas; - case \\"Alabama\\": return CustomEnum.Alabama; - case \\"California\\": return CustomEnum.California; - } - return null; - } -} -" -`; diff --git a/test/generators/csharp/__snapshots__/CSharpRenderer.spec.ts.snap b/test/generators/csharp/__snapshots__/CSharpRenderer.spec.ts.snap new file mode 100644 index 0000000000..62da57dcef --- /dev/null +++ b/test/generators/csharp/__snapshots__/CSharpRenderer.spec.ts.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CSharpRenderer renderComments() should render multiple lines correctly 1`] = ` +"// test +// test2" +`; + +exports[`CSharpRenderer renderComments() should render no lines 1`] = `""`; + +exports[`CSharpRenderer renderComments() should render single lines correctly 1`] = `"// test"`; diff --git a/test/generators/csharp/constrainer/EnumConstrainer.spec.ts b/test/generators/csharp/constrainer/EnumConstrainer.spec.ts new file mode 100644 index 0000000000..387ec07699 --- /dev/null +++ b/test/generators/csharp/constrainer/EnumConstrainer.spec.ts @@ -0,0 +1,167 @@ +import { CSharpDefaultConstraints } from '../../../../src/generators/csharp/CSharpConstrainer'; +import { EnumModel } from '../../../../src/models/MetaModel'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../../src'; +import { + defaultEnumKeyConstraints, + ModelEnumKeyConstraints, + DefaultEnumKeyConstraints +} from '../../../../src/generators/csharp/constrainer/EnumConstrainer'; +describe('EnumConstrainer', () => { + const enumModel = new EnumModel('test', undefined, []); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [] + ); + + describe('enum keys', () => { + test('should never render special chars', () => { + const constrainedKey = CSharpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '%' + }); + expect(constrainedKey).toEqual('PERCENT'); + }); + test('should not render number as start char', () => { + const constrainedKey = CSharpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '1' + }); + expect(constrainedKey).toEqual('NUMBER_1'); + }); + test('should not contain duplicate keys', () => { + const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel( + 'EMPTY', + 'return' + ); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [existingConstrainedEnumValueModel] + ); + const constrainedKey = CSharpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('RESERVED_EMPTY'); + }); + test('should never contain empty keys', () => { + const constrainedKey = CSharpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('EMPTY'); + }); + test('should use constant naming format', () => { + const constrainedKey = CSharpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual( + 'SOME_SPACE_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2' + ); + }); + test('should never render reserved keywords', () => { + const constrainedKey = CSharpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'return' + }); + expect(constrainedKey).toEqual('RESERVED_RETURN'); + }); + }); + describe('enum values', () => { + test('should render string values', () => { + const constrainedValue = CSharpDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 'string value' + }); + expect(constrainedValue).toEqual('"string value"'); + }); + test('should render boolean values', () => { + const constrainedValue = CSharpDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: true + }); + expect(constrainedValue).toEqual(true); + }); + test('should render numbers', () => { + const constrainedValue = CSharpDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 123 + }); + expect(constrainedValue).toEqual(123); + }); + test('should render object', () => { + const constrainedValue = CSharpDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: { test: 'test' } + }); + expect(constrainedValue).toEqual('"{\\"test\\":\\"test\\"}"'); + }); + test('should render unknown value', () => { + const constrainedValue = CSharpDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: undefined + }); + expect(constrainedValue).toEqual('"undefined"'); + }); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks for enum key', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultEnumKeyConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks for enum key', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultEnumKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_DUPLICATE_KEYS'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultEnumKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/csharp/constrainer/ModelNameConstrainer.spec.ts b/test/generators/csharp/constrainer/ModelNameConstrainer.spec.ts new file mode 100644 index 0000000000..9aa2f539f9 --- /dev/null +++ b/test/generators/csharp/constrainer/ModelNameConstrainer.spec.ts @@ -0,0 +1,75 @@ +import { CSharpDefaultConstraints } from '../../../../src/generators/csharp/CSharpConstrainer'; +import { + DefaultModelNameConstraints, + defaultModelNameConstraints, + ModelNameConstraints +} from '../../../../src/generators/csharp/constrainer/ModelNameConstrainer'; +describe('ModelNameConstrainer', () => { + test('should never render special chars', () => { + const constrainedKey = CSharpDefaultConstraints.modelName({ + modelName: '%' + }); + expect(constrainedKey).toEqual('Percent'); + }); + test('should never render number as start char', () => { + const constrainedKey = CSharpDefaultConstraints.modelName({ + modelName: '1' + }); + expect(constrainedKey).toEqual('Number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = CSharpDefaultConstraints.modelName({ + modelName: '' + }); + expect(constrainedKey).toEqual('Empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = CSharpDefaultConstraints.modelName({ + modelName: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = CSharpDefaultConstraints.modelName({ + modelName: 'return' + }); + expect(constrainedKey).toEqual('ReservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: ModelNameConstraints = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultModelNameConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ modelName: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultModelNameConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultModelNameConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultModelNameConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ modelName: '' }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/csharp/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/csharp/constrainer/PropertyKeyConstrainer.spec.ts new file mode 100644 index 0000000000..2193c17291 --- /dev/null +++ b/test/generators/csharp/constrainer/PropertyKeyConstrainer.spec.ts @@ -0,0 +1,185 @@ +import { CSharpDefaultConstraints } from '../../../../src/generators/csharp/CSharpConstrainer'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ObjectModel, + ObjectPropertyModel +} from '../../../../src'; +import { + DefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints, + PropertyKeyConstraintOptions +} from '../../../../src/generators/csharp/constrainer/PropertyKeyConstrainer'; +describe('PropertyKeyConstrainer', () => { + const objectModel = new ObjectModel('MyObjectModelName', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'MyObjectModelName', + undefined, + '', + {} + ); + + const constrainPropertyName = (propertyName: string) => { + const objectPropertyModel = new ObjectPropertyModel( + propertyName, + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + return CSharpDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + }; + + test('should never render special chars', () => { + const constrainedKey = constrainPropertyName('%'); + expect(constrainedKey).toEqual('percent'); + }); + test('should not render number as start char', () => { + const constrainedKey = constrainPropertyName('1'); + expect(constrainedKey).toEqual('number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = constrainPropertyName(''); + expect(constrainedKey).toEqual('empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = constrainPropertyName('some weird_value!"#2'); + expect(constrainedKey).toEqual('someWeirdValueExclamationQuotationHash_2'); + }); + test('should not render enclosing type name', () => { + const constrainedKey = constrainPropertyName('MyObjectModelName'); + expect(constrainedKey).toEqual('reservedMyObjectModelName'); + }); + test('should not contain duplicate properties', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + const objectPropertyModel = new ObjectPropertyModel( + 'reservedReturn', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + 'reservedReturn', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const objectPropertyModel2 = new ObjectPropertyModel( + 'return', + false, + objectModel + ); + const constrainedObjectPropertyModel2 = new ConstrainedObjectPropertyModel( + 'return', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainedObjectModel.properties['reservedReturn'] = + constrainedObjectPropertyModel; + constrainedObjectModel.properties['return'] = + constrainedObjectPropertyModel2; + const constrainedKey = CSharpDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel: objectPropertyModel2, + constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + }); + expect(constrainedKey).toEqual('reservedReservedReturn'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = constrainPropertyName('return'); + expect(constrainedKey).toEqual('reservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue(''), + NO_ENCLOSING_NAMES: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultPropertyKeyConstraints( + mockedConstraintCallbacks + ); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_RESERVED_KEYWORDS'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_ENCLOSING_NAMES') + ]; + const overwrittenDefaultFunction = jest.spyOn( + DefaultPropertyKeyConstraints, + 'NAMING_FORMATTER' + ); + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultPropertyKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const constrainedValue = constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + expect(constrainedValue).toEqual(''); + expect(jestCallback).toHaveBeenCalled(); + expect(overwrittenDefaultFunction).not.toHaveBeenCalled(); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/csharp/presets/CommonPreset.spec.ts b/test/generators/csharp/presets/CommonPreset.spec.ts index 69606065ea..4ba4258fac 100644 --- a/test/generators/csharp/presets/CommonPreset.spec.ts +++ b/test/generators/csharp/presets/CommonPreset.spec.ts @@ -1,4 +1,8 @@ -import { CSharpGenerator, CSHARP_COMMON_PRESET, CSHARP_DEFAULT_PRESET } from '../../../../src/generators'; +import { + CSharpGenerator, + CSHARP_COMMON_PRESET, + CSHARP_DEFAULT_PRESET +} from '../../../../src/generators'; const doc = { $id: 'Test', type: 'object', @@ -7,20 +11,24 @@ const doc = { properties: { 'string prop': { type: 'string' }, numberProp: { type: 'number' }, - objectProp: { type: 'object', $id: 'NestedTest', properties: {stringProp: { type: 'string' }}} + objectProp: { + type: 'object', + $id: 'NestedTest', + properties: { stringProp: { type: 'string' } } + } }, patternProperties: { '^S(.?)test': { type: 'string' } - }, + } }; describe('CSHARP_COMMON_PRESET', () => { test('should render Equals support function', async () => { - const generator = new CSharpGenerator({ + const generator = new CSharpGenerator({ presets: [ { - preset: CSHARP_COMMON_PRESET, + preset: CSHARP_COMMON_PRESET, options: { equal: true, hashCode: false @@ -29,14 +37,15 @@ describe('CSHARP_COMMON_PRESET', () => { ] }); const inputModel = await generator.generate(doc); + expect(inputModel).toHaveLength(2); expect(inputModel[0].result).toMatchSnapshot(); expect(inputModel[1].result).toMatchSnapshot(); }); test('should render GetHashCode support function', async () => { - const generator = new CSharpGenerator({ + const generator = new CSharpGenerator({ presets: [ { - preset: CSHARP_COMMON_PRESET, + preset: CSHARP_COMMON_PRESET, options: { equal: false, hashCode: true @@ -45,6 +54,7 @@ describe('CSHARP_COMMON_PRESET', () => { ] }); const inputModel = await generator.generate(doc); + expect(inputModel).toHaveLength(2); expect(inputModel[0].result).toMatchSnapshot(); expect(inputModel[1].result).toMatchSnapshot(); }); diff --git a/test/generators/csharp/presets/JsonSerializerPreset.spec.ts b/test/generators/csharp/presets/JsonSerializerPreset.spec.ts index 98499be939..b7cc944833 100644 --- a/test/generators/csharp/presets/JsonSerializerPreset.spec.ts +++ b/test/generators/csharp/presets/JsonSerializerPreset.spec.ts @@ -1,4 +1,7 @@ -import { CSharpGenerator, CSHARP_JSON_SERIALIZER_PRESET } from '../../../../src/generators'; +import { + CSharpGenerator, + CSHARP_JSON_SERIALIZER_PRESET +} from '../../../../src/generators'; const doc = { $id: 'Test', type: 'object', @@ -7,32 +10,32 @@ const doc = { properties: { 'string prop': { type: 'string' }, numberProp: { type: 'number' }, - enumProp: { $id: 'EnumTest', enum: ['Some enum String', true, {test: 'test'}, 2]}, - objectProp: { type: 'object', $id: 'NestedTest', properties: {stringProp: { type: 'string' }}} + enumProp: { + $id: 'EnumTest', + enum: ['Some enum String', true, { test: 'test' }, 2] + }, + objectProp: { + type: 'object', + $id: 'NestedTest', + properties: { stringProp: { type: 'string' } } + } }, patternProperties: { '^S(.?)test': { type: 'string' } - }, + } }; describe('JSON serializer preset', () => { test('should render serialize and deserialize converters', async () => { - const generator = new CSharpGenerator({ - presets: [ - CSHARP_JSON_SERIALIZER_PRESET - ] + const generator = new CSharpGenerator({ + presets: [CSHARP_JSON_SERIALIZER_PRESET] }); - const inputModel = await generator.process(doc); - const nestedTestModel = inputModel.models['NestedTest']; - const testModel = inputModel.models['Test']; - const enumModel = inputModel.models['EnumTest']; - const nestedTestClass = await generator.renderClass(nestedTestModel, inputModel); - const testClass = await generator.renderClass(testModel, inputModel); - const enumEnum = await generator.renderEnum(enumModel, inputModel); - expect(nestedTestClass.result).toMatchSnapshot(); - expect(testClass.result).toMatchSnapshot(); - expect(enumEnum.result).toMatchSnapshot(); + const inputModel = await generator.generate(doc); + expect(inputModel).toHaveLength(3); + expect(inputModel[0].result).toMatchSnapshot(); + expect(inputModel[1].result).toMatchSnapshot(); + expect(inputModel[2].result).toMatchSnapshot(); }); }); diff --git a/test/generators/csharp/presets/NewtonsoftSerializerPreset.spec.ts b/test/generators/csharp/presets/NewtonsoftSerializerPreset.spec.ts new file mode 100644 index 0000000000..08eae68dfb --- /dev/null +++ b/test/generators/csharp/presets/NewtonsoftSerializerPreset.spec.ts @@ -0,0 +1,41 @@ +import { + CSharpGenerator, + CSHARP_NEWTONSOFT_SERIALIZER_PRESET +} from '../../../../src/generators'; +const doc = { + $id: 'Test', + type: 'object', + additionalProperties: true, + required: ['string prop'], + properties: { + 'string prop': { type: 'string' }, + numberProp: { type: 'number' }, + enumProp: { + $id: 'EnumTest', + enum: ['Some enum String', true, { test: 'test' }, 2] + }, + objectProp: { + type: 'object', + $id: 'NestedTest', + properties: { stringProp: { type: 'string' } } + } + }, + patternProperties: { + '^S(.?)test': { + type: 'string' + } + } +}; +describe('Newtonsoft JSON serializer preset', () => { + test('should render serialize and deserialize converters', async () => { + const generator = new CSharpGenerator({ + presets: [CSHARP_NEWTONSOFT_SERIALIZER_PRESET] + }); + + const outputModels = await generator.generate(doc); + expect(outputModels).toHaveLength(3); + expect(outputModels[0].result).toMatchSnapshot(); + expect(outputModels[1].result).toMatchSnapshot(); + expect(outputModels[2].result).toMatchSnapshot(); + }); +}); diff --git a/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap b/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap index 5a72e8bf15..dd35db1ca1 100644 --- a/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap +++ b/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap @@ -5,9 +5,8 @@ exports[`CSHARP_COMMON_PRESET should render Equals support function 1`] = ` { private string stringProp; private double? numberProp; - private NestedTest objectProp; - private Dictionary additionalProperties; - private Dictionary sTestPatternProperties; + private NestedTest? objectProp; + private Dictionary? additionalProperties; public string StringProp { @@ -21,24 +20,18 @@ exports[`CSHARP_COMMON_PRESET should render Equals support function 1`] = ` set { numberProp = value; } } - public NestedTest ObjectProp + public NestedTest? ObjectProp { get { return objectProp; } set { objectProp = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } } - public Dictionary STestPatternProperties - { - get { return sTestPatternProperties; } - set { sTestPatternProperties = value; } - } - public override bool Equals(object obj) { if(obj is Test model) @@ -47,28 +40,37 @@ exports[`CSHARP_COMMON_PRESET should render Equals support function 1`] = ` return StringProp == model.StringProp && NumberProp == model.NumberProp && ObjectProp == model.ObjectProp && - AdditionalProperties == model.AdditionalProperties && - STestPatternProperties == model.STestPatternProperties; + AdditionalProperties == model.AdditionalProperties; } return false; } + + public override int GetHashCode() + { + HashCode hash = new HashCode(); + hash.Add(StringProp); + hash.Add(NumberProp); + hash.Add(ObjectProp); + hash.Add(AdditionalProperties); + return hash.ToHashCode(); + } }" `; exports[`CSHARP_COMMON_PRESET should render Equals support function 2`] = ` "public class NestedTest { - private string stringProp; - private Dictionary additionalProperties; + private string? stringProp; + private Dictionary? additionalProperties; - public string StringProp + public string? StringProp { get { return stringProp; } set { stringProp = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } @@ -85,6 +87,14 @@ exports[`CSHARP_COMMON_PRESET should render Equals support function 2`] = ` return false; } + + public override int GetHashCode() + { + HashCode hash = new HashCode(); + hash.Add(StringProp); + hash.Add(AdditionalProperties); + return hash.ToHashCode(); + } }" `; @@ -93,9 +103,8 @@ exports[`CSHARP_COMMON_PRESET should render GetHashCode support function 1`] = ` { private string stringProp; private double? numberProp; - private NestedTest objectProp; - private Dictionary additionalProperties; - private Dictionary sTestPatternProperties; + private NestedTest? objectProp; + private Dictionary? additionalProperties; public string StringProp { @@ -109,24 +118,18 @@ exports[`CSHARP_COMMON_PRESET should render GetHashCode support function 1`] = ` set { numberProp = value; } } - public NestedTest ObjectProp + public NestedTest? ObjectProp { get { return objectProp; } set { objectProp = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } } - public Dictionary STestPatternProperties - { - get { return sTestPatternProperties; } - set { sTestPatternProperties = value; } - } - public override int GetHashCode() { HashCode hash = new HashCode(); @@ -134,7 +137,6 @@ exports[`CSHARP_COMMON_PRESET should render GetHashCode support function 1`] = ` hash.Add(NumberProp); hash.Add(ObjectProp); hash.Add(AdditionalProperties); - hash.Add(STestPatternProperties); return hash.ToHashCode(); } }" @@ -143,16 +145,16 @@ exports[`CSHARP_COMMON_PRESET should render GetHashCode support function 1`] = ` exports[`CSHARP_COMMON_PRESET should render GetHashCode support function 2`] = ` "public class NestedTest { - private string stringProp; - private Dictionary additionalProperties; + private string? stringProp; + private Dictionary? additionalProperties; - public string StringProp + public string? StringProp { get { return stringProp; } set { stringProp = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } @@ -166,4 +168,4 @@ exports[`CSHARP_COMMON_PRESET should render GetHashCode support function 2`] = ` return hash.ToHashCode(); } }" -`; \ No newline at end of file +`; diff --git a/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap b/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap index 51670f7add..460b551694 100644 --- a/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap +++ b/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap @@ -1,11 +1,14 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`JSON serializer preset should render serialize and deserialize converters 1`] = ` -"[JsonConverter(typeof(NestedTestConverter))] -public class NestedTest +"[JsonConverter(typeof(TestConverter))] +public class Test { private string stringProp; - private Dictionary additionalProperties; + private double? numberProp; + private EnumTest? enumProp; + private NestedTest? objectProp; + private Dictionary? additionalProperties; public string StringProp { @@ -13,28 +16,46 @@ public class NestedTest set { stringProp = value; } } - public Dictionary AdditionalProperties + public double? NumberProp + { + get { return numberProp; } + set { numberProp = value; } + } + + public EnumTest? EnumProp + { + get { return enumProp; } + set { enumProp = value; } + } + + public NestedTest? ObjectProp + { + get { return objectProp; } + set { objectProp = value; } + } + + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } } } -internal class NestedTestConverter : JsonConverter +internal class TestConverter : JsonConverter { public override bool CanConvert(System.Type objectType) { // this converter can be applied to any type return true; } - public override NestedTest Read(ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options) + public override Test Read(ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException(); } - var instance = new NestedTest(); + var instance = new Test(); while (reader.Read()) { @@ -50,58 +71,89 @@ internal class NestedTestConverter : JsonConverter } string propertyName = reader.GetString(); - if (propertyName == \\"stringProp\\") - { - var value = JsonSerializer.Deserialize(ref reader, options); - instance.StringProp = value; - continue; - } - - - - if(instance.AdditionalProperties == null) { instance.AdditionalProperties = new Dictionary(); } - var deserializedValue = JsonSerializer.Deserialize(ref reader, options); - instance.AdditionalProperties.Add(propertyName, deserializedValue); - continue; + if (propertyName == \\"string prop\\") + { + var value = JsonSerializer.Deserialize(ref reader, options); + instance.stringProp = value; + continue; + } + if (propertyName == \\"numberProp\\") + { + var value = JsonSerializer.Deserialize(ref reader, options); + instance.numberProp = value; + continue; + } + if (propertyName == \\"enumProp\\") + { + var value = EnumTestExtension.ToEnumTest(JsonSerializer.Deserialize(ref reader, options)); + instance.enumProp = value; + continue; + } + if (propertyName == \\"objectProp\\") + { + var value = JsonSerializer.Deserialize(ref reader, options); + instance.objectProp = value; + continue; + } + if(instance.additionalProperties == null) { instance.additionalProperties = new Dictionary(); } + var deserializedValue = JsonSerializer.Deserialize?>(ref reader, options); + instance.additionalProperties.Add(propertyName, deserializedValue); + continue; } throw new JsonException(); } - public override void Write(Utf8JsonWriter writer, NestedTest value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, Test value, JsonSerializerOptions options) { if (value == null) { JsonSerializer.Serialize(writer, null, options); return; } - var properties = value.GetType().GetProperties().Where(prop => prop.Name != \\"AdditionalProperties\\"); + var properties = value.GetType().GetProperties().Where(prop => additionalProperties); writer.WriteStartObject(); - if(value.StringProp != null) { + if(value.stringProp != null) { // write property name and let the serializer serialize the value itself - writer.WritePropertyName(\\"stringProp\\"); - JsonSerializer.Serialize(writer, value.StringProp, options); + writer.WritePropertyName(\\"string prop\\"); + JsonSerializer.Serialize(writer, value.stringProp, options); } - - - - - // Unwrap additional properties in object - if (value.AdditionalProperties != null) { - foreach (var additionalProperty in value.AdditionalProperties) + if(value.numberProp != null) { + // write property name and let the serializer serialize the value itself + writer.WritePropertyName(\\"numberProp\\"); + JsonSerializer.Serialize(writer, value.numberProp, options); + } + if(value.enumProp != null) { + // write property name and let the serializer serialize the value itself + writer.WritePropertyName(\\"enumProp\\"); + JsonSerializer.Serialize(writer, EnumTest?.GetValue(), options); + } + if(value.objectProp != null) { + // write property name and let the serializer serialize the value itself + writer.WritePropertyName(\\"objectProp\\"); + JsonSerializer.Serialize(writer, value.objectProp, options); + } + // Unwrap dictionary properties + if (value.additionalProperties != null) { + foreach (var unwrappedProperty in value.additionalProperties) { - //Ignore any additional properties which might already be part of the core properties - if (properties.Any(prop => prop.Name == additionalProperty.Key)) + // Ignore any unwrapped properties which might already be part of the core properties + if (properties.Any(prop => prop.Name == unwrappedProperty.Key)) { continue; } - // write property name and let the serializer serialize the value itself - writer.WritePropertyName(additionalProperty.Key); - JsonSerializer.Serialize(writer, additionalProperty.Value, options); + // Write property name and let the serializer serialize the value itself + writer.WritePropertyName(unwrappedProperty.Key); + JsonSerializer.Serialize(writer, unwrappedProperty.Value, options); } + }if(value.additionalProperties != null) { + // write property name and let the serializer serialize the value itself + writer.WritePropertyName(\\"additionalProperties\\"); + JsonSerializer.Serialize(writer, value.additionalProperties, options); } + writer.WriteEndObject(); } @@ -110,68 +162,78 @@ internal class NestedTestConverter : JsonConverter `; exports[`JSON serializer preset should render serialize and deserialize converters 2`] = ` -"[JsonConverter(typeof(TestConverter))] -public class Test +"public enum EnumTest { - private string stringProp; - private double? numberProp; - private EnumTest enumProp; - private NestedTest objectProp; - private Dictionary additionalProperties; - private Dictionary sTestPatternProperties; + SOME_SPACE_ENUM_SPACE_STRING, + RESERVED_TRUE, + CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT, + NUMBER_2 +} - public string StringProp +public static class EnumTestExtensions +{ + public static dynamic? GetValue(this EnumTest enumValue) { - get { return stringProp; } - set { stringProp = value; } + switch (enumValue) + { + case EnumTest.SOME_SPACE_ENUM_SPACE_STRING: return \\"Some enum String\\"; + case EnumTest.RESERVED_TRUE: return true; + case EnumTest.CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT: return \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\"; + case EnumTest.NUMBER_2: return 2; + } + return null; } - public double? NumberProp + public static EnumTest? ToEnumTest(dynamic? value) { - get { return numberProp; } - set { numberProp = value; } + switch (value) + { + case \\"Some enum String\\": return EnumTest.SOME_SPACE_ENUM_SPACE_STRING; + case true: return EnumTest.RESERVED_TRUE; + case \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\": return EnumTest.CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT; + case 2: return EnumTest.NUMBER_2; + } + return null; } +} +" +`; - public EnumTest EnumProp - { - get { return enumProp; } - set { enumProp = value; } - } +exports[`JSON serializer preset should render serialize and deserialize converters 3`] = ` +"[JsonConverter(typeof(NestedTestConverter))] +public class NestedTest +{ + private string? stringProp; + private Dictionary? additionalProperties; - public NestedTest ObjectProp + public string? StringProp { - get { return objectProp; } - set { objectProp = value; } + get { return stringProp; } + set { stringProp = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } } - - public Dictionary STestPatternProperties - { - get { return sTestPatternProperties; } - set { sTestPatternProperties = value; } - } } -internal class TestConverter : JsonConverter +internal class NestedTestConverter : JsonConverter { public override bool CanConvert(System.Type objectType) { // this converter can be applied to any type return true; } - public override Test Read(ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options) + public override NestedTest Read(ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException(); } - var instance = new Test(); + var instance = new NestedTest(); while (reader.Read()) { @@ -187,110 +249,55 @@ internal class TestConverter : JsonConverter } string propertyName = reader.GetString(); - if (propertyName == \\"string prop\\") - { - var value = JsonSerializer.Deserialize(ref reader, options); - instance.StringProp = value; - continue; - } - if (propertyName == \\"numberProp\\") - { - var value = JsonSerializer.Deserialize(ref reader, options); - instance.NumberProp = value; - continue; - } - if (propertyName == \\"enumProp\\") - { - var value = EnumTestExtensions.ToEnumTest(JsonSerializer.Deserialize(ref reader, options)); - instance.EnumProp = value; - continue; - } - if (propertyName == \\"objectProp\\") - { - var value = JsonSerializer.Deserialize(ref reader, options); - instance.ObjectProp = value; - continue; - } - - if(instance.STestPatternProperties == null) { instance.STestPatternProperties = new Dictionary(); } - var match = Regex.Match(propertyName, @\\"^S(.?)test\\"); - if (match.Success) - { - var deserializedValue = JsonSerializer.Deserialize(ref reader, options); - instance.STestPatternProperties.Add(propertyName, deserializedValue); - continue; - } - - if(instance.AdditionalProperties == null) { instance.AdditionalProperties = new Dictionary(); } - var deserializedValue = JsonSerializer.Deserialize(ref reader, options); - instance.AdditionalProperties.Add(propertyName, deserializedValue); - continue; + if (propertyName == \\"stringProp\\") + { + var value = JsonSerializer.Deserialize(ref reader, options); + instance.stringProp = value; + continue; + } + if(instance.additionalProperties == null) { instance.additionalProperties = new Dictionary(); } + var deserializedValue = JsonSerializer.Deserialize?>(ref reader, options); + instance.additionalProperties.Add(propertyName, deserializedValue); + continue; } throw new JsonException(); } - public override void Write(Utf8JsonWriter writer, Test value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, NestedTest value, JsonSerializerOptions options) { if (value == null) { JsonSerializer.Serialize(writer, null, options); return; } - var properties = value.GetType().GetProperties().Where(prop => prop.Name != \\"AdditionalProperties\\" && prop.Name != \\"STestPatternProperties\\"); + var properties = value.GetType().GetProperties().Where(prop => additionalProperties); writer.WriteStartObject(); - if(value.StringProp != null) { + if(value.stringProp != null) { // write property name and let the serializer serialize the value itself - writer.WritePropertyName(\\"string prop\\"); - JsonSerializer.Serialize(writer, value.StringProp, options); - } - if(value.NumberProp != null) { - // write property name and let the serializer serialize the value itself - writer.WritePropertyName(\\"numberProp\\"); - JsonSerializer.Serialize(writer, value.NumberProp, options); - } - if(value.EnumProp != null) { - // write property name and let the serializer serialize the value itself - writer.WritePropertyName(\\"enumProp\\"); - JsonSerializer.Serialize(writer, value.EnumProp.GetValue(), options); - } - if(value.ObjectProp != null) { - // write property name and let the serializer serialize the value itself - writer.WritePropertyName(\\"objectProp\\"); - JsonSerializer.Serialize(writer, value.ObjectProp, options); + writer.WritePropertyName(\\"stringProp\\"); + JsonSerializer.Serialize(writer, value.stringProp, options); } - - - // Unwrap pattern properties in object - if(value.STestPatternProperties != null) { - foreach (var patternProp in value.STestPatternProperties) + // Unwrap dictionary properties + if (value.additionalProperties != null) { + foreach (var unwrappedProperty in value.additionalProperties) { - //Ignore any pattern properties which might already be part of the core properties - if (properties.Any(prop => prop.Name == patternProp.Key)) + // Ignore any unwrapped properties which might already be part of the core properties + if (properties.Any(prop => prop.Name == unwrappedProperty.Key)) { continue; } - // write property name and let the serializer serialize the value itself - writer.WritePropertyName(patternProp.Key); - JsonSerializer.Serialize(writer, patternProp.Value, options); + // Write property name and let the serializer serialize the value itself + writer.WritePropertyName(unwrappedProperty.Key); + JsonSerializer.Serialize(writer, unwrappedProperty.Value, options); } + }if(value.additionalProperties != null) { + // write property name and let the serializer serialize the value itself + writer.WritePropertyName(\\"additionalProperties\\"); + JsonSerializer.Serialize(writer, value.additionalProperties, options); } - // Unwrap additional properties in object - if (value.AdditionalProperties != null) { - foreach (var additionalProperty in value.AdditionalProperties) - { - //Ignore any additional properties which might already be part of the core properties - if (properties.Any(prop => prop.Name == additionalProperty.Key)) - { - continue; - } - // write property name and let the serializer serialize the value itself - writer.WritePropertyName(additionalProperty.Key); - JsonSerializer.Serialize(writer, additionalProperty.Value, options); - } - } writer.WriteEndObject(); } @@ -298,41 +305,3 @@ internal class TestConverter : JsonConverter } " `; - -exports[`JSON serializer preset should render serialize and deserialize converters 3`] = ` -"public enum EnumTest -{ - SomeEnumString, - True, - TestTest, - Number_2 -} - -public static class EnumTestExtensions -{ - public static dynamic GetValue(this EnumTest enumValue) - { - switch (enumValue) - { - case EnumTest.SomeEnumString: return \\"Some enum String\\"; - case EnumTest.True: return true; - case EnumTest.TestTest: return \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\"; - case EnumTest.Number_2: return 2; - } - return null; - } - - public static EnumTest? ToEnumTest(dynamic value) - { - switch (value) - { - case \\"Some enum String\\": return EnumTest.SomeEnumString; - case true: return EnumTest.True; - case \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\": return EnumTest.TestTest; - case 2: return EnumTest.Number_2; - } - return null; - } -} -" -`; diff --git a/test/generators/csharp/presets/__snapshots__/NewtonsoftSerializerPreset.spec.ts.snap b/test/generators/csharp/presets/__snapshots__/NewtonsoftSerializerPreset.spec.ts.snap new file mode 100644 index 0000000000..3ea6419f36 --- /dev/null +++ b/test/generators/csharp/presets/__snapshots__/NewtonsoftSerializerPreset.spec.ts.snap @@ -0,0 +1,218 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Newtonsoft JSON serializer preset should render serialize and deserialize converters 1`] = ` +"[JsonConverter(typeof(TestConverter))] +public class Test +{ + private string stringProp; + private double? numberProp; + private EnumTest? enumProp; + private NestedTest? objectProp; + private Dictionary? additionalProperties; + + public string StringProp + { + get { return stringProp; } + set { stringProp = value; } + } + + public double? NumberProp + { + get { return numberProp; } + set { numberProp = value; } + } + + public EnumTest? EnumProp + { + get { return enumProp; } + set { enumProp = value; } + } + + public NestedTest? ObjectProp + { + get { return objectProp; } + set { objectProp = value; } + } + + public Dictionary? AdditionalProperties + { + get { return additionalProperties; } + set { additionalProperties = value; } + } +} + +public class TestConverter : JsonConverter +{ + public override Test ReadJson(JsonReader reader, System.Type objectType, Test existingValue, bool hasExistingValue, JsonSerializer serializer) +{ + JObject jo = JObject.Load(reader); + Test value = new Test(); + + if(jo[\\"string prop\\"] != null) { + value.StringProp = jo[\\"string prop\\"].ToObject(serializer); +} +if(jo[\\"numberProp\\"] != null) { + value.NumberProp = jo[\\"numberProp\\"].ToObject(serializer); +} +if(jo[\\"enumProp\\"] != null) { + value.EnumProp = EnumTestExtensions.ToEnumTest(jo[\\"enumProp\\"]); +} +if(jo[\\"objectProp\\"] != null) { + value.ObjectProp = jo[\\"objectProp\\"].ToObject(serializer); +} + + var additionalProperties = jo.Properties().Where((prop) => prop.Name != \\"string prop\\" || prop.Name != \\"numberProp\\" || prop.Name != \\"enumProp\\" || prop.Name != \\"objectProp\\"); + value.AdditionalProperties = new Dictionary(); + + foreach (var additionalProperty in additionalProperties) + { + value.AdditionalProperties[additionalProperty.Name] = additionalProperty.Value.ToObject(serializer); + } + return value; +} + public override void WriteJson(JsonWriter writer, Test value, JsonSerializer serializer) +{ + JObject jo = new JObject(); + + if (value.StringProp != null) +{ + jo.Add(\\"string prop\\", JToken.FromObject(value.StringProp, serializer)); +} +if (value.NumberProp != null) +{ + jo.Add(\\"numberProp\\", JToken.FromObject(value.NumberProp, serializer)); +} +if (value.EnumProp != null) +{ + var enumValue = EnumTestExtensions.GetValue((EnumTest)value.EnumProp); +var stringEnumValue = enumValue.ToString(); +// C# converts booleans to uppercase True and False, which newtonsoft cannot understand +var jsonStringCompliant = stringEnumValue == \\"True\\" || stringEnumValue == \\"False\\" ? stringEnumValue.ToLower() : stringEnumValue; +var jsonToken = JToken.Parse(jsonStringCompliant); +jo.Add(\\"enumProp\\", jsonToken); +} +if (value.ObjectProp != null) +{ + jo.Add(\\"objectProp\\", JToken.FromObject(value.ObjectProp, serializer)); +} + if (value.AdditionalProperties != null) + { + foreach (var unwrapProperty in value.AdditionalProperties) + { + var hasProp = jo[unwrapProperty.Key]; + if (hasProp != null) continue; + jo.Add(unwrapProperty.Key, JToken.FromObject(unwrapProperty.Value, serializer)); + } +} + + jo.WriteTo(writer); +} + + public override bool CanRead => true; + public override bool CanWrite => true; +}" +`; + +exports[`Newtonsoft JSON serializer preset should render serialize and deserialize converters 2`] = ` +"public enum EnumTest +{ + SOME_SPACE_ENUM_SPACE_STRING, + RESERVED_TRUE, + CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT, + NUMBER_2 +} + +public static class EnumTestExtensions +{ + public static dynamic? GetValue(this EnumTest enumValue) + { + switch (enumValue) + { + case EnumTest.SOME_SPACE_ENUM_SPACE_STRING: return \\"Some enum String\\"; + case EnumTest.RESERVED_TRUE: return true; + case EnumTest.CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT: return \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\"; + case EnumTest.NUMBER_2: return 2; + } + return null; + } + + public static EnumTest? ToEnumTest(dynamic? value) + { + switch (value) + { + case \\"Some enum String\\": return EnumTest.SOME_SPACE_ENUM_SPACE_STRING; + case true: return EnumTest.RESERVED_TRUE; + case \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\": return EnumTest.CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT; + case 2: return EnumTest.NUMBER_2; + } + return null; + } +} +" +`; + +exports[`Newtonsoft JSON serializer preset should render serialize and deserialize converters 3`] = ` +"[JsonConverter(typeof(NestedTestConverter))] +public class NestedTest +{ + private string? stringProp; + private Dictionary? additionalProperties; + + public string? StringProp + { + get { return stringProp; } + set { stringProp = value; } + } + + public Dictionary? AdditionalProperties + { + get { return additionalProperties; } + set { additionalProperties = value; } + } +} + +public class NestedTestConverter : JsonConverter +{ + public override NestedTest ReadJson(JsonReader reader, System.Type objectType, NestedTest existingValue, bool hasExistingValue, JsonSerializer serializer) +{ + JObject jo = JObject.Load(reader); + NestedTest value = new NestedTest(); + + if(jo[\\"stringProp\\"] != null) { + value.StringProp = jo[\\"stringProp\\"].ToObject(serializer); +} + + var additionalProperties = jo.Properties().Where((prop) => prop.Name != \\"stringProp\\"); + value.AdditionalProperties = new Dictionary(); + + foreach (var additionalProperty in additionalProperties) + { + value.AdditionalProperties[additionalProperty.Name] = additionalProperty.Value.ToObject(serializer); + } + return value; +} + public override void WriteJson(JsonWriter writer, NestedTest value, JsonSerializer serializer) +{ + JObject jo = new JObject(); + + if (value.StringProp != null) +{ + jo.Add(\\"stringProp\\", JToken.FromObject(value.StringProp, serializer)); +} + if (value.AdditionalProperties != null) + { + foreach (var unwrapProperty in value.AdditionalProperties) + { + var hasProp = jo[unwrapProperty.Key]; + if (hasProp != null) continue; + jo.Add(unwrapProperty.Key, JToken.FromObject(unwrapProperty.Value, serializer)); + } +} + + jo.WriteTo(writer); +} + + public override bool CanRead => true; + public override bool CanWrite => true; +}" +`; diff --git a/test/generators/dart/DartConstrainer.spec.ts b/test/generators/dart/DartConstrainer.spec.ts new file mode 100644 index 0000000000..a2f54af2bb --- /dev/null +++ b/test/generators/dart/DartConstrainer.spec.ts @@ -0,0 +1,216 @@ +import { DartGenerator, DartOptions } from '../../../src/generators'; +import { DartDefaultTypeMapping } from '../../../src/generators/dart/DartConstrainer'; +import { DartDependencyManager } from '../../../src/generators/dart/DartDependencyManager'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedUnionModel, + InputMetaModel +} from '../../../src/models'; + +describe('DartConstrainer', () => { + const defaultOptions = { + options: DartGenerator.defaultOptions, + dependencyManager: new DartDependencyManager(DartGenerator.defaultOptions) + }; + describe('ObjectModel', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedObjectModel('test', undefined, '', {}); + const type = DartDefaultTypeMapping.Object({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Reference', () => { + test('should render the constrained name as type', () => { + const refModel = new ConstrainedAnyModel('test', undefined, ''); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + '', + refModel + ); + const type = DartDefaultTypeMapping.Reference({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Any', () => { + test('should render type', () => { + const model = new ConstrainedAnyModel('test', undefined, ''); + const type = DartDefaultTypeMapping.Any({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Object'); + }); + }); + describe('Float', () => { + test('should render type', () => { + const model = new ConstrainedFloatModel('test', undefined, ''); + const type = DartDefaultTypeMapping.Float({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('double'); + }); + }); + describe('Integer', () => { + test('should render type', () => { + const model = new ConstrainedIntegerModel('test', undefined, ''); + const type = DartDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('int'); + }); + }); + describe('String', () => { + test('should render type', () => { + const model = new ConstrainedStringModel('test', undefined, ''); + const type = DartDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('String'); + }); + }); + describe('Boolean', () => { + test('should render type', () => { + const model = new ConstrainedBooleanModel('test', undefined, ''); + const type = DartDefaultTypeMapping.Boolean({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('bool'); + }); + }); + + describe('Tuple', () => { + test('should render default type', () => { + const model = new ConstrainedTupleModel('test', undefined, '', []); + const optionsToUse: DartOptions = { ...DartGenerator.defaultOptions }; + const type = DartDefaultTypeMapping.Tuple({ + constrainedModel: model, + options: optionsToUse, + dependencyManager: new DartDependencyManager(optionsToUse) + }); + expect(type).toEqual('List'); + }); + test('should render type with custom collection type', () => { + const model = new ConstrainedTupleModel('test', undefined, '', []); + const optionsToUse: DartOptions = { + ...DartGenerator.defaultOptions, + collectionType: 'List' + }; + const type = DartDefaultTypeMapping.Tuple({ + constrainedModel: model, + options: optionsToUse, + dependencyManager: new DartDependencyManager(optionsToUse) + }); + expect(type).toEqual('List'); + }); + }); + + describe('Array', () => { + test('should render default type', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'string' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const type = DartDefaultTypeMapping.Array({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('List'); + }); + test('should render type with custom collection type', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'string' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const optionsToUse: DartOptions = { + ...DartGenerator.defaultOptions, + collectionType: 'List' + }; + const type = DartDefaultTypeMapping.Array({ + constrainedModel: model, + options: optionsToUse, + dependencyManager: new DartDependencyManager(optionsToUse) + }); + expect(type).toEqual('List'); + }); + }); + + describe('Enum', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedEnumModel('test', undefined, '', []); + const type = DartDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + + describe('Union', () => { + test('should render type', () => { + const model = new ConstrainedUnionModel('test', undefined, '', []); + const type = DartDefaultTypeMapping.Union({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Object'); + }); + }); + + describe('Dictionary', () => { + test('should render type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'string'); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + 'string' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = DartDefaultTypeMapping.Dictionary({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Map'); + }); + }); +}); diff --git a/test/generators/dart/DartFileGenerator.spec.ts b/test/generators/dart/DartFileGenerator.spec.ts deleted file mode 100644 index 5577f7678d..0000000000 --- a/test/generators/dart/DartFileGenerator.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { CommonInputModel, CommonModel, FileHelpers, DartFileGenerator, OutputModel } from '../../../src'; -import * as path from 'path'; - -describe('DartFileGenerator', () => { - afterEach(() => { - jest.restoreAllMocks(); - }); - - describe('generateToFile()', () => { - const doc = { - $id: 'CustomClass', - type: 'object', - additionalProperties: true, - properties: { - someProp: { type: 'string' }, - someEnum: { - $id: 'CustomEnum', - type: 'string', - enum: ['Texas', 'Alabama', 'California'], - } - } - }; - test('should throw accurate error if file cannot be written', async () => { - const generator = new DartFileGenerator(); - const expectedError = new Error('write error'); - jest.spyOn(FileHelpers, 'writerToFileSystem').mockRejectedValue(expectedError); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), '', new CommonInputModel(), [])]); - - await expect(generator.generateToFiles(doc, '/test/', {packageName: 'SomePackage'})).rejects.toEqual(expectedError); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); - }); - test('should try and generate models to files', async () => { - const generator = new DartFileGenerator(); - const outputDir = './test'; - const expectedOutputDirPath = path.resolve(outputDir); - const expectedOutputFilePath = path.resolve(`${outputDir}/test.dart`); - const expectedWriteToFileParameters = [ - 'content', - expectedOutputFilePath, - ]; - jest.spyOn(FileHelpers, 'writerToFileSystem').mockResolvedValue(undefined); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), 'test', new CommonInputModel(), [])]); - - await generator.generateToFiles(doc, expectedOutputDirPath, {packageName: 'SomePackage'}); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); - expect((FileHelpers.writerToFileSystem as jest.Mock).mock.calls[0]).toEqual(expectedWriteToFileParameters); - }); - }); -}); diff --git a/test/generators/dart/DartGenerator.spec.ts b/test/generators/dart/DartGenerator.spec.ts index dc0d070771..ee008ab166 100644 --- a/test/generators/dart/DartGenerator.spec.ts +++ b/test/generators/dart/DartGenerator.spec.ts @@ -1,4 +1,4 @@ -import {DartGenerator} from '../../../src/generators'; +import { DartGenerator } from '../../../src/generators'; describe('DartGenerator', () => { let generator: DartGenerator; @@ -9,174 +9,88 @@ describe('DartGenerator', () => { jest.restoreAllMocks(); }); - test('should not render reserved keyword', async () => { - const doc = { - $id: 'Address', - type: 'object', - properties: { - enum: {type: 'string'}, - reservedEnum: {type: 'string'} - }, - additionalProperties: false - }; - const expected = `class Address { - String? reservedReservedEnum; - String? reservedEnum; - - Address(); -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Address']; - - let classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toEqual(expected); - - classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); - }); - test('should render `class` type', async () => { const doc = { $id: 'Address', type: 'object', properties: { - street_name: {type: 'string'}, - city: {type: 'string', description: 'City description'}, - state: {type: 'string'}, - house_number: {type: 'number'}, - marriage: {type: 'boolean', description: 'Status if marriage live in given house'}, - members: {oneOf: [{type: 'string'}, {type: 'number'}, {type: 'boolean'}],}, - array_type: {type: 'array', items: [{type: 'string'}, {type: 'number'}]}, + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const expected = `class Address { - String? streetName; - String? city; - String? state; - double? houseNumber; - bool? marriage; - Object? members; - List? arrayType; - Map? sTestPatternProperties; - - Address(); -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Address']; - let classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toEqual(expected); - classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); }); test('should render `enum` type (string type)', async () => { const doc = { $id: 'States', type: 'string', - enum: ['Texas', 'Alabama', 'California', 'New York'], + enum: ['Texas', 'Alabama', 'California', 'New York'] }; - const expected = `enum States { - Texas, Alabama, California, New York -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['States']; - - let enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); }); test('should render `enum` type (integer type)', async () => { const doc = { $id: 'Numbers', type: 'integer', - enum: [0, 1, 2, 3], + enum: [0, 1, 2, 3] }; - const expected = `enum Numbers { - NUMBER_0, NUMBER_1, NUMBER_2, NUMBER_3 -}`; - const inputModel = await generator.process(doc); - const model = inputModel.models['Numbers']; - let enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); }); test('should render custom preset for `enum` type', async () => { const doc = { $id: 'CustomEnum', type: 'string', - enum: ['Texas', 'Alabama', 'California'], + enum: ['Texas', 'Alabama', 'California'] }; - const expected = `@EnumAnnotation -enum CustomEnum { - Texas, Alabama, California -}`; generator = new DartGenerator({ presets: [ { enum: { - self({renderer, content}) { + self({ renderer, content }) { const annotation = renderer.renderAnnotation('EnumAnnotation'); return `${annotation}\n${content}`; - }, + } } } ] }); - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomEnum']; - - let enumModel = await generator.render(model, inputModel); - const expectedDependencies: any[] = []; - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual(expectedDependencies); - - enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual(expectedDependencies); - }); - - test('should render List type for collections', async () => { - const doc = { - $id: 'CustomClass', - type: 'object', - additionalProperties: false, - properties: { - arrayType: {type: 'array'}, - } - }; - const expected = `class CustomClass { - List? arrayType; - - CustomClass(); -}`; - const expectedDependencies: any[] = []; - - generator = new DartGenerator({collectionType: 'List'}); - - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomClass']; - - const classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual(expectedDependencies); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render models and their dependencies', async () => { @@ -184,50 +98,40 @@ enum CustomEnum { $id: 'Address', type: 'object', properties: { - street_name: {type: 'string'}, - city: {type: 'string', description: 'City description'}, - state: {type: 'string'}, - house_number: {type: 'number'}, - marriage: {type: 'boolean', description: 'Status if marriage live in given house'}, - members: {oneOf: [{type: 'string'}, {type: 'number'}, {type: 'boolean'}],}, - array_type: {type: 'array', items: [{type: 'string'}, {type: 'number'}]}, - other_model: {type: 'object', $id: 'OtherModel', properties: {street_name: {type: 'string'}}}, + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + other_model: { + type: 'object', + $id: 'OtherModel', + properties: { street_name: { type: 'string' } } + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const config = {packageName: 'test.package'}; + + const config = { packageName: 'test.package' }; + const models = await generator.generateCompleteModels(doc, config); expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); expect(models[1].result).toMatchSnapshot(); }); - test('should throw error when reserved keyword is used for package name', async () => { - const doc = { - $id: 'Address', - type: 'object', - properties: { - street_name: {type: 'string'}, - city: {type: 'string', description: 'City description'}, - state: {type: 'string'}, - house_number: {type: 'number'}, - marriage: {type: 'boolean', description: 'Status if marriage live in given house'}, - members: {oneOf: [{type: 'string'}, {type: 'number'}, {type: 'boolean'}],}, - array_type: {type: 'array', items: [{type: 'string'}, {type: 'number'}]}, - }, - patternProperties: { - '^S(.?*)test&': { - type: 'string' - } - }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], - }; - const config = {packageName: 'interface'}; - const expectedError = new Error('You cannot use reserved Dart keyword (interface) as package name, please use another.'); - await expect(generator.generateCompleteModels(doc, config)).rejects.toEqual(expectedError); - }); }); diff --git a/test/generators/dart/DartRenderer.spec.ts b/test/generators/dart/DartRenderer.spec.ts index b3cbefbc35..bc41f65695 100644 --- a/test/generators/dart/DartRenderer.spec.ts +++ b/test/generators/dart/DartRenderer.spec.ts @@ -1,128 +1,23 @@ -import { defaultGeneratorOptions, DartGenerator } from '../../../src/generators'; +import { + defaultGeneratorOptions, + DartGenerator +} from '../../../src/generators'; +import { DartDependencyManager } from '../../../src/generators/dart/DartDependencyManager'; import { DartRenderer } from '../../../src/generators/dart/DartRenderer'; -import { CommonInputModel, CommonModel } from '../../../src/models'; -class MockDartRenderer extends DartRenderer { - -} +import { ConstrainedObjectModel, InputMetaModel } from '../../../src/models'; +class MockDartRenderer extends DartRenderer {} describe('DartRenderer', () => { - let renderer: DartRenderer; + let renderer: DartRenderer; beforeEach(() => { - renderer = new MockDartRenderer(DartGenerator.defaultOptions, new DartGenerator(), [], new CommonModel(), new CommonInputModel()); - }); - - describe('nameType()', () => { - test('should name the type', () => { - const name = renderer.nameType('type__someType'); - expect(name).toEqual('TypeSomeType'); - }); - test('should render reserved type keyword correctly', () => { - const name = renderer.nameType('enum'); - expect(name).toEqual('Enum'); - }); - }); - - describe('nameProperty()', () => { - test('should name the property', () => { - const name = renderer.nameProperty('property__someProperty'); - expect(name).toEqual('propertySomeProperty'); - }); - test('should render reserved property keyword correctly', () => { - const name = renderer.nameProperty('enum'); - expect(name).toEqual('reservedEnum'); - }); - }); - describe('renderType()', () => { - test('Should render refs with pascal case', () => { - const model = new CommonModel(); - model.$ref = ''; - expect(renderer.renderType(model)).toEqual('AnonymousSchema_1'); - }); + renderer = new MockDartRenderer( + DartGenerator.defaultOptions, + new DartGenerator(), + [], + new ConstrainedObjectModel('', undefined, '', {}), + new InputMetaModel(), + new DartDependencyManager(DartGenerator.defaultOptions) + ); }); - - describe('toJavaType()', () => { - test('Should be able to return long', () => { - expect(renderer.toDartType('long', new CommonModel())).toEqual('int'); - expect(renderer.toDartType('int64', new CommonModel())).toEqual('int'); - }); - test('Should be able to return date', () => { - expect(renderer.toDartType('date', new CommonModel())).toEqual('DateTime'); - }); - test('Should be able to return time', () => { - expect(renderer.toDartType('time', new CommonModel())).toEqual('DateTime'); - }); - test('Should be able to return offset date time', () => { - expect(renderer.toDartType('dateTime', new CommonModel())).toEqual('DateTime'); - expect(renderer.toDartType('date-time', new CommonModel())).toEqual('DateTime'); - }); - test('Should be able to return float', () => { - expect(renderer.toDartType('float', new CommonModel())).toEqual('double'); - }); - test('Should be able to return byte array', () => { - expect(renderer.toDartType('binary', new CommonModel())).toEqual('byte[]'); - }); - test('Should render matching tuple types as is', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - }, - { - type: 'string' - } - ] - }); - expect(renderer.toDartType('array', model)).toEqual('List'); - }); - test('Should render mismatching tuple types as Object', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - }, - { - type: 'number' - } - ] - }); - expect(renderer.toDartType('array', model)).toEqual('List'); - }); - test('Should render matching tuple and additionalItem types', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - } - ], - additionalItems: { - type: 'string' - } - }); - expect(renderer.toDartType('array', model)).toEqual('List'); - }); - test('Should render Object for tuple and additionalItem type mismatch', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - } - ], - additionalItems: { - type: 'number' - } - }); - expect(renderer.toDartType('array', model)).toEqual('List'); - }); - }); - - describe('toClassType()', () => { - test('Should be able to return long object', () => { - expect(renderer.toClassType('long')).toEqual('int'); - }); - test('Should be able to return float object', () => { - expect(renderer.toClassType('float')).toEqual('double'); - }); - }); - describe('renderComments()', () => { test('Should be able to render comments', () => { expect(renderer.renderComments('someComment')).toEqual(`/** @@ -133,7 +28,9 @@ describe('DartRenderer', () => { describe('renderAnnotation()', () => { test('Should be able to render multiple annotations', () => { - expect(renderer.renderAnnotation('someComment', {test: 'test2'})).toEqual('@SomeComment(test=test2)'); + expect( + renderer.renderAnnotation('someComment', { test: 'test2' }) + ).toEqual('@SomeComment(test=test2)'); }); }); }); diff --git a/test/generators/dart/__snapshots__/DartGenerator.spec.ts.snap b/test/generators/dart/__snapshots__/DartGenerator.spec.ts.snap index d3fed2efc3..f854488fa2 100644 --- a/test/generators/dart/__snapshots__/DartGenerator.spec.ts.snap +++ b/test/generators/dart/__snapshots__/DartGenerator.spec.ts.snap @@ -1,5 +1,39 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`DartGenerator should render \`class\` type 1`] = ` +"class Address { + String? streetName; + String? city; + String? state; + double? houseNumber; + bool? marriage; + Object? members; + List? arrayType; + Map? additionalProperties; + + Address(); +}" +`; + +exports[`DartGenerator should render \`enum\` type (integer type) 1`] = ` +"enum Numbers { + 0, 1, 2, 3 +}" +`; + +exports[`DartGenerator should render \`enum\` type (string type) 1`] = ` +"enum States { + \\"Texas\\", \\"Alabama\\", \\"California\\", \\"New York\\" +}" +`; + +exports[`DartGenerator should render custom preset for \`enum\` type 1`] = ` +"@EnumAnnotation +enum CustomEnum { + \\"Texas\\", \\"Alabama\\", \\"California\\" +}" +`; + exports[`DartGenerator should render models and their dependencies 1`] = ` "import 'package:test.package/other_model.dart'; @@ -12,7 +46,7 @@ exports[`DartGenerator should render models and their dependencies 1`] = ` Object? members; List? arrayType; OtherModel? otherModel; - Map? sTestPatternProperties; + Map? additionalProperties; Address(); }" @@ -23,9 +57,8 @@ exports[`DartGenerator should render models and their dependencies 2`] = ` class OtherModel { String? streetName; + Map? additionalProperties; OtherModel(); }" `; - - diff --git a/test/generators/dart/constrainer/EnumConstrainer.spec.ts b/test/generators/dart/constrainer/EnumConstrainer.spec.ts new file mode 100644 index 0000000000..bb26864b4b --- /dev/null +++ b/test/generators/dart/constrainer/EnumConstrainer.spec.ts @@ -0,0 +1,167 @@ +import { DartDefaultConstraints } from '../../../../src/generators/dart/DartConstrainer'; +import { EnumModel } from '../../../../src/models/MetaModel'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../../src'; +import { + defaultEnumKeyConstraints, + ModelEnumKeyConstraints, + DefaultEnumKeyConstraints +} from '../../../../src/generators/dart/constrainer/EnumConstrainer'; +describe('EnumConstrainer', () => { + const enumModel = new EnumModel('test', undefined, []); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [] + ); + + describe('enum keys', () => { + test('should never render special chars', () => { + const constrainedKey = DartDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '%' + }); + expect(constrainedKey).toEqual('PERCENT'); + }); + test('should not render number as start char', () => { + const constrainedKey = DartDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '1' + }); + expect(constrainedKey).toEqual('NUMBER_1'); + }); + test('should not contain duplicate keys', () => { + const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel( + 'EMPTY', + 'return' + ); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [existingConstrainedEnumValueModel] + ); + const constrainedKey = DartDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('RESERVED_EMPTY'); + }); + test('should never contain empty keys', () => { + const constrainedKey = DartDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('EMPTY'); + }); + test('should use constant naming format', () => { + const constrainedKey = DartDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual( + 'SOME_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2' + ); + }); + test('should never render reserved keywords', () => { + const constrainedKey = DartDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'return' + }); + expect(constrainedKey).toEqual('RESERVED_RETURN'); + }); + }); + describe('enum values', () => { + test('should render string values', () => { + const constrainedValue = DartDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 'string value' + }); + expect(constrainedValue).toEqual('"string value"'); + }); + test('should render boolean values', () => { + const constrainedValue = DartDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: true + }); + expect(constrainedValue).toEqual('"true"'); + }); + test('should render numbers', () => { + const constrainedValue = DartDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 123 + }); + expect(constrainedValue).toEqual(123); + }); + test('should render object', () => { + const constrainedValue = DartDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: { test: 'test' } + }); + expect(constrainedValue).toEqual('"{\\"test\\":\\"test\\"}"'); + }); + test('should render unknown value', () => { + const constrainedValue = DartDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: undefined + }); + expect(constrainedValue).toEqual('"undefined"'); + }); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks for enum key', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultEnumKeyConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks for enum key', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultEnumKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_DUPLICATE_KEYS'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultEnumKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/dart/constrainer/ModelNameConstrainer.spec.ts b/test/generators/dart/constrainer/ModelNameConstrainer.spec.ts new file mode 100644 index 0000000000..1a6ad450a6 --- /dev/null +++ b/test/generators/dart/constrainer/ModelNameConstrainer.spec.ts @@ -0,0 +1,71 @@ +import { DartDefaultConstraints } from '../../../../src/generators/dart/DartConstrainer'; +import { + DefaultModelNameConstraints, + defaultModelNameConstraints, + ModelNameConstraints +} from '../../../../src/generators/dart/constrainer/ModelNameConstrainer'; +describe('ModelNameConstrainer', () => { + test('should never render special chars', () => { + const constrainedKey = DartDefaultConstraints.modelName({ modelName: '%' }); + expect(constrainedKey).toEqual('Percent'); + }); + test('should never render number as start char', () => { + const constrainedKey = DartDefaultConstraints.modelName({ modelName: '1' }); + expect(constrainedKey).toEqual('Number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = DartDefaultConstraints.modelName({ modelName: '' }); + expect(constrainedKey).toEqual('Empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = DartDefaultConstraints.modelName({ + modelName: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual( + 'SomeSpaceWeirdUnderscoreValueExclamationQuotationHash_2' + ); + }); + test('should never render reserved keywords', () => { + const constrainedKey = DartDefaultConstraints.modelName({ + modelName: 'return' + }); + expect(constrainedKey).toEqual('ReservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: ModelNameConstraints = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultModelNameConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ modelName: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultModelNameConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultModelNameConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultModelNameConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ modelName: '' }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/dart/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/dart/constrainer/PropertyKeyConstrainer.spec.ts new file mode 100644 index 0000000000..dd16f45f8b --- /dev/null +++ b/test/generators/dart/constrainer/PropertyKeyConstrainer.spec.ts @@ -0,0 +1,183 @@ +import { DartDefaultConstraints } from '../../../../src/generators/dart/DartConstrainer'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ObjectModel, + ObjectPropertyModel +} from '../../../../src'; +import { + DefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints, + PropertyKeyConstraintOptions +} from '../../../../src/generators/dart/constrainer/PropertyKeyConstrainer'; +describe('PropertyKeyConstrainer', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + + const constrainPropertyName = (propertyName: string) => { + const objectPropertyModel = new ObjectPropertyModel( + propertyName, + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + return DartDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + }; + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('should never render special chars', () => { + const constrainedKey = constrainPropertyName('%'); + expect(constrainedKey).toEqual('percent'); + }); + test('should not render number as start char', () => { + const constrainedKey = constrainPropertyName('1'); + expect(constrainedKey).toEqual('number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = constrainPropertyName(''); + expect(constrainedKey).toEqual('empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = constrainPropertyName('some weird_value!"#2'); + expect(constrainedKey).toEqual('someWeirdValueExclamationQuotationHash_2'); + }); + test('should not contain duplicate properties', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + const objectPropertyModel = new ObjectPropertyModel( + 'reservedReturn', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + 'reservedReturn', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const objectPropertyModel2 = new ObjectPropertyModel( + 'return', + false, + objectModel + ); + const constrainedObjectPropertyModel2 = new ConstrainedObjectPropertyModel( + 'return', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainedObjectModel.properties['reservedReturn'] = + constrainedObjectPropertyModel; + constrainedObjectModel.properties['return'] = + constrainedObjectPropertyModel2; + const constrainedKey = DartDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel: objectPropertyModel2, + constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + }); + expect(constrainedKey).toEqual('reservedReservedReturn'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = constrainPropertyName('return'); + expect(constrainedKey).toEqual('reservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultPropertyKeyConstraints( + mockedConstraintCallbacks + ); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_RESERVED_KEYWORDS'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_DUPLICATE_PROPERTIES') + ]; + const overwrittenDefaultFunction = jest.spyOn( + DefaultPropertyKeyConstraints, + 'NAMING_FORMATTER' + ); + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultPropertyKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const constrainedValue = constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + expect(constrainedValue).toEqual(''); + expect(jestCallback).toHaveBeenCalled(); + expect(overwrittenDefaultFunction).not.toHaveBeenCalled(); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/dart/presets/JsonPreset.spec.ts b/test/generators/dart/presets/JsonPreset.spec.ts index b329849a0c..f43ddac456 100644 --- a/test/generators/dart/presets/JsonPreset.spec.ts +++ b/test/generators/dart/presets/JsonPreset.spec.ts @@ -1,3 +1,9 @@ +import { + ConstrainedFloatModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + InputMetaModel +} from '../../../../src'; import { DartGenerator, DART_JSON_PRESET } from '../../../../src/generators'; describe('DART_JSON_PRESET', () => { @@ -7,30 +13,29 @@ describe('DART_JSON_PRESET', () => { }); test('should render json annotations', async () => { - const doc = { - $id: 'Clazz', - type: 'object', - properties: { - min_number_prop: { type: 'number' }, - max_number_prop: { type: 'number' }, - }, - }; - const expected = `class Clazz { - double? minNumberProp; - double? maxNumberProp; - - Clazz(); - - factory Clazz.fromJson(Map json) => _$ClazzFromJson(json); - Map toJson() => _$ClazzToJson(this); -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Clazz']; + const model = new ConstrainedObjectModel('Clazz', undefined, 'Clazz', { + minNumberProp: new ConstrainedObjectPropertyModel( + 'minNumberProp', + 'min_number_prop', + false, + new ConstrainedFloatModel('minNumberProp', undefined, 'double') + ), + maxNumberProp: new ConstrainedObjectPropertyModel( + 'maxNumberProp', + 'max_number_prop', + false, + new ConstrainedFloatModel('maxNumberProp', undefined, 'double') + ) + }); + const inputModel = new InputMetaModel(); const classModel = await generator.renderClass(model, inputModel); - const expectedDependencies = ['import \'package:json_annotation/json_annotation.dart\';', 'part \'clazz.g.dart\';', '@JsonSerializable()']; - expect(classModel.result).toEqual(expected); + const expectedDependencies = [ + `import 'package:json_annotation/json_annotation.dart';`, + `part 'clazz.g.dart';`, + '@JsonSerializable()' + ]; + expect(classModel.result).toMatchSnapshot(); expect(classModel.dependencies).toEqual(expectedDependencies); }); }); diff --git a/test/generators/dart/presets/__snapshots__/JsonPreset.spec.ts.snap b/test/generators/dart/presets/__snapshots__/JsonPreset.spec.ts.snap new file mode 100644 index 0000000000..45e85efaf8 --- /dev/null +++ b/test/generators/dart/presets/__snapshots__/JsonPreset.spec.ts.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DART_JSON_PRESET should render json annotations 1`] = ` +"class Clazz { + double? minNumberProp; + double? maxNumberProp; + + Clazz(); + + factory Clazz.fromJson(Map json) => _$ClazzFromJson(json); + Map toJson() => _$ClazzToJson(this); +}" +`; diff --git a/test/generators/go/Constants.spec.ts b/test/generators/go/Constants.spec.ts new file mode 100644 index 0000000000..03bfb69b46 --- /dev/null +++ b/test/generators/go/Constants.spec.ts @@ -0,0 +1,35 @@ +import { isReservedGoKeyword } from '../../../src/generators/go/Constants'; + +describe('Constants', () => { + it('should return true if the word is a reserved keyword', () => { + expect(isReservedGoKeyword('break')).toBe(true); + expect(isReservedGoKeyword('case')).toBe(true); + expect(isReservedGoKeyword('chan')).toBe(true); + expect(isReservedGoKeyword('const')).toBe(true); + expect(isReservedGoKeyword('continue')).toBe(true); + expect(isReservedGoKeyword('default')).toBe(true); + expect(isReservedGoKeyword('defer')).toBe(true); + expect(isReservedGoKeyword('else')).toBe(true); + expect(isReservedGoKeyword('fallthrough')).toBe(true); + expect(isReservedGoKeyword('for')).toBe(true); + expect(isReservedGoKeyword('func')).toBe(true); + expect(isReservedGoKeyword('go')).toBe(true); + expect(isReservedGoKeyword('goto')).toBe(true); + expect(isReservedGoKeyword('if')).toBe(true); + expect(isReservedGoKeyword('import')).toBe(true); + expect(isReservedGoKeyword('interface')).toBe(true); + expect(isReservedGoKeyword('map')).toBe(true); + expect(isReservedGoKeyword('package')).toBe(true); + expect(isReservedGoKeyword('range')).toBe(true); + expect(isReservedGoKeyword('return')).toBe(true); + expect(isReservedGoKeyword('select')).toBe(true); + expect(isReservedGoKeyword('struct')).toBe(true); + expect(isReservedGoKeyword('switch')).toBe(true); + expect(isReservedGoKeyword('type')).toBe(true); + expect(isReservedGoKeyword('var')).toBe(true); + }); + + it('should return false if the word is not a reserved keyword', () => { + expect(isReservedGoKeyword('enum')).toBe(false); + }); +}); diff --git a/test/generators/go/GoConstrainer.spec.ts b/test/generators/go/GoConstrainer.spec.ts new file mode 100644 index 0000000000..54eacc3f7e --- /dev/null +++ b/test/generators/go/GoConstrainer.spec.ts @@ -0,0 +1,176 @@ +import { GoDefaultTypeMapping } from '../../../src/generators/go/GoConstrainer'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedUnionModel, + GoGenerator +} from '../../../src'; +import { GoDependencyManager } from '../../../src/generators/go/GoDependencyManager'; +describe('GoConstrainer', () => { + const defaultOptions = { + options: GoGenerator.defaultOptions, + dependencyManager: new GoDependencyManager(GoGenerator.defaultOptions) + }; + describe('ObjectModel', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedObjectModel('test', undefined, '', {}); + const type = GoDefaultTypeMapping.Object({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Reference', () => { + test('should render the constrained name as type', () => { + const refModel = new ConstrainedAnyModel('test', undefined, ''); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + '', + refModel + ); + const type = GoDefaultTypeMapping.Reference({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Any', () => { + test('should render type', () => { + const model = new ConstrainedAnyModel('test', undefined, ''); + const type = GoDefaultTypeMapping.Any({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('interface{}'); + }); + }); + describe('Float', () => { + test('should render type', () => { + const model = new ConstrainedFloatModel('test', undefined, ''); + const type = GoDefaultTypeMapping.Float({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('float64'); + }); + }); + describe('Integer', () => { + test('should render type', () => { + const model = new ConstrainedIntegerModel('test', undefined, ''); + const type = GoDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('int'); + }); + }); + describe('String', () => { + test('should render type', () => { + const model = new ConstrainedStringModel('test', undefined, ''); + const type = GoDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('string'); + }); + }); + describe('Boolean', () => { + test('should render type', () => { + const model = new ConstrainedBooleanModel('test', undefined, ''); + const type = GoDefaultTypeMapping.Boolean({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('bool'); + }); + }); + + describe('Tuple', () => { + test('should render type', () => { + const model = new ConstrainedTupleModel('test', undefined, '', []); + const type = GoDefaultTypeMapping.Tuple({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('[]interface{}'); + }); + }); + + describe('Array', () => { + test('should render type', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'string' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const type = GoDefaultTypeMapping.Array({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('[]string'); + }); + }); + + describe('Enum', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedEnumModel('test', undefined, '', []); + const type = GoDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + + describe('Union', () => { + test('should render type', () => { + const model = new ConstrainedUnionModel('test', undefined, '', []); + const type = GoDefaultTypeMapping.Union({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('interface{}'); + }); + }); + + describe('Dictionary', () => { + test('should render type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'string'); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + 'string' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = GoDefaultTypeMapping.Dictionary({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('map[string]string'); + }); + }); +}); diff --git a/test/generators/go/GoGenerator.spec.ts b/test/generators/go/GoGenerator.spec.ts index 5979167140..350c158418 100644 --- a/test/generators/go/GoGenerator.spec.ts +++ b/test/generators/go/GoGenerator.spec.ts @@ -5,39 +5,6 @@ describe('GoGenerator', () => { beforeEach(() => { generator = new GoGenerator(); }); - - it('should return true if the word is a reserved keyword', () => { - expect(generator.reservedGoKeyword('break')).toBe(true); - expect(generator.reservedGoKeyword('case')).toBe(true); - expect(generator.reservedGoKeyword('chan')).toBe(true); - expect(generator.reservedGoKeyword('const')).toBe(true); - expect(generator.reservedGoKeyword('continue')).toBe(true); - expect(generator.reservedGoKeyword('default')).toBe(true); - expect(generator.reservedGoKeyword('defer')).toBe(true); - expect(generator.reservedGoKeyword('else')).toBe(true); - expect(generator.reservedGoKeyword('fallthrough')).toBe(true); - expect(generator.reservedGoKeyword('for')).toBe(true); - expect(generator.reservedGoKeyword('func')).toBe(true); - expect(generator.reservedGoKeyword('go')).toBe(true); - expect(generator.reservedGoKeyword('goto')).toBe(true); - expect(generator.reservedGoKeyword('if')).toBe(true); - expect(generator.reservedGoKeyword('import')).toBe(true); - expect(generator.reservedGoKeyword('interface')).toBe(true); - expect(generator.reservedGoKeyword('map')).toBe(true); - expect(generator.reservedGoKeyword('package')).toBe(true); - expect(generator.reservedGoKeyword('range')).toBe(true); - expect(generator.reservedGoKeyword('return')).toBe(true); - expect(generator.reservedGoKeyword('select')).toBe(true); - expect(generator.reservedGoKeyword('struct')).toBe(true); - expect(generator.reservedGoKeyword('switch')).toBe(true); - expect(generator.reservedGoKeyword('type')).toBe(true); - expect(generator.reservedGoKeyword('var')).toBe(true); - }); - - it('should return false if the word is not a reserved keyword', () => { - expect(generator.reservedGoKeyword('enum')).toBe(false); - }); - test('should render `struct` type', async () => { const doc = { $id: '_address', @@ -47,10 +14,18 @@ describe('GoGenerator', () => { city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - tuple_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, - array_type: { type: 'array', items: { type: 'string' } }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + tuple_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + array_type: { type: 'array', items: { type: 'string' } } }, required: ['street_name', 'city', 'state', 'house_number', 'array_type'], additionalProperties: { @@ -60,32 +35,12 @@ describe('GoGenerator', () => { '^S(.?*)test&': { type: 'string' } - }, + } }; - const expected = `// Address represents a Address model. -type Address struct { - StreetName string - City string - State string - HouseNumber float64 - Marriage bool - Members []interface{} - TupleType []interface{} - ArrayType []string - AdditionalProperties map[string]string - STestPatternProperties map[string]string -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['_address']; - - let structModel = await generator.renderStruct(model, inputModel); - expect(structModel.result).toEqual(expected); - expect(structModel.dependencies).toEqual([]); - structModel = await generator.render(model, inputModel); - expect(structModel.result).toEqual(expected); - expect(structModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); }); test('should work custom preset for `struct` type', async () => { @@ -93,93 +48,45 @@ type Address struct { $id: 'CustomStruct', type: 'object', properties: { - property: { type: 'string' }, + property: { type: 'string' } }, additionalProperties: { type: 'string' } }; - const expected = `// CustomStruct represents a CustomStruct model. -type CustomStruct struct { - property string - additionalProperties string -}`; - generator = new GoGenerator({ presets: [ { struct: { - field({ fieldName, field, renderer }) { - return `${fieldName} ${renderer.renderType(field)}`; // private fields - }, + field({ field }) { + return `${field.propertyName} ${field.property.type}`; // private fields + } } } ] }); - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomStruct']; - - const structModel = await generator.render(model, inputModel); - expect(structModel.result).toEqual(expected); - expect(structModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); }); - describe.each([ - { - name: 'with enums sharing same type', - doc: { - $id: 'States', - type: 'string', - enum: ['Texas', 'Alabama', 'California'], - }, - expected: `// States represents an enum of string. -type States string - -const ( - StatesTexas States = "Texas" - StatesAlabama = "Alabama" - StatesCalifornia = "California" -)`, - }, - { - name: 'with enums of mixed types', - doc: { - $id: 'Things', - enum: ['Texas', 1, '1', false, { test: 'test' }], - }, - expected: `// Things represents an enum of mixed types. -type Things interface{}`, - }, - ])('should render `enum` type $name', ({ doc, expected }) => { - test('should not be empty', async () => { - const inputModel = await generator.process(doc); - const model = inputModel.models[doc.$id]; - - let enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); - - enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); - }); + test('should render `enum` with mixed types', async () => { + const doc = { + $id: 'Things', + enum: ['Texas', 1, '1', false, { test: 'test' }] + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); }); test('should work custom preset for `enum` type', async () => { const doc = { $id: 'CustomEnum', type: 'string', - enum: ['Texas', 'Alabama', 'California'], + enum: ['Texas', 'Alabama', 'California'] }; - const expected = `// CustomEnum represents an enum of string. -type CustomEnum string - -const ( - CustomEnumTexas CustomEnum = "Texas" - CustomEnumAlabama = "Alabama" - CustomEnumCalifornia = "California" -)`; generator = new GoGenerator({ presets: [ @@ -187,22 +94,15 @@ const ( enum: { self({ content }) { return content; - }, + } } } ] }); - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomEnum']; - - let enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); - - enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); }); describe('generateCompleteModels()', () => { test('should render models', async () => { @@ -214,17 +114,29 @@ const ( city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, - other_model: { type: 'object', $id: 'OtherModel', properties: { street_name: { type: 'string' } } }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + other_model: { + type: 'object', + $id: 'OtherModel', + properties: { street_name: { type: 'string' } } + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; const config = { packageName: 'some_package' }; const models = await generator.generateCompleteModels(doc, config); @@ -242,26 +154,38 @@ const ( city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, - other_model: { type: 'object', $id: 'OtherModel', properties: { street_name: { type: 'string' } } }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + other_model: { + type: 'object', + $id: 'OtherModel', + properties: { street_name: { type: 'string' } } + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; generator = new GoGenerator({ presets: [ { struct: { self({ renderer, content }) { - renderer.addDependency('time'); + renderer.dependencyManager.addDependency('time'); return content; - }, + } } } ] @@ -271,6 +195,8 @@ const ( expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); expect(models[1].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(['time']); + expect(models[1].dependencies).toEqual(['time']); }); }); }); diff --git a/test/generators/go/GoRenderer.spec.ts b/test/generators/go/GoRenderer.spec.ts index 933a79eab1..1f1c228558 100644 --- a/test/generators/go/GoRenderer.spec.ts +++ b/test/generators/go/GoRenderer.spec.ts @@ -1,72 +1,36 @@ +import { GoDependencyManager } from '../../../src/generators/go/GoDependencyManager'; import { GoGenerator } from '../../../src/generators/go/GoGenerator'; import { GoRenderer } from '../../../src/generators/go/GoRenderer'; -import { CommonInputModel, CommonModel } from '../../../src/models'; -class MockGoRenderer extends GoRenderer { +import { ConstrainedObjectModel, InputMetaModel } from '../../../src/models'; +import { MockGoRenderer } from '../../TestUtils/TestRenderers'; -} describe('GoRenderer', () => { - let renderer: MockGoRenderer; + let renderer: GoRenderer; beforeEach(() => { - renderer = new MockGoRenderer(GoGenerator.defaultOptions, new GoGenerator(), [], new CommonModel(), new CommonInputModel()); + renderer = new MockGoRenderer( + GoGenerator.defaultOptions, + new GoGenerator(), + [], + new ConstrainedObjectModel('', undefined, '', {}), + new InputMetaModel(), + new GoDependencyManager(GoGenerator.defaultOptions) + ); }); - - describe('toGoType()', () => { - test('Should render undefined as interface type', () => { - expect(renderer.toGoType(undefined, new CommonModel())).toEqual('interface{}'); - }); - test('Should render integer as int type', () => { - expect(renderer.toGoType('integer', new CommonModel())).toEqual('int'); - }); - test('Should render number as float64 type', () => { - expect(renderer.toGoType('number', new CommonModel())).toEqual('float64'); - }); - test('Should render array as slice of the type', () => { - const model = new CommonModel(); - model.items = CommonModel.toCommonModel({ type: 'number' }); - expect(renderer.toGoType('array', model)).toEqual('[]float64'); - }); - test('Should render tuple with one type as slice of that type', () => { - const model = new CommonModel(); - model.items = [CommonModel.toCommonModel({ type: 'number' })]; - expect(renderer.toGoType('array', model)).toEqual('[]float64'); - }); - test('Should render tuple with multiple types as slice of interface{}', () => { - const model = new CommonModel(); - model.items = [CommonModel.toCommonModel({ type: 'number' }), CommonModel.toCommonModel({ type: 'string' })]; - expect(renderer.toGoType('array', model)).toEqual('[]interface{}'); - }); - test('Should render object as interface type', () => { - expect(renderer.toGoType('object', new CommonModel())).toEqual('interface{}'); - }); - }); - describe('nameType()', () => { - test('Should call naming convention', () => { - const name = renderer.nameType('type__someType'); - expect(name).toEqual('TypeSomeType'); - }); - }); - describe('nameField()', () => { - test('Should call naming convention', () => { - const name = renderer.nameField('field__someField'); - expect(name).toEqual('FieldSomeField'); - }); - }); - describe('renderType()', () => { - test('Should render refs using type naming convention', () => { - const model = new CommonModel(); - model.$ref = ''; - const renderedType = renderer.renderType(model); - expect(renderedType).toEqual('*AnonymousSchema1'); - }); - test('Should render union types with one type as slice of that type', () => { - const model = CommonModel.toCommonModel({ type: ['number'] }); - const renderedType = renderer.renderType(model); - expect(renderedType).toEqual('[]float64'); - }); - test('Should render union types with multiple types as slice of interface', () => { - const model = CommonModel.toCommonModel({ type: ['number', 'string'] }); - const renderedType = renderer.renderType(model); - expect(renderedType).toEqual('[]interface{}'); + describe('renderComments()', () => { + test('should render single lines correctly', () => { + const lines = 'test'; + const renderedLines = renderer.renderComments(lines); + expect(renderedLines).toMatchSnapshot(); + }); + test('should render multiple lines correctly', () => { + const lines = ['test', 'test2']; + const renderedLines = renderer.renderComments(lines); + expect(renderedLines).toMatchSnapshot(); + }); + test('should render no lines', () => { + const lines: string[] = []; + const renderedLines = renderer.renderComments(lines); + expect(renderedLines).toMatchSnapshot(); }); }); }); diff --git a/test/generators/go/__snapshots__/GoGenerator.spec.ts.snap b/test/generators/go/__snapshots__/GoGenerator.spec.ts.snap index 4a89f5aca1..636dbec177 100644 --- a/test/generators/go/__snapshots__/GoGenerator.spec.ts.snap +++ b/test/generators/go/__snapshots__/GoGenerator.spec.ts.snap @@ -13,11 +13,10 @@ type Address struct { State string HouseNumber float64 Marriage bool - Members []interface{} + Members interface{} ArrayType []interface{} OtherModel *OtherModel - AdditionalProperties map[string][]interface{} - STestPatternProperties map[string]string + AdditionalProperties map[string]interface{} }" `; @@ -30,7 +29,7 @@ import ( // OtherModel represents a OtherModel model. type OtherModel struct { StreetName string - AdditionalProperties map[string][]interface{} + AdditionalProperties map[string]interface{} }" `; @@ -45,11 +44,10 @@ type Address struct { State string HouseNumber float64 Marriage bool - Members []interface{} + Members interface{} ArrayType []interface{} OtherModel *OtherModel - AdditionalProperties map[string][]interface{} - STestPatternProperties map[string]string + AdditionalProperties map[string]interface{} }" `; @@ -60,6 +58,87 @@ package some_package // OtherModel represents a OtherModel model. type OtherModel struct { StreetName string - AdditionalProperties map[string][]interface{} + AdditionalProperties map[string]interface{} +}" +`; + +exports[`GoGenerator should render \`enum\` with mixed types 1`] = ` +"// Things represents an enum of Things. +type Things uint + +const ( + ThingsTexas Things = iota + ThingsNumber_1 + ThingsReservedNumber_1 + ThingsFalse + ThingsCurlyleftQuotationTestQuotationColonQuotationTestQuotationCurlyright +) + +// Value returns the value of the enum. +func (op Things) Value() any { + if op >= Things(len(ThingsValues)) { + return nil + } + return ThingsValues[op] +} + +var ThingsValues = []any{\\"Texas\\",1,\\"1\\",false,{\\"test\\":\\"test\\"}} +var ValuesToThings = map[any]Things{ + ThingsValues[ThingsTexas]: ThingsTexas, + ThingsValues[ThingsNumber_1]: ThingsNumber_1, + ThingsValues[ThingsReservedNumber_1]: ThingsReservedNumber_1, + ThingsValues[ThingsFalse]: ThingsFalse, + ThingsValues[ThingsCurlyleftQuotationTestQuotationColonQuotationTestQuotationCurlyright]: ThingsCurlyleftQuotationTestQuotationColonQuotationTestQuotationCurlyright, +} +" +`; + +exports[`GoGenerator should render \`struct\` type 1`] = ` +"// Address represents a Address model. +type Address struct { + StreetName string + City string + State string + HouseNumber float64 + Marriage bool + Members interface{} + TupleType []interface{} + ArrayType []interface{} + AdditionalProperties map[string]interface{} +}" +`; + +exports[`GoGenerator should work custom preset for \`enum\` type 1`] = ` +"// CustomEnum represents an enum of CustomEnum. +type CustomEnum uint + +const ( + CustomEnumTexas CustomEnum = iota + CustomEnumAlabama + CustomEnumCalifornia +) + +// Value returns the value of the enum. +func (op CustomEnum) Value() any { + if op >= CustomEnum(len(CustomEnumValues)) { + return nil + } + return CustomEnumValues[op] +} + +var CustomEnumValues = []any{\\"Texas\\",\\"Alabama\\",\\"California\\"} +var ValuesToCustomEnum = map[any]CustomEnum{ + CustomEnumValues[CustomEnumTexas]: CustomEnumTexas, + CustomEnumValues[CustomEnumAlabama]: CustomEnumAlabama, + CustomEnumValues[CustomEnumCalifornia]: CustomEnumCalifornia, +} +" +`; + +exports[`GoGenerator should work custom preset for \`struct\` type 1`] = ` +"// CustomStruct represents a CustomStruct model. +type CustomStruct struct { + Property string + AdditionalProperties map[string]string }" `; diff --git a/test/generators/go/__snapshots__/GoRenderer.spec.ts.snap b/test/generators/go/__snapshots__/GoRenderer.spec.ts.snap new file mode 100644 index 0000000000..7f81781ebf --- /dev/null +++ b/test/generators/go/__snapshots__/GoRenderer.spec.ts.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GoRenderer renderComments() should render multiple lines correctly 1`] = ` +"// test +// test2" +`; + +exports[`GoRenderer renderComments() should render no lines 1`] = `""`; + +exports[`GoRenderer renderComments() should render single lines correctly 1`] = `"// test"`; diff --git a/test/generators/go/constrainer/EnumConstrainer.spec.ts b/test/generators/go/constrainer/EnumConstrainer.spec.ts new file mode 100644 index 0000000000..c6d7a8e68d --- /dev/null +++ b/test/generators/go/constrainer/EnumConstrainer.spec.ts @@ -0,0 +1,176 @@ +import { GoDefaultConstraints } from '../../../../src/generators/go/GoConstrainer'; +import { EnumModel } from '../../../../src/models/MetaModel'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../../src'; +import { + defaultEnumKeyConstraints, + ModelEnumKeyConstraints, + DefaultEnumKeyConstraints +} from '../../../../src/generators/go/constrainer/EnumConstrainer'; +describe('EnumConstrainer', () => { + const enumModel = new EnumModel('test', undefined, []); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [] + ); + + describe('enum keys', () => { + test('should never render special chars', () => { + const constrainedKey = GoDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '%' + }); + expect(constrainedKey).toEqual('TestPercent'); + }); + test('should not render number as start char', () => { + const constrainedKey = GoDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '1' + }); + expect(constrainedKey).toEqual('TestNumber_1'); + }); + test('should not contain duplicate keys', () => { + const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel( + 'TestEmpty', + 'TestEmpty' + ); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [existingConstrainedEnumValueModel] + ); + const constrainedKey = GoDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('TestReservedEmpty'); + }); + test('should never contain empty keys', () => { + const constrainedKey = GoDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('TestEmpty'); + }); + test('should use constant naming format', () => { + const constrainedKey = GoDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual( + 'TestSomeSpaceWeirdValueExclamationQuotationHash_2' + ); + }); + test('should never render reserved keywords', () => { + const constrainedKey = GoDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'return' + }); + expect(constrainedKey).toEqual('TestReservedReturn'); + }); + }); + describe('enum values', () => { + test('should render string values', () => { + const constrainedValue = GoDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 'string value' + }); + expect(constrainedValue).toEqual('"string value"'); + }); + test('should render boolean values', () => { + const constrainedValue = GoDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: true + }); + expect(constrainedValue).toEqual('true'); + }); + test('should render numbers', () => { + const constrainedValue = GoDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 123 + }); + expect(constrainedValue).toEqual(123); + }); + test('should render object', () => { + const constrainedValue = GoDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: { test: 'test' } + }); + expect(constrainedValue).toEqual('{"test":"test"}'); + }); + test('should render unknown value', () => { + const constrainedValue = GoDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: undefined + }); + expect(constrainedValue).toEqual(undefined); + }); + }); + describe('custom constraints', () => { + test('should be able to handle undefined', () => { + const constrainFunction = defaultEnumKeyConstraints(undefined); + const value = constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: 'TEST' + }); + expect(value).toEqual('TestTest'); + }); + test('should be able to overwrite all hooks for enum key', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultEnumKeyConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks for enum key', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultEnumKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_DUPLICATE_KEYS'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultEnumKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/go/constrainer/ModelNameConstrainer.spec.ts b/test/generators/go/constrainer/ModelNameConstrainer.spec.ts new file mode 100644 index 0000000000..0be52ccbfe --- /dev/null +++ b/test/generators/go/constrainer/ModelNameConstrainer.spec.ts @@ -0,0 +1,69 @@ +import { GoDefaultConstraints } from '../../../../src/generators/go/GoConstrainer'; +import { + DefaultModelNameConstraints, + defaultModelNameConstraints, + ModelNameConstraints +} from '../../../../src/generators/go/constrainer/ModelNameConstrainer'; +describe('ModelNameConstrainer', () => { + test('should never render special chars', () => { + const constrainedKey = GoDefaultConstraints.modelName({ modelName: '%' }); + expect(constrainedKey).toEqual('Percent'); + }); + test('should never render number as start char', () => { + const constrainedKey = GoDefaultConstraints.modelName({ modelName: '1' }); + expect(constrainedKey).toEqual('Number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = GoDefaultConstraints.modelName({ modelName: '' }); + expect(constrainedKey).toEqual('Empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = GoDefaultConstraints.modelName({ + modelName: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = GoDefaultConstraints.modelName({ + modelName: 'return' + }); + expect(constrainedKey).toEqual('ReservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: ModelNameConstraints = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultModelNameConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ modelName: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultModelNameConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultModelNameConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultModelNameConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ modelName: '' }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/go/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/go/constrainer/PropertyKeyConstrainer.spec.ts new file mode 100644 index 0000000000..d518e456da --- /dev/null +++ b/test/generators/go/constrainer/PropertyKeyConstrainer.spec.ts @@ -0,0 +1,180 @@ +import { GoDefaultConstraints } from '../../../../src/generators/go/GoConstrainer'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ObjectModel, + ObjectPropertyModel +} from '../../../../src'; +import { + DefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints, + PropertyKeyConstraintOptions +} from '../../../../src/generators/go/constrainer/PropertyKeyConstrainer'; +describe('PropertyKeyConstrainer', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + + const constrainPropertyName = (propertyName: string) => { + const objectPropertyModel = new ObjectPropertyModel( + propertyName, + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + return GoDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + }; + + test('should never render special chars', () => { + const constrainedKey = constrainPropertyName('%'); + expect(constrainedKey).toEqual('Percent'); + }); + test('should not render number as start char', () => { + const constrainedKey = constrainPropertyName('1'); + expect(constrainedKey).toEqual('Number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = constrainPropertyName(''); + expect(constrainedKey).toEqual('Empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = constrainPropertyName('some weird_value!"#2'); + expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); + }); + test('should not contain duplicate properties', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + const objectPropertyModel = new ObjectPropertyModel( + 'ReservedReturn', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + 'ReservedReturn', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const objectPropertyModel2 = new ObjectPropertyModel( + 'return', + false, + objectModel + ); + const constrainedObjectPropertyModel2 = new ConstrainedObjectPropertyModel( + 'return', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainedObjectModel.properties['ReservedReturn'] = + constrainedObjectPropertyModel; + constrainedObjectModel.properties['return'] = + constrainedObjectPropertyModel2; + const constrainedKey = GoDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel: objectPropertyModel2, + constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + }); + expect(constrainedKey).toEqual('ReservedReservedReturn'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = constrainPropertyName('return'); + expect(constrainedKey).toEqual('ReservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultPropertyKeyConstraints( + mockedConstraintCallbacks + ); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_RESERVED_KEYWORDS'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_DUPLICATE_PROPERTIES') + ]; + const overwrittenDefaultFunction = jest.spyOn( + DefaultPropertyKeyConstraints, + 'NAMING_FORMATTER' + ); + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultPropertyKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const constrainedValue = constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + expect(constrainedValue).toEqual(''); + expect(jestCallback).toHaveBeenCalled(); + expect(overwrittenDefaultFunction).not.toHaveBeenCalled(); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/java/JavaConstrainer.spec.ts b/test/generators/java/JavaConstrainer.spec.ts new file mode 100644 index 0000000000..84f07ab29d --- /dev/null +++ b/test/generators/java/JavaConstrainer.spec.ts @@ -0,0 +1,436 @@ +import { JavaDefaultTypeMapping } from '../../../src/generators/java/JavaConstrainer'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedEnumValueModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedUnionModel, + JavaGenerator, + JavaOptions +} from '../../../src'; +import { JavaDependencyManager } from '../../../src/generators/java/JavaDependencyManager'; +describe('JavaConstrainer', () => { + const defaultOptions = { + options: JavaGenerator.defaultOptions, + dependencyManager: new JavaDependencyManager(JavaGenerator.defaultOptions) + }; + describe('ObjectModel', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedObjectModel('test', undefined, '', {}); + const type = JavaDefaultTypeMapping.Object({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Reference', () => { + test('should render the constrained name as type', () => { + const refModel = new ConstrainedAnyModel('test', undefined, ''); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + '', + refModel + ); + const type = JavaDefaultTypeMapping.Reference({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Any', () => { + test('should render type', () => { + const model = new ConstrainedAnyModel('test', undefined, ''); + const type = JavaDefaultTypeMapping.Any({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Object'); + }); + }); + describe('Float', () => { + test('should render type', () => { + const model = new ConstrainedFloatModel('test', undefined, ''); + const type = JavaDefaultTypeMapping.Float({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Double'); + }); + test('should render double when original input has number format', () => { + const model = new ConstrainedFloatModel('test', { format: 'float' }, ''); + const type = JavaDefaultTypeMapping.Float({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('float'); + }); + }); + describe('Integer', () => { + test('should render type', () => { + const model = new ConstrainedIntegerModel('test', undefined, ''); + const type = JavaDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Integer'); + }); + test('should render int when original input has integer format', () => { + const model = new ConstrainedIntegerModel( + 'test', + { format: 'integer' }, + '' + ); + const type = JavaDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('int'); + }); + test('should render int when original input has int32 format', () => { + const model = new ConstrainedIntegerModel( + 'test', + { format: 'int32' }, + '' + ); + const type = JavaDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('int'); + }); + test('should render long when original input has long format', () => { + const model = new ConstrainedIntegerModel('test', { format: 'long' }, ''); + const type = JavaDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('long'); + }); + test('should render long when original input has int64 format', () => { + const model = new ConstrainedIntegerModel( + 'test', + { format: 'int64' }, + '' + ); + const type = JavaDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('long'); + }); + }); + describe('String', () => { + test('should render type', () => { + const model = new ConstrainedStringModel('test', undefined, ''); + const type = JavaDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('String'); + }); + test('should render LocalDate when original input has date format', () => { + const model = new ConstrainedStringModel('test', { format: 'date' }, ''); + const type = JavaDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('java.time.LocalDate'); + }); + test('should render OffsetTime when original input has time format', () => { + const model = new ConstrainedStringModel('test', { format: 'time' }, ''); + const type = JavaDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('java.time.OffsetTime'); + }); + test('should render OffsetDateTime when original input has dateTime format', () => { + const model = new ConstrainedStringModel( + 'test', + { format: 'dateTime' }, + '' + ); + const type = JavaDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('java.time.OffsetDateTime'); + }); + test('should render OffsetDateTime when original input has date-time format', () => { + const model = new ConstrainedStringModel( + 'test', + { format: 'date-time' }, + '' + ); + const type = JavaDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('java.time.OffsetDateTime'); + }); + test('should render byte when original input has binary format', () => { + const model = new ConstrainedStringModel( + 'test', + { format: 'binary' }, + '' + ); + const type = JavaDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('byte[]'); + }); + }); + describe('Boolean', () => { + test('should render type', () => { + const model = new ConstrainedBooleanModel('test', undefined, ''); + const type = JavaDefaultTypeMapping.Boolean({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Boolean'); + }); + }); + + describe('Tuple', () => { + test('should render type', () => { + const model = new ConstrainedTupleModel('test', undefined, '', []); + const type = JavaDefaultTypeMapping.Tuple({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Object[]'); + }); + test('should render tuple as list', () => { + const model = new ConstrainedTupleModel('test', undefined, '', []); + const options: JavaOptions = { + ...JavaGenerator.defaultOptions, + collectionType: 'List' + }; + const type = JavaDefaultTypeMapping.Tuple({ + constrainedModel: model, + options, + dependencyManager: new JavaDependencyManager(options) + }); + expect(type).toEqual('List'); + }); + }); + + describe('Array', () => { + test('should render type', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const options: JavaOptions = { + ...JavaGenerator.defaultOptions, + collectionType: 'Array' + }; + const type = JavaDefaultTypeMapping.Array({ + constrainedModel: model, + options, + dependencyManager: new JavaDependencyManager(options) + }); + expect(type).toEqual('String[]'); + }); + test('should render array as a list', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const options: JavaOptions = { + ...JavaGenerator.defaultOptions, + collectionType: 'List' + }; + const type = JavaDefaultTypeMapping.Array({ + constrainedModel: model, + options, + dependencyManager: new JavaDependencyManager(options) + }); + expect(type).toEqual('List'); + }); + }); + + describe('Enum', () => { + test('should render string enum values as String type', () => { + const enumValue = new ConstrainedEnumValueModel('test', 'string type'); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = JavaDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('String'); + }); + test('should render boolean enum values as boolean type', () => { + const enumValue = new ConstrainedEnumValueModel('test', true); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = JavaDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('boolean'); + }); + test('should render generic number enum value with format ', () => { + const enumValue = new ConstrainedEnumValueModel('test', 123); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = JavaDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('int'); + }); + test('should render generic number enum value with float format as float type', () => { + const enumValue = new ConstrainedEnumValueModel('test', 12.0); + const model = new ConstrainedEnumModel('test', { format: 'float' }, '', [ + enumValue + ]); + const type = JavaDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('float'); + }); + test('should render generic number enum value with double format as double type', () => { + const enumValue = new ConstrainedEnumValueModel('test', 12.0); + const model = new ConstrainedEnumModel('test', { format: 'double' }, '', [ + enumValue + ]); + const type = JavaDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('double'); + }); + test('should render object enum value as generic Object', () => { + const enumValue = new ConstrainedEnumValueModel('test', {}); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = JavaDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Object'); + }); + test('should render multiple value types as generic Object', () => { + const enumValue2 = new ConstrainedEnumValueModel('test', true); + const enumValue1 = new ConstrainedEnumValueModel('test', 'string type'); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue1, + enumValue2 + ]); + const type = JavaDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Object'); + }); + test('should render double and integer as double type', () => { + const enumValue2 = new ConstrainedEnumValueModel('test', 123); + const enumValue1 = new ConstrainedEnumValueModel('test', 123.12); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue1, + enumValue2 + ]); + const type = JavaDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('double'); + }); + test('should render int and long as long type', () => { + const enumValue2 = new ConstrainedEnumValueModel('test', 123); + const enumValue1 = new ConstrainedEnumValueModel('test', 123); + const model = new ConstrainedEnumModel('test', { format: 'long' }, '', [ + enumValue1, + enumValue2 + ]); + const type = JavaDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('long'); + }); + }); + + describe('Union', () => { + test('should render type', () => { + const model = new ConstrainedUnionModel('test', undefined, '', []); + const type = JavaDefaultTypeMapping.Union({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Object'); + }); + }); + + describe('Dictionary', () => { + test('should render type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'String'); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = JavaDefaultTypeMapping.Dictionary({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Map'); + }); + test('should not render simple integer type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'String'); + const valueModel = new ConstrainedIntegerModel('test', undefined, 'int'); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = JavaDefaultTypeMapping.Dictionary({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Map'); + }); + }); +}); diff --git a/test/generators/java/JavaFileGenerator.spec.ts b/test/generators/java/JavaFileGenerator.spec.ts deleted file mode 100644 index d3a09957be..0000000000 --- a/test/generators/java/JavaFileGenerator.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { CommonInputModel, CommonModel, FileHelpers, JavaFileGenerator, OutputModel } from '../../../src'; -import * as path from 'path'; - -describe('JavaFileGenerator', () => { - afterEach(() => { - jest.restoreAllMocks(); - }); - - describe('generateToFile()', () => { - const doc = { - $id: 'CustomClass', - type: 'object', - additionalProperties: true, - properties: { - someProp: { type: 'string' }, - someEnum: { - $id: 'CustomEnum', - type: 'string', - enum: ['Texas', 'Alabama', 'California'], - } - } - }; - test('should throw accurate error if file cannot be written', async () => { - const generator = new JavaFileGenerator(); - const expectedError = new Error('write error'); - jest.spyOn(FileHelpers, 'writerToFileSystem').mockRejectedValue(expectedError); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), '', new CommonInputModel(), [])]); - - await expect(generator.generateToFiles(doc, '/test/', {packageName: 'SomePackage'})).rejects.toEqual(expectedError); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); - }); - test('should try and generate models to files', async () => { - const generator = new JavaFileGenerator(); - const outputDir = './test'; - const expectedOutputDirPath = path.resolve(outputDir); - const expectedOutputFilePath = path.resolve(`${outputDir}/Test.java`); - const expectedWriteToFileParameters = [ - 'content', - expectedOutputFilePath, - ]; - jest.spyOn(FileHelpers, 'writerToFileSystem').mockResolvedValue(undefined); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), 'Test', new CommonInputModel(), [])]); - - await generator.generateToFiles(doc, expectedOutputDirPath, {packageName: 'SomePackage'}); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); - expect((FileHelpers.writerToFileSystem as jest.Mock).mock.calls[0]).toEqual(expectedWriteToFileParameters); - }); - }); -}); diff --git a/test/generators/java/JavaGenerator.spec.ts b/test/generators/java/JavaGenerator.spec.ts index 14dfa88760..8e44c82ecb 100644 --- a/test/generators/java/JavaGenerator.spec.ts +++ b/test/generators/java/JavaGenerator.spec.ts @@ -1,4 +1,4 @@ -import { JavaGenerator } from '../../../src/generators'; +import { JavaGenerator } from '../../../src/generators'; describe('JavaGenerator', () => { let generator: JavaGenerator; @@ -19,25 +19,10 @@ describe('JavaGenerator', () => { }, additionalProperties: false }; - const expected = `public class Address { - private String reservedReservedEnum; - private String reservedEnum; - public String getEnum() { return this.reservedReservedEnum; } - public void setEnum(String reservedReservedEnum) { this.reservedReservedEnum = reservedReservedEnum; } - - public String getReservedEnum() { return this.reservedEnum; } - public void setReservedEnum(String reservedEnum) { this.reservedEnum = reservedEnum; } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Address']; - - let classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toEqual(expected); - - classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); }); test('should render `class` type', async () => { const doc = { @@ -48,67 +33,30 @@ describe('JavaGenerator', () => { city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const expected = `public class Address { - private String streetName; - private String city; - private String state; - private Double houseNumber; - private Boolean marriage; - private Object members; - private Object[] arrayType; - private Map additionalProperties; - private Map sTestPatternProperties; - - public String getStreetName() { return this.streetName; } - public void setStreetName(String streetName) { this.streetName = streetName; } - - public String getCity() { return this.city; } - public void setCity(String city) { this.city = city; } - - public String getState() { return this.state; } - public void setState(String state) { this.state = state; } - - public Double getHouseNumber() { return this.houseNumber; } - public void setHouseNumber(Double houseNumber) { this.houseNumber = houseNumber; } - - public Boolean getMarriage() { return this.marriage; } - public void setMarriage(Boolean marriage) { this.marriage = marriage; } - - public Object getMembers() { return this.members; } - public void setMembers(Object members) { this.members = members; } - - public Object[] getArrayType() { return this.arrayType; } - public void setArrayType(Object[] arrayType) { this.arrayType = arrayType; } - - public Map getAdditionalProperties() { return this.additionalProperties; } - public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } - - public Map getSTestPatternProperties() { return this.sTestPatternProperties; } - public void setSTestPatternProperties(Map sTestPatternProperties) { this.sTestPatternProperties = sTestPatternProperties; } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Address']; - - let classModel = await generator.renderClass(model, inputModel); const expectedDependencies = ['import java.util.Map;']; - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual(expectedDependencies); - - classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual(expectedDependencies); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); }); test('should work custom preset for `class` type', async () => { @@ -116,252 +64,109 @@ describe('JavaGenerator', () => { $id: 'CustomClass', type: 'object', properties: { - property: { type: 'string' }, + property: { type: 'string' } } }; - const expected = `public class CustomClass { - @JsonProperty("property") - private String property; - @JsonProperty("additionalProperties") - private Map additionalProperties; - - @JsonProperty("property") - public String getProperty() { return this.property; } - @JsonProperty("property") - public void setProperty(String property) { this.property = property; } - - @JsonProperty("additionalProperties") - public Map getAdditionalProperties() { return this.additionalProperties; } - @JsonProperty("additionalProperties") - public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } -}`; - - generator = new JavaGenerator({ presets: [ - { - class: { - property({ renderer, propertyName, content }) { - const annotation = renderer.renderAnnotation('JsonProperty', `"${propertyName}"`); - return `${annotation}\n${content}`; - }, - getter({ renderer, propertyName, content }) { - const annotation = renderer.renderAnnotation('JsonProperty', `"${propertyName}"`); - return `${annotation}\n${content}`; - }, - setter({ renderer, propertyName, content }) { - const annotation = renderer.renderAnnotation('JsonProperty', `"${propertyName}"`); - return `${annotation}\n${content}`; - }, + generator = new JavaGenerator({ + presets: [ + { + class: { + property({ renderer, property, content }) { + const annotation = renderer.renderAnnotation( + 'JsonProperty', + `"${property.propertyName}"` + ); + return `${annotation}\n${content}`; + }, + getter({ renderer, property, content }) { + const annotation = renderer.renderAnnotation( + 'JsonProperty', + `"${property.propertyName}"` + ); + return `${annotation}\n${content}`; + }, + setter({ renderer, property, content }) { + const annotation = renderer.renderAnnotation( + 'JsonProperty', + `"${property.propertyName}"` + ); + return `${annotation}\n${content}`; + } + } } - } - ] }); - - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomClass']; - - let classModel = await generator.renderClass(model, inputModel); + ] + }); const expectedDependencies = ['import java.util.Map;']; - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual(expectedDependencies); - classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual(expectedDependencies); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); }); test('should render `enum` type (string type)', async () => { const doc = { $id: 'States', type: 'string', - enum: ['Texas', 'Alabama', 'California', 'New York'], + enum: ['Texas', 'Alabama', 'California', 'New York'] }; - const expected = `public enum States { - TEXAS("Texas"), ALABAMA("Alabama"), CALIFORNIA("California"), NEW_YORK("New York"); - - private String value; - - States(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - public static States fromValue(String value) { - for (States e : States.values()) { - if (e.value.equals(value)) { - return e; - } - } - throw new IllegalArgumentException("Unexpected value '" + value + "'"); - } - - @Override - public String toString() { - return String.valueOf(value); - } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['States']; - - let enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies.length).toEqual(0); - - enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies.length).toEqual(0); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render `enum` type (integer type)', async () => { const doc = { $id: 'Numbers', type: 'integer', - enum: [0, 1, 2, 3], + enum: [0, 1, 2, 3] }; - const expected = `public enum Numbers { - NUMBER_0(0), NUMBER_1(1), NUMBER_2(2), NUMBER_3(3); - - private Integer value; - - Numbers(Integer value) { - this.value = value; - } - - public Integer getValue() { - return value; - } - public static Numbers fromValue(Integer value) { - for (Numbers e : Numbers.values()) { - if (e.value.equals(value)) { - return e; - } - } - throw new IllegalArgumentException("Unexpected value '" + value + "'"); - } - - @Override - public String toString() { - return String.valueOf(value); - } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Numbers']; - - let enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies.length).toEqual(0); - - enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies.length).toEqual(0); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render `enum` type (union type)', async () => { const doc = { $id: 'Union', type: ['string', 'integer', 'boolean'], - enum: ['Texas', 'Alabama', 0, 1, '1', true, {test: 'test'}], + enum: ['Texas', 'Alabama', 0, 1, '1', true, { test: 'test' }] }; - const expected = `public enum Union { - TEXAS("Texas"), ALABAMA("Alabama"), NUMBER_0(0), NUMBER_1(1), STRING_1("1"), BOOLEAN_TRUE(true), TEST_TEST("{\\"test\\":\\"test\\"}"); - - private Object value; - - Union(Object value) { - this.value = value; - } - public Object getValue() { - return value; - } - - public static Union fromValue(Object value) { - for (Union e : Union.values()) { - if (e.value.equals(value)) { - return e; - } - } - throw new IllegalArgumentException("Unexpected value '" + value + "'"); - } - - @Override - public String toString() { - return String.valueOf(value); - } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Union']; - - let enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies.length).toEqual(0); - - enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies.length).toEqual(0); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render custom preset for `enum` type', async () => { const doc = { $id: 'CustomEnum', type: 'string', - enum: ['Texas', 'Alabama', 'California'], + enum: ['Texas', 'Alabama', 'California'] }; - const expected = `@EnumAnnotation -public enum CustomEnum { - TEXAS("Texas"), ALABAMA("Alabama"), CALIFORNIA("California"); - private String value; - - CustomEnum(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public static CustomEnum fromValue(String value) { - for (CustomEnum e : CustomEnum.values()) { - if (e.value.equals(value)) { - return e; - } - } - throw new IllegalArgumentException("Unexpected value '" + value + "'"); - } - - @Override - public String toString() { - return String.valueOf(value); - } -}`; - - generator = new JavaGenerator({ presets: [ - { - enum: { - self({ renderer, content }) { - const annotation = renderer.renderAnnotation('EnumAnnotation'); - return `${annotation}\n${content}`; - }, + generator = new JavaGenerator({ + presets: [ + { + enum: { + self({ renderer, content }) { + const annotation = renderer.renderAnnotation('EnumAnnotation'); + return `${annotation}\n${content}`; + } + } } - } - ] }); + ] + }); - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomEnum']; - - let enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies.length).toEqual(0); - - enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies.length).toEqual(0); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render enums with translated special characters', async () => { @@ -369,44 +174,11 @@ public enum CustomEnum { $id: 'States', enum: ['test+', 'test', 'test-', 'test?!', '*test'] }; - const expected = `public enum States { - TEST_PLUS("test+"), TEST("test"), TEST_MINUS("test-"), TEST_QUESTION_EXCLAMATION("test?!"), ASTERISK_TEST("*test"); - - private String value; - - States(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public static States fromValue(String value) { - for (States e : States.values()) { - if (e.value.equals(value)) { - return e; - } - } - throw new IllegalArgumentException("Unexpected value '" + value + "'"); - } - @Override - public String toString() { - return String.valueOf(value); - } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['States']; - - let enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies.length).toEqual(0); - - enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies.length).toEqual(0); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render List type for collections', async () => { @@ -415,25 +187,17 @@ public enum CustomEnum { type: 'object', additionalProperties: false, properties: { - arrayType: { type: 'array' }, + arrayType: { type: 'array' } } }; - const expected = `public class CustomClass { - private List arrayType; - - public List getArrayType() { return this.arrayType; } - public void setArrayType(List arrayType) { this.arrayType = arrayType; } -}`; const expectedDependencies = ['import java.util.List;']; generator = new JavaGenerator({ collectionType: 'List' }); - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomClass']; - - const classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual(expectedDependencies); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); }); test('should render models and their dependencies', async () => { @@ -445,25 +209,37 @@ public enum CustomEnum { city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, - other_model: { type: 'object', $id: 'OtherModel', properties: {street_name: { type: 'string' }} }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + other_model: { + type: 'object', + $id: 'OtherModel', + properties: { street_name: { type: 'string' } } + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const config = {packageName: 'test.package'}; + const config = { packageName: 'test.packageName' }; const models = await generator.generateCompleteModels(doc, config); expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); expect(models[1].result).toMatchSnapshot(); }); - test('should throw error when reserved keyword is used for package name', async () => { + test('should throw error when reserved keyword is used in any part of the package name', async () => { const doc = { $id: 'Address', type: 'object', @@ -472,19 +248,31 @@ public enum CustomEnum { city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const config = {packageName: 'package'}; - const expectedError = new Error('You cannot use reserved Java keyword (package) as package name, please use another.'); - await expect(generator.generateCompleteModels(doc, config)).rejects.toEqual(expectedError); + const config = { packageName: 'valid.package.correct.class' }; + const expectedError = new Error( + `You cannot use 'valid.package.correct.class' as a package name, contains reserved keywords: [package, class]` + ); + await expect(generator.generateCompleteModels(doc, config)).rejects.toEqual( + expectedError + ); }); }); diff --git a/test/generators/java/JavaRenderer.spec.ts b/test/generators/java/JavaRenderer.spec.ts index 50dde4b3d8..42fe75765d 100644 --- a/test/generators/java/JavaRenderer.spec.ts +++ b/test/generators/java/JavaRenderer.spec.ts @@ -1,133 +1,24 @@ -import { defaultGeneratorOptions, JavaGenerator } from '../../../src/generators'; +import { JavaGenerator } from '../../../src/generators'; +import { JavaDependencyManager } from '../../../src/generators/java/JavaDependencyManager'; import { JavaRenderer } from '../../../src/generators/java/JavaRenderer'; -import { CommonInputModel, CommonModel } from '../../../src/models'; -class MockJavaRenderer extends JavaRenderer { +import { + CommonModel, + ConstrainedObjectModel, + InputMetaModel +} from '../../../src/models'; +import { MockJavaRenderer } from '../../TestUtils/TestRenderers'; -} describe('JavaRenderer', () => { - let renderer: JavaRenderer; + let renderer: JavaRenderer; beforeEach(() => { - renderer = new MockJavaRenderer(JavaGenerator.defaultOptions, new JavaGenerator(), [], new CommonModel(), new CommonInputModel()); - }); - - describe('nameType()', () => { - test('should name the type', () => { - const name = renderer.nameType('type__someType'); - expect(name).toEqual('TypeSomeType'); - }); - test('should render reserved type keyword correctly', () => { - const name = renderer.nameType('enum'); - expect(name).toEqual('Enum'); - }); - }); - - describe('nameProperty()', () => { - test('should name the property', () => { - const name = renderer.nameProperty('property__someProperty'); - expect(name).toEqual('propertySomeProperty'); - }); - test('should render reserved property keyword correctly', () => { - const name = renderer.nameProperty('enum'); - expect(name).toEqual('reservedEnum'); - }); - }); - describe('renderType()', () => { - test('Should render refs with pascal case', () => { - const model = new CommonModel(); - model.$ref = ''; - expect(renderer.renderType(model)).toEqual('AnonymousSchema_1'); - }); - }); - describe('toJavaTypeWithFormat()', () => { - test('Should be able to return type if format ist not handled', () => { - expect(renderer.toJavaTypeWithFormat('string', 'email', new CommonModel())).toEqual('String'); - }); - test('Should be able to return type for format', () => { - expect(renderer.toJavaTypeWithFormat('string', 'password', new CommonModel())).toEqual('String'); - }); - }); - describe('toJavaType()', () => { - test('Should be able to return long', () => { - expect(renderer.toJavaType('long', new CommonModel())).toEqual('long'); - expect(renderer.toJavaType('int64', new CommonModel())).toEqual('long'); - }); - test('Should be able to return date', () => { - expect(renderer.toJavaType('date', new CommonModel())).toEqual('java.time.LocalDate'); - }); - test('Should be able to return time', () => { - expect(renderer.toJavaType('time', new CommonModel())).toEqual('java.time.OffsetTime'); - }); - test('Should be able to return offset date time', () => { - expect(renderer.toJavaType('dateTime', new CommonModel())).toEqual('java.time.OffsetDateTime'); - expect(renderer.toJavaType('date-time', new CommonModel())).toEqual('java.time.OffsetDateTime'); - }); - test('Should be able to return float', () => { - expect(renderer.toJavaType('float', new CommonModel())).toEqual('float'); - }); - test('Should be able to return byte array', () => { - expect(renderer.toJavaType('binary', new CommonModel())).toEqual('byte[]'); - }); - test('Should render matching tuple types as is', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - }, - { - type: 'string' - } - ] - }); - expect(renderer.toJavaType('array', model)).toEqual('String[]'); - }); - test('Should render mismatching tuple types as Object', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - }, - { - type: 'number' - } - ] - }); - expect(renderer.toJavaType('array', model)).toEqual('Object[]'); - }); - test('Should render matching tuple and additionalItem types', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - } - ], - additionalItems: { - type: 'string' - } - }); - expect(renderer.toJavaType('array', model)).toEqual('String[]'); - }); - test('Should render Object for tuple and additionalItem type mismatch', () => { - const model = CommonModel.toCommonModel({ - items: [ - { - type: 'string' - } - ], - additionalItems: { - type: 'number' - } - }); - expect(renderer.toJavaType('array', model)).toEqual('Object[]'); - }); - }); - - describe('toClassType()', () => { - test('Should be able to return long object', () => { - expect(renderer.toClassType('long')).toEqual('Long'); - }); - test('Should be able to return float object', () => { - expect(renderer.toClassType('float')).toEqual('Float'); - }); + renderer = new MockJavaRenderer( + JavaGenerator.defaultOptions, + new JavaGenerator(), + [], + new ConstrainedObjectModel('', undefined, '', {}), + new InputMetaModel(), + new JavaDependencyManager(JavaGenerator.defaultOptions) + ); }); describe('renderComments()', () => { @@ -140,7 +31,9 @@ describe('JavaRenderer', () => { describe('renderAnnotation()', () => { test('Should be able to render multiple annotations', () => { - expect(renderer.renderAnnotation('someComment', {test: 'test2'})).toEqual('@SomeComment(test=test2)'); + expect( + renderer.renderAnnotation('someComment', { test: 'test2' }) + ).toEqual('@SomeComment(test=test2)'); }); }); }); diff --git a/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap b/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap index bd46d10de4..931979b5f8 100644 --- a/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap +++ b/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap @@ -1,8 +1,218 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`JavaGenerator should not render reserved keyword 1`] = ` +"public class Address { + private String reservedReservedEnum; + private String reservedEnum; + + public String getReservedReservedEnum() { return this.reservedReservedEnum; } + public void setReservedReservedEnum(String reservedReservedEnum) { this.reservedReservedEnum = reservedReservedEnum; } + + public String getReservedEnum() { return this.reservedEnum; } + public void setReservedEnum(String reservedEnum) { this.reservedEnum = reservedEnum; } +}" +`; + +exports[`JavaGenerator should render \`class\` type 1`] = ` +"public class Address { + private String streetName; + private String city; + private String state; + private Double houseNumber; + private Boolean marriage; + private Object members; + private Object[] arrayType; + private Map additionalProperties; + + public String getStreetName() { return this.streetName; } + public void setStreetName(String streetName) { this.streetName = streetName; } + + public String getCity() { return this.city; } + public void setCity(String city) { this.city = city; } + + public String getState() { return this.state; } + public void setState(String state) { this.state = state; } + + public Double getHouseNumber() { return this.houseNumber; } + public void setHouseNumber(Double houseNumber) { this.houseNumber = houseNumber; } + + public Boolean getMarriage() { return this.marriage; } + public void setMarriage(Boolean marriage) { this.marriage = marriage; } + + public Object getMembers() { return this.members; } + public void setMembers(Object members) { this.members = members; } + + public Object[] getArrayType() { return this.arrayType; } + public void setArrayType(Object[] arrayType) { this.arrayType = arrayType; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } +}" +`; + +exports[`JavaGenerator should render \`enum\` type (integer type) 1`] = ` +"public enum Numbers { + NUMBER_0((int)0), NUMBER_1((int)1), NUMBER_2((int)2), NUMBER_3((int)3); + + private int value; + + Numbers(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Numbers fromValue(int value) { + for (Numbers e : Numbers.values()) { + if (e.value == value) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +}" +`; + +exports[`JavaGenerator should render \`enum\` type (string type) 1`] = ` +"public enum States { + TEXAS((String)\\"Texas\\"), ALABAMA((String)\\"Alabama\\"), CALIFORNIA((String)\\"California\\"), NEW_YORK((String)\\"New York\\"); + + private String value; + + States(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static States fromValue(String value) { + for (States e : States.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +}" +`; + +exports[`JavaGenerator should render \`enum\` type (union type) 1`] = ` +"public enum Union { + TEXAS((Object)\\"Texas\\"), ALABAMA((Object)\\"Alabama\\"), NUMBER_0((Object)0), NUMBER_1((Object)1), RESERVED_NUMBER_1((Object)\\"1\\"), TRUE((Object)\\"true\\"), CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT((Object)\\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\"); + + private Object value; + + Union(Object value) { + this.value = value; + } + + public Object getValue() { + return value; + } + + public static Union fromValue(Object value) { + for (Union e : Union.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +}" +`; + +exports[`JavaGenerator should render List type for collections 1`] = ` +"public class CustomClass { + private List arrayType; + + public List getArrayType() { return this.arrayType; } + public void setArrayType(List arrayType) { this.arrayType = arrayType; } +}" +`; + +exports[`JavaGenerator should render custom preset for \`enum\` type 1`] = ` +"@EnumAnnotation +public enum CustomEnum { + TEXAS((String)\\"Texas\\"), ALABAMA((String)\\"Alabama\\"), CALIFORNIA((String)\\"California\\"); + + private String value; + + CustomEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static CustomEnum fromValue(String value) { + for (CustomEnum e : CustomEnum.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +}" +`; + +exports[`JavaGenerator should render enums with translated special characters 1`] = ` +"public enum States { + TEST_PLUS((String)\\"test+\\"), TEST((String)\\"test\\"), TEST_MINUS((String)\\"test-\\"), TEST_QUESTION_EXCLAMATION((String)\\"test?!\\"), ASTERISK_TEST((String)\\"*test\\"); + + private String value; + + States(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static States fromValue(String value) { + for (States e : States.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +}" +`; + exports[`JavaGenerator should render models and their dependencies 1`] = ` -"package test.package; -import test.package.OtherModel; +"package test.packageName; +import test.packageName.OtherModel; import java.util.Map; public class Address { private String streetName; @@ -14,7 +224,6 @@ public class Address { private Object[] arrayType; private OtherModel otherModel; private Map additionalProperties; - private Map sTestPatternProperties; public String getStreetName() { return this.streetName; } public void setStreetName(String streetName) { this.streetName = streetName; } @@ -42,14 +251,11 @@ public class Address { public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } - - public Map getSTestPatternProperties() { return this.sTestPatternProperties; } - public void setSTestPatternProperties(Map sTestPatternProperties) { this.sTestPatternProperties = sTestPatternProperties; } }" `; exports[`JavaGenerator should render models and their dependencies 2`] = ` -"package test.package; +"package test.packageName; import java.util.Map; public class OtherModel { @@ -63,3 +269,22 @@ public class OtherModel { public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } }" `; + +exports[`JavaGenerator should work custom preset for \`class\` type 1`] = ` +"public class CustomClass { + @JsonProperty(\\"property\\") + private String property; + @JsonProperty(\\"additionalProperties\\") + private Map additionalProperties; + + @JsonProperty(\\"property\\") + public String getProperty() { return this.property; } + @JsonProperty(\\"property\\") + public void setProperty(String property) { this.property = property; } + + @JsonProperty(\\"additionalProperties\\") + public Map getAdditionalProperties() { return this.additionalProperties; } + @JsonProperty(\\"additionalProperties\\") + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } +}" +`; diff --git a/test/generators/java/constrainer/EnumConstrainer.spec.ts b/test/generators/java/constrainer/EnumConstrainer.spec.ts new file mode 100644 index 0000000000..d97eef748f --- /dev/null +++ b/test/generators/java/constrainer/EnumConstrainer.spec.ts @@ -0,0 +1,167 @@ +import { JavaDefaultConstraints } from '../../../../src/generators/java/JavaConstrainer'; +import { EnumModel } from '../../../../src/models/MetaModel'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../../src'; +import { + defaultEnumKeyConstraints, + ModelEnumKeyConstraints, + DefaultEnumKeyConstraints +} from '../../../../src/generators/java/constrainer/EnumConstrainer'; +describe('EnumConstrainer', () => { + const enumModel = new EnumModel('test', undefined, []); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [] + ); + + describe('enum keys', () => { + test('should never render special chars', () => { + const constrainedKey = JavaDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '%' + }); + expect(constrainedKey).toEqual('PERCENT'); + }); + test('should not render number as start char', () => { + const constrainedKey = JavaDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '1' + }); + expect(constrainedKey).toEqual('NUMBER_1'); + }); + test('should not contain duplicate keys', () => { + const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel( + 'EMPTY', + 'return' + ); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [existingConstrainedEnumValueModel] + ); + const constrainedKey = JavaDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('RESERVED_EMPTY'); + }); + test('should never contain empty keys', () => { + const constrainedKey = JavaDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('EMPTY'); + }); + test('should use constant naming format', () => { + const constrainedKey = JavaDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual( + 'SOME_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2' + ); + }); + test('should never render reserved keywords', () => { + const constrainedKey = JavaDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'return' + }); + expect(constrainedKey).toEqual('RESERVED_RETURN'); + }); + }); + describe('enum values', () => { + test('should render string values', () => { + const constrainedValue = JavaDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 'string value' + }); + expect(constrainedValue).toEqual('"string value"'); + }); + test('should render boolean values', () => { + const constrainedValue = JavaDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: true + }); + expect(constrainedValue).toEqual('"true"'); + }); + test('should render numbers', () => { + const constrainedValue = JavaDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 123 + }); + expect(constrainedValue).toEqual(123); + }); + test('should render object', () => { + const constrainedValue = JavaDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: { test: 'test' } + }); + expect(constrainedValue).toEqual('"{\\"test\\":\\"test\\"}"'); + }); + test('should render unknown value', () => { + const constrainedValue = JavaDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: undefined + }); + expect(constrainedValue).toEqual('"undefined"'); + }); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks for enum key', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultEnumKeyConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks for enum key', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultEnumKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_DUPLICATE_KEYS'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultEnumKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/java/constrainer/ModelNameConstrainer.spec.ts b/test/generators/java/constrainer/ModelNameConstrainer.spec.ts new file mode 100644 index 0000000000..11f7eaa8f5 --- /dev/null +++ b/test/generators/java/constrainer/ModelNameConstrainer.spec.ts @@ -0,0 +1,69 @@ +import { JavaDefaultConstraints } from '../../../../src/generators/java/JavaConstrainer'; +import { + DefaultModelNameConstraints, + defaultModelNameConstraints, + ModelNameConstraints +} from '../../../../src/generators/java/constrainer/ModelNameConstrainer'; +describe('ModelNameConstrainer', () => { + test('should never render special chars', () => { + const constrainedKey = JavaDefaultConstraints.modelName({ modelName: '%' }); + expect(constrainedKey).toEqual('Percent'); + }); + test('should never render number as start char', () => { + const constrainedKey = JavaDefaultConstraints.modelName({ modelName: '1' }); + expect(constrainedKey).toEqual('Number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = JavaDefaultConstraints.modelName({ modelName: '' }); + expect(constrainedKey).toEqual('Empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = JavaDefaultConstraints.modelName({ + modelName: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = JavaDefaultConstraints.modelName({ + modelName: 'return' + }); + expect(constrainedKey).toEqual('ReservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: ModelNameConstraints = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultModelNameConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ modelName: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultModelNameConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultModelNameConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultModelNameConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ modelName: '' }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/java/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/java/constrainer/PropertyKeyConstrainer.spec.ts new file mode 100644 index 0000000000..8feac99981 --- /dev/null +++ b/test/generators/java/constrainer/PropertyKeyConstrainer.spec.ts @@ -0,0 +1,183 @@ +import { JavaDefaultConstraints } from '../../../../src/generators/java/JavaConstrainer'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ObjectModel, + ObjectPropertyModel +} from '../../../../src'; +import { + DefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints, + PropertyKeyConstraintOptions +} from '../../../../src/generators/java/constrainer/PropertyKeyConstrainer'; +describe('PropertyKeyConstrainer', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + + const constrainPropertyName = (propertyName: string) => { + const objectPropertyModel = new ObjectPropertyModel( + propertyName, + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + return JavaDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + }; + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('should never render special chars', () => { + const constrainedKey = constrainPropertyName('%'); + expect(constrainedKey).toEqual('percent'); + }); + test('should not render number as start char', () => { + const constrainedKey = constrainPropertyName('1'); + expect(constrainedKey).toEqual('number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = constrainPropertyName(''); + expect(constrainedKey).toEqual('empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = constrainPropertyName('some weird_value!"#2'); + expect(constrainedKey).toEqual('someWeirdValueExclamationQuotationHash_2'); + }); + test('should not contain duplicate properties', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + const objectPropertyModel = new ObjectPropertyModel( + 'reservedReturn', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + 'reservedReturn', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const objectPropertyModel2 = new ObjectPropertyModel( + 'return', + false, + objectModel + ); + const constrainedObjectPropertyModel2 = new ConstrainedObjectPropertyModel( + 'return', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainedObjectModel.properties['reservedReturn'] = + constrainedObjectPropertyModel; + constrainedObjectModel.properties['return'] = + constrainedObjectPropertyModel2; + const constrainedKey = JavaDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel: objectPropertyModel2, + constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + }); + expect(constrainedKey).toEqual('reservedReservedReturn'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = constrainPropertyName('return'); + expect(constrainedKey).toEqual('reservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultPropertyKeyConstraints( + mockedConstraintCallbacks + ); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_RESERVED_KEYWORDS'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_DUPLICATE_PROPERTIES') + ]; + const overwrittenDefaultFunction = jest.spyOn( + DefaultPropertyKeyConstraints, + 'NAMING_FORMATTER' + ); + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultPropertyKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const constrainedValue = constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + expect(constrainedValue).toEqual(''); + expect(jestCallback).toHaveBeenCalled(); + expect(overwrittenDefaultFunction).not.toHaveBeenCalled(); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/java/presets/CommonPreset.spec.ts b/test/generators/java/presets/CommonPreset.spec.ts index 9052bb8776..f8e24de703 100644 --- a/test/generators/java/presets/CommonPreset.spec.ts +++ b/test/generators/java/presets/CommonPreset.spec.ts @@ -1,4 +1,8 @@ -import {JavaGenerator, JAVA_COMMON_PRESET, JavaCommonPresetOptions} from '../../../../src/generators'; +import { + JavaGenerator, + JAVA_COMMON_PRESET, + JavaCommonPresetOptions +} from '../../../../src/generators'; describe('JAVA_COMMON_PRESET', () => { const doc = { @@ -10,152 +14,152 @@ describe('JAVA_COMMON_PRESET', () => { stringProp: { type: 'string' }, numberProp: { type: 'number' }, booleanProp: { type: 'boolean' }, - arrayProp: { type: 'array', items: { type: 'string' } }, - }, + arrayProp: { type: 'array', items: { type: 'string' } } + } }; test('should render common function in class by common preset', async () => { const generator = new JavaGenerator({ presets: [JAVA_COMMON_PRESET] }); - const inputModel = await generator.process(doc); - const model = inputModel.models['Clazz']; - - const classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toMatchSnapshot(); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); }); test('should render accurately when there is no additional properties', async () => { const generator = new JavaGenerator({ presets: [JAVA_COMMON_PRESET] }); - const inputModel = await generator.process({...doc, additionalProperties: false}); - const model = inputModel.models['Clazz']; - - const classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toMatchSnapshot(); + const models = await generator.generate({ + ...doc, + additionalProperties: false + }); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); }); describe('with option', () => { test('should render all functions', async () => { - const generator = new JavaGenerator( - { - presets: [{ + const generator = new JavaGenerator({ + presets: [ + { preset: JAVA_COMMON_PRESET, options: { equal: true, hashCode: true, classToString: true, - marshalling: false, + marshalling: false } - }] - } - ); - const inputModel = await generator.process(doc); - const model = inputModel.models['Clazz']; - - const classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toMatchSnapshot(); - expect(classModel.dependencies.includes('import java.util.Objects;')).toEqual(true); + } + ] + }); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([ + 'import java.util.Objects;', + 'import java.util.Map;' + ]); }); test('should not render any functions when all 4 options are disabled', async () => { const options: JavaCommonPresetOptions = { equal: false, hashCode: false, classToString: false, - marshalling: false, + marshalling: false }; - const generator = new JavaGenerator( - { - presets: [{ + const generator = new JavaGenerator({ + presets: [ + { preset: JAVA_COMMON_PRESET, options - }] - } - ); - const inputModel = await generator.process(doc); - const model = inputModel.models['Clazz']; - - const classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toMatchSnapshot(); + } + ] + }); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); }); test('should render equals', async () => { - const generator = new JavaGenerator( - { - presets: [{ + const generator = new JavaGenerator({ + presets: [ + { preset: JAVA_COMMON_PRESET, options: { equal: true, hashCode: false, classToString: false, - marshalling: false, + marshalling: false } - }] - } - ); - const inputModel = await generator.process(doc); - const model = inputModel.models['Clazz']; - - const classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toMatchSnapshot(); - expect(classModel.dependencies.includes('import java.util.Objects;')).toEqual(true); + } + ] + }); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([ + 'import java.util.Objects;', + 'import java.util.Map;' + ]); }); test('should render hashCode', async () => { - const generator = new JavaGenerator( - { - presets: [{ + const generator = new JavaGenerator({ + presets: [ + { preset: JAVA_COMMON_PRESET, options: { equal: false, hashCode: true, classToString: false, - marshalling: false, + marshalling: false } - }] - } - ); - const inputModel = await generator.process(doc); - const model = inputModel.models['Clazz']; - - const classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toMatchSnapshot(); - expect(classModel.dependencies.includes('import java.util.Objects;')).toEqual(true); + } + ] + }); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([ + 'import java.util.Objects;', + 'import java.util.Map;' + ]); }); test('should render classToString', async () => { - const generator = new JavaGenerator( - { - presets: [{ + const generator = new JavaGenerator({ + presets: [ + { preset: JAVA_COMMON_PRESET, options: { equal: false, hashCode: false, classToString: true, - marshalling: false, + marshalling: false } - }] - } - ); - const inputModel = await generator.process(doc); - const model = inputModel.models['Clazz']; - - const classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toMatchSnapshot(); + } + ] + }); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(['import java.util.Map;']); }); test('should render un/marshal', async () => { - const generator = new JavaGenerator( - { - presets: [{ + const generator = new JavaGenerator({ + presets: [ + { preset: JAVA_COMMON_PRESET, options: { equal: false, hashCode: false, classToString: false, - marshalling: true, + marshalling: true } - }] - } - ); - const inputModel = await generator.process(doc); - const model = inputModel.models['Clazz']; - - const classModel = await generator.renderClass(model, inputModel); - expect(classModel.dependencies.includes('import java.util.Map;')).toEqual(true); - expect(classModel.dependencies.includes('import java.util.stream;')).toEqual(true); - expect(classModel.dependencies.includes('import org.json.JSONObject;')).toEqual(true); + } + ] + }); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([ + 'import java.util.stream;', + 'import org.json.JSONObject;', + 'import java.util.Map;' + ]); }); }); }); diff --git a/test/generators/java/presets/ConstraintsPreset.spec.ts b/test/generators/java/presets/ConstraintsPreset.spec.ts index 99eec2ef32..5d0ba4100b 100644 --- a/test/generators/java/presets/ConstraintsPreset.spec.ts +++ b/test/generators/java/presets/ConstraintsPreset.spec.ts @@ -1,4 +1,7 @@ -import { JavaGenerator, JAVA_CONSTRAINTS_PRESET } from '../../../../src/generators'; +import { + JavaGenerator, + JAVA_CONSTRAINTS_PRESET +} from '../../../../src/generators'; describe('JAVA_CONSTRAINTS_PRESET', () => { let generator: JavaGenerator; @@ -6,54 +9,26 @@ describe('JAVA_CONSTRAINTS_PRESET', () => { generator = new JavaGenerator({ presets: [JAVA_CONSTRAINTS_PRESET] }); }); - test('should render constaints annotations', async () => { + test('should render constraints annotations', async () => { const doc = { $id: 'Clazz', type: 'object', properties: { min_number_prop: { type: 'number', minimum: 0 }, max_number_prop: { type: 'number', exclusiveMaximum: 100 }, - array_prop: { type: 'array', minItems: 2, maxItems: 3, }, + array_prop: { type: 'array', minItems: 2, maxItems: 3 }, string_prop: { type: 'string', pattern: '^I_', minLength: 3 } }, required: ['min_number_prop', 'max_number_prop'] }; - const expected = `public class Clazz { - private Double minNumberProp; - private Double maxNumberProp; - private Object[] arrayProp; - private String stringProp; - private Map additionalProperties; - - @NotNull - @Min(0) - public Double getMinNumberProp() { return this.minNumberProp; } - public void setMinNumberProp(Double minNumberProp) { this.minNumberProp = minNumberProp; } - - @NotNull - @Max(99) - public Double getMaxNumberProp() { return this.maxNumberProp; } - public void setMaxNumberProp(Double maxNumberProp) { this.maxNumberProp = maxNumberProp; } - - @Size(min=2, max=3) - public Object[] getArrayProp() { return this.arrayProp; } - public void setArrayProp(Object[] arrayProp) { this.arrayProp = arrayProp; } - - @Pattern(regexp="^I_") - @Size(min=3) - public String getStringProp() { return this.stringProp; } - public void setStringProp(String stringProp) { this.stringProp = stringProp; } - - public Map getAdditionalProperties() { return this.additionalProperties; } - public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Clazz']; - - const classModel = await generator.renderClass(model, inputModel); - const expectedDependencies = ['import java.util.Map;', 'import javax.validation.constraints.*;']; - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual(expectedDependencies); + const expectedDependencies = [ + 'import java.util.Map;', + 'import javax.validation.constraints.*;' + ]; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); }); }); diff --git a/test/generators/java/presets/DescriptionPreset.spec.ts b/test/generators/java/presets/DescriptionPreset.spec.ts index 5c3e7c81cb..2116b2a477 100644 --- a/test/generators/java/presets/DescriptionPreset.spec.ts +++ b/test/generators/java/presets/DescriptionPreset.spec.ts @@ -1,4 +1,7 @@ -import { JavaGenerator, JAVA_DESCRIPTION_PRESET } from '../../../../src/generators'; +import { + JavaGenerator, + JAVA_DESCRIPTION_PRESET +} from '../../../../src/generators'; describe('JAVA_DESCRIPTION_PRESET', () => { let generator: JavaGenerator; @@ -13,35 +16,18 @@ describe('JAVA_DESCRIPTION_PRESET', () => { description: 'Description for class', examples: [{ prop: 'value' }], properties: { - prop: { type: 'string', description: 'Description for prop', examples: ['exampleValue'] }, - }, + prop: { + type: 'string', + description: 'Description for prop', + examples: ['exampleValue'] + } + } }; - const expected = `/** - * Description for class - * Examples: {"prop":"value"} - */ -public class Clazz { - private String prop; - private Map additionalProperties; - - /** - * Description for prop - * Examples: exampleValue - */ - public String getProp() { return this.prop; } - public void setProp(String prop) { this.prop = prop; } - - public Map getAdditionalProperties() { return this.additionalProperties; } - public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Clazz']; - - const classModel = await generator.renderClass(model, inputModel); const expectedDependencies = ['import java.util.Map;']; - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual(expectedDependencies); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); }); test('should render description and examples for enum', async () => { @@ -50,48 +36,12 @@ public class Clazz { type: 'string', description: 'Description for enum', examples: ['value'], - enum: [ - 'on', - 'off', - ] + enum: ['on', 'off'] }; - const expected = `/** - * Description for enum - * Examples: value - */ -public enum Enum { - ON("on"), OFF("off"); - - private String value; - - Enum(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public static Enum fromValue(String value) { - for (Enum e : Enum.values()) { - if (e.value.equals(value)) { - return e; - } - } - throw new IllegalArgumentException("Unexpected value '" + value + "'"); - } - - @Override - public String toString() { - return String.valueOf(value); - } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Enum']; - const enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); }); diff --git a/test/generators/java/presets/JacksonPreset.spec.ts b/test/generators/java/presets/JacksonPreset.spec.ts index a22768e1c4..41a4787705 100644 --- a/test/generators/java/presets/JacksonPreset.spec.ts +++ b/test/generators/java/presets/JacksonPreset.spec.ts @@ -1,4 +1,4 @@ -import { JavaGenerator, JAVA_JACKSON_PRESET } from '../../../../src/generators'; +import { JavaGenerator, JAVA_JACKSON_PRESET } from '../../../../src/generators'; describe('JAVA_JACKSON_PRESET', () => { let generator: JavaGenerator; @@ -12,33 +12,18 @@ describe('JAVA_JACKSON_PRESET', () => { type: 'object', properties: { min_number_prop: { type: 'number' }, - max_number_prop: { type: 'number' }, - }, + max_number_prop: { type: 'number' } + } }; - const expected = `public class Clazz { - private Double minNumberProp; - private Double maxNumberProp; - private Map additionalProperties; - - @JsonProperty("min_number_prop") - public Double getMinNumberProp() { return this.minNumberProp; } - public void setMinNumberProp(Double minNumberProp) { this.minNumberProp = minNumberProp; } - - @JsonProperty("max_number_prop") - public Double getMaxNumberProp() { return this.maxNumberProp; } - public void setMaxNumberProp(Double maxNumberProp) { this.maxNumberProp = maxNumberProp; } - - public Map getAdditionalProperties() { return this.additionalProperties; } - public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Clazz']; - - const classModel = await generator.renderClass(model, inputModel); - const expectedDependencies = ['import java.util.Map;', 'import com.fasterxml.jackson.annotation.*;']; - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual(expectedDependencies); + const expectedDependencies = [ + 'import java.util.Map;', + 'import com.fasterxml.jackson.annotation.*;' + ]; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); }); test('should render Jackson annotations for enum', async () => { @@ -47,46 +32,14 @@ describe('JAVA_JACKSON_PRESET', () => { type: 'string', description: 'Description for enum', examples: ['value'], - enum: [ - 'on', - 'off', - ] + enum: ['on', 'off'] }; - const expected = `public enum Enum { - ON("on"), OFF("off"); - - private String value; - - Enum(String value) { - this.value = value; - } - - @JsonValue - public String getValue() { - return value; - } - - @JsonCreator - public static Enum fromValue(String value) { - for (Enum e : Enum.values()) { - if (e.value.equals(value)) { - return e; - } - } - throw new IllegalArgumentException("Unexpected value '" + value + "'"); - } - - @Override - public String toString() { - return String.valueOf(value); - } -}`; - const inputModel = await generator.process(doc); - const model = inputModel.models['Enum']; + const expectedDependencies = ['import com.fasterxml.jackson.annotation.*;']; - const enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual(['import com.fasterxml.jackson.annotation.*;']); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); }); }); diff --git a/test/generators/java/presets/__snapshots__/CommonPreset.spec.ts.snap b/test/generators/java/presets/__snapshots__/CommonPreset.spec.ts.snap index 3f62d1bc1d..229ca33199 100644 --- a/test/generators/java/presets/__snapshots__/CommonPreset.spec.ts.snap +++ b/test/generators/java/presets/__snapshots__/CommonPreset.spec.ts.snap @@ -6,7 +6,7 @@ exports[`JAVA_COMMON_PRESET should render accurately when there is no additional private String stringProp; private Double numberProp; private Boolean booleanProp; - private String[] arrayProp; + private Object[] arrayProp; public Boolean getRequiredProp() { return this.requiredProp; } public void setRequiredProp(Boolean requiredProp) { this.requiredProp = requiredProp; } @@ -20,8 +20,8 @@ exports[`JAVA_COMMON_PRESET should render accurately when there is no additional public Boolean getBooleanProp() { return this.booleanProp; } public void setBooleanProp(Boolean booleanProp) { this.booleanProp = booleanProp; } - public String[] getArrayProp() { return this.arrayProp; } - public void setArrayProp(String[] arrayProp) { this.arrayProp = arrayProp; } + public Object[] getArrayProp() { return this.arrayProp; } + public void setArrayProp(Object[] arrayProp) { this.arrayProp = arrayProp; } @Override public boolean equals(Object o) { @@ -75,7 +75,7 @@ exports[`JAVA_COMMON_PRESET should render common function in class by common pre private String stringProp; private Double numberProp; private Boolean booleanProp; - private String[] arrayProp; + private Object[] arrayProp; private Map additionalProperties; public Boolean getRequiredProp() { return this.requiredProp; } @@ -90,8 +90,8 @@ exports[`JAVA_COMMON_PRESET should render common function in class by common pre public Boolean getBooleanProp() { return this.booleanProp; } public void setBooleanProp(Boolean booleanProp) { this.booleanProp = booleanProp; } - public String[] getArrayProp() { return this.arrayProp; } - public void setArrayProp(String[] arrayProp) { this.arrayProp = arrayProp; } + public Object[] getArrayProp() { return this.arrayProp; } + public void setArrayProp(Object[] arrayProp) { this.arrayProp = arrayProp; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } @@ -150,7 +150,7 @@ exports[`JAVA_COMMON_PRESET with option should not render any functions when all private String stringProp; private Double numberProp; private Boolean booleanProp; - private String[] arrayProp; + private Object[] arrayProp; private Map additionalProperties; public Boolean getRequiredProp() { return this.requiredProp; } @@ -165,8 +165,8 @@ exports[`JAVA_COMMON_PRESET with option should not render any functions when all public Boolean getBooleanProp() { return this.booleanProp; } public void setBooleanProp(Boolean booleanProp) { this.booleanProp = booleanProp; } - public String[] getArrayProp() { return this.arrayProp; } - public void setArrayProp(String[] arrayProp) { this.arrayProp = arrayProp; } + public Object[] getArrayProp() { return this.arrayProp; } + public void setArrayProp(Object[] arrayProp) { this.arrayProp = arrayProp; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } @@ -179,7 +179,7 @@ exports[`JAVA_COMMON_PRESET with option should render all functions 1`] = ` private String stringProp; private Double numberProp; private Boolean booleanProp; - private String[] arrayProp; + private Object[] arrayProp; private Map additionalProperties; public Boolean getRequiredProp() { return this.requiredProp; } @@ -194,8 +194,8 @@ exports[`JAVA_COMMON_PRESET with option should render all functions 1`] = ` public Boolean getBooleanProp() { return this.booleanProp; } public void setBooleanProp(Boolean booleanProp) { this.booleanProp = booleanProp; } - public String[] getArrayProp() { return this.arrayProp; } - public void setArrayProp(String[] arrayProp) { this.arrayProp = arrayProp; } + public Object[] getArrayProp() { return this.arrayProp; } + public void setArrayProp(Object[] arrayProp) { this.arrayProp = arrayProp; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } @@ -254,7 +254,7 @@ exports[`JAVA_COMMON_PRESET with option should render classToString 1`] = ` private String stringProp; private Double numberProp; private Boolean booleanProp; - private String[] arrayProp; + private Object[] arrayProp; private Map additionalProperties; public Boolean getRequiredProp() { return this.requiredProp; } @@ -269,8 +269,8 @@ exports[`JAVA_COMMON_PRESET with option should render classToString 1`] = ` public Boolean getBooleanProp() { return this.booleanProp; } public void setBooleanProp(Boolean booleanProp) { this.booleanProp = booleanProp; } - public String[] getArrayProp() { return this.arrayProp; } - public void setArrayProp(String[] arrayProp) { this.arrayProp = arrayProp; } + public Object[] getArrayProp() { return this.arrayProp; } + public void setArrayProp(Object[] arrayProp) { this.arrayProp = arrayProp; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } @@ -306,7 +306,7 @@ exports[`JAVA_COMMON_PRESET with option should render equals 1`] = ` private String stringProp; private Double numberProp; private Boolean booleanProp; - private String[] arrayProp; + private Object[] arrayProp; private Map additionalProperties; public Boolean getRequiredProp() { return this.requiredProp; } @@ -321,8 +321,8 @@ exports[`JAVA_COMMON_PRESET with option should render equals 1`] = ` public Boolean getBooleanProp() { return this.booleanProp; } public void setBooleanProp(Boolean booleanProp) { this.booleanProp = booleanProp; } - public String[] getArrayProp() { return this.arrayProp; } - public void setArrayProp(String[] arrayProp) { this.arrayProp = arrayProp; } + public Object[] getArrayProp() { return this.arrayProp; } + public void setArrayProp(Object[] arrayProp) { this.arrayProp = arrayProp; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } @@ -353,7 +353,7 @@ exports[`JAVA_COMMON_PRESET with option should render hashCode 1`] = ` private String stringProp; private Double numberProp; private Boolean booleanProp; - private String[] arrayProp; + private Object[] arrayProp; private Map additionalProperties; public Boolean getRequiredProp() { return this.requiredProp; } @@ -368,8 +368,8 @@ exports[`JAVA_COMMON_PRESET with option should render hashCode 1`] = ` public Boolean getBooleanProp() { return this.booleanProp; } public void setBooleanProp(Boolean booleanProp) { this.booleanProp = booleanProp; } - public String[] getArrayProp() { return this.arrayProp; } - public void setArrayProp(String[] arrayProp) { this.arrayProp = arrayProp; } + public Object[] getArrayProp() { return this.arrayProp; } + public void setArrayProp(Object[] arrayProp) { this.arrayProp = arrayProp; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } @@ -380,3 +380,79 @@ exports[`JAVA_COMMON_PRESET with option should render hashCode 1`] = ` } }" `; + +exports[`JAVA_COMMON_PRESET with option should render un/marshal 1`] = ` +"public class Clazz { + private Boolean requiredProp; + private String stringProp; + private Double numberProp; + private Boolean booleanProp; + private Object[] arrayProp; + private Map additionalProperties; + + public Boolean getRequiredProp() { return this.requiredProp; } + public void setRequiredProp(Boolean requiredProp) { this.requiredProp = requiredProp; } + + public String getStringProp() { return this.stringProp; } + public void setStringProp(String stringProp) { this.stringProp = stringProp; } + + public Double getNumberProp() { return this.numberProp; } + public void setNumberProp(Double numberProp) { this.numberProp = numberProp; } + + public Boolean getBooleanProp() { return this.booleanProp; } + public void setBooleanProp(Boolean booleanProp) { this.booleanProp = booleanProp; } + + public Object[] getArrayProp() { return this.arrayProp; } + public void setArrayProp(Object[] arrayProp) { this.arrayProp = arrayProp; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } + + public String marshal() { + List propList = new ArrayList(); + if(this.requiredProp != null) { + propList.add(\\"requiredProp:\\"+this.requiredProp.toString()); + } + if(this.stringProp != null) { + propList.add(\\"stringProp:\\"+this.stringProp.toString()); + } + if(this.numberProp != null) { + propList.add(\\"numberProp:\\"+this.numberProp.toString()); + } + if(this.booleanProp != null) { + propList.add(\\"booleanProp:\\"+this.booleanProp.toString()); + } + if(this.arrayProp != null) { + propList.add(\\"arrayProp:\\"+this.arrayProp.toString()); + } + if(this.additionalProperties != null) { + propList.add(\\"additionalProperties:\\"+this.additionalProperties.toString()); + } + return propList.stream().collect(Collectors.joining(\\",\\")); + } + + public static Clazz unmarshal(String json) { + Clazz result = new Clazz(); + JSONObject jsonObject = new JSONObject(json); + if(jsonObject.has(\\"requiredProp\\")) { + result.setRequiredProp(jsonObject.getBoolean(\\"requiredProp\\")); + } + if(jsonObject.has(\\"stringProp\\")) { + result.setStringProp(jsonObject.getString(\\"stringProp\\")); + } + if(jsonObject.has(\\"numberProp\\")) { + result.setNumberProp(jsonObject.getDouble(\\"numberProp\\")); + } + if(jsonObject.has(\\"booleanProp\\")) { + result.setBooleanProp(jsonObject.getBoolean(\\"booleanProp\\")); + } + if(jsonObject.has(\\"arrayProp\\")) { + result.setArrayProp(jsonObject.getObject[](\\"arrayProp\\")); + } + if(jsonObject.has(\\"additionalProperties\\")) { + result.setAdditionalProperties(jsonObject.getMap(\\"additionalProperties\\")); + } + return result; + } +}" +`; diff --git a/test/generators/java/presets/__snapshots__/ConstraintsPreset.spec.ts.snap b/test/generators/java/presets/__snapshots__/ConstraintsPreset.spec.ts.snap new file mode 100644 index 0000000000..eddd84c8dc --- /dev/null +++ b/test/generators/java/presets/__snapshots__/ConstraintsPreset.spec.ts.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`JAVA_CONSTRAINTS_PRESET should render constraints annotations 1`] = ` +"public class Clazz { + @NotNull + @Min(0) + private Double minNumberProp; + @NotNull + @Max(99) + private Double maxNumberProp; + @Size(min=2, max=3) + private Object[] arrayProp; + @Pattern(regexp=\\"^I_\\") + @Size(min=3) + private String stringProp; + private Map additionalProperties; + + public Double getMinNumberProp() { return this.minNumberProp; } + public void setMinNumberProp(Double minNumberProp) { this.minNumberProp = minNumberProp; } + + public Double getMaxNumberProp() { return this.maxNumberProp; } + public void setMaxNumberProp(Double maxNumberProp) { this.maxNumberProp = maxNumberProp; } + + public Object[] getArrayProp() { return this.arrayProp; } + public void setArrayProp(Object[] arrayProp) { this.arrayProp = arrayProp; } + + public String getStringProp() { return this.stringProp; } + public void setStringProp(String stringProp) { this.stringProp = stringProp; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } +}" +`; diff --git a/test/generators/java/presets/__snapshots__/DescriptionPreset.spec.ts.snap b/test/generators/java/presets/__snapshots__/DescriptionPreset.spec.ts.snap new file mode 100644 index 0000000000..a0cffbd923 --- /dev/null +++ b/test/generators/java/presets/__snapshots__/DescriptionPreset.spec.ts.snap @@ -0,0 +1,56 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`JAVA_DESCRIPTION_PRESET should render description and examples for class 1`] = ` +"/** + * Description for class + * Examples: {\\"prop\\":\\"value\\"} + */ +public class Clazz { + private String prop; + private Map additionalProperties; + + /** + * Description for prop + * Examples: exampleValue + */ + public String getProp() { return this.prop; } + public void setProp(String prop) { this.prop = prop; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } +}" +`; + +exports[`JAVA_DESCRIPTION_PRESET should render description and examples for enum 1`] = ` +"/** + * Description for enum + * Examples: value + */ +public enum ReservedEnum { + ON((String)\\"on\\"), OFF((String)\\"off\\"); + + private String value; + + ReservedEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static ReservedEnum fromValue(String value) { + for (ReservedEnum e : ReservedEnum.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +}" +`; diff --git a/test/generators/java/presets/__snapshots__/JacksonPreset.spec.ts.snap b/test/generators/java/presets/__snapshots__/JacksonPreset.spec.ts.snap new file mode 100644 index 0000000000..1cb46f0ae1 --- /dev/null +++ b/test/generators/java/presets/__snapshots__/JacksonPreset.spec.ts.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`JAVA_JACKSON_PRESET should render Jackson annotations for class 1`] = ` +"public class Clazz { + @JsonProperty(\\"min_number_prop\\") + private Double minNumberProp; + @JsonProperty(\\"max_number_prop\\") + private Double maxNumberProp; + private Map additionalProperties; + + public Double getMinNumberProp() { return this.minNumberProp; } + public void setMinNumberProp(Double minNumberProp) { this.minNumberProp = minNumberProp; } + + public Double getMaxNumberProp() { return this.maxNumberProp; } + public void setMaxNumberProp(Double maxNumberProp) { this.maxNumberProp = maxNumberProp; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } +}" +`; + +exports[`JAVA_JACKSON_PRESET should render Jackson annotations for enum 1`] = ` +"public enum ReservedEnum { + ON((String)\\"on\\"), OFF((String)\\"off\\"); + + private String value; + + ReservedEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @JsonCreator + public static ReservedEnum fromValue(String value) { + for (ReservedEnum e : ReservedEnum.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +}" +`; diff --git a/test/generators/java/renderers/EnumRenderer.spec.ts b/test/generators/java/renderers/EnumRenderer.spec.ts deleted file mode 100644 index 8345b093c2..0000000000 --- a/test/generators/java/renderers/EnumRenderer.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { JavaGenerator } from '../../../../src/generators'; -import { EnumRenderer } from '../../../../src/generators/java/renderers/EnumRenderer'; -import { CommonInputModel, CommonModel } from '../../../../src/models'; - -describe('EnumRenderer', () => { - let renderer: EnumRenderer; - beforeEach(() => { - renderer = new EnumRenderer(JavaGenerator.defaultOptions, new JavaGenerator(), [], new CommonModel(), new CommonInputModel()); - }); - - describe('normalizeKey()', () => { - test('should correctly format " " to correct key', () => { - const key = renderer.normalizeKey('something something'); - expect(key).toEqual('SOMETHING_SOMETHING'); - }); - test('should correctly format "_" to correct key', () => { - const key = renderer.normalizeKey('something_something'); - expect(key).toEqual('SOMETHING_SOMETHING'); - }); - }); -}); diff --git a/test/generators/javascript/JavaScriptConstrainer.spec.ts b/test/generators/javascript/JavaScriptConstrainer.spec.ts new file mode 100644 index 0000000000..54dd8ccebd --- /dev/null +++ b/test/generators/javascript/JavaScriptConstrainer.spec.ts @@ -0,0 +1,178 @@ +import { JavaScriptDefaultTypeMapping } from '../../../src/generators/javascript/JavaScriptConstrainer'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedUnionModel, + JavaScriptGenerator +} from '../../../src'; +import { JavaScriptDependencyManager } from '../../../src/generators/javascript/JavaScriptDependencyManager'; +describe('JavaScriptConstrainer', () => { + const defaultOptions = { + options: JavaScriptGenerator.defaultOptions, + dependencyManager: new JavaScriptDependencyManager( + JavaScriptGenerator.defaultOptions + ) + }; + describe('ObjectModel', () => { + test('should have no type', () => { + const model = new ConstrainedObjectModel('test', undefined, '', {}); + const type = JavaScriptDefaultTypeMapping.Object({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); + describe('Reference', () => { + test('should have no type', () => { + const refModel = new ConstrainedAnyModel('test', undefined, ''); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + '', + refModel + ); + const type = JavaScriptDefaultTypeMapping.Reference({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); + describe('Any', () => { + test('should have no type', () => { + const model = new ConstrainedAnyModel('test', undefined, ''); + const type = JavaScriptDefaultTypeMapping.Any({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); + describe('Float', () => { + test('should have no type', () => { + const model = new ConstrainedFloatModel('test', undefined, ''); + const type = JavaScriptDefaultTypeMapping.Float({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); + describe('Integer', () => { + test('should have no type', () => { + const model = new ConstrainedIntegerModel('test', undefined, ''); + const type = JavaScriptDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); + describe('String', () => { + test('should have no type', () => { + const model = new ConstrainedStringModel('test', undefined, ''); + const type = JavaScriptDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); + describe('Boolean', () => { + test('should have no type', () => { + const model = new ConstrainedBooleanModel('test', undefined, ''); + const type = JavaScriptDefaultTypeMapping.Boolean({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); + + describe('Tuple', () => { + test('should have no type', () => { + const model = new ConstrainedTupleModel('test', undefined, '', []); + const type = JavaScriptDefaultTypeMapping.Tuple({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); + + describe('Array', () => { + test('should have no type', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const type = JavaScriptDefaultTypeMapping.Array({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); + + describe('Enum', () => { + test('should have no type', () => { + const model = new ConstrainedEnumModel('test', undefined, '', []); + const type = JavaScriptDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); + + describe('Union', () => { + test('should have no type', () => { + const model = new ConstrainedUnionModel('test', undefined, '', []); + const type = JavaScriptDefaultTypeMapping.Union({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); + + describe('Dictionary', () => { + test('should have no type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'String'); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = JavaScriptDefaultTypeMapping.Dictionary({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(''); + }); + }); +}); diff --git a/test/generators/javascript/JavaScriptFileGenerator.spec.ts b/test/generators/javascript/JavaScriptFileGenerator.spec.ts deleted file mode 100644 index 074201be9e..0000000000 --- a/test/generators/javascript/JavaScriptFileGenerator.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { CommonInputModel, CommonModel, FileHelpers, JavaScriptFileGenerator, OutputModel } from '../../../src'; -import * as path from 'path'; - -describe('JavaScriptFileGenerator', () => { - afterEach(() => { - jest.restoreAllMocks(); - }); - - describe('generateToFile()', () => { - const doc = { - $id: 'CustomClass', - type: 'object', - additionalProperties: true, - properties: { - someProp: { type: 'string' }, - someEnum: { - $id: 'CustomEnum', - type: 'string', - enum: ['Texas', 'Alabama', 'California'], - } - } - }; - test('should throw accurate error if file cannot be written', async () => { - const generator = new JavaScriptFileGenerator(); - const expectedError = new Error('write error'); - jest.spyOn(FileHelpers, 'writerToFileSystem').mockRejectedValue(expectedError); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), 'Test', new CommonInputModel(), [])]); - - await expect(generator.generateToFiles(doc, '/test/')).rejects.toEqual(expectedError); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); - }); - test('should try and generate models to files', async () => { - const generator = new JavaScriptFileGenerator(); - const outputDir = './test'; - const expectedOutputDirPath = path.resolve(outputDir); - const expectedOutputFilePath = path.resolve(`${outputDir}/Test.js`); - const expectedWriteToFileParameters = [ - 'content', - expectedOutputFilePath, - ]; - jest.spyOn(FileHelpers, 'writerToFileSystem').mockResolvedValue(undefined); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), 'Test', new CommonInputModel(), [])]); - - await generator.generateToFiles(doc, expectedOutputDirPath); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); - expect((FileHelpers.writerToFileSystem as jest.Mock).mock.calls[0]).toEqual(expectedWriteToFileParameters); - }); - test('should ignore models that have not been rendered', async () => { - const generator = new JavaScriptFileGenerator(); - const outputDir = './test'; - const expectedOutputDirPath = path.resolve(outputDir); - jest.spyOn(FileHelpers, 'writerToFileSystem').mockResolvedValue(undefined); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), '', new CommonInputModel(), [])]); - - const models = await generator.generateToFiles(doc, expectedOutputDirPath); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(0); - expect(models).toHaveLength(0); - }); - }); -}); diff --git a/test/generators/javascript/JavaScriptGenerator.spec.ts b/test/generators/javascript/JavaScriptGenerator.spec.ts index 4566ea10ab..c03733391c 100644 --- a/test/generators/javascript/JavaScriptGenerator.spec.ts +++ b/test/generators/javascript/JavaScriptGenerator.spec.ts @@ -1,4 +1,4 @@ -import { JavaScriptGenerator } from '../../../src/generators'; +import { JavaScriptGenerator } from '../../../src/generators'; describe('JavaScriptGenerator', () => { let generator: JavaScriptGenerator; @@ -15,33 +15,30 @@ describe('JavaScriptGenerator', () => { reservedEnum: { type: 'string' } }, additionalProperties: false, - required: ['reservedEnum', 'enum'], + required: ['reservedEnum', 'enum'] }; - const expected = `class Address { - reservedReservedEnum; - reservedEnum; - constructor(input) { - this.reservedReservedEnum = input.reservedReservedEnum; - this.reservedEnum = input.reservedEnum; - } - - get reservedReservedEnum() { return this.reservedReservedEnum; } - set reservedReservedEnum(reservedReservedEnum) { this.reservedReservedEnum = reservedReservedEnum; } - - get reservedEnum() { return this.reservedEnum; } - set reservedEnum(reservedEnum) { this.reservedEnum = reservedEnum; } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Address']; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); + }); - let classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toEqual(expected); + test('should not render enums type', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + enum: { type: 'string', enum: ['test', 'test2'] } + } + }; - classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); + test('should render `class` type', async () => { const doc = { $id: 'Address', @@ -51,95 +48,45 @@ describe('JavaScriptGenerator', () => { city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const expected = `class Address { - streetName; - city; - state; - houseNumber; - marriage; - members; - arrayType; - additionalProperties; - sTestPatternProperties; - - constructor(input) { - this.streetName = input.streetName; - this.city = input.city; - this.state = input.state; - this.houseNumber = input.houseNumber; - if (input.hasOwnProperty('marriage')) { - this.marriage = input.marriage; - } - if (input.hasOwnProperty('members')) { - this.members = input.members; - } - this.arrayType = input.arrayType; - } - - get streetName() { return this.streetName; } - set streetName(streetName) { this.streetName = streetName; } - - get city() { return this.city; } - set city(city) { this.city = city; } - - get state() { return this.state; } - set state(state) { this.state = state; } - get houseNumber() { return this.houseNumber; } - set houseNumber(houseNumber) { this.houseNumber = houseNumber; } - - get marriage() { return this.marriage; } - set marriage(marriage) { this.marriage = marriage; } - - get members() { return this.members; } - set members(members) { this.members = members; } - - get arrayType() { return this.arrayType; } - set arrayType(arrayType) { this.arrayType = arrayType; } - - get additionalProperties() { return this.additionalProperties; } - set additionalProperties(additionalProperties) { this.additionalProperties = additionalProperties; } - - get sTestPatternProperties() { return this.sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties) { this.sTestPatternProperties = sTestPatternProperties; } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Address']; - - let classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual([]); - - classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should not render another type than `object`', async () => { const doc = { $id: 'AnyType', - type: ['string', 'number'], + type: ['string', 'number'] }; - const expected = ''; const inputModel = await generator.process(doc); const model = inputModel.models['AnyType']; - const anyModel = await generator.render(model, inputModel); - expect(anyModel.result).toEqual(expected); - expect(anyModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toEqual(''); + expect(models[0].dependencies).toEqual([]); }); test('should work custom preset for `class` type', async () => { @@ -147,51 +94,40 @@ describe('JavaScriptGenerator', () => { $id: 'CustomClass', type: 'object', properties: { - property: { type: 'string' }, + property: { type: 'string' } }, additionalProperties: false }; - const expected = `export class CustomClass { - #property; - - constructor(input) { - this.#property = input.property; - } - - get property() { return this.#property; } - set property(property) { this.#property = property; } -}`; - - generator = new JavaScriptGenerator({ presets: [ - { - class: { - self({ content }) { - return `export ${content}`; - }, - property({ content }) { - return `#${content}`; - }, - ctor() { - return `constructor(input) { + generator = new JavaScriptGenerator({ + presets: [ + { + class: { + self({ content }) { + return `export ${content}`; + }, + property({ content }) { + return `#${content}`; + }, + ctor() { + return `constructor(input) { this.#property = input.property; }`; - }, - getter() { - return 'get property() { return this.#property; }'; - }, - setter() { - return 'set property(property) { this.#property = property; }'; + }, + getter() { + return 'get property() { return this.#property; }'; + }, + setter() { + return 'set property(property) { this.#property = property; }'; + } } } - } - ] }); + ] + }); - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomClass']; - - const classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render models and their dependencies for CJS module system', async () => { @@ -203,19 +139,33 @@ describe('JavaScriptGenerator', () => { city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, - other_model: { type: 'object', $id: 'OtherModel', properties: { street_name: { type: 'string' } }, required: ['street_name'] }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + other_model: { + type: 'object', + $id: 'OtherModel', + properties: { street_name: { type: 'string' } }, + required: ['street_name'] + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const models = await generator.generateCompleteModels(doc, {moduleSystem: 'CJS'}); + generator = new JavaScriptGenerator({ moduleSystem: 'CJS' }); + const models = await generator.generateCompleteModels(doc, {}); expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); expect(models[1].result).toMatchSnapshot(); @@ -229,19 +179,33 @@ describe('JavaScriptGenerator', () => { city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, - other_model: { type: 'object', $id: 'OtherModel', properties: { street_name: { type: 'string' } }, required: ['street_name'] }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + other_model: { + type: 'object', + $id: 'OtherModel', + properties: { street_name: { type: 'string' } }, + required: ['street_name'] + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const models = await generator.generateCompleteModels(doc, {moduleSystem: 'ESM'}); + generator = new JavaScriptGenerator({ moduleSystem: 'ESM' }); + const models = await generator.generateCompleteModels(doc, {}); expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); expect(models[1].result).toMatchSnapshot(); diff --git a/test/generators/javascript/JavaScriptRenderer.spec.ts b/test/generators/javascript/JavaScriptRenderer.spec.ts index 896362b2b9..8f9c2451d2 100644 --- a/test/generators/javascript/JavaScriptRenderer.spec.ts +++ b/test/generators/javascript/JavaScriptRenderer.spec.ts @@ -1,36 +1,22 @@ import { JavaScriptGenerator } from '../../../src/generators'; +import { JavaScriptDependencyManager } from '../../../src/generators/javascript/JavaScriptDependencyManager'; import { JavaScriptRenderer } from '../../../src/generators/javascript/JavaScriptRenderer'; -import { CommonInputModel, CommonModel } from '../../../src/models'; -class MockJavaScriptRenderer extends JavaScriptRenderer { +import { ConstrainedObjectModel, InputMetaModel } from '../../../src/models'; +import { MockJavaScriptRenderer } from '../../TestUtils/TestRenderers'; -} describe('JavaScriptRenderer', () => { - let renderer: JavaScriptRenderer; + let renderer: JavaScriptRenderer; beforeEach(() => { - renderer = new MockJavaScriptRenderer(JavaScriptGenerator.defaultOptions, new JavaScriptGenerator(), [], new CommonModel(), new CommonInputModel()); + renderer = new MockJavaScriptRenderer( + JavaScriptGenerator.defaultOptions, + new JavaScriptGenerator(), + [], + new ConstrainedObjectModel('', undefined, '', {}), + new InputMetaModel(), + new JavaScriptDependencyManager(JavaScriptGenerator.defaultOptions) + ); }); - describe('nameType()', () => { - test('should name the type', () => { - const name = renderer.nameType('type__someType'); - expect(name).toEqual('TypeSomeType'); - }); - test('should render reserved type keyword correctly', () => { - const name = renderer.nameType('enum'); - expect(name).toEqual('Enum'); - }); - }); - - describe('nameProperty()', () => { - test('should name the property', () => { - const name = renderer.nameProperty('property__someProperty'); - expect(name).toEqual('propertySomeProperty'); - }); - test('should render reserved property keyword correctly', () => { - const name = renderer.nameProperty('enum'); - expect(name).toEqual('reservedEnum'); - }); - }); describe('renderComments()', () => { test('Should be able to render comments', () => { expect(renderer.renderComments('someComment')).toEqual(`/** diff --git a/test/generators/javascript/__snapshots__/JavaScriptGenerator.spec.ts.snap b/test/generators/javascript/__snapshots__/JavaScriptGenerator.spec.ts.snap index 03941edbdd..8e12660dcd 100644 --- a/test/generators/javascript/__snapshots__/JavaScriptGenerator.spec.ts.snap +++ b/test/generators/javascript/__snapshots__/JavaScriptGenerator.spec.ts.snap @@ -1,5 +1,99 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`JavaScriptGenerator should not render \`class\` with reserved keyword 1`] = ` +"class Address { + reservedReservedEnum; + reservedEnum; + + constructor(input) { + this.reservedReservedEnum = input.reservedReservedEnum; + this.reservedEnum = input.reservedEnum; + } + + get reservedReservedEnum() { return this.reservedReservedEnum; } + set reservedReservedEnum(reservedReservedEnum) { this.reservedReservedEnum = reservedReservedEnum; } + + get reservedEnum() { return this.reservedEnum; } + set reservedEnum(reservedEnum) { this.reservedEnum = reservedEnum; } +}" +`; + +exports[`JavaScriptGenerator should not render enums type 1`] = ` +"class Address { + reservedEnum; + additionalProperties; + + constructor(input) { + if (input.hasOwnProperty('reservedEnum')) { + this.reservedEnum = input.reservedEnum; + } + if (input.hasOwnProperty('additionalProperties')) { + this.additionalProperties = input.additionalProperties; + } + } + + get reservedEnum() { return this.reservedEnum; } + set reservedEnum(reservedEnum) { this.reservedEnum = reservedEnum; } + + get additionalProperties() { return this.additionalProperties; } + set additionalProperties(additionalProperties) { this.additionalProperties = additionalProperties; } +}" +`; + +exports[`JavaScriptGenerator should render \`class\` type 1`] = ` +"class Address { + streetName; + city; + state; + houseNumber; + marriage; + members; + arrayType; + additionalProperties; + + constructor(input) { + this.streetName = input.streetName; + this.city = input.city; + this.state = input.state; + this.houseNumber = input.houseNumber; + if (input.hasOwnProperty('marriage')) { + this.marriage = input.marriage; + } + if (input.hasOwnProperty('members')) { + this.members = input.members; + } + this.arrayType = input.arrayType; + if (input.hasOwnProperty('additionalProperties')) { + this.additionalProperties = input.additionalProperties; + } + } + + get streetName() { return this.streetName; } + set streetName(streetName) { this.streetName = streetName; } + + get city() { return this.city; } + set city(city) { this.city = city; } + + get state() { return this.state; } + set state(state) { this.state = state; } + + get houseNumber() { return this.houseNumber; } + set houseNumber(houseNumber) { this.houseNumber = houseNumber; } + + get marriage() { return this.marriage; } + set marriage(marriage) { this.marriage = marriage; } + + get members() { return this.members; } + set members(members) { this.members = members; } + + get arrayType() { return this.arrayType; } + set arrayType(arrayType) { this.arrayType = arrayType; } + + get additionalProperties() { return this.additionalProperties; } + set additionalProperties(additionalProperties) { this.additionalProperties = additionalProperties; } +}" +`; + exports[`JavaScriptGenerator should render models and their dependencies for CJS module system 1`] = ` "const OtherModel = require('./OtherModel'); @@ -13,7 +107,6 @@ class Address { arrayType; otherModel; additionalProperties; - sTestPatternProperties; constructor(input) { this.streetName = input.streetName; @@ -30,6 +123,9 @@ class Address { if (input.hasOwnProperty('otherModel')) { this.otherModel = input.otherModel; } + if (input.hasOwnProperty('additionalProperties')) { + this.additionalProperties = input.additionalProperties; + } } get streetName() { return this.streetName; } @@ -58,9 +154,6 @@ class Address { get additionalProperties() { return this.additionalProperties; } set additionalProperties(additionalProperties) { this.additionalProperties = additionalProperties; } - - get sTestPatternProperties() { return this.sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties) { this.sTestPatternProperties = sTestPatternProperties; } } module.exports = Address;" `; @@ -74,6 +167,9 @@ class OtherModel { constructor(input) { this.streetName = input.streetName; + if (input.hasOwnProperty('additionalProperties')) { + this.additionalProperties = input.additionalProperties; + } } get streetName() { return this.streetName; } @@ -98,7 +194,6 @@ class Address { arrayType; otherModel; additionalProperties; - sTestPatternProperties; constructor(input) { this.streetName = input.streetName; @@ -115,6 +210,9 @@ class Address { if (input.hasOwnProperty('otherModel')) { this.otherModel = input.otherModel; } + if (input.hasOwnProperty('additionalProperties')) { + this.additionalProperties = input.additionalProperties; + } } get streetName() { return this.streetName; } @@ -143,9 +241,6 @@ class Address { get additionalProperties() { return this.additionalProperties; } set additionalProperties(additionalProperties) { this.additionalProperties = additionalProperties; } - - get sTestPatternProperties() { return this.sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties) { this.sTestPatternProperties = sTestPatternProperties; } } export default Address; " @@ -160,6 +255,9 @@ class OtherModel { constructor(input) { this.streetName = input.streetName; + if (input.hasOwnProperty('additionalProperties')) { + this.additionalProperties = input.additionalProperties; + } } get streetName() { return this.streetName; } @@ -171,3 +269,16 @@ class OtherModel { export default OtherModel; " `; + +exports[`JavaScriptGenerator should work custom preset for \`class\` type 1`] = ` +"export class CustomClass { + #property; + + constructor(input) { + this.#property = input.property; + } + + get property() { return this.#property; } + set property(property) { this.#property = property; } +}" +`; diff --git a/test/generators/javascript/constrainer/EnumConstrainer.spec.ts b/test/generators/javascript/constrainer/EnumConstrainer.spec.ts new file mode 100644 index 0000000000..3c6bde74f5 --- /dev/null +++ b/test/generators/javascript/constrainer/EnumConstrainer.spec.ts @@ -0,0 +1,118 @@ +import { JavaScriptDefaultConstraints } from '../../../../src/generators/javascript/JavaScriptConstrainer'; +import { EnumModel } from '../../../../src/models/MetaModel'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../../src'; +describe('EnumConstrainer', () => { + const enumModel = new EnumModel('test', undefined, []); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [] + ); + + describe('enum keys', () => { + test('should never render special chars', () => { + const constrainedKey = JavaScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '%' + }); + expect(constrainedKey).toEqual('%'); + }); + test('should not render number as start char', () => { + const constrainedKey = JavaScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '1' + }); + expect(constrainedKey).toEqual('1'); + }); + test('should not contain duplicate keys', () => { + const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel( + 'EMPTY', + 'return' + ); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [existingConstrainedEnumValueModel] + ); + const constrainedKey = JavaScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual(''); + }); + test('should never contain empty keys', () => { + const constrainedKey = JavaScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual(''); + }); + test('should use constant naming format', () => { + const constrainedKey = JavaScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual('some weird_value!"#2'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = JavaScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'return' + }); + expect(constrainedKey).toEqual('return'); + }); + }); + describe('enum values', () => { + test('should render value as is', () => { + const constrainedValue = JavaScriptDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 'string value' + }); + expect(constrainedValue).toEqual('string value'); + }); + test('should render boolean values', () => { + const constrainedValue = JavaScriptDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: true + }); + expect(constrainedValue).toEqual(true); + }); + test('should render numbers', () => { + const constrainedValue = JavaScriptDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 123 + }); + expect(constrainedValue).toEqual(123); + }); + test('should render object', () => { + const constrainedValue = JavaScriptDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: { test: 'test' } + }); + expect(constrainedValue).toEqual({ test: 'test' }); + }); + test('should render unknown value', () => { + const constrainedValue = JavaScriptDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: undefined + }); + expect(constrainedValue).toEqual(undefined); + }); + }); +}); diff --git a/test/generators/javascript/constrainer/ModelNameConstrainer.spec.ts b/test/generators/javascript/constrainer/ModelNameConstrainer.spec.ts new file mode 100644 index 0000000000..5cae1a39f8 --- /dev/null +++ b/test/generators/javascript/constrainer/ModelNameConstrainer.spec.ts @@ -0,0 +1,75 @@ +import { JavaScriptDefaultConstraints } from '../../../../src/generators/javascript/JavaScriptConstrainer'; +import { + DefaultModelNameConstraints, + defaultModelNameConstraints, + ModelNameConstraints +} from '../../../../src/generators/javascript/constrainer/ModelNameConstrainer'; +describe('ModelNameConstrainer', () => { + test('should never render special chars', () => { + const constrainedKey = JavaScriptDefaultConstraints.modelName({ + modelName: '%' + }); + expect(constrainedKey).toEqual('Percent'); + }); + test('should never render number as start char', () => { + const constrainedKey = JavaScriptDefaultConstraints.modelName({ + modelName: '1' + }); + expect(constrainedKey).toEqual('Number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = JavaScriptDefaultConstraints.modelName({ + modelName: '' + }); + expect(constrainedKey).toEqual('Empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = JavaScriptDefaultConstraints.modelName({ + modelName: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = JavaScriptDefaultConstraints.modelName({ + modelName: 'return' + }); + expect(constrainedKey).toEqual('ReservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: ModelNameConstraints = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultModelNameConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ modelName: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultModelNameConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultModelNameConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultModelNameConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ modelName: '' }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/javascript/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/javascript/constrainer/PropertyKeyConstrainer.spec.ts new file mode 100644 index 0000000000..9d814bd282 --- /dev/null +++ b/test/generators/javascript/constrainer/PropertyKeyConstrainer.spec.ts @@ -0,0 +1,180 @@ +import { JavaScriptDefaultConstraints } from '../../../../src/generators/javascript/JavaScriptConstrainer'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ObjectModel, + ObjectPropertyModel +} from '../../../../src'; +import { + DefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints, + PropertyKeyConstraintOptions +} from '../../../../src/generators/javascript/constrainer/PropertyKeyConstrainer'; +describe('PropertyKeyConstrainer', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + + const constrainPropertyName = (propertyName: string) => { + const objectPropertyModel = new ObjectPropertyModel( + propertyName, + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + return JavaScriptDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + }; + + test('should never render special chars', () => { + const constrainedKey = constrainPropertyName('%'); + expect(constrainedKey).toEqual('percent'); + }); + test('should not render number as start char', () => { + const constrainedKey = constrainPropertyName('1'); + expect(constrainedKey).toEqual('number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = constrainPropertyName(''); + expect(constrainedKey).toEqual('empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = constrainPropertyName('some weird_value!"#2'); + expect(constrainedKey).toEqual('someWeirdValueExclamationQuotationHash_2'); + }); + test('should not contain duplicate properties', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + const objectPropertyModel = new ObjectPropertyModel( + 'reservedReturn', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + 'reservedReturn', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const objectPropertyModel2 = new ObjectPropertyModel( + 'return', + false, + objectModel + ); + const constrainedObjectPropertyModel2 = new ConstrainedObjectPropertyModel( + 'return', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainedObjectModel.properties['reservedReturn'] = + constrainedObjectPropertyModel; + constrainedObjectModel.properties['return'] = + constrainedObjectPropertyModel2; + const constrainedKey = JavaScriptDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel: objectPropertyModel2, + constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + }); + expect(constrainedKey).toEqual('reservedReservedReturn'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = constrainPropertyName('return'); + expect(constrainedKey).toEqual('reservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultPropertyKeyConstraints( + mockedConstraintCallbacks + ); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_RESERVED_KEYWORDS'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_DUPLICATE_PROPERTIES') + ]; + const overwrittenDefaultFunction = jest.spyOn( + DefaultPropertyKeyConstraints, + 'NAMING_FORMATTER' + ); + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultPropertyKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const constrainedValue = constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + expect(constrainedValue).toEqual(''); + expect(jestCallback).toHaveBeenCalled(); + expect(overwrittenDefaultFunction).not.toHaveBeenCalled(); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/javascript/preset/ExamplePreset.spec.ts b/test/generators/javascript/preset/ExamplePreset.spec.ts index 885e69a5ef..2072cefb61 100644 --- a/test/generators/javascript/preset/ExamplePreset.spec.ts +++ b/test/generators/javascript/preset/ExamplePreset.spec.ts @@ -1,4 +1,7 @@ -import { JavaScriptGenerator, JS_COMMON_PRESET } from '../../../../src/generators'; +import { + JavaScriptGenerator, + JS_COMMON_PRESET +} from '../../../../src/generators'; const doc = { $id: 'Test', type: 'object', @@ -7,13 +10,17 @@ const doc = { properties: { 'string prop': { type: 'string' }, numberProp: { type: 'number' }, - objectProp: { type: 'object', $id: 'NestedTest', properties: { stringProp: { type: 'string' } } } + objectProp: { + type: 'object', + $id: 'NestedTest', + properties: { stringProp: { type: 'string' } } + } }, patternProperties: { '^S(.?)test': { type: 'string' } - }, + } }; describe('Example function generation', () => { test('should render example function for model', async () => { @@ -27,14 +34,9 @@ describe('Example function generation', () => { } ] }); - const inputModel = await generator.process(doc); - const testModel = inputModel.models['Test']; - const nestedTestModel = inputModel.models['NestedTest']; - - const testClass = await generator.renderClass(testModel, inputModel); - const nestedTestClass = await generator.renderClass(nestedTestModel, inputModel); - - expect(testClass.result).toMatchSnapshot(); - expect(nestedTestClass.result).toMatchSnapshot(); + const models = await generator.generate(doc); + expect(models).toHaveLength(2); + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); }); }); diff --git a/test/generators/javascript/preset/MarshallingPreset.spec.ts b/test/generators/javascript/preset/MarshallingPreset.spec.ts index 23cadf7b0d..2939b75263 100644 --- a/test/generators/javascript/preset/MarshallingPreset.spec.ts +++ b/test/generators/javascript/preset/MarshallingPreset.spec.ts @@ -1,31 +1,27 @@ -/* eslint-disable */ - -import { JavaScriptGenerator, JS_COMMON_PRESET } from '../../../../src/generators'; - -import Ajv from 'ajv'; +import { + JavaScriptGenerator, + JS_COMMON_PRESET +} from '../../../../src/generators'; const doc = { definitions: { - 'NestedTest': { - type: 'object', $id: 'NestedTest', properties: {stringProp: { type: 'string' }} + NestedTest: { + type: 'object', + $id: 'NestedTest', + properties: { stringProp: { type: 'string' } } } }, $id: 'Test', type: 'object', - additionalProperties: {$ref: '#/definitions/NestedTest'}, + additionalProperties: { $ref: '#/definitions/NestedTest' }, required: ['string prop'], properties: { 'string prop': { type: 'string' }, - numberProp: { type: 'number' }, - objectProp: { $ref: '#/definitions/NestedTest' } - }, - patternProperties: { - '^S(.?)test': { type: 'string' }, - '^S(.?)AnotherTest': { $ref: '#/definitions/NestedTest' }, - }, + numberProp: { type: 'number' } + } }; describe('Marshalling preset', () => { test('should render un/marshal code', async () => { - const generator = new JavaScriptGenerator({ + const generator = new JavaScriptGenerator({ presets: [ { preset: JS_COMMON_PRESET, @@ -35,15 +31,9 @@ describe('Marshalling preset', () => { } ] }); - const inputModel = await generator.process(doc); - - const testModel = inputModel.models['Test']; - const nestedTestModel = inputModel.models['NestedTest']; - - const testClass = await generator.renderClass(testModel, inputModel); - const nestedTestClass = await generator.renderClass(nestedTestModel, inputModel); - - expect(testClass.result).toMatchSnapshot(); - expect(nestedTestClass.result).toMatchSnapshot(); + const models = await generator.generate(doc); + expect(models).toHaveLength(2); + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); }); }); diff --git a/test/generators/javascript/preset/__snapshots__/ExamplePreset.spec.ts.snap b/test/generators/javascript/preset/__snapshots__/ExamplePreset.spec.ts.snap index 46f550772f..750053d46b 100644 --- a/test/generators/javascript/preset/__snapshots__/ExamplePreset.spec.ts.snap +++ b/test/generators/javascript/preset/__snapshots__/ExamplePreset.spec.ts.snap @@ -6,7 +6,6 @@ exports[`Example function generation should render example function for model 1` numberProp; objectProp; additionalProperties; - sTestPatternProperties; constructor(input) { this.stringProp = input.stringProp; @@ -16,6 +15,9 @@ exports[`Example function generation should render example function for model 1` if (input.hasOwnProperty('objectProp')) { this.objectProp = input.objectProp; } + if (input.hasOwnProperty('additionalProperties')) { + this.additionalProperties = input.additionalProperties; + } } get stringProp() { return this.stringProp; } @@ -30,14 +32,11 @@ exports[`Example function generation should render example function for model 1` get additionalProperties() { return this.additionalProperties; } set additionalProperties(additionalProperties) { this.additionalProperties = additionalProperties; } - get sTestPatternProperties() { return this.sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties) { this.sTestPatternProperties = sTestPatternProperties; } - example(){ const instance = new Test({}); instance.stringProp = \\"string\\"; instance.numberProp = 0; - instance.objectProp = NestedTest.example(); + instance.objectProp = .example(); return instance; } }" @@ -52,6 +51,9 @@ exports[`Example function generation should render example function for model 2` if (input.hasOwnProperty('stringProp')) { this.stringProp = input.stringProp; } + if (input.hasOwnProperty('additionalProperties')) { + this.additionalProperties = input.additionalProperties; + } } get stringProp() { return this.stringProp; } diff --git a/test/generators/javascript/preset/__snapshots__/MarshallingPreset.spec.ts.snap b/test/generators/javascript/preset/__snapshots__/MarshallingPreset.spec.ts.snap index f79f17868d..2eca68bb72 100644 --- a/test/generators/javascript/preset/__snapshots__/MarshallingPreset.spec.ts.snap +++ b/test/generators/javascript/preset/__snapshots__/MarshallingPreset.spec.ts.snap @@ -4,18 +4,15 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` "class Test { stringProp; numberProp; - objectProp; additionalProperties; - sTestPatternProperties; - sAnotherTestPatternProperties; constructor(input) { this.stringProp = input.stringProp; if (input.hasOwnProperty('numberProp')) { this.numberProp = input.numberProp; } - if (input.hasOwnProperty('objectProp')) { - this.objectProp = input.objectProp; + if (input.hasOwnProperty('additionalProperties')) { + this.additionalProperties = input.additionalProperties; } } @@ -25,18 +22,9 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` get numberProp() { return this.numberProp; } set numberProp(numberProp) { this.numberProp = numberProp; } - get objectProp() { return this.objectProp; } - set objectProp(objectProp) { this.objectProp = objectProp; } - get additionalProperties() { return this.additionalProperties; } set additionalProperties(additionalProperties) { this.additionalProperties = additionalProperties; } - get sTestPatternProperties() { return this.sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties) { this.sTestPatternProperties = sTestPatternProperties; } - - get sAnotherTestPatternProperties() { return this.sAnotherTestPatternProperties; } - set sAnotherTestPatternProperties(sAnotherTestPatternProperties) { this.sAnotherTestPatternProperties = sAnotherTestPatternProperties; } - marshal(){ let json = '{' if(this.stringProp !== undefined) { @@ -45,28 +33,8 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` if(this.numberProp !== undefined) { json += \`\\"numberProp\\": \${typeof this.numberProp === 'number' || typeof this.numberProp === 'boolean' ? this.numberProp : JSON.stringify(this.numberProp)},\`; } - if(this.objectProp !== undefined) { - json += \`\\"objectProp\\": \${this.objectProp.marshal()},\`; - } - if(this.sTestPatternProperties !== undefined) { - for (const [key, value] of this.sTestPatternProperties.entries()) { - //Only render pattern properties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; - } - }if(this.sAnotherTestPatternProperties !== undefined) { - for (const [key, value] of this.sAnotherTestPatternProperties.entries()) { - //Only render pattern properties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - json += \`\\"\${key}\\": \${value.marshal()},\`; - } - } - if(this.additionalProperties !== undefined) { - for (const [key, value] of this.additionalProperties.entries()) { - //Only render additionalProperties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - json += \`\\"\${key}\\": \${value.marshal()},\`; - } + if(this.additionalProperties !== undefined) { + json += \`\\"additionalProperties\\": \${typeof this.additionalProperties === 'number' || typeof this.additionalProperties === 'boolean' ? this.additionalProperties : JSON.stringify(this.additionalProperties)},\`; } //Remove potential last comma @@ -83,27 +51,19 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` if (obj[\\"numberProp\\"] !== undefined) { instance.numberProp = obj[\\"numberProp\\"]; } - if (obj[\\"objectProp\\"] !== undefined) { - instance.objectProp = NestedTest.unmarshal(obj[\\"objectProp\\"]); + if (obj[\\"additionalProperties\\"] !== undefined) { + instance.additionalProperties = obj[\\"additionalProperties\\"]; } + + //Not part of core properties - if (instance.sTestPatternProperties === undefined) {instance.sTestPatternProperties = new Map();} - if (instance.sAnotherTestPatternProperties === undefined) {instance.sAnotherTestPatternProperties = new Map();} - - if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"string prop\\",\\"numberProp\\",\\"objectProp\\"].includes(key);}))) { - //Check all pattern properties - if (key.match(new RegExp('^S(.?)test'))) { - instance.sTestPatternProperties.set(key, value); - continue; - }//Check all pattern properties - if (key.match(new RegExp('^S(.?)AnotherTest'))) { - instance.sAnotherTestPatternProperties.set(key, NestedTest.unmarshal(value)); - continue; - } - instance.additionalProperties.set(key, NestedTest.unmarshal(value)); + + //Only go over remaining. properties + for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"stringProp\\",\\"numberProp\\",\\"additionalProperties\\"].includes(key);}))) { + } + return instance; } }" @@ -118,6 +78,9 @@ exports[`Marshalling preset should render un/marshal code 2`] = ` if (input.hasOwnProperty('stringProp')) { this.stringProp = input.stringProp; } + if (input.hasOwnProperty('additionalProperties')) { + this.additionalProperties = input.additionalProperties; + } } get stringProp() { return this.stringProp; } @@ -131,13 +94,8 @@ exports[`Marshalling preset should render un/marshal code 2`] = ` if(this.stringProp !== undefined) { json += \`\\"stringProp\\": \${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},\`; } - - if(this.additionalProperties !== undefined) { - for (const [key, value] of this.additionalProperties.entries()) { - //Only render additionalProperties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; - } + if(this.additionalProperties !== undefined) { + json += \`\\"additionalProperties\\": \${typeof this.additionalProperties === 'number' || typeof this.additionalProperties === 'boolean' ? this.additionalProperties : JSON.stringify(this.additionalProperties)},\`; } //Remove potential last comma @@ -151,14 +109,19 @@ exports[`Marshalling preset should render un/marshal code 2`] = ` if (obj[\\"stringProp\\"] !== undefined) { instance.stringProp = obj[\\"stringProp\\"]; } + if (obj[\\"additionalProperties\\"] !== undefined) { + instance.additionalProperties = obj[\\"additionalProperties\\"]; + } + + //Not part of core properties + + //Only go over remaining. properties + for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"stringProp\\",\\"additionalProperties\\"].includes(key);}))) { - if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"stringProp\\"].includes(key);}))) { - - instance.additionalProperties.set(key, value); } + return instance; } }" diff --git a/test/generators/javascript/preset/utils/ExampleFunctions.spec.ts b/test/generators/javascript/preset/utils/ExampleFunctions.spec.ts index d4f80e0ec1..4038384f3b 100644 --- a/test/generators/javascript/preset/utils/ExampleFunctions.spec.ts +++ b/test/generators/javascript/preset/utils/ExampleFunctions.spec.ts @@ -1,60 +1,95 @@ -import { JavaScriptGenerator } from '../../../../../src/generators'; import { renderValueFromModel } from '../../../../../src/generators/javascript/presets/utils/ExampleFunction'; -import { JavaScriptRenderer } from '../../../../../src/generators/javascript/JavaScriptRenderer'; -import { CommonInputModel, CommonModel } from '../../../../../src/models'; -class MockJavaScriptRenderer extends JavaScriptRenderer { -} -const renderer = new MockJavaScriptRenderer(JavaScriptGenerator.defaultOptions, new JavaScriptGenerator(), [], new CommonModel(), new CommonInputModel()); +import { + CommonModel, + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedIntegerModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedTupleValueModel, + ConstrainedUnionModel +} from '../../../../../src/models'; describe('Example preset', () => { describe('.renderValueFromModel()', () => { test('should render refs correctly', () => { - const input = CommonModel.toCommonModel({ $ref: 'SomeOtherModel' }); - const output = renderValueFromModel(input, renderer); + const refModel = new ConstrainedAnyModel( + 'SomeOtherModel', + undefined, + 'SomeOtherModel' + ); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + '', + refModel + ); + const output = renderValueFromModel(model); expect(output).toEqual('SomeOtherModel.example()'); }); describe('types', () => { test('Should render strings correctly', () => { - const input = CommonModel.toCommonModel({ type: 'string' }); - const output = renderValueFromModel(input, renderer); + const model = new ConstrainedStringModel('test', undefined, ''); + const output = renderValueFromModel(model); expect(output).toEqual('"string"'); }); test('Should render numbers correctly', () => { - const input = CommonModel.toCommonModel({ type: 'number' }); - const output = renderValueFromModel(input, renderer); + const model = new ConstrainedIntegerModel('test', undefined, ''); + const output = renderValueFromModel(model); expect(output).toEqual('0'); }); test('Should render booleans correctly', () => { - const input = CommonModel.toCommonModel({ type: 'boolean' }); - const output = renderValueFromModel(input, renderer); + const model = new ConstrainedBooleanModel('test', undefined, ''); + const output = renderValueFromModel(model); expect(output).toEqual('true'); }); test('Should use first value if there is more then one', () => { - const input = CommonModel.toCommonModel({ type: ['boolean', 'string'] }); - const output = renderValueFromModel(input, renderer); + const stringModel = new ConstrainedStringModel('test', undefined, ''); + const booleanModel = new ConstrainedBooleanModel('test', undefined, ''); + const model = new ConstrainedUnionModel('test', undefined, '', [ + booleanModel, + stringModel + ]); + const output = renderValueFromModel(model); expect(output).toEqual('true'); }); describe('array', () => { - test('should not render anything if no items are defined', () => { - const input = CommonModel.toCommonModel({ type: 'array' }); - const output = renderValueFromModel(input, renderer); - expect(output).toEqual('[]'); - }); test('should render multiple array values', () => { - const input = CommonModel.toCommonModel({ type: 'array', items: [{ type: 'string' }, { type: 'number' }] }); - const output = renderValueFromModel(input, renderer); + const stringModel = new ConstrainedStringModel('test', undefined, ''); + const integerModel = new ConstrainedIntegerModel( + 'test', + undefined, + '' + ); + const model = new ConstrainedTupleModel('test', undefined, '', [ + new ConstrainedTupleValueModel(0, stringModel), + new ConstrainedTupleValueModel(1, integerModel) + ]); + const output = renderValueFromModel(model); expect(output).toEqual('["string", 0]'); }); test('should render single array value', () => { - const input = CommonModel.toCommonModel({ type: 'array', items: { type: 'string' } }); - const output = renderValueFromModel(input, renderer); + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const output = renderValueFromModel(model); expect(output).toEqual('["string"]'); }); }); test('Should ignore if none are present', () => { - const input = CommonModel.toCommonModel({ type: [] }); - const output = renderValueFromModel(input, renderer); + const model = new ConstrainedAnyModel('test', undefined, ''); + const output = renderValueFromModel(model); expect(output).toBeUndefined(); }); }); }); -}); +}); diff --git a/test/generators/kotlin/Constants.spec.ts b/test/generators/kotlin/Constants.spec.ts new file mode 100644 index 0000000000..8c3477c443 --- /dev/null +++ b/test/generators/kotlin/Constants.spec.ts @@ -0,0 +1,11 @@ +import { isReservedKotlinKeyword } from '../../../src/generators/kotlin/Constants'; + +describe('Reserved keywords', () => { + it('shoud return true if the word is a reserved keyword', () => { + expect(isReservedKotlinKeyword('as')).toBe(true); + }); + + it('should return false if the word is not a reserved keyword', () => { + expect(isReservedKotlinKeyword('dinosaur')).toBe(false); + }); +}); diff --git a/test/generators/kotlin/KotlinConstrainer.spec.ts b/test/generators/kotlin/KotlinConstrainer.spec.ts new file mode 100644 index 0000000000..81f1412607 --- /dev/null +++ b/test/generators/kotlin/KotlinConstrainer.spec.ts @@ -0,0 +1,418 @@ +import { KotlinDefaultTypeMapping } from '../../../src/generators/kotlin/KotlinConstrainer'; +import { KotlinGenerator, KotlinOptions } from '../../../src'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedEnumValueModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedTupleValueModel, + ConstrainedUnionModel +} from '../../../src'; +describe('KotlinConstrainer', () => { + describe('ObjectModel', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedObjectModel('test', undefined, '', {}); + const type = KotlinDefaultTypeMapping.Object({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Reference', () => { + test('should render the constrained name as type', () => { + const refModel = new ConstrainedAnyModel('test', undefined, ''); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + '', + refModel + ); + const type = KotlinDefaultTypeMapping.Reference({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Any', () => { + test('should render type', () => { + const model = new ConstrainedAnyModel('test', undefined, ''); + const type = KotlinDefaultTypeMapping.Any({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Any'); + }); + }); + describe('Float', () => { + test('should render type', () => { + const model = new ConstrainedFloatModel('test', undefined, ''); + const type = KotlinDefaultTypeMapping.Float({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Double'); + }); + test('should render Float when original input has number format', () => { + const model = new ConstrainedFloatModel('test', { format: 'float' }, ''); + const type = KotlinDefaultTypeMapping.Float({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Float'); + }); + }); + describe('Integer', () => { + test('should render type', () => { + const model = new ConstrainedIntegerModel('test', undefined, ''); + const type = KotlinDefaultTypeMapping.Integer({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Int'); + }); + test('should render Int when original input has integer format', () => { + const model = new ConstrainedIntegerModel( + 'test', + { format: 'int32' }, + '' + ); + const type = KotlinDefaultTypeMapping.Integer({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Int'); + }); + test('should render Long when original input has long format', () => { + const model = new ConstrainedIntegerModel('test', { format: 'long' }, ''); + const type = KotlinDefaultTypeMapping.Integer({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Long'); + }); + test('should render Long when original input has int64 format', () => { + const model = new ConstrainedIntegerModel( + 'test', + { format: 'int64' }, + '' + ); + const type = KotlinDefaultTypeMapping.Integer({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Long'); + }); + }); + describe('String', () => { + test('should render type', () => { + const model = new ConstrainedStringModel('test', undefined, ''); + const type = KotlinDefaultTypeMapping.String({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('String'); + }); + test('should render LocalDate when original input has date format', () => { + const model = new ConstrainedStringModel('test', { format: 'date' }, ''); + const type = KotlinDefaultTypeMapping.String({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('java.time.LocalDate'); + }); + test('should render OffsetTime when original input has time format', () => { + const model = new ConstrainedStringModel('test', { format: 'time' }, ''); + const type = KotlinDefaultTypeMapping.String({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('java.time.OffsetTime'); + }); + test('should render OffsetDateTime when original input has dateTime format', () => { + const model = new ConstrainedStringModel( + 'test', + { format: 'dateTime' }, + '' + ); + const type = KotlinDefaultTypeMapping.String({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('java.time.OffsetDateTime'); + }); + test('should render OffsetDateTime when original input has date-time format', () => { + const model = new ConstrainedStringModel( + 'test', + { format: 'date-time' }, + '' + ); + const type = KotlinDefaultTypeMapping.String({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('java.time.OffsetDateTime'); + }); + test('should render byte when original input has binary format', () => { + const model = new ConstrainedStringModel( + 'test', + { format: 'binary' }, + '' + ); + const type = KotlinDefaultTypeMapping.String({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('ByteArray'); + }); + }); + + describe('Boolean', () => { + test('should render type', () => { + const model = new ConstrainedBooleanModel('test', undefined, ''); + const type = KotlinDefaultTypeMapping.Boolean({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Boolean'); + }); + }); + + describe('Tuple', () => { + test('should render type', () => { + const stringModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const tupleValueModel = new ConstrainedTupleValueModel(0, stringModel); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel + ]); + const type = KotlinDefaultTypeMapping.Tuple({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('List'); + }); + test('should render multiple tuple types', () => { + const stringModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const tupleValueModel0 = new ConstrainedTupleValueModel(0, stringModel); + const tupleValueModel1 = new ConstrainedTupleValueModel(1, stringModel); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel0, + tupleValueModel1 + ]); + const type = KotlinDefaultTypeMapping.Tuple({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('List'); + }); + }); + + describe('Array', () => { + test('should render type', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const options: KotlinOptions = { + ...KotlinGenerator.defaultOptions, + collectionType: 'Array' + }; + const type = KotlinDefaultTypeMapping.Array({ + constrainedModel: model, + options + }); + expect(type).toEqual('Array'); + }); + test('should render array as a list', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const options: KotlinOptions = { + ...KotlinGenerator.defaultOptions, + collectionType: 'List' + }; + const type = KotlinDefaultTypeMapping.Array({ + constrainedModel: model, + options + }); + expect(type).toEqual('List'); + }); + }); + + describe('Enum', () => { + test('should render string enum values as String type', () => { + const enumValue = new ConstrainedEnumValueModel('test', 'string type'); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = KotlinDefaultTypeMapping.Enum({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('String'); + }); + test('should render boolean enum values as boolean type', () => { + const enumValue = new ConstrainedEnumValueModel('test', true); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = KotlinDefaultTypeMapping.Enum({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Boolean'); + }); + test('should render generic number enum value with format ', () => { + const enumValue = new ConstrainedEnumValueModel('test', 123); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = KotlinDefaultTypeMapping.Enum({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Int'); + }); + test('should render generic number enum value with float format as float type', () => { + const enumValue = new ConstrainedEnumValueModel('test', 12.0); + const model = new ConstrainedEnumModel('test', { format: 'float' }, '', [ + enumValue + ]); + const type = KotlinDefaultTypeMapping.Enum({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Float'); + }); + test('should render generic number enum value with double format as double type', () => { + const enumValue = new ConstrainedEnumValueModel('test', 12.0); + const model = new ConstrainedEnumModel('test', { format: 'double' }, '', [ + enumValue + ]); + const type = KotlinDefaultTypeMapping.Enum({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Double'); + }); + test('should render object enum value as generic Object', () => { + const enumValue = new ConstrainedEnumValueModel('test', {}); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue + ]); + const type = KotlinDefaultTypeMapping.Enum({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Any'); + }); + test('should render multiple value types as generic Object', () => { + const enumValue2 = new ConstrainedEnumValueModel('test', true); + const enumValue1 = new ConstrainedEnumValueModel('test', 'string type'); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue1, + enumValue2 + ]); + const type = KotlinDefaultTypeMapping.Enum({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Any'); + }); + test('should render double and integer as double type', () => { + const enumValue2 = new ConstrainedEnumValueModel('test', 123); + const enumValue1 = new ConstrainedEnumValueModel('test', 123.12); + const model = new ConstrainedEnumModel('test', undefined, '', [ + enumValue1, + enumValue2 + ]); + const type = KotlinDefaultTypeMapping.Enum({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Double'); + }); + test('should render int and long as long type', () => { + const enumValue2 = new ConstrainedEnumValueModel('test', 123); + const enumValue1 = new ConstrainedEnumValueModel('test', 123); + const model = new ConstrainedEnumModel('test', { format: 'long' }, '', [ + enumValue1, + enumValue2 + ]); + const type = KotlinDefaultTypeMapping.Enum({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Long'); + }); + }); + + describe('Union', () => { + test('should render type', () => { + const unionModel = new ConstrainedStringModel('test', undefined, 'str'); + const model = new ConstrainedUnionModel('test', undefined, '', [ + unionModel + ]); + const type = KotlinDefaultTypeMapping.Union({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Any'); + }); + }); + + describe('Dictionary', () => { + test('should render type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'String'); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = KotlinDefaultTypeMapping.Dictionary({ + constrainedModel: model, + options: KotlinGenerator.defaultOptions + }); + expect(type).toEqual('Map'); + }); + }); +}); diff --git a/test/generators/kotlin/KotlinDependencyManager.spec.ts b/test/generators/kotlin/KotlinDependencyManager.spec.ts new file mode 100644 index 0000000000..0e40f085a0 --- /dev/null +++ b/test/generators/kotlin/KotlinDependencyManager.spec.ts @@ -0,0 +1,16 @@ +import { KotlinGenerator } from '../../../src/generators'; +import { KotlinDependencyManager } from '../../../src/generators/kotlin/KotlinDependencyManager'; +describe('KotlinDependencyManager', () => { + describe('addDependency()', () => { + test('Should be able to render dependency', () => { + const dependencyManager = new KotlinDependencyManager( + KotlinGenerator.defaultOptions, + [] + ); + dependencyManager.addDependency('javax.validation.*'); + expect(dependencyManager.dependencies).toEqual([ + 'import javax.validation.*' + ]); + }); + }); +}); diff --git a/test/generators/kotlin/KotlinGenerator.spec.ts b/test/generators/kotlin/KotlinGenerator.spec.ts new file mode 100644 index 0000000000..2183d23f20 --- /dev/null +++ b/test/generators/kotlin/KotlinGenerator.spec.ts @@ -0,0 +1,211 @@ +import { KotlinGenerator } from '../../../src'; + +describe('KotlinGenerator', () => { + let generator: KotlinGenerator; + beforeEach(() => { + generator = new KotlinGenerator(); + }); + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('should not render reserved keyword', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + class: { type: 'string' } + }, + additionalProperties: false + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render `data class` type', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + date: { type: 'string', format: 'date' }, + time: { type: 'string', format: 'time' }, + dateTime: { type: 'string', format: 'date-time' }, + binary: { type: 'string', format: 'binary' } + }, + patternProperties: { + '^S(.?*)test&': { + type: 'string' + } + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] + }; + + const expectedDependencies = []; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); + }); + + test('should render `enum class` type (string type)', async () => { + const doc = { + $id: 'States', + type: 'string', + enum: ['Texas', 'Alabama', 'California', 'New York'] + }; + const expectedDependencies = []; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); + }); + + test('should render `enum` type (integer type)', async () => { + const doc = { + $id: 'Numbers', + type: 'integer', + enum: [0, 1, 2, 3] + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render `enum` type (union type)', async () => { + const doc = { + $id: 'Union', + type: ['string', 'integer', 'boolean'], + enum: ['Texas', 'Alabama', 0, 1, '1', true, { test: 'test' }] + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render enums with translated special characters', async () => { + const doc = { + $id: 'States', + enum: ['test+', 'test', 'test-', 'test?!', '*test'] + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render List type for collections', async () => { + const doc = { + $id: 'CustomClass', + type: 'object', + additionalProperties: false, + properties: { + arrayType: { + type: 'array', + items: { type: 'integer' }, + additionalItems: false + } + } + }; + + generator = new KotlinGenerator({ collectionType: 'List' }); + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render models and their dependencies', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + other_model: { + type: 'object', + $id: 'OtherModel', + properties: { street_name: { type: 'string' } } + } + }, + patternProperties: { + '^S(.?*)test&': { + type: 'string' + } + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] + }; + const config = { packageName: 'test.package' }; + const models = await generator.generateCompleteModels(doc, config); + expect(models).toHaveLength(2); + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); + }); + test('should escape reserved keywords in package name', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + } + }, + patternProperties: { + '^S(.?*)test&': { + type: 'string' + } + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] + }; + const config = { packageName: 'test.class.package' }; + const models = await generator.generateCompleteModels(doc, config); + + const expectedPackageDeclaration = 'package test.`class`.`package'; + expect(models[0].result).toContain(expectedPackageDeclaration); + }); +}); diff --git a/test/generators/kotlin/KotlinRenderer.spec.ts b/test/generators/kotlin/KotlinRenderer.spec.ts new file mode 100644 index 0000000000..b71827f6cb --- /dev/null +++ b/test/generators/kotlin/KotlinRenderer.spec.ts @@ -0,0 +1,53 @@ +import { KotlinGenerator } from '../../../src/generators/kotlin'; +import { KotlinRenderer } from '../../../src/generators/kotlin/KotlinRenderer'; +import { ConstrainedObjectModel, InputMetaModel } from '../../../src/models'; +import { MockKotlinRenderer } from '../../TestUtils/TestRenderers'; +import { prefix } from 'concurrently/dist/src/defaults'; + +describe('KotlinRenderer', () => { + let renderer: KotlinRenderer; + beforeEach(() => { + renderer = new MockKotlinRenderer( + KotlinGenerator.defaultOptions, + new KotlinGenerator(), + [], + new ConstrainedObjectModel('', undefined, '', {}), + new InputMetaModel() + ); + }); + + describe('renderComments()', () => { + test('Should be able to render comments', () => { + expect(renderer.renderComments('someComment')).toEqual(`/** + * someComment + */`); + }); + }); + + describe('renderAnnotation()', () => { + test('Should render', () => { + expect(renderer.renderAnnotation('someComment')).toEqual('@SomeComment'); + }); + test('Should be able to render multiple values', () => { + expect( + renderer.renderAnnotation('someComment', { test: 1, cool: '"story"' }) + ).toEqual('@SomeComment(test=1, cool="story")'); + }); + test('Should be able to render one value', () => { + expect( + renderer.renderAnnotation('someComment', { test: '"test2"' }) + ).toEqual('@SomeComment(test="test2")'); + }); + test('Should be able to use different prefixes', () => { + expect(renderer.renderAnnotation('someComment', null, 'get:')).toEqual( + '@get:SomeComment' + ); + expect(renderer.renderAnnotation('someComment', null, 'field:')).toEqual( + '@field:SomeComment' + ); + expect(renderer.renderAnnotation('someComment', null, 'param:')).toEqual( + '@param:SomeComment' + ); + }); + }); +}); diff --git a/test/generators/kotlin/__snapshots__/KotlinGenerator.spec.ts.snap b/test/generators/kotlin/__snapshots__/KotlinGenerator.spec.ts.snap new file mode 100644 index 0000000000..fe4c92ca7a --- /dev/null +++ b/test/generators/kotlin/__snapshots__/KotlinGenerator.spec.ts.snap @@ -0,0 +1,97 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`KotlinGenerator should not render reserved keyword 1`] = ` +"data class Address( + val reservedClass: String, +)" +`; + +exports[`KotlinGenerator should render \`data class\` type 1`] = ` +"data class Address( + val streetName: String, + val city: String, + val state: String, + val houseNumber: Double, + val marriage: Boolean, + val members: Any, + val arrayType: List, + val date: java.time.LocalDate, + val time: java.time.OffsetTime, + val dateTime: java.time.OffsetDateTime, + val binary: ByteArray, + val additionalProperties: Map, +)" +`; + +exports[`KotlinGenerator should render \`enum class\` type (string type) 1`] = ` +"enum class States(val value: String) { + TEXAS(\\"Texas\\"), + ALABAMA(\\"Alabama\\"), + CALIFORNIA(\\"California\\"), + NEW_YORK(\\"New York\\"); +}" +`; + +exports[`KotlinGenerator should render \`enum\` type (integer type) 1`] = ` +"enum class Numbers(val value: Int) { + NUMBER_0(0), + NUMBER_1(1), + NUMBER_2(2), + NUMBER_3(3); +}" +`; + +exports[`KotlinGenerator should render \`enum\` type (union type) 1`] = ` +"enum class Union(val value: Any) { + TEXAS(\\"Texas\\"), + ALABAMA(\\"Alabama\\"), + NUMBER_0(0), + NUMBER_1(1), + RESERVED_NUMBER_1(\\"1\\"), + TRUE(true), + CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT(\\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\"); +}" +`; + +exports[`KotlinGenerator should render List type for collections 1`] = ` +"data class CustomClass( + val arrayType: List, +)" +`; + +exports[`KotlinGenerator should render enums with translated special characters 1`] = ` +"enum class States(val value: String) { + TEST_PLUS(\\"test+\\"), + TEST(\\"test\\"), + TEST_MINUS(\\"test-\\"), + TEST_QUESTION_EXCLAMATION(\\"test?!\\"), + ASTERISK_TEST(\\"*test\\"); +}" +`; + +exports[`KotlinGenerator should render models and their dependencies 1`] = ` +"package test.\`package\` + + +data class Address( + val streetName: String, + val city: String, + val state: String, + val houseNumber: Double, + val marriage: Boolean, + val members: Any, + val arrayType: List, + val otherModel: OtherModel, + val additionalProperties: Map, +)" +`; + +exports[`KotlinGenerator should render models and their dependencies 2`] = ` +"package test.\`package\` + + +data class OtherModel( + val streetName: String, + val additionalProperties: Map, +)" +`; diff --git a/test/generators/kotlin/constrainer/EnumConstrainer.spec.ts b/test/generators/kotlin/constrainer/EnumConstrainer.spec.ts new file mode 100644 index 0000000000..a2580ab409 --- /dev/null +++ b/test/generators/kotlin/constrainer/EnumConstrainer.spec.ts @@ -0,0 +1,160 @@ +import { KotlinDefaultConstraints } from '../../../../src/generators/kotlin/KotlinConstrainer'; +import { EnumModel } from '../../../../src'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../../src'; +import { + defaultEnumKeyConstraints, + ModelEnumKeyConstraints, + DefaultEnumKeyConstraints +} from '../../../../src/generators/kotlin/constrainer/EnumConstrainer'; + +describe('EnumConstrainer', () => { + const enumModel = new EnumModel('test', undefined, []); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [] + ); + + describe('enum keys', () => { + test('should never render special chars', () => { + const constrainedKey = KotlinDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '%' + }); + expect(constrainedKey).toEqual('PERCENT'); + }); + test('should not render number as start char', () => { + const constrainedKey = KotlinDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '1' + }); + expect(constrainedKey).toEqual('NUMBER_1'); + }); + test('should not contain duplicate keys', () => { + const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel( + 'EMPTY', + 'return' + ); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [existingConstrainedEnumValueModel] + ); + const constrainedKey = KotlinDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('RESERVED_EMPTY'); + }); + test('should never contain empty keys', () => { + const constrainedKey = KotlinDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('EMPTY'); + }); + test('should use constant naming format', () => { + const constrainedKey = KotlinDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual( + 'SOME_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2' + ); + }); + }); + describe('enum values', () => { + test('should render string values', () => { + const constrainedValue = KotlinDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 'string value' + }); + expect(constrainedValue).toEqual('"string value"'); + }); + test('should render boolean values', () => { + const constrainedValue = KotlinDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: true + }); + expect(constrainedValue).toEqual(true); + }); + test('should render numbers', () => { + const constrainedValue = KotlinDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 123 + }); + expect(constrainedValue).toEqual(123); + }); + test('should render object', () => { + const constrainedValue = KotlinDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: { test: 'test' } + }); + expect(constrainedValue).toEqual('"{\\"test\\":\\"test\\"}"'); + }); + test('should render unknown value', () => { + const constrainedValue = KotlinDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: undefined + }); + expect(constrainedValue).toEqual('"undefined"'); + }); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks for enum key', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultEnumKeyConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks for enum key', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultEnumKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_DUPLICATE_KEYS'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultEnumKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/kotlin/constrainer/ModelNameConstrainer.spec.ts b/test/generators/kotlin/constrainer/ModelNameConstrainer.spec.ts new file mode 100644 index 0000000000..40d9bbc29f --- /dev/null +++ b/test/generators/kotlin/constrainer/ModelNameConstrainer.spec.ts @@ -0,0 +1,75 @@ +import { KotlinDefaultConstraints } from '../../../../src/generators/kotlin/KotlinConstrainer'; +import { + DefaultModelNameConstraints, + defaultModelNameConstraints, + ModelNameConstraints +} from '../../../../src/generators/kotlin/constrainer/ModelNameConstrainer'; +describe('ModelNameConstrainer', () => { + test('should never render special chars', () => { + const constrainedKey = KotlinDefaultConstraints.modelName({ + modelName: '%' + }); + expect(constrainedKey).toEqual('Percent'); + }); + test('should never render number as start char', () => { + const constrainedKey = KotlinDefaultConstraints.modelName({ + modelName: '1' + }); + expect(constrainedKey).toEqual('Number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = KotlinDefaultConstraints.modelName({ + modelName: '' + }); + expect(constrainedKey).toEqual('Empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = KotlinDefaultConstraints.modelName({ + modelName: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = KotlinDefaultConstraints.modelName({ + modelName: 'return' + }); + expect(constrainedKey).toEqual('ReservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: ModelNameConstraints = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultModelNameConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ modelName: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultModelNameConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultModelNameConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultModelNameConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ modelName: '' }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/kotlin/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/kotlin/constrainer/PropertyKeyConstrainer.spec.ts new file mode 100644 index 0000000000..eb42e26c29 --- /dev/null +++ b/test/generators/kotlin/constrainer/PropertyKeyConstrainer.spec.ts @@ -0,0 +1,183 @@ +import { KotlinDefaultConstraints } from '../../../../src/generators/kotlin/KotlinConstrainer'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ObjectModel, + ObjectPropertyModel +} from '../../../../src'; +import { + DefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints, + PropertyKeyConstraintOptions +} from '../../../../src/generators/kotlin/constrainer/PropertyKeyConstrainer'; +describe('PropertyKeyConstrainer', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + + const constrainPropertyName = (propertyName: string) => { + const objectPropertyModel = new ObjectPropertyModel( + propertyName, + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + return KotlinDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + }; + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('should never render special chars', () => { + const constrainedKey = constrainPropertyName('%'); + expect(constrainedKey).toEqual('percent'); + }); + test('should not render number as start char', () => { + const constrainedKey = constrainPropertyName('1'); + expect(constrainedKey).toEqual('number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = constrainPropertyName(''); + expect(constrainedKey).toEqual('empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = constrainPropertyName('some weird_value!"#2'); + expect(constrainedKey).toEqual('someWeirdValueExclamationQuotationHash_2'); + }); + test('should not contain duplicate properties', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + const objectPropertyModel = new ObjectPropertyModel( + 'reservedReturn', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + 'reservedReturn', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const objectPropertyModel2 = new ObjectPropertyModel( + 'return', + false, + objectModel + ); + const constrainedObjectPropertyModel2 = new ConstrainedObjectPropertyModel( + 'return', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainedObjectModel.properties['reservedReturn'] = + constrainedObjectPropertyModel; + constrainedObjectModel.properties['return'] = + constrainedObjectPropertyModel2; + const constrainedKey = KotlinDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel: objectPropertyModel2, + constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + }); + expect(constrainedKey).toEqual('reservedReservedReturn'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = constrainPropertyName('return'); + expect(constrainedKey).toEqual('reservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultPropertyKeyConstraints( + mockedConstraintCallbacks + ); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_RESERVED_KEYWORDS'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_DUPLICATE_PROPERTIES') + ]; + const overwrittenDefaultFunction = jest.spyOn( + DefaultPropertyKeyConstraints, + 'NAMING_FORMATTER' + ); + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultPropertyKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const constrainedValue = constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + expect(constrainedValue).toEqual(''); + expect(jestCallback).toHaveBeenCalled(); + expect(overwrittenDefaultFunction).not.toHaveBeenCalled(); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/kotlin/presets/ConstraintsPreset.spec.ts b/test/generators/kotlin/presets/ConstraintsPreset.spec.ts new file mode 100644 index 0000000000..d320810e35 --- /dev/null +++ b/test/generators/kotlin/presets/ConstraintsPreset.spec.ts @@ -0,0 +1,27 @@ +import { KotlinGenerator, KOTLIN_CONSTRAINTS_PRESET } from '../../../../src'; +describe('KOTLIN_CONSTRAINTS_PRESET', () => { + let generator: KotlinGenerator; + beforeEach(() => { + generator = new KotlinGenerator({ presets: [KOTLIN_CONSTRAINTS_PRESET] }); + }); + + test('should render constraints annotations', async () => { + const doc = { + $id: 'Clazz', + type: 'object', + properties: { + min_number_prop: { type: 'number', minimum: 0 }, + max_number_prop: { type: 'number', exclusiveMaximum: 100 }, + array_prop: { type: 'array', minItems: 2, maxItems: 3 }, + string_prop: { type: 'string', pattern: '^I_', minLength: 3 } + }, + required: ['min_number_prop', 'max_number_prop'] + }; + const expectedDependencies = ['import javax.validation.constraints.*']; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); + }); +}); diff --git a/test/generators/kotlin/presets/DescriptionPreset.spec.ts b/test/generators/kotlin/presets/DescriptionPreset.spec.ts new file mode 100644 index 0000000000..68ef1d2524 --- /dev/null +++ b/test/generators/kotlin/presets/DescriptionPreset.spec.ts @@ -0,0 +1,44 @@ +import { + KotlinGenerator, + KOTLIN_DESCRIPTION_PRESET +} from '../../../../src/generators/kotlin'; + +describe('KOTLIN_DESCRIPTION_PRESET', () => { + let generator: KotlinGenerator; + beforeEach(() => { + generator = new KotlinGenerator({ presets: [KOTLIN_DESCRIPTION_PRESET] }); + }); + + test('should render description and examples for class', async () => { + const doc = { + $id: 'Clazz', + type: 'object', + description: 'Description for class', + examples: [{ prop: 'value' }], + properties: { + prop: { + type: 'string', + description: 'Description for prop', + examples: ['exampleValue'] + } + } + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render description and examples for enum', async () => { + const doc = { + $id: 'Enum', + type: 'string', + description: 'Description for enum', + examples: ['value'], + enum: ['on', 'off'] + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); +}); diff --git a/test/generators/kotlin/presets/__snapshots__/ConstraintsPreset.spec.ts.snap b/test/generators/kotlin/presets/__snapshots__/ConstraintsPreset.spec.ts.snap new file mode 100644 index 0000000000..cb10445098 --- /dev/null +++ b/test/generators/kotlin/presets/__snapshots__/ConstraintsPreset.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`KOTLIN_CONSTRAINTS_PRESET should render constraints annotations 1`] = ` +"data class Clazz( + @get:NotNull + @get:Min(0) + val minNumberProp: Double, + @get:NotNull + @get:Max(99) + val maxNumberProp: Double, + @get:Size(min=2, max=3) + val arrayProp: List, + @get:Pattern(regexp=\\"^I_\\") + @get:Size(min=3) + val stringProp: String, + val additionalProperties: Map, +)" +`; diff --git a/test/generators/kotlin/presets/__snapshots__/DescriptionPreset.spec.ts.snap b/test/generators/kotlin/presets/__snapshots__/DescriptionPreset.spec.ts.snap new file mode 100644 index 0000000000..d8a4c26f41 --- /dev/null +++ b/test/generators/kotlin/presets/__snapshots__/DescriptionPreset.spec.ts.snap @@ -0,0 +1,30 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`KOTLIN_DESCRIPTION_PRESET should render description and examples for class 1`] = ` +"/** + * Description for class + * + * @property prop Description for prop + * @property additionalProperties + * + * Examples: + * {\\"prop\\":\\"value\\"} + */ +data class Clazz( + val prop: String, + val additionalProperties: Map, +)" +`; + +exports[`KOTLIN_DESCRIPTION_PRESET should render description and examples for enum 1`] = ` +"/** + * Description for enum + * + * Examples: + * value + */ +enum class Enum(val value: String) { + ON(\\"on\\"), + OFF(\\"off\\"); +}" +`; diff --git a/test/generators/python/Constants.spec.ts b/test/generators/python/Constants.spec.ts new file mode 100644 index 0000000000..e1df9d06fc --- /dev/null +++ b/test/generators/python/Constants.spec.ts @@ -0,0 +1,44 @@ +import { isReservedPythonKeyword } from '../../../src/generators/python/Constants'; + +describe('Reserved keywords', () => { + it('shoud return true if the word is a reserved keyword', () => { + expect(isReservedPythonKeyword('False')).toBe(true); + expect(isReservedPythonKeyword('def')).toBe(true); + expect(isReservedPythonKeyword('if')).toBe(true); + expect(isReservedPythonKeyword('raise')).toBe(true); + expect(isReservedPythonKeyword('None')).toBe(true); + expect(isReservedPythonKeyword('del')).toBe(true); + expect(isReservedPythonKeyword('import')).toBe(true); + expect(isReservedPythonKeyword('return')).toBe(true); + expect(isReservedPythonKeyword('True')).toBe(true); + expect(isReservedPythonKeyword('elif')).toBe(true); + expect(isReservedPythonKeyword('in')).toBe(true); + expect(isReservedPythonKeyword('try')).toBe(true); + expect(isReservedPythonKeyword('and')).toBe(true); + expect(isReservedPythonKeyword('else')).toBe(true); + expect(isReservedPythonKeyword('is')).toBe(true); + expect(isReservedPythonKeyword('while')).toBe(true); + expect(isReservedPythonKeyword('as')).toBe(true); + expect(isReservedPythonKeyword('except')).toBe(true); + expect(isReservedPythonKeyword('lambda')).toBe(true); + expect(isReservedPythonKeyword('with')).toBe(true); + expect(isReservedPythonKeyword('assert')).toBe(true); + expect(isReservedPythonKeyword('finally')).toBe(true); + expect(isReservedPythonKeyword('nonlocal')).toBe(true); + expect(isReservedPythonKeyword('yield')).toBe(true); + expect(isReservedPythonKeyword('break')).toBe(true); + expect(isReservedPythonKeyword('for')).toBe(true); + expect(isReservedPythonKeyword('not')).toBe(true); + expect(isReservedPythonKeyword('class')).toBe(true); + expect(isReservedPythonKeyword('from')).toBe(true); + expect(isReservedPythonKeyword('or')).toBe(true); + expect(isReservedPythonKeyword('continue')).toBe(true); + expect(isReservedPythonKeyword('global')).toBe(true); + expect(isReservedPythonKeyword('pass')).toBe(true); + expect(isReservedPythonKeyword('exec')).toBe(true); + }); + + it('should return false if the word is not a reserved keyword', () => { + expect(isReservedPythonKeyword('dinosaur')).toBe(false); + }); +}); diff --git a/test/generators/python/PythonConstrainer.spec.ts b/test/generators/python/PythonConstrainer.spec.ts new file mode 100644 index 0000000000..ac86be41b2 --- /dev/null +++ b/test/generators/python/PythonConstrainer.spec.ts @@ -0,0 +1,205 @@ +import { PythonDefaultTypeMapping } from '../../../src/generators/python/PythonConstrainer'; +import { PythonGenerator } from '../../../src/generators/python'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedTupleValueModel, + ConstrainedUnionModel +} from '../../../src'; +import { PythonDependencyManager } from '../../../src/generators/python/PythonDependencyManager'; +describe('PythonConstrainer', () => { + const defaultOptions = { + options: PythonGenerator.defaultOptions, + dependencyManager: new PythonDependencyManager( + PythonGenerator.defaultOptions + ) + }; + describe('ObjectModel', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedObjectModel('test', undefined, '', {}); + const type = PythonDefaultTypeMapping.Object({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Reference', () => { + test('should render the constrained name as type', () => { + const refModel = new ConstrainedAnyModel('test', undefined, ''); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + '', + refModel + ); + const type = PythonDefaultTypeMapping.Reference({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Any', () => { + test('should render type', () => { + const model = new ConstrainedAnyModel('test', undefined, ''); + const type = PythonDefaultTypeMapping.Any({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Any'); + }); + }); + describe('Float', () => { + test('should render type', () => { + const model = new ConstrainedFloatModel('test', undefined, ''); + const type = PythonDefaultTypeMapping.Float({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('float'); + }); + }); + describe('Integer', () => { + test('should render type', () => { + const model = new ConstrainedIntegerModel('test', undefined, ''); + const type = PythonDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('int'); + }); + }); + describe('String', () => { + test('should render type', () => { + const model = new ConstrainedStringModel('test', undefined, ''); + const type = PythonDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('str'); + }); + }); + describe('Boolean', () => { + test('should render type', () => { + const model = new ConstrainedBooleanModel('test', undefined, ''); + const type = PythonDefaultTypeMapping.Boolean({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('bool'); + }); + }); + + describe('Tuple', () => { + test('should render type', () => { + const stringModel = new ConstrainedStringModel('test', undefined, 'str'); + const tupleValueModel = new ConstrainedTupleValueModel(0, stringModel); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel + ]); + const type = PythonDefaultTypeMapping.Tuple({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('tuple[str]'); + }); + test('should render multiple tuple types', () => { + const stringModel = new ConstrainedStringModel('test', undefined, 'str'); + const tupleValueModel0 = new ConstrainedTupleValueModel(0, stringModel); + const tupleValueModel1 = new ConstrainedTupleValueModel(1, stringModel); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel0, + tupleValueModel1 + ]); + const type = PythonDefaultTypeMapping.Tuple({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('tuple[str, str]'); + }); + }); + + describe('Array', () => { + test('should render type', () => { + const arrayModel = new ConstrainedStringModel('test', undefined, 'str'); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const type = PythonDefaultTypeMapping.Array({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('list[str]'); + }); + }); + + describe('Enum', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedEnumModel('Test', undefined, '', []); + const type = PythonDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + + describe('Union', () => { + test('should render type', () => { + const unionModel = new ConstrainedStringModel('test', undefined, 'str'); + const model = new ConstrainedUnionModel('test', undefined, '', [ + unionModel + ]); + const type = PythonDefaultTypeMapping.Union({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('str'); + }); + test('should render multiple types', () => { + const unionModel1 = new ConstrainedStringModel('test', undefined, 'str'); + const unionModel2 = new ConstrainedStringModel('test', undefined, 'str'); + const model = new ConstrainedUnionModel('test', undefined, '', [ + unionModel1, + unionModel2 + ]); + const type = PythonDefaultTypeMapping.Union({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('str | str'); + }); + }); + + describe('Dictionary', () => { + test('should render type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'str'); + const valueModel = new ConstrainedStringModel('test', undefined, 'str'); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = PythonDefaultTypeMapping.Dictionary({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('dict[str, str]'); + }); + }); +}); diff --git a/test/generators/python/PythonGenerator.spec.ts b/test/generators/python/PythonGenerator.spec.ts new file mode 100644 index 0000000000..d98c5d8e7e --- /dev/null +++ b/test/generators/python/PythonGenerator.spec.ts @@ -0,0 +1,156 @@ +import { PythonGenerator } from '../../../src/generators/python'; + +describe('PythonGenerator', () => { + let generator: PythonGenerator; + beforeEach(() => { + generator = new PythonGenerator(); + }); + + describe('Enum', () => { + test('should render `enum` with mixed types (union type)', async () => { + const doc = { + $id: 'Things', + enum: ['Texas', 1, '1', false, { test: 'test' }] + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should work custom preset for `enum` type', async () => { + const doc = { + $id: 'CustomEnum', + type: 'string', + enum: ['Texas', 'Alabama', 'California'] + }; + + generator = new PythonGenerator({ + presets: [ + { + enum: { + self({ content }) { + return content; + } + } + } + ] + }); + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render enums with translated special characters', async () => { + const doc = { + $id: 'States', + enum: ['test+', '$test', 'test-', 'test?!', '*test'] + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + }); + describe('Class', () => { + test('should not render reserved keyword', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + del: { type: 'string' }, + reservedDel: { type: 'string' } + }, + additionalProperties: false + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render `class` type', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + } + }, + patternProperties: { + '^S(.?*)test&': { + type: 'string' + } + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] + }; + const expectedDependencies: string[] = []; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); + }); + + test('should work with custom preset for `class` type', async () => { + const doc = { + $id: 'CustomClass', + type: 'object', + properties: { + property: { type: 'string' } + } + }; + generator = new PythonGenerator({ + presets: [ + { + class: { + property({ content }) { + const annotation = 'test1'; + return `${annotation}\n${content}`; + }, + getter({ content }) { + const annotation = 'test2'; + return `${annotation}\n${content}`; + }, + setter({ content }) { + const annotation = 'test3'; + return `${annotation}\n${content}`; + } + } + } + ] + }); + const expectedDependencies: string[] = []; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); + }); + test('should work with empty objects', async () => { + const doc = { + $id: 'CustomClass', + type: 'object', + additionalProperties: false + }; + generator = new PythonGenerator(); + const expectedDependencies: string[] = []; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); + }); + }); +}); diff --git a/test/generators/python/PythonRenderer.spec.ts b/test/generators/python/PythonRenderer.spec.ts new file mode 100644 index 0000000000..d5f2c70858 --- /dev/null +++ b/test/generators/python/PythonRenderer.spec.ts @@ -0,0 +1,34 @@ +import { PythonGenerator } from '../../../src/generators/python'; +import { PythonDependencyManager } from '../../../src/generators/python/PythonDependencyManager'; +import { PythonRenderer } from '../../../src/generators/python/PythonRenderer'; +import { ConstrainedObjectModel, InputMetaModel } from '../../../src/models'; +import { MockPythonRenderer } from '../../TestUtils/TestRenderers'; + +describe('PythonRenderer', () => { + let renderer: PythonRenderer; + beforeEach(() => { + renderer = new MockPythonRenderer( + PythonGenerator.defaultOptions, + new PythonGenerator(), + [], + new ConstrainedObjectModel('', undefined, '', {}), + new InputMetaModel(), + new PythonDependencyManager(PythonGenerator.defaultOptions) + ); + }); + + describe('renderComments()', () => { + test('Should be able to render comments', () => { + expect(renderer.renderComments('someComment')).toEqual(`""" +someComment +"""`); + }); + test('Should be able to render multiple comments', () => { + expect(renderer.renderComments(['someComment', 'someComment'])) + .toEqual(`""" +someComment +someComment +"""`); + }); + }); +}); diff --git a/test/generators/python/__snapshots__/PythonGenerator.spec.ts.snap b/test/generators/python/__snapshots__/PythonGenerator.spec.ts.snap new file mode 100644 index 0000000000..6b3b91c906 --- /dev/null +++ b/test/generators/python/__snapshots__/PythonGenerator.spec.ts.snap @@ -0,0 +1,165 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PythonGenerator Class should not render reserved keyword 1`] = ` +"class Address: + def __init__(self, input): + if hasattr(input, 'reservedReservedDel'): + self._reservedReservedDel = input.reservedReservedDel + if hasattr(input, 'reservedDel'): + self._reservedDel = input.reservedDel + + @property + def reservedReservedDel(self): + return self._reservedReservedDel + @reservedReservedDel.setter + def reservedReservedDel(self, reservedReservedDel): + self._reservedReservedDel = reservedReservedDel + + @property + def reservedDel(self): + return self._reservedDel + @reservedDel.setter + def reservedDel(self, reservedDel): + self._reservedDel = reservedDel +" +`; + +exports[`PythonGenerator Class should render \`class\` type 1`] = ` +"class Address: + def __init__(self, input): + self._streetName = input.streetName + self._city = input.city + self._state = input.state + self._houseNumber = input.houseNumber + if hasattr(input, 'marriage'): + self._marriage = input.marriage + if hasattr(input, 'members'): + self._members = input.members + self._arrayType = input.arrayType + if hasattr(input, 'additionalProperties'): + self._additionalProperties = input.additionalProperties + + @property + def streetName(self): + return self._streetName + @streetName.setter + def streetName(self, streetName): + self._streetName = streetName + + @property + def city(self): + return self._city + @city.setter + def city(self, city): + self._city = city + + @property + def state(self): + return self._state + @state.setter + def state(self, state): + self._state = state + + @property + def houseNumber(self): + return self._houseNumber + @houseNumber.setter + def houseNumber(self, houseNumber): + self._houseNumber = houseNumber + + @property + def marriage(self): + return self._marriage + @marriage.setter + def marriage(self, marriage): + self._marriage = marriage + + @property + def members(self): + return self._members + @members.setter + def members(self, members): + self._members = members + + @property + def arrayType(self): + return self._arrayType + @arrayType.setter + def arrayType(self, arrayType): + self._arrayType = arrayType + + @property + def additionalProperties(self): + return self._additionalProperties + @additionalProperties.setter + def additionalProperties(self, additionalProperties): + self._additionalProperties = additionalProperties +" +`; + +exports[`PythonGenerator Class should work with custom preset for \`class\` type 1`] = ` +"class CustomClass: + test1 + + test1 + + + def __init__(self, input): + if hasattr(input, 'property'): + self._property = input.property + if hasattr(input, 'additionalProperties'): + self._additionalProperties = input.additionalProperties + + test2 + @property + def property(self): + return self._property + test3 + @property.setter + def property(self, property): + self._property = property + + test2 + @property + def additionalProperties(self): + return self._additionalProperties + test3 + @additionalProperties.setter + def additionalProperties(self, additionalProperties): + self._additionalProperties = additionalProperties +" +`; + +exports[`PythonGenerator Class should work with empty objects 1`] = ` +"class CustomClass: + def __init__(self, input): + \\"\\"\\" + No properties + \\"\\"\\" +" +`; + +exports[`PythonGenerator Enum should render \`enum\` with mixed types (union type) 1`] = ` +"class Things(Enum): + TEXAS = \\"Texas\\" + NUMBER_1 = 1 + RESERVED_NUMBER_1 = \\"1\\" + RESERVED_FALSE = \\"false\\" + CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT = \\"{\\\\\\"test\\\\\\":\\\\\\"test\\\\\\"}\\"" +`; + +exports[`PythonGenerator Enum should render enums with translated special characters 1`] = ` +"class States(Enum): + TEST_PLUS = \\"test+\\" + DOLLAR_TEST = \\"$test\\" + TEST_MINUS = \\"test-\\" + TEST_QUESTION_EXCLAMATION = \\"test?!\\" + ASTERISK_TEST = \\"*test\\"" +`; + +exports[`PythonGenerator Enum should work custom preset for \`enum\` type 1`] = ` +"class CustomEnum(Enum): + TEXAS = \\"Texas\\" + ALABAMA = \\"Alabama\\" + CALIFORNIA = \\"California\\"" +`; diff --git a/test/generators/rust/Constants.spec.ts b/test/generators/rust/Constants.spec.ts new file mode 100644 index 0000000000..0e7f270445 --- /dev/null +++ b/test/generators/rust/Constants.spec.ts @@ -0,0 +1,64 @@ +import { isReservedRustKeyword } from '../../../src/generators/rust/Constants'; + +describe('Reserved keywords', () => { + it('shoud return true if the word is a reserved keyword', () => { + expect(isReservedRustKeyword('as')).toBe(true); + expect(isReservedRustKeyword('async')).toBe(true); + expect(isReservedRustKeyword('await')).toBe(true); + expect(isReservedRustKeyword('break')).toBe(true); + expect(isReservedRustKeyword('const')).toBe(true); + expect(isReservedRustKeyword('continue')).toBe(true); + expect(isReservedRustKeyword('crate')).toBe(true); + expect(isReservedRustKeyword('dyn')).toBe(true); + expect(isReservedRustKeyword('else')).toBe(true); + expect(isReservedRustKeyword('enum')).toBe(true); + expect(isReservedRustKeyword('extern')).toBe(true); + expect(isReservedRustKeyword('false')).toBe(true); + expect(isReservedRustKeyword('fn')).toBe(true); + expect(isReservedRustKeyword('for')).toBe(true); + expect(isReservedRustKeyword('if')).toBe(true); + expect(isReservedRustKeyword('impl')).toBe(true); + expect(isReservedRustKeyword('in')).toBe(true); + expect(isReservedRustKeyword('let')).toBe(true); + expect(isReservedRustKeyword('loop')).toBe(true); + expect(isReservedRustKeyword('match')).toBe(true); + expect(isReservedRustKeyword('mod')).toBe(true); + expect(isReservedRustKeyword('move')).toBe(true); + expect(isReservedRustKeyword('mut')).toBe(true); + expect(isReservedRustKeyword('pub')).toBe(true); + expect(isReservedRustKeyword('ref')).toBe(true); + expect(isReservedRustKeyword('return')).toBe(true); + expect(isReservedRustKeyword('self')).toBe(true); + expect(isReservedRustKeyword('Self')).toBe(true); + expect(isReservedRustKeyword('static')).toBe(true); + expect(isReservedRustKeyword('struct')).toBe(true); + expect(isReservedRustKeyword('super')).toBe(true); + expect(isReservedRustKeyword('trait')).toBe(true); + expect(isReservedRustKeyword('true')).toBe(true); + expect(isReservedRustKeyword('try')).toBe(true); + expect(isReservedRustKeyword('type')).toBe(true); + expect(isReservedRustKeyword('unsafe')).toBe(true); + expect(isReservedRustKeyword('use')).toBe(true); + expect(isReservedRustKeyword('where')).toBe(true); + expect(isReservedRustKeyword('while')).toBe(true); + expect(isReservedRustKeyword('union')).toBe(true); + expect(isReservedRustKeyword(`'static`)).toBe(true); + expect(isReservedRustKeyword('macro_rules')).toBe(true); + expect(isReservedRustKeyword('abstract')).toBe(true); + expect(isReservedRustKeyword('become')).toBe(true); + expect(isReservedRustKeyword('box')).toBe(true); + expect(isReservedRustKeyword('do')).toBe(true); + expect(isReservedRustKeyword('final')).toBe(true); + expect(isReservedRustKeyword('macro')).toBe(true); + expect(isReservedRustKeyword('override')).toBe(true); + expect(isReservedRustKeyword('priv')).toBe(true); + expect(isReservedRustKeyword('typeof')).toBe(true); + expect(isReservedRustKeyword('unsized')).toBe(true); + expect(isReservedRustKeyword('yield')).toBe(true); + }); + + it('should return false if the word is not a reserved keyword', () => { + expect(isReservedRustKeyword('dinosaur')).toBe(false); + expect(isReservedRustKeyword('class')).toBe(false); + }); +}); diff --git a/test/generators/rust/RustConstrainer.spec.ts b/test/generators/rust/RustConstrainer.spec.ts new file mode 100644 index 0000000000..22e8c6b8f8 --- /dev/null +++ b/test/generators/rust/RustConstrainer.spec.ts @@ -0,0 +1,639 @@ +import { + deriveEq, + deriveHash, + deriveCopy, + RustDefaultTypeMapping +} from '../../../src/generators/rust/RustConstrainer'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedTupleValueModel, + ConstrainedEnumValueModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedUnionModel, + RustGenerator, + RustOptions, + ConstrainedObjectPropertyModel, + ConstrainedMetaModel +} from '../../../src'; +import { RustDependencyManager } from '../../../src/generators/rust/RustDependencyManager'; +describe('RustConstrainer', () => { + const defaultOptions = { + options: RustGenerator.defaultOptions, + dependencyManager: new RustDependencyManager(RustGenerator.defaultOptions) + }; + describe('ObjectModel', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedObjectModel('test', undefined, '', {}); + const type = RustDefaultTypeMapping.Object({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Reference', () => { + test('should render the constrained name as type', () => { + const refModel = new ConstrainedAnyModel('test', undefined, ''); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + '', + refModel + ); + const type = RustDefaultTypeMapping.Reference({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Any', () => { + test('should render type', () => { + const model = new ConstrainedAnyModel('test', undefined, ''); + const type = RustDefaultTypeMapping.Any({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('serde_json::Value'); + }); + }); + describe('Float', () => { + test('should render type', () => { + const model = new ConstrainedFloatModel('test', undefined, ''); + const type = RustDefaultTypeMapping.Float({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('f64'); + }); + test('should render f32 when original input has number format', () => { + const model = new ConstrainedFloatModel( + 'test', + { format: 'float32' }, + '' + ); + const type = RustDefaultTypeMapping.Float({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('f32'); + }); + }); + describe('Integer', () => { + test('should render type', () => { + const model = new ConstrainedIntegerModel('test', undefined, ''); + const type = RustDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('i32'); + }); + test('should render int when original input has integer format', () => { + const model = new ConstrainedIntegerModel( + 'test', + { format: 'integer' }, + '' + ); + const type = RustDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('i32'); + }); + test('should render int when original input has int32 format', () => { + const model = new ConstrainedIntegerModel( + 'test', + { format: 'int32' }, + '' + ); + const type = RustDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('i32'); + }); + test('should render long when original input has long format', () => { + const model = new ConstrainedIntegerModel('test', { format: 'long' }, ''); + const type = RustDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('i64'); + }); + test('should render long when original input has int64 format', () => { + const model = new ConstrainedIntegerModel( + 'test', + { format: 'int64' }, + '' + ); + const type = RustDefaultTypeMapping.Integer({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('i64'); + }); + }); + describe('String', () => { + test('should render type', () => { + const model = new ConstrainedStringModel('test', undefined, ''); + const type = RustDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('String'); + }); + test('should render Vec when original input has binary format', () => { + const model = new ConstrainedStringModel( + 'test', + { format: 'binary' }, + '' + ); + const type = RustDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Vec'); + }); + }); + describe('Boolean', () => { + test('should render type', () => { + const model = new ConstrainedBooleanModel('test', undefined, ''); + const type = RustDefaultTypeMapping.Boolean({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('bool'); + }); + }); + + describe('Tuple', () => { + test('should render type', () => { + const model = new ConstrainedTupleModel('Test', undefined, '', []); + const type = RustDefaultTypeMapping.Tuple({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Test'); + }); + }); + + describe('Array', () => { + test('should render type', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const type = RustDefaultTypeMapping.Array({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Vec'); + }); + }); + + describe('Enum', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedEnumModel('Test', undefined, '', []); + const type = RustDefaultTypeMapping.Enum({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + + describe('Union', () => { + test('should render type', () => { + const model = new ConstrainedUnionModel('Test', undefined, '', []); + const type = RustDefaultTypeMapping.Union({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('Test'); + }); + }); + + describe('Dictionary', () => { + test('should render type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'String'); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = RustDefaultTypeMapping.Dictionary({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('std::collections::HashMap'); + }); + }); + + describe('derive', () => { + const i32Model = new ConstrainedIntegerModel('test', undefined, ''); + const i64Model = new ConstrainedIntegerModel( + 'test', + { format: 'int64' }, + '' + ); + const f64Model = new ConstrainedFloatModel('test', undefined, ''); + const f32Model = new ConstrainedFloatModel( + 'test', + { format: 'float32' }, + '' + ); + const dictModel = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + f32Model, + f64Model + ); + const anyModel = new ConstrainedAnyModel('test', undefined, ''); + const boolModel = new ConstrainedBooleanModel('test', undefined, ''); + const stringModel = new ConstrainedStringModel('test', undefined, 'String'); + + describe('deriveCopy', () => { + test('should return false for types that do not implement Copy trait', () => { + const refModel = new ConstrainedReferenceModel( + 'test', + undefined, + '', + stringModel + ); + const tupleModel = new ConstrainedTupleModel('test', undefined, '', [ + new ConstrainedTupleValueModel(0, stringModel), + new ConstrainedTupleValueModel(1, anyModel) + ]); + const unionModel = new ConstrainedUnionModel('test', undefined, '', [ + stringModel, + refModel, + dictModel, + anyModel, + tupleModel + ]); + const enumModel = new ConstrainedEnumModel('test', undefined, '', [ + new ConstrainedEnumValueModel('one', stringModel), + new ConstrainedEnumValueModel('two', stringModel) + ]); + + const objectModel = new ConstrainedObjectModel('test', undefined, '', { + stringProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + stringModel + ), + refProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + refModel + ), + dictProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + dictModel + ), + anyProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + anyModel + ), + tupleModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + tupleModel + ), + unionModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + unionModel + ), + enumModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + enumModel + ) + }); + const arrayModel = new ConstrainedArrayModel( + 'test', + undefined, + '', + refModel + ); + expect(deriveCopy(stringModel)).toEqual(false); + expect(deriveCopy(refModel)).toEqual(false); + expect(deriveCopy(unionModel)).toEqual(false); + expect(deriveCopy(dictModel)).toEqual(false); + expect(deriveCopy(anyModel)).toEqual(false); + expect(deriveCopy(tupleModel)).toEqual(false); + expect(deriveCopy(refModel)).toEqual(false); + expect(deriveCopy(enumModel)).toEqual(false); + expect(deriveCopy(objectModel)).toEqual(false); + expect(deriveCopy(arrayModel)).toEqual(false); + }); + + test('should return true for types that implement Copy trait', () => { + const i32Model = new ConstrainedIntegerModel('test', undefined, ''); + const i64Model = new ConstrainedIntegerModel( + 'test', + { format: 'int64' }, + '' + ); + const f64Model = new ConstrainedFloatModel('test', undefined, ''); + const f32Model = new ConstrainedFloatModel( + 'test', + { format: 'float32' }, + '' + ); + const boolModel = new ConstrainedBooleanModel('test', undefined, ''); + const tupleModel = new ConstrainedTupleModel('test', undefined, '', [ + new ConstrainedTupleValueModel(0, boolModel), + new ConstrainedTupleValueModel(1, boolModel) + ]); + const arrayModel = new ConstrainedArrayModel( + 'test', + undefined, + '', + i32Model + ); + const enumModel = new ConstrainedEnumModel('test', undefined, '', [ + new ConstrainedEnumValueModel('one', i32Model), + new ConstrainedEnumValueModel('two', i32Model) + ]); + + const unionModel = new ConstrainedUnionModel('test', undefined, '', [ + i32Model, + i64Model, + f64Model, + f32Model, + boolModel, + tupleModel, + arrayModel, + enumModel + ]); + + const objectModel = new ConstrainedObjectModel('test', undefined, '', { + i32Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i32Model + ), + i64Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i64Model + ), + f32Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + f32Model + ), + f64Mode: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + f64Model + ), + tupleModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + tupleModel + ), + unionModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + unionModel + ), + enumModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + enumModel + ) + }); + + expect(deriveCopy(i32Model)).toEqual(true); + expect(deriveCopy(i64Model)).toEqual(true); + expect(deriveCopy(f64Model)).toEqual(true); + expect(deriveCopy(f32Model)).toEqual(true); + expect(deriveCopy(boolModel)).toEqual(true); + expect(deriveCopy(tupleModel)).toEqual(true); + expect(deriveCopy(arrayModel)).toEqual(true); + expect(deriveCopy(enumModel)).toEqual(true); + expect(deriveCopy(unionModel)).toEqual(true); + expect(deriveCopy(objectModel)).toEqual(true); + }); + }); + + describe('deriveHash', () => { + test('should return false for types that do not implement Hash trait', () => { + expect(deriveHash(f64Model)).toEqual(false); + expect(deriveHash(f32Model)).toEqual(false); + expect(deriveHash(dictModel)).toEqual(false); + expect(deriveHash(anyModel)).toEqual(false); + }); + + test('should return true for types that implement Hash trait', () => { + const tupleModel = new ConstrainedTupleModel('test', undefined, '', [ + new ConstrainedTupleValueModel(0, boolModel), + new ConstrainedTupleValueModel(1, boolModel) + ]); + const arrayModel = new ConstrainedArrayModel( + 'test', + undefined, + '', + i32Model + ); + const enumModel = new ConstrainedEnumModel('test', undefined, '', [ + new ConstrainedEnumValueModel('one', i32Model), + new ConstrainedEnumValueModel('two', i32Model) + ]); + + const unionModel = new ConstrainedUnionModel('test', undefined, '', [ + i32Model, + i64Model, + boolModel, + tupleModel, + arrayModel, + enumModel + ]); + + const objectModel = new ConstrainedObjectModel('test', undefined, '', { + stringProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + stringModel + ), + i32Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i32Model + ), + i64Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i64Model + ), + boolProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + boolModel + ), + tupleModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + tupleModel + ), + unionModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + unionModel + ), + enumModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + enumModel + ) + }); + expect(deriveHash(stringModel)).toEqual(true); + expect(deriveHash(i32Model)).toEqual(true); + expect(deriveHash(i64Model)).toEqual(true); + expect(deriveHash(boolModel)).toEqual(true); + expect(deriveHash(tupleModel)).toEqual(true); + expect(deriveHash(arrayModel)).toEqual(true); + expect(deriveHash(enumModel)).toEqual(true); + expect(deriveHash(unionModel)).toEqual(true); + expect(deriveHash(objectModel)).toEqual(true); + }); + }); + + describe('deriveEq', () => { + test('should return false for types that do not implement Eq trait', () => { + expect(deriveHash(f64Model)).toEqual(false); + expect(deriveHash(f32Model)).toEqual(false); + expect(deriveHash(anyModel)).toEqual(false); + }); + + test('should return true for types that implement Eq trait', () => { + const tupleModel = new ConstrainedTupleModel('test', undefined, '', [ + new ConstrainedTupleValueModel(0, boolModel), + new ConstrainedTupleValueModel(1, boolModel) + ]); + const arrayModel = new ConstrainedArrayModel( + 'test', + undefined, + '', + i32Model + ); + const enumModel = new ConstrainedEnumModel('test', undefined, '', [ + new ConstrainedEnumValueModel('one', i32Model), + new ConstrainedEnumValueModel('two', i32Model) + ]); + + const unionModel = new ConstrainedUnionModel('test', undefined, '', [ + i32Model, + i64Model, + boolModel, + tupleModel, + arrayModel, + enumModel + ]); + const objectModel = new ConstrainedObjectModel('test', undefined, '', { + stringProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + stringModel + ), + i32Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i32Model + ), + i64Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i64Model + ), + boolProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + boolModel + ), + tupleModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + tupleModel + ), + unionModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + unionModel + ), + enumModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + enumModel + ) + }); + expect(deriveEq(stringModel)).toEqual(true); + expect(deriveEq(i32Model)).toEqual(true); + expect(deriveEq(i64Model)).toEqual(true); + expect(deriveEq(boolModel)).toEqual(true); + expect(deriveEq(tupleModel)).toEqual(true); + expect(deriveEq(arrayModel)).toEqual(true); + expect(deriveEq(enumModel)).toEqual(true); + expect(deriveEq(unionModel)).toEqual(true); + expect(deriveEq(objectModel)).toEqual(true); + }); + }); + }); +}); diff --git a/test/generators/rust/RustGenerator.spec.ts b/test/generators/rust/RustGenerator.spec.ts new file mode 100644 index 0000000000..6fdb7a6bee --- /dev/null +++ b/test/generators/rust/RustGenerator.spec.ts @@ -0,0 +1,308 @@ +import { + defaultRustRenderCompleteModelOptions, + RustGenerator, + RustRenderCompleteModelOptions +} from '../../../src/generators'; + +describe('RustGenerator', () => { + let generator: RustGenerator; + beforeEach(() => { + generator = new RustGenerator(); + }); + + describe('Enum', () => { + test('should not render reserved keyword', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + enum: { type: 'string' }, + reservedEnum: { type: 'string' } + }, + additionalProperties: false + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + test('should render `enum` type (integer type)', async () => { + const doc = { + $id: 'Numbers', + type: 'integer', + enum: [0, 1, 2, 3] + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render `enum` with mixed types (union type)', async () => { + const doc = { + $id: 'Things_123', + enum: ['Texas', 1, '1', false, { test: 'test' }] + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should work custom preset for `enum` type', async () => { + const doc = { + $id: 'CustomEnum', + type: 'string', + enum: ['Texas', 'Alabama', 'California'] + }; + + generator = new RustGenerator({ + presets: [ + { + enum: { + self({ content }) { + return content; + } + } + } + ] + }); + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render implement Default for `enum (default provided)', async () => { + // if Default is not specified, the first item in enum is implemented instead + const doc = { + $id: 'CustomEnum', + type: 'string', + default: 'Texas', + enum: ['Texas', 'Alabama', 'California'] + }; + + const options = { + ...defaultRustRenderCompleteModelOptions, + implementDefault: true, + packageName: 'test' + } as RustRenderCompleteModelOptions; + + const models = await generator.generateCompleteModels(doc, options); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render enums with translated special characters', async () => { + const doc = { + $id: 'States', + enum: ['test+', '$test', 'test-', 'test?!', '*test'] + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + }); + describe('Struct & Complete Models', () => { + const doc = { + $id: '_address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + tuple_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }], + additionalItems: false + }, + array_type: { + type: 'array', + items: { type: 'string' }, + additionalItems: false + } + // not yet implemented + // tuple_type_with_untyped_additional_items: { type: 'array', items: [{ type: 'string' }, { type: 'number' }], additionalItems: true }, + // tuple_type_with_typed_additional_items: { type: 'array', items: [{ type: 'string' }, { type: 'number' }], additionalItems: { type: 'string' } }, + // array_type_with_typed_additional_items: { type: 'array', items: { type: 'string' }, additionalItems: { type: 'string' } }, + // array_type_with_any_additional_items: { type: 'array', items: { type: 'string' }, additionalItems: true }, + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + additionalProperties: { + type: 'string' + } + // not yet implemented + // patternProperties: { + // '^S(.?*)test&': { + // type: 'string' + // } + // }, + }; + + test('should render `struct` type ', async () => { + const models = await generator.generate(doc); + expect(models).toHaveLength(3); // Adress, TupleType, and Member models should be generated + expect(models[0].result).toMatchSnapshot(); // Address model + expect(models[1].result).toMatchSnapshot(); // Member model + expect(models[2].result).toMatchSnapshot(); // TupleType model + }); + + test('Should render complete models', async () => { + const options = { + ...defaultRustRenderCompleteModelOptions, + implementDefault: true, + implementNew: true, + supportFiles: true + } as RustRenderCompleteModelOptions; + + const models = await generator.generateCompleteModels(doc, options); + expect(models).toHaveLength(3); + expect(models[0].result).toMatchSnapshot(); // Address model + expect(models[1].result).toMatchSnapshot(); // Member model + expect(models[2].result).toMatchSnapshot(); // TupleType model + }); + }); + + describe('Packaging', () => { + test('Should render supporting files', async () => { + const doc = { + $id: 'Numbers', + type: 'integer', + enum: [0, 1, 2, 3] + }; + const options = { + ...defaultRustRenderCompleteModelOptions, + implementDefault: true, + implementNew: true, + supportFiles: true + } as RustRenderCompleteModelOptions; + const output = await generator.generateCompleteSupport(doc, options); + expect(output).toHaveLength(2); + expect(output[0].result).toMatchSnapshot(); // Cargo.toml + expect(output[1].result).toMatchSnapshot(); // lib.rs + }); + }); + + describe('AsyncAPI with polymorphism', () => { + const asyncapiDoc = { + asyncapi: '2.4.0', + info: { + title: 'Pet', + version: '1.0.0' + }, + channels: {}, + components: { + messages: { + PetMessage: { + payload: { + $id: 'Critter', + discriminator: 'petType', + oneOf: [ + { $ref: '#/components/schemas/Cat' }, + { $ref: '#/components/schemas/Dog' }, + { $ref: '#/components/schemas/StickInsect' } + ] + } + } + }, + schemas: { + Pet: { + type: 'object', + additionalProperties: false, + discriminator: 'petType', + properties: { + petType: { + $id: 'PetType', + type: 'string' + }, + name: { + type: 'string' + } + }, + required: ['petType', 'name'] + }, + Cat: { + allOf: [ + { $ref: '#/components/schemas/Pet' }, + { + type: 'object', + additionalProperties: false, + properties: { + petType: { + const: 'Cat' + }, + huntingSkill: { + $id: 'HuntingSkill', + type: 'string', + enum: ['clueless', 'lazy', 'adventurous', 'aggressive'] + } + }, + required: ['huntingSkill'] + } + ] + }, + Dog: { + allOf: [ + { $ref: '#/components/schemas/Pet' }, + { + type: 'object', + additionalProperties: false, + properties: { + petType: { + const: 'Dog' + }, + packSize: { + type: 'integer', + format: 'int32', + description: 'the size of the pack the dog is from', + minimum: 0 + } + }, + required: ['packSize'] + } + ] + }, + StickInsect: { + allOf: [ + { $ref: '#/components/schemas/Pet' }, + { + type: 'object', + additionalProperties: false, + properties: { + petType: { + const: 'StickBug' + }, + color: { + type: 'string' + } + }, + required: ['color'] + } + ] + } + } + } + }; + + test('should render', async () => { + const models = await generator.generate(asyncapiDoc); + expect(models).toHaveLength(6); + expect(models.map((model) => model.result)).toMatchSnapshot(); + + const cat = models.find((model) => model.modelName === 'Cat'); + expect(cat).not.toBeUndefined(); + expect(cat?.result).toContain('petType'); + expect(cat?.result).toContain('name'); + expect(cat?.result).toContain('huntingSkill'); + expect(cat?.result).not.toContain('packSize'); + expect(cat?.result).not.toContain('color'); + }); + }); +}); diff --git a/test/generators/rust/RustRenderer.spec.ts b/test/generators/rust/RustRenderer.spec.ts new file mode 100644 index 0000000000..153ea95be9 --- /dev/null +++ b/test/generators/rust/RustRenderer.spec.ts @@ -0,0 +1,512 @@ +import { RustGenerator } from '../../../src/generators'; +import { RustRenderer } from '../../../src/generators/rust/RustRenderer'; +import { + CommonModel, + ConstrainedObjectModel, + ConstrainedEnumModel, + ConstrainedStringModel, + ConstrainedFloatModel, + ConstrainedDictionaryModel, + ConstrainedAnyModel, + ConstrainedObjectPropertyModel, + ConstrainedUnionModel, + InputMetaModel, + ConstrainedTupleModel, + ConstrainedTupleValueModel, + ConstrainedEnumValueModel, + ConstrainedIntegerModel, + ConstrainedBooleanModel, + ConstrainedArrayModel +} from '../../../src/models'; +import { MockRustRenderer } from '../../TestUtils/TestRenderers'; + +describe('RustRenderer', () => { + let renderer: RustRenderer; + beforeEach(() => { + renderer = new MockRustRenderer( + RustGenerator.defaultOptions, + new RustGenerator(), + [], + new ConstrainedObjectModel('', undefined, '', {}), + new InputMetaModel() + ); + }); + + describe('renderComments()', () => { + test('Should be able to render comments', () => { + expect(renderer.renderComments('someComment')).toEqual('// someComment'); + }); + }); + + describe('renderMacro()', () => { + const i32Model = new ConstrainedIntegerModel('test', undefined, ''); + const i64Model = new ConstrainedIntegerModel( + 'test', + { format: 'int64' }, + '' + ); + const boolModel = new ConstrainedBooleanModel('test', undefined, ''); + const stringModel = new ConstrainedStringModel('test', undefined, 'String'); + const f64Model = new ConstrainedFloatModel('test', undefined, ''); + const f32Model = new ConstrainedFloatModel( + 'test', + { format: 'float32' }, + '' + ); + const dictModel = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + i32Model, + i64Model + ); + const floatDictModel = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + f32Model, + f64Model + ); + const anyModel = new ConstrainedAnyModel('test', undefined, ''); + + test('Should derive all traits', () => { + const tupleModel = new ConstrainedTupleModel('test', undefined, '', [ + new ConstrainedTupleValueModel(0, boolModel), + new ConstrainedTupleValueModel(1, boolModel) + ]); + const arrayModel = new ConstrainedArrayModel( + 'test', + undefined, + '', + i32Model + ); + const enumModel = new ConstrainedEnumModel('test', undefined, '', [ + new ConstrainedEnumValueModel('one', i32Model), + new ConstrainedEnumValueModel('two', i32Model) + ]); + const unionModel = new ConstrainedUnionModel('test', undefined, '', [ + i32Model, + i64Model, + boolModel, + tupleModel, + arrayModel, + enumModel + ]); + const objectModel = new ConstrainedObjectModel('test', undefined, '', { + i32Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i32Model + ), + i64Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i64Model + ), + boolProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + boolModel + ), + tupleModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + tupleModel + ), + unionModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + unionModel + ), + enumModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + enumModel + ) + }); + + expect(renderer.renderMacro(arrayModel)).toEqual( + '#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]' + ); + expect(renderer.renderMacro(enumModel)).toEqual( + '#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]' + ); + expect(renderer.renderMacro(objectModel)).toEqual( + '#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]' + ); + }); + + test('Should not derive Copy trait', () => { + // String does not implement Copy + const tupleModel = new ConstrainedTupleModel('test', undefined, '', [ + new ConstrainedTupleValueModel(0, stringModel), + new ConstrainedTupleValueModel(1, stringModel) + ]); + const arrayModel = new ConstrainedArrayModel( + 'test', + undefined, + '', + stringModel + ); + const enumModel = new ConstrainedEnumModel('test', undefined, '', [ + new ConstrainedEnumValueModel('one', stringModel), + new ConstrainedEnumValueModel('two', stringModel) + ]); + const unionModel = new ConstrainedUnionModel('test', undefined, '', [ + stringModel, + i32Model, + i64Model, + boolModel, + tupleModel, + arrayModel, + enumModel + ]); + const objectModel = new ConstrainedObjectModel('test', undefined, '', { + i32Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i32Model + ), + i64Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i64Model + ), + boolProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + boolModel + ), + tupleModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + tupleModel + ), + unionModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + unionModel + ), + enumModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + enumModel + ) + }); + + expect(renderer.renderMacro(arrayModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]' + ); + expect(renderer.renderMacro(enumModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]' + ); + expect(renderer.renderMacro(objectModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]' + ); + }); + test('Should not derive Hash, Eq, Ord traits', () => { + // float does not implement Hash, Eq, Ord + const tupleModel = new ConstrainedTupleModel('test', undefined, '', [ + new ConstrainedTupleValueModel(0, f32Model), + new ConstrainedTupleValueModel(1, f64Model) + ]); + const arrayModel = new ConstrainedArrayModel( + 'test', + undefined, + '', + f64Model + ); + const enumModel = new ConstrainedEnumModel('test', undefined, '', [ + new ConstrainedEnumValueModel('one', f64Model), + new ConstrainedEnumValueModel('two', f32Model) + ]); + const unionModel = new ConstrainedUnionModel('test', undefined, '', [ + i32Model, + i64Model, + boolModel, + tupleModel, + arrayModel, + enumModel + ]); + const objectModel = new ConstrainedObjectModel('test', undefined, '', { + i32Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i32Model + ), + i64Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i64Model + ), + boolProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + boolModel + ), + tupleModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + tupleModel + ), + unionModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + unionModel + ), + enumModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + enumModel + ) + }); + + expect(renderer.renderMacro(arrayModel)).toEqual( + '#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]' + ); + expect(renderer.renderMacro(enumModel)).toEqual( + '#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]' + ); + expect(renderer.renderMacro(objectModel)).toEqual( + '#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]' + ); + }); + test('Should not derive Hash, Copy, PartialOrd, Ord traits', () => { + // dict (HashMap) does not implement Copy, PartialOrd, Ord + const tupleModel = new ConstrainedTupleModel('test', undefined, '', [ + new ConstrainedTupleValueModel(0, boolModel), + new ConstrainedTupleValueModel(1, boolModel) + ]); + const arrayModel = new ConstrainedArrayModel( + 'test', + undefined, + '', + dictModel + ); + const enumModel = new ConstrainedEnumModel('test', undefined, '', [ + new ConstrainedEnumValueModel('one', i32Model), + new ConstrainedEnumValueModel('two', dictModel) + ]); + const unionModel = new ConstrainedUnionModel('test', undefined, '', [ + i32Model, + i64Model, + boolModel, + tupleModel, + arrayModel, + enumModel + ]); + const objectModel = new ConstrainedObjectModel('test', undefined, '', { + i32Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i32Model + ), + i64Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i64Model + ), + boolProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + boolModel + ), + tupleModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + tupleModel + ), + unionModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + unionModel + ), + enumModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + enumModel + ) + }); + + expect(renderer.renderMacro(arrayModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]' + ); + expect(renderer.renderMacro(enumModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]' + ); + expect(renderer.renderMacro(objectModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]' + ); + }); + test('Should not derive Hash, Copy, PartialOrd, Ord, PartialEq, traits', () => { + // dict (HashMap) + float does not implement Copy, PartialOrd, Ord, Eq + const tupleModel = new ConstrainedTupleModel('test', undefined, '', [ + new ConstrainedTupleValueModel(0, boolModel), + new ConstrainedTupleValueModel(1, f32Model) + ]); + const arrayModel = new ConstrainedArrayModel( + 'test', + undefined, + '', + floatDictModel + ); + const enumModel = new ConstrainedEnumModel('test', undefined, '', [ + new ConstrainedEnumValueModel('one', f32Model), + new ConstrainedEnumValueModel('two', dictModel) + ]); + const unionModel = new ConstrainedUnionModel('test', undefined, '', [ + i32Model, + i64Model, + boolModel, + tupleModel, + arrayModel, + enumModel + ]); + const objectModel = new ConstrainedObjectModel('test', undefined, '', { + i32Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i32Model + ), + i64Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i64Model + ), + boolProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + boolModel + ), + tupleModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + tupleModel + ), + unionModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + unionModel + ), + enumModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + enumModel + ) + }); + + expect(renderer.renderMacro(arrayModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]' + ); + expect(renderer.renderMacro(enumModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]' + ); + expect(renderer.renderMacro(objectModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]' + ); + }); + test('Should not derive Hash, Copy, PartialOrd, Ord, PartialEq, Eq', () => { + // any prevents those traits + const tupleModel = new ConstrainedTupleModel('test', undefined, '', [ + new ConstrainedTupleValueModel(0, boolModel), + new ConstrainedTupleValueModel(1, anyModel) + ]); + const arrayModel = new ConstrainedArrayModel( + 'test', + undefined, + '', + anyModel + ); + const enumModel = new ConstrainedEnumModel('test', undefined, '', [ + new ConstrainedEnumValueModel('one', i32Model), + new ConstrainedEnumValueModel('two', anyModel) + ]); + const unionModel = new ConstrainedUnionModel('test', undefined, '', [ + i32Model, + i64Model, + boolModel, + tupleModel, + arrayModel, + enumModel + ]); + const objectModel = new ConstrainedObjectModel('test', undefined, '', { + i32Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i32Model + ), + i64Model: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + i64Model + ), + boolProp: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + boolModel + ), + tupleModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + tupleModel + ), + unionModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + unionModel + ), + enumModel: new ConstrainedObjectPropertyModel( + 'test', + 'test', + true, + enumModel + ) + }); + + expect(renderer.renderMacro(arrayModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, Serialize)]' + ); + expect(renderer.renderMacro(enumModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, Serialize)]' + ); + expect(renderer.renderMacro(objectModel)).toEqual( + '#[derive(Clone, Debug, Deserialize, Serialize)]' + ); + }); + }); +}); diff --git a/test/generators/rust/__snapshots__/RustGenerator.spec.ts.snap b/test/generators/rust/__snapshots__/RustGenerator.spec.ts.snap new file mode 100644 index 0000000000..54d570282b --- /dev/null +++ b/test/generators/rust/__snapshots__/RustGenerator.spec.ts.snap @@ -0,0 +1,299 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RustGenerator AsyncAPI with polymorphism should render 1`] = ` +Array [ + "// Critter represents a union of types: Cat, Dog, StickInsect +#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[serde(tag = \\"petType\\")] +pub enum Critter { + #[serde(rename=\\"Cat\\")] + Cat(crate::Cat), + #[serde(rename=\\"Dog\\")] + Dog(crate::Dog), + #[serde(rename=\\"StickInsect\\")] + StickInsect(crate::StickInsect), +} + +", + "// Cat represents a Cat model. +#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Cat { + #[serde(rename=\\"petType\\")] + pub pet_type: Box, + #[serde(rename=\\"name\\")] + pub name: String, + #[serde(rename=\\"huntingSkill\\")] + pub hunting_skill: Box, +} +", + "// PetType represents a PetType model. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum PetType { + #[serde(rename=\\"Cat\\")] + Cat, + #[serde(rename=\\"Dog\\")] + Dog, + #[serde(rename=\\"StickBug\\")] + StickBug, +} +", + "// HuntingSkill represents a HuntingSkill model. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum HuntingSkill { + #[serde(rename=\\"clueless\\")] + Clueless, + #[serde(rename=\\"lazy\\")] + Lazy, + #[serde(rename=\\"adventurous\\")] + Adventurous, + #[serde(rename=\\"aggressive\\")] + Aggressive, +} +", + "// Dog represents a Dog model. +#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Dog { + #[serde(rename=\\"petType\\")] + pub pet_type: Box, + #[serde(rename=\\"name\\")] + pub name: String, + #[serde(rename=\\"packSize\\")] + pub pack_size: i32, +} +", + "// StickInsect represents a StickInsect model. +#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct StickInsect { + #[serde(rename=\\"petType\\")] + pub pet_type: Box, + #[serde(rename=\\"name\\")] + pub name: String, + #[serde(rename=\\"color\\")] + pub color: String, +} +", +] +`; + +exports[`RustGenerator Enum should not render reserved keyword 1`] = ` +"// Address represents a Address model. +#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Address { + #[serde(rename=\\"enum\\", skip_serializing_if = \\"Option::is_none\\")] + pub reserved_enum: Option, + #[serde(rename=\\"reservedEnum\\", skip_serializing_if = \\"Option::is_none\\")] + pub reserved_reserved_enum: Option, +} +" +`; + +exports[`RustGenerator Enum should render \`enum\` type (integer type) 1`] = ` +"// Numbers represents a Numbers model. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum Numbers { + #[serde(rename=\\"0\\")] + Number_0, + #[serde(rename=\\"1\\")] + Number_1, + #[serde(rename=\\"2\\")] + Number_2, + #[serde(rename=\\"3\\")] + Number_3, +} +" +`; + +exports[`RustGenerator Enum should render \`enum\` with mixed types (union type) 1`] = ` +"// Things123 represents a Things123 model. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum Things123 { + #[serde(rename=\\"Texas\\")] + Texas, + #[serde(rename=\\"1\\")] + Number_1, + #[serde(rename=\\"1\\")] + ReservedNumber_1, + #[serde(rename=\\"false\\")] + False, + #[serde(flatten)] + CurlyleftQuotationTestQuotationColonQuotationTestQuotationCurlyright(HashMap), +} +" +`; + +exports[`RustGenerator Enum should render enums with translated special characters 1`] = ` +"// States represents a States model. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum States { + #[serde(rename=\\"test+\\")] + TestPlus, + #[serde(rename=\\"$test\\")] + DollarTest, + #[serde(rename=\\"test-\\")] + TestMinus, + #[serde(rename=\\"test?!\\")] + TestQuestionExclamation, + #[serde(rename=\\"*test\\")] + AsteriskTest, +} +" +`; + +exports[`RustGenerator Enum should render implement Default for \`enum (default provided) 1`] = ` +"// CustomEnum represents a CustomEnum model. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum CustomEnum { + #[serde(rename=\\"Texas\\")] + Texas, + #[serde(rename=\\"Alabama\\")] + Alabama, + #[serde(rename=\\"California\\")] + California, +} +" +`; + +exports[`RustGenerator Enum should work custom preset for \`enum\` type 1`] = ` +"// CustomEnum represents a CustomEnum model. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum CustomEnum { + #[serde(rename=\\"Texas\\")] + Texas, + #[serde(rename=\\"Alabama\\")] + Alabama, + #[serde(rename=\\"California\\")] + California, +} +" +`; + +exports[`RustGenerator Packaging Should render supporting files 1`] = ` +"[package] +name = \\"asyncapi-rs-models\\" +version = \\"0.0.0\\" +authors = [\\"AsyncAPI Rust Champions\\"] +homepage = \\"https://www.asyncapi.com/tools/modelina\\" +repository = \\"https://github.com/asyncapi/modelina\\" +license = \\"Apache-2.0\\" +description = \\"Rust models generated by AsyncAPI Modelina\\" +edition = \\"2018\\" + +[dependencies] +serde = { version = \\"1\\", features = [\\"derive\\"] } +serde_json = { version=\\"1\\", optional = true } + +[dev-dependencies] + +[features] +default = [\\"json\\"] +json = [\\"dep:serde_json\\"]" +`; + +exports[`RustGenerator Packaging Should render supporting files 2`] = ` +"#[macro_use] +extern crate serde; +extern crate serde_json; + +pub mod numbers; +pub use self::numbers::*;" +`; + +exports[`RustGenerator Struct & Complete Models Should render complete models 1`] = ` +"// Address represents a Address model. +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct Address { + #[serde(rename=\\"street_name\\")] + pub street_name: String, + #[serde(rename=\\"city\\")] + pub city: String, + #[serde(rename=\\"state\\")] + pub state: String, + #[serde(rename=\\"house_number\\")] + pub house_number: f64, + #[serde(rename=\\"marriage\\", skip_serializing_if = \\"Option::is_none\\")] + pub marriage: Option, + #[serde(rename=\\"members\\", skip_serializing_if = \\"Option::is_none\\")] + pub members: Option>, + #[serde(rename=\\"tuple_type\\", skip_serializing_if = \\"Option::is_none\\")] + pub tuple_type: Option>, + #[serde(rename=\\"array_type\\")] + pub array_type: Vec, + #[serde(rename=\\"additionalProperties\\", skip_serializing_if = \\"Option::is_none\\")] + pub additional_properties: Option>, +} +" +`; + +exports[`RustGenerator Struct & Complete Models Should render complete models 2`] = ` +"// Members represents a union of types: String, f64, bool +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +#[serde(untagged)] +pub enum Members { + #[serde(rename=\\"MembersOneOf0\\")] + MembersOneOf0(String), + #[serde(rename=\\"MembersOneOf1\\")] + MembersOneOf1(f64), + #[serde(rename=\\"MembersOneOf2\\")] + MembersOneOf2(bool), +} + +" +`; + +exports[`RustGenerator Struct & Complete Models Should render complete models 3`] = ` +"// TupleType represents a TupleType model. +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +pub struct TupleType(String, f64); + +" +`; + +exports[`RustGenerator Struct & Complete Models should render \`struct\` type 1`] = ` +"// Address represents a Address model. +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct Address { + #[serde(rename=\\"street_name\\")] + pub street_name: String, + #[serde(rename=\\"city\\")] + pub city: String, + #[serde(rename=\\"state\\")] + pub state: String, + #[serde(rename=\\"house_number\\")] + pub house_number: f64, + #[serde(rename=\\"marriage\\", skip_serializing_if = \\"Option::is_none\\")] + pub marriage: Option, + #[serde(rename=\\"members\\", skip_serializing_if = \\"Option::is_none\\")] + pub members: Option>, + #[serde(rename=\\"tuple_type\\", skip_serializing_if = \\"Option::is_none\\")] + pub tuple_type: Option>, + #[serde(rename=\\"array_type\\")] + pub array_type: Vec, + #[serde(rename=\\"additionalProperties\\", skip_serializing_if = \\"Option::is_none\\")] + pub additional_properties: Option>, +} +" +`; + +exports[`RustGenerator Struct & Complete Models should render \`struct\` type 2`] = ` +"// Members represents a union of types: String, f64, bool +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +#[serde(untagged)] +pub enum Members { + #[serde(rename=\\"MembersOneOf0\\")] + MembersOneOf0(String), + #[serde(rename=\\"MembersOneOf1\\")] + MembersOneOf1(f64), + #[serde(rename=\\"MembersOneOf2\\")] + MembersOneOf2(bool), +} + +" +`; + +exports[`RustGenerator Struct & Complete Models should render \`struct\` type 3`] = ` +"// TupleType represents a TupleType model. +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +pub struct TupleType(String, f64); + +" +`; diff --git a/test/generators/rust/constrainer/EnumConstrainer.spec.ts b/test/generators/rust/constrainer/EnumConstrainer.spec.ts new file mode 100644 index 0000000000..7aec65e61b --- /dev/null +++ b/test/generators/rust/constrainer/EnumConstrainer.spec.ts @@ -0,0 +1,138 @@ +import { RustDefaultConstraints } from '../../../../src/generators/rust/RustConstrainer'; +import { EnumModel } from '../../../../src/models/MetaModel'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel, + EnumKeyConstraint +} from '../../../../src'; +import { RESERVED_RUST_KEYWORDS } from '../../../../src/generators/rust/Constants'; +import { defaultEnumKeyConstraints } from '../../../../src/generators/rust/constrainer/EnumConstrainer'; +describe('EnumConstrainer', () => { + const enumModel = new EnumModel('test', undefined, []); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [] + ); + + describe('enum keys', () => { + test('should never render special chars', () => { + const constrainedKey = RustDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '%' + }); + expect(constrainedKey).toEqual('Percent'); + }); + test('should not render number as start char', () => { + const constrainedKey = RustDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '1' + }); + expect(constrainedKey).toEqual('Number_1'); + }); + test('should not contain duplicate keys', () => { + const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel( + 'EMPTY', + 'return' + ); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [existingConstrainedEnumValueModel] + ); + const constrainedKey = RustDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('Empty'); + }); + test('should never contain empty keys', () => { + const constrainedKey = RustDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('Empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = RustDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual( + 'SomeWeirdValueExclamationQuotationHash_2' + ); + }); + test('Reserved keywords should not take effect when naming formatter changes its format', () => { + const constrainedKey = RustDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'return' + }); + expect(constrainedKey).toEqual('Return'); + }); + + describe('custom constraints', () => { + test('should make sure reserved keywords cannot be rendered', () => { + const customNamingFormat: Partial = { + NAMING_FORMATTER: (value) => value + }; + const constrainFunction = defaultEnumKeyConstraints(customNamingFormat); + const constrainedKey = constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: 'return' + }); + expect(constrainedKey).toEqual('reserved_return'); + }); + }); + }); + describe('enum values', () => { + test('should render value as is', () => { + const constrainedValue = RustDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 'string value' + }); + expect(constrainedValue).toEqual('string value'); + }); + test('should render boolean values', () => { + const constrainedValue = RustDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: true + }); + expect(constrainedValue).toEqual(true); + }); + test('should render numbers', () => { + const constrainedValue = RustDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 123 + }); + expect(constrainedValue).toEqual(123); + }); + test('should render object', () => { + const constrainedValue = RustDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: { test: 'test' } + }); + expect(constrainedValue).toEqual({ test: 'test' }); + }); + test('should render unknown value', () => { + const constrainedValue = RustDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: undefined + }); + expect(constrainedValue).toEqual(undefined); + }); + }); +}); diff --git a/test/generators/rust/constrainer/ModelNameConstrainer.spec.ts b/test/generators/rust/constrainer/ModelNameConstrainer.spec.ts new file mode 100644 index 0000000000..e196b5024b --- /dev/null +++ b/test/generators/rust/constrainer/ModelNameConstrainer.spec.ts @@ -0,0 +1,41 @@ +import { RustDefaultConstraints } from '../../../../src/generators/rust/RustConstrainer'; +import { + defaultModelNameConstraints, + ModelNameConstraints +} from '../../../../src/generators/rust/constrainer/ModelNameConstrainer'; +describe('ModelNameConstrainer', () => { + test('should never render special chars', () => { + const constrainedKey = RustDefaultConstraints.modelName({ modelName: '%' }); + expect(constrainedKey).toEqual('Percent'); + }); + test('should never render number as start char', () => { + const constrainedKey = RustDefaultConstraints.modelName({ modelName: '1' }); + expect(constrainedKey).toEqual('Number1'); + }); + test('should never contain empty name', () => { + const constrainedKey = RustDefaultConstraints.modelName({ modelName: '' }); + expect(constrainedKey).toEqual('Empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = RustDefaultConstraints.modelName({ + modelName: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash2'); + }); + test('Reserved keywords should not take effect when naming formatter changes its format', () => { + const constrainedKey = RustDefaultConstraints.modelName({ + modelName: 'return' + }); + expect(constrainedKey).toEqual('Return'); + }); + describe('custom constraints', () => { + test('should make sure reserved keywords cannot be rendered', () => { + const customNamingFormat: Partial = { + NAMING_FORMATTER: (value) => value + }; + const constrainFunction = defaultModelNameConstraints(customNamingFormat); + const constrainedKey = constrainFunction({ modelName: 'return' }); + expect(constrainedKey).toEqual('reserved_return'); + }); + }); +}); diff --git a/test/generators/rust/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/rust/constrainer/PropertyKeyConstrainer.spec.ts new file mode 100644 index 0000000000..8d2702f3ef --- /dev/null +++ b/test/generators/rust/constrainer/PropertyKeyConstrainer.spec.ts @@ -0,0 +1,101 @@ +import { RustDefaultConstraints } from '../../../../src/generators/rust/RustConstrainer'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ObjectModel, + ObjectPropertyModel +} from '../../../../src'; +describe('PropertyKeyConstrainer', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + + const constrainPropertyName = (propertyName: string) => { + const objectPropertyModel = new ObjectPropertyModel( + propertyName, + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + return RustDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + }; + + test('should never render special chars', () => { + const constrainedKey = constrainPropertyName('%'); + expect(constrainedKey).toEqual('percent'); + }); + test('should not render number as start char', () => { + const constrainedKey = constrainPropertyName('1'); + expect(constrainedKey).toEqual('number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = constrainPropertyName(''); + expect(constrainedKey).toEqual('empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = constrainPropertyName('some weird_value!"#2'); + expect(constrainedKey).toEqual( + 'some_weird_value_exclamation_quotation_hash_2' + ); + }); + test('should not contain duplicate properties', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + const objectPropertyModel = new ObjectPropertyModel( + 'reserved_return', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + 'reserved_return', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const objectPropertyModel2 = new ObjectPropertyModel( + 'return', + false, + objectModel + ); + const constrainedObjectPropertyModel2 = new ConstrainedObjectPropertyModel( + 'return', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainedObjectModel.properties['reserved_return'] = + constrainedObjectPropertyModel; + constrainedObjectModel.properties['return'] = + constrainedObjectPropertyModel2; + const constrainedKey = RustDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel: objectPropertyModel2, + constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + }); + expect(constrainedKey).toEqual('reserved_reserved_return'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = constrainPropertyName('return'); + expect(constrainedKey).toEqual('reserved_return'); + }); +}); diff --git a/test/generators/rust/presets/CommonPreset.spec.ts b/test/generators/rust/presets/CommonPreset.spec.ts new file mode 100644 index 0000000000..238bbdd564 --- /dev/null +++ b/test/generators/rust/presets/CommonPreset.spec.ts @@ -0,0 +1,210 @@ +import { + defaultRustRenderCompleteModelOptions, + RustGenerator, + RustRenderCompleteModelOptions, + RUST_COMMON_PRESET, + defaultRustCommonPresetOptions, + RustCommonPresetOptions +} from '../../../../src/generators'; + +describe('RUST_COMMON_PRESET', () => { + let generator: RustGenerator; + beforeEach(() => { + generator = new RustGenerator({ presets: [RUST_COMMON_PRESET] }); + }); + + describe('Enum', () => { + test('should render `enum` without Default implementation', async () => { + const doc = { + $id: 'Things', + enum: ['Texas', 1, '1', false, { test: 'test' }] + }; + + generator = new RustGenerator({ + presets: [ + { preset: RUST_COMMON_PRESET, options: { implementDefault: false } } + ] + }); + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render `enum` with mixed types (union type) and Default implementation', async () => { + const doc = { + $id: 'Things', + enum: ['Texas', 1, '1', false, { test: 'test' }] + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render `enum` of union type with Default implementation of string type', async () => { + const doc = { + $id: '_address', + type: 'object', + properties: { + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + optional_members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + } + }, + required: ['members'], + additionalProperties: { + type: 'string' + } + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(3); // Address, Members, OptionalMembers + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); + expect(models[2].result).toMatchSnapshot(); + }); + test('should render `enum` of union type with Default implementation of integer type', async () => { + const doc = { + $id: '_address', + type: 'object', + properties: { + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + optional_members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + } + }, + required: ['members'], + additionalProperties: { + type: 'string' + } + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(3); // Address, Members, OptionalMembers + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); + expect(models[2].result).toMatchSnapshot(); + }); + + test('should render implement Default for `enum (default provided)', async () => { + // if Default is not specified, the first item in enum is implemented instead + const doc = { + $id: 'CustomEnum', + type: 'string', + default: 'Texas', + enum: ['Texas', 'Alabama', 'California'] + }; + + const options = { + ...defaultRustRenderCompleteModelOptions, + implementDefault: true, + packageName: 'test' + } as RustRenderCompleteModelOptions; + + const models = await generator.generateCompleteModels(doc, options); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render enums with translated special characters', async () => { + const doc = { + $id: 'States', + enum: ['test+', '$test', 'test-', 'test?!', '*test'] + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render reserved union for dict array', async () => { + const doc = { + $id: '_class', + type: 'object', + definitions: { + student: { + $id: '_student', + type: 'object', + properties: { + name: { type: 'string' }, + birth: { type: 'number' } + }, + required: ['name', 'birth'] + } + }, + properties: { + students: { + type: 'array', + items: { $ref: '#/definitions/student' } + } + }, + required: ['students'] + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(3); + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); + expect(models[2].result).toMatchSnapshot(); + }); + }); + + describe('Struct & Complete Models', () => { + const doc = { + $id: '_address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + tuple_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }], + additionalItems: false + }, + array_type: { + type: 'array', + items: { type: 'string' }, + additionalItems: false + } + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + additionalProperties: { + type: 'string' + } + }; + + test('should render `struct` without Default or new() implementation', async () => { + const options: RustCommonPresetOptions = { + ...defaultRustCommonPresetOptions, + implementDefault: false, + implementNew: false + }; + + generator = new RustGenerator({ + presets: [{ preset: RUST_COMMON_PRESET, options }] + }); + + const models = await generator.generate(doc); + expect(models).toHaveLength(3); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render `struct` type with all implementations', async () => { + const models = await generator.generate(doc); + expect(models).toHaveLength(3); // Adress, TupleType, and Member models should be generated with default and new implementations + expect(models[0].result).toMatchSnapshot(); // Address model + expect(models[1].result).toMatchSnapshot(); // Member model + expect(models[2].result).toMatchSnapshot(); // TupleType model + }); + }); +}); diff --git a/test/generators/rust/presets/__snapshots__/CommonPreset.spec.ts.snap b/test/generators/rust/presets/__snapshots__/CommonPreset.spec.ts.snap new file mode 100644 index 0000000000..a171cefa0b --- /dev/null +++ b/test/generators/rust/presets/__snapshots__/CommonPreset.spec.ts.snap @@ -0,0 +1,354 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RUST_COMMON_PRESET Enum should render \`enum\` of union type with Default implementation of integer type 1`] = ` +"// Address represents a Address model. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Address { + #[serde(rename=\\"members\\")] + pub members: Box, + #[serde(rename=\\"optional_members\\", skip_serializing_if = \\"Option::is_none\\")] + pub optional_members: Option>, + #[serde(rename=\\"additionalProperties\\", skip_serializing_if = \\"Option::is_none\\")] + pub additional_properties: Option>, +} + +impl Address { + pub fn new(members: crate::Members, optional_members: Option, additional_properties: Option>) -> Address { + Address { + members: Box::new(members), + optional_members: optional_members.map(Box::new), + additional_properties, + } + } +} +" +`; + +exports[`RUST_COMMON_PRESET Enum should render \`enum\` of union type with Default implementation of integer type 2`] = ` +"// Members represents a union of types: String, f64, bool +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +#[serde(untagged)] +pub enum Members { + #[serde(rename=\\"MembersOneOf0\\")] + MembersOneOf0(String), + #[serde(rename=\\"MembersOneOf1\\")] + MembersOneOf1(f64), + #[serde(rename=\\"MembersOneOf2\\")] + MembersOneOf2(bool), +} + +" +`; + +exports[`RUST_COMMON_PRESET Enum should render \`enum\` of union type with Default implementation of integer type 3`] = ` +"// OptionalMembers represents a union of types: String, f64, bool +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +#[serde(untagged)] +pub enum OptionalMembers { + #[serde(rename=\\"OptionalMembersOneOf0\\")] + OptionalMembersOneOf0(String), + #[serde(rename=\\"OptionalMembersOneOf1\\")] + OptionalMembersOneOf1(f64), + #[serde(rename=\\"OptionalMembersOneOf2\\")] + OptionalMembersOneOf2(bool), +} + +" +`; + +exports[`RUST_COMMON_PRESET Enum should render \`enum\` of union type with Default implementation of string type 1`] = ` +"// Address represents a Address model. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Address { + #[serde(rename=\\"members\\")] + pub members: Box, + #[serde(rename=\\"optional_members\\", skip_serializing_if = \\"Option::is_none\\")] + pub optional_members: Option>, + #[serde(rename=\\"additionalProperties\\", skip_serializing_if = \\"Option::is_none\\")] + pub additional_properties: Option>, +} + +impl Address { + pub fn new(members: crate::Members, optional_members: Option, additional_properties: Option>) -> Address { + Address { + members: Box::new(members), + optional_members: optional_members.map(Box::new), + additional_properties, + } + } +} +" +`; + +exports[`RUST_COMMON_PRESET Enum should render \`enum\` of union type with Default implementation of string type 2`] = ` +"// Members represents a union of types: String, f64, bool +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +#[serde(untagged)] +pub enum Members { + #[serde(rename=\\"MembersOneOf0\\")] + MembersOneOf0(String), + #[serde(rename=\\"MembersOneOf1\\")] + MembersOneOf1(f64), + #[serde(rename=\\"MembersOneOf2\\")] + MembersOneOf2(bool), +} + +" +`; + +exports[`RUST_COMMON_PRESET Enum should render \`enum\` of union type with Default implementation of string type 3`] = ` +"// OptionalMembers represents a union of types: String, f64, bool +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +#[serde(untagged)] +pub enum OptionalMembers { + #[serde(rename=\\"OptionalMembersOneOf0\\")] + OptionalMembersOneOf0(String), + #[serde(rename=\\"OptionalMembersOneOf1\\")] + OptionalMembersOneOf1(f64), + #[serde(rename=\\"OptionalMembersOneOf2\\")] + OptionalMembersOneOf2(bool), +} + +" +`; + +exports[`RUST_COMMON_PRESET Enum should render \`enum\` with mixed types (union type) and Default implementation 1`] = ` +"// Things represents a Things model. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum Things { + #[serde(rename=\\"Texas\\")] + Texas, + #[serde(rename=\\"1\\")] + Number_1, + #[serde(rename=\\"1\\")] + ReservedNumber_1, + #[serde(rename=\\"false\\")] + False, + #[serde(flatten)] + CurlyleftQuotationTestQuotationColonQuotationTestQuotationCurlyright(HashMap), +} +impl Default for Things { + fn default() -> Things { + Things::Texas + } +}" +`; + +exports[`RUST_COMMON_PRESET Enum should render \`enum\` without Default implementation 1`] = ` +"// Things represents a Things model. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum Things { + #[serde(rename=\\"Texas\\")] + Texas, + #[serde(rename=\\"1\\")] + Number_1, + #[serde(rename=\\"1\\")] + ReservedNumber_1, + #[serde(rename=\\"false\\")] + False, + #[serde(flatten)] + CurlyleftQuotationTestQuotationColonQuotationTestQuotationCurlyright(HashMap), +} +" +`; + +exports[`RUST_COMMON_PRESET Enum should render enums with translated special characters 1`] = ` +"// States represents a States model. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum States { + #[serde(rename=\\"test+\\")] + TestPlus, + #[serde(rename=\\"$test\\")] + DollarTest, + #[serde(rename=\\"test-\\")] + TestMinus, + #[serde(rename=\\"test?!\\")] + TestQuestionExclamation, + #[serde(rename=\\"*test\\")] + AsteriskTest, +} +impl Default for States { + fn default() -> States { + States::TestPlus + } +}" +`; + +exports[`RUST_COMMON_PRESET Enum should render implement Default for \`enum (default provided) 1`] = ` +"// CustomEnum represents a CustomEnum model. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum CustomEnum { + #[serde(rename=\\"Texas\\")] + Texas, + #[serde(rename=\\"Alabama\\")] + Alabama, + #[serde(rename=\\"California\\")] + California, +} +impl Default for CustomEnum { + fn default() -> CustomEnum { + CustomEnum::Texas + } +}" +`; + +exports[`RUST_COMMON_PRESET Enum should render reserved union for dict array 1`] = ` +"// Class represents a Class model. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Class { + #[serde(rename=\\"students\\")] + pub students: Vec, + #[serde(rename=\\"additionalProperties\\", skip_serializing_if = \\"Option::is_none\\")] + pub additional_properties: Option>, +} + +impl Class { + pub fn new(students: Vec, additional_properties: Option>) -> Class { + Class { + students, + additional_properties, + } + } +} +" +`; + +exports[`RUST_COMMON_PRESET Enum should render reserved union for dict array 2`] = ` +"// Union represents a union of types: Student, serde_json::Value +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum Union { + #[serde(rename=\\"Student\\")] + Student(crate::Student), + #[serde(rename=\\"Undefined\\")] + Undefined(serde_json::Value), +} + +" +`; + +exports[`RUST_COMMON_PRESET Enum should render reserved union for dict array 3`] = ` +"// Student represents a Student model. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Student { + #[serde(rename=\\"name\\")] + pub name: String, + #[serde(rename=\\"birth\\")] + pub birth: f64, + #[serde(rename=\\"additionalProperties\\", skip_serializing_if = \\"Option::is_none\\")] + pub additional_properties: Option>, +} + +impl Student { + pub fn new(name: String, birth: f64, additional_properties: Option>) -> Student { + Student { + name, + birth, + additional_properties, + } + } +} +" +`; + +exports[`RUST_COMMON_PRESET Struct & Complete Models should render \`struct\` type with all implementations 1`] = ` +"// Address represents a Address model. +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct Address { + #[serde(rename=\\"street_name\\")] + pub street_name: String, + #[serde(rename=\\"city\\")] + pub city: String, + #[serde(rename=\\"state\\")] + pub state: String, + #[serde(rename=\\"house_number\\")] + pub house_number: f64, + #[serde(rename=\\"marriage\\", skip_serializing_if = \\"Option::is_none\\")] + pub marriage: Option, + #[serde(rename=\\"members\\", skip_serializing_if = \\"Option::is_none\\")] + pub members: Option>, + #[serde(rename=\\"tuple_type\\", skip_serializing_if = \\"Option::is_none\\")] + pub tuple_type: Option>, + #[serde(rename=\\"array_type\\")] + pub array_type: Vec, + #[serde(rename=\\"additionalProperties\\", skip_serializing_if = \\"Option::is_none\\")] + pub additional_properties: Option>, +} + +impl Address { + pub fn new(street_name: String, city: String, state: String, house_number: f64, marriage: Option, members: Option, tuple_type: Option, array_type: Vec, additional_properties: Option>) -> Address { + Address { + street_name, + city, + state, + house_number, + marriage, + members: members.map(Box::new), + tuple_type: tuple_type.map(Box::new), + array_type, + additional_properties, + } + } +} +" +`; + +exports[`RUST_COMMON_PRESET Struct & Complete Models should render \`struct\` type with all implementations 2`] = ` +"// Members represents a union of types: String, f64, bool +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +#[serde(untagged)] +pub enum Members { + #[serde(rename=\\"MembersOneOf0\\")] + MembersOneOf0(String), + #[serde(rename=\\"MembersOneOf1\\")] + MembersOneOf1(f64), + #[serde(rename=\\"MembersOneOf2\\")] + MembersOneOf2(bool), +} + +" +`; + +exports[`RUST_COMMON_PRESET Struct & Complete Models should render \`struct\` type with all implementations 3`] = ` +"// TupleType represents a TupleType model. +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +pub struct TupleType(String, f64); + +impl TupleType { + pub fn new(value_0: String, value_1: f64) -> TupleType { + TupleType(value_0, value_1) + } +} + +" +`; + +exports[`RUST_COMMON_PRESET Struct & Complete Models should render \`struct\` without Default or new() implementation 1`] = ` +"// Address represents a Address model. +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct Address { + #[serde(rename=\\"street_name\\")] + pub street_name: String, + #[serde(rename=\\"city\\")] + pub city: String, + #[serde(rename=\\"state\\")] + pub state: String, + #[serde(rename=\\"house_number\\")] + pub house_number: f64, + #[serde(rename=\\"marriage\\", skip_serializing_if = \\"Option::is_none\\")] + pub marriage: Option, + #[serde(rename=\\"members\\", skip_serializing_if = \\"Option::is_none\\")] + pub members: Option>, + #[serde(rename=\\"tuple_type\\", skip_serializing_if = \\"Option::is_none\\")] + pub tuple_type: Option>, + #[serde(rename=\\"array_type\\")] + pub array_type: Vec, + #[serde(rename=\\"additionalProperties\\", skip_serializing_if = \\"Option::is_none\\")] + pub additional_properties: Option>, +} + +impl Address { + +} +" +`; diff --git a/test/generators/template/Constants.spec.ts b/test/generators/template/Constants.spec.ts new file mode 100644 index 0000000000..a64a234f4d --- /dev/null +++ b/test/generators/template/Constants.spec.ts @@ -0,0 +1,12 @@ +import { isReservedTemplateKeyword } from '../../../src/generators/template/Constants'; + +describe('Reserved keywords', () => { + it('shoud return true if the word is a reserved keyword', () => { + expect(isReservedTemplateKeyword('as')).toBe(true); + expect(isReservedTemplateKeyword('async')).toBe(true); + }); + + it('should return false if the word is not a reserved keyword', () => { + expect(isReservedTemplateKeyword('dinosaur')).toBe(false); + }); +}); diff --git a/test/generators/template/TemplateConstrainer.spec.ts b/test/generators/template/TemplateConstrainer.spec.ts new file mode 100644 index 0000000000..4891ef3799 --- /dev/null +++ b/test/generators/template/TemplateConstrainer.spec.ts @@ -0,0 +1,210 @@ +import { TemplateDefaultTypeMapping } from '../../../src/generators/template/TemplateConstrainer'; +import { TemplateGenerator } from '../../../src/generators/template'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedTupleValueModel, + ConstrainedUnionModel +} from '../../../src'; +describe('TemplateConstrainer', () => { + describe('ObjectModel', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedObjectModel('test', undefined, '', {}); + const type = TemplateDefaultTypeMapping.Object({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Reference', () => { + test('should render the constrained name as type', () => { + const refModel = new ConstrainedAnyModel('test', undefined, ''); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + '', + refModel + ); + const type = TemplateDefaultTypeMapping.Reference({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Any', () => { + test('should render type', () => { + const model = new ConstrainedAnyModel('test', undefined, ''); + const type = TemplateDefaultTypeMapping.Any({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(''); + }); + }); + describe('Float', () => { + test('should render type', () => { + const model = new ConstrainedFloatModel('test', undefined, ''); + const type = TemplateDefaultTypeMapping.Float({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(''); + }); + }); + describe('Integer', () => { + test('should render type', () => { + const model = new ConstrainedIntegerModel('test', undefined, ''); + const type = TemplateDefaultTypeMapping.Integer({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(''); + }); + }); + describe('String', () => { + test('should render type', () => { + const model = new ConstrainedStringModel('test', undefined, ''); + const type = TemplateDefaultTypeMapping.String({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(''); + }); + }); + describe('Boolean', () => { + test('should render type', () => { + const model = new ConstrainedBooleanModel('test', undefined, ''); + const type = TemplateDefaultTypeMapping.Boolean({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(''); + }); + }); + + describe('Tuple', () => { + test('should render type', () => { + const stringModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const tupleValueModel = new ConstrainedTupleValueModel(0, stringModel); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel + ]); + const type = TemplateDefaultTypeMapping.Tuple({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(''); + }); + test('should render multiple tuple types', () => { + const stringModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const tupleValueModel0 = new ConstrainedTupleValueModel(0, stringModel); + const tupleValueModel1 = new ConstrainedTupleValueModel(1, stringModel); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel0, + tupleValueModel1 + ]); + const type = TemplateDefaultTypeMapping.Tuple({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(''); + }); + }); + + describe('Array', () => { + test('should render type', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const type = TemplateDefaultTypeMapping.Array({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(''); + }); + }); + + describe('Enum', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedEnumModel('Test', undefined, '', []); + const type = TemplateDefaultTypeMapping.Enum({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + + describe('Union', () => { + test('should render type', () => { + const unionModel = new ConstrainedStringModel('test', undefined, 'str'); + const model = new ConstrainedUnionModel('test', undefined, '', [ + unionModel + ]); + const type = TemplateDefaultTypeMapping.Union({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(''); + }); + test('should render multiple types', () => { + const unionModel1 = new ConstrainedStringModel('test', undefined, 'str'); + const unionModel2 = new ConstrainedStringModel('test', undefined, 'str'); + const model = new ConstrainedUnionModel('test', undefined, '', [ + unionModel1, + unionModel2 + ]); + const type = TemplateDefaultTypeMapping.Union({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(''); + }); + }); + + describe('Dictionary', () => { + test('should render type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'str'); + const valueModel = new ConstrainedStringModel('test', undefined, 'str'); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = TemplateDefaultTypeMapping.Dictionary({ + constrainedModel: model, + options: TemplateGenerator.defaultOptions + }); + expect(type).toEqual(''); + }); + }); +}); diff --git a/test/generators/template/TemplateGenerator.spec.ts b/test/generators/template/TemplateGenerator.spec.ts new file mode 100644 index 0000000000..b9377d6201 --- /dev/null +++ b/test/generators/template/TemplateGenerator.spec.ts @@ -0,0 +1,142 @@ +import { TemplateGenerator } from '../../../src/generators/template'; + +describe('TemplateGenerator', () => { + let generator: TemplateGenerator; + beforeEach(() => { + generator = new TemplateGenerator(); + }); + + describe('Enum', () => { + test('should render `enum` with mixed types (union type)', async () => { + const doc = { + $id: 'Things', + enum: ['Texas', 1, '1', false, { test: 'test' }] + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should work custom preset for `enum` type', async () => { + const doc = { + $id: 'CustomEnum', + type: 'string', + enum: ['Texas', 'Alabama', 'California'] + }; + + generator = new TemplateGenerator({ + presets: [ + { + enum: { + self({ content }) { + return content; + } + } + } + ] + }); + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render enums with translated special characters', async () => { + const doc = { + $id: 'States', + enum: ['test+', '$test', 'test-', 'test?!', '*test'] + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + }); + describe('Class', () => { + test('should not render reserved keyword', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + enum: { type: 'string' }, + reservedEnum: { type: 'string' } + }, + additionalProperties: false + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render `class` type', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + } + }, + patternProperties: { + '^S(.?*)test&': { + type: 'string' + } + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] + }; + const expectedDependencies: string[] = []; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); + }); + + test('should work with custom preset for `class` type', async () => { + const doc = { + $id: 'CustomClass', + type: 'object', + properties: { + property: { type: 'string' } + } + }; + generator = new TemplateGenerator({ + presets: [ + { + class: { + property({ content }) { + const annotation = 'test1'; + return `${annotation}\n${content}`; + }, + getter({ content }) { + const annotation = 'test2'; + return `${annotation}\n${content}`; + }, + setter({ content }) { + const annotation = 'test3'; + return `${annotation}\n${content}`; + } + } + } + ] + }); + const expectedDependencies: string[] = []; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); + }); + }); +}); diff --git a/test/generators/template/TemplateRenderer.spec.ts b/test/generators/template/TemplateRenderer.spec.ts new file mode 100644 index 0000000000..cf68fc476a --- /dev/null +++ b/test/generators/template/TemplateRenderer.spec.ts @@ -0,0 +1,23 @@ +import { TemplateGenerator } from '../../../src/generators/template'; +import { TemplateRenderer } from '../../../src/generators/template/TemplateRenderer'; +import { ConstrainedObjectModel, InputMetaModel } from '../../../src/models'; +import { MockTemplateRenderer } from '../../TestUtils/TestRenderers'; + +describe('TemplateRenderer', () => { + let renderer: TemplateRenderer; + beforeEach(() => { + renderer = new MockTemplateRenderer( + TemplateGenerator.defaultOptions, + new TemplateGenerator(), + [], + new ConstrainedObjectModel('', undefined, '', {}), + new InputMetaModel() + ); + }); + + describe('renderComments()', () => { + test('Should be able to render comments', () => { + expect(renderer.renderComments('someComment')).toEqual('// someComment'); + }); + }); +}); diff --git a/test/generators/template/presets/DescriptionPreset.spec.ts b/test/generators/template/presets/DescriptionPreset.spec.ts new file mode 100644 index 0000000000..6ce164ac3a --- /dev/null +++ b/test/generators/template/presets/DescriptionPreset.spec.ts @@ -0,0 +1,46 @@ +import { + TemplateGenerator, + TEMPLATE_DESCRIPTION_PRESET +} from '../../../../src/generators/template'; + +describe('TEMPLATE_DESCRIPTION_PRESET', () => { + let generator: TemplateGenerator; + beforeEach(() => { + generator = new TemplateGenerator({ + presets: [TEMPLATE_DESCRIPTION_PRESET] + }); + }); + + test('should render description and examples for class', async () => { + const doc = { + $id: 'Clazz', + type: 'object', + description: 'Description for class', + examples: [{ prop: 'value' }], + properties: { + prop: { + type: 'string', + description: 'Description for prop', + examples: ['exampleValue'] + } + } + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render description and examples for enum', async () => { + const doc = { + $id: 'Enum', + type: 'string', + description: 'Description for enum', + examples: ['value'], + enum: ['on', 'off'] + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); +}); diff --git a/test/generators/typescript/TypeScriptConstrainer.spec.ts b/test/generators/typescript/TypeScriptConstrainer.spec.ts new file mode 100644 index 0000000000..ae556e0ab4 --- /dev/null +++ b/test/generators/typescript/TypeScriptConstrainer.spec.ts @@ -0,0 +1,319 @@ +import { TypeScriptDefaultTypeMapping } from '../../../src/generators/typescript/TypeScriptConstrainer'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedTupleValueModel, + ConstrainedUnionModel, + TypeScriptGenerator +} from '../../../src'; +describe('TypeScriptConstrainer', () => { + describe('ObjectModel', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedObjectModel('test', undefined, '', {}); + const type = TypeScriptDefaultTypeMapping.Object({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Reference', () => { + test('should render the constrained name as type', () => { + const refModel = new ConstrainedAnyModel('test', undefined, ''); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + '', + refModel + ); + const type = TypeScriptDefaultTypeMapping.Reference({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + describe('Any', () => { + test('should render type', () => { + const model = new ConstrainedAnyModel('test', undefined, ''); + const type = TypeScriptDefaultTypeMapping.Any({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('any'); + }); + }); + describe('Float', () => { + test('should render type', () => { + const model = new ConstrainedFloatModel('test', undefined, ''); + const type = TypeScriptDefaultTypeMapping.Float({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('number'); + }); + }); + describe('Integer', () => { + test('should render type', () => { + const model = new ConstrainedIntegerModel('test', undefined, ''); + const type = TypeScriptDefaultTypeMapping.Integer({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('number'); + }); + }); + describe('String', () => { + test('should render type', () => { + const model = new ConstrainedStringModel('test', undefined, ''); + const type = TypeScriptDefaultTypeMapping.String({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('string'); + }); + }); + describe('Boolean', () => { + test('should render type', () => { + const model = new ConstrainedBooleanModel('test', undefined, ''); + const type = TypeScriptDefaultTypeMapping.Boolean({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('boolean'); + }); + }); + + describe('Tuple', () => { + test('should render type', () => { + const stringModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const tupleValueModel = new ConstrainedTupleValueModel(0, stringModel); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel + ]); + const type = TypeScriptDefaultTypeMapping.Tuple({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('[String]'); + }); + test('should render multiple tuple types', () => { + const stringModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const tupleValueModel0 = new ConstrainedTupleValueModel(0, stringModel); + const tupleValueModel1 = new ConstrainedTupleValueModel(1, stringModel); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel0, + tupleValueModel1 + ]); + const type = TypeScriptDefaultTypeMapping.Tuple({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('[String, String]'); + }); + }); + + describe('Array', () => { + test('should render type', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const type = TypeScriptDefaultTypeMapping.Array({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('String[]'); + }); + test('should render union types correctly', () => { + const stringModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const anyModel = new ConstrainedAnyModel('test', undefined, 'any'); + const unionModel = new ConstrainedUnionModel( + 'test', + undefined, + 'String | any', + [anyModel, stringModel] + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + unionModel + ); + const type = TypeScriptDefaultTypeMapping.Array({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('(String | any)[]'); + }); + }); + + describe('Enum', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedEnumModel('test', undefined, '', []); + const type = TypeScriptDefaultTypeMapping.Enum({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual(model.name); + }); + }); + + describe('Union', () => { + test('should render type', () => { + const unionModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedUnionModel('test', undefined, '', [ + unionModel + ]); + const type = TypeScriptDefaultTypeMapping.Union({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('String'); + }); + test('should render multiple types', () => { + const unionModel1 = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const unionModel2 = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedUnionModel('test', undefined, '', [ + unionModel1, + unionModel2 + ]); + const type = TypeScriptDefaultTypeMapping.Union({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('String | String'); + }); + }); + + describe('Dictionary', () => { + test('should render type with default map type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'String'); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = TypeScriptDefaultTypeMapping.Dictionary({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('Map'); + }); + test('should render type with indexed object map type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'String'); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = TypeScriptDefaultTypeMapping.Dictionary({ + constrainedModel: model, + options: { + ...TypeScriptGenerator.defaultOptions, + mapType: 'indexedObject' + } + }); + expect(type).toEqual('{ [name: String]: String }'); + }); + test('should render type with record map type', () => { + const keyModel = new ConstrainedStringModel('test', undefined, 'String'); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = TypeScriptDefaultTypeMapping.Dictionary({ + constrainedModel: model, + options: { ...TypeScriptGenerator.defaultOptions, mapType: 'record' } + }); + expect(type).toEqual('Record'); + }); + test('should not be able to render dictionary with union key type', () => { + const keyModel = new ConstrainedUnionModel( + 'test', + undefined, + 'String', + [] + ); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + '', + keyModel, + valueModel + ); + const type = TypeScriptDefaultTypeMapping.Dictionary({ + constrainedModel: model, + options: TypeScriptGenerator.defaultOptions + }); + expect(type).toEqual('Map'); + }); + }); +}); diff --git a/test/generators/typescript/TypeScriptDependencyManager.spec.ts b/test/generators/typescript/TypeScriptDependencyManager.spec.ts new file mode 100644 index 0000000000..285eff5903 --- /dev/null +++ b/test/generators/typescript/TypeScriptDependencyManager.spec.ts @@ -0,0 +1,15 @@ +import { TypeScriptGenerator } from '../../../src/generators'; +import { TypeScriptDependencyManager } from '../../../src/generators/typescript/TypeScriptDependencyManager'; +describe('TypeScriptDependencyManager', () => { + describe('renderDependency()', () => { + test('Should be able to render dependency', () => { + const dependencyManager = new TypeScriptDependencyManager( + TypeScriptGenerator.defaultOptions, + [] + ); + expect( + dependencyManager.renderDependency('someComment', 'someComment2') + ).toEqual(`import someComment from 'someComment2';`); + }); + }); +}); diff --git a/test/generators/typescript/TypeScriptFileGenerator.spec.ts b/test/generators/typescript/TypeScriptFileGenerator.spec.ts deleted file mode 100644 index cad65e642b..0000000000 --- a/test/generators/typescript/TypeScriptFileGenerator.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { CommonInputModel, CommonModel, FileHelpers, TypeScriptFileGenerator, OutputModel } from '../../../src'; -import * as path from 'path'; - -describe('TypeScriptFileGenerator', () => { - afterEach(() => { - jest.restoreAllMocks(); - }); - - describe('generateToFile()', () => { - const doc = { - $id: 'CustomClass', - type: 'object', - additionalProperties: true, - properties: { - someProp: { type: 'string' }, - someEnum: { - $id: 'CustomEnum', - type: 'string', - enum: ['Texas', 'Alabama', 'California'], - } - } - }; - test('should throw accurate error if file cannot be written', async () => { - const generator = new TypeScriptFileGenerator(); - const expectedError = new Error('write error'); - jest.spyOn(FileHelpers, 'writerToFileSystem').mockRejectedValue(expectedError); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), 'Test', new CommonInputModel(), [])]); - - await expect(generator.generateToFiles(doc, '/test/')).rejects.toEqual(expectedError); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); - }); - test('should try and generate models to files', async () => { - const generator = new TypeScriptFileGenerator(); - const outputDir = './test'; - const expectedOutputDirPath = path.resolve(outputDir); - const expectedOutputFilePath = path.resolve(`${outputDir}/Test.ts`); - const expectedWriteToFileParameters = [ - 'content', - expectedOutputFilePath, - ]; - jest.spyOn(FileHelpers, 'writerToFileSystem').mockResolvedValue(undefined); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), 'Test', new CommonInputModel(), [])]); - - await generator.generateToFiles(doc, expectedOutputDirPath); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(1); - expect((FileHelpers.writerToFileSystem as jest.Mock).mock.calls[0]).toEqual(expectedWriteToFileParameters); - }); - - test('should ignore models that have not been rendered', async () => { - const generator = new TypeScriptFileGenerator(); - const outputDir = './test'; - const expectedOutputDirPath = path.resolve(outputDir); - jest.spyOn(FileHelpers, 'writerToFileSystem').mockResolvedValue(undefined); - jest.spyOn(generator, 'generateCompleteModels').mockResolvedValue([new OutputModel('content', new CommonModel(), '', new CommonInputModel(), [])]); - - const models = await generator.generateToFiles(doc, expectedOutputDirPath); - expect(generator.generateCompleteModels).toHaveBeenCalledTimes(1); - expect(FileHelpers.writerToFileSystem).toHaveBeenCalledTimes(0); - expect(models).toHaveLength(0); - }); - }); -}); diff --git a/test/generators/typescript/TypeScriptGenerator.spec.ts b/test/generators/typescript/TypeScriptGenerator.spec.ts index 393c78d301..034a917c5f 100644 --- a/test/generators/typescript/TypeScriptGenerator.spec.ts +++ b/test/generators/typescript/TypeScriptGenerator.spec.ts @@ -16,33 +16,10 @@ describe('TypeScriptGenerator', () => { }, additionalProperties: false }; - const expected = `class Address { - private _reservedReservedEnum?: string; - private _reservedEnum?: string; - - constructor(input: { - reservedReservedEnum?: string, - reservedEnum?: string, - }) { - this._reservedReservedEnum = input.reservedReservedEnum; - this._reservedEnum = input.reservedEnum; - } - - get reservedReservedEnum(): string | undefined { return this._reservedReservedEnum; } - set reservedReservedEnum(reservedReservedEnum: string | undefined) { this._reservedReservedEnum = reservedReservedEnum; } - - get reservedEnum(): string | undefined { return this._reservedEnum; } - set reservedEnum(reservedEnum: string | undefined) { this._reservedEnum = reservedEnum; } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['Address']; - - let classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toEqual(expected); - - classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render `class` type', async () => { @@ -54,98 +31,36 @@ describe('TypeScriptGenerator', () => { city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - tuple_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }], additionalItems: false }, - tuple_type_with_additional_items: { type: 'array', items: [{ type: 'string' }, { type: 'number' }], additionalItems: true }, - array_type: { type: 'array', items: { type: 'string' } }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + tuple_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }], + additionalItems: false + }, + tuple_type_with_additional_items: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }], + additionalItems: true + }, + array_type: { type: 'array', items: { type: 'string' } } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const expected = `class Address { - private _streetName: string; - private _city: string; - private _state: string; - private _houseNumber: number; - private _marriage?: boolean; - private _members?: string | number | boolean; - private _tupleType?: [string, number]; - private _tupleTypeWithAdditionalItems?: [string, number, ...(object | string | number | Array | boolean | null)[]]; - private _arrayType: Array; - private _additionalProperties?: Map | boolean | null>; - private _sTestPatternProperties?: Map; - - constructor(input: { - streetName: string, - city: string, - state: string, - houseNumber: number, - marriage?: boolean, - members?: string | number | boolean, - tupleType?: [string, number], - tupleTypeWithAdditionalItems?: [string, number, ...(object | string | number | Array | boolean | null)[]], - arrayType: Array, - }) { - this._streetName = input.streetName; - this._city = input.city; - this._state = input.state; - this._houseNumber = input.houseNumber; - this._marriage = input.marriage; - this._members = input.members; - this._tupleType = input.tupleType; - this._tupleTypeWithAdditionalItems = input.tupleTypeWithAdditionalItems; - this._arrayType = input.arrayType; - } - - get streetName(): string { return this._streetName; } - set streetName(streetName: string) { this._streetName = streetName; } - - get city(): string { return this._city; } - set city(city: string) { this._city = city; } - - get state(): string { return this._state; } - set state(state: string) { this._state = state; } - - get houseNumber(): number { return this._houseNumber; } - set houseNumber(houseNumber: number) { this._houseNumber = houseNumber; } - - get marriage(): boolean | undefined { return this._marriage; } - set marriage(marriage: boolean | undefined) { this._marriage = marriage; } - - get members(): string | number | boolean | undefined { return this._members; } - set members(members: string | number | boolean | undefined) { this._members = members; } - - get tupleType(): [string, number] | undefined { return this._tupleType; } - set tupleType(tupleType: [string, number] | undefined) { this._tupleType = tupleType; } - - get tupleTypeWithAdditionalItems(): [string, number, ...(object | string | number | Array | boolean | null)[]] | undefined { return this._tupleTypeWithAdditionalItems; } - set tupleTypeWithAdditionalItems(tupleTypeWithAdditionalItems: [string, number, ...(object | string | number | Array | boolean | null)[]] | undefined) { this._tupleTypeWithAdditionalItems = tupleTypeWithAdditionalItems; } - - get arrayType(): Array { return this._arrayType; } - set arrayType(arrayType: Array) { this._arrayType = arrayType; } - - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } - - get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['_address']; - - let classModel = await generator.renderClass(model, inputModel); - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual([]); - - classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should work custom preset for `class` type', async () => { @@ -153,47 +68,27 @@ describe('TypeScriptGenerator', () => { $id: 'CustomClass', type: 'object', properties: { - property: { type: 'string' }, + property: { type: 'string' } } }; - const expected = `class CustomClass { - @JsonProperty("property") - private _property?: string; - @JsonProperty("additionalProperties") - private _additionalProperties?: Map | boolean | null>; - - constructor(input: { - property?: string, - }) { - this._property = input.property; - } - - get property(): string | undefined { return this._property; } - set property(property: string | undefined) { this._property = property; } - - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } -}`; generator = new TypeScriptGenerator({ presets: [ { class: { - property({ propertyName, content }) { - return `@JsonProperty("${propertyName}") + property({ property, content }) { + return `@JsonProperty("${property.propertyName}") ${content}`; - }, + } } } ] }); - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomClass']; - - const classModel = await generator.render(model, inputModel); - expect(classModel.result).toEqual(expected); - expect(classModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render `interface` type', async () => { @@ -205,40 +100,38 @@ ${content}`; city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - tuple_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }], additionalItems: false }, - tuple_type_with_additional_items: { type: 'array', items: [{ type: 'string' }, { type: 'number' }], additionalItems: true }, - array_type: { type: 'array', items: { type: 'string' } }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + tuple_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }], + additionalItems: false + }, + tuple_type_with_additional_items: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }], + additionalItems: true + }, + array_type: { type: 'array', items: { type: 'string' } } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; - const expected = `interface Address { - streetName: string; - city: string; - state: string; - houseNumber: number; - marriage?: boolean; - members?: string | number | boolean; - tupleType?: [string, number]; - tupleTypeWithAdditionalItems?: [string, number, ...(object | string | number | Array | boolean | null)[]]; - arrayType: Array; - additionalProperties?: Map | boolean | null>; - sTestPatternProperties?: Map; -}`; - - const interfaceGenerator = new TypeScriptGenerator({ modelType: 'interface' }); - const inputModel = await interfaceGenerator.process(doc); - const model = inputModel.models['Address']; - - const interfaceModel = await interfaceGenerator.render(model, inputModel); - expect(interfaceModel.result).toEqual(expected); - expect(interfaceModel.dependencies).toEqual([]); + + generator = new TypeScriptGenerator({ modelType: 'interface' }); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should work custom preset for `interface` type', async () => { @@ -246,13 +139,9 @@ ${content}`; $id: 'CustomInterface', type: 'object', properties: { - property: { type: 'string' }, + property: { type: 'string' } } }; - const expected = `interface CustomInterface { - property?: string; - additionalProperties?: Map | boolean | null>; -}`; generator = new TypeScriptGenerator({ presets: [ @@ -260,63 +149,42 @@ ${content}`; interface: { self({ content }) { return content; - }, + } } } ] }); - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomInterface']; - - const interfaceModel = await generator.renderInterface(model, inputModel); - expect(interfaceModel.result).toEqual(expected); - expect(interfaceModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render `enum` type', async () => { const doc = { $id: 'States', type: 'string', - enum: ['Texas', 'Alabama', 'California'], + enum: ['Texas', 'Alabama', 'California'] }; - const expected = `enum States { - TEXAS = "Texas", - ALABAMA = "Alabama", - CALIFORNIA = "California", -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['States']; - - let enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); - - enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render `enum` type as `union` if option enumType = `union`', async () => { const doc = { $id: 'States', type: 'string', - enum: ['Texas', 'Alabama', 'California'], + enum: ['Texas', 'Alabama', 'California'] }; - const expected = 'type States = "Texas" | "Alabama" | "California";'; - - const unionGenerator = new TypeScriptGenerator({ enumType: 'union' }); - const inputModel = await unionGenerator.process(doc); - const model = inputModel.models['States']; - let enumModel = await unionGenerator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); - - enumModel = await unionGenerator.renderType(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); + generator = new TypeScriptGenerator({ enumType: 'union' }); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render union `enum` values', async () => { @@ -324,24 +192,10 @@ ${content}`; $id: 'States', enum: [2, '2', 'test', true, { test: 'test' }] }; - const expected = `enum States { - NUMBER_2 = 2, - STRING_2 = "2", - TEST = "test", - TRUE = "true", - TEST_TEST = '{"test":"test"}', -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['States']; - - let enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); - - enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render enums with translated special characters', async () => { @@ -349,37 +203,19 @@ ${content}`; $id: 'States', enum: ['test+', 'test', 'test-', 'test?!', '*test'] }; - const expected = `enum States { - TEST_PLUS = "test+", - TEST = "test", - TEST_MINUS = "test-", - TEST_QUESTION_EXCLAMATION = "test?!", - ASTERISK_TEST = "*test", -}`; - - const inputModel = await generator.process(doc); - const model = inputModel.models['States']; - - let enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); - - enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should work custom preset for `enum` type', async () => { const doc = { $id: 'CustomEnum', type: 'string', - enum: ['Texas', 'Alabama', 'California'], + enum: ['Texas', 'Alabama', 'California'] }; - const expected = `enum CustomEnum { - TEXAS = "Texas", - ALABAMA = "Alabama", - CALIFORNIA = "California", -}`; generator = new TypeScriptGenerator({ presets: [ @@ -387,71 +223,52 @@ ${content}`; enum: { self({ content }) { return content; - }, + } } } ] }); - const inputModel = await generator.process(doc); - const model = inputModel.models['CustomEnum']; - - let enumModel = await generator.render(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); - - enumModel = await generator.renderEnum(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render `type` type - primitive', async () => { const doc = { $id: 'TypePrimitive', - type: 'string', + type: 'string' }; - const expected = 'type TypePrimitive = string;'; - - const inputModel = await generator.process(doc); - const model = inputModel.models['TypePrimitive']; - let primitiveModel = await generator.renderType(model, inputModel); - expect(primitiveModel.result).toEqual(expected); - expect(primitiveModel.dependencies).toEqual([]); - - primitiveModel = await generator.render(model, inputModel); - expect(primitiveModel.result).toEqual(expected); - expect(primitiveModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render `type` type - enum', async () => { const doc = { $id: 'TypeEnum', - enum: ['Texas', 'Alabama', 'California', 0, 1, false, true], + enum: ['Texas', 'Alabama', 'California', 0, 1, false, true] }; - const expected = 'type TypeEnum = "Texas" | "Alabama" | "California" | 0 | 1 | false | true;'; - - const inputModel = await generator.process(doc); - const model = inputModel.models['TypeEnum']; - const enumModel = await generator.renderType(model, inputModel); - expect(enumModel.result).toEqual(expected); - expect(enumModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render `type` type - union', async () => { const doc = { $id: 'TypeUnion', - type: ['string', 'number', 'boolean'], + type: ['string', 'number', 'boolean'] }; - const expected = 'type TypeUnion = string | number | boolean;'; - - const inputModel = await generator.process(doc); - const model = inputModel.models['TypeUnion']; - const unionModel = await generator.renderType(model, inputModel); - expect(unionModel.result).toEqual(expected); - expect(unionModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render `type` type - array of primitive type', async () => { @@ -460,17 +277,14 @@ ${content}`; type: 'array', items: { $id: 'StringArray', - type: 'string', + type: 'string' } }; - const expected = 'type TypeArray = Array;'; - - const inputModel = await generator.process(doc); - const model = inputModel.models['TypeArray']; - const arrayModel = await generator.renderType(model, inputModel); - expect(arrayModel.result).toEqual(expected); - expect(arrayModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); test('should render `type` type - array of union type', async () => { @@ -479,16 +293,14 @@ ${content}`; type: 'array', items: { $id: 'StringArray', - type: ['string', 'number', 'boolean'], + type: ['string', 'number', 'boolean'] } }; - const inputModel = await generator.process(doc); - const model = inputModel.models['TypeArray']; - - const arrayModel = await generator.renderType(model, inputModel); - expect(arrayModel.result).toMatchSnapshot(); - expect(arrayModel.dependencies).toEqual([]); + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual([]); }); const doc = { @@ -499,44 +311,379 @@ ${content}`; city: { type: 'string', description: 'City description' }, state: { type: 'string' }, house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, - other_model: { type: 'object', $id: 'OtherModel', properties: { street_name: { type: 'string' } } }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + other_model: { + type: 'object', + $id: 'OtherModel', + properties: { street_name: { type: 'string' } } + } }, patternProperties: { '^S(.?*)test&': { type: 'string' } }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] }; test('should render models and their dependencies for CJS module system', async () => { - const models = await generator.generateCompleteModels(doc, {moduleSystem: 'CJS'}); + generator = new TypeScriptGenerator({ + moduleSystem: 'CJS' + }); + const models = await generator.generateCompleteModels(doc, {}); + expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); expect(models[1].result).toMatchSnapshot(); }); test('should render models and their dependencies for CJS module system with named exports', async () => { - const models = await generator.generateCompleteModels(doc, {moduleSystem: 'CJS', exportType: 'named'}); + generator = new TypeScriptGenerator({ + moduleSystem: 'CJS' + }); + const models = await generator.generateCompleteModels(doc, { + exportType: 'named' + }); expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); expect(models[1].result).toMatchSnapshot(); }); test('should render models and their dependencies for ESM module system', async () => { - const models = await generator.generateCompleteModels(doc, {moduleSystem: 'ESM'}); + generator = new TypeScriptGenerator({ + moduleSystem: 'ESM' + }); + const models = await generator.generateCompleteModels(doc, {}); expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); expect(models[1].result).toMatchSnapshot(); }); test('should render models and their dependencies for ESM module system with named exports', async () => { - const models = await generator.generateCompleteModels(doc, {moduleSystem: 'ESM', exportType: 'named'}); + generator = new TypeScriptGenerator({ + moduleSystem: 'ESM' + }); + const models = await generator.generateCompleteModels(doc, { + exportType: 'named' + }); expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); expect(models[1].result).toMatchSnapshot(); }); + + describe('AsyncAPI with polymorphism', () => { + const asyncapiDoc = { + asyncapi: '2.4.0', + info: { + title: 'Pet', + version: '1.0.0' + }, + channels: {}, + components: { + messages: { + PetMessage: { + payload: { + oneOf: [ + { $ref: '#/components/schemas/Cat' }, + { $ref: '#/components/schemas/Dog' }, + { $ref: '#/components/schemas/StickInsect' } + ] + } + } + }, + schemas: { + Pet: { + type: 'object', + additionalProperties: false, + discriminator: 'petType', + properties: { + petType: { + $id: 'PetType', + type: 'string' + }, + name: { + type: 'string' + } + }, + required: ['petType', 'name'] + }, + Cat: { + allOf: [ + { $ref: '#/components/schemas/Pet' }, + { + type: 'object', + additionalProperties: false, + properties: { + petType: { + const: 'Cat' + }, + huntingSkill: { + type: 'string', + enum: ['clueless', 'lazy', 'adventurous', 'aggressive'] + } + }, + required: ['huntingSkill'] + } + ] + }, + Dog: { + allOf: [ + { $ref: '#/components/schemas/Pet' }, + { + type: 'object', + additionalProperties: false, + properties: { + petType: { + const: 'Dog' + }, + packSize: { + type: 'integer', + format: 'int32', + description: 'the size of the pack the dog is from', + minimum: 0 + } + }, + required: ['packSize'] + } + ] + }, + StickInsect: { + allOf: [ + { $ref: '#/components/schemas/Pet' }, + { + type: 'object', + additionalProperties: false, + properties: { + petType: { + const: 'StickBug' + }, + color: { + type: 'string' + } + }, + required: ['color'] + } + ] + } + } + } + }; + + test('should render 6 models (1 oneOf, 3 classes and 2 enums)', async () => { + const models = await generator.generate(asyncapiDoc); + expect(models).toHaveLength(6); + expect(models.map((model) => model.result)).toMatchSnapshot(); + + const cat = models.find((model) => model.modelName === 'Cat'); + expect(cat).not.toBeUndefined(); + expect(cat?.result).toContain('petType'); + expect(cat?.result).toContain('reservedName'); + expect(cat?.result).toContain('huntingSkill'); + expect(cat?.result).not.toContain('packSize'); + expect(cat?.result).not.toContain('color'); + }); + + test('should render enum with discriminator', async () => { + const models = await generator.generate(asyncapiDoc); + const enums = models.filter((model) => model.result.includes('enum')); + + expect(enums).toHaveLength(2); + const discriminatorEnum = enums[0]; + expect(discriminatorEnum?.modelName).not.toContain('AnonymousSchema'); + // Should contain Cat, Dog, and StickBug + expect(discriminatorEnum?.result).toMatchSnapshot(); + }); + }); + + describe('Combine oneOf and allOf', () => { + const asyncapiDoc = { + asyncapi: '2.4.0', + info: { + title: 'Pet', + version: '1.0.0' + }, + channels: {}, + components: { + messages: { + Pet: { + payload: { + title: 'Pet', + allOf: [{ $ref: '#/components/schemas/Animal' }], + oneOf: [ + { $ref: '#/components/schemas/Cat' }, + { $ref: '#/components/schemas/Dog' } + ] + } + } + }, + schemas: { + Animal: { + title: 'Animal', + type: 'object', + additionalProperties: false, + discriminator: 'animalType', + properties: { + animalType: { + title: 'Animal Type', + type: 'string' + }, + age: { + type: 'integer', + min: 0 + } + } + }, + Cat: { + title: 'Cat', + type: 'object', + additionalProperties: false, + properties: { + animalType: { + const: 'Cat' + }, + huntingSkill: { + title: 'Hunting Skill', + type: 'string', + enum: ['clueless', 'lazy'] + } + } + }, + Dog: { + title: 'Dog', + type: 'object', + additionalProperties: false, + properties: { + animalType: { + const: 'Dog' + }, + breed: { + title: 'Dog Breed', + type: 'string', + enum: ['bulldog', 'bichons frise'] + } + } + } + } + } + }; + + test('should combine oneOf and allOf', async () => { + const models = await generator.generate(asyncapiDoc); + expect(models).toHaveLength(6); + expect(models.map((model) => model.result)).toMatchSnapshot(); + + const cat = models.find((model) => model.modelName === 'Cat'); + expect(cat).not.toBeUndefined(); + expect(cat?.result).toContain('animalType'); + expect(cat?.result).toContain('age'); + expect(cat?.result).toContain('huntingSkill'); + expect(cat?.result).not.toContain('breed'); + + const dog = models.find((model) => model.modelName === 'Dog'); + expect(dog).not.toBeUndefined(); + expect(dog?.result).toContain('animalType'); + expect(dog?.result).toContain('age'); + expect(dog?.result).toContain('breed'); + expect(dog?.result).not.toContain('huntingSkill'); + }); + }); + + describe('Combine properties and oneOf', () => { + const asyncapiDoc = { + asyncapi: '2.4.0', + info: { + title: 'Pet', + version: '1.0.0' + }, + channels: {}, + components: { + messages: { + Pet: { + payload: { + title: 'Pet', + type: 'object', + additionalProperties: false, + discriminator: 'petType', + properties: { + petType: { + title: 'Pet Type', + type: 'string' + }, + age: { + type: 'integer', + min: 0 + } + }, + oneOf: [ + { $ref: '#/components/schemas/Cat' }, + { $ref: '#/components/schemas/Dog' } + ] + } + } + }, + schemas: { + Cat: { + title: 'Cat', + type: 'object', + additionalProperties: false, + properties: { + petType: { + const: 'Cat' + }, + huntingSkill: { + title: 'Hunting Skill', + type: 'string', + enum: ['clueless', 'lazy'] + } + } + }, + Dog: { + title: 'Dog', + type: 'object', + additionalProperties: false, + properties: { + petType: { + const: 'Dog' + }, + breed: { + title: 'Dog Breed', + type: 'string', + enum: ['bulldog', 'bichons frise'] + } + } + } + } + } + }; + + test('should combine properties and oneOf', async () => { + const models = await generator.generate(asyncapiDoc); + expect(models).toHaveLength(6); + expect(models.map((model) => model.result)).toMatchSnapshot(); + + const cat = models.find((model) => model.modelName === 'Cat'); + expect(cat).not.toBeUndefined(); + expect(cat?.result).toContain('petType'); + expect(cat?.result).toContain('age'); + expect(cat?.result).toContain('huntingSkill'); + expect(cat?.result).not.toContain('breed'); + + const dog = models.find((model) => model.modelName === 'Dog'); + expect(dog).not.toBeUndefined(); + expect(dog?.result).toContain('petType'); + expect(dog?.result).toContain('age'); + expect(dog?.result).toContain('breed'); + expect(dog?.result).not.toContain('huntingSkill'); + }); + }); }); diff --git a/test/generators/typescript/TypeScriptRenderer.spec.ts b/test/generators/typescript/TypeScriptRenderer.spec.ts index 3a07db5eb9..35b6f94fb1 100644 --- a/test/generators/typescript/TypeScriptRenderer.spec.ts +++ b/test/generators/typescript/TypeScriptRenderer.spec.ts @@ -1,35 +1,17 @@ -import { defaultGeneratorOptions, TypeScriptGenerator } from '../../../src/generators'; +import { ConstrainedObjectModel, InputMetaModel } from '../../../src'; +import { TypeScriptGenerator } from '../../../src/generators'; import { TypeScriptRenderer } from '../../../src/generators/typescript/TypeScriptRenderer'; -import { CommonInputModel, CommonModel } from '../../../src/models'; -class MockTypeScriptRenderer extends TypeScriptRenderer { - -} +import { MockTypeScriptRenderer } from '../../TestUtils/TestRenderers'; describe('TypeScriptRenderer', () => { - let renderer: TypeScriptRenderer; + let renderer: TypeScriptRenderer; beforeEach(() => { - renderer = new MockTypeScriptRenderer(TypeScriptGenerator.defaultOptions, new TypeScriptGenerator(), [], new CommonModel(), new CommonInputModel()); - }); - - describe('nameType()', () => { - test('should name the type', () => { - const name = renderer.nameType('type__someType'); - expect(name).toEqual('TypeSomeType'); - }); - test('should render reserved type keyword correctly', () => { - const name = renderer.nameType('enum'); - expect(name).toEqual('Enum'); - }); - }); - - describe('nameProperty()', () => { - test('should name the property', () => { - const name = renderer.nameProperty('property__someProperty'); - expect(name).toEqual('propertySomeProperty'); - }); - test('should render reserved property keyword correctly', () => { - const name = renderer.nameProperty('enum'); - expect(name).toEqual('reservedEnum'); - }); + renderer = new MockTypeScriptRenderer( + TypeScriptGenerator.defaultOptions, + new TypeScriptGenerator(), + [], + new ConstrainedObjectModel('', undefined, '', {}), + new InputMetaModel() + ); }); describe('renderComments()', () => { @@ -39,67 +21,4 @@ describe('TypeScriptRenderer', () => { */`); }); }); - - describe('toTsType()', () => { - test('should not render duplicate types', () => { - expect(renderer.toTsType(undefined, new CommonModel())).toEqual('any'); - }); - test('Should render unknown type', () => { - expect(renderer.toTsType(undefined, new CommonModel())).toEqual('any'); - }); - test('Should render number type', () => { - expect(renderer.toTsType('number', new CommonModel())).toEqual('number'); - }); - test('Should render object type', () => { - expect(renderer.toTsType('object', new CommonModel())).toEqual('object'); - }); - test('Should render null type', () => { - expect(renderer.toTsType('null', new CommonModel())).toEqual('null'); - }); - test('Should render array type', () => { - const model = new CommonModel(); - model.items = CommonModel.toCommonModel({type: 'number'}); - expect(renderer.toTsType('array', model)).toEqual('Array'); - }); - test('Should render tuple type', () => { - const model = new CommonModel(); - model.items = [CommonModel.toCommonModel({type: 'number'})]; - expect(renderer.toTsType('array', model)).toEqual('[number]'); - }); - test('Should render tuple type with additionalItems', () => { - const model = new CommonModel(); - model.items = [CommonModel.toCommonModel({type: 'number'})]; - model.additionalItems = CommonModel.toCommonModel({type: 'string'}); - expect(renderer.toTsType('array', model)).toEqual('[number, ...(string)[]]'); - }); - test('Should render multiple tuples', () => { - const model = new CommonModel(); - model.items = [CommonModel.toCommonModel({type: 'number'}), CommonModel.toCommonModel({type: 'string'})]; - expect(renderer.toTsType('array', model)).toEqual('[number, string]'); - }); - }); - describe('renderType()', () => { - test('Should render refs with pascal case', () => { - const model = new CommonModel(); - model.$ref = ''; - expect(renderer.renderType(model)).toEqual('AnonymousSchema_1'); - }); - test('Should render array of CommonModels', () => { - const model1 = new CommonModel(); - model1.$ref = 'ref1'; - const model2 = new CommonModel(); - model2.$ref = 'ref2'; - expect(renderer.renderType([model1, model2])).toEqual('Ref1 | Ref2'); - }); - test('Should render enums', () => { - const model = new CommonModel(); - model.enum = ['enum1', 'enum2', 9]; - expect(renderer.renderType(model)).toEqual('"enum1" | "enum2" | 9'); - }); - test('should not render duplicate types', () => { - const model = new CommonModel(); - model.type = ['integer', 'number']; - expect(renderer.renderType(model)).toEqual('number'); - }); - }); }); diff --git a/test/generators/typescript/__snapshots__/TypeScriptGenerator.spec.ts.snap b/test/generators/typescript/__snapshots__/TypeScriptGenerator.spec.ts.snap index 79eb6ee6bd..6a78713c18 100644 --- a/test/generators/typescript/__snapshots__/TypeScriptGenerator.spec.ts.snap +++ b/test/generators/typescript/__snapshots__/TypeScriptGenerator.spec.ts.snap @@ -1,10 +1,381 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`TypeScriptGenerator should render \`type\` type - array of union type 1`] = `"type TypeArray = Array;"`; +exports[`TypeScriptGenerator AsyncAPI with polymorphism should render 6 models (1 oneOf, 3 classes and 2 enums) 1`] = ` +Array [ + "type AnonymousSchema_1 = Cat | Dog | StickInsect;", + "class Cat { + private _petType: PetType; + private _reservedName: string; + private _huntingSkill: AnonymousSchema_5; + + constructor(input: { + petType: PetType, + reservedName: string, + huntingSkill: AnonymousSchema_5, + }) { + this._petType = input.petType; + this._reservedName = input.reservedName; + this._huntingSkill = input.huntingSkill; + } + + get petType(): PetType { return this._petType; } + set petType(petType: PetType) { this._petType = petType; } + + get reservedName(): string { return this._reservedName; } + set reservedName(reservedName: string) { this._reservedName = reservedName; } + + get huntingSkill(): AnonymousSchema_5 { return this._huntingSkill; } + set huntingSkill(huntingSkill: AnonymousSchema_5) { this._huntingSkill = huntingSkill; } +}", + "enum PetType { + CAT = \\"Cat\\", + DOG = \\"Dog\\", + STICK_BUG = \\"StickBug\\", +}", + "enum AnonymousSchema_5 { + CLUELESS = \\"clueless\\", + LAZY = \\"lazy\\", + ADVENTUROUS = \\"adventurous\\", + AGGRESSIVE = \\"aggressive\\", +}", + "class Dog { + private _petType: PetType; + private _reservedName: string; + private _packSize: number; + + constructor(input: { + petType: PetType, + reservedName: string, + packSize: number, + }) { + this._petType = input.petType; + this._reservedName = input.reservedName; + this._packSize = input.packSize; + } + + get petType(): PetType { return this._petType; } + set petType(petType: PetType) { this._petType = petType; } + + get reservedName(): string { return this._reservedName; } + set reservedName(reservedName: string) { this._reservedName = reservedName; } + + get packSize(): number { return this._packSize; } + set packSize(packSize: number) { this._packSize = packSize; } +}", + "class StickInsect { + private _petType: PetType; + private _reservedName: string; + private _color: string; + + constructor(input: { + petType: PetType, + reservedName: string, + color: string, + }) { + this._petType = input.petType; + this._reservedName = input.reservedName; + this._color = input.color; + } + + get petType(): PetType { return this._petType; } + set petType(petType: PetType) { this._petType = petType; } + + get reservedName(): string { return this._reservedName; } + set reservedName(reservedName: string) { this._reservedName = reservedName; } + + get color(): string { return this._color; } + set color(color: string) { this._color = color; } +}", +] +`; + +exports[`TypeScriptGenerator AsyncAPI with polymorphism should render enum with discriminator 1`] = ` +"enum PetType { + CAT = \\"Cat\\", + DOG = \\"Dog\\", + STICK_BUG = \\"StickBug\\", +}" +`; + +exports[`TypeScriptGenerator Combine oneOf and allOf should combine oneOf and allOf 1`] = ` +Array [ + "type Pet = Cat | Dog;", + "class Cat { + private _animalType?: AnimalType; + private _age?: number; + private _huntingSkill?: HuntingSkill; + + constructor(input: { + animalType?: AnimalType, + age?: number, + huntingSkill?: HuntingSkill, + }) { + this._animalType = input.animalType; + this._age = input.age; + this._huntingSkill = input.huntingSkill; + } + + get animalType(): AnimalType | undefined { return this._animalType; } + set animalType(animalType: AnimalType | undefined) { this._animalType = animalType; } + + get age(): number | undefined { return this._age; } + set age(age: number | undefined) { this._age = age; } + + get huntingSkill(): HuntingSkill | undefined { return this._huntingSkill; } + set huntingSkill(huntingSkill: HuntingSkill | undefined) { this._huntingSkill = huntingSkill; } +}", + "enum AnimalType { + CAT = \\"Cat\\", + DOG = \\"Dog\\", +}", + "enum HuntingSkill { + CLUELESS = \\"clueless\\", + LAZY = \\"lazy\\", +}", + "class Dog { + private _animalType?: AnimalType; + private _age?: number; + private _breed?: DogBreed; + + constructor(input: { + animalType?: AnimalType, + age?: number, + breed?: DogBreed, + }) { + this._animalType = input.animalType; + this._age = input.age; + this._breed = input.breed; + } + + get animalType(): AnimalType | undefined { return this._animalType; } + set animalType(animalType: AnimalType | undefined) { this._animalType = animalType; } + + get age(): number | undefined { return this._age; } + set age(age: number | undefined) { this._age = age; } + + get breed(): DogBreed | undefined { return this._breed; } + set breed(breed: DogBreed | undefined) { this._breed = breed; } +}", + "enum DogBreed { + BULLDOG = \\"bulldog\\", + BICHONS_SPACE_FRISE = \\"bichons frise\\", +}", +] +`; + +exports[`TypeScriptGenerator Combine properties and oneOf should combine properties and oneOf 1`] = ` +Array [ + "type Pet = Cat | Dog;", + "class Cat { + private _petType?: PetType; + private _age?: number; + private _huntingSkill?: HuntingSkill; + + constructor(input: { + petType?: PetType, + age?: number, + huntingSkill?: HuntingSkill, + }) { + this._petType = input.petType; + this._age = input.age; + this._huntingSkill = input.huntingSkill; + } + + get petType(): PetType | undefined { return this._petType; } + set petType(petType: PetType | undefined) { this._petType = petType; } + + get age(): number | undefined { return this._age; } + set age(age: number | undefined) { this._age = age; } + + get huntingSkill(): HuntingSkill | undefined { return this._huntingSkill; } + set huntingSkill(huntingSkill: HuntingSkill | undefined) { this._huntingSkill = huntingSkill; } +}", + "enum PetType { + CAT = \\"Cat\\", + DOG = \\"Dog\\", +}", + "enum HuntingSkill { + CLUELESS = \\"clueless\\", + LAZY = \\"lazy\\", +}", + "class Dog { + private _petType?: PetType; + private _age?: number; + private _breed?: DogBreed; + + constructor(input: { + petType?: PetType, + age?: number, + breed?: DogBreed, + }) { + this._petType = input.petType; + this._age = input.age; + this._breed = input.breed; + } + + get petType(): PetType | undefined { return this._petType; } + set petType(petType: PetType | undefined) { this._petType = petType; } + + get age(): number | undefined { return this._age; } + set age(age: number | undefined) { this._age = age; } + + get breed(): DogBreed | undefined { return this._breed; } + set breed(breed: DogBreed | undefined) { this._breed = breed; } +}", + "enum DogBreed { + BULLDOG = \\"bulldog\\", + BICHONS_SPACE_FRISE = \\"bichons frise\\", +}", +] +`; + +exports[`TypeScriptGenerator should not render \`class\` with reserved keyword 1`] = ` +"class Address { + private _reservedReservedEnum?: string; + private _reservedEnum?: string; + + constructor(input: { + reservedReservedEnum?: string, + reservedEnum?: string, + }) { + this._reservedReservedEnum = input.reservedReservedEnum; + this._reservedEnum = input.reservedEnum; + } + + get reservedReservedEnum(): string | undefined { return this._reservedReservedEnum; } + set reservedReservedEnum(reservedReservedEnum: string | undefined) { this._reservedReservedEnum = reservedReservedEnum; } + + get reservedEnum(): string | undefined { return this._reservedEnum; } + set reservedEnum(reservedEnum: string | undefined) { this._reservedEnum = reservedEnum; } +}" +`; + +exports[`TypeScriptGenerator should render \`class\` type 1`] = ` +"class Address { + private _streetName: string; + private _city: string; + private _state: string; + private _houseNumber: number; + private _marriage?: boolean; + private _members?: string | number | boolean; + private _tupleType?: [string, number]; + private _tupleTypeWithAdditionalItems?: (string | number | any)[]; + private _arrayType: (string | any)[]; + private _additionalProperties?: Map; + + constructor(input: { + streetName: string, + city: string, + state: string, + houseNumber: number, + marriage?: boolean, + members?: string | number | boolean, + tupleType?: [string, number], + tupleTypeWithAdditionalItems?: (string | number | any)[], + arrayType: (string | any)[], + additionalProperties?: Map, + }) { + this._streetName = input.streetName; + this._city = input.city; + this._state = input.state; + this._houseNumber = input.houseNumber; + this._marriage = input.marriage; + this._members = input.members; + this._tupleType = input.tupleType; + this._tupleTypeWithAdditionalItems = input.tupleTypeWithAdditionalItems; + this._arrayType = input.arrayType; + this._additionalProperties = input.additionalProperties; + } + + get streetName(): string { return this._streetName; } + set streetName(streetName: string) { this._streetName = streetName; } + + get city(): string { return this._city; } + set city(city: string) { this._city = city; } + + get state(): string { return this._state; } + set state(state: string) { this._state = state; } + + get houseNumber(): number { return this._houseNumber; } + set houseNumber(houseNumber: number) { this._houseNumber = houseNumber; } + + get marriage(): boolean | undefined { return this._marriage; } + set marriage(marriage: boolean | undefined) { this._marriage = marriage; } + + get members(): string | number | boolean | undefined { return this._members; } + set members(members: string | number | boolean | undefined) { this._members = members; } + + get tupleType(): [string, number] | undefined { return this._tupleType; } + set tupleType(tupleType: [string, number] | undefined) { this._tupleType = tupleType; } + + get tupleTypeWithAdditionalItems(): (string | number | any)[] | undefined { return this._tupleTypeWithAdditionalItems; } + set tupleTypeWithAdditionalItems(tupleTypeWithAdditionalItems: (string | number | any)[] | undefined) { this._tupleTypeWithAdditionalItems = tupleTypeWithAdditionalItems; } + + get arrayType(): (string | any)[] { return this._arrayType; } + set arrayType(arrayType: (string | any)[]) { this._arrayType = arrayType; } + + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } +}" +`; + +exports[`TypeScriptGenerator should render \`enum\` type 1`] = ` +"enum States { + TEXAS = \\"Texas\\", + ALABAMA = \\"Alabama\\", + CALIFORNIA = \\"California\\", +}" +`; + +exports[`TypeScriptGenerator should render \`enum\` type as \`union\` if option enumType = \`union\` 1`] = `"type States = \\"Texas\\" | \\"Alabama\\" | \\"California\\";"`; + +exports[`TypeScriptGenerator should render \`interface\` type 1`] = ` +"interface Address { + streetName: string; + city: string; + state: string; + houseNumber: number; + marriage?: boolean; + members?: string | number | boolean; + tupleType?: [string, number]; + tupleTypeWithAdditionalItems?: (string | number | any)[]; + arrayType: (string | any)[]; + additionalProperties?: Map; +}" +`; + +exports[`TypeScriptGenerator should render \`type\` type - array of primitive type 1`] = `"type TypeArray = (string | any)[];"`; + +exports[`TypeScriptGenerator should render \`type\` type - array of union type 1`] = `"type TypeArray = (string | number | boolean | any)[];"`; + +exports[`TypeScriptGenerator should render \`type\` type - enum 1`] = ` +"enum TypeEnum { + TEXAS = \\"Texas\\", + ALABAMA = \\"Alabama\\", + CALIFORNIA = \\"California\\", + NUMBER_0 = 0, + NUMBER_1 = 1, + RESERVED_FALSE = \\"false\\", + RESERVED_TRUE = \\"true\\", +}" +`; + +exports[`TypeScriptGenerator should render \`type\` type - primitive 1`] = `"type TypePrimitive = string;"`; + +exports[`TypeScriptGenerator should render \`type\` type - union 1`] = `"type TypeUnion = string | number | boolean;"`; + +exports[`TypeScriptGenerator should render enums with translated special characters 1`] = ` +"enum States { + TEST_PLUS = \\"test+\\", + TEST = \\"test\\", + TEST_MINUS = \\"test-\\", + TEST_QUESTION_EXCLAMATION = \\"test?!\\", + ASTERISK_TEST = \\"*test\\", +}" +`; exports[`TypeScriptGenerator should render models and their dependencies for CJS module system 1`] = ` "const OtherModel = require('./OtherModel'); - class Address { private _streetName: string; private _city: string; @@ -12,10 +383,9 @@ class Address { private _houseNumber: number; private _marriage?: boolean; private _members?: string | number | boolean; - private _arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]; + private _arrayType: (string | number | any)[]; private _otherModel?: OtherModel; - private _additionalProperties?: Map | boolean | null>; - private _sTestPatternProperties?: Map; + private _additionalProperties?: Map; constructor(input: { streetName: string, @@ -24,8 +394,9 @@ class Address { houseNumber: number, marriage?: boolean, members?: string | number | boolean, - arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]], + arrayType: (string | number | any)[], otherModel?: OtherModel, + additionalProperties?: Map, }) { this._streetName = input.streetName; this._city = input.city; @@ -35,6 +406,7 @@ class Address { this._members = input.members; this._arrayType = input.arrayType; this._otherModel = input.otherModel; + this._additionalProperties = input.additionalProperties; } get streetName(): string { return this._streetName; } @@ -55,46 +427,43 @@ class Address { get members(): string | number | boolean | undefined { return this._members; } set members(members: string | number | boolean | undefined) { this._members = members; } - get arrayType(): [string, number, ...(object | string | number | Array | boolean | null)[]] { return this._arrayType; } - set arrayType(arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]) { this._arrayType = arrayType; } + get arrayType(): (string | number | any)[] { return this._arrayType; } + set arrayType(arrayType: (string | number | any)[]) { this._arrayType = arrayType; } get otherModel(): OtherModel | undefined { return this._otherModel; } set otherModel(otherModel: OtherModel | undefined) { this._otherModel = otherModel; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } - - get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } } module.exports = Address;" `; exports[`TypeScriptGenerator should render models and their dependencies for CJS module system 2`] = ` " - class OtherModel { private _streetName?: string; - private _additionalProperties?: Map | boolean | null>; + private _additionalProperties?: Map; constructor(input: { streetName?: string, + additionalProperties?: Map, }) { this._streetName = input.streetName; + this._additionalProperties = input.additionalProperties; } get streetName(): string | undefined { return this._streetName; } set streetName(streetName: string | undefined) { this._streetName = streetName; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } } module.exports = OtherModel;" `; exports[`TypeScriptGenerator should render models and their dependencies for CJS module system with named exports 1`] = ` "const {OtherModel} = require('./OtherModel'); - class Address { private _streetName: string; private _city: string; @@ -102,10 +471,9 @@ class Address { private _houseNumber: number; private _marriage?: boolean; private _members?: string | number | boolean; - private _arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]; + private _arrayType: (string | number | any)[]; private _otherModel?: OtherModel; - private _additionalProperties?: Map | boolean | null>; - private _sTestPatternProperties?: Map; + private _additionalProperties?: Map; constructor(input: { streetName: string, @@ -114,8 +482,9 @@ class Address { houseNumber: number, marriage?: boolean, members?: string | number | boolean, - arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]], + arrayType: (string | number | any)[], otherModel?: OtherModel, + additionalProperties?: Map, }) { this._streetName = input.streetName; this._city = input.city; @@ -125,6 +494,7 @@ class Address { this._members = input.members; this._arrayType = input.arrayType; this._otherModel = input.otherModel; + this._additionalProperties = input.additionalProperties; } get streetName(): string { return this._streetName; } @@ -145,46 +515,43 @@ class Address { get members(): string | number | boolean | undefined { return this._members; } set members(members: string | number | boolean | undefined) { this._members = members; } - get arrayType(): [string, number, ...(object | string | number | Array | boolean | null)[]] { return this._arrayType; } - set arrayType(arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]) { this._arrayType = arrayType; } + get arrayType(): (string | number | any)[] { return this._arrayType; } + set arrayType(arrayType: (string | number | any)[]) { this._arrayType = arrayType; } get otherModel(): OtherModel | undefined { return this._otherModel; } set otherModel(otherModel: OtherModel | undefined) { this._otherModel = otherModel; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } - - get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } } exports.Address = Address;" `; exports[`TypeScriptGenerator should render models and their dependencies for CJS module system with named exports 2`] = ` " - class OtherModel { private _streetName?: string; - private _additionalProperties?: Map | boolean | null>; + private _additionalProperties?: Map; constructor(input: { streetName?: string, + additionalProperties?: Map, }) { this._streetName = input.streetName; + this._additionalProperties = input.additionalProperties; } get streetName(): string | undefined { return this._streetName; } set streetName(streetName: string | undefined) { this._streetName = streetName; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } } exports.OtherModel = OtherModel;" `; exports[`TypeScriptGenerator should render models and their dependencies for ESM module system 1`] = ` "import OtherModel from './OtherModel'; - class Address { private _streetName: string; private _city: string; @@ -192,10 +559,9 @@ class Address { private _houseNumber: number; private _marriage?: boolean; private _members?: string | number | boolean; - private _arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]; + private _arrayType: (string | number | any)[]; private _otherModel?: OtherModel; - private _additionalProperties?: Map | boolean | null>; - private _sTestPatternProperties?: Map; + private _additionalProperties?: Map; constructor(input: { streetName: string, @@ -204,8 +570,9 @@ class Address { houseNumber: number, marriage?: boolean, members?: string | number | boolean, - arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]], + arrayType: (string | number | any)[], otherModel?: OtherModel, + additionalProperties?: Map, }) { this._streetName = input.streetName; this._city = input.city; @@ -215,6 +582,7 @@ class Address { this._members = input.members; this._arrayType = input.arrayType; this._otherModel = input.otherModel; + this._additionalProperties = input.additionalProperties; } get streetName(): string { return this._streetName; } @@ -235,17 +603,14 @@ class Address { get members(): string | number | boolean | undefined { return this._members; } set members(members: string | number | boolean | undefined) { this._members = members; } - get arrayType(): [string, number, ...(object | string | number | Array | boolean | null)[]] { return this._arrayType; } - set arrayType(arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]) { this._arrayType = arrayType; } + get arrayType(): (string | number | any)[] { return this._arrayType; } + set arrayType(arrayType: (string | number | any)[]) { this._arrayType = arrayType; } get otherModel(): OtherModel | undefined { return this._otherModel; } set otherModel(otherModel: OtherModel | undefined) { this._otherModel = otherModel; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } - - get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } } export default Address; " @@ -253,22 +618,23 @@ export default Address; exports[`TypeScriptGenerator should render models and their dependencies for ESM module system 2`] = ` " - class OtherModel { private _streetName?: string; - private _additionalProperties?: Map | boolean | null>; + private _additionalProperties?: Map; constructor(input: { streetName?: string, + additionalProperties?: Map, }) { this._streetName = input.streetName; + this._additionalProperties = input.additionalProperties; } get streetName(): string | undefined { return this._streetName; } set streetName(streetName: string | undefined) { this._streetName = streetName; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } } export default OtherModel; " @@ -276,18 +642,16 @@ export default OtherModel; exports[`TypeScriptGenerator should render models and their dependencies for ESM module system with named exports 1`] = ` "import {OtherModel} from './OtherModel'; - -export class Address { +class Address { private _streetName: string; private _city: string; private _state: string; private _houseNumber: number; private _marriage?: boolean; private _members?: string | number | boolean; - private _arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]; + private _arrayType: (string | number | any)[]; private _otherModel?: OtherModel; - private _additionalProperties?: Map | boolean | null>; - private _sTestPatternProperties?: Map; + private _additionalProperties?: Map; constructor(input: { streetName: string, @@ -296,8 +660,9 @@ export class Address { houseNumber: number, marriage?: boolean, members?: string | number | boolean, - arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]], + arrayType: (string | number | any)[], otherModel?: OtherModel, + additionalProperties?: Map, }) { this._streetName = input.streetName; this._city = input.city; @@ -307,6 +672,7 @@ export class Address { this._members = input.members; this._arrayType = input.arrayType; this._otherModel = input.otherModel; + this._additionalProperties = input.additionalProperties; } get streetName(): string { return this._streetName; } @@ -327,39 +693,99 @@ export class Address { get members(): string | number | boolean | undefined { return this._members; } set members(members: string | number | boolean | undefined) { this._members = members; } - get arrayType(): [string, number, ...(object | string | number | Array | boolean | null)[]] { return this._arrayType; } - set arrayType(arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]) { this._arrayType = arrayType; } + get arrayType(): (string | number | any)[] { return this._arrayType; } + set arrayType(arrayType: (string | number | any)[]) { this._arrayType = arrayType; } get otherModel(): OtherModel | undefined { return this._otherModel; } set otherModel(otherModel: OtherModel | undefined) { this._otherModel = otherModel; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } - - get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } } -" +export { Address };" `; exports[`TypeScriptGenerator should render models and their dependencies for ESM module system with named exports 2`] = ` " - -export class OtherModel { +class OtherModel { private _streetName?: string; - private _additionalProperties?: Map | boolean | null>; + private _additionalProperties?: Map; constructor(input: { streetName?: string, + additionalProperties?: Map, }) { this._streetName = input.streetName; + this._additionalProperties = input.additionalProperties; } get streetName(): string | undefined { return this._streetName; } set streetName(streetName: string | undefined) { this._streetName = streetName; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } } -" +export { OtherModel };" +`; + +exports[`TypeScriptGenerator should render union \`enum\` values 1`] = ` +"enum States { + NUMBER_2 = 2, + RESERVED_NUMBER_2 = \\"2\\", + TEST = \\"test\\", + RESERVED_TRUE = \\"true\\", + CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT = '{\\"test\\":\\"test\\"}', +}" +`; + +exports[`TypeScriptGenerator should work custom preset for \`class\` type 1`] = ` +"class CustomClass { + @JsonProperty(\\"property\\") + private _property?: string; + @JsonProperty(\\"additionalProperties\\") + private _additionalProperties?: Map; + + constructor(input: { + property?: string, + additionalProperties?: Map, + }) { + this._property = input.property; + this._additionalProperties = input.additionalProperties; + } + + get property(): string | undefined { return this._property; } + set property(property: string | undefined) { this._property = property; } + + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } +}" +`; + +exports[`TypeScriptGenerator should work custom preset for \`enum\` type 1`] = ` +"enum CustomEnum { + TEXAS = \\"Texas\\", + ALABAMA = \\"Alabama\\", + CALIFORNIA = \\"California\\", +}" +`; + +exports[`TypeScriptGenerator should work custom preset for \`interface\` type 1`] = ` +"class CustomInterface { + private _property?: string; + private _additionalProperties?: Map; + + constructor(input: { + property?: string, + additionalProperties?: Map, + }) { + this._property = input.property; + this._additionalProperties = input.additionalProperties; + } + + get property(): string | undefined { return this._property; } + set property(property: string | undefined) { this._property = property; } + + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } +}" `; diff --git a/test/generators/typescript/constrainer/EnumConstrainer.spec.ts b/test/generators/typescript/constrainer/EnumConstrainer.spec.ts new file mode 100644 index 0000000000..52c02bf620 --- /dev/null +++ b/test/generators/typescript/constrainer/EnumConstrainer.spec.ts @@ -0,0 +1,167 @@ +import { TypeScriptDefaultConstraints } from '../../../../src/generators/typescript/TypeScriptConstrainer'; +import { EnumModel } from '../../../../src/models/MetaModel'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../../src'; +import { + defaultEnumKeyConstraints, + ModelEnumKeyConstraints, + DefaultEnumKeyConstraints +} from '../../../../src/generators/typescript/constrainer/EnumConstrainer'; +describe('EnumConstrainer', () => { + const enumModel = new EnumModel('test', undefined, []); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [] + ); + + describe('enum keys', () => { + test('should never render special chars', () => { + const constrainedKey = TypeScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '%' + }); + expect(constrainedKey).toEqual('PERCENT'); + }); + test('should not render number as start char', () => { + const constrainedKey = TypeScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '1' + }); + expect(constrainedKey).toEqual('NUMBER_1'); + }); + test('should not contain duplicate keys', () => { + const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel( + 'EMPTY', + 'return' + ); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + '', + [existingConstrainedEnumValueModel] + ); + const constrainedKey = TypeScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('RESERVED_EMPTY'); + }); + test('should never contain empty keys', () => { + const constrainedKey = TypeScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('EMPTY'); + }); + test('should use constant naming format', () => { + const constrainedKey = TypeScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual( + 'SOME_SPACE_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2' + ); + }); + test('should never render reserved keywords', () => { + const constrainedKey = TypeScriptDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'return' + }); + expect(constrainedKey).toEqual('RESERVED_RETURN'); + }); + }); + describe('enum values', () => { + test('should render string values', () => { + const constrainedValue = TypeScriptDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 'string value' + }); + expect(constrainedValue).toEqual('"string value"'); + }); + test('should render boolean values', () => { + const constrainedValue = TypeScriptDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: true + }); + expect(constrainedValue).toEqual('"true"'); + }); + test.skip('should render numbers', () => { + const constrainedValue = TypeScriptDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 123 + }); + expect(constrainedValue).toEqual(123); + }); + test.skip('should render object', () => { + const constrainedValue = TypeScriptDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: { test: 'test' } + }); + expect(constrainedValue).toEqual('"{\\"test\\":\\"test\\"}"'); + }); + test.skip('should render unknown value', () => { + const constrainedValue = TypeScriptDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: undefined + }); + expect(constrainedValue).toEqual('"undefined"'); + }); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks for enum key', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultEnumKeyConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks for enum key', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultEnumKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_DUPLICATE_KEYS'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultEnumKeyConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultEnumKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/typescript/constrainer/ModelNameConstrainer.spec.ts b/test/generators/typescript/constrainer/ModelNameConstrainer.spec.ts new file mode 100644 index 0000000000..11bcd88ba5 --- /dev/null +++ b/test/generators/typescript/constrainer/ModelNameConstrainer.spec.ts @@ -0,0 +1,75 @@ +import { TypeScriptDefaultConstraints } from '../../../../src/generators/typescript/TypeScriptConstrainer'; +import { + DefaultModelNameConstraints, + defaultModelNameConstraints, + ModelNameConstraints +} from '../../../../src/generators/typescript/constrainer/ModelNameConstrainer'; +describe('ModelNameConstrainer', () => { + test('should never render special chars', () => { + const constrainedKey = TypeScriptDefaultConstraints.modelName({ + modelName: '%' + }); + expect(constrainedKey).toEqual('Percent'); + }); + test('should never render number as start char', () => { + const constrainedKey = TypeScriptDefaultConstraints.modelName({ + modelName: '1' + }); + expect(constrainedKey).toEqual('Number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = TypeScriptDefaultConstraints.modelName({ + modelName: '' + }); + expect(constrainedKey).toEqual('Empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = TypeScriptDefaultConstraints.modelName({ + modelName: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = TypeScriptDefaultConstraints.modelName({ + modelName: 'return' + }); + expect(constrainedKey).toEqual('ReservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: ModelNameConstraints = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultModelNameConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ modelName: '' }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultModelNameConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultModelNameConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultModelNameConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ modelName: '' }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/typescript/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/typescript/constrainer/PropertyKeyConstrainer.spec.ts new file mode 100644 index 0000000000..3da3a8b430 --- /dev/null +++ b/test/generators/typescript/constrainer/PropertyKeyConstrainer.spec.ts @@ -0,0 +1,180 @@ +import { TypeScriptDefaultConstraints } from '../../../../src/generators/typescript/TypeScriptConstrainer'; +import { + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ObjectModel, + ObjectPropertyModel +} from '../../../../src'; +import { + DefaultPropertyKeyConstraints, + defaultPropertyKeyConstraints, + PropertyKeyConstraintOptions +} from '../../../../src/generators/typescript/constrainer/PropertyKeyConstrainer'; +describe('PropertyKeyConstrainer', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + + const constrainPropertyName = (propertyName: string) => { + const objectPropertyModel = new ObjectPropertyModel( + propertyName, + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + return TypeScriptDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + }; + + test('should never render special chars', () => { + const constrainedKey = constrainPropertyName('%'); + expect(constrainedKey).toEqual('percent'); + }); + test('should not render number as start char', () => { + const constrainedKey = constrainPropertyName('1'); + expect(constrainedKey).toEqual('number_1'); + }); + test('should never contain empty name', () => { + const constrainedKey = constrainPropertyName(''); + expect(constrainedKey).toEqual('empty'); + }); + test('should use constant naming format', () => { + const constrainedKey = constrainPropertyName('some weird_value!"#2'); + expect(constrainedKey).toEqual('someWeirdValueExclamationQuotationHash_2'); + }); + test('should not contain duplicate properties', () => { + const objectModel = new ObjectModel('test', undefined, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + '', + {} + ); + const objectPropertyModel = new ObjectPropertyModel( + 'reservedReturn', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + 'reservedReturn', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const objectPropertyModel2 = new ObjectPropertyModel( + 'return', + false, + objectModel + ); + const constrainedObjectPropertyModel2 = new ConstrainedObjectPropertyModel( + 'return', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainedObjectModel.properties['reservedReturn'] = + constrainedObjectPropertyModel; + constrainedObjectModel.properties['return'] = + constrainedObjectPropertyModel2; + const constrainedKey = TypeScriptDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel: objectPropertyModel2, + constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + }); + expect(constrainedKey).toEqual('reservedReservedReturn'); + }); + test('should never render reserved keywords', () => { + const constrainedKey = constrainPropertyName('return'); + expect(constrainedKey).toEqual('reservedReturn'); + }); + describe('custom constraints', () => { + test('should be able to overwrite all hooks', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultPropertyKeyConstraints( + mockedConstraintCallbacks + ); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + //Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite one hooks', () => { + //All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_RESERVED_KEYWORDS'), + jest.spyOn(DefaultPropertyKeyConstraints, 'NO_DUPLICATE_PROPERTIES') + ]; + const overwrittenDefaultFunction = jest.spyOn( + DefaultPropertyKeyConstraints, + 'NAMING_FORMATTER' + ); + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultPropertyKeyConstraints({ + NAMING_FORMATTER: jestCallback + }); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + const constrainedValue = constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + expect(constrainedValue).toEqual(''); + expect(jestCallback).toHaveBeenCalled(); + expect(overwrittenDefaultFunction).not.toHaveBeenCalled(); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/typescript/preset/DescriptionPreset.spec.ts b/test/generators/typescript/preset/DescriptionPreset.spec.ts index f5477d561e..7a2048a91c 100644 --- a/test/generators/typescript/preset/DescriptionPreset.spec.ts +++ b/test/generators/typescript/preset/DescriptionPreset.spec.ts @@ -1,4 +1,4 @@ -import {TS_DESCRIPTION_PRESET, TypeScriptGenerator} from '../../../../src'; +import { TS_DESCRIPTION_PRESET, TypeScriptGenerator } from '../../../../src'; const doc = { $id: 'Test', @@ -11,37 +11,29 @@ const doc = { numberProp: { type: 'number', description: 'Description', - examples: 'Example', + examples: 'Example' }, objectProp: { type: 'object', $id: 'NestedTest', properties: { stringProp: { type: 'string' } }, - examples: ['Example 1', 'Example 2'], - }, - }, + examples: ['Example 1', 'Example 2'] + } + } }; describe('Description generation', () => { let generator: TypeScriptGenerator; beforeEach(() => { generator = new TypeScriptGenerator({ - presets: [TS_DESCRIPTION_PRESET], + presets: [TS_DESCRIPTION_PRESET] }); }); test('should render example function for model', async () => { - const inputModel = await generator.process(doc); - const testModel = inputModel.models['Test']; - const nestedTestModel = inputModel.models['NestedTest']; - - const testClass = await generator.renderClass(testModel, inputModel); - const nestedTestClass = await generator.renderClass( - nestedTestModel, - inputModel - ); - - expect(testClass.result).toMatchSnapshot(); - expect(nestedTestClass.result).toMatchSnapshot(); + const models = await generator.generate(doc); + expect(models).toHaveLength(2); + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); }); }); diff --git a/test/generators/typescript/preset/ExamplePreset.spec.ts b/test/generators/typescript/preset/ExamplePreset.spec.ts index c38b8c02ef..724097e4b3 100644 --- a/test/generators/typescript/preset/ExamplePreset.spec.ts +++ b/test/generators/typescript/preset/ExamplePreset.spec.ts @@ -1,4 +1,7 @@ -import { TypeScriptGenerator, TS_COMMON_PRESET } from '../../../../src/generators'; +import { + TypeScriptGenerator, + TS_COMMON_PRESET +} from '../../../../src/generators'; const doc = { $id: 'Test', type: 'object', @@ -7,17 +10,21 @@ const doc = { properties: { 'string prop': { type: 'string' }, numberProp: { type: 'number' }, - objectProp: { type: 'object', $id: 'NestedTest', properties: {stringProp: { type: 'string' }}} + objectProp: { + type: 'object', + $id: 'NestedTest', + properties: { stringProp: { type: 'string' } } + } }, patternProperties: { '^S(.?)test': { type: 'string' } - }, + } }; describe('Example function generation', () => { test('should render example function for model', async () => { - const generator = new TypeScriptGenerator({ + const generator = new TypeScriptGenerator({ presets: [ { preset: TS_COMMON_PRESET, @@ -27,14 +34,9 @@ describe('Example function generation', () => { } ] }); - const inputModel = await generator.process(doc); - const testModel = inputModel.models['Test']; - const nestedTestModel = inputModel.models['NestedTest']; - - const testClass = await generator.renderClass(testModel, inputModel); - const nestedTestClass = await generator.renderClass(nestedTestModel, inputModel); - - expect(testClass.result).toMatchSnapshot(); - expect(nestedTestClass.result).toMatchSnapshot(); + const models = await generator.generate(doc); + expect(models).toHaveLength(2); + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); }); }); diff --git a/test/generators/typescript/preset/JsonBinPackPreset.spec.ts b/test/generators/typescript/preset/JsonBinPackPreset.spec.ts new file mode 100644 index 0000000000..e9a88a8a30 --- /dev/null +++ b/test/generators/typescript/preset/JsonBinPackPreset.spec.ts @@ -0,0 +1,107 @@ +import { + TS_COMMON_PRESET, + TS_JSONBINPACK_PRESET, + TypeScriptGenerator +} from '../../../../src'; + +const doc = { + $id: 'Test', + type: 'object', + additionalProperties: true, + required: ['string prop'], + description: 'Main Description', + properties: { + 'string prop': { type: 'string' }, + numberProp: { + type: 'number', + description: 'Description', + examples: 'Example' + }, + objectProp: { + type: 'object', + $id: 'NestedTest', + properties: { stringProp: { type: 'string' } }, + examples: ['Example 1', 'Example 2'] + } + } +}; + +describe('JsonBinPack preset', () => { + let generator: TypeScriptGenerator; + beforeEach(() => { + generator = new TypeScriptGenerator({ + presets: [ + { + preset: TS_COMMON_PRESET, + options: { + marshalling: true + } + }, + TS_JSONBINPACK_PRESET + ] + }); + }); + + test('should work fine with AsyncAPI inputs', async () => { + const input = { + asyncapi: '2.0.0', + defaultContentType: 'application/json', + info: { + title: 'Signup service example (internal)', + version: '0.1.0' + }, + channels: { + '/user/signedup': { + subscribe: { + message: { + payload: { + type: 'object', + properties: { + email: { + type: 'string', + format: 'email' + } + } + } + } + } + } + } + }; + const models = await generator.generate(input); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should work fine with JSON Schema draft 4', async () => { + const input = { + $schema: 'http://json-schema.org/draft-04/schema', + type: 'object', + properties: { + email: { + type: 'string', + format: 'email' + } + } + }; + const models = await generator.generate(input); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should work fine with JSON Schema draft 6', async () => { + const input = { + $schema: 'http://json-schema.org/draft-06/schema', + type: 'object', + properties: { + email: { + type: 'string', + format: 'email' + } + } + }; + const models = await generator.generate(input); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); +}); diff --git a/test/generators/typescript/preset/MarshallingPreset.spec.ts b/test/generators/typescript/preset/MarshallingPreset.spec.ts index aea117ac52..e0d457d315 100644 --- a/test/generators/typescript/preset/MarshallingPreset.spec.ts +++ b/test/generators/typescript/preset/MarshallingPreset.spec.ts @@ -1,31 +1,34 @@ /* eslint-disable */ -import { TypeScriptGenerator, TS_COMMON_PRESET } from '../../../../src/generators'; -import Ajv from 'ajv'; +import { + TypeScriptGenerator, + TS_COMMON_PRESET +} from '../../../../src/generators'; const doc = { definitions: { - 'NestedTest': { - type: 'object', $id: 'NestedTest', properties: {stringProp: { type: 'string' }} + NestedTest: { + type: 'object', + $id: 'NestedTest', + properties: { stringProp: { type: 'string' } } } }, $id: 'Test', type: 'object', - additionalProperties: {$ref: '#/definitions/NestedTest'}, + additionalProperties: { $ref: '#/definitions/NestedTest' }, required: ['string prop'], properties: { 'string prop': { type: 'string' }, - enumProp: { $id: 'EnumTest', enum: ['Some enum String', true, {test: 'test'}, 2]}, + enumProp: { + $id: 'EnumTest', + enum: ['Some enum String', true, { test: 'test' }, 2] + }, numberProp: { type: 'number' }, - objectProp: { $ref: '#/definitions/NestedTest' } - }, - patternProperties: { - '^S(.?)test': { type: 'string' }, - '^S(.?)AnotherTest': { $ref: '#/definitions/NestedTest' }, - }, + nestedObject: { $ref: '#/definitions/NestedTest' } + } }; describe('Marshalling preset', () => { test('should render un/marshal code', async () => { - const generator = new TypeScriptGenerator({ + const generator = new TypeScriptGenerator({ presets: [ { preset: TS_COMMON_PRESET, @@ -35,214 +38,10 @@ describe('Marshalling preset', () => { } ] }); - const inputModel = await generator.process(doc); - - const testModel = inputModel.models['Test']; - const nestedTestModel = inputModel.models['NestedTest']; - - const testClass = await generator.renderClass(testModel, inputModel); - const nestedTestClass = await generator.renderClass(nestedTestModel, inputModel); - - expect(testClass.result).toMatchSnapshot(); - expect(nestedTestClass.result).toMatchSnapshot(); - }); - - test('should provide a two way conversion', async () => { - enum EnumTest { - SOME_ENUM_STRING = "Some enum String", - NUMBER_2 = 2, - TRUE = "true", - TEST_TEST = '{"test": "test"}', - } - class NestedTest { - private _stringProp?: string; - private _additionalProperties?: Map | boolean | null>; - - constructor(input: { - stringProp?: string, - }) { - this._stringProp = input.stringProp; - } - - get stringProp(): string | undefined { return this._stringProp; } - set stringProp(stringProp: string | undefined) { this._stringProp = stringProp; } - - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } - - public marshal() : string { - let json = '{' - if(this.stringProp !== undefined) { - json += `"stringProp": ${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},`; - } - - if(this.additionalProperties !== undefined) { - for (const [key, value] of this.additionalProperties.entries()) { - //Only render additionalProperties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - json += `"${key}": ${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},`; - } - } - - //Remove potential last comma - return `${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}`; - } - - public static unmarshal(json: string | object): NestedTest { - const obj = typeof json === "object" ? json : JSON.parse(json); - const instance = new NestedTest({} as any); - - if (obj["stringProp"] !== undefined) { - instance.stringProp = obj["stringProp"] as any; - } - - //Not part of core properties - - if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return !["stringProp"].includes(key);}))) { - - instance.additionalProperties.set(key, value as any); - } - return instance; - } - } - class Test { - private _stringProp: string; - private _enumProp?: EnumTest; - private _numberProp?: number; - private _objectProp?: NestedTest; - private _additionalProperties?: Map; - private _sTestPatternProperties?: Map; - private _sAnotherTestPatternProperties?: Map; - - constructor(input: { - stringProp: string, - enumProp?: EnumTest, - numberProp?: number, - objectProp?: NestedTest, - }) { - this._stringProp = input.stringProp; - this._enumProp = input.enumProp; - this._numberProp = input.numberProp; - this._objectProp = input.objectProp; - } - - get stringProp(): string { return this._stringProp; } - set stringProp(stringProp: string) { this._stringProp = stringProp; } - - get enumProp(): EnumTest | undefined { return this._enumProp; } - set enumProp(enumProp: EnumTest | undefined) { this._enumProp = enumProp; } - - get numberProp(): number | undefined { return this._numberProp; } - set numberProp(numberProp: number | undefined) { this._numberProp = numberProp; } - - get objectProp(): NestedTest | undefined { return this._objectProp; } - set objectProp(objectProp: NestedTest | undefined) { this._objectProp = objectProp; } - - get additionalProperties(): Map | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } - - get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } - - get sAnotherTestPatternProperties(): Map | undefined { return this._sAnotherTestPatternProperties; } - set sAnotherTestPatternProperties(sTest2PatternProperties: Map | undefined) { this._sAnotherTestPatternProperties = sTest2PatternProperties; } - - public marshal() : string { - let json = '{' - if(this.stringProp !== undefined) { - json += `"string prop": ${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},`; - } - if(this.enumProp !== undefined) { - json += `"enumProp": ${typeof this.enumProp === 'number' || typeof this.enumProp === 'boolean' ? this.enumProp : JSON.stringify(this.enumProp)},`; - } - if(this.numberProp !== undefined) { - json += `"numberProp": ${typeof this.numberProp === 'number' || typeof this.numberProp === 'boolean' ? this.numberProp : JSON.stringify(this.numberProp)},`; - } - if(this.objectProp !== undefined) { - json += `"objectProp": ${this.objectProp.marshal()},`; - } - if(this.sTestPatternProperties !== undefined) { - for (const [key, value] of this.sTestPatternProperties.entries()) { - //Only render pattern properties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - json += `"${key}": ${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},`; - } - }if(this.sAnotherTestPatternProperties !== undefined) { - for (const [key, value] of this.sAnotherTestPatternProperties.entries()) { - //Only render pattern properties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - json += `"${key}": ${value.marshal()},`; - } - } - if(this.additionalProperties !== undefined) { - for (const [key, value] of this.additionalProperties.entries()) { - //Only render additionalProperties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - json += `"${key}": ${value.marshal()},`; - } - } - - //Remove potential last comma - return `${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}`; - } - - public static unmarshal(json: string | object): Test { - const obj = typeof json === "object" ? json : JSON.parse(json); - const instance = new Test({} as any); - - if (obj["string prop"] !== undefined) { - instance.stringProp = obj["string prop"]; - } - if (obj["enumProp"] !== undefined) { - instance.enumProp = obj["enumProp"]; - } - if (obj["numberProp"] !== undefined) { - instance.numberProp = obj["numberProp"]; - } - if (obj["objectProp"] !== undefined) { - instance.objectProp = NestedTest.unmarshal(obj["objectProp"]); - } - - //Not part of core properties - if (instance.sTestPatternProperties === undefined) {instance.sTestPatternProperties = new Map();} - if (instance.sAnotherTestPatternProperties === undefined) {instance.sAnotherTestPatternProperties = new Map();} - - if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return !["string prop","enumProp","numberProp","objectProp"].includes(key);}))) { - //Check all pattern properties - if (key.match(new RegExp('^S(.?)test'))) { - instance.sTestPatternProperties.set(key, value as any); - continue; - }//Check all pattern properties - if (key.match(new RegExp('^S(.?)AnotherTest'))) { - instance.sAnotherTestPatternProperties.set(key, NestedTest.unmarshal(value as any)); - continue; - } - instance.additionalProperties.set(key, NestedTest.unmarshal(value as any)); - } - return instance; - } - } - const ajv = new Ajv(); - const nestedTestInstance = new NestedTest({stringProp: "SomeTestString"}); - nestedTestInstance.additionalProperties = new Map(); - nestedTestInstance.additionalProperties.set("someProp", "someValue"); - const testInstance = new Test({numberProp: 0, stringProp: "SomeTestString", objectProp: nestedTestInstance, enumProp: EnumTest.SOME_ENUM_STRING}); - testInstance.additionalProperties = new Map(); - testInstance.additionalProperties.set('additionalProp', nestedTestInstance); - testInstance.sTestPatternProperties = new Map(); - testInstance.sTestPatternProperties.set('Stest', 'Some pattern value'); - testInstance.sAnotherTestPatternProperties = new Map(); - testInstance.sAnotherTestPatternProperties.set('SAnotherTest', nestedTestInstance); - - - const marshalContent = testInstance.marshal(); - const unmarshalInstance = Test.unmarshal(marshalContent); - const validationResult = ajv.validate(doc, JSON.parse(marshalContent)); - - expect(marshalContent).toEqual("{\"string prop\": \"SomeTestString\",\"enumProp\": \"Some enum String\",\"numberProp\": 0,\"objectProp\": {\"stringProp\": \"SomeTestString\",\"someProp\": \"someValue\"},\"Stest\": \"Some pattern value\",\"SAnotherTest\": {\"stringProp\": \"SomeTestString\",\"someProp\": \"someValue\"},\"additionalProp\": {\"stringProp\": \"SomeTestString\",\"someProp\": \"someValue\"}}"); - expect(validationResult).toEqual(true); - expect(unmarshalInstance).toEqual(testInstance); + const models = await generator.generate(doc); + expect(models).toHaveLength(3); + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); + expect(models[2].result).toMatchSnapshot(); }); }); diff --git a/test/generators/typescript/preset/__snapshots__/DescriptionPreset.spec.ts.snap b/test/generators/typescript/preset/__snapshots__/DescriptionPreset.spec.ts.snap index 8462215394..59f18c2017 100644 --- a/test/generators/typescript/preset/__snapshots__/DescriptionPreset.spec.ts.snap +++ b/test/generators/typescript/preset/__snapshots__/DescriptionPreset.spec.ts.snap @@ -11,17 +11,22 @@ class Test { * @example Example */ private _numberProp?: number; + /** + * @example Example 1, Example 2 + */ private _objectProp?: NestedTest; - private _additionalProperties?: Map | boolean | null>; + private _additionalProperties?: Map; constructor(input: { stringProp: string, numberProp?: number, objectProp?: NestedTest, + additionalProperties?: Map, }) { this._stringProp = input.stringProp; this._numberProp = input.numberProp; this._objectProp = input.objectProp; + this._additionalProperties = input.additionalProperties; } get stringProp(): string { return this._stringProp; } @@ -33,8 +38,8 @@ class Test { get objectProp(): NestedTest | undefined { return this._objectProp; } set objectProp(objectProp: NestedTest | undefined) { this._objectProp = objectProp; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } }" `; @@ -44,18 +49,20 @@ exports[`Description generation should render example function for model 2`] = ` */ class NestedTest { private _stringProp?: string; - private _additionalProperties?: Map | boolean | null>; + private _additionalProperties?: Map; constructor(input: { stringProp?: string, + additionalProperties?: Map, }) { this._stringProp = input.stringProp; + this._additionalProperties = input.additionalProperties; } get stringProp(): string | undefined { return this._stringProp; } set stringProp(stringProp: string | undefined) { this._stringProp = stringProp; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } }" `; diff --git a/test/generators/typescript/preset/__snapshots__/ExamplePreset.spec.ts.snap b/test/generators/typescript/preset/__snapshots__/ExamplePreset.spec.ts.snap index 72bb598db4..af28da444b 100644 --- a/test/generators/typescript/preset/__snapshots__/ExamplePreset.spec.ts.snap +++ b/test/generators/typescript/preset/__snapshots__/ExamplePreset.spec.ts.snap @@ -5,17 +5,18 @@ exports[`Example function generation should render example function for model 1` private _stringProp: string; private _numberProp?: number; private _objectProp?: NestedTest; - private _additionalProperties?: Map | boolean | null>; - private _sTestPatternProperties?: Map; + private _additionalProperties?: Map; constructor(input: { stringProp: string, numberProp?: number, objectProp?: NestedTest, + additionalProperties?: Map, }) { this._stringProp = input.stringProp; this._numberProp = input.numberProp; this._objectProp = input.objectProp; + this._additionalProperties = input.additionalProperties; } get stringProp(): string { return this._stringProp; } @@ -27,16 +28,13 @@ exports[`Example function generation should render example function for model 1` get objectProp(): NestedTest | undefined { return this._objectProp; } set objectProp(objectProp: NestedTest | undefined) { this._objectProp = objectProp; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } - - get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } public static example(): Test { const instance = new Test({} as any); instance.stringProp = \\"string\\"; - instance.numberProp = 0; + instance.numberProp = 0.0; instance.objectProp = NestedTest.example(); return instance; } @@ -46,19 +44,21 @@ exports[`Example function generation should render example function for model 1` exports[`Example function generation should render example function for model 2`] = ` "class NestedTest { private _stringProp?: string; - private _additionalProperties?: Map | boolean | null>; + private _additionalProperties?: Map; constructor(input: { stringProp?: string, + additionalProperties?: Map, }) { this._stringProp = input.stringProp; + this._additionalProperties = input.additionalProperties; } get stringProp(): string | undefined { return this._stringProp; } set stringProp(stringProp: string | undefined) { this._stringProp = stringProp; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } public static example(): NestedTest { const instance = new NestedTest({} as any); diff --git a/test/generators/typescript/preset/__snapshots__/JsonBinPackPreset.spec.ts.snap b/test/generators/typescript/preset/__snapshots__/JsonBinPackPreset.spec.ts.snap new file mode 100644 index 0000000000..8e23a3f4c3 --- /dev/null +++ b/test/generators/typescript/preset/__snapshots__/JsonBinPackPreset.spec.ts.snap @@ -0,0 +1,199 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`JsonBinPack preset should work fine with AsyncAPI inputs 1`] = ` +"class AnonymousSchema_1 { + private _email?: string; + private _additionalProperties?: Map; + + constructor(input: { + email?: string, + additionalProperties?: Map, + }) { + this._email = input.email; + this._additionalProperties = input.additionalProperties; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } + + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } + + public marshal() : string { + let json = '{' + if(this.email !== undefined) { + json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; + } + if(this.additionalProperties !== undefined) { + for (const [key, value] of this.additionalProperties.entries()) { + //Only unwrap those who are not already a property in the JSON object + if(Object.keys(this).includes(String(key))) continue; + json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; + } + } + + //Remove potential last comma + return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; + } + + public static unmarshal(json: string | object): AnonymousSchema_1 { + const obj = typeof json === \\"object\\" ? json : JSON.parse(json); + const instance = new AnonymousSchema_1({} as any); + + if (obj[\\"email\\"] !== undefined) { + instance.email = obj[\\"email\\"]; + } + + if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} + for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"email\\",\\"additionalProperties\\"].includes(key);}))) { + instance.additionalProperties.set(key, value as any); + } + + return instance; + } + + public async jsonbinSerialize(): Promise{ + const jsonData = JSON.parse(this.marshal()); + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_2\\"}},\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_1\\"}); + return jsonbinpack.serialize(jsonbinpackEncodedSchema, jsonData); + } + + public static async jsonbinDeserialize(buffer: Buffer): Promise { + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_2\\"}},\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_1\\"}); + const json = jsonbinpack.deserialize(jsonbinpackEncodedSchema, buffer); + return AnonymousSchema_1.unmarshal(json); + } +}" +`; + +exports[`JsonBinPack preset should work fine with JSON Schema draft 4 1`] = ` +"class Root { + private _email?: string; + private _additionalProperties?: Map; + + constructor(input: { + email?: string, + additionalProperties?: Map, + }) { + this._email = input.email; + this._additionalProperties = input.additionalProperties; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } + + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } + + public marshal() : string { + let json = '{' + if(this.email !== undefined) { + json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; + } + if(this.additionalProperties !== undefined) { + for (const [key, value] of this.additionalProperties.entries()) { + //Only unwrap those who are not already a property in the JSON object + if(Object.keys(this).includes(String(key))) continue; + json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; + } + } + + //Remove potential last comma + return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; + } + + public static unmarshal(json: string | object): Root { + const obj = typeof json === \\"object\\" ? json : JSON.parse(json); + const instance = new Root({} as any); + + if (obj[\\"email\\"] !== undefined) { + instance.email = obj[\\"email\\"]; + } + + if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} + for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"email\\",\\"additionalProperties\\"].includes(key);}))) { + instance.additionalProperties.set(key, value as any); + } + + return instance; + } + + public async jsonbinSerialize(): Promise{ + const jsonData = JSON.parse(this.marshal()); + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"http://json-schema.org/draft-04/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); + return jsonbinpack.serialize(jsonbinpackEncodedSchema, jsonData); + } + + public static async jsonbinDeserialize(buffer: Buffer): Promise { + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"http://json-schema.org/draft-04/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); + const json = jsonbinpack.deserialize(jsonbinpackEncodedSchema, buffer); + return Root.unmarshal(json); + } +}" +`; + +exports[`JsonBinPack preset should work fine with JSON Schema draft 6 1`] = ` +"class Root { + private _email?: string; + private _additionalProperties?: Map; + + constructor(input: { + email?: string, + additionalProperties?: Map, + }) { + this._email = input.email; + this._additionalProperties = input.additionalProperties; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } + + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } + + public marshal() : string { + let json = '{' + if(this.email !== undefined) { + json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; + } + if(this.additionalProperties !== undefined) { + for (const [key, value] of this.additionalProperties.entries()) { + //Only unwrap those who are not already a property in the JSON object + if(Object.keys(this).includes(String(key))) continue; + json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; + } + } + + //Remove potential last comma + return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; + } + + public static unmarshal(json: string | object): Root { + const obj = typeof json === \\"object\\" ? json : JSON.parse(json); + const instance = new Root({} as any); + + if (obj[\\"email\\"] !== undefined) { + instance.email = obj[\\"email\\"]; + } + + if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} + for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"email\\",\\"additionalProperties\\"].includes(key);}))) { + instance.additionalProperties.set(key, value as any); + } + + return instance; + } + + public async jsonbinSerialize(): Promise{ + const jsonData = JSON.parse(this.marshal()); + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"http://json-schema.org/draft-06/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); + return jsonbinpack.serialize(jsonbinpackEncodedSchema, jsonData); + } + + public static async jsonbinDeserialize(buffer: Buffer): Promise { + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"http://json-schema.org/draft-06/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); + const json = jsonbinpack.deserialize(jsonbinpackEncodedSchema, buffer); + return Root.unmarshal(json); + } +}" +`; diff --git a/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap b/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap index fc1b58543f..48933560c8 100644 --- a/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap +++ b/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap @@ -5,21 +5,21 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` private _stringProp: string; private _enumProp?: EnumTest; private _numberProp?: number; - private _objectProp?: NestedTest; - private _additionalProperties?: Map; - private _sTestPatternProperties?: Map; - private _sAnotherTestPatternProperties?: Map; + private _nestedObject?: NestedTest; + private _additionalProperties?: Map; constructor(input: { stringProp: string, enumProp?: EnumTest, numberProp?: number, - objectProp?: NestedTest, + nestedObject?: NestedTest, + additionalProperties?: Map, }) { this._stringProp = input.stringProp; this._enumProp = input.enumProp; this._numberProp = input.numberProp; - this._objectProp = input.objectProp; + this._nestedObject = input.nestedObject; + this._additionalProperties = input.additionalProperties; } get stringProp(): string { return this._stringProp; } @@ -31,17 +31,11 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` get numberProp(): number | undefined { return this._numberProp; } set numberProp(numberProp: number | undefined) { this._numberProp = numberProp; } - get objectProp(): NestedTest | undefined { return this._objectProp; } - set objectProp(objectProp: NestedTest | undefined) { this._objectProp = objectProp; } + get nestedObject(): NestedTest | undefined { return this._nestedObject; } + set nestedObject(nestedObject: NestedTest | undefined) { this._nestedObject = nestedObject; } - get additionalProperties(): Map | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } - - get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } - set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } - - get sAnotherTestPatternProperties(): Map | undefined { return this._sAnotherTestPatternProperties; } - set sAnotherTestPatternProperties(sAnotherTestPatternProperties: Map | undefined) { this._sAnotherTestPatternProperties = sAnotherTestPatternProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } public marshal() : string { let json = '{' @@ -54,26 +48,13 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` if(this.numberProp !== undefined) { json += \`\\"numberProp\\": \${typeof this.numberProp === 'number' || typeof this.numberProp === 'boolean' ? this.numberProp : JSON.stringify(this.numberProp)},\`; } - if(this.objectProp !== undefined) { - json += \`\\"objectProp\\": \${this.objectProp.marshal()},\`; - } - if(this.sTestPatternProperties !== undefined) { - for (const [key, value] of this.sTestPatternProperties.entries()) { - //Only render pattern properties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; - } - }if(this.sAnotherTestPatternProperties !== undefined) { - for (const [key, value] of this.sAnotherTestPatternProperties.entries()) { - //Only render pattern properties which are not already a property - if(Object.keys(this).includes(String(key))) continue; - json += \`\\"\${key}\\": \${value.marshal()},\`; - } + if(this.nestedObject !== undefined) { + json += \`\\"nestedObject\\": \${this.nestedObject.marshal()},\`; } if(this.additionalProperties !== undefined) { - for (const [key, value] of this.additionalProperties.entries()) { - //Only render additionalProperties which are not already a property - if(Object.keys(this).includes(String(key))) continue; + for (const [key, value] of this.additionalProperties.entries()) { + //Only unwrap those who are not already a property in the JSON object + if(Object.keys(this).includes(String(key))) continue; json += \`\\"\${key}\\": \${value.marshal()},\`; } } @@ -95,59 +76,57 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` if (obj[\\"numberProp\\"] !== undefined) { instance.numberProp = obj[\\"numberProp\\"]; } - if (obj[\\"objectProp\\"] !== undefined) { - instance.objectProp = NestedTest.unmarshal(obj[\\"objectProp\\"]); + if (obj[\\"nestedObject\\"] !== undefined) { + instance.nestedObject = NestedTest.unmarshal(obj[\\"nestedObject\\"]); } - //Not part of core properties - if (instance.sTestPatternProperties === undefined) {instance.sTestPatternProperties = new Map();} - if (instance.sAnotherTestPatternProperties === undefined) {instance.sAnotherTestPatternProperties = new Map();} - if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"string prop\\",\\"enumProp\\",\\"numberProp\\",\\"objectProp\\"].includes(key);}))) { - //Check all pattern properties - if (key.match(new RegExp('^S(.?)test'))) { - instance.sTestPatternProperties.set(key, value as any); - continue; - }//Check all pattern properties - if (key.match(new RegExp('^S(.?)AnotherTest'))) { - instance.sAnotherTestPatternProperties.set(key, NestedTest.unmarshal(value as any)); - continue; + for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"stringProp\\",\\"enumProp\\",\\"numberProp\\",\\"nestedObject\\",\\"additionalProperties\\"].includes(key);}))) { + instance.additionalProperties.set(key, NestedTest.unmarshal(value as any)); } - instance.additionalProperties.set(key, NestedTest.unmarshal(value as any)); - } + return instance; } }" `; exports[`Marshalling preset should render un/marshal code 2`] = ` +"enum EnumTest { + SOME_SPACE_ENUM_SPACE_STRING = \\"Some enum String\\", + RESERVED_TRUE = \\"true\\", + CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_QUOTATION_TEST_QUOTATION_CURLYRIGHT = '{\\"test\\":\\"test\\"}', + NUMBER_2 = 2, +}" +`; + +exports[`Marshalling preset should render un/marshal code 3`] = ` "class NestedTest { private _stringProp?: string; - private _additionalProperties?: Map | boolean | null>; + private _additionalProperties?: Map; constructor(input: { stringProp?: string, + additionalProperties?: Map, }) { this._stringProp = input.stringProp; + this._additionalProperties = input.additionalProperties; } get stringProp(): string | undefined { return this._stringProp; } set stringProp(stringProp: string | undefined) { this._stringProp = stringProp; } - get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } public marshal() : string { let json = '{' if(this.stringProp !== undefined) { json += \`\\"stringProp\\": \${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},\`; } - if(this.additionalProperties !== undefined) { - for (const [key, value] of this.additionalProperties.entries()) { - //Only render additionalProperties which are not already a property - if(Object.keys(this).includes(String(key))) continue; + for (const [key, value] of this.additionalProperties.entries()) { + //Only unwrap those who are not already a property in the JSON object + if(Object.keys(this).includes(String(key))) continue; json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; } } @@ -164,13 +143,11 @@ exports[`Marshalling preset should render un/marshal code 2`] = ` instance.stringProp = obj[\\"stringProp\\"]; } - //Not part of core properties - if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"stringProp\\"].includes(key);}))) { - - instance.additionalProperties.set(key, value as any); - } + for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"stringProp\\",\\"additionalProperties\\"].includes(key);}))) { + instance.additionalProperties.set(key, value as any); + } + return instance; } }" diff --git a/test/generators/typescript/preset/utils/ExampleFunctions.spec.ts b/test/generators/typescript/preset/utils/ExampleFunctions.spec.ts index 3c74cb759c..da480db654 100644 --- a/test/generators/typescript/preset/utils/ExampleFunctions.spec.ts +++ b/test/generators/typescript/preset/utils/ExampleFunctions.spec.ts @@ -1,82 +1,112 @@ -import { TypeScriptGenerator } from '../../../../../src/generators'; import { renderValueFromModel } from '../../../../../src/generators/typescript/presets/utils/ExampleFunction'; -import { TypeScriptRenderer } from '../../../../../src/generators/typescript/TypeScriptRenderer'; -import { CommonInputModel, CommonModel } from '../../../../../src/models'; -class MockTypeScriptRenderer extends TypeScriptRenderer { +import { + CommonModel, + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedTupleValueModel, + ConstrainedUnionModel +} from '../../../../../src/models'; -} -const renderer = new MockTypeScriptRenderer(TypeScriptGenerator.defaultOptions, new TypeScriptGenerator(), [], new CommonModel(), new CommonInputModel()); describe('Marshalling preset', () => { describe('.renderValueFromModel()', () => { - describe('enums', () => { - test('Should render strings correctly', () => { - const input = CommonModel.toCommonModel({enum: ['somevalue']}); - const output = renderValueFromModel(input, renderer); - expect(output).toEqual('"somevalue"'); - }); - test('Should render numbers correctly', () => { - const input = CommonModel.toCommonModel({enum: [1]}); - const output = renderValueFromModel(input, renderer); - expect(output).toEqual('1'); - }); - test('Should ignore multiple values', () => { - const input = CommonModel.toCommonModel({enum: [1, 'somevalue']}); - const output = renderValueFromModel(input, renderer); - expect(output).toEqual('1'); - }); - test('Should use first value if there is more then one', () => { - const input = CommonModel.toCommonModel({enum: []}); - const output = renderValueFromModel(input, renderer); - expect(output).toBeUndefined(); - }); - }); test('should render refs correctly', () => { - const input = CommonModel.toCommonModel({$ref: 'SomeOtherModel'}); - const output = renderValueFromModel(input, renderer); + const refModel = new ConstrainedAnyModel('test', undefined, ''); + const model = new ConstrainedReferenceModel( + 'SomeOtherModel', + undefined, + '', + refModel + ); + const output = renderValueFromModel(model); expect(output).toEqual('SomeOtherModel.example()'); }); - describe('types', () => { - test('Should render strings correctly', () => { - const input = CommonModel.toCommonModel({type: 'string'}); - const output = renderValueFromModel(input, renderer); - expect(output).toEqual('"string"'); - }); - test('Should render numbers correctly', () => { - const input = CommonModel.toCommonModel({type: 'number'}); - const output = renderValueFromModel(input, renderer); - expect(output).toEqual('0'); - }); - test('Should render booleans correctly', () => { - const input = CommonModel.toCommonModel({type: 'boolean'}); - const output = renderValueFromModel(input, renderer); - expect(output).toEqual('true'); - }); - test('Should use first value if there is more then one', () => { - const input = CommonModel.toCommonModel({type: ['boolean', 'string']}); - const output = renderValueFromModel(input, renderer); - expect(output).toEqual('true'); + test('Should render strings correctly', () => { + const model = new ConstrainedStringModel('test', undefined, ''); + const output = renderValueFromModel(model); + expect(output).toEqual('"string"'); + }); + test('Should render numbers correctly', () => { + const model = new ConstrainedIntegerModel('test', undefined, ''); + const output = renderValueFromModel(model); + expect(output).toEqual('0'); + }); + test('Should render floating numbers correctly', () => { + const model = new ConstrainedFloatModel('test', undefined, ''); + const output = renderValueFromModel(model); + expect(output).toEqual('0.0'); + }); + test('Should render booleans correctly', () => { + const model = new ConstrainedBooleanModel('test', undefined, ''); + const output = renderValueFromModel(model); + expect(output).toEqual('true'); + }); + test('Should use first value if there is more then one', () => { + const unionModel = new ConstrainedBooleanModel('test', undefined, ''); + const model = new ConstrainedUnionModel('test', undefined, '', [ + unionModel + ]); + const output = renderValueFromModel(model); + expect(output).toEqual('true'); + }); + test('Should render array value', () => { + const arrayModel = new ConstrainedBooleanModel('test', undefined, ''); + const model = new ConstrainedArrayModel( + 'test', + undefined, + '', + arrayModel + ); + const output = renderValueFromModel(model); + expect(output).toEqual('[true]'); + }); + test('Should handle unknown types', () => { + const model = new ConstrainedAnyModel('test', undefined, ''); + const output = renderValueFromModel(model); + expect(output).toEqual(undefined); + }); + describe('tuples', () => { + test('should not render anything if no items are defined', () => { + const model = new ConstrainedTupleModel('test', undefined, '', []); + const output = renderValueFromModel(model); + expect(output).toEqual(undefined); }); - describe('array', () => { - test('should not render anything if no items are defined', () => { - const input = CommonModel.toCommonModel({type: 'array'}); - const output = renderValueFromModel(input, renderer); - expect(output).toEqual('[]'); - }); - test('should render multiple array values', () => { - const input = CommonModel.toCommonModel({type: 'array', items: [{type: 'string'}, {type: 'number'}]}); - const output = renderValueFromModel(input, renderer); - expect(output).toEqual('["string", 0]'); - }); - test('should render single array value', () => { - const input = CommonModel.toCommonModel({type: 'array', items: {type: 'string'}}); - const output = renderValueFromModel(input, renderer); - expect(output).toEqual('["string"]'); - }); + test('should render multiple array values', () => { + const stringModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const tupleValueModel1 = new ConstrainedTupleValueModel(0, stringModel); + const integerModel = new ConstrainedIntegerModel('test', undefined, ''); + const tupleValueModel2 = new ConstrainedTupleValueModel( + 0, + integerModel + ); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel1, + tupleValueModel2 + ]); + const output = renderValueFromModel(model); + expect(output).toEqual('["string",0]'); }); - test('Should ignore if none are present', () => { - const input = CommonModel.toCommonModel({type: []}); - const output = renderValueFromModel(input, renderer); - expect(output).toBeUndefined(); + test('should render single array value', () => { + const stringModel = new ConstrainedStringModel( + 'test', + undefined, + 'String' + ); + const tupleValueModel = new ConstrainedTupleValueModel(0, stringModel); + const model = new ConstrainedTupleModel('test', undefined, '', [ + tupleValueModel + ]); + const output = renderValueFromModel(model); + expect(output).toEqual('["string"]'); }); }); }); diff --git a/test/generators/typescript/renderers/EnumRenderer.spec.ts b/test/generators/typescript/renderers/EnumRenderer.spec.ts deleted file mode 100644 index e305031f45..0000000000 --- a/test/generators/typescript/renderers/EnumRenderer.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { TypeScriptGenerator } from '../../../../src/generators'; -import { EnumRenderer } from '../../../../src/generators/typescript/renderers/EnumRenderer'; -import { CommonInputModel, CommonModel } from '../../../../src/models'; - -describe('EnumRenderer', () => { - let renderer: EnumRenderer; - beforeEach(() => { - renderer = new EnumRenderer(TypeScriptGenerator.defaultOptions, new TypeScriptGenerator(), [], new CommonModel(), new CommonInputModel()); - }); - - describe('normalizeKey()', () => { - test('should correctly format " " to correct key', () => { - const key = renderer.normalizeKey('something something'); - expect(key).toEqual('SOMETHING_SOMETHING'); - }); - test('should correctly format "_" to correct key', () => { - const key = renderer.normalizeKey('something_something'); - expect(key).toEqual('SOMETHING_SOMETHING'); - }); - }); -}); diff --git a/test/helpers/CommonModelToMetaModel.spec.ts b/test/helpers/CommonModelToMetaModel.spec.ts new file mode 100644 index 0000000000..0a02fd3c34 --- /dev/null +++ b/test/helpers/CommonModelToMetaModel.spec.ts @@ -0,0 +1,345 @@ +import { + AnyModel, + ArrayModel, + BooleanModel, + CommonModel, + DictionaryModel, + EnumModel, + FloatModel, + IntegerModel, + ObjectModel, + StringModel, + TupleModel, + UnionModel +} from '../../src'; +import { convertToMetaModel } from '../../src/helpers'; +describe('CommonModelToMetaModel', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + test('should default to any model', () => { + const cm = new CommonModel(); + cm.$id = 'test'; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof AnyModel).toEqual(true); + }); + test('should convert to any model', () => { + const cm = new CommonModel(); + cm.type = [ + 'string', + 'number', + 'integer', + 'boolean', + 'object', + 'array', + 'null' + ]; + cm.$id = 'test'; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof AnyModel).toEqual(true); + }); + describe('should convert to enum model', () => { + test('when string enums', () => { + const cm = new CommonModel(); + cm.type = 'string'; + cm.$id = 'test'; + cm.enum = ['test']; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof EnumModel).toEqual(true); + expect((model as EnumModel).values.length).toEqual(1); + expect((model as EnumModel).values[0].key).toEqual('test'); + }); + test('when different types of values', () => { + const cm = new CommonModel(); + cm.type = ['string', 'object', 'number', 'boolean']; + cm.$id = 'test'; + cm.enum = [{ test: 1 }, 123, 'test', true]; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof EnumModel).toEqual(true); + expect((model as EnumModel).values.length).toEqual(4); + expect((model as EnumModel).values[0].value).toEqual({ test: 1 }); + expect((model as EnumModel).values[1].value).toEqual(123); + expect((model as EnumModel).values[2].value).toEqual('test'); + expect((model as EnumModel).values[3].value).toEqual(true); + }); + }); + test('should convert to string model', () => { + const cm = new CommonModel(); + cm.type = 'string'; + cm.$id = 'test'; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof StringModel).toEqual(true); + }); + test('should convert to float model', () => { + const cm = new CommonModel(); + cm.type = 'number'; + cm.$id = 'test'; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof FloatModel).toEqual(true); + }); + test('should convert to integer model', () => { + const cm = new CommonModel(); + cm.type = 'integer'; + cm.$id = 'test'; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof IntegerModel).toEqual(true); + }); + test('should convert to boolean model', () => { + const cm = new CommonModel(); + cm.type = 'boolean'; + cm.$id = 'test'; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof BooleanModel).toEqual(true); + }); + test('should convert to model', () => { + const cm = new CommonModel(); + cm.type = 'object'; + cm.$id = 'test'; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof ObjectModel).toEqual(true); + }); + test('should convert to array model', () => { + const cm = new CommonModel(); + cm.type = 'array'; + cm.$id = 'test'; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof ArrayModel).toEqual(true); + }); + test('should convert to object model', () => { + const spm = new CommonModel(); + spm.type = 'string'; + const cm = new CommonModel(); + cm.type = 'object'; + cm.$id = 'test'; + cm.properties = { + test: spm + }; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof ObjectModel).toEqual(true); + }); + test('should convert to object model with additional properties', () => { + const spm = new CommonModel(); + spm.type = 'string'; + const cm = new CommonModel(); + cm.type = 'object'; + cm.$id = 'test'; + cm.properties = { + test: spm + }; + cm.additionalProperties = spm; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof ObjectModel).toEqual(true); + expect( + (model as ObjectModel).properties['additionalProperties'] + ).not.toBeUndefined(); + }); + test('should convert to object model with additional properties and already existing property with that name', () => { + const spm = new CommonModel(); + spm.type = 'string'; + const cm = new CommonModel(); + cm.type = 'object'; + cm.$id = 'test'; + cm.properties = { + additionalProperties: spm + }; + cm.additionalProperties = spm; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof ObjectModel).toEqual(true); + expect( + (model as ObjectModel).properties['additionalProperties'] + ).not.toBeUndefined(); + expect( + (model as ObjectModel).properties['reserved_additionalProperties'] + ).not.toBeUndefined(); + }); + + test('should merge both patternProperties and additionalProperties into one property', () => { + const stringCM = new CommonModel(); + stringCM.type = 'string'; + stringCM.$id = 'stringModel'; + const booleanCM = new CommonModel(); + booleanCM.type = 'boolean'; + booleanCM.$id = 'booleanModel'; + const cm = new CommonModel(); + cm.type = 'object'; + cm.$id = 'test'; + cm.properties = { + some_prop: stringCM + }; + cm.patternProperties = { + some_random_pattern: stringCM + }; + cm.additionalProperties = booleanCM; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof ObjectModel).toEqual(true); + expect( + (model as ObjectModel).properties['additionalProperties'] + ).not.toBeUndefined(); + const additionalPropModel = (model as ObjectModel).properties[ + 'additionalProperties' + ].property; + expect(additionalPropModel instanceof DictionaryModel).toEqual(true); + expect( + (additionalPropModel as DictionaryModel).value instanceof UnionModel + ).toEqual(true); + const unionModel = (additionalPropModel as DictionaryModel) + .value as UnionModel; + expect(unionModel.union.length).toEqual(2); + expect(unionModel.union[0] instanceof BooleanModel).toEqual(true); + expect(unionModel.union[1] instanceof StringModel).toEqual(true); + }); + + test('should merge both patternProperties and additionalProperties into one for a dictionary', () => { + const stringCM = new CommonModel(); + stringCM.type = 'string'; + stringCM.$id = 'stringModel'; + const booleanCM = new CommonModel(); + booleanCM.type = 'boolean'; + booleanCM.$id = 'booleanModel'; + const cm = new CommonModel(); + cm.type = 'object'; + cm.$id = 'test'; + cm.patternProperties = { + some_random_pattern: stringCM + }; + cm.additionalProperties = booleanCM; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof DictionaryModel).toEqual(true); + const dictionaryValueModel = (model as DictionaryModel).value; + expect(dictionaryValueModel instanceof UnionModel).toEqual(true); + expect((dictionaryValueModel as UnionModel).union.length).toEqual(2); + expect( + (dictionaryValueModel as UnionModel).union[0] instanceof BooleanModel + ).toEqual(true); + expect( + (dictionaryValueModel as UnionModel).union[1] instanceof StringModel + ).toEqual(true); + }); + + test('should convert normal array to array model', () => { + const spm = new CommonModel(); + spm.type = 'string'; + const cm = new CommonModel(); + cm.type = 'array'; + cm.$id = 'test'; + cm.items = spm; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof ArrayModel).toEqual(true); + }); + test('should convert array with additional items to array model as union type', () => { + const spm = new CommonModel(); + spm.type = 'string'; + const cm = new CommonModel(); + cm.type = 'array'; + cm.$id = 'test'; + cm.items = spm; + cm.additionalItems = spm; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof ArrayModel).toEqual(true); + expect((model as ArrayModel).valueModel instanceof UnionModel).toEqual( + true + ); + }); + test('should convert array of types to union model', () => { + const cm = new CommonModel(); + cm.type = ['string', 'number']; + cm.$id = 'test'; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof UnionModel).toEqual(true); + expect((model as UnionModel).union.length).toEqual(2); + }); + test('should convert array of models to union model', () => { + const cm = new CommonModel(); + cm.$id = 'Pet'; + const cat = new CommonModel(); + cat.$id = 'Cat'; + const dog = new CommonModel(); + dog.$id = 'Dog'; + cm.union = [cat, dog]; + const model = convertToMetaModel(cm); + expect(model).not.toBeUndefined(); + expect(model instanceof UnionModel).toEqual(true); + expect((model as UnionModel).union.length).toEqual(2); + }); + test('should convert tuple to tuple model', () => { + const scm = new CommonModel(); + scm.type = 'string'; + const cm = new CommonModel(); + cm.type = 'array'; + cm.$id = 'test'; + cm.items = [scm, scm]; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof TupleModel).toEqual(true); + expect((model as TupleModel).tuple.length).toEqual(2); + }); + test('should handle recursive models', () => { + const cm = new CommonModel(); + cm.type = 'object'; + cm.$id = 'test'; + cm.properties = { + test: cm + }; + + const model = convertToMetaModel(cm); + + expect(model).not.toBeUndefined(); + expect(model instanceof ObjectModel).toEqual(true); + }); +}); diff --git a/test/helpers/ConstrainHelpers.spec.ts b/test/helpers/ConstrainHelpers.spec.ts new file mode 100644 index 0000000000..28a0cd9c59 --- /dev/null +++ b/test/helpers/ConstrainHelpers.spec.ts @@ -0,0 +1,431 @@ +import { constrainMetaModel } from '../../src/helpers'; +import { + AnyModel, + ArrayModel, + BooleanModel, + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedEnumValueModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedUnionModel, + DictionaryModel, + EnumModel, + EnumValueModel, + FloatModel, + IntegerModel, + ObjectModel, + ObjectPropertyModel, + ReferenceModel, + StringModel, + TupleModel, + TupleValueModel, + UnionModel +} from '../../src/models'; +import { + mockedConstraints, + mockedTypeMapping +} from '../TestUtils/TestConstrainer'; + +describe('ConstrainHelpers', () => { + const placeHolderModel = new AnyModel('', undefined); + afterEach(() => { + jest.clearAllMocks(); + }); + describe('constrain ObjectModel', () => { + test('should constrain correctly', () => { + const testProperty = new StringModel('', undefined); + const metaModel = new ObjectModel('test', undefined, { + testProperty: new ObjectPropertyModel( + 'testProperty', + false, + testProperty + ) + }); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedObjectModel).toEqual(true); + expect( + (constrainedModel as ConstrainedObjectModel).properties['testProperty'] + .property instanceof ConstrainedStringModel + ).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(2); + expect(mockedConstraints.propertyKey).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.Object).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.String).toHaveBeenCalledTimes(1); + }); + + test('should have access to partOfProperty', () => { + const testProperty = new StringModel('', undefined); + const metaModel = new ObjectModel('test', undefined, { + testProperty: new ObjectPropertyModel( + 'testProperty', + false, + testProperty + ) + }); + constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel, + options: {}, + constrainedName: '' + }); + expect(mockedTypeMapping.String).toBeCalledWith( + expect.objectContaining({ partOfProperty: expect.objectContaining({}) }) + ); + }); + test('should handle recursive models', () => { + const model = new ObjectModel('testObj', undefined, {}); + const objectPropertyModel = new ObjectPropertyModel( + 'recursiveProp', + false, + model + ); + model.properties['recursive'] = objectPropertyModel; + + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel: model, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedObjectModel).toEqual(true); + }); + }); + describe('constrain ReferenceModel', () => { + test('should constrain correctly', () => { + const stringModel = new StringModel('', undefined); + const metaModel = new ReferenceModel('', undefined, stringModel); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedReferenceModel).toEqual( + true + ); + expect( + (constrainedModel as ConstrainedReferenceModel).ref instanceof + ConstrainedStringModel + ).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(2); + expect(mockedTypeMapping.String).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.Reference).toHaveBeenCalledTimes(1); + }); + + test('should handle recursive models', () => { + const metaModel = new ReferenceModel('', undefined, placeHolderModel); + metaModel.ref = metaModel; + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedReferenceModel).toEqual( + true + ); + }); + }); + describe('constrain AnyModel', () => { + test('should constrain correctly', () => { + const metaModel = new AnyModel('', undefined); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedAnyModel).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.Any).toHaveBeenCalledTimes(1); + }); + }); + describe('constrain FloatModel', () => { + test('should constrain correctly', () => { + const metaModel = new FloatModel('', undefined); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedFloatModel).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.Float).toHaveBeenCalledTimes(1); + }); + }); + describe('constrain IntegerModel', () => { + test('should constrain correctly', () => { + const metaModel = new IntegerModel('', undefined); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedIntegerModel).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.Integer).toHaveBeenCalledTimes(1); + }); + }); + describe('constrain StringModel', () => { + test('should constrain correctly', () => { + const metaModel = new StringModel('', undefined); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedStringModel).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.String).toHaveBeenCalledTimes(1); + }); + }); + describe('constrain BooleanModel', () => { + test('should constrain correctly', () => { + const metaModel = new BooleanModel('', undefined); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedBooleanModel).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.Boolean).toHaveBeenCalledTimes(1); + }); + }); + + describe('constrain TupleModel', () => { + test('should constrain correctly', () => { + const stringModel = new StringModel('', undefined); + const metaModel = new TupleModel('test', undefined, [ + new TupleValueModel(0, stringModel) + ]); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedTupleModel).toEqual(true); + expect( + (constrainedModel as ConstrainedTupleModel).tuple[0].value instanceof + ConstrainedStringModel + ).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(2); + expect(mockedTypeMapping.Tuple).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.String).toHaveBeenCalledTimes(1); + }); + test('should handle recursive models', () => { + const metaModel = new TupleModel('test', undefined, []); + metaModel.tuple.push(new TupleValueModel(0, metaModel)); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedTupleModel).toEqual(true); + }); + }); + describe('constrain ArrayModel', () => { + test('should constrain correctly', () => { + const stringModel = new StringModel('', undefined); + const metaModel = new ArrayModel('test', undefined, stringModel); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedArrayModel).toEqual(true); + expect( + (constrainedModel as ConstrainedArrayModel).valueModel instanceof + ConstrainedStringModel + ).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(2); + expect(mockedTypeMapping.Array).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.String).toHaveBeenCalledTimes(1); + }); + test('should handle recursive models', () => { + const metaModel = new ArrayModel('test', undefined, placeHolderModel); + metaModel.valueModel = metaModel; + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedArrayModel).toEqual(true); + }); + }); + describe('constrain UnionModel', () => { + test('should constrain correctly', () => { + const stringModel = new StringModel('', undefined); + const metaModel = new UnionModel('test', undefined, [stringModel]); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedUnionModel).toEqual(true); + expect( + (constrainedModel as ConstrainedUnionModel).union[0] instanceof + ConstrainedStringModel + ).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(2); + expect(mockedTypeMapping.Union).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.String).toHaveBeenCalledTimes(1); + }); + test('should handle recursive models', () => { + const metaModel = new UnionModel('test', undefined, []); + metaModel.union.push(metaModel); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedUnionModel).toEqual(true); + }); + }); + describe('constrain EnumModel', () => { + test('should constrain correctly', () => { + const metaModel = new EnumModel('test', undefined, [ + new EnumValueModel('test', 123) + ]); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedEnumModel).toEqual(true); + const enumModel = constrainedModel as ConstrainedEnumModel; + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.Enum).toHaveBeenCalledTimes(1); + expect(enumModel.values[0].key).toEqual('test'); + expect(enumModel.values[0].value).toEqual(123); + }); + }); + describe('constrain DictionaryModel', () => { + test('should constrain correctly', () => { + const stringModel = new StringModel('', undefined); + const stringModel2 = new StringModel('', undefined); + const metaModel = new DictionaryModel( + 'test', + undefined, + stringModel, + stringModel2 + ); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedDictionaryModel).toEqual( + true + ); + expect( + (constrainedModel as ConstrainedDictionaryModel).key instanceof + ConstrainedStringModel + ).toEqual(true); + expect( + (constrainedModel as ConstrainedDictionaryModel).value instanceof + ConstrainedStringModel + ).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(3); + expect(mockedTypeMapping.Dictionary).toHaveBeenCalledTimes(1); + expect(mockedTypeMapping.String).toHaveBeenCalledTimes(2); + }); + test('should handle recursive models', () => { + const metaModel = new DictionaryModel( + 'test', + undefined, + placeHolderModel, + placeHolderModel + ); + metaModel.key = metaModel; + metaModel.value = metaModel; + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '' + } + ); + expect(constrainedModel instanceof ConstrainedDictionaryModel).toEqual( + true + ); + }); + }); +}); diff --git a/test/helpers/Constraints.spec.ts b/test/helpers/Constraints.spec.ts new file mode 100644 index 0000000000..4477a2bd11 --- /dev/null +++ b/test/helpers/Constraints.spec.ts @@ -0,0 +1,225 @@ +import { + NO_NUMBER_START_CHAR, + NO_EMPTY_VALUE, + NO_DUPLICATE_PROPERTIES, + FormatHelpers, + NO_DUPLICATE_ENUM_KEYS +} from '../../src/helpers'; +import { + AnyModel, + ConstrainedAnyModel, + ConstrainedEnumModel, + ConstrainedEnumValueModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + EnumModel, + EnumValueModel, + ObjectModel, + ObjectPropertyModel +} from '../../src/models'; + +describe('Constraints', () => { + describe('NO_NUMBER_START_CHAR', () => { + test('should not prepend anything to empty values', () => { + const renderedValue = NO_NUMBER_START_CHAR(''); + expect(renderedValue).toEqual(''); + }); + test('should prepend something to numbers', () => { + const renderedValue = NO_NUMBER_START_CHAR('1'); + expect(renderedValue).toEqual('number_1'); + }); + }); + + describe('NO_EMPTY_VALUE', () => { + test('should not allow empty values', () => { + const renderedValue = NO_EMPTY_VALUE(''); + expect(renderedValue).toEqual('empty'); + }); + test('should not do anything to nonempty values', () => { + const renderedValue = NO_EMPTY_VALUE('1'); + expect(renderedValue).toEqual('1'); + }); + }); + describe('NO_DUPLICATE_PROPERTIES', () => { + test('should not do anything with no duplicate properties', () => { + const constrainedModel = new ConstrainedObjectModel( + '', + undefined, + '', + {} + ); + const metaModel = new ObjectModel('', undefined, { + Test: new ObjectPropertyModel( + 'Test', + false, + new AnyModel('', undefined) + ) + }); + const renderedValue = NO_DUPLICATE_PROPERTIES( + constrainedModel, + metaModel, + 'Test', + FormatHelpers.toPascalCase + ); + expect(renderedValue).toEqual('Test'); + }); + test('should not use formatted property name which another raw property is', () => { + const constrainedModel = new ConstrainedObjectModel( + '', + undefined, + '', + {} + ); + const metaModel = new ObjectModel('', undefined, { + test: new ObjectPropertyModel( + 'test', + false, + new AnyModel('', undefined) + ), + Test: new ObjectPropertyModel( + 'Test', + false, + new AnyModel('', undefined) + ) + }); + const renderedValue = NO_DUPLICATE_PROPERTIES( + constrainedModel, + metaModel, + 'test', + FormatHelpers.toPascalCase + ); + expect(renderedValue).toEqual('reserved_test'); + }); + test('should should keep the raw property name if no clash', () => { + const constrainedModel = new ConstrainedObjectModel( + '', + undefined, + '', + {} + ); + const metaModel = new ObjectModel('', undefined, { + test: new ObjectPropertyModel( + 'test', + false, + new AnyModel('', undefined) + ), + Test: new ObjectPropertyModel( + 'Test', + false, + new AnyModel('', undefined) + ) + }); + const renderedValue = NO_DUPLICATE_PROPERTIES( + constrainedModel, + metaModel, + 'Test', + FormatHelpers.toPascalCase + ); + expect(renderedValue).toEqual('Test'); + }); + test('should be able to handle multiple reserved properties', () => { + const constrainedModel = new ConstrainedObjectModel('', undefined, '', { + Test: new ConstrainedObjectPropertyModel( + 'Test', + 'Test', + false, + new ConstrainedAnyModel('', undefined, '') + ), + ReservedTest: new ConstrainedObjectPropertyModel( + 'ReservedTest', + 'ReservedTest', + false, + new ConstrainedAnyModel('', undefined, '') + ) + }); + const metaModel = new ObjectModel('', undefined, { + test: new ObjectPropertyModel( + 'test', + false, + new AnyModel('', undefined) + ), + Test: new ObjectPropertyModel( + 'Test', + false, + new AnyModel('', undefined) + ), + ReservedTest: new ObjectPropertyModel( + 'ReservedTest', + false, + new AnyModel('', undefined) + ) + }); + const renderedValue = NO_DUPLICATE_PROPERTIES( + constrainedModel, + metaModel, + 'test', + FormatHelpers.toPascalCase + ); + expect(renderedValue).toEqual('reserved_reserved_test'); + }); + }); + describe('NO_DUPLICATE_ENUM_KEYS', () => { + test('should not do anything with no duplicate enum keys', () => { + const constrainedModel = new ConstrainedEnumModel('', undefined, '', []); + const metaModel = new EnumModel('', undefined, [ + new EnumValueModel('test', new AnyModel('', undefined)) + ]); + const renderedValue = NO_DUPLICATE_ENUM_KEYS( + constrainedModel, + metaModel, + 'test', + FormatHelpers.toPascalCase + ); + expect(renderedValue).toEqual('test'); + }); + test('should not use formatted enum key which another raw enum key is', () => { + const constrainedModel = new ConstrainedEnumModel('', undefined, '', []); + const metaModel = new EnumModel('', undefined, [ + new EnumValueModel('test', new AnyModel('', undefined)), + new EnumValueModel('Test', new AnyModel('', undefined)) + ]); + const renderedValue = NO_DUPLICATE_ENUM_KEYS( + constrainedModel, + metaModel, + 'test', + FormatHelpers.toPascalCase + ); + expect(renderedValue).toEqual('reserved_test'); + }); + test('should should keep the raw enum key if no clash', () => { + const constrainedModel = new ConstrainedEnumModel('', undefined, '', []); + const metaModel = new EnumModel('', undefined, [ + new EnumValueModel('test', new AnyModel('', undefined)), + new EnumValueModel('Test', new AnyModel('', undefined)) + ]); + const renderedValue = NO_DUPLICATE_ENUM_KEYS( + constrainedModel, + metaModel, + 'Test', + FormatHelpers.toPascalCase + ); + expect(renderedValue).toEqual('Test'); + }); + test('should be able to handle multiple reserved enum keys', () => { + const constrainedModel = new ConstrainedEnumModel('', undefined, '', [ + new ConstrainedEnumValueModel('Test', new AnyModel('', undefined)), + new ConstrainedEnumValueModel( + 'ReservedTest', + new AnyModel('', undefined) + ) + ]); + const metaModel = new EnumModel('', undefined, [ + new EnumValueModel('test', new AnyModel('', undefined)), + new EnumValueModel('Test', new AnyModel('', undefined)), + new EnumValueModel('ReservedTest', new AnyModel('', undefined)) + ]); + const renderedValue = NO_DUPLICATE_ENUM_KEYS( + constrainedModel, + metaModel, + 'test', + FormatHelpers.toPascalCase + ); + expect(renderedValue).toEqual('reserved_reserved_test'); + }); + }); +}); diff --git a/test/helpers/DependencyHelpers.spec.ts b/test/helpers/DependencyHelpers.spec.ts new file mode 100644 index 0000000000..0d4d154a9f --- /dev/null +++ b/test/helpers/DependencyHelpers.spec.ts @@ -0,0 +1,96 @@ +import { + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedAnyModel +} from '../../src'; +import { renderJavaScriptDependency, makeUnique } from '../../src/helpers'; + +describe('DependencyHelper', () => { + describe('renderJavaScriptDependency', () => { + test('should render accurate CJS dependency', () => { + const renderedDependency = renderJavaScriptDependency( + 'test', + 'test2', + 'CJS' + ); + expect(renderedDependency).toEqual(`const test = require('test2');`); + }); + test('should render accurate ESM dependency', () => { + const renderedDependency = renderJavaScriptDependency( + 'test', + 'test2', + 'ESM' + ); + expect(renderedDependency).toEqual(`import test from 'test2';`); + }); + }); + describe('makeUnique', () => { + test('should remove duplicate regular instances', () => { + const stringModel = new ConstrainedStringModel('', undefined, ''); + const nonUniqueArray = [stringModel, stringModel]; + const uniqueArray = makeUnique(nonUniqueArray); + expect(uniqueArray).toHaveLength(1); + }); + test('should remove duplicate reference instances', () => { + const stringModel = new ConstrainedStringModel('', undefined, ''); + const ref1 = new ConstrainedReferenceModel( + '', + undefined, + '', + stringModel + ); + const nonUniqueArray = [ref1, ref1]; + const uniqueArray = makeUnique(nonUniqueArray); + expect(uniqueArray).toHaveLength(1); + }); + test('should remove duplicate reference instances if the same name but different instance', () => { + const stringModel = new ConstrainedStringModel('', undefined, ''); + const ref1 = new ConstrainedReferenceModel( + 'name', + undefined, + '', + stringModel + ); + const ref2 = new ConstrainedReferenceModel( + 'name', + undefined, + '', + stringModel + ); + const nonUniqueArray = [ref1, ref2]; + const uniqueArray = makeUnique(nonUniqueArray); + expect(uniqueArray).toHaveLength(1); + }); + test('should remove duplicate reference value instances', () => { + const stringModel = new ConstrainedStringModel('', undefined, ''); + const ref1 = new ConstrainedReferenceModel( + '', + undefined, + '', + stringModel + ); + const ref2 = new ConstrainedReferenceModel( + '', + undefined, + '', + stringModel + ); + const nonUniqueArray = [ref1, ref2]; + const uniqueArray = makeUnique(nonUniqueArray); + expect(uniqueArray).toHaveLength(1); + }); + test('should remove duplicate name and type models', () => { + const stringModel = new ConstrainedStringModel('', undefined, ''); + const ref = new ConstrainedReferenceModel( + 'name', + undefined, + 'type', + stringModel + ); + const any = new ConstrainedAnyModel('name', undefined, 'type'); + const nonUniqueArray = [ref, any]; + const uniqueArray = makeUnique(nonUniqueArray); + expect(uniqueArray).toHaveLength(1); + }); + }); +}); diff --git a/test/helpers/FileHelpers.spec.ts b/test/helpers/FileHelpers.spec.ts index 07a0aa710a..f210bfbc10 100644 --- a/test/helpers/FileHelpers.spec.ts +++ b/test/helpers/FileHelpers.spec.ts @@ -1,4 +1,4 @@ -import {promises as fs} from 'fs'; +import { promises as fs } from 'fs'; import { FileHelpers } from '../../src'; import * as path from 'path'; describe('FileHelper', () => { @@ -6,7 +6,7 @@ describe('FileHelper', () => { jest.restoreAllMocks(); }); describe('writerToFileSystem', () => { - test('should write file content to correct location', async () => { + test('should write file content to correct location', async () => { jest.spyOn(fs, 'mkdir').mockResolvedValue(undefined); jest.spyOn(fs, 'writeFile').mockResolvedValue(undefined); @@ -15,11 +15,17 @@ describe('FileHelper', () => { const expectedOutputDirPath = path.resolve(outputDir); const expectedOutputFilePath = path.resolve(outputFile); await FileHelpers.writerToFileSystem('content', outputFile); - - expect(fs.mkdir).toHaveBeenNthCalledWith(1, expectedOutputDirPath, { recursive: true }); - expect(fs.writeFile).toHaveBeenNthCalledWith(1, expectedOutputFilePath, 'content'); + + expect(fs.mkdir).toHaveBeenNthCalledWith(1, expectedOutputDirPath, { + recursive: true + }); + expect(fs.writeFile).toHaveBeenNthCalledWith( + 1, + expectedOutputFilePath, + 'content' + ); }); - test('should handle rejected promises', async () => { + test('should handle rejected promises', async () => { const error = new Error('Test error'); jest.spyOn(fs, 'mkdir').mockRejectedValue(error); jest.spyOn(fs, 'writeFile').mockResolvedValue(undefined); @@ -28,8 +34,12 @@ describe('FileHelper', () => { const outputFile = `${outputDir}/output.ts`; const expectedOutputPath = path.resolve(outputDir); - await expect(FileHelpers.writerToFileSystem('content', outputFile)).rejects.toEqual(error); - expect(fs.mkdir).toHaveBeenNthCalledWith(1, expectedOutputPath, { recursive: true }); + await expect( + FileHelpers.writerToFileSystem('content', outputFile) + ).rejects.toEqual(error); + expect(fs.mkdir).toHaveBeenNthCalledWith(1, expectedOutputPath, { + recursive: true + }); expect(fs.writeFile).not.toHaveBeenCalled(); }); }); diff --git a/test/helpers/FormatHelpers.spec.ts b/test/helpers/FormatHelpers.spec.ts index 7243e4aa4b..a5eb5cf627 100644 --- a/test/helpers/FormatHelpers.spec.ts +++ b/test/helpers/FormatHelpers.spec.ts @@ -1,6 +1,12 @@ import { FormatHelpers, IndentationTypes } from '../../src/helpers'; describe('FormatHelpers', () => { + describe('lowerFirst', () => { + test('should convert first char to lowercase', () => { + const returnedText = FormatHelpers.lowerFirst('Test'); + expect(returnedText).toEqual('test'); + }); + }); describe('breakLines', () => { test('should break single text', () => { const breakedTexts = FormatHelpers.breakLines('text1\ntext2\ntext3'); @@ -8,7 +14,11 @@ describe('FormatHelpers', () => { expect(breakedTexts).toStrictEqual(['text1', 'text2', 'text3']); }); test('should support multiple lines', () => { - const breakedTexts = FormatHelpers.breakLines(['text1', 'text2', 'text3']); + const breakedTexts = FormatHelpers.breakLines([ + 'text1', + 'text2', + 'text3' + ]); expect(breakedTexts).toHaveLength(3); expect(breakedTexts).toStrictEqual(['text1', 'text2', 'text3']); }); @@ -53,32 +63,50 @@ describe('FormatHelpers', () => { }); test('should surround replaced parts with separators from each other', () => { - const content = FormatHelpers.replaceSpecialCharacters('&\'()*+', { separator: '_' }); - expect(content).toEqual('ampersand_apostrophe_roundleft_roundright_asterisk_plus'); + const content = FormatHelpers.replaceSpecialCharacters(`&'()*+`, { + separator: '_' + }); + expect(content).toEqual( + 'ampersand_apostrophe_roundleft_roundright_asterisk_plus' + ); }); test('should surround replaced parts with separators from text', () => { - const content = FormatHelpers.replaceSpecialCharacters(',-.test/:;', { separator: '_' }); + const content = FormatHelpers.replaceSpecialCharacters(',-.test/:;', { + separator: '_' + }); expect(content).toEqual('comma_minus_dot_test_slash_colon_semicolon'); }); test('should surround replaced parts with separators from text at the beginning', () => { - const content = FormatHelpers.replaceSpecialCharacters('test<=>?@[', { separator: '_' }); + const content = FormatHelpers.replaceSpecialCharacters('test<=>?@[', { + separator: '_' + }); expect(content).toEqual('test_less_equal_greater_question_at_squareleft'); }); test('should surround replaced parts with separators from text at the end', () => { - const content = FormatHelpers.replaceSpecialCharacters('\\]^_`test', { separator: '_' }); - expect(content).toEqual('backslash_squareright_circumflex_underscore_graveaccent_test'); + const content = FormatHelpers.replaceSpecialCharacters('\\]^_`test', { + separator: '_' + }); + expect(content).toEqual( + 'backslash_squareright_circumflex_underscore_graveaccent_test' + ); }); test('should exclude one special characters if defined', () => { - const content = FormatHelpers.replaceSpecialCharacters('{|}~_', { separator: ' ', exclude: ['_'] }); + const content = FormatHelpers.replaceSpecialCharacters('{|}~_', { + separator: ' ', + exclude: ['_'] + }); expect(content).toEqual('curlyleft vertical curlyright tilde _'); }); test('should exclude many special characters if defined', () => { - const content = FormatHelpers.replaceSpecialCharacters('{?_', { separator: ' ', exclude: ['_', '?', '{'] }); + const content = FormatHelpers.replaceSpecialCharacters('{?_', { + separator: ' ', + exclude: ['_', '?', '{'] + }); expect(content).toEqual('{?_'); }); }); diff --git a/test/helpers/NameHelpers.spec.ts b/test/helpers/NameHelpers.spec.ts deleted file mode 100644 index 4c22406044..0000000000 --- a/test/helpers/NameHelpers.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { CommonNamingConventionImplementation, DefaultPropertyNames, getUniquePropertyName } from '../../src/helpers'; -import { CommonInputModel, CommonModel } from '../../src/models'; - -describe('NameHelpers', () => { - describe('getUniquePropertyName', () => { - test('should return correct name for additionalProperties', () => { - const model = CommonModel.toCommonModel({}); - - const additionalPropertiesName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties); - - expect(additionalPropertiesName).toEqual('additionalProperties'); - }); - test('should handle duplicate names', () => { - const model = CommonModel.toCommonModel({properties: {additionalProperties: {}}}); - - const additionalPropertiesName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties); - - expect(additionalPropertiesName).toEqual('reserved_additionalProperties'); - }); - }); - - describe('CommonNamingConventionImplementation', () => { - const isReservedKeyword = jest.fn().mockReturnValue(false); - const defaultCtx = {model: CommonModel.toCommonModel({}), inputModel: new CommonInputModel(), reservedKeywordCallback: isReservedKeyword}; - describe('type', () => { - test('should handle undefined', () => { - const name = undefined; - const formattedName = CommonNamingConventionImplementation.type!(name, defaultCtx); - expect(formattedName).toEqual(''); - }); - test('Should default name to pascal case', () => { - const name = 'some_not Pascal string'; - const formattedName = CommonNamingConventionImplementation!.type!(name, defaultCtx); - expect(formattedName).toEqual('SomeNotPascalString'); - }); - }); - describe('property', () => { - test('should handle undefined', () => { - const name = undefined; - const formattedName = CommonNamingConventionImplementation!.property!(name, defaultCtx); - expect(formattedName).toEqual(''); - }); - test('Should default name to camel case', () => { - const name = 'some_not Pascal string'; - const formattedName = CommonNamingConventionImplementation!.property!(name, defaultCtx); - expect(formattedName).toEqual('someNotPascalString'); - }); - test('Should return accurate reserved property name', () => { - const name = '$ref'; - isReservedKeyword.mockReturnValueOnce(true); - const formattedName = CommonNamingConventionImplementation!.property!(name, defaultCtx); - expect(formattedName).toEqual('reservedRef'); - }); - }); - }); -}); diff --git a/test/helpers/Splitter.spec.ts b/test/helpers/Splitter.spec.ts new file mode 100644 index 0000000000..d6658a61f9 --- /dev/null +++ b/test/helpers/Splitter.spec.ts @@ -0,0 +1,141 @@ +import { split, SplitOptions } from '../../src/helpers'; +import { + ObjectModel, + ReferenceModel, + StringModel, + ObjectPropertyModel +} from '../../src/models'; +describe('Splitter', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + test('should split models when asked for it', () => { + const stringModel = new StringModel('testString', undefined); + const propertyModel = new ObjectPropertyModel('test', false, stringModel); + const model = new ObjectModel('testObj', undefined, { + test: propertyModel + }); + const options: SplitOptions = { + splitString: true + }; + const splittedModels = split(model, options); + + const expectedObjectModel = model; + expectedObjectModel.properties['test'].property = new ReferenceModel( + stringModel.name, + stringModel.originalInput, + stringModel + ); + + expect(splittedModels.length).toEqual(2); + expect(splittedModels[0] instanceof ObjectModel).toEqual(true); + expect(splittedModels[0]).toEqual(expectedObjectModel); + expect(splittedModels[1] instanceof StringModel).toEqual(true); + }); + test('should not split models when not asked for', () => { + const stringModel = new StringModel('testString', undefined); + const propertyModel = new ObjectPropertyModel('test', false, stringModel); + const model = new ObjectModel('testObj', undefined, { + test: propertyModel + }); + const options: SplitOptions = { + splitString: false + }; + const splittedModels = split(model, options); + expect(splittedModels.length).toEqual(1); + + expect(splittedModels[0]).toEqual(model); + }); + test('should not split models when asked for something else', () => { + const stringModel = new StringModel('testString', undefined); + const propertyModel = new ObjectPropertyModel('test', false, stringModel); + const model = new ObjectModel('testObj', undefined, { + test: propertyModel + }); + const options: SplitOptions = { + splitBoolean: true + }; + const splittedModels = split(model, options); + expect(splittedModels.length).toEqual(1); + expect(splittedModels[0]).toEqual(model); + }); + test('should split out nested models', () => { + const nestedStringModel = new StringModel('nestedString', undefined); + const nestedPropertyModel = new ObjectPropertyModel( + 'nestedStringProp', + false, + nestedStringModel + ); + const nestedObjectModel = new ObjectModel('nestedTestObj', undefined, { + test: nestedPropertyModel + }); + const objectPropertyModel = new ObjectPropertyModel( + 'stringProp', + false, + nestedObjectModel + ); + const stringModel = new StringModel('string', undefined); + const propertyModel = new ObjectPropertyModel('test', false, stringModel); + const model = new ObjectModel('testObj', undefined, { + test: propertyModel, + nested: objectPropertyModel + }); + const options: SplitOptions = { + splitString: true, + splitObject: true + }; + const splittedModels = split(model, options); + expect(splittedModels.length).toEqual(4); + expect(splittedModels[0]).toEqual(model); + expect(splittedModels[1]).toEqual(stringModel); + expect(splittedModels[2]).toEqual(nestedObjectModel); + expect(splittedModels[3]).toEqual(nestedStringModel); + }); + test('should handle already seen models', () => { + const stringModel = new StringModel('string', undefined); + const nestedPropertyModel = new ObjectPropertyModel( + 'nestedStringProp', + false, + stringModel + ); + const nestedObjectModel = new ObjectModel('nestedTestObj', undefined, { + test: nestedPropertyModel + }); + const objectPropertyModel = new ObjectPropertyModel( + 'stringProp', + false, + nestedObjectModel + ); + const propertyModel = new ObjectPropertyModel('test', false, stringModel); + const model = new ObjectModel('testObj', undefined, { + test: propertyModel, + nested: objectPropertyModel + }); + const options: SplitOptions = { + splitString: true, + splitObject: true + }; + const splittedModels = split(model, options); + expect(splittedModels.length).toEqual(3); + expect(splittedModels[0]).toEqual(model); + expect(splittedModels[1]).toEqual(stringModel); + expect(splittedModels[2]).toEqual(nestedObjectModel); + }); + test('should handle recursive models', () => { + const model = new ObjectModel('testObj', undefined, {}); + const objectPropertyModel = new ObjectPropertyModel( + 'recursiveProp', + false, + model + ); + model.properties['recursive'] = objectPropertyModel; + + const options: SplitOptions = { + splitString: true, + splitObject: true + }; + const splittedModels = split(model, options); + expect(splittedModels.length).toEqual(1); + expect(splittedModels[0]).toEqual(model); + }); +}); diff --git a/test/helpers/TypeHelpers.spec.ts b/test/helpers/TypeHelpers.spec.ts index 7283d2f554..13df93ac86 100644 --- a/test/helpers/TypeHelpers.spec.ts +++ b/test/helpers/TypeHelpers.spec.ts @@ -1,58 +1,94 @@ -import { TypeHelpers, ModelKind } from '../../src/helpers'; -import { CommonModel } from '../../src/models'; +import { getTypeFromMapping, TypeMapping } from '../../src/helpers'; +import { + CommonModel, + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedUnionModel +} from '../../src/models'; describe('TypeHelpers', () => { - describe('extractKind', () => { - test('should return object', () => { - const model = new CommonModel(); - model.type = 'object'; - const kind = TypeHelpers.extractKind(model); - - expect(kind).toEqual(ModelKind.OBJECT); + describe('getTypeFromMapping', () => { + const typeMapping: TypeMapping = { + Object: jest.fn().mockReturnValue('test'), + Reference: jest.fn().mockReturnValue('test'), + Any: jest.fn().mockReturnValue('test'), + Float: jest.fn().mockReturnValue('test'), + Integer: jest.fn().mockReturnValue('test'), + String: jest.fn().mockReturnValue('test'), + Boolean: jest.fn().mockReturnValue('test'), + Tuple: jest.fn().mockReturnValue('test'), + Array: jest.fn().mockReturnValue('test'), + Enum: jest.fn().mockReturnValue('test'), + Union: jest.fn().mockReturnValue('test'), + Dictionary: jest.fn().mockReturnValue('test') + }; + class CustomConstrainedMetaModel extends ConstrainedMetaModel { + getNearestDependencies(): ConstrainedMetaModel[] { + throw new Error('Method not implemented.'); + } + } + test('should return undefined with generic constrained model', () => { + const constrainedModel = new CustomConstrainedMetaModel( + '', + undefined, + '' + ); + const t = () => { + getTypeFromMapping(typeMapping, { constrainedModel, options: {} }); + }; + expect(t).toThrow('Could not find type for model'); }); - test('should return array', () => { - const model = new CommonModel(); - model.type = 'array'; - const kind = TypeHelpers.extractKind(model); - - expect(kind).toEqual(ModelKind.ARRAY); - }); - - test('should return enum', () => { - const model = new CommonModel(); - model.type = 'string'; - model.enum = ['someValue1', 'someValue2']; - const kind = TypeHelpers.extractKind(model); - - expect(kind).toEqual(ModelKind.ENUM); - }); - - test('should return union', () => { - const model = new CommonModel(); - model.type = ['number', 'string']; - const kind = TypeHelpers.extractKind(model); - - expect(kind).toEqual(ModelKind.UNION); - }); - - test('should return primitive', () => { - const model = new CommonModel(); - model.type = 'string'; - let kind = TypeHelpers.extractKind(model); - expect(kind).toEqual(ModelKind.PRIMITIVE); - - model.type = 'number'; - kind = TypeHelpers.extractKind(model); - expect(kind).toEqual(ModelKind.PRIMITIVE); - - model.type = 'integer'; - kind = TypeHelpers.extractKind(model); - expect(kind).toEqual(ModelKind.PRIMITIVE); - - model.type = 'boolean'; - kind = TypeHelpers.extractKind(model); - expect(kind).toEqual(ModelKind.PRIMITIVE); - }); + const modelsToCheck = [ + new ConstrainedObjectModel('', undefined, '', {}), + new ConstrainedReferenceModel( + '', + undefined, + '', + new CustomConstrainedMetaModel('', undefined, '') + ), + new ConstrainedAnyModel('', undefined, ''), + new ConstrainedFloatModel('', undefined, ''), + new ConstrainedIntegerModel('', undefined, ''), + new ConstrainedStringModel('', undefined, ''), + new ConstrainedBooleanModel('', undefined, ''), + new ConstrainedTupleModel('', undefined, '', []), + new ConstrainedArrayModel( + '', + undefined, + '', + new CustomConstrainedMetaModel('', undefined, '') + ), + new ConstrainedEnumModel('', undefined, '', []), + new ConstrainedUnionModel('', undefined, '', []), + new ConstrainedDictionaryModel( + '', + undefined, + '', + new CustomConstrainedMetaModel('', undefined, ''), + new CustomConstrainedMetaModel('', undefined, '') + ) + ]; + test.each(modelsToCheck)( + 'should return type from mapping', + (constrainedModel: ConstrainedMetaModel) => { + const foundType = getTypeFromMapping(typeMapping, { + constrainedModel, + options: {} + }); + expect(foundType).toEqual('test'); + } + ); }); }); diff --git a/test/interpreter/PostInterpreter.spec.ts b/test/interpreter/PostInterpreter.spec.ts deleted file mode 100644 index f6e5f6762b..0000000000 --- a/test/interpreter/PostInterpreter.spec.ts +++ /dev/null @@ -1,288 +0,0 @@ -import { CommonModel } from '../../src'; -import { postInterpretModel } from '../../src/interpreter/PostInterpreter'; -import { isEnum, isModelObject } from '../../src/interpreter/Utils'; -jest.mock('../../src/interpreter/Utils'); -describe('PostInterpreter', () => { - beforeEach(() => { - jest.resetAllMocks(); - }); - afterAll(() => { - jest.restoreAllMocks(); - }); - describe('postInterpretModel()', () => { - test('should handle recursive models', () => { - const model = CommonModel.toCommonModel({ - $id: 'schema1', - properties: { } - }); - model.properties!['recursive'] = model; - (isModelObject as jest.Mock).mockReturnValue(true); - - const postProcessedModels = postInterpretModel(model); - - const expectedSchema1Model = new CommonModel(); - expectedSchema1Model.$id = 'schema1'; - expectedSchema1Model.properties = { - recursive: CommonModel.toCommonModel({ - $ref: 'schema1' - }) - }; - expect(postProcessedModels).toHaveLength(1); - expect(postProcessedModels[0]).toMatchObject(expectedSchema1Model); - }); - test('should split models on enums', () => { - const rawModel = { - $id: 'schema1', - properties: { - testProp: { - $id: 'schema2', - enum: [ - 'test' - ] - } - } - }; - const model = CommonModel.toCommonModel(rawModel); - (isEnum as jest.Mock).mockReturnValue(true); - - const postProcessedModels = postInterpretModel(model); - - const expectedSchema1Model = CommonModel.toCommonModel({ - $id: 'schema1', - properties: { - testProp: { - $ref: 'schema2' - } - } - }); - const expectedSchema2Model = CommonModel.toCommonModel({ - $id: 'schema2', - enum: [ - 'test' - ] - }); - - expect(postProcessedModels).toHaveLength(2); - expect(isEnum).toHaveBeenNthCalledWith(1, expectedSchema2Model); - expect(postProcessedModels[0]).toMatchObject(expectedSchema1Model); - expect(postProcessedModels[1]).toMatchObject(expectedSchema2Model); - }); - test('should split models when nested models occur', () => { - const rawModel = { - $id: 'schema1', - properties: { - testProp: { - type: 'array', - items: { - $id: 'schema2', - type: 'object' - } - } - } - }; - const model = CommonModel.toCommonModel(rawModel); - (isModelObject as jest.Mock) - .mockReturnValueOnce(false) - .mockReturnValueOnce(true); - - const postProcessedModels = postInterpretModel(model); - - const expectedSchema1Model = CommonModel.toCommonModel({ - $id: 'schema1', - properties: { - testProp: { - type: 'array', - items: { - $ref: 'schema2' - } - } - } - }); - const expectedSchema2Model = CommonModel.toCommonModel({ - $id: 'schema2', - type: 'object' - }); - - expect(postProcessedModels).toHaveLength(2); - expect(isModelObject).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: 'array' - })); - expect(isModelObject).toHaveBeenNthCalledWith(2, { - $id: 'schema2', - type: 'object' - }); - expect(postProcessedModels[0]).toMatchObject(expectedSchema1Model); - expect(postProcessedModels[1]).toMatchObject(expectedSchema2Model); - }); - test('should split models if properties contains model object', () => { - const rawModel = { - $id: 'schema1', - properties: { - testProp: { - $id: 'schema2', - type: 'object' - } - } - }; - const model = CommonModel.toCommonModel(rawModel); - (isModelObject as jest.Mock).mockReturnValue(true); - - const postProcessedModels = postInterpretModel(model); - - const expectedSchema1Model = new CommonModel(); - expectedSchema1Model.$id = 'schema1'; - const expectedPropertyModel = new CommonModel(); - expectedPropertyModel.$ref = 'schema2'; - expectedSchema1Model.properties = { - testProp: expectedPropertyModel - }; - const expectedSchema2Model = new CommonModel(); - expectedSchema2Model.$id = 'schema2'; - expectedSchema2Model.type = 'object'; - - expect(postProcessedModels).toHaveLength(2); - expect(isModelObject).toHaveBeenNthCalledWith(1, expectedSchema2Model); - expect(postProcessedModels[0]).toMatchObject(expectedSchema1Model); - expect(postProcessedModels[1]).toMatchObject(expectedSchema2Model); - }); - test('should split models if tuple items contains model object', () => { - const rawModel = { - $id: 'schema1', - items: [ - { - $id: 'schema2', - type: 'object' - } - ] - }; - const model = CommonModel.toCommonModel(rawModel); - (isModelObject as jest.Mock).mockReturnValue(true); - - const postProcessedModels = postInterpretModel(model); - - const expectedSchema1Model = new CommonModel(); - expectedSchema1Model.$id = 'schema1'; - const expectedItemModel = new CommonModel(); - expectedItemModel.$ref = 'schema2'; - expectedSchema1Model.items = [expectedItemModel]; - const expectedSchema2Model = new CommonModel(); - expectedSchema2Model.$id = 'schema2'; - expectedSchema2Model.type = 'object'; - - expect(postProcessedModels).toHaveLength(2); - expect(isModelObject).toHaveBeenNthCalledWith(1, rawModel.items[0]); - expect(postProcessedModels[0]).toMatchObject(expectedSchema1Model); - expect(postProcessedModels[1]).toMatchObject(expectedSchema2Model); - }); - test('should split models if array item contains model object', () => { - const rawModel = { - $id: 'schema1', - items: { - $id: 'schema2', - type: 'object' - } - }; - const model = CommonModel.toCommonModel(rawModel); - (isModelObject as jest.Mock).mockReturnValue(true); - - const postProcessedModels = postInterpretModel(model); - - const expectedSchema1Model = new CommonModel(); - expectedSchema1Model.$id = 'schema1'; - const expectedItemModel = new CommonModel(); - expectedItemModel.$ref = 'schema2'; - expectedSchema1Model.items = expectedItemModel; - const expectedSchema2Model = new CommonModel(); - expectedSchema2Model.$id = 'schema2'; - expectedSchema2Model.type = 'object'; - - expect(postProcessedModels).toHaveLength(2); - expect(isModelObject).toHaveBeenNthCalledWith(1, rawModel.items); - expect(postProcessedModels[0]).toMatchObject(expectedSchema1Model); - expect(postProcessedModels[1]).toMatchObject(expectedSchema2Model); - }); - test('should split models if patternProperties contains model object', () => { - const rawModel = { - $id: 'schema1', - patternProperties: { - testPattern: { - $id: 'schema2', - type: 'object' - } - } - }; - const model = CommonModel.toCommonModel(rawModel); - (isModelObject as jest.Mock).mockReturnValue(true); - - const postProcessedModels = postInterpretModel(model); - - const expectedSchema1Model = new CommonModel(); - expectedSchema1Model.$id = 'schema1'; - const expectedPatternModel = new CommonModel(); - expectedPatternModel.$ref = 'schema2'; - expectedSchema1Model.patternProperties = { - testPattern: expectedPatternModel - }; - const expectedSchema2Model = new CommonModel(); - expectedSchema2Model.$id = 'schema2'; - - expect(postProcessedModels).toHaveLength(2); - expect(isModelObject).toHaveBeenNthCalledWith(1, rawModel.patternProperties!['testPattern']); - expect(postProcessedModels[0]).toMatchObject(expectedSchema1Model); - expect(postProcessedModels[1]).toMatchObject(expectedSchema2Model); - }); - test('should split models if additionalProperties contains model object', () => { - const rawModel = { - $id: 'schema1', - additionalProperties: { - $id: 'schema2', - type: 'object' - } - }; - const model = CommonModel.toCommonModel(rawModel); - (isModelObject as jest.Mock).mockReturnValue(true); - - const postProcessedModels = postInterpretModel(model); - - const expectedAdditionalPropertyModel = new CommonModel(); - expectedAdditionalPropertyModel.$ref = 'schema2'; - const expectedSchema1Model = new CommonModel(); - expectedSchema1Model.$id = 'schema1'; - expectedSchema1Model.additionalProperties = expectedAdditionalPropertyModel; - const expectedSchema2Model = new CommonModel(); - expectedSchema2Model.$id = 'schema2'; - - expect(postProcessedModels).toHaveLength(2); - expect(isModelObject).toHaveBeenNthCalledWith(1, rawModel.additionalProperties); - expect(postProcessedModels[0]).toMatchObject(expectedSchema1Model); - expect(postProcessedModels[1]).toMatchObject(expectedSchema2Model); - }); - test('should not split models if it is not considered model object', () => { - const rawModel = { - $id: 'schema1', - properties: { - testProp: { - $id: 'schema2', - type: 'object' - } - } - }; - const model = CommonModel.toCommonModel(rawModel); - (isModelObject as jest.Mock).mockReturnValue(false); - - const postProcessedModels = postInterpretModel(model); - - const expectedSchema1Model = new CommonModel(); - expectedSchema1Model.$id = 'schema1'; - const expectedSchema2Model = new CommonModel(); - expectedSchema2Model.$id = 'schema2'; - expectedSchema1Model.properties = { - testProp: expectedSchema2Model - }; - - expect(postProcessedModels).toHaveLength(1); - expect(isModelObject).toHaveBeenNthCalledWith(1, rawModel.properties!['testProp']); - expect(postProcessedModels[0]).toMatchObject(expectedSchema1Model); - }); - }); -}); diff --git a/test/interpreter/unit/InterpretAdditionalItems.spec.ts b/test/interpreter/unit/InterpretAdditionalItems.spec.ts index 66ce03ac4f..96b638d61e 100644 --- a/test/interpreter/unit/InterpretAdditionalItems.spec.ts +++ b/test/interpreter/unit/InterpretAdditionalItems.spec.ts @@ -21,11 +21,19 @@ describe('Interpretation of additionalItems', () => { interpretAdditionalItems(schema, model, interpreter); - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, { type: 'string' }, Interpreter.defaultInterpreterOptions); - expect(model.addAdditionalItems).toHaveBeenNthCalledWith(1, mockedReturnModel, schema); + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + { type: 'string' }, + Interpreter.defaultInterpreterOptions + ); + expect(model.addAdditionalItems).toHaveBeenNthCalledWith( + 1, + mockedReturnModel, + schema + ); }); test('should ignore model if interpreter cannot interpret additionalItems schema', () => { - const schema: any = { }; + const schema: any = {}; const model = new CommonModel(); model.type = 'array'; const interpreter = new Interpreter(); @@ -44,11 +52,15 @@ describe('Interpretation of additionalItems', () => { interpretAdditionalItems(schema, model, interpreter); - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, false, Interpreter.defaultInterpreterOptions); + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + false, + Interpreter.defaultInterpreterOptions + ); expect(model.addAdditionalItems).not.toHaveBeenCalled(); }); test('should only work if model is array type', () => { - const schema: any = { }; + const schema: any = {}; const model = new CommonModel(); model.type = 'string'; const interpreter = new Interpreter(); @@ -61,7 +73,7 @@ describe('Interpretation of additionalItems', () => { expect(model.addAdditionalItems).not.toHaveBeenCalled(); }); test('should default to true', () => { - const schema: any = { }; + const schema: any = {}; const model = new CommonModel(); model.type = 'array'; const interpreter = new Interpreter(); @@ -69,8 +81,16 @@ describe('Interpretation of additionalItems', () => { (interpreter.interpret as jest.Mock).mockReturnValue(mockedReturnModel); interpretAdditionalItems(schema, model, interpreter); - - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, true, Interpreter.defaultInterpreterOptions); - expect(model.addAdditionalItems).toHaveBeenNthCalledWith(1, mockedReturnModel, schema); + + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + true, + Interpreter.defaultInterpreterOptions + ); + expect(model.addAdditionalItems).toHaveBeenNthCalledWith( + 1, + mockedReturnModel, + schema + ); }); }); diff --git a/test/interpreter/unit/InterpretAdditionalProperties.spec.ts b/test/interpreter/unit/InterpretAdditionalProperties.spec.ts index 5771694e5a..507061120f 100644 --- a/test/interpreter/unit/InterpretAdditionalProperties.spec.ts +++ b/test/interpreter/unit/InterpretAdditionalProperties.spec.ts @@ -21,11 +21,19 @@ describe('Interpretation of additionalProperties', () => { interpretAdditionalProperties(schema, model, interpreter); - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, { type: 'string' }, Interpreter.defaultInterpreterOptions); - expect(model.addAdditionalProperty).toHaveBeenNthCalledWith(1, mockedReturnModel, schema); + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + { type: 'string' }, + Interpreter.defaultInterpreterOptions + ); + expect(model.addAdditionalProperty).toHaveBeenNthCalledWith( + 1, + mockedReturnModel, + schema + ); }); test('should ignore model if interpreter cannot interpret additionalProperty schema', () => { - const schema: any = { }; + const schema: any = {}; const model = new CommonModel(); model.type = 'object'; const interpreter = new Interpreter(); @@ -44,11 +52,15 @@ describe('Interpretation of additionalProperties', () => { interpretAdditionalProperties(schema, model, interpreter); - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, false, Interpreter.defaultInterpreterOptions); + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + false, + Interpreter.defaultInterpreterOptions + ); expect(model.addAdditionalProperty).not.toHaveBeenCalled(); }); test('should only work if model is object type', () => { - const schema: any = { }; + const schema: any = {}; const model = new CommonModel(); model.type = 'string'; const interpreter = new Interpreter(); @@ -61,7 +73,7 @@ describe('Interpretation of additionalProperties', () => { expect(model.addAdditionalProperty).not.toHaveBeenCalled(); }); test('should default to true', () => { - const schema: any = { }; + const schema: any = {}; const model = new CommonModel(); model.type = 'object'; const interpreter = new Interpreter(); @@ -69,8 +81,16 @@ describe('Interpretation of additionalProperties', () => { (interpreter.interpret as jest.Mock).mockReturnValue(mockedReturnModel); interpretAdditionalProperties(schema, model, interpreter); - - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, true, Interpreter.defaultInterpreterOptions); - expect(model.addAdditionalProperty).toHaveBeenNthCalledWith(1, mockedReturnModel, schema); + + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + true, + Interpreter.defaultInterpreterOptions + ); + expect(model.addAdditionalProperty).toHaveBeenNthCalledWith( + 1, + mockedReturnModel, + schema + ); }); }); diff --git a/test/interpreter/unit/InterpretAllOf.spec.ts b/test/interpreter/unit/InterpretAllOf.spec.ts index b1867e927d..11661dabe3 100644 --- a/test/interpreter/unit/InterpretAllOf.spec.ts +++ b/test/interpreter/unit/InterpretAllOf.spec.ts @@ -3,13 +3,13 @@ import { CommonModel } from '../../../src/models/CommonModel'; import { Interpreter } from '../../../src/interpreter/Interpreter'; import { isModelObject } from '../../../src/interpreter/Utils'; import interpretAllOf from '../../../src/interpreter/InterpretAllOf'; -const interpreterOptionsAllowInheritance = {allowInheritance: true}; +const interpreterOptionsAllowInheritance = { allowInheritance: true }; jest.mock('../../../src/interpreter/Interpreter'); jest.mock('../../../src/models/CommonModel'); jest.mock('../../../src/interpreter/Utils'); CommonModel.mergeCommonModels = jest.fn(); /** - * Some of these test are purely theoretical and have little if any merit + * Some of these test are purely theoretical and have little if any merit * on a JSON Schema which actually makes sense but are used to test the principles. */ describe('Interpretation of allOf', () => { @@ -39,10 +39,20 @@ describe('Interpretation of allOf', () => { (interpreter.interpret as jest.Mock).mockReturnValue(new CommonModel()); (isModelObject as jest.Mock).mockReturnValue(false); - interpretAllOf(schema, model, interpreter, {allowInheritance: false}); + interpretAllOf(schema, model, interpreter, { allowInheritance: false }); - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, {}, {allowInheritance: false}); - expect(interpreter.interpretAndCombineSchema).toHaveBeenNthCalledWith(1, schema.allOf[0], model, schema, {allowInheritance: false}); + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + {}, + { allowInheritance: false } + ); + expect(interpreter.interpretAndCombineSchema).toHaveBeenNthCalledWith( + 1, + schema.allOf[0], + model, + schema, + { allowInheritance: false } + ); expect(JSON.stringify(model)).toEqual(JSON.stringify(new CommonModel())); }); @@ -53,7 +63,12 @@ describe('Interpretation of allOf', () => { (interpreter.interpret as jest.Mock).mockReturnValue(undefined); (isModelObject as jest.Mock).mockReturnValue(false); - interpretAllOf(schema, model, interpreter, interpreterOptionsAllowInheritance); + interpretAllOf( + schema, + model, + interpreter, + interpreterOptionsAllowInheritance + ); expect(interpreter.interpretAndCombineSchema).not.toHaveBeenCalled(); expect(JSON.stringify(model)).toEqual(JSON.stringify(new CommonModel())); @@ -65,21 +80,31 @@ describe('Interpretation of allOf', () => { (interpreter.interpret as jest.Mock).mockReturnValue(new CommonModel()); (isModelObject as jest.Mock).mockReturnValue(false); - interpretAllOf(schema, model, interpreter, interpreterOptionsAllowInheritance); + interpretAllOf( + schema, + model, + interpreter, + interpreterOptionsAllowInheritance + ); expect(interpreter.interpretAndCombineSchema).not.toHaveBeenCalled(); expect(JSON.stringify(model)).toEqual(JSON.stringify(new CommonModel())); }); test('should extend model', () => { const model = new CommonModel(); - const schema = { allOf: [{type: 'object', $id: 'test'}] }; + const schema = { allOf: [{ type: 'object', $id: 'test' }] }; const interpreter = new Interpreter(); const interpretedModel = new CommonModel(); interpretedModel.$id = 'test'; (isModelObject as jest.Mock).mockReturnValue(true); (interpreter.interpret as jest.Mock).mockReturnValue(interpretedModel); - interpretAllOf(schema, model, interpreter, interpreterOptionsAllowInheritance); + interpretAllOf( + schema, + model, + interpreter, + interpreterOptionsAllowInheritance + ); expect(interpreter.interpretAndCombineSchema).not.toHaveBeenCalled(); expect(isModelObject).toHaveBeenCalled(); diff --git a/test/interpreter/unit/InterpretAnyOf.spec.ts b/test/interpreter/unit/InterpretAnyOf.spec.ts new file mode 100644 index 0000000000..5a4412645b --- /dev/null +++ b/test/interpreter/unit/InterpretAnyOf.spec.ts @@ -0,0 +1,51 @@ +/* eslint-disable no-undef */ +import { CommonModel } from '../../../src/models/CommonModel'; +import { Interpreter } from '../../../src/interpreter/Interpreter'; +import { isModelObject } from '../../../src/interpreter/Utils'; +import InterpretAnyOf from '../../../src/interpreter/InterpretAnyOf'; +jest.mock('../../../src/interpreter/Interpreter'); +jest.mock('../../../src/models/CommonModel'); +jest.mock('../../../src/interpreter/Utils'); +CommonModel.mergeCommonModels = jest.fn(); + +describe('Interpretation of anyOf', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + afterAll(() => { + jest.restoreAllMocks(); + }); + + test('should not do anything if schema does not contain anyOf', () => { + const model = new CommonModel(); + const interpreter = new Interpreter(); + (interpreter.interpret as jest.Mock).mockReturnValue(new CommonModel()); + (isModelObject as jest.Mock).mockReturnValue(false); + + InterpretAnyOf({}, model, interpreter); + + expect(interpreter.interpret).not.toHaveBeenCalled(); + expect(model.addItemUnion).not.toHaveBeenCalled(); + expect(JSON.stringify(model)).toEqual(JSON.stringify(new CommonModel())); + }); + + test('should add anyOf items to CommonModel union', () => { + const model = new CommonModel(); + model.addItemUnion = jest.fn(); + const schema = { anyOf: [{}, {}] }; + const interpreter = new Interpreter(); + (interpreter.interpret as jest.Mock).mockReturnValue(new CommonModel()); + (isModelObject as jest.Mock).mockReturnValue(false); + + InterpretAnyOf(schema, model, interpreter, { allowInheritance: false }); + + expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.anyOf[0], { + allowInheritance: false + }); + expect(interpreter.interpret).toHaveBeenNthCalledWith(2, schema.anyOf[1], { + allowInheritance: false + }); + expect(model.addItemUnion).toHaveBeenCalledTimes(2); + expect(JSON.stringify(model)).toEqual(JSON.stringify(new CommonModel())); + }); +}); diff --git a/test/interpreter/unit/InterpretConst.spec.ts b/test/interpreter/unit/InterpretConst.spec.ts index 1dd1066119..bbec94cd6b 100644 --- a/test/interpreter/unit/InterpretConst.spec.ts +++ b/test/interpreter/unit/InterpretConst.spec.ts @@ -1,7 +1,6 @@ - import { CommonModel } from '../../../src/models/CommonModel'; import interpretConst from '../../../src/interpreter/InterpretConst'; -import {inferTypeFromValue} from '../../../src/interpreter/Utils'; +import { inferTypeFromValue } from '../../../src/interpreter/Utils'; jest.mock('../../../src/interpreter/Utils'); jest.mock('../../../src/models/CommonModel'); describe('Interpretation of const', () => { @@ -13,21 +12,21 @@ describe('Interpretation of const', () => { }); test('should not do anything if schema does not contain const', () => { const model = new CommonModel(); - const schema: any = { type: 'string'}; + const schema: any = { type: 'string' }; interpretConst(schema, model); expect(model.setType).not.toHaveBeenCalled(); expect(model.enum).toBeUndefined(); }); test('should not infer type from const if schema have type', () => { const model = new CommonModel(); - const schema: any = { type: 'string', const: 'test'}; + const schema: any = { type: 'string', const: 'test' }; interpretConst(schema, model); expect(model.setType).not.toHaveBeenCalled(); expect(model.enum).toEqual([schema.const]); }); test('should infer type and enum', () => { (inferTypeFromValue as jest.Mock).mockReturnValue('string'); - const schema: any = { const: 'test'}; + const schema: any = { const: 'test' }; const model = new CommonModel(); interpretConst(schema, model); expect(model.enum).toEqual([schema.const]); @@ -36,7 +35,7 @@ describe('Interpretation of const', () => { }); test('should not infer unknown type', () => { (inferTypeFromValue as jest.Mock).mockReturnValue(undefined); - const schema: any = { const: 'test'}; + const schema: any = { const: 'test' }; const model = new CommonModel(); interpretConst(schema, model); expect(model.enum).toEqual([schema.const]); diff --git a/test/interpreter/unit/InterpretDependencies.spec.ts b/test/interpreter/unit/InterpretDependencies.spec.ts index 2cce98e5af..d505ebcc72 100644 --- a/test/interpreter/unit/InterpretDependencies.spec.ts +++ b/test/interpreter/unit/InterpretDependencies.spec.ts @@ -6,7 +6,7 @@ jest.mock('../../../src/interpreter/Interpreter'); jest.mock('../../../src/models/CommonModel'); jest.mock('../../../src/interpreter/Utils'); /** - * Some of these test are purely theoretical and have little if any merit + * Some of these test are purely theoretical and have little if any merit * on a JSON Schema which actually makes sense but are used to test the principles. */ describe('Interpretation of dependencies', () => { @@ -19,8 +19,8 @@ describe('Interpretation of dependencies', () => { test('should not do anything if schema does not contain dependencies', () => { const model = new CommonModel(); - const interpreter = new Interpreter(); - + const interpreter = new Interpreter(); + interpretDependencies({}, model, interpreter); expect(interpreter.interpretAndCombineSchema).not.toHaveBeenCalled(); @@ -28,9 +28,9 @@ describe('Interpretation of dependencies', () => { }); test('should not do anything with property dependencies', () => { const model = new CommonModel(); - const interpreter = new Interpreter(); - const schema = { dependencies: {dep1: ['dep2']} }; - + const interpreter = new Interpreter(); + const schema = { dependencies: { dep1: ['dep2'] } }; + interpretDependencies(schema, model, interpreter); expect(interpreter.interpretAndCombineSchema).not.toHaveBeenCalled(); @@ -39,11 +39,17 @@ describe('Interpretation of dependencies', () => { test('should interpret and combine schema dependencies', () => { const model = new CommonModel(); - const schema = { dependencies: {dep1: {}} }; + const schema = { dependencies: { dep1: {} } }; const interpreter = new Interpreter(); interpretDependencies(schema, model, interpreter); - expect(interpreter.interpretAndCombineSchema).toHaveBeenNthCalledWith(1, schema.dependencies.dep1, model, schema, Interpreter.defaultInterpreterOptions); + expect(interpreter.interpretAndCombineSchema).toHaveBeenNthCalledWith( + 1, + schema.dependencies.dep1, + model, + schema, + Interpreter.defaultInterpreterOptions + ); }); }); diff --git a/test/interpreter/unit/InterpretEnum.spec.ts b/test/interpreter/unit/InterpretEnum.spec.ts index f12f62edbd..5046aa4aba 100644 --- a/test/interpreter/unit/InterpretEnum.spec.ts +++ b/test/interpreter/unit/InterpretEnum.spec.ts @@ -1,6 +1,6 @@ import { CommonModel } from '../../../src/models/CommonModel'; import interpretEnum from '../../../src/interpreter/InterpretEnum'; -import {inferTypeFromValue} from '../../../src/interpreter/Utils'; +import { inferTypeFromValue } from '../../../src/interpreter/Utils'; jest.mock('../../../src/interpreter/Utils'); jest.mock('../../../src/models/CommonModel'); @@ -13,7 +13,7 @@ describe('Interpretation of enum', () => { }); test('should not infer type if schema have type', () => { const model = new CommonModel(); - const schema: any = { type: 'string', enum: ['test']}; + const schema: any = { type: 'string', enum: ['test'] }; interpretEnum(schema, model); expect(model.addTypes).not.toHaveBeenCalled(); expect(model.addEnum).toHaveBeenNthCalledWith(1, schema.enum[0]); @@ -21,7 +21,7 @@ describe('Interpretation of enum', () => { test('Should not add enum if it already exist', () => { const model = new CommonModel(); model.enum = ['test']; - const schema: any = {enum: ['test']}; + const schema: any = { enum: ['test'] }; interpretEnum(schema, model); expect(model.addEnum).toHaveBeenNthCalledWith(1, schema.enum[0]); }); @@ -33,7 +33,7 @@ describe('Interpretation of enum', () => { }); test('should not infer type from unknown value type', () => { (inferTypeFromValue as jest.Mock).mockReturnValue(undefined); - const schema: any = { enum: ['test']}; + const schema: any = { enum: ['test'] }; const model = new CommonModel(); interpretEnum(schema, model); expect(model.addEnum).toHaveBeenNthCalledWith(1, schema.enum[0]); @@ -42,7 +42,7 @@ describe('Interpretation of enum', () => { }); test('should add inferred value', () => { (inferTypeFromValue as jest.Mock).mockReturnValue('string'); - const schema: any = { enum: ['test']}; + const schema: any = { enum: ['test'] }; const model = new CommonModel(); interpretEnum(schema, model); expect(inferTypeFromValue).toHaveBeenNthCalledWith(1, 'test'); diff --git a/test/interpreter/unit/InterpretItems.spec.ts b/test/interpreter/unit/InterpretItems.spec.ts index ed0ef1fee5..d5703015e0 100644 --- a/test/interpreter/unit/InterpretItems.spec.ts +++ b/test/interpreter/unit/InterpretItems.spec.ts @@ -29,7 +29,7 @@ describe('Interpretation of', () => { const model = new CommonModel(); const interpreter = new Interpreter(); (interpreter.interpret as jest.Mock).mockReturnValue(undefined); - + interpretItems(schema, model, interpreter); expect(model.type).toBeUndefined(); @@ -45,8 +45,16 @@ describe('Interpretation of', () => { interpretItems(schema, model, interpreter); - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, { type: 'string' }, Interpreter.defaultInterpreterOptions); - expect(model.addItem).toHaveBeenNthCalledWith(1, mockedReturnModel, schema); + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + { type: 'string' }, + Interpreter.defaultInterpreterOptions + ); + expect(model.addItem).toHaveBeenNthCalledWith( + 1, + mockedReturnModel, + schema + ); }); test('should infer type of model', () => { const schema: any = { items: { type: 'string' } }; @@ -70,10 +78,28 @@ describe('Interpretation of', () => { interpretItems(schema, model, interpreter); - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, { type: 'string' }, Interpreter.defaultInterpreterOptions); - expect(interpreter.interpret).toHaveBeenNthCalledWith(2, { type: 'number' }, Interpreter.defaultInterpreterOptions); - expect(model.addItemTuple).toHaveBeenNthCalledWith(1, mockedReturnModel, schema, 0); - expect(model.addItemTuple).toHaveBeenNthCalledWith(2, mockedReturnModel, schema, 1); + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + { type: 'string' }, + Interpreter.defaultInterpreterOptions + ); + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 2, + { type: 'number' }, + Interpreter.defaultInterpreterOptions + ); + expect(model.addItemTuple).toHaveBeenNthCalledWith( + 1, + mockedReturnModel, + schema, + 0 + ); + expect(model.addItemTuple).toHaveBeenNthCalledWith( + 2, + mockedReturnModel, + schema, + 1 + ); }); test('should infer type of model', () => { const schema: any = { items: [{ type: 'string' }, { type: 'number' }] }; @@ -83,7 +109,7 @@ describe('Interpretation of', () => { (interpreter.interpret as jest.Mock).mockReturnValue(mockedReturnModel); interpretItems(schema, model, interpreter); - + expect(model.addTypes).toHaveBeenNthCalledWith(1, 'array'); }); }); diff --git a/test/interpreter/unit/InterpretNot.spec.ts b/test/interpreter/unit/InterpretNot.spec.ts index 7201f8b8c7..34a5259f13 100644 --- a/test/interpreter/unit/InterpretNot.spec.ts +++ b/test/interpreter/unit/InterpretNot.spec.ts @@ -1,8 +1,7 @@ - import { CommonModel } from '../../../src/models/CommonModel'; import { Interpreter } from '../../../src/interpreter/Interpreter'; import interpretNot from '../../../src/interpreter/InterpretNot'; -import {inferTypeFromValue} from '../../../src/interpreter/Utils'; +import { inferTypeFromValue } from '../../../src/interpreter/Utils'; import { Logger } from '../../../src/utils'; let interpreterOptions = Interpreter.defaultInterpreterOptions; jest.mock('../../../src/interpreter/Utils'); @@ -11,7 +10,9 @@ jest.mock('../../../src/interpreter/Interpreter'); describe('Interpretation of not', () => { beforeEach(() => { jest.resetAllMocks(); - (inferTypeFromValue as jest.Mock).mockImplementation(() => {return;}); + (inferTypeFromValue as jest.Mock).mockImplementation(() => { + return; + }); interpreterOptions = Interpreter.defaultInterpreterOptions; }); afterAll(() => { @@ -28,7 +29,7 @@ describe('Interpretation of not', () => { }); test('should ignore model if interpreter cannot interpret not schema', () => { - const schema: any = { not: { } }; + const schema: any = { not: {} }; const model = new CommonModel(); const interpreter = new Interpreter(); const mockedReturnModel = new CommonModel(); @@ -39,7 +40,7 @@ describe('Interpretation of not', () => { expect(JSON.stringify(model)).toEqual(JSON.stringify(new CommonModel())); }); test('should warn about true schemas', () => { - const schema: any = { not: true}; + const schema: any = { not: true }; const model = new CommonModel(); const interpreter = new Interpreter(); const mockedReturnModel = new CommonModel(); @@ -53,7 +54,9 @@ describe('Interpretation of not', () => { }); describe('double negate', () => { test('should double negate enum', () => { - const schema: any = { not: { enum: ['value'], not: { enum: ['value'] } }}; + const schema: any = { + not: { enum: ['value'], not: { enum: ['value'] } } + }; const model = new CommonModel(); model.enum = ['value']; const interpreter = new Interpreter(); @@ -62,13 +65,20 @@ describe('Interpretation of not', () => { interpretNot(schema, model, interpreter, interpreterOptions); - const expectedInterpreterOptions = {...interpreterOptions, allowInheritance: false}; - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.not, expectedInterpreterOptions); + const expectedInterpreterOptions = { + ...interpreterOptions, + allowInheritance: false + }; + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + schema.not, + expectedInterpreterOptions + ); expect(model.enum).toEqual(['value']); }); test('should double negate types', () => { - const schema: any = { not: { type: 'string', not: { type: 'string' }}}; + const schema: any = { not: { type: 'string', not: { type: 'string' } } }; const model = new CommonModel(); model.type = 'string'; const interpreter = new Interpreter(); @@ -76,15 +86,22 @@ describe('Interpretation of not', () => { (interpreter.interpret as jest.Mock).mockReturnValue(mockedReturnModel); interpretNot(schema, model, interpreter, interpreterOptions); - - const expectedInterpreterOptions = {...interpreterOptions, allowInheritance: false}; - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.not, expectedInterpreterOptions); + + const expectedInterpreterOptions = { + ...interpreterOptions, + allowInheritance: false + }; + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + schema.not, + expectedInterpreterOptions + ); expect(model.type).toEqual('string'); }); }); describe('enums', () => { test('should remove already existing inferred enums', () => { - const schema: any = { not: { enum: ['value'] }}; + const schema: any = { not: { enum: ['value'] } }; const model = new CommonModel(); model.enum = ['value']; const interpreter = new Interpreter(); @@ -94,12 +111,19 @@ describe('Interpretation of not', () => { interpretNot(schema, model, interpreter, interpreterOptions); - const expectedInterpreterOptions = {...interpreterOptions, allowInheritance: false}; - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.not, expectedInterpreterOptions); + const expectedInterpreterOptions = { + ...interpreterOptions, + allowInheritance: false + }; + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + schema.not, + expectedInterpreterOptions + ); expect(model.enum).toBeUndefined(); }); test('should handle negating only existing enum', () => { - const schema: any = { not: { enum: ['value'] }}; + const schema: any = { not: { enum: ['value'] } }; const model = new CommonModel(); model.enum = ['value', 'value2']; const interpreter = new Interpreter(); @@ -109,12 +133,19 @@ describe('Interpretation of not', () => { interpretNot(schema, model, interpreter, interpreterOptions); - const expectedInterpreterOptions = {...interpreterOptions, allowInheritance: false}; - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.not, expectedInterpreterOptions); + const expectedInterpreterOptions = { + ...interpreterOptions, + allowInheritance: false + }; + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + schema.not, + expectedInterpreterOptions + ); expect(model.enum).toEqual(['value2']); }); test('should not negating non existing enum', () => { - const schema: any = { not: { enum: ['value'] }}; + const schema: any = { not: { enum: ['value'] } }; const model = new CommonModel(); model.enum = ['value2']; const interpreter = new Interpreter(); @@ -124,12 +155,19 @@ describe('Interpretation of not', () => { interpretNot(schema, model, interpreter, interpreterOptions); - const expectedInterpreterOptions = {...interpreterOptions, allowInheritance: false}; - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.not, expectedInterpreterOptions); + const expectedInterpreterOptions = { + ...interpreterOptions, + allowInheritance: false + }; + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + schema.not, + expectedInterpreterOptions + ); expect(model.enum).toEqual(['value2']); }); test('should handle multiple negated enums', () => { - const schema: any = { not: { enum: ['value', 'value2'] }}; + const schema: any = { not: { enum: ['value', 'value2'] } }; const model = new CommonModel(); model.enum = ['value', 'value2', 'value3']; const interpreter = new Interpreter(); @@ -139,14 +177,21 @@ describe('Interpretation of not', () => { interpretNot(schema, model, interpreter, interpreterOptions); - const expectedInterpreterOptions = {...interpreterOptions, allowInheritance: false}; - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.not, expectedInterpreterOptions); + const expectedInterpreterOptions = { + ...interpreterOptions, + allowInheritance: false + }; + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + schema.not, + expectedInterpreterOptions + ); expect(model.enum).toEqual(['value3']); }); }); describe('types', () => { test('should handle negating only existing type', () => { - const schema: any = { not: { type: 'string' }}; + const schema: any = { not: { type: 'string' } }; const model = new CommonModel(); model.type = 'string'; const interpreter = new Interpreter(); @@ -156,12 +201,19 @@ describe('Interpretation of not', () => { interpretNot(schema, model, interpreter, interpreterOptions); - const expectedInterpreterOptions = {...interpreterOptions, allowInheritance: false}; - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.not, expectedInterpreterOptions); + const expectedInterpreterOptions = { + ...interpreterOptions, + allowInheritance: false + }; + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + schema.not, + expectedInterpreterOptions + ); expect(model.type).toBeUndefined(); }); test('should remove already existing inferred type', () => { - const schema: any = { not: { type: 'string' }}; + const schema: any = { not: { type: 'string' } }; const model = new CommonModel(); model.type = ['string', 'number']; const interpreter = new Interpreter(); @@ -171,12 +223,19 @@ describe('Interpretation of not', () => { interpretNot(schema, model, interpreter, interpreterOptions); - const expectedInterpreterOptions = {...interpreterOptions, allowInheritance: false}; - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.not, expectedInterpreterOptions); + const expectedInterpreterOptions = { + ...interpreterOptions, + allowInheritance: false + }; + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + schema.not, + expectedInterpreterOptions + ); expect(model.type).toEqual('number'); }); test('should not negating non existing type', () => { - const schema: any = { not: { type: 'string' }}; + const schema: any = { not: { type: 'string' } }; const model = new CommonModel(); model.type = 'number'; const interpreter = new Interpreter(); @@ -186,12 +245,19 @@ describe('Interpretation of not', () => { interpretNot(schema, model, interpreter, interpreterOptions); - const expectedInterpreterOptions = {...interpreterOptions, allowInheritance: false}; - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.not, expectedInterpreterOptions); + const expectedInterpreterOptions = { + ...interpreterOptions, + allowInheritance: false + }; + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + schema.not, + expectedInterpreterOptions + ); expect(model.type).toEqual('number'); }); test('should handle multiple negated types', () => { - const schema: any = { not: { type: ['string', 'number'] }}; + const schema: any = { not: { type: ['string', 'number'] } }; const model = new CommonModel(); model.type = ['number', 'string', 'integer']; const interpreter = new Interpreter(); @@ -201,8 +267,15 @@ describe('Interpretation of not', () => { interpretNot(schema, model, interpreter, interpreterOptions); - const expectedInterpreterOptions = {...interpreterOptions, allowInheritance: false}; - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.not, expectedInterpreterOptions); + const expectedInterpreterOptions = { + ...interpreterOptions, + allowInheritance: false + }; + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + schema.not, + expectedInterpreterOptions + ); expect(model.type).toEqual('integer'); }); }); diff --git a/test/interpreter/unit/InterpretOneOf.spec.ts b/test/interpreter/unit/InterpretOneOf.spec.ts new file mode 100644 index 0000000000..e058abc2b5 --- /dev/null +++ b/test/interpreter/unit/InterpretOneOf.spec.ts @@ -0,0 +1,51 @@ +/* eslint-disable no-undef */ +import { CommonModel } from '../../../src/models/CommonModel'; +import { Interpreter } from '../../../src/interpreter/Interpreter'; +import { isModelObject } from '../../../src/interpreter/Utils'; +import InterpretOneOf from '../../../src/interpreter/InterpretOneOf'; +jest.mock('../../../src/interpreter/Interpreter'); +jest.mock('../../../src/models/CommonModel'); +jest.mock('../../../src/interpreter/Utils'); +CommonModel.mergeCommonModels = jest.fn(); + +describe('Interpretation of oneOf', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + afterAll(() => { + jest.restoreAllMocks(); + }); + + test('should not do anything if schema does not contain oneOf', () => { + const model = new CommonModel(); + const interpreter = new Interpreter(); + (interpreter.interpret as jest.Mock).mockReturnValue(new CommonModel()); + (isModelObject as jest.Mock).mockReturnValue(false); + + InterpretOneOf({}, model, interpreter); + + expect(interpreter.interpret).not.toHaveBeenCalled(); + expect(model.addItemUnion).not.toHaveBeenCalled(); + expect(JSON.stringify(model)).toEqual(JSON.stringify(new CommonModel())); + }); + + test('should add oneOf items to CommonModel union', () => { + const model = new CommonModel(); + model.addItemUnion = jest.fn(); + const schema = { oneOf: [{}, {}] }; + const interpreter = new Interpreter(); + (interpreter.interpret as jest.Mock).mockReturnValue(new CommonModel()); + (isModelObject as jest.Mock).mockReturnValue(false); + + InterpretOneOf(schema, model, interpreter, { allowInheritance: false }); + + expect(interpreter.interpret).toHaveBeenNthCalledWith(1, schema.oneOf[0], { + allowInheritance: false + }); + expect(interpreter.interpret).toHaveBeenNthCalledWith(2, schema.oneOf[1], { + allowInheritance: false + }); + expect(model.addItemUnion).toHaveBeenCalledTimes(2); + expect(JSON.stringify(model)).toEqual(JSON.stringify(new CommonModel())); + }); +}); diff --git a/test/interpreter/unit/InterpretOneOfWithAllOf.spec.ts b/test/interpreter/unit/InterpretOneOfWithAllOf.spec.ts new file mode 100644 index 0000000000..f660ab7239 --- /dev/null +++ b/test/interpreter/unit/InterpretOneOfWithAllOf.spec.ts @@ -0,0 +1,107 @@ +import { CommonModel } from '../../../src/models/CommonModel'; +import { Interpreter } from '../../../src/interpreter/Interpreter'; +import InterpretOneOfWithAllOf from '../../../src/interpreter/InterpretOneOfWithAllOf'; + +describe('Interpretation of oneOf with allOf', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + afterAll(() => { + jest.restoreAllMocks(); + }); + + test('should not do anything if schema does not contain oneOf and allOf', () => { + const model = new CommonModel(); + model.addItemUnion = jest.fn(); + const interpreter = new Interpreter(); + interpreter.interpret = jest.fn(); + + InterpretOneOfWithAllOf({}, model, interpreter); + + expect(interpreter.interpret).not.toHaveBeenCalled(); + expect(model.addItemUnion).not.toHaveBeenCalled(); + expect(JSON.stringify(model)).toEqual(JSON.stringify(new CommonModel())); + }); + + test('should add oneOf items to CommonModel union', () => { + const model = new CommonModel(); + const schema = { + allOf: [ + { + title: 'Animal', + type: 'object', + properties: { + animalType: { + title: 'Animal Type', + type: 'string' + }, + age: { + type: 'integer', + min: 0 + } + } + } + ], + oneOf: [ + { + title: 'Cat', + type: 'object', + properties: { + animalType: { + const: 'Cat' + }, + huntingSkill: { + title: 'Hunting Skill', + type: 'string', + enum: ['clueless', 'lazy'] + } + } + }, + { + title: 'Dog', + type: 'object', + additionalProperties: false, + properties: { + animalType: { + const: 'Dog' + }, + breed: { + title: 'Dog Breed', + type: 'string', + enum: ['bulldog', 'bichons frise'] + } + } + } + ] + }; + + const interpreter = new Interpreter(); + + InterpretOneOfWithAllOf(schema, model, interpreter, { + allowInheritance: false + }); + + expect(model.type).toBeUndefined(); + expect(model.union).toHaveLength(2); + + const cat = model.union?.find((item) => item.$id === 'Cat'); + expect(cat).not.toBeUndefined(); + expect(cat?.properties?.animalType).toMatchObject({ + $id: 'Animal Type', + enum: ['Cat', 'Dog'] + }); + expect(cat?.properties).toHaveProperty('age'); + expect(cat?.properties).toHaveProperty('huntingSkill'); + expect(cat?.properties).not.toHaveProperty('breed'); + + const dog = model.union?.find((item) => item.$id === 'Dog'); + expect(dog).not.toBeUndefined(); + expect(dog?.properties?.animalType).toMatchObject({ + $id: 'Animal Type', + enum: ['Cat', 'Dog'] + }); + expect(dog?.properties).toHaveProperty('age'); + expect(dog?.properties).toHaveProperty('breed'); + expect(dog?.properties).not.toHaveProperty('huntingSkill'); + }); +}); diff --git a/test/interpreter/unit/InterpretProperties.spec.ts b/test/interpreter/unit/InterpretProperties.spec.ts index a23787c4ed..513e1278ad 100644 --- a/test/interpreter/unit/InterpretProperties.spec.ts +++ b/test/interpreter/unit/InterpretProperties.spec.ts @@ -6,7 +6,7 @@ jest.mock('../../../src/interpreter/Interpreter'); jest.mock('../../../src/models/CommonModel'); CommonModel.mergeCommonModels = jest.fn(); /** - * Some of these test are purely theoretical and have little if any merit + * Some of these test are purely theoretical and have little if any merit * on a JSON Schema which actually makes sense but are used to test the principles. */ describe('Interpretation of properties', () => { @@ -25,7 +25,7 @@ describe('Interpretation of properties', () => { (interpreter.interpret as jest.Mock).mockReturnValue(mockedReturnModel); interpretProperties(schema, model, interpreter); - + expect(JSON.stringify(model)).toEqual(JSON.stringify(new CommonModel())); }); test('should ignore model if interpreter cannot interpret property schema', () => { @@ -55,10 +55,19 @@ describe('Interpretation of properties', () => { const interpreter = new Interpreter(); const mockedReturnModel = new CommonModel(); (interpreter.interpret as jest.Mock).mockReturnValue(mockedReturnModel); - + interpretProperties(schema, model, interpreter); - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, { type: 'string' }, Interpreter.defaultInterpreterOptions); - expect(model.addProperty).toHaveBeenNthCalledWith(1, 'property1', mockedReturnModel, schema); + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + { type: 'string' }, + Interpreter.defaultInterpreterOptions + ); + expect(model.addProperty).toHaveBeenNthCalledWith( + 1, + 'property1', + mockedReturnModel, + schema + ); }); }); diff --git a/test/interpreter/unit/Intepreter.spec.ts b/test/interpreter/unit/Interpreter.spec.ts similarity index 77% rename from test/interpreter/unit/Intepreter.spec.ts rename to test/interpreter/unit/Interpreter.spec.ts index 679f9f7525..004be6db2c 100644 --- a/test/interpreter/unit/Intepreter.spec.ts +++ b/test/interpreter/unit/Interpreter.spec.ts @@ -1,5 +1,9 @@ -import {Interpreter} from '../../../src/interpreter/Interpreter'; -import {interpretName, isEnum, isModelObject} from '../../../src/interpreter/Utils'; +import { Interpreter } from '../../../src/interpreter/Interpreter'; +import { + interpretName, + isEnum, + isModelObject +} from '../../../src/interpreter/Utils'; import interpretProperties from '../../../src/interpreter/InterpretProperties'; import interpretConst from '../../../src/interpreter/InterpretConst'; import interpretEnum from '../../../src/interpreter/InterpretEnum'; @@ -24,7 +28,7 @@ jest.mock('../../../src/interpreter/InterpretDependencies'); jest.mock('../../../src/interpreter/InterpretAdditionalItems'); CommonModel.mergeCommonModels = jest.fn(); /** - * Some of these test are purely theoretical and have little if any merit + * Some of these test are purely theoretical and have little if any merit * on a JSON Schema which actually makes sense but are used to test the principles. */ describe('Interpreter', () => { @@ -63,7 +67,15 @@ describe('Interpreter', () => { const interpreter = new Interpreter(); const model = interpreter.interpret(schema); expect(model).not.toBeUndefined(); - expect(model?.type).toEqual(['object', 'string', 'number', 'array', 'boolean', 'null', 'integer']); + expect(model?.type).toEqual([ + 'object', + 'string', + 'number', + 'array', + 'boolean', + 'null', + 'integer' + ]); }); test('should set id of model if enum', () => { const schema = { enum: ['value'] }; @@ -99,13 +111,16 @@ describe('Interpreter', () => { }); test('should support recursive schemas', () => { - const schema1: Draft7Schema = { }; + const schema1: Draft7Schema = {}; const schema2 = { anyOf: [schema1] }; schema1.anyOf = [schema2]; const interpreter = new Interpreter(); const model = interpreter.interpret(schema1); expect(model).not.toBeUndefined(); - expect(model).toEqual({originalInput: schema1}); + expect(model).toMatchObject({ + originalInput: schema1, + $id: 'anonymSchema2' + }); }); describe('combineSchemas', () => { test('should combine single schema with model', () => { @@ -113,34 +128,40 @@ describe('Interpreter', () => { const interpreter = new Interpreter(); const model = new CommonModel(); const expectedSimplifiedModel = new CommonModel(); + expectedSimplifiedModel.$id = 'anonymSchema1'; expectedSimplifiedModel.required = ['test']; expectedSimplifiedModel.originalInput = schema; interpreter.interpretAndCombineSchema(schema, model, schema); - expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith(1, model, expectedSimplifiedModel, schema); - }); - test('should combine multiple schema with model', () => { - const schema = { required: ['test'] }; - const interpreter = new Interpreter(); - const model = new CommonModel(); - const expectedSimplifiedModel = new CommonModel(); - expectedSimplifiedModel.required = ['test']; - expectedSimplifiedModel.originalInput = schema; - interpreter.interpretAndCombineMultipleSchemas([schema], model, schema); - expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith(1, model, expectedSimplifiedModel, schema); + expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith( + 1, + model, + expectedSimplifiedModel, + schema + ); }); }); test('should always try to interpret properties', () => { const schema = {}; const interpreter = new Interpreter(); interpreter.interpret(schema); - expect(interpretProperties).toHaveBeenNthCalledWith(1, schema, expect.anything(), interpreter, Interpreter.defaultInterpreterOptions); + expect(interpretProperties).toHaveBeenNthCalledWith( + 1, + schema, + expect.anything(), + interpreter, + Interpreter.defaultInterpreterOptions + ); }); test('should always try to interpret const', () => { const schema = {}; const interpreter = new Interpreter(); interpreter.interpret(schema); - expect(interpretConst).toHaveBeenNthCalledWith(1, schema, expect.anything()); + expect(interpretConst).toHaveBeenNthCalledWith( + 1, + schema, + expect.anything() + ); }); test('should always try to interpret enum', () => { const schema = {}; @@ -152,37 +173,73 @@ describe('Interpreter', () => { const schema = {}; const interpreter = new Interpreter(); interpreter.interpret(schema); - expect(interpretAllOf).toHaveBeenNthCalledWith(1, schema, expect.anything(), expect.anything(), Interpreter.defaultInterpreterOptions); + expect(interpretAllOf).toHaveBeenNthCalledWith( + 1, + schema, + expect.anything(), + expect.anything(), + Interpreter.defaultInterpreterOptions + ); }); test('should always try to interpret items', () => { const schema = {}; const interpreter = new Interpreter(); interpreter.interpret(schema); - expect(interpretItems).toHaveBeenNthCalledWith(1, schema, expect.anything(), interpreter, Interpreter.defaultInterpreterOptions); + expect(interpretItems).toHaveBeenNthCalledWith( + 1, + schema, + expect.anything(), + interpreter, + Interpreter.defaultInterpreterOptions + ); }); test('should always try to interpret additionalProperties', () => { const schema = {}; const interpreter = new Interpreter(); interpreter.interpret(schema); - expect(interpretAdditionalProperties).toHaveBeenNthCalledWith(1, schema, expect.anything(), interpreter, Interpreter.defaultInterpreterOptions); + expect(interpretAdditionalProperties).toHaveBeenNthCalledWith( + 1, + schema, + expect.anything(), + interpreter, + Interpreter.defaultInterpreterOptions + ); }); test('should always try to interpret not', () => { const schema = {}; const interpreter = new Interpreter(); interpreter.interpret(schema); - expect(interpretNot).toHaveBeenNthCalledWith(1, schema, expect.anything(), interpreter, Interpreter.defaultInterpreterOptions); + expect(interpretNot).toHaveBeenNthCalledWith( + 1, + schema, + expect.anything(), + interpreter, + Interpreter.defaultInterpreterOptions + ); }); test('should always try to interpret dependencies', () => { const schema = {}; const interpreter = new Interpreter(); interpreter.interpret(schema); - expect(interpretDependencies).toHaveBeenNthCalledWith(1, schema, expect.anything(), expect.anything(), Interpreter.defaultInterpreterOptions); + expect(interpretDependencies).toHaveBeenNthCalledWith( + 1, + schema, + expect.anything(), + expect.anything(), + Interpreter.defaultInterpreterOptions + ); }); test('should always try to interpret additionalItems', () => { const schema = {}; const interpreter = new Interpreter(); interpreter.interpret(schema); - expect(interpretAdditionalItems).toHaveBeenNthCalledWith(1, schema, expect.anything(), expect.anything(), Interpreter.defaultInterpreterOptions); + expect(interpretAdditionalItems).toHaveBeenNthCalledWith( + 1, + schema, + expect.anything(), + expect.anything(), + Interpreter.defaultInterpreterOptions + ); }); test('should support primitive roots', () => { const schema = { type: 'string' }; @@ -193,6 +250,7 @@ describe('Interpreter', () => { originalInput: { type: 'string' }, + $id: 'anonymSchema1', type: 'string' }); }); diff --git a/test/interpreter/unit/Utils.spec.ts b/test/interpreter/unit/Utils.spec.ts index f7aadeee11..af09ae661b 100644 --- a/test/interpreter/unit/Utils.spec.ts +++ b/test/interpreter/unit/Utils.spec.ts @@ -1,6 +1,10 @@ - import { CommonModel } from '../../../src/models/CommonModel'; -import { interpretName, inferTypeFromValue, isModelObject, isEnum } from '../../../src/interpreter/Utils'; +import { + interpretName, + inferTypeFromValue, + isModelObject, + isEnum +} from '../../../src/interpreter/Utils'; describe('utils', () => { describe('isEnum', () => { @@ -31,7 +35,15 @@ describe('utils', () => { }); test('should return false if contains all types', () => { const model = new CommonModel(); - model.type = ['object', 'string', 'number', 'array', 'boolean', 'null', 'integer']; + model.type = [ + 'object', + 'string', + 'number', + 'array', + 'boolean', + 'null', + 'integer' + ]; const isModel = isModelObject(model); expect(isModel).toEqual(false); }); diff --git a/test/interpreter/unit/interpretOneOfWithProperties.spec.ts b/test/interpreter/unit/interpretOneOfWithProperties.spec.ts new file mode 100644 index 0000000000..1fb13f0a97 --- /dev/null +++ b/test/interpreter/unit/interpretOneOfWithProperties.spec.ts @@ -0,0 +1,103 @@ +import { CommonModel } from '../../../src/models/CommonModel'; +import { Interpreter } from '../../../src/interpreter/Interpreter'; +import interpretOneOfWithProperties from '../../../src/interpreter/InterpretOneOfWithProperties'; + +describe('Interpretation of oneOf with properties', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + afterAll(() => { + jest.restoreAllMocks(); + }); + + test('should not do anything if schema does not contain oneOf and properties', () => { + const model = new CommonModel(); + model.addItemUnion = jest.fn(); + const interpreter = new Interpreter(); + interpreter.interpret = jest.fn(); + + interpretOneOfWithProperties({}, model, interpreter); + + expect(interpreter.interpret).not.toHaveBeenCalled(); + expect(model.addItemUnion).not.toHaveBeenCalled(); + expect(JSON.stringify(model)).toEqual(JSON.stringify(new CommonModel())); + }); + + test('should add oneOf items to CommonModel union', () => { + const model = new CommonModel(); + const schema = { + title: 'Animal', + type: 'object', + properties: { + animalType: { + title: 'Animal Type', + type: 'string' + }, + age: { + type: 'integer', + min: 0 + } + }, + oneOf: [ + { + title: 'Cat', + type: 'object', + properties: { + animalType: { + const: 'Cat' + }, + huntingSkill: { + title: 'Hunting Skill', + type: 'string', + enum: ['clueless', 'lazy'] + } + } + }, + { + title: 'Dog', + type: 'object', + additionalProperties: false, + properties: { + animalType: { + const: 'Dog' + }, + breed: { + title: 'Dog Breed', + type: 'string', + enum: ['bulldog', 'bichons frise'] + } + } + } + ] + }; + + const interpreter = new Interpreter(); + + interpretOneOfWithProperties(schema, model, interpreter, { + allowInheritance: false + }); + + expect(model.type).toBeUndefined(); + expect(model.union).toHaveLength(2); + + const cat = model.union?.find((item) => item.$id === 'Cat'); + expect(cat).not.toBeUndefined(); + expect(cat?.properties?.animalType).toMatchObject({ + $id: 'Animal Type', + enum: ['Cat', 'Dog'] + }); + expect(cat?.properties).toHaveProperty('age'); + expect(cat?.properties).toHaveProperty('huntingSkill'); + expect(cat?.properties).not.toHaveProperty('breed'); + + const dog = model.union?.find((item) => item.$id === 'Dog'); + expect(dog).not.toBeUndefined(); + expect(dog?.properties?.animalType).toMatchObject({ + $id: 'Animal Type', + enum: ['Cat', 'Dog'] + }); + expect(dog?.properties).toHaveProperty('age'); + expect(dog?.properties).toHaveProperty('breed'); + expect(dog?.properties).not.toHaveProperty('huntingSkill'); + }); +}); diff --git a/test/interpreter/unit/patternProperties.spec.ts b/test/interpreter/unit/interpretPatternProperties.spec.ts similarity index 88% rename from test/interpreter/unit/patternProperties.spec.ts rename to test/interpreter/unit/interpretPatternProperties.spec.ts index 428bb965cd..9d94e34902 100644 --- a/test/interpreter/unit/patternProperties.spec.ts +++ b/test/interpreter/unit/interpretPatternProperties.spec.ts @@ -54,8 +54,17 @@ describe('Interpretation of patternProperties', () => { (interpreter.interpret as jest.Mock).mockReturnValue(mockedReturnModel); interpretPatternProperties(schema, model, interpreter); - - expect(interpreter.interpret).toHaveBeenNthCalledWith(1, { type: 'string' }, Interpreter.defaultInterpreterOptions); - expect(model.addPatternProperty).toHaveBeenNthCalledWith(1, 'pattern', mockedReturnModel, schema); + + expect(interpreter.interpret).toHaveBeenNthCalledWith( + 1, + { type: 'string' }, + Interpreter.defaultInterpreterOptions + ); + expect(model.addPatternProperty).toHaveBeenNthCalledWith( + 1, + 'pattern', + mockedReturnModel, + schema + ); }); }); diff --git a/test/models/AsyncapiV2Schema.spec.ts b/test/models/AsyncapiV2Schema.spec.ts index 3b7985d8a2..c1420be469 100644 --- a/test/models/AsyncapiV2Schema.spec.ts +++ b/test/models/AsyncapiV2Schema.spec.ts @@ -1,13 +1,19 @@ -import {AsyncapiV2ExternalDocumentation, AsyncapiV2Schema} from '../../src/models/AsyncapiV2Schema'; +import { + AsyncapiV2ExternalDocumentation, + AsyncapiV2Schema +} from '../../src/models/AsyncapiV2Schema'; describe('AsyncapiV2Schema', () => { describe('toSchema', () => { test('should throw error when trying to convert non-object', () => { - const expectedError = 'Could not convert input to expected copy of AsyncapiV2Schema'; - expect(() => { AsyncapiV2Schema.toSchema(1 as any); }).toThrow(expectedError); + const expectedError = + 'Could not convert input to expected copy of AsyncapiV2Schema'; + expect(() => { + AsyncapiV2Schema.toSchema(1 as any); + }).toThrow(expectedError); }); test('should handle recursive schemas', () => { - const recursiveDoc = { type: 'object', properties: { } }; + const recursiveDoc = { type: 'object', properties: {} }; const doc = { type: 'object', properties: { test: recursiveDoc } }; (recursiveDoc.properties as any)['test'] = doc; const d = AsyncapiV2Schema.toSchema(doc) as AsyncapiV2Schema; @@ -16,16 +22,26 @@ describe('AsyncapiV2Schema', () => { expect(typeof d2).toEqual('object'); }); test('should handle external documentation', () => { - const doc = { type: 'string', properties: { test: { type: 'string' } }, externalDocs: { description: 'test' } }; + const doc = { + type: 'string', + properties: { test: { type: 'string' } }, + externalDocs: { description: 'test' } + }; const d = AsyncapiV2Schema.toSchema(doc) as AsyncapiV2Schema; expect(typeof d).toEqual('object'); const d2 = AsyncapiV2Schema.toSchema(d as any) as AsyncapiV2Schema; expect(typeof d2).toEqual('object'); - expect(d.externalDocs instanceof AsyncapiV2ExternalDocumentation).toEqual(true); + expect(d.externalDocs instanceof AsyncapiV2ExternalDocumentation).toEqual( + true + ); expect(d.externalDocs!.description).toEqual('test'); }); test('should never return the same instance of external documentation', () => { - const doc = { type: 'string', properties: { test: { type: 'string' } }, externalDocs: { description: 'test' } }; + const doc = { + type: 'string', + properties: { test: { type: 'string' } }, + externalDocs: { description: 'test' } + }; const d = AsyncapiV2Schema.toSchema(doc) as AsyncapiV2Schema; expect(typeof d).toEqual('object'); const d2 = AsyncapiV2Schema.toSchema(d as any) as AsyncapiV2Schema; @@ -35,29 +51,31 @@ describe('AsyncapiV2Schema', () => { expect(d2.externalDocs!.description).not.toEqual('test2'); }); test('should never return the same instance of properties', () => { - const doc = { type: 'string', properties: {test: {type: 'string'}} }; + const doc = { type: 'string', properties: { test: { type: 'string' } } }; const d = AsyncapiV2Schema.toSchema(doc) as AsyncapiV2Schema; expect(typeof d).toEqual('object'); const d2 = AsyncapiV2Schema.toSchema(d as any) as AsyncapiV2Schema; expect(typeof d2).toEqual('object'); (d.properties!['test'] as AsyncapiV2Schema).$id = 'test'; expect((d.properties!['test'] as AsyncapiV2Schema).$id).toEqual('test'); - expect((d2.properties!['test'] as AsyncapiV2Schema).$id).not.toEqual('test'); + expect((d2.properties!['test'] as AsyncapiV2Schema).$id).not.toEqual( + 'test' + ); }); test('should never return the same instance of items', () => { - const doc = { type: 'string', items: [{type: 'string'}] }; + const doc = { type: 'string', items: [{ type: 'string' }] }; const d = AsyncapiV2Schema.toSchema(doc) as AsyncapiV2Schema; expect(typeof d).toEqual('object'); const d2 = AsyncapiV2Schema.toSchema(d as any) as AsyncapiV2Schema; expect(typeof d2).toEqual('object'); - const d_items : AsyncapiV2Schema[] = d.items as AsyncapiV2Schema[]; - const d2_items : AsyncapiV2Schema[] = d2.items as AsyncapiV2Schema[]; + const d_items: AsyncapiV2Schema[] = d.items as AsyncapiV2Schema[]; + const d2_items: AsyncapiV2Schema[] = d2.items as AsyncapiV2Schema[]; d_items[0].$id = 'test'; expect(d_items[0].$id).toEqual('test'); expect(d2_items[0].$id).not.toEqual('test'); }); test('should never convert value properties', () => { - const doc = { const: { test: { type: 'string'} } }; + const doc = { const: { test: { type: 'string' } } }; const d = AsyncapiV2Schema.toSchema(doc) as AsyncapiV2Schema; expect(typeof d).toEqual('object'); expect(d.const instanceof AsyncapiV2Schema).toEqual(false); diff --git a/test/models/CommonModel.spec.ts b/test/models/CommonModel.spec.ts index d69522a2d6..ba3a17c1b4 100644 --- a/test/models/CommonModel.spec.ts +++ b/test/models/CommonModel.spec.ts @@ -1,4 +1,4 @@ -import {CommonModel} from '../../src/models/CommonModel'; +import { CommonModel } from '../../src/models/CommonModel'; describe('CommonModel', () => { describe('$id', () => { test('should return a string', () => { @@ -28,7 +28,7 @@ describe('CommonModel', () => { expect(typeof d.type).toEqual('string'); expect(d.type).toEqual(doc.type); }); - + test('should return an array of strings', () => { const doc = { type: ['number', 'string'] }; const d = CommonModel.toCommonModel(doc); @@ -46,7 +46,7 @@ describe('CommonModel', () => { expect(d.items instanceof CommonModel).toEqual(true); expect(d.items).toEqual(doc.items); }); - + test('should return an array of CommonModel objects', () => { const doc = { items: [{ type: 'string' }, { type: 'number' }] }; const d = CommonModel.toCommonModel(doc); @@ -82,7 +82,7 @@ describe('CommonModel', () => { expect(d.additionalProperties!.constructor.name).toEqual('CommonModel'); expect(d.additionalProperties!).toEqual(doc.additionalProperties); }); - + test('should return a boolean', () => { const doc = { additionalProperties: true }; const d = CommonModel.toCommonModel(doc); @@ -90,7 +90,7 @@ describe('CommonModel', () => { expect(typeof d.additionalProperties).toEqual('boolean'); expect(d.additionalProperties).toEqual(doc.additionalProperties); }); - + test('should return undefined when not defined', () => { const doc = {}; const d = CommonModel.toCommonModel(doc); @@ -107,14 +107,14 @@ describe('CommonModel', () => { expect(d.additionalItems!.constructor.name).toEqual('CommonModel'); expect(d.additionalItems).toEqual(doc.additionalItems); }); - + test('should return undefined when not defined', () => { const doc = {}; const d = CommonModel.toCommonModel(doc); expect(typeof d).toEqual('object'); expect(d.additionalItems).toEqual(undefined); }); - + test('should return undefined when undefined', () => { const doc = { additionalItems: undefined }; const d = CommonModel.toCommonModel(doc); @@ -122,15 +122,6 @@ describe('CommonModel', () => { expect(d.additionalItems).toEqual(undefined); }); }); - describe('$ref', () => { - test('should return a string ', () => { - const doc = { $ref: 'some/reference' }; - const d = CommonModel.toCommonModel(doc); - expect(d.$ref).not.toBeUndefined(); - expect(typeof d.$ref).toEqual('string'); - expect(d.$ref).toEqual(doc.$ref); - }); - }); describe('extend', () => { test('should return a string ', () => { const doc = { extend: 'reference' }; @@ -150,7 +141,7 @@ describe('CommonModel', () => { }); describe('toCommonModel', () => { test('should never return the same instance of properties', () => { - const doc = { type: 'string', properties: {test: {type: 'string'}} }; + const doc = { type: 'string', properties: { test: { type: 'string' } } }; const d = CommonModel.toCommonModel(doc); const d2 = CommonModel.toCommonModel(d); d.properties!['test'].$id = 'test'; @@ -158,11 +149,11 @@ describe('CommonModel', () => { expect(d2.properties!['test'].$id).not.toEqual('test'); }); test('should never return the same instance of items', () => { - const doc = { type: 'string', items: [{type: 'string'}] }; + const doc = { type: 'string', items: [{ type: 'string' }] }; const d = CommonModel.toCommonModel(doc); const d2 = CommonModel.toCommonModel(d); - const d_items : CommonModel[] = d.items as CommonModel[]; - const d2_items : CommonModel[] = d2.items as CommonModel[]; + const d_items: CommonModel[] = d.items as CommonModel[]; + const d2_items: CommonModel[] = d2.items as CommonModel[]; d_items[0].$id = 'test'; expect(d_items[0].$id).toEqual('test'); expect(d2_items[0].$id).not.toEqual('test'); @@ -170,7 +161,7 @@ describe('CommonModel', () => { }); describe('mergeCommonModels', () => { test('should handle recursive models', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); doc1.properties = { recursive: doc1 @@ -186,7 +177,7 @@ describe('CommonModel', () => { }); describe('$id', () => { test('should be merged when only right side is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc2.$id = 'test'; @@ -194,7 +185,7 @@ describe('CommonModel', () => { expect(doc1.$id).toEqual(doc2.$id); }); test('should not be merged when both sides are defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc2.$id = 'test'; @@ -204,7 +195,7 @@ describe('CommonModel', () => { expect(doc1.$id).toEqual('temp'); }); test('should not change if nothing is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); @@ -213,7 +204,7 @@ describe('CommonModel', () => { }); describe('required', () => { test('should contain the same if right side is not defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1.required = ['test']; @@ -221,7 +212,7 @@ describe('CommonModel', () => { expect(doc1.required).toEqual(['test']); }); test('should be merged when only right side is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc2.required = ['test']; @@ -229,7 +220,7 @@ describe('CommonModel', () => { expect(doc1.required).toEqual(doc2.required); }); test('should be merged when both sides are defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1.required = ['test']; @@ -238,7 +229,7 @@ describe('CommonModel', () => { expect(doc1.required).toEqual(['test', 'test2']); }); test('should only contain one if duplicate', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1.required = ['test']; @@ -247,42 +238,16 @@ describe('CommonModel', () => { expect(doc1.required).toEqual(['test']); }); test('should not change if nothing is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); expect(doc1.required).toBeUndefined(); }); }); - describe('$ref', () => { - test('should be merged when only right side is defined', () => { - const doc = { }; - let doc1 = CommonModel.toCommonModel(doc); - const doc2 = CommonModel.toCommonModel(doc); - doc2.$ref = 'test'; - doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.$ref).toEqual(doc2.$ref); - }); - test('should be merged when both sides are defined', () => { - const doc = { }; - let doc1 = CommonModel.toCommonModel(doc); - const doc2 = CommonModel.toCommonModel(doc); - doc2.$id = 'test'; - doc1.$id = 'temp'; - doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.$ref).toEqual(doc2.$ref); - }); - test('should not change if nothing is defined', () => { - const doc = { }; - let doc1 = CommonModel.toCommonModel(doc); - const doc2 = CommonModel.toCommonModel(doc); - doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.$ref).toBeUndefined(); - }); - }); describe('extend', () => { test('should be merged when only right side is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc2.extend = ['test']; @@ -290,7 +255,7 @@ describe('CommonModel', () => { expect(doc1.extend).toEqual(doc2.extend); }); test('should be merged when both sides are defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc2.$id = 'test'; @@ -299,7 +264,7 @@ describe('CommonModel', () => { expect(doc1.extend).toEqual(doc2.extend); }); test('should not change if nothing is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); @@ -308,7 +273,7 @@ describe('CommonModel', () => { }); describe('type', () => { test('should be merged when only right side is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc2.type = ['string']; @@ -316,7 +281,7 @@ describe('CommonModel', () => { expect(doc1.type).toEqual(doc2.type); }); test('should be merged when both sides are defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc2.type = ['string']; @@ -325,7 +290,7 @@ describe('CommonModel', () => { expect(doc1.type).toEqual(['number', 'string']); }); test('should not change if nothing is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); @@ -334,7 +299,7 @@ describe('CommonModel', () => { }); describe('enum', () => { test('should be merged when only right side is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc2.enum = ['string']; @@ -342,7 +307,7 @@ describe('CommonModel', () => { expect(doc1.enum).toEqual(doc2.enum); }); test('Should not contain duplicate values', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc2.enum = ['string']; @@ -351,7 +316,7 @@ describe('CommonModel', () => { expect(doc1.enum).toEqual(['string']); }); test('should be merged when both sides are defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc2.enum = ['string']; @@ -360,7 +325,7 @@ describe('CommonModel', () => { expect(doc1.enum).toEqual(['number', 'string']); }); test('should not change if nothing is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); @@ -369,75 +334,81 @@ describe('CommonModel', () => { }); describe('items', () => { test('should be merged when only right side is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.items = CommonModel.toCommonModel({type: 'string'}); + doc2.items = CommonModel.toCommonModel({ type: 'string' }); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); expect(doc1.items).toEqual(doc2.items); }); test('should be merged when only left side is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc1.items = CommonModel.toCommonModel({type: 'string'}); + doc1.items = CommonModel.toCommonModel({ type: 'string' }); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.items).toMatchObject({type: 'string'}); + expect(doc1.items).toMatchObject({ type: 'string' }); }); test('should handle empty items', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); expect(doc1.items).toBeUndefined(); }); test('should be merged when both sides are defined as schemas', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.items = CommonModel.toCommonModel({type: 'string'}); - doc1.items = CommonModel.toCommonModel({type: 'number'}); + doc2.items = CommonModel.toCommonModel({ type: 'string' }); + doc1.items = CommonModel.toCommonModel({ type: 'number' }); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.items).toMatchObject({type: ['number', 'string']}); + expect(doc1.items).toMatchObject({ type: ['number', 'string'] }); }); test('should not do anything if right side is an empty array', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc2.items = []; - doc1.items = CommonModel.toCommonModel({type: 'number'}); + doc1.items = CommonModel.toCommonModel({ type: 'number' }); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.items).toMatchObject({type: 'number'}); + expect(doc1.items).toMatchObject({ type: 'number' }); }); test('Should handle left side is a tuple and right side is not', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.items = CommonModel.toCommonModel({type: 'string'}); - doc1.items = [CommonModel.toCommonModel({type: 'number'})]; + doc2.items = CommonModel.toCommonModel({ type: 'string' }); + doc1.items = [CommonModel.toCommonModel({ type: 'number' })]; doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.items).toMatchObject([{type: 'number'}]); + expect(doc1.items).toMatchObject([{ type: 'number' }]); }); test('Should handle right side is a tuple and left side is not', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.items = [CommonModel.toCommonModel({type: 'string'})]; - doc1.items = CommonModel.toCommonModel({type: 'number'}); + doc2.items = [CommonModel.toCommonModel({ type: 'string' })]; + doc1.items = CommonModel.toCommonModel({ type: 'number' }); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.items).toMatchObject([{type: 'string'}]); + expect(doc1.items).toMatchObject([{ type: 'string' }]); }); test('should be merged when both sides are defined as array of schemas with different lengths', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.items = [CommonModel.toCommonModel({type: 'string'}), CommonModel.toCommonModel({type: 'boolean'})]; - doc1.items = [CommonModel.toCommonModel({type: 'number'})]; + doc2.items = [ + CommonModel.toCommonModel({ type: 'string' }), + CommonModel.toCommonModel({ type: 'boolean' }) + ]; + doc1.items = [CommonModel.toCommonModel({ type: 'number' })]; doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.items).toMatchObject([{type: ['number', 'string']}, {type: 'boolean'}]); + expect(doc1.items).toMatchObject([ + { type: ['number', 'string'] }, + { type: 'boolean' } + ]); }); test('should not change if nothing is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); @@ -447,131 +418,278 @@ describe('CommonModel', () => { describe('properties', () => { test('should be merged when only right side is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.properties = {testProp: CommonModel.toCommonModel({type: 'string'})}; + doc2.properties = { + testProp: CommonModel.toCommonModel({ type: 'string' }) + }; doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); expect(doc1.properties).toEqual(doc2.properties); }); test('should be merged when both sides are defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.properties = {testProp: CommonModel.toCommonModel({type: 'string'})}; - doc1.properties = {testProp2: CommonModel.toCommonModel({type: 'number'})}; + doc2.properties = { + testProp: CommonModel.toCommonModel({ type: 'string' }) + }; + doc1.properties = { + testProp2: CommonModel.toCommonModel({ type: 'number' }) + }; doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.properties).toEqual({testProp: {type: 'string'}, testProp2: {type: 'number'}}); + expect(doc1.properties).toEqual({ + testProp: { type: 'string' }, + testProp2: { type: 'number' } + }); }); test('should be merged together when both sides are defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.properties = {testProp: CommonModel.toCommonModel({type: 'string'})}; - doc1.properties = {testProp: CommonModel.toCommonModel({type: 'number'})}; + doc2.properties = { + testProp: CommonModel.toCommonModel({ type: 'string' }) + }; + doc1.properties = { + testProp: CommonModel.toCommonModel({ type: 'number' }) + }; doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.properties).toEqual({testProp: {type: ['number', 'string'], originalInput: {}}}); + expect(doc1.properties).toEqual({ + testProp: { type: ['number', 'string'], originalInput: {} } + }); }); test('should not change if nothing is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); expect(doc1.properties).toBeUndefined(); }); + test('should not carry over properties to other models', () => { + const petModel = CommonModel.toCommonModel({ + title: 'Pet', + type: 'object', + discriminator: 'petType', + properties: { + petType: { + $id: 'PetType', + type: 'string' + }, + name: { + type: 'string' + } + } + }); + + const cat = {}; + const catModel = CommonModel.toCommonModel(cat); + CommonModel.mergeCommonModels(catModel, petModel, cat); + CommonModel.mergeCommonModels( + catModel, + CommonModel.toCommonModel({ + title: 'Cat', + properties: { + petType: { + const: 'Cat' + }, + huntingSkill: { + type: 'string' + } + } + }), + cat + ); + + const dog = {}; + const dogModel = CommonModel.toCommonModel(dog); + CommonModel.mergeCommonModels(dogModel, petModel, dog); + CommonModel.mergeCommonModels( + dogModel, + CommonModel.toCommonModel({ + title: 'Dog', + properties: { + petType: { + const: 'Dog' + }, + packSize: { + type: 'integer' + } + } + }), + dog + ); + + expect(catModel.properties).toHaveProperty('petType'); + expect(catModel.properties?.petType.$id).toBe('PetType'); + expect(catModel.properties).toHaveProperty('name'); + expect(catModel.properties).toHaveProperty('huntingSkill'); + expect(catModel.properties).not.toHaveProperty('packSize'); + + expect(dogModel.properties).toHaveProperty('petType'); + expect(dogModel.properties?.petType.$id).toBe('PetType'); + expect(dogModel.properties).toHaveProperty('name'); + expect(dogModel.properties).toHaveProperty('packSize'); + expect(dogModel.properties).not.toHaveProperty('huntingSkill'); + }); }); - describe('additionalProperties', () => { + + describe('patternProperties', () => { test('should be merged when only right side is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.additionalProperties = CommonModel.toCommonModel({type: 'string'}); + doc2.patternProperties = { + pattern1: CommonModel.toCommonModel({ type: 'string' }) + }; doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.additionalProperties).toEqual(doc2.additionalProperties); + expect(doc1.patternProperties).toEqual(doc2.patternProperties); + }); + test('should be merged when both sides are defined', () => { + const doc = {}; + let doc1 = CommonModel.toCommonModel(doc); + const doc2 = CommonModel.toCommonModel(doc); + doc2.patternProperties = { + pattern1: CommonModel.toCommonModel({ type: 'string' }) + }; + doc1.patternProperties = { + pattern2: CommonModel.toCommonModel({ type: 'number' }) + }; + doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); + expect(doc1.patternProperties).toEqual({ + pattern1: { type: 'string' }, + pattern2: { type: 'number' } + }); }); test('should be merged together when both sides are defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.additionalProperties = CommonModel.toCommonModel({type: 'string'}); - doc1.additionalProperties = CommonModel.toCommonModel({type: 'number'}); + doc2.patternProperties = { + pattern1: CommonModel.toCommonModel({ type: 'string' }) + }; + doc1.patternProperties = { + pattern1: CommonModel.toCommonModel({ type: 'number' }) + }; doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.additionalProperties).toEqual({type: ['number', 'string'], originalInput: {}}); + expect(doc1.patternProperties).toEqual({ + pattern1: { type: ['number', 'string'], originalInput: {} } + }); }); test('should not change if nothing is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.additionalProperties).toBeUndefined(); + expect(doc1.patternProperties).toBeUndefined(); }); }); - describe('additionalItems', () => { + + describe('addPatternProperty', () => { + beforeAll(() => { + jest.spyOn(CommonModel, 'mergeCommonModels'); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + test('should add patternProperty to model', () => { + const patternPropertyModel = new CommonModel(); + const pattern = 'TestPattern'; + patternPropertyModel.$id = 'test'; + const model = new CommonModel(); + model.addPatternProperty(pattern, patternPropertyModel, {}); + expect(model.patternProperties).toEqual({ + TestPattern: patternPropertyModel + }); + expect(CommonModel.mergeCommonModels).not.toHaveBeenCalled(); + }); + test('should merge additionalProperties together', () => { + const patternPropertyModel = new CommonModel(); + const pattern = 'TestPattern'; + patternPropertyModel.$id = 'test'; + const model = new CommonModel(); + model.addPatternProperty(pattern, patternPropertyModel, {}); + model.addPatternProperty(pattern, patternPropertyModel, {}); + expect(model.patternProperties).toEqual({ + TestPattern: patternPropertyModel + }); + expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith( + 1, + patternPropertyModel, + patternPropertyModel, + {} + ); + }); + }); + + describe('additionalProperties', () => { test('should be merged when only right side is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.additionalItems = CommonModel.toCommonModel({type: 'string'}); + doc2.additionalProperties = CommonModel.toCommonModel({ + type: 'string' + }); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.additionalItems).toEqual(doc2.additionalItems); + expect(doc1.additionalProperties).toEqual(doc2.additionalProperties); }); test('should be merged together when both sides are defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.additionalItems = CommonModel.toCommonModel({type: 'string'}); - doc1.additionalItems = CommonModel.toCommonModel({type: 'number'}); + doc2.additionalProperties = CommonModel.toCommonModel({ + type: 'string' + }); + doc1.additionalProperties = CommonModel.toCommonModel({ + type: 'number' + }); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.additionalItems).toEqual({type: ['number', 'string'], originalInput: {}}); + expect(doc1.additionalProperties).toEqual({ + type: ['number', 'string'], + originalInput: {} + }); }); test('should not change if nothing is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.additionalItems).toBeUndefined(); + expect(doc1.additionalProperties).toBeUndefined(); }); }); - - describe('patternProperties', () => { + describe('additionalItems', () => { test('should be merged when only right side is defined', () => { - const doc = { }; - let doc1 = CommonModel.toCommonModel(doc); - const doc2 = CommonModel.toCommonModel(doc); - doc2.patternProperties = {pattern1: CommonModel.toCommonModel({type: 'string'})}; - doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.patternProperties).toEqual(doc2.patternProperties); - }); - test('should be merged when both sides are defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.patternProperties = {pattern1: CommonModel.toCommonModel({type: 'string'})}; - doc1.patternProperties = {pattern2: CommonModel.toCommonModel({type: 'number'})}; + doc2.additionalItems = CommonModel.toCommonModel({ type: 'string' }); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.patternProperties).toEqual({pattern1: {type: 'string'}, pattern2: {type: 'number'}}); + expect(doc1.additionalItems).toEqual(doc2.additionalItems); }); test('should be merged together when both sides are defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.patternProperties = {pattern1: CommonModel.toCommonModel({type: 'string'})}; - doc1.patternProperties = {pattern1: CommonModel.toCommonModel({type: 'number'})}; + doc2.additionalItems = CommonModel.toCommonModel({ type: 'string' }); + doc1.additionalItems = CommonModel.toCommonModel({ type: 'number' }); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.patternProperties).toEqual({pattern1: {type: ['number', 'string'], originalInput: {}}}); + expect(doc1.additionalItems).toEqual({ + type: ['number', 'string'], + originalInput: {} + }); }); test('should not change if nothing is defined', () => { - const doc = { }; + const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); - expect(doc1.patternProperties).toBeUndefined(); + expect(doc1.additionalItems).toBeUndefined(); }); }); }); - + describe('addItem', () => { beforeAll(() => { + jest.clearAllMocks(); jest.spyOn(CommonModel, 'mergeCommonModels'); }); afterEach(() => { @@ -579,25 +697,31 @@ describe('CommonModel', () => { }); test('should add items to model', () => { const itemModel = new CommonModel(); - itemModel.$id = 'test'; - const model = new CommonModel(); + itemModel.$id = 'test'; + const model = new CommonModel(); model.addItem(itemModel, {}); expect(model.items).toEqual(itemModel); expect(CommonModel.mergeCommonModels).not.toHaveBeenCalled(); }); test('should merge items together', () => { const itemModel = new CommonModel(); - itemModel.$id = 'test'; + itemModel.$id = 'test'; const model = new CommonModel(); model.items = itemModel; model.addItem(itemModel, {}); model.addItem(itemModel, {}); expect(model.items).toEqual(itemModel); - expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith(1, itemModel, itemModel, {}); + expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith( + 1, + itemModel, + itemModel, + {} + ); }); }); describe('addItemTuple', () => { beforeAll(() => { + jest.clearAllMocks(); jest.spyOn(CommonModel, 'mergeCommonModels'); }); afterEach(() => { @@ -605,25 +729,31 @@ describe('CommonModel', () => { }); test('should add tuple item to model', () => { const itemModel = new CommonModel(); - itemModel.$id = 'test'; - const model = new CommonModel(); + itemModel.$id = 'test'; + const model = new CommonModel(); model.addItemTuple(itemModel, {}, 0); expect(model.items).toEqual([itemModel]); expect(CommonModel.mergeCommonModels).not.toHaveBeenCalled(); }); test('should merge tuple item if same index', () => { const itemModel = new CommonModel(); - itemModel.$id = 'test'; + itemModel.$id = 'test'; const model = new CommonModel(); model.items = itemModel; model.addItemTuple(itemModel, {}, 0); model.addItemTuple(itemModel, {}, 0); expect(model.items).toEqual([itemModel]); - expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith(1, itemModel, itemModel, {}); + expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith( + 1, + itemModel, + itemModel, + {} + ); }); }); describe('addProperty', () => { beforeAll(() => { + jest.clearAllMocks(); jest.spyOn(CommonModel, 'mergeCommonModels'); }); afterEach(() => { @@ -631,42 +761,47 @@ describe('CommonModel', () => { }); test('should add property to model', () => { const propertyModel = new CommonModel(); - propertyModel.$id = 'test'; - const model = new CommonModel(); + propertyModel.$id = 'test'; + const model = new CommonModel(); model.addProperty('test', propertyModel, {}); - expect(model.properties).toEqual({test: propertyModel}); + expect(model.properties).toEqual({ test: propertyModel }); expect(CommonModel.mergeCommonModels).not.toHaveBeenCalled(); }); test('should merge if already existing property', () => { const propertyModel = new CommonModel(); - propertyModel.$id = 'test'; - const model = new CommonModel(); + propertyModel.$id = 'test'; + const model = new CommonModel(); model.properties = { test: propertyModel }; model.addProperty('test', propertyModel, {}); - expect(model.properties).toEqual({test: propertyModel}); - expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith(1, propertyModel, propertyModel, {}); + expect(model.properties).toEqual({ test: propertyModel }); + expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith( + 1, + propertyModel, + propertyModel, + {} + ); }); }); describe('addExtendedModel', () => { test('should extend model', () => { const extendedModel = new CommonModel(); - extendedModel.$id = 'test'; - const model = new CommonModel(); + extendedModel.$id = 'test'; + const model = new CommonModel(); model.addExtendedModel(extendedModel); expect(model.extend).toEqual(['test']); }); test('should ignore model if it has no $id', () => { const extendedModel = new CommonModel(); - const model = new CommonModel(); + const model = new CommonModel(); model.addExtendedModel(extendedModel); expect(model.extend).toBeUndefined(); }); test('should ignore duplicate model $id', () => { const extendedModel = new CommonModel(); - extendedModel.$id = 'test'; - const model = new CommonModel(); + extendedModel.$id = 'test'; + const model = new CommonModel(); model.addExtendedModel(extendedModel); model.addExtendedModel(extendedModel); expect(model.extend).toEqual(['test']); @@ -674,33 +809,33 @@ describe('CommonModel', () => { }); describe('setTypes', () => { test('should set multiple types', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.setType(['type1', 'type2']); expect(model.type).toEqual(['type1', 'type2']); }); test('should set array type as regular type with length 1', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.setType(['type']); expect(model.type).toEqual('type'); }); test('should set type undefined with array of length 0', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.setType([]); expect(model.type).toBeUndefined(); }); test('should set type as is', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.setType('type'); expect(model.type).toEqual('type'); }); test('should set type overwriting existing type', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.type = ['type1']; model.setType('type2'); expect(model.type).toEqual('type2'); }); test('should overwrite already sat type', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.setType('type1'); model.setType('type2'); expect(model.type).toEqual('type2'); @@ -709,6 +844,7 @@ describe('CommonModel', () => { describe('addAdditionalProperty', () => { beforeAll(() => { + jest.clearAllMocks(); jest.spyOn(CommonModel, 'mergeCommonModels'); }); afterEach(() => { @@ -716,25 +852,31 @@ describe('CommonModel', () => { }); test('should add additionalProperties to model', () => { const additionalPropertiesModel = new CommonModel(); - additionalPropertiesModel.$id = 'test'; - const model = new CommonModel(); + additionalPropertiesModel.$id = 'test'; + const model = new CommonModel(); model.addAdditionalProperty(additionalPropertiesModel, {}); expect(model.additionalProperties).toEqual(additionalPropertiesModel); expect(CommonModel.mergeCommonModels).not.toHaveBeenCalled(); }); test('should merge additionalProperties together', () => { const additionalPropertiesModel = new CommonModel(); - additionalPropertiesModel.$id = 'test'; - const model = new CommonModel(); + additionalPropertiesModel.$id = 'test'; + const model = new CommonModel(); model.addAdditionalProperty(additionalPropertiesModel, {}); model.addAdditionalProperty(additionalPropertiesModel, {}); expect(model.additionalProperties).toEqual(additionalPropertiesModel); - expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith(1, additionalPropertiesModel, additionalPropertiesModel, {}); + expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith( + 1, + additionalPropertiesModel, + additionalPropertiesModel, + {} + ); }); }); describe('addAdditionalItems', () => { beforeAll(() => { + jest.clearAllMocks(); jest.spyOn(CommonModel, 'mergeCommonModels'); }); afterEach(() => { @@ -742,58 +884,36 @@ describe('CommonModel', () => { }); test('should add additionalItems to model', () => { const additionalItemsModel = new CommonModel(); - additionalItemsModel.$id = 'test'; - const model = new CommonModel(); + additionalItemsModel.$id = 'test'; + const model = new CommonModel(); model.addAdditionalItems(additionalItemsModel, {}); expect(model.additionalItems).toEqual(additionalItemsModel); expect(CommonModel.mergeCommonModels).not.toHaveBeenCalled(); }); test('should merge additionalItems together', () => { const additionalItemsModel = new CommonModel(); - additionalItemsModel.$id = 'test'; - const model = new CommonModel(); + additionalItemsModel.$id = 'test'; + const model = new CommonModel(); model.addAdditionalItems(additionalItemsModel, {}); model.addAdditionalItems(additionalItemsModel, {}); expect(model.additionalItems).toEqual(additionalItemsModel); - expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith(1, additionalItemsModel, additionalItemsModel, {}); - }); - }); - describe('addPatternProperty', () => { - beforeAll(() => { - jest.spyOn(CommonModel, 'mergeCommonModels'); - }); - afterEach(() => { - jest.clearAllMocks(); - }); - test('should add patternProperty to model', () => { - const patternPropertyModel = new CommonModel(); - const pattern = 'TestPattern'; - patternPropertyModel.$id = 'test'; - const model = new CommonModel(); - model.addPatternProperty(pattern, patternPropertyModel, {}); - expect(model.patternProperties).toEqual({TestPattern: patternPropertyModel}); - expect(CommonModel.mergeCommonModels).not.toHaveBeenCalled(); - }); - test('should merge additionalProperties together', () => { - const patternPropertyModel = new CommonModel(); - const pattern = 'TestPattern'; - patternPropertyModel.$id = 'test'; - const model = new CommonModel(); - model.addPatternProperty(pattern, patternPropertyModel, {}); - model.addPatternProperty(pattern, patternPropertyModel, {}); - expect(model.patternProperties).toEqual({TestPattern: patternPropertyModel}); - expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith(1, patternPropertyModel, patternPropertyModel, {}); + expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith( + 1, + additionalItemsModel, + additionalItemsModel, + {} + ); }); }); describe('addEnum', () => { test('should add enum', () => { - const model = new CommonModel(); + const model = new CommonModel(); const enumToAdd = 'test'; model.addEnum(enumToAdd); expect(model.enum).toEqual([enumToAdd]); }); test('should not add enum if it already exist', () => { - const model = new CommonModel(); + const model = new CommonModel(); const enumToAdd = 'test'; model.addEnum(enumToAdd); model.addEnum(enumToAdd); @@ -802,23 +922,23 @@ describe('CommonModel', () => { }); describe('addTypes', () => { test('should add multiple types', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.addTypes(['type1', 'type2']); expect(model.type).toEqual(['type1', 'type2']); }); test('should add type as is', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.addTypes('type'); expect(model.type).toEqual('type'); }); test('should add type to existing type', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.type = ['type1']; model.addTypes('type2'); expect(model.type).toEqual(['type1', 'type2']); }); test('should set an array when adding two types', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.addTypes('type1'); model.addTypes('type2'); expect(model.type).toEqual(['type1', 'type2']); @@ -827,19 +947,19 @@ describe('CommonModel', () => { describe('removeType', () => { test('should remove single type', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.addTypes('type1'); model.removeType('type1'); expect(model.type).toBeUndefined(); }); test('should not remove non matching type', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.addTypes('type'); model.removeType('type1'); expect(model.type).toEqual('type'); }); test('should remove multiple types', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.addTypes(['type1', 'type2']); model.removeType(['type1', 'type2']); expect(model.type).toBeUndefined(); @@ -847,19 +967,19 @@ describe('CommonModel', () => { }); describe('removeEnum', () => { test('should remove single enum', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.addEnum('enum1'); model.removeEnum('enum1'); expect(model.enum).toBeUndefined(); }); test('should not remove non matching enum', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.addEnum('enum'); model.removeEnum('enum1'); expect(model.enum).toEqual(['enum']); }); test('should remove multiple enums', () => { - const model = new CommonModel(); + const model = new CommonModel(); model.addEnum('enum1'); model.addEnum('enum2'); model.removeEnum(['enum1', 'enum2']); @@ -879,40 +999,15 @@ describe('CommonModel', () => { describe('isRequired', () => { test('check that property is required', () => { - const doc = { type: 'object', properties: { prop: { type: 'string' } } }; + const doc = { + type: 'object', + properties: { prop: { type: 'string' } } + }; const d = CommonModel.toCommonModel(doc); d.required = ['prop']; expect(d.isRequired('prop')).toEqual(true); expect(d.isRequired('propX')).toEqual(false); }); }); - - describe('getNearestDependencies', () => { - test('should work with array of items', () => { - const doc = { items: [{ $ref: '1' }] }; - const d = CommonModel.toCommonModel(doc); - expect(d.getNearestDependencies()).toEqual(['1']); - }); - test('check that all dependencies are returned', () => { - const doc = { additionalProperties: { $ref: '1' }, extend: ['2'], items: { $ref: '3' }, properties: { testProp: { $ref: '4' } }, patternProperties: {testPattern: {$ref: '5'}}, additionalItems: { $ref: '6' } }; - const d = CommonModel.toCommonModel(doc); - expect(d.getNearestDependencies()).toEqual(['1', '2', '3', '4', '5', '6']); - }); - test('should work with nested items', () => { - const doc = {properties: { testProp: { items: { $ref: '1' } } }, patternProperties: { testPattern: { items: { $ref: '2' } } } }; - const d = CommonModel.toCommonModel(doc); - expect(d.getNearestDependencies()).toEqual(['1', '2']); - }); - test('check that no dependencies is returned if there are none', () => { - const doc = { }; - const d = CommonModel.toCommonModel(doc); - expect(d.getNearestDependencies()).toEqual([]); - }); - test('should not return duplicate dependencies', () => { - const doc = { properties: { test1: { $ref: '1' }, test2: { $ref: '1' } } }; - const d = CommonModel.toCommonModel(doc); - expect(d.getNearestDependencies()).toEqual(['1']); - }); - }); }); }); diff --git a/test/models/ConstrainedMetaModel.spec.ts b/test/models/ConstrainedMetaModel.spec.ts new file mode 100644 index 0000000000..aadad816d8 --- /dev/null +++ b/test/models/ConstrainedMetaModel.spec.ts @@ -0,0 +1,581 @@ +import { constrainMetaModel } from '../../src/helpers'; +import { + AnyModel, + ArrayModel, + BooleanModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedUnionModel, + DictionaryModel, + EnumModel, + EnumValueModel, + FloatModel, + IntegerModel, + ObjectModel, + ObjectPropertyModel, + ReferenceModel, + StringModel, + TupleModel, + TupleValueModel, + UnionModel +} from '../../src/models'; +import { + mockedConstraints, + mockedTypeMapping +} from '../TestUtils/TestConstrainer'; + +describe('ConstrainedMetaModel', () => { + describe('ReferenceModel', () => { + test('should return no dependencies', () => { + const stringModel = new StringModel('', undefined); + const rawModel = new ReferenceModel('', undefined, stringModel); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }); + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(0); + }); + }); + describe('StringModel', () => { + test('should return no dependencies', () => { + const rawModel = new StringModel('', undefined); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }); + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(0); + }); + }); + describe('AnyModel', () => { + test('should return no dependencies', () => { + const rawModel = new AnyModel('', undefined); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }); + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(0); + }); + }); + describe('FloatModel', () => { + test('should return no dependencies', () => { + const rawModel = new FloatModel('', undefined); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }); + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(0); + }); + }); + describe('IntegerModel', () => { + test('should return no dependencies', () => { + const rawModel = new IntegerModel('', undefined); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }); + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(0); + }); + }); + describe('BooleanModel', () => { + test('should return no dependencies', () => { + const rawModel = new BooleanModel('', undefined); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }); + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(0); + }); + }); + describe('TupleModel', () => { + test('should return all reference dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const referenceTupleModel = new TupleValueModel(0, referenceModel); + const stringTupleModel = new TupleValueModel(1, stringModel); + const rawModel = new TupleModel('test', undefined, [ + referenceTupleModel, + stringTupleModel + ]); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedTupleModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.tuple[0].value); + }); + test('should return inner reference dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const unionModel = new UnionModel('union', undefined, [ + stringModel, + referenceModel + ]); + const unionTupleModel = new TupleValueModel(0, unionModel); + const stringTupleModel = new TupleValueModel(1, stringModel); + const rawModel = new TupleModel('test', undefined, [ + unionTupleModel, + stringTupleModel + ]); + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedTupleModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual( + (model.tuple[0].value as ConstrainedUnionModel).union[1] + ); + }); + + test('should not return duplicate dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const referenceTupleModel = new TupleValueModel(0, referenceModel); + const reference2TupleModel = new TupleValueModel(1, referenceModel); + const rawModel = new TupleModel('test', undefined, [ + referenceTupleModel, + reference2TupleModel + ]); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedTupleModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + }); + + test('should not return duplicate dependencies when different reference instances', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const referenceModel2 = new ReferenceModel('', undefined, stringModel); + const referenceTupleModel = new TupleValueModel(0, referenceModel); + const reference2TupleModel = new TupleValueModel(1, referenceModel2); + const rawModel = new TupleModel('test', undefined, [ + referenceTupleModel, + reference2TupleModel + ]); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedTupleModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + }); + }); + describe('ObjectModel', () => { + test('should return inner reference dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const unionModel = new UnionModel('union', undefined, [ + stringModel, + referenceModel + ]); + const unionObjectPropertyModel = new ObjectPropertyModel( + 'union', + false, + unionModel + ); + const rawModel = new ObjectModel('test', undefined, { + union: unionObjectPropertyModel + }); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedObjectModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual( + (model.properties['union'].property as ConstrainedUnionModel).union[1] + ); + }); + test('should return all reference dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const referenceObjectPropertyModel = new ObjectPropertyModel( + 'reference', + false, + referenceModel + ); + const stringObjectPropertyModel = new ObjectPropertyModel( + 'string', + false, + stringModel + ); + const rawModel = new ObjectModel('test', undefined, { + reference: referenceObjectPropertyModel, + string: stringObjectPropertyModel + }); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedObjectModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.properties['reference'].property); + }); + + test('should not return self reference', () => { + const rawModel = new ObjectModel('ObjectTest', undefined, {}); + const referenceModel = new ReferenceModel( + rawModel.name, + undefined, + rawModel + ); + const referenceObjectPropertyModel = new ObjectPropertyModel( + 'self', + false, + referenceModel + ); + rawModel.properties['self'] = referenceObjectPropertyModel; + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedObjectModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(0); + }); + + test('should not return duplicate dependencies', () => { + const stringModel = new StringModel('string', undefined); + const referenceModel = new ReferenceModel( + 'reference', + undefined, + stringModel + ); + const referenceObjectPropertyModel = new ObjectPropertyModel( + 'reference', + false, + referenceModel + ); + const reference2ObjectPropertyModel = new ObjectPropertyModel( + 'reference2', + false, + referenceModel + ); + const rawModel = new ObjectModel('test', undefined, { + reference: referenceObjectPropertyModel, + reference2: reference2ObjectPropertyModel + }); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedObjectModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.properties['reference'].property); + }); + + test('should not return duplicate dependencies when different reference instances', () => { + const stringModel = new StringModel('string', undefined); + const referenceModel = new ReferenceModel( + 'reference', + undefined, + stringModel + ); + const referenceModel2 = new ReferenceModel( + 'reference', + undefined, + stringModel + ); + const referenceObjectPropertyModel = new ObjectPropertyModel( + 'reference', + false, + referenceModel + ); + const reference2ObjectPropertyModel = new ObjectPropertyModel( + 'reference2', + false, + referenceModel2 + ); + const rawModel = new ObjectModel('test', undefined, { + reference: referenceObjectPropertyModel, + reference2: reference2ObjectPropertyModel + }); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedObjectModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.properties['reference'].property); + }); + + describe('containsPropertyType', () => { + test('should find present property type and those who are not', () => { + const stringModel = new ConstrainedStringModel('', undefined, ''); + const stringObjectPropertyModel = new ConstrainedObjectPropertyModel( + 'string', + '', + false, + stringModel + ); + const rawModel = new ConstrainedObjectModel('test', undefined, '', { + string: stringObjectPropertyModel + }); + expect(rawModel.containsPropertyType(ConstrainedStringModel)).toEqual( + true + ); + expect(rawModel.containsPropertyType(ConstrainedBooleanModel)).toEqual( + false + ); + }); + }); + }); + describe('EnumModel', () => { + test('should return no dependencies', () => { + const referenceEnumValueModel = new EnumValueModel( + 'reference', + 'referenceModel' + ); + const stringEnumValueModel = new EnumValueModel('string', 'stringModel'); + const rawModel = new EnumModel('test', undefined, [ + referenceEnumValueModel, + stringEnumValueModel + ]); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedEnumModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(0); + }); + }); + describe('DictionaryModel', () => { + test('should return all reference dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const rawModel = new DictionaryModel( + 'test', + undefined, + referenceModel, + stringModel + ); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedDictionaryModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.key); + }); + + test('should return inner reference dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const unionModel = new UnionModel('union', undefined, [ + stringModel, + referenceModel + ]); + const rawModel = new DictionaryModel( + 'test', + undefined, + unionModel, + stringModel + ); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedDictionaryModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual( + (model.key as ConstrainedUnionModel).union[1] + ); + }); + + test('should not return duplicate dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const rawModel = new DictionaryModel( + 'test', + undefined, + referenceModel, + referenceModel + ); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedDictionaryModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.key); + }); + + test('should not return duplicate dependencies when different reference instances', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const referenceModel2 = new ReferenceModel('', undefined, stringModel); + const rawModel = new DictionaryModel( + 'test', + undefined, + referenceModel, + referenceModel2 + ); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedDictionaryModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.key); + }); + }); + describe('ArrayModel', () => { + test('should return all reference dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const rawModel = new ArrayModel('test', undefined, referenceModel); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedArrayModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.valueModel); + }); + test('should return nothing if no references are used', () => { + const stringModel = new StringModel('', undefined); + const rawModel = new ArrayModel('', undefined, stringModel); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }); + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(0); + }); + + test('should return inner reference dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const unionModel = new UnionModel('union', undefined, [ + stringModel, + referenceModel + ]); + const rawModel = new ArrayModel('', undefined, unionModel); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedArrayModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual( + (model.valueModel as ConstrainedUnionModel).union[1] + ); + }); + }); + describe('UnionModel', () => { + test('should return all reference dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const rawModel = new UnionModel('test', undefined, [ + referenceModel, + stringModel + ]); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedUnionModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.union[0]); + }); + + test('should not return duplicate dependencies', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const rawModel = new UnionModel('test', undefined, [ + referenceModel, + referenceModel + ]); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedUnionModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.union[0]); + }); + + test('should not return duplicate dependencies when different reference instances', () => { + const stringModel = new StringModel('', undefined); + const referenceModel = new ReferenceModel('', undefined, stringModel); + const referenceModel2 = new ReferenceModel('', undefined, stringModel); + const rawModel = new UnionModel('test', undefined, [ + referenceModel, + referenceModel2 + ]); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined + }) as ConstrainedUnionModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.union[0]); + }); + }); +}); diff --git a/test/models/Draft4Schema.spec.ts b/test/models/Draft4Schema.spec.ts index 31cd30f96d..5d9c3ed664 100644 --- a/test/models/Draft4Schema.spec.ts +++ b/test/models/Draft4Schema.spec.ts @@ -1,13 +1,16 @@ -import {Draft4Schema} from '../../src/models'; +import { Draft4Schema } from '../../src/models'; describe('Draft4Schema', () => { describe('toSchema', () => { test('should throw error when trying to convert non-object', () => { - const expectedError = 'Could not convert input to expected copy of Draft4Schema'; - expect(() => { Draft4Schema.toSchema(1 as any); }).toThrow(expectedError); + const expectedError = + 'Could not convert input to expected copy of Draft4Schema'; + expect(() => { + Draft4Schema.toSchema(1 as any); + }).toThrow(expectedError); }); test('should handle recursive schemas', () => { - const recursiveDoc = { type: 'object', properties: { } }; + const recursiveDoc = { type: 'object', properties: {} }; const doc = { type: 'object', properties: { test: recursiveDoc } }; (recursiveDoc.properties as any)['test'] = doc; const d = Draft4Schema.toSchema(doc) as Draft4Schema; @@ -16,7 +19,7 @@ describe('Draft4Schema', () => { expect(typeof d2).toEqual('object'); }); test('should never return the same instance of properties', () => { - const doc = { type: 'string', properties: {test: {type: 'string'}} }; + const doc = { type: 'string', properties: { test: { type: 'string' } } }; const d = Draft4Schema.toSchema(doc) as Draft4Schema; expect(typeof d).toEqual('object'); const d2 = Draft4Schema.toSchema(d as any) as Draft4Schema; @@ -26,19 +29,19 @@ describe('Draft4Schema', () => { expect((d2.properties!['test'] as Draft4Schema).id).not.toEqual('test'); }); test('should never return the same instance of items', () => { - const doc = { type: 'string', items: [{type: 'string'}] }; + const doc = { type: 'string', items: [{ type: 'string' }] }; const d = Draft4Schema.toSchema(doc) as Draft4Schema; expect(typeof d).toEqual('object'); const d2 = Draft4Schema.toSchema(d as any) as Draft4Schema; expect(typeof d2).toEqual('object'); - const d_items : Draft4Schema[] = d.items as Draft4Schema[]; - const d2_items : Draft4Schema[] = d2.items as Draft4Schema[]; + const d_items: Draft4Schema[] = d.items as Draft4Schema[]; + const d2_items: Draft4Schema[] = d2.items as Draft4Schema[]; d_items[0].id = 'test'; expect(d_items[0].id).toEqual('test'); expect(d2_items[0].id).not.toEqual('test'); }); test('should never convert value properties', () => { - const doc = { enum: [{ test: { type: 'string'} }] }; + const doc = { enum: [{ test: { type: 'string' } }] }; const d = Draft4Schema.toSchema(doc) as Draft4Schema; expect(typeof d).toEqual('object'); expect(d.enum![0] instanceof Draft4Schema).toEqual(false); diff --git a/test/models/Draft6Schema.spec.ts b/test/models/Draft6Schema.spec.ts index 0a4ef44879..ff3d000db2 100644 --- a/test/models/Draft6Schema.spec.ts +++ b/test/models/Draft6Schema.spec.ts @@ -1,13 +1,16 @@ -import {Draft6Schema} from '../../src/models'; +import { Draft6Schema } from '../../src/models'; describe('Draft6Schema', () => { describe('toSchema', () => { test('should throw error when trying to convert non-object', () => { - const expectedError = 'Could not convert input to expected copy of Draft6Schema'; - expect(() => { Draft6Schema.toSchema(1 as any); }).toThrow(expectedError); + const expectedError = + 'Could not convert input to expected copy of Draft6Schema'; + expect(() => { + Draft6Schema.toSchema(1 as any); + }).toThrow(expectedError); }); test('should handle recursive schemas', () => { - const recursiveDoc = { type: 'object', properties: { } }; + const recursiveDoc = { type: 'object', properties: {} }; const doc = { type: 'object', properties: { test: recursiveDoc } }; (recursiveDoc.properties as any)['test'] = doc; const d = Draft6Schema.toSchema(doc) as Draft6Schema; @@ -16,7 +19,7 @@ describe('Draft6Schema', () => { expect(typeof d2).toEqual('object'); }); test('should never return the same instance of properties', () => { - const doc = { type: 'string', properties: {test: {type: 'string'}} }; + const doc = { type: 'string', properties: { test: { type: 'string' } } }; const d = Draft6Schema.toSchema(doc) as Draft6Schema; expect(typeof d).toEqual('object'); const d2 = Draft6Schema.toSchema(d as any) as Draft6Schema; @@ -26,19 +29,19 @@ describe('Draft6Schema', () => { expect((d2.properties!['test'] as Draft6Schema).$id).not.toEqual('test'); }); test('should never return the same instance of items', () => { - const doc = { type: 'string', items: [{type: 'string'}] }; + const doc = { type: 'string', items: [{ type: 'string' }] }; const d = Draft6Schema.toSchema(doc) as Draft6Schema; expect(typeof d).toEqual('object'); const d2 = Draft6Schema.toSchema(d as any) as Draft6Schema; expect(typeof d2).toEqual('object'); - const d_items : Draft6Schema[] = d.items as Draft6Schema[]; - const d2_items : Draft6Schema[] = d2.items as Draft6Schema[]; + const d_items: Draft6Schema[] = d.items as Draft6Schema[]; + const d2_items: Draft6Schema[] = d2.items as Draft6Schema[]; d_items[0].$id = 'test'; expect(d_items[0].$id).toEqual('test'); expect(d2_items[0].$id).not.toEqual('test'); }); test('should never convert value properties', () => { - const doc = { enum: [{ test: { type: 'string'} }] }; + const doc = { enum: [{ test: { type: 'string' } }] }; const d = Draft6Schema.toSchema(doc) as Draft6Schema; expect(typeof d).toEqual('object'); expect(d.enum![0] instanceof Draft6Schema).toEqual(false); diff --git a/test/models/Draft7Schema.spec.ts b/test/models/Draft7Schema.spec.ts index b0d17259e1..7fa8572a52 100644 --- a/test/models/Draft7Schema.spec.ts +++ b/test/models/Draft7Schema.spec.ts @@ -1,4 +1,4 @@ -import {Draft7Schema} from '../../src/models/Draft7Schema'; +import { Draft7Schema } from '../../src/models/Draft7Schema'; describe('Draft7Schema', () => { describe('multipleOf', () => { @@ -22,7 +22,7 @@ describe('Draft7Schema', () => { expect(d.$id).toEqual(doc.$id); }); }); - + describe('maximum', () => { test('should return a number', () => { const doc = { type: 'number', maximum: 10 }; @@ -33,7 +33,7 @@ describe('Draft7Schema', () => { expect(d.maximum).toEqual(doc.maximum); }); }); - + describe('exclusiveMaximum', () => { test('should return a number', () => { const doc = { type: 'number', exclusiveMaximum: 10 }; @@ -44,7 +44,7 @@ describe('Draft7Schema', () => { expect(d.exclusiveMaximum).toEqual(doc.exclusiveMaximum); }); }); - + describe('minimum', () => { test('should return a number', () => { const doc = { type: 'number', minimum: 10 }; @@ -55,7 +55,7 @@ describe('Draft7Schema', () => { expect(d.minimum).toEqual(doc.minimum); }); }); - + describe('exclusiveMinimum', () => { test('should return a number', () => { const doc = { type: 'number', exclusiveMinimum: 10 }; @@ -66,7 +66,7 @@ describe('Draft7Schema', () => { expect(d.exclusiveMinimum).toEqual(doc.exclusiveMinimum); }); }); - + describe('maxLength', () => { test('should return a number', () => { const doc = { type: 'string', maxLength: 10 }; @@ -77,7 +77,7 @@ describe('Draft7Schema', () => { expect(d.maxLength).toEqual(doc.maxLength); }); }); - + describe('minLength', () => { test('should return a number', () => { const doc = { type: 'string', minLength: 10 }; @@ -88,7 +88,7 @@ describe('Draft7Schema', () => { expect(d.minLength).toEqual(doc.minLength); }); }); - + describe('pattern', () => { test('should return a string', () => { const doc = { type: 'string', pattern: '^test' }; @@ -99,7 +99,7 @@ describe('Draft7Schema', () => { expect(d.pattern).toEqual(doc.pattern); }); }); - + describe('maxItems', () => { test('should return a number', () => { const doc = { type: 'array', maxItems: 10 }; @@ -110,7 +110,7 @@ describe('Draft7Schema', () => { expect(d.maxItems).toEqual(doc.maxItems); }); }); - + describe('minItems', () => { test('should return a number', () => { const doc = { type: 'array', minItems: 10 }; @@ -121,7 +121,7 @@ describe('Draft7Schema', () => { expect(d.minItems).toEqual(doc.minItems); }); }); - + describe('uniqueItems', () => { test('should return a boolean', () => { const doc = { type: 'array', uniqueItems: true }; @@ -186,7 +186,7 @@ describe('Draft7Schema', () => { expect(typeof d.type).toEqual('string'); expect(d.type).toEqual(doc.type); }); - + test('should return an array of strings', () => { const doc = { type: ['number', 'string'] }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; @@ -259,7 +259,7 @@ describe('Draft7Schema', () => { expect(d.items!.constructor.name).toEqual('Draft7Schema'); expect(d.items).toEqual(doc.items); }); - + test('should return an array of Draft7Schema objects', () => { const doc = { items: [{ type: 'string' }, { type: 'number' }] }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; @@ -297,7 +297,7 @@ describe('Draft7Schema', () => { expect(d.additionalProperties!.constructor.name).toEqual('Draft7Schema'); expect(d.additionalProperties).toEqual(doc.additionalProperties); }); - + test('should return a boolean', () => { const doc = { additionalProperties: true }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; @@ -306,7 +306,7 @@ describe('Draft7Schema', () => { expect(typeof d.additionalProperties).toEqual('boolean'); expect(d.additionalProperties).toEqual(doc.additionalProperties); }); - + test('should return undefined when not defined', () => { const doc = {}; const d = Draft7Schema.toSchema(doc) as Draft7Schema; @@ -324,14 +324,14 @@ describe('Draft7Schema', () => { expect(d.additionalItems!.constructor.name).toEqual('Draft7Schema'); expect(d.additionalItems).toEqual(doc.additionalItems); }); - + test('should return undefined when not defined', () => { const doc = {}; const d = Draft7Schema.toSchema(doc) as Draft7Schema; expect(typeof d).toEqual('object'); expect(d.additionalItems).toEqual(undefined); }); - + test('should return undefined when undefined', () => { const doc = { additionalItems: undefined }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; @@ -364,7 +364,7 @@ describe('Draft7Schema', () => { expect(typeof d.const).toEqual('number'); expect(d.const).toEqual(doc.const); }); - + test('should return null', () => { const doc = { type: 'object', const: null }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; @@ -372,7 +372,7 @@ describe('Draft7Schema', () => { expect(d.const).not.toBeUndefined(); expect(d.const).toEqual(doc.const); }); - + test('should return an object', () => { const doc = { type: 'object', const: { test: true } }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; @@ -381,7 +381,7 @@ describe('Draft7Schema', () => { expect(typeof d.const).toEqual('object'); expect(d.const).toEqual(doc.const); }); - + test('should return an array', () => { const doc = { type: 'object', const: ['test'] }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; @@ -405,7 +405,10 @@ describe('Draft7Schema', () => { describe('dependencies', () => { test('should return a map with an array value', () => { - const doc = { properties: { test: { type: 'string' }, test2: { type: 'number' } }, dependencies: { test: ['test2'] } }; + const doc = { + properties: { test: { type: 'string' }, test2: { type: 'number' } }, + dependencies: { test: ['test2'] } + }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; expect(typeof d).toEqual('object'); expect(d.dependencies).not.toBeUndefined(); @@ -416,9 +419,12 @@ describe('Draft7Schema', () => { expect(v).toEqual((doc.dependencies as Record)[key]); } }); - + test('should return a map with a Draft7Schema value', () => { - const doc = { properties: { test: { type: 'string' } }, dependencies: { test: { properties: { test2: { type: 'number' } } } } }; + const doc = { + properties: { test: { type: 'string' } }, + dependencies: { test: { properties: { test2: { type: 'number' } } } } + }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; expect(typeof d).toEqual('object'); expect(d.dependencies).not.toBeUndefined(); @@ -463,7 +469,7 @@ describe('Draft7Schema', () => { expect(d.then).toEqual(doc.then); }); }); - + describe('else', () => { test('should return a Draft7Schema object', () => { const doc = { else: { type: 'string' } }; @@ -510,7 +516,7 @@ describe('Draft7Schema', () => { describe('description', () => { test('should return a string', () => { - const doc = { description: 'some description'}; + const doc = { description: 'some description' }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; expect(typeof d).toEqual('object'); expect(d.description).not.toBeUndefined(); @@ -579,7 +585,7 @@ describe('Draft7Schema', () => { describe('$ref', () => { test('should return a string ', () => { - const doc = { $ref: 'some/reference'}; + const doc = { $ref: 'some/reference' }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; expect(typeof d).toEqual('object'); expect(d.$ref).not.toBeUndefined(); @@ -601,11 +607,14 @@ describe('Draft7Schema', () => { describe('toSchema', () => { test('should throw error when trying to convert non-object', () => { - const expectedError = 'Could not convert input to expected copy of Draft7Schema'; - expect(() => { Draft7Schema.toSchema(1 as any); }).toThrow(expectedError); + const expectedError = + 'Could not convert input to expected copy of Draft7Schema'; + expect(() => { + Draft7Schema.toSchema(1 as any); + }).toThrow(expectedError); }); test('should handle recursive schemas', () => { - const recursiveDoc = { type: 'object', properties: { } }; + const recursiveDoc = { type: 'object', properties: {} }; const doc = { type: 'object', properties: { test: recursiveDoc } }; (recursiveDoc.properties as any)['test'] = doc; const d = Draft7Schema.toSchema(doc) as Draft7Schema; @@ -614,7 +623,7 @@ describe('Draft7Schema', () => { expect(typeof d2).toEqual('object'); }); test('should never return the same instance of properties', () => { - const doc = { type: 'string', properties: {test: {type: 'string'}} }; + const doc = { type: 'string', properties: { test: { type: 'string' } } }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; expect(typeof d).toEqual('object'); const d2 = Draft7Schema.toSchema(d as any) as Draft7Schema; @@ -624,19 +633,19 @@ describe('Draft7Schema', () => { expect((d2.properties!['test'] as Draft7Schema).$id).not.toEqual('test'); }); test('should never return the same instance of items', () => { - const doc = { type: 'string', items: [{type: 'string'}] }; + const doc = { type: 'string', items: [{ type: 'string' }] }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; expect(typeof d).toEqual('object'); const d2 = Draft7Schema.toSchema(d as any) as Draft7Schema; expect(typeof d2).toEqual('object'); - const d_items : Draft7Schema[] = d.items as Draft7Schema[]; - const d2_items : Draft7Schema[] = d2.items as Draft7Schema[]; + const d_items: Draft7Schema[] = d.items as Draft7Schema[]; + const d2_items: Draft7Schema[] = d2.items as Draft7Schema[]; d_items[0].$id = 'test'; expect(d_items[0].$id).toEqual('test'); expect(d2_items[0].$id).not.toEqual('test'); }); test('should never convert value properties', () => { - const doc = { const: { test: { type: 'string'} } }; + const doc = { const: { test: { type: 'string' } } }; const d = Draft7Schema.toSchema(doc) as Draft7Schema; expect(typeof d).toEqual('object'); expect(d.const instanceof Draft7Schema).toEqual(false); diff --git a/test/models/OpenAPIV3Schema.spec.ts b/test/models/OpenAPIV3Schema.spec.ts index 576d6f8892..7bf0dbd34a 100644 --- a/test/models/OpenAPIV3Schema.spec.ts +++ b/test/models/OpenAPIV3Schema.spec.ts @@ -1,13 +1,21 @@ -import {OpenAPIV3ExternalDocumentation, OpenapiV3Schema, OpenapiV3Xml, OpenapiV3Discriminator} from '../../src/models'; +import { + OpenAPIV3ExternalDocumentation, + OpenapiV3Schema, + OpenapiV3Xml, + OpenapiV3Discriminator +} from '../../src/models'; describe('OpenapiV3Schema', () => { describe('toSchema', () => { test('should throw error when trying to convert non-object', () => { - const expectedError = 'Could not convert input to expected copy of OpenapiV3Schema'; - expect(() => { OpenapiV3Schema.toSchema(1 as any); }).toThrow(expectedError); + const expectedError = + 'Could not convert input to expected copy of OpenapiV3Schema'; + expect(() => { + OpenapiV3Schema.toSchema(1 as any); + }).toThrow(expectedError); }); test('should handle recursive schemas', () => { - const recursiveDoc = { type: 'object', properties: { } }; + const recursiveDoc = { type: 'object', properties: {} }; const doc = { type: 'object', properties: { test: recursiveDoc } }; (recursiveDoc.properties as any)['test'] = doc; const d = OpenapiV3Schema.toSchema(doc) as OpenapiV3Schema; @@ -25,7 +33,7 @@ describe('OpenapiV3Schema', () => { expect(d.discriminator instanceof OpenapiV3Discriminator).toEqual(true); expect(d.discriminator!.propertyName).toEqual('test'); }); - + test('should never return the same instance of discriminator', () => { const doc = { discriminator: { propertyName: 'test' } }; const d = OpenapiV3Schema.toSchema(doc) as OpenapiV3Schema; @@ -64,7 +72,9 @@ describe('OpenapiV3Schema', () => { expect(typeof d).toEqual('object'); const d2 = OpenapiV3Schema.toSchema(d as any) as OpenapiV3Schema; expect(typeof d2).toEqual('object'); - expect(d.externalDocs instanceof OpenAPIV3ExternalDocumentation).toEqual(true); + expect(d.externalDocs instanceof OpenAPIV3ExternalDocumentation).toEqual( + true + ); expect(d.externalDocs!.description).toEqual('test'); }); test('should never return the same instance of external documentation', () => { @@ -78,29 +88,31 @@ describe('OpenapiV3Schema', () => { expect(d2.externalDocs!.description).not.toEqual('test2'); }); test('should never return the same instance of properties', () => { - const doc = { type: 'string', properties: {test: {type: 'string'}} }; + const doc = { type: 'string', properties: { test: { type: 'string' } } }; const d = OpenapiV3Schema.toSchema(doc) as OpenapiV3Schema; expect(typeof d).toEqual('object'); const d2 = OpenapiV3Schema.toSchema(d as any) as OpenapiV3Schema; expect(typeof d2).toEqual('object'); (d.properties!['test'] as OpenapiV3Schema).id = 'test'; expect((d.properties!['test'] as OpenapiV3Schema).id).toEqual('test'); - expect((d2.properties!['test'] as OpenapiV3Schema).id).not.toEqual('test'); + expect((d2.properties!['test'] as OpenapiV3Schema).id).not.toEqual( + 'test' + ); }); test('should never return the same instance of items', () => { - const doc = { type: 'string', items: [{type: 'string'}] }; + const doc = { type: 'string', items: [{ type: 'string' }] }; const d = OpenapiV3Schema.toSchema(doc) as OpenapiV3Schema; expect(typeof d).toEqual('object'); const d2 = OpenapiV3Schema.toSchema(d as any) as OpenapiV3Schema; expect(typeof d2).toEqual('object'); - const d_items : OpenapiV3Schema[] = d.items as OpenapiV3Schema[]; - const d2_items : OpenapiV3Schema[] = d2.items as OpenapiV3Schema[]; + const d_items: OpenapiV3Schema[] = d.items as OpenapiV3Schema[]; + const d2_items: OpenapiV3Schema[] = d2.items as OpenapiV3Schema[]; d_items[0].id = 'test'; expect(d_items[0].id).toEqual('test'); expect(d2_items[0].id).not.toEqual('test'); }); test('should never convert value properties', () => { - const doc = { enum: [{ test: { type: 'string'} }] }; + const doc = { enum: [{ test: { type: 'string' } }] }; const d = OpenapiV3Schema.toSchema(doc) as OpenapiV3Schema; expect(typeof d).toEqual('object'); expect(d.enum![0] instanceof OpenapiV3Schema).toEqual(false); diff --git a/test/models/OutputModel.spec.ts b/test/models/OutputModel.spec.ts index d126291a86..c4a65b2c9f 100644 --- a/test/models/OutputModel.spec.ts +++ b/test/models/OutputModel.spec.ts @@ -1,15 +1,18 @@ -import { CommonInputModel, CommonModel, ToOutputModelArg, OutputModel } from '../../src/models'; +import { + InputMetaModel, + CommonModel, + ToOutputModelArg, + OutputModel, + ConstrainedAnyModel +} from '../../src/models'; describe('OutputModel', () => { test('should return an OutputModel', () => { - const doc: any = { $id: 'test' }; - const commonModel = CommonModel.toCommonModel(doc); - const ioutput: ToOutputModelArg = { result: 'result', - model: commonModel, + model: new ConstrainedAnyModel('', undefined, ''), modelName: 'someModel', - inputModel: new CommonInputModel(), + inputModel: new InputMetaModel(), dependencies: ['test'] }; const output = OutputModel.toOutputModel(ioutput); @@ -22,14 +25,11 @@ describe('OutputModel', () => { }); test('should return an array of OutputModel', () => { - const doc: any = { $id: 'test' }; - const commonModel = CommonModel.toCommonModel(doc); - const ioutput: ToOutputModelArg = { result: 'result', - model: commonModel, + model: new ConstrainedAnyModel('', undefined, ''), modelName: 'someModel', - inputModel: new CommonInputModel(), + inputModel: new InputMetaModel(), dependencies: ['test'] }; const output = OutputModel.toOutputModel([ioutput, ioutput]); diff --git a/test/models/RenderOutput.spec.ts b/test/models/RenderOutput.spec.ts index cd12f9d918..273c056962 100644 --- a/test/models/RenderOutput.spec.ts +++ b/test/models/RenderOutput.spec.ts @@ -1,4 +1,4 @@ -import { ToRenderOutputArg, RenderOutput } from '../../src/models'; +import { ToRenderOutputArg, RenderOutput } from '../../src/models'; describe('RenderOutput', () => { test('should return an RenderOutput', () => { diff --git a/test/models/SwaggerV2Schema.spec.ts b/test/models/SwaggerV2Schema.spec.ts index c1135e6c16..058b79def5 100644 --- a/test/models/SwaggerV2Schema.spec.ts +++ b/test/models/SwaggerV2Schema.spec.ts @@ -1,13 +1,20 @@ -import {SwaggerV2ExternalDocumentation, SwaggerV2Schema, SwaggerV2Xml} from '../../src/models'; +import { + SwaggerV2ExternalDocumentation, + SwaggerV2Schema, + SwaggerV2Xml +} from '../../src/models'; describe('SwaggerV2Schema', () => { describe('toSchema', () => { test('should throw error when trying to convert non-object', () => { - const expectedError = 'Could not convert input to expected copy of SwaggerV2Schema'; - expect(() => { SwaggerV2Schema.toSchema(1 as any); }).toThrow(expectedError); + const expectedError = + 'Could not convert input to expected copy of SwaggerV2Schema'; + expect(() => { + SwaggerV2Schema.toSchema(1 as any); + }).toThrow(expectedError); }); test('should handle recursive schemas', () => { - const recursiveDoc = { type: 'object', properties: { } }; + const recursiveDoc = { type: 'object', properties: {} }; const doc = { type: 'object', properties: { test: recursiveDoc } }; (recursiveDoc.properties as any)['test'] = doc; const d = SwaggerV2Schema.toSchema(doc) as SwaggerV2Schema; @@ -37,16 +44,26 @@ describe('SwaggerV2Schema', () => { }); test('should handle external documentation', () => { - const doc = { type: 'string', properties: { test: { type: 'string' } }, externalDocs: { description: 'test' } }; + const doc = { + type: 'string', + properties: { test: { type: 'string' } }, + externalDocs: { description: 'test' } + }; const d = SwaggerV2Schema.toSchema(doc) as SwaggerV2Schema; expect(typeof d).toEqual('object'); const d2 = SwaggerV2Schema.toSchema(d as any) as SwaggerV2Schema; expect(typeof d2).toEqual('object'); - expect(d.externalDocs instanceof SwaggerV2ExternalDocumentation).toEqual(true); + expect(d.externalDocs instanceof SwaggerV2ExternalDocumentation).toEqual( + true + ); expect(d.externalDocs!.description).toEqual('test'); }); test('should never return the same instance of external documentation', () => { - const doc = { type: 'string', properties: { test: { type: 'string' } }, externalDocs: { description: 'test' } }; + const doc = { + type: 'string', + properties: { test: { type: 'string' } }, + externalDocs: { description: 'test' } + }; const d = SwaggerV2Schema.toSchema(doc) as SwaggerV2Schema; expect(typeof d).toEqual('object'); const d2 = SwaggerV2Schema.toSchema(d as any) as SwaggerV2Schema; @@ -56,29 +73,31 @@ describe('SwaggerV2Schema', () => { expect(d2.externalDocs!.description).not.toEqual('test2'); }); test('should never return the same instance of properties', () => { - const doc = { type: 'string', properties: {test: {type: 'string'}} }; + const doc = { type: 'string', properties: { test: { type: 'string' } } }; const d = SwaggerV2Schema.toSchema(doc) as SwaggerV2Schema; expect(typeof d).toEqual('object'); const d2 = SwaggerV2Schema.toSchema(d as any) as SwaggerV2Schema; expect(typeof d2).toEqual('object'); (d.properties!['test'] as SwaggerV2Schema).id = 'test'; expect((d.properties!['test'] as SwaggerV2Schema).id).toEqual('test'); - expect((d2.properties!['test'] as SwaggerV2Schema).id).not.toEqual('test'); + expect((d2.properties!['test'] as SwaggerV2Schema).id).not.toEqual( + 'test' + ); }); test('should never return the same instance of items', () => { - const doc = { type: 'string', items: [{type: 'string'}] }; + const doc = { type: 'string', items: [{ type: 'string' }] }; const d = SwaggerV2Schema.toSchema(doc) as SwaggerV2Schema; expect(typeof d).toEqual('object'); const d2 = SwaggerV2Schema.toSchema(d as any) as SwaggerV2Schema; expect(typeof d2).toEqual('object'); - const d_items : SwaggerV2Schema[] = d.items as SwaggerV2Schema[]; - const d2_items : SwaggerV2Schema[] = d2.items as SwaggerV2Schema[]; + const d_items: SwaggerV2Schema[] = d.items as SwaggerV2Schema[]; + const d2_items: SwaggerV2Schema[] = d2.items as SwaggerV2Schema[]; d_items[0].id = 'test'; expect(d_items[0].id).toEqual('test'); expect(d2_items[0].id).not.toEqual('test'); }); test('should never convert value properties', () => { - const doc = { enum: [{ test: { type: 'string'} }] }; + const doc = { enum: [{ test: { type: 'string' } }] }; const d = SwaggerV2Schema.toSchema(doc) as SwaggerV2Schema; expect(typeof d).toEqual('object'); expect(d.enum![0] instanceof SwaggerV2Schema).toEqual(false); diff --git a/test/processors/AsyncAPIInputProcessor.spec.ts b/test/processors/AsyncAPIInputProcessor.spec.ts index 8b0c8b0f5b..68563f4161 100644 --- a/test/processors/AsyncAPIInputProcessor.spec.ts +++ b/test/processors/AsyncAPIInputProcessor.spec.ts @@ -1,29 +1,37 @@ import * as fs from 'fs'; import * as path from 'path'; -import {parse, ParserOptions} from '@asyncapi/parser'; -import {AsyncAPIInputProcessor} from '../../src/processors/AsyncAPIInputProcessor'; -import { CommonModel } from '../../src/models'; -const basicDocString = fs.readFileSync(path.resolve(__dirname, './AsyncAPIInputProcessor/basic.json'), 'utf8'); -jest.mock('../../src/interpreter/Interpreter'); -jest.mock('../../src/interpreter/PostInterpreter'); -jest.mock('../../src/utils/LoggingInterface'); +import { Parser } from '@asyncapi/parser'; +import { AsyncAPIInputProcessor } from '../../src/processors/AsyncAPIInputProcessor'; +import { AnyModel, CommonModel } from '../../src/models'; +const basicDocString = fs.readFileSync( + path.resolve(__dirname, './AsyncAPIInputProcessor/basic.json'), + 'utf8' +); +jest.mock('../../src/utils/LoggingInterface'); const mockedReturnModels = [new CommonModel()]; +const mockedMetaModel = new AnyModel('', undefined); +jest.mock('../../src/helpers/CommonModelToMetaModel', () => { + return { + convertToMetaModel: jest.fn().mockImplementation(() => { + return mockedMetaModel; + }) + }; +}); jest.mock('../../src/interpreter/Interpreter', () => { return { Interpreter: jest.fn().mockImplementation(() => { return { - interpret: jest.fn().mockImplementation(() => {return mockedReturnModels[0];}) + interpret: jest.fn().mockImplementation(() => { + return mockedReturnModels[0]; + }) }; }) }; }); -jest.mock('../../src/interpreter/PostInterpreter', () => { - return { - postInterpretModel: jest.fn().mockImplementation(() => {return mockedReturnModels;}) - }; -}); describe('AsyncAPIInputProcessor', () => { + const parser = new Parser(); + describe('shouldProcess()', () => { const processor = new AsyncAPIInputProcessor(); test('should be able to detect pure object', () => { @@ -31,27 +39,31 @@ describe('AsyncAPIInputProcessor', () => { expect(processor.shouldProcess(basicDoc)).toEqual(true); }); test('should be able to detect parsed object', async () => { - const parsedObject = await parse(basicDocString, {} as ParserOptions); - expect(processor.shouldProcess(parsedObject)).toEqual(true); + const { document } = await parser.parse(basicDocString); + expect(processor.shouldProcess(document)).toEqual(true); }); test('should be able to process AsyncAPI 2.0.0', () => { - const parsedObject = {asyncapi: '2.0.0'}; + const parsedObject = { asyncapi: '2.0.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); }); test('should be able to process AsyncAPI 2.1.0', () => { - const parsedObject = {asyncapi: '2.1.0'}; + const parsedObject = { asyncapi: '2.1.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); }); test('should be able to process AsyncAPI 2.2.0', () => { - const parsedObject = {asyncapi: '2.2.0'}; + const parsedObject = { asyncapi: '2.2.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); }); test('should be able to process AsyncAPI 2.3.0', () => { - const parsedObject = {asyncapi: '2.3.0'}; + const parsedObject = { asyncapi: '2.3.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); }); test('should be able to process AsyncAPI 2.4.0', () => { - const parsedObject = {asyncapi: '2.4.0'}; + const parsedObject = { asyncapi: '2.4.0' }; + expect(processor.shouldProcess(parsedObject)).toEqual(true); + }); + test('should be able to process AsyncAPI 2.5.0', () => { + const parsedObject = { asyncapi: '2.5.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); }); }); @@ -65,8 +77,8 @@ describe('AsyncAPIInputProcessor', () => { expect(processor.tryGetVersionOfDocument({})).toBeUndefined(); }); test('should be able to find AsyncAPI version from parsed document', async () => { - const parsedObject = await parse(basicDocString, {} as ParserOptions); - expect(processor.tryGetVersionOfDocument(parsedObject)).toEqual('2.0.0'); + const { document } = await parser.parse(basicDocString); + expect(processor.tryGetVersionOfDocument(document)).toEqual('2.0.0'); }); }); describe('isFromParser()', () => { @@ -75,77 +87,152 @@ describe('AsyncAPIInputProcessor', () => { expect(AsyncAPIInputProcessor.isFromParser(basicDoc)).toEqual(false); }); test('should be able to detect parsed object', async () => { - const parsedObject = await parse(basicDocString, {} as ParserOptions); - expect(AsyncAPIInputProcessor.isFromParser(parsedObject)).toEqual(true); + const { document } = await parser.parse(basicDocString); + expect(AsyncAPIInputProcessor.isFromParser(document)).toEqual(true); }); }); describe('process()', () => { + test('should throw error when trying to process empty schema', async () => { + const processor = new AsyncAPIInputProcessor(); + await expect(processor.process({})).rejects.toThrow( + 'Input is not an AsyncAPI document so it cannot be processed.' + ); + }); + test('should throw error when trying to process wrong schema', async () => { const processor = new AsyncAPIInputProcessor(); - await expect(processor.process({})) - .rejects - .toThrow('Input is not an AsyncAPI document so it cannot be processed.'); + await expect( + processor.process({ asyncapi: '2.5.0', nonExistingField: {} }) + ).rejects.toThrow( + 'Input is not an correct AsyncAPI document so it cannot be processed.' + ); }); + test('should be able to process pure object', async () => { const basicDoc = JSON.parse(basicDocString); const processor = new AsyncAPIInputProcessor(); const commonInputModel = await processor.process(basicDoc); expect(commonInputModel).toMatchSnapshot(); }); + test('should be able to process parsed objects', async () => { - const parsedObject = await parse(basicDocString, {} as ParserOptions); + const { document } = await parser.parse(basicDocString); const processor = new AsyncAPIInputProcessor(); - const commonInputModel = await processor.process(parsedObject); + const commonInputModel = await processor.process(document); expect(commonInputModel).toMatchSnapshot(); }); }); describe('convertToInternalSchema()', () => { test('should work', async () => { - const basicDocString = fs.readFileSync(path.resolve(__dirname, './AsyncAPIInputProcessor/schema_name_reflection.json'), 'utf8'); - const doc = await parse(basicDocString, {} as ParserOptions); - const schema = doc.channels()['/user/signedup'].subscribe().message().payload(); - const expected = AsyncAPIInputProcessor.convertToInternalSchema(schema) as any; + const basicDocString = fs.readFileSync( + path.resolve( + __dirname, + './AsyncAPIInputProcessor/schema_name_reflection.json' + ), + 'utf8' + ); + const { document } = await parser.parse(basicDocString); + const schema = document + ?.channels() + .get('/user/signedup') + ?.operations()[0] + .messages()[0] + ?.payload(); + const expected = AsyncAPIInputProcessor.convertToInternalSchema( + schema as any + ) as any; // root expect(expected['x-modelgen-inferred-name']).toEqual('MainSchema'); // properties - expect(expected.properties.prop['x-modelgen-inferred-name']).toEqual(''); - expect(expected.properties.allOfCase['x-modelgen-inferred-name']).toEqual(''); - expect(expected.properties.allOfCase.allOf[0]['x-modelgen-inferred-name']).toEqual(''); - expect(expected.properties.allOfCase.allOf[1]['x-modelgen-inferred-name']).toEqual(''); - expect(expected.properties.object['x-modelgen-inferred-name']).toEqual(''); - expect(expected.properties.object.properties.prop['x-modelgen-inferred-name']).toEqual(''); - expect(expected.properties.propWithObject['x-modelgen-inferred-name']).toEqual(''); - expect(expected.properties.propWithObject.properties.propWithObject['x-modelgen-inferred-name']).toEqual(''); + expect(expected.properties.prop['x-modelgen-inferred-name']).toEqual( + 'anonymous_schema_2' + ); + expect(expected.properties.allOfCase['x-modelgen-inferred-name']).toEqual( + 'anonymous_schema_3' + ); + expect( + expected.properties.allOfCase.allOf[0]['x-modelgen-inferred-name'] + ).toEqual('anonymous_schema_4'); + expect( + expected.properties.allOfCase.allOf[1]['x-modelgen-inferred-name'] + ).toEqual('anonymous_schema_5'); + expect(expected.properties.object['x-modelgen-inferred-name']).toEqual( + 'anonymous_schema_6' + ); + expect( + expected.properties.object.properties.prop['x-modelgen-inferred-name'] + ).toEqual('anonymous_schema_7'); + expect( + expected.properties.propWithObject['x-modelgen-inferred-name'] + ).toEqual('anonymous_schema_8'); + expect( + expected.properties.propWithObject.properties.propWithObject[ + 'x-modelgen-inferred-name' + ] + ).toEqual('anonymous_schema_9'); // patternProperties - expect(expected.patternProperties.patternProp['x-modelgen-inferred-name']).toEqual(''); + expect( + expected.patternProperties.patternProp['x-modelgen-inferred-name'] + ).toEqual('anonymous_schema_10'); // dependencies - expect(expected.dependencies.dep['x-modelgen-inferred-name']).toEqual(''); + expect(expected.dependencies.dep['x-modelgen-inferred-name']).toEqual( + 'anonymous_schema_11' + ); // definitions - expect(expected.definitions.def['x-modelgen-inferred-name']).toEqual(''); - expect(expected.definitions.oneOfCase['x-modelgen-inferred-name']).toEqual(''); - expect(expected.definitions.oneOfCase.oneOf[0]['x-modelgen-inferred-name']).toEqual(''); - expect(expected.definitions.oneOfCase.oneOf[1]['x-modelgen-inferred-name']).toEqual(''); + expect(expected.definitions.def['x-modelgen-inferred-name']).toEqual( + 'anonymous_schema_12' + ); + expect( + expected.definitions.oneOfCase['x-modelgen-inferred-name'] + ).toEqual('anonymous_schema_13'); + expect( + expected.definitions.oneOfCase.oneOf[0]['x-modelgen-inferred-name'] + ).toEqual('anonymous_schema_14'); + expect( + expected.definitions.oneOfCase.oneOf[1]['x-modelgen-inferred-name'] + ).toEqual('anonymous_schema_15'); // anyOf - expect(expected.anyOf[0]['x-modelgen-inferred-name']).toEqual(''); - expect(expected.anyOf[1]['x-modelgen-inferred-name']).toEqual(''); - expect(expected.anyOf[1].properties.prop['x-modelgen-inferred-name']).toEqual(''); + expect(expected.anyOf[0]['x-modelgen-inferred-name']).toEqual( + 'anonymous_schema_16' + ); + expect(expected.anyOf[1]['x-modelgen-inferred-name']).toEqual( + 'anonymous_schema_17' + ); + expect( + expected.anyOf[1].properties.prop['x-modelgen-inferred-name'] + ).toEqual('anonymous_schema_18'); }); test('should correctly convert when schema has more than one properties referencing one other schema', async () => { - const basicDocString = fs.readFileSync(path.resolve(__dirname, './AsyncAPIInputProcessor/schema_with_2_properties_referencing_one_schema.json'), 'utf8'); - const doc = await parse(basicDocString, {} as ParserOptions); - const schema = doc.channels()['/user/signedup'].subscribe().message().payload(); - const result = AsyncAPIInputProcessor.convertToInternalSchema(schema) as any; + const basicDocString = fs.readFileSync( + path.resolve( + __dirname, + './AsyncAPIInputProcessor/schema_with_2_properties_referencing_one_schema.json' + ), + 'utf8' + ); + const { document } = await parser.parse(basicDocString); + const schema = document + ?.channels() + .get('/user/signedup') + ?.operations()[0] + ?.messages()[0] + ?.payload(); + const result = AsyncAPIInputProcessor.convertToInternalSchema( + schema as any + ) as any; expect(result.properties['lastName']).not.toEqual({}); - expect(result.properties['firstName']).toEqual(result.properties['lastName']); + expect(result.properties['firstName']).toEqual( + result.properties['lastName'] + ); }); }); }); diff --git a/test/processors/InputProcessor.spec.ts b/test/processors/InputProcessor.spec.ts index 4019b85d1e..1e9e61bb59 100644 --- a/test/processors/InputProcessor.spec.ts +++ b/test/processors/InputProcessor.spec.ts @@ -1,9 +1,16 @@ import * as fs from 'fs'; import * as path from 'path'; -import { CommonInputModel, ProcessorOptions } from '../../src/models'; -import { AbstractInputProcessor, AsyncAPIInputProcessor, JsonSchemaInputProcessor, InputProcessor, SwaggerInputProcessor } from '../../src/processors'; -import AsyncAPIParser, { ParserOptions } from '@asyncapi/parser'; +import { InputMetaModel, ProcessorOptions } from '../../src/models'; +import { + AbstractInputProcessor, + AsyncAPIInputProcessor, + JsonSchemaInputProcessor, + InputProcessor, + SwaggerInputProcessor, + TypeScriptInputProcessor +} from '../../src/processors'; import { OpenAPIInputProcessor } from '../../src/processors/OpenAPIInputProcessor'; + describe('InputProcessor', () => { beforeEach(() => { jest.resetAllMocks(); @@ -11,11 +18,16 @@ describe('InputProcessor', () => { afterAll(() => { jest.restoreAllMocks(); }); - + class TempProcessor extends AbstractInputProcessor { - process(input: any): Promise { return Promise.resolve(new CommonInputModel()); } - shouldProcess(input: any): boolean { return true; } + process(input: any): Promise { + return Promise.resolve(new InputMetaModel()); + } + shouldProcess(input: any): boolean { + return true; + } } + test('should add processor to map', () => { const testProcessor = new TempProcessor(); const processor = new InputProcessor(); @@ -24,16 +36,24 @@ describe('InputProcessor', () => { const foundProcessor = processors.get('some_key'); expect(foundProcessor).toEqual(testProcessor); }); + test('overwriting processor should use new and not old', () => { const testProcessor = new TempProcessor(); const processor = new InputProcessor(); const oldDefaultProcessor = processor.getProcessors().get('default'); processor.setProcessor('default', testProcessor); const currentDefaultProcessor = processor.getProcessors().get('default'); - expect(currentDefaultProcessor?.constructor).not.toEqual(oldDefaultProcessor?.constructor); - expect(oldDefaultProcessor?.constructor).toEqual(oldDefaultProcessor?.constructor); - expect(currentDefaultProcessor?.constructor).toEqual(currentDefaultProcessor?.constructor); + expect(currentDefaultProcessor?.constructor).not.toEqual( + oldDefaultProcessor?.constructor + ); + expect(oldDefaultProcessor?.constructor).toEqual( + oldDefaultProcessor?.constructor + ); + expect(currentDefaultProcessor?.constructor).toEqual( + currentDefaultProcessor?.constructor + ); }); + describe('process()', () => { const getProcessors = () => { const asyncInputProcessor = new AsyncAPIInputProcessor(); @@ -53,73 +73,135 @@ describe('InputProcessor', () => { processor.setProcessor('swagger', swaggerInputProcessor); processor.setProcessor('openapi', openAPIInputProcessor); processor.setProcessor('default', defaultInputProcessor); - return {processor, asyncInputProcessor, swaggerInputProcessor, openAPIInputProcessor, defaultInputProcessor}; + return { + processor, + asyncInputProcessor, + swaggerInputProcessor, + openAPIInputProcessor, + defaultInputProcessor + }; }; test('should throw error when no default processor found', async () => { const processor = new InputProcessor(); const map = processor.getProcessors(); map.delete('default'); - await expect(processor.process({})) - .rejects - .toThrow('No default processor found'); + await expect(processor.process({})).rejects.toThrow( + 'No default processor found' + ); }); test('should be able to process default JSON schema input', async () => { - const {processor, asyncInputProcessor, defaultInputProcessor} = getProcessors(); - const inputSchemaString = fs.readFileSync(path.resolve(__dirname, './JsonSchemaInputProcessor/draft-7.json'), 'utf8'); + const { processor, asyncInputProcessor, defaultInputProcessor } = + getProcessors(); + const inputSchemaString = fs.readFileSync( + path.resolve(__dirname, './JsonSchemaInputProcessor/draft-7.json'), + 'utf8' + ); const inputSchema = JSON.parse(inputSchemaString); await processor.process(inputSchema); expect(asyncInputProcessor.process).not.toHaveBeenCalled(); - expect(asyncInputProcessor.shouldProcess).toHaveBeenNthCalledWith(1, inputSchema); - expect(defaultInputProcessor.process).toHaveBeenNthCalledWith(1, inputSchema, undefined); - expect(defaultInputProcessor.shouldProcess).toHaveBeenNthCalledWith(1, inputSchema); + expect(asyncInputProcessor.shouldProcess).toHaveBeenNthCalledWith( + 1, + inputSchema + ); + expect(defaultInputProcessor.process).toHaveBeenNthCalledWith( + 1, + inputSchema, + undefined + ); + expect(defaultInputProcessor.shouldProcess).toHaveBeenNthCalledWith( + 1, + inputSchema + ); }); test('should be able to process AsyncAPI schema input', async () => { - const {processor, asyncInputProcessor, defaultInputProcessor} = getProcessors(); - const inputSchemaString = fs.readFileSync(path.resolve(__dirname, './AsyncAPIInputProcessor/basic.json'), 'utf8'); + const { processor, asyncInputProcessor, defaultInputProcessor } = + getProcessors(); + const inputSchemaString = fs.readFileSync( + path.resolve(__dirname, './AsyncAPIInputProcessor/basic.json'), + 'utf8' + ); const inputSchema = JSON.parse(inputSchemaString); await processor.process(inputSchema); - expect(asyncInputProcessor.process).toHaveBeenNthCalledWith(1, inputSchema, undefined); - expect(asyncInputProcessor.shouldProcess).toHaveBeenNthCalledWith(1, inputSchema); + expect(asyncInputProcessor.process).toHaveBeenNthCalledWith( + 1, + inputSchema, + undefined + ); + expect(asyncInputProcessor.shouldProcess).toHaveBeenNthCalledWith( + 1, + inputSchema + ); expect(defaultInputProcessor.process).not.toHaveBeenCalled(); expect(defaultInputProcessor.shouldProcess).not.toHaveBeenCalled(); }); test('should be able to process Swagger (OpenAPI 2.0) input', async () => { - const {processor, swaggerInputProcessor, defaultInputProcessor} = getProcessors(); - const inputSchemaString = fs.readFileSync(path.resolve(__dirname, './SwaggerInputProcessor/basic.json'), 'utf8'); + const { processor, swaggerInputProcessor, defaultInputProcessor } = + getProcessors(); + const inputSchemaString = fs.readFileSync( + path.resolve(__dirname, './SwaggerInputProcessor/basic.json'), + 'utf8' + ); const inputSchema = JSON.parse(inputSchemaString); await processor.process(inputSchema); - expect(swaggerInputProcessor.process).toHaveBeenNthCalledWith(1, inputSchema, undefined); - expect(swaggerInputProcessor.shouldProcess).toHaveBeenNthCalledWith(1, inputSchema); + expect(swaggerInputProcessor.process).toHaveBeenNthCalledWith( + 1, + inputSchema, + undefined + ); + expect(swaggerInputProcessor.shouldProcess).toHaveBeenNthCalledWith( + 1, + inputSchema + ); expect(defaultInputProcessor.process).not.toHaveBeenCalled(); expect(defaultInputProcessor.shouldProcess).not.toHaveBeenCalled(); }); test('should be able to process OpenAPI input', async () => { - const {processor, openAPIInputProcessor, defaultInputProcessor} = getProcessors(); - const inputSchemaString = fs.readFileSync(path.resolve(__dirname, './OpenAPIInputProcessor/basic.json'), 'utf8'); + const { processor, openAPIInputProcessor, defaultInputProcessor } = + getProcessors(); + const inputSchemaString = fs.readFileSync( + path.resolve(__dirname, './OpenAPIInputProcessor/basic.json'), + 'utf8' + ); const inputSchema = JSON.parse(inputSchemaString); await processor.process(inputSchema); - expect(openAPIInputProcessor.process).toHaveBeenNthCalledWith(1, inputSchema, undefined); - expect(openAPIInputProcessor.shouldProcess).toHaveBeenNthCalledWith(1, inputSchema); + expect(openAPIInputProcessor.process).toHaveBeenNthCalledWith( + 1, + inputSchema, + undefined + ); + expect(openAPIInputProcessor.shouldProcess).toHaveBeenNthCalledWith( + 1, + inputSchema + ); expect(defaultInputProcessor.process).not.toHaveBeenCalled(); expect(defaultInputProcessor.shouldProcess).not.toHaveBeenCalled(); }); test('should be able to process AsyncAPI schema input with options', async () => { - const {processor, asyncInputProcessor, defaultInputProcessor} = getProcessors(); - const spy = jest.spyOn(AsyncAPIParser, 'parse'); + const { processor, asyncInputProcessor, defaultInputProcessor } = + getProcessors(); const options: ProcessorOptions = { asyncapi: { - path: 'test' - } as ParserOptions + source: 'test' + } }; - const inputSchemaString = fs.readFileSync(path.resolve(__dirname, './AsyncAPIInputProcessor/basic.json'), 'utf8'); + const inputSchemaString = fs.readFileSync( + path.resolve(__dirname, './AsyncAPIInputProcessor/basic.json'), + 'utf8' + ); const inputSchema = JSON.parse(inputSchemaString); await processor.process(inputSchema, options); - expect(asyncInputProcessor.process).toHaveBeenNthCalledWith(1, inputSchema, options); - expect(asyncInputProcessor.shouldProcess).toHaveBeenNthCalledWith(1, inputSchema); - expect(spy).toHaveBeenNthCalledWith(1, inputSchema, expect.objectContaining(options.asyncapi)); + expect(asyncInputProcessor.process).toHaveBeenNthCalledWith( + 1, + inputSchema, + options + ); + expect(asyncInputProcessor.shouldProcess).toHaveBeenNthCalledWith( + 1, + inputSchema + ); expect(defaultInputProcessor.process).not.toHaveBeenCalled(); expect(defaultInputProcessor.shouldProcess).not.toHaveBeenCalled(); }); diff --git a/test/processors/JsonSchemaInputProcessor.spec.ts b/test/processors/JsonSchemaInputProcessor.spec.ts index b60da1a3f5..601c3ee7bd 100644 --- a/test/processors/JsonSchemaInputProcessor.spec.ts +++ b/test/processors/JsonSchemaInputProcessor.spec.ts @@ -1,32 +1,31 @@ import * as fs from 'fs'; import * as path from 'path'; import { JsonSchemaInputProcessor } from '../../src/processors/JsonSchemaInputProcessor'; -import { CommonModel } from '../../src/models'; -import { Logger } from '../../src/utils'; -import { postInterpretModel } from '../../src/interpreter/PostInterpreter'; -import { Draft7Schema } from '../../src/models/Draft7Schema'; -jest.mock('../../src/interpreter/Interpreter'); -jest.mock('../../src/interpreter/PostInterpreter'); +import { AnyModel, CommonModel, StringModel } from '../../src/models'; jest.mock('../../src/utils/LoggingInterface'); jest.spyOn(JsonSchemaInputProcessor, 'convertSchemaToCommonModel'); - let mockedReturnModels = [new CommonModel()]; +const mockedMetaModel = new AnyModel('test', undefined); +jest.mock('../../src/helpers/CommonModelToMetaModel', () => { + return { + convertToMetaModel: jest.fn().mockImplementation(() => { + return mockedMetaModel; + }) + }; +}); jest.mock('../../src/interpreter/Interpreter', () => { return { Interpreter: jest.fn().mockImplementation(() => { return { - interpret: jest.fn().mockImplementation(() => {return mockedReturnModels[0];}) + interpret: jest.fn().mockImplementation(() => { + return mockedReturnModels[0]; + }) }; }) }; }); -jest.mock('../../src/interpreter/PostInterpreter', () => { - return { - postInterpretModel: jest.fn().mockImplementation(() => {return mockedReturnModels;}) - }; -}); describe('JsonSchemaInputProcessor', () => { - beforeEach(() => { + afterEach(() => { jest.clearAllMocks(); const model = new CommonModel(); model.$id = 'test'; @@ -35,114 +34,196 @@ describe('JsonSchemaInputProcessor', () => { afterAll(() => { jest.restoreAllMocks(); }); + const getCommonInput = async (inputSchemaPath: string) => { + const inputSchemaString = fs.readFileSync( + path.resolve(__dirname, inputSchemaPath), + 'utf8' + ); + const inputSchema = JSON.parse(inputSchemaString); + const processor = new JsonSchemaInputProcessor(); + const inputMetaModel = await processor.process(inputSchema); + return { inputSchema, inputMetaModel }; + }; describe('process()', () => { - const getCommonInput = async (inputSchemaPath: string) => { - const inputSchemaString = fs.readFileSync(path.resolve(__dirname, inputSchemaPath), 'utf8'); - const inputSchema = JSON.parse(inputSchemaString); - const processor = new JsonSchemaInputProcessor(); - const commonInputModel = await processor.process(inputSchema); - return {inputSchema, commonInputModel}; - }; test('should throw error when trying to process wrong schema', async () => { const processor = new JsonSchemaInputProcessor(); - await expect(processor.process({$schema: 'http://json-schema.org/draft-99/schema#'})) - .rejects - .toThrow('Input is not a JSON Schema, so it cannot be processed.'); + await expect( + processor.process({ + $schema: 'http://json-schema.org/draft-99/schema#' + }) + ).rejects.toThrow( + 'Input is not a JSON Schema, so it cannot be processed.' + ); }); test('should process draft 7 schemas', async () => { const inputSchemaPath = './JsonSchemaInputProcessor/draft-7.json'; - const {commonInputModel, inputSchema} = await getCommonInput(inputSchemaPath); - expect(commonInputModel).toMatchObject({models: {test: {$id: 'test'}}, originalInput: inputSchema}); - expect(JsonSchemaInputProcessor.convertSchemaToCommonModel).toHaveBeenCalledTimes(1); - const functionArgConvertSchemaToCommonModel = (JsonSchemaInputProcessor.convertSchemaToCommonModel as jest.Mock).mock.calls[0][0]; + const { inputMetaModel, inputSchema } = await getCommonInput( + inputSchemaPath + ); + expect(inputMetaModel).toMatchObject({ + models: { test: mockedMetaModel }, + originalInput: inputSchema + }); + expect( + JsonSchemaInputProcessor.convertSchemaToCommonModel + ).toHaveBeenCalledTimes(1); + const functionArgConvertSchemaToCommonModel = ( + JsonSchemaInputProcessor.convertSchemaToCommonModel as jest.Mock + ).mock.calls[0][0]; expect(functionArgConvertSchemaToCommonModel).toMatchObject(inputSchema); - expect(postInterpretModel).toHaveBeenCalledTimes(1); }); - + test('should process draft 6 schemas', async () => { const inputSchemaPath = './JsonSchemaInputProcessor/draft-6.json'; - const {commonInputModel, inputSchema} = await getCommonInput(inputSchemaPath); - expect(commonInputModel).toMatchObject({models: {test: {$id: 'test'}}, originalInput: inputSchema}); - expect(JsonSchemaInputProcessor.convertSchemaToCommonModel).toHaveBeenCalledTimes(1); - const functionArgConvertSchemaToCommonModel = (JsonSchemaInputProcessor.convertSchemaToCommonModel as jest.Mock).mock.calls[0][0]; + const { inputMetaModel, inputSchema } = await getCommonInput( + inputSchemaPath + ); + expect(inputMetaModel).toMatchObject({ + models: { test: mockedMetaModel }, + originalInput: inputSchema + }); + expect( + JsonSchemaInputProcessor.convertSchemaToCommonModel + ).toHaveBeenCalledTimes(1); + const functionArgConvertSchemaToCommonModel = ( + JsonSchemaInputProcessor.convertSchemaToCommonModel as jest.Mock + ).mock.calls[0][0]; expect(functionArgConvertSchemaToCommonModel).toMatchObject(inputSchema); - expect(postInterpretModel).toHaveBeenCalledTimes(1); }); - + test('should process draft 4 schemas', async () => { const inputSchemaPath = './JsonSchemaInputProcessor/draft-4.json'; - const {commonInputModel, inputSchema} = await getCommonInput(inputSchemaPath); - expect(commonInputModel).toMatchObject({models: {test: {$id: 'test'}}, originalInput: inputSchema}); - expect(JsonSchemaInputProcessor.convertSchemaToCommonModel).toHaveBeenCalledTimes(1); - const functionArgConvertSchemaToCommonModel = (JsonSchemaInputProcessor.convertSchemaToCommonModel as jest.Mock).mock.calls[0][0]; + const { inputMetaModel, inputSchema } = await getCommonInput( + inputSchemaPath + ); + expect(inputMetaModel).toMatchObject({ + models: { test: mockedMetaModel }, + originalInput: inputSchema + }); + expect( + JsonSchemaInputProcessor.convertSchemaToCommonModel + ).toHaveBeenCalledTimes(1); + const functionArgConvertSchemaToCommonModel = ( + JsonSchemaInputProcessor.convertSchemaToCommonModel as jest.Mock + ).mock.calls[0][0]; expect(functionArgConvertSchemaToCommonModel).toMatchObject(inputSchema); - expect(postInterpretModel).toHaveBeenCalledTimes(1); }); - + test('should be able to use $ref', async () => { const inputSchemaPath = './JsonSchemaInputProcessor/references.json'; - - const {commonInputModel, inputSchema} = await getCommonInput(inputSchemaPath); - - const expectedResolvedInput = {...inputSchema, properties: { street_address: { type: 'string' }}}; - const functionArgConvertSchemaToCommonModel = (JsonSchemaInputProcessor.convertSchemaToCommonModel as jest.Mock).mock.calls[0][0]; - expect(commonInputModel).toMatchObject({models: {test: {$id: 'test'}}, originalInput: inputSchema}); - expect(JsonSchemaInputProcessor.convertSchemaToCommonModel).toHaveBeenCalledTimes(1); - expect(functionArgConvertSchemaToCommonModel).toMatchObject(expectedResolvedInput); - expect(postInterpretModel).toHaveBeenCalledTimes(1); + const { inputMetaModel, inputSchema } = await getCommonInput( + inputSchemaPath + ); + const expectedResolvedInput = { + ...inputSchema, + properties: { street_address: { type: 'string' } } + }; + const functionArgConvertSchemaToCommonModel = ( + JsonSchemaInputProcessor.convertSchemaToCommonModel as jest.Mock + ).mock.calls[0][0]; + expect(inputMetaModel).toMatchObject({ + models: { test: mockedMetaModel }, + originalInput: inputSchema + }); + expect( + JsonSchemaInputProcessor.convertSchemaToCommonModel + ).toHaveBeenCalledTimes(1); + expect(functionArgConvertSchemaToCommonModel).toMatchObject( + expectedResolvedInput + ); }); - test('should be able to use $ref when circular', async () => { - const inputSchemaPath = './JsonSchemaInputProcessor/references_circular.json'; - const {commonInputModel, inputSchema} = await getCommonInput(inputSchemaPath); - - const expectedResolvedInput = {...inputSchema, definitions: {}, properties: { street_address: { type: 'object', properties: { floor: { type: 'object', properties: {} } }}}}; - const functionArgConvertSchemaToCommonModel = (JsonSchemaInputProcessor.convertSchemaToCommonModel as jest.Mock).mock.calls[0][0]; - expect(commonInputModel).toMatchObject({models: {test: {$id: 'test'}}, originalInput: inputSchema}); - expect(JsonSchemaInputProcessor.convertSchemaToCommonModel).toHaveBeenCalledTimes(1); - expect(functionArgConvertSchemaToCommonModel).toMatchObject(expectedResolvedInput); - expect(postInterpretModel).toHaveBeenCalledTimes(1); + test('should be able to use $ref when circular', async () => { + const inputSchemaPath = + './JsonSchemaInputProcessor/references_circular.json'; + const { inputMetaModel, inputSchema } = await getCommonInput( + inputSchemaPath + ); + const expectedResolvedInput = { + ...inputSchema, + definitions: {}, + properties: { + street_address: { + type: 'object', + properties: { floor: { type: 'object', properties: {} } } + } + } + }; + const functionArgConvertSchemaToCommonModel = ( + JsonSchemaInputProcessor.convertSchemaToCommonModel as jest.Mock + ).mock.calls[0][0]; + expect(inputMetaModel).toMatchObject({ + models: { test: mockedMetaModel }, + originalInput: inputSchema + }); + expect( + JsonSchemaInputProcessor.convertSchemaToCommonModel + ).toHaveBeenCalledTimes(1); + expect(functionArgConvertSchemaToCommonModel).toMatchObject( + expectedResolvedInput + ); }); + test('should fail correctly when reference cannot be resolved', async () => { - const inputSchemaPath = './JsonSchemaInputProcessor/wrong_references.json'; - const inputSchemaString = fs.readFileSync(path.resolve(__dirname, inputSchemaPath), 'utf8'); + const inputSchemaPath = + './JsonSchemaInputProcessor/wrong_references.json'; + const inputSchemaString = fs.readFileSync( + path.resolve(__dirname, inputSchemaPath), + 'utf8' + ); const inputSchema = JSON.parse(inputSchemaString); const processor = new JsonSchemaInputProcessor(); - await expect(processor.process(inputSchema)).rejects.toThrow('Could not dereference $ref in input, is all the references correct?'); + await expect(processor.process(inputSchema)).rejects.toThrow( + 'Could not dereference $ref in input, is all the references correct?' + ); }); }); describe('shouldProcess()', () => { test('should process draft 7 input schema', () => { const processor = new JsonSchemaInputProcessor(); - const shouldProcess = processor.shouldProcess({$schema: 'http://json-schema.org/draft-07/schema#'}); + const shouldProcess = processor.shouldProcess({ + $schema: 'http://json-schema.org/draft-07/schema#' + }); expect(shouldProcess).toEqual(true); - const shouldProcess2 = processor.shouldProcess({$schema: 'http://json-schema.org/draft-07/schema'}); + const shouldProcess2 = processor.shouldProcess({ + $schema: 'http://json-schema.org/draft-07/schema' + }); expect(shouldProcess2).toEqual(true); }); - + test('should process draft 6 input schema', () => { const processor = new JsonSchemaInputProcessor(); - const shouldProcess = processor.shouldProcess({$schema: 'http://json-schema.org/draft-06/schema#'}); + const shouldProcess = processor.shouldProcess({ + $schema: 'http://json-schema.org/draft-06/schema#' + }); expect(shouldProcess).toEqual(true); - const shouldProcess2 = processor.shouldProcess({$schema: 'http://json-schema.org/draft-06/schema'}); + const shouldProcess2 = processor.shouldProcess({ + $schema: 'http://json-schema.org/draft-06/schema' + }); expect(shouldProcess2).toEqual(true); }); - + test('should process draft 4 input schema', () => { const processor = new JsonSchemaInputProcessor(); - const shouldProcess = processor.shouldProcess({$schema: 'http://json-schema.org/draft-04/schema#'}); + const shouldProcess = processor.shouldProcess({ + $schema: 'http://json-schema.org/draft-04/schema#' + }); expect(shouldProcess).toEqual(true); - const shouldProcess2 = processor.shouldProcess({$schema: 'http://json-schema.org/draft-04/schema'}); + const shouldProcess2 = processor.shouldProcess({ + $schema: 'http://json-schema.org/draft-04/schema' + }); expect(shouldProcess2).toEqual(true); }); - + test('should not process input with wrong $schema', () => { const processor = new JsonSchemaInputProcessor(); - const shouldProcess = processor.shouldProcess({$schema: 'http://json-schema.org/draft-99/schema#'}); + const shouldProcess = processor.shouldProcess({ + $schema: 'http://json-schema.org/draft-99/schema#' + }); expect(shouldProcess).toEqual(false); }); - + test('should by default process input if $schema is not defined', () => { const processor = new JsonSchemaInputProcessor(); const shouldProcess = processor.shouldProcess({}); @@ -150,141 +231,195 @@ describe('JsonSchemaInputProcessor', () => { }); }); + describe('dereferenceInputs()', () => { + test('should handle root $ref', () => { + const processor = new JsonSchemaInputProcessor(); + const schema = { + definitions: { + root: { + type: 'string' + } + }, + $ref: '#/definitions/root' + }; + const dereferencedSchema = processor.handleRootReference(schema); + expect(dereferencedSchema).toEqual({ + definitions: { root: { type: 'string' } }, + type: 'string' + }); + }); + test('should handle root $ref that cannot be processed', () => { + const processor = new JsonSchemaInputProcessor(); + const schema = { + definitions: { + root: { + definitions: { + innerRoot: { + type: 'string' + } + } + } + }, + $ref: '#/definitions/root/definitions/innerRoot' + }; + expect(() => processor.handleRootReference(schema)).toThrowError( + 'Cannot handle input, because it has a root `$ref`, please manually resolve the first reference.' + ); + }); + }); + describe('convertSchemaToCommonModel()', () => { test('Should ignore models which does not have $id', () => { const model = new CommonModel(); mockedReturnModels = [model]; - const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel({}); - expect(Logger.warn).toHaveBeenCalled(); + const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel( + {} + ); expect(Object.entries(commonModels)).toHaveLength(0); }); }); - describe('schemaToCommonModel()', () => { - const getCommonInput = (inputSchemaPath: string) => { - const inputSchemaString = fs.readFileSync(path.resolve(__dirname, inputSchemaPath), 'utf8'); - const inferredSchema = JSON.parse(inputSchemaString); - const inputSchema = Draft7Schema.toSchema(inferredSchema); - return {inputSchema, commonInputModel: JsonSchemaInputProcessor.convertSchemaToCommonModel(inputSchema)}; - }; - test('should simplify schema and return a set of common models', () => { - const inputSchemaPath = './JsonSchemaInputProcessor/draft-7.json'; - const {commonInputModel} = getCommonInput(inputSchemaPath); - expect(commonInputModel).toEqual({test: {$id: 'test'}}); - }); - test('should not contain duplicate models', () => { - const model1 = new CommonModel(); - model1.$id = 'same'; - const model2 = new CommonModel(); - model2.$id = 'same'; - mockedReturnModels = [model1, model2]; - const inputSchemaPath = './JsonSchemaInputProcessor/draft-7.json'; - const {commonInputModel} = getCommonInput(inputSchemaPath); - expect(commonInputModel).toEqual({same: {$id: 'same'}}); - }); - }); describe('reflectSchemaName()', () => { test('should work', () => { const schema = { properties: { + reference: { + $ref: '#/definitions/def' + }, prop: { - type: 'string', + type: 'string' }, allOfCase: { allOf: [ { - type: 'string', + type: 'string' }, { - type: 'string', - }, - ], + type: 'string' + } + ] }, object: { type: 'object', properties: { prop: { - type: 'string', - }, + type: 'string' + } } }, propWithObject: { type: 'object', properties: { propWithObject: { - type: 'object', + type: 'object' } } - }, + } }, patternProperties: { patternProp: { - type: 'string', + type: 'string' } }, dependencies: { dep: { - type: 'string', - }, + type: 'string' + } }, definitions: { def: { - type: 'string', + type: 'string' }, oneOfCase: { oneOf: [ { - type: 'string', + type: 'string' }, { - type: 'string', - }, - ], - }, + type: 'string' + } + ] + } }, anyOf: [ { - type: 'string', + type: 'string' }, { type: 'object', properties: { prop: { - type: 'string', - }, + type: 'string' + } } - }, + } ] }; - const expected = JsonSchemaInputProcessor.reflectSchemaNames(schema, {}, 'root', true) as any; + const expected = JsonSchemaInputProcessor.reflectSchemaNames( + schema, + {}, + 'root', + true + ) as any; // root expect(expected['x-modelgen-inferred-name']).toEqual('root'); // properties - expect(expected.properties.prop['x-modelgen-inferred-name']).toEqual('prop'); - expect(expected.properties.allOfCase.allOf[0]['x-modelgen-inferred-name']).toEqual('allOfCase_allOf_0'); - expect(expected.properties.allOfCase.allOf[1]['x-modelgen-inferred-name']).toEqual('allOfCase_allOf_1'); - expect(expected.properties.object['x-modelgen-inferred-name']).toEqual('object'); - expect(expected.properties.object.properties.prop['x-modelgen-inferred-name']).toEqual('object_prop'); - expect(expected.properties.propWithObject['x-modelgen-inferred-name']).toEqual('propWithObject'); - expect(expected.properties.propWithObject.properties.propWithObject['x-modelgen-inferred-name']).toEqual('propWithObject_propWithObject'); + expect( + expected.properties.reference['x-modelgen-inferred-name'] + ).toBeUndefined(); + expect(expected.properties.prop['x-modelgen-inferred-name']).toEqual( + 'prop' + ); + expect( + expected.properties.allOfCase.allOf[0]['x-modelgen-inferred-name'] + ).toEqual('allOfCase_allOf_0'); + expect( + expected.properties.allOfCase.allOf[1]['x-modelgen-inferred-name'] + ).toEqual('allOfCase_allOf_1'); + expect(expected.properties.object['x-modelgen-inferred-name']).toEqual( + 'object' + ); + expect( + expected.properties.object.properties.prop['x-modelgen-inferred-name'] + ).toEqual('object_prop'); + expect( + expected.properties.propWithObject['x-modelgen-inferred-name'] + ).toEqual('propWithObject'); + expect( + expected.properties.propWithObject.properties.propWithObject[ + 'x-modelgen-inferred-name' + ] + ).toEqual('propWithObject_propWithObject'); // patternProperties - expect(expected.patternProperties.patternProp['x-modelgen-inferred-name']).toEqual('pattern_property_0'); + expect( + expected.patternProperties.patternProp['x-modelgen-inferred-name'] + ).toEqual('pattern_property_0'); // dependencies - expect(expected.dependencies.dep['x-modelgen-inferred-name']).toEqual('dep'); + expect(expected.dependencies.dep['x-modelgen-inferred-name']).toEqual( + 'dep' + ); // definitions - expect(expected.definitions.def['x-modelgen-inferred-name']).toEqual('def'); - expect(expected.definitions.oneOfCase.oneOf[0]['x-modelgen-inferred-name']).toEqual('oneOfCase_oneOf_0'); - expect(expected.definitions.oneOfCase.oneOf[1]['x-modelgen-inferred-name']).toEqual('oneOfCase_oneOf_1'); + expect(expected.definitions.def['x-modelgen-inferred-name']).toEqual( + 'def' + ); + expect( + expected.definitions.oneOfCase.oneOf[0]['x-modelgen-inferred-name'] + ).toEqual('oneOfCase_oneOf_0'); + expect( + expected.definitions.oneOfCase.oneOf[1]['x-modelgen-inferred-name'] + ).toEqual('oneOfCase_oneOf_1'); // anyOf expect(expected.anyOf[0]['x-modelgen-inferred-name']).toEqual('anyOf_0'); expect(expected.anyOf[1]['x-modelgen-inferred-name']).toEqual('anyOf_1'); - expect(expected.anyOf[1].properties.prop['x-modelgen-inferred-name']).toEqual('anyOf_1_prop'); + expect( + expected.anyOf[1].properties.prop['x-modelgen-inferred-name'] + ).toEqual('anyOf_1_prop'); }); }); }); diff --git a/test/processors/OpenAPIInputProcessor.spec.ts b/test/processors/OpenAPIInputProcessor.spec.ts index 40713e70c6..3b1cf5afdb 100644 --- a/test/processors/OpenAPIInputProcessor.spec.ts +++ b/test/processors/OpenAPIInputProcessor.spec.ts @@ -1,28 +1,35 @@ import * as fs from 'fs'; import * as path from 'path'; -import { CommonModel } from '../../src/models'; -import {OpenAPIInputProcessor} from '../../src/processors/OpenAPIInputProcessor'; -const basicDoc = JSON.parse(fs.readFileSync(path.resolve(__dirname, './OpenAPIInputProcessor/basic.json'), 'utf8')); -jest.mock('../../src/interpreter/Interpreter'); -jest.mock('../../src/interpreter/PostInterpreter'); +import { AnyModel, CommonModel } from '../../src/models'; +import { OpenAPIInputProcessor } from '../../src/processors/OpenAPIInputProcessor'; +const basicDoc = JSON.parse( + fs.readFileSync( + path.resolve(__dirname, './OpenAPIInputProcessor/basic.json'), + 'utf8' + ) +); jest.mock('../../src/utils/LoggingInterface'); jest.spyOn(OpenAPIInputProcessor, 'convertToInternalSchema'); - const mockedReturnModels = [new CommonModel()]; +const mockedMetaModel = new AnyModel('', undefined); +jest.mock('../../src/helpers/CommonModelToMetaModel', () => { + return { + convertToMetaModel: jest.fn().mockImplementation(() => { + return mockedMetaModel; + }) + }; +}); jest.mock('../../src/interpreter/Interpreter', () => { return { Interpreter: jest.fn().mockImplementation(() => { return { - interpret: jest.fn().mockImplementation(() => {return mockedReturnModels[0];}) + interpret: jest.fn().mockImplementation(() => { + return mockedReturnModels[0]; + }) }; }) }; }); -jest.mock('../../src/interpreter/PostInterpreter', () => { - return { - postInterpretModel: jest.fn().mockImplementation(() => {return mockedReturnModels;}) - }; -}); describe('OpenAPIInputProcessor', () => { afterAll(() => { jest.restoreAllMocks(); @@ -30,23 +37,23 @@ describe('OpenAPIInputProcessor', () => { describe('shouldProcess()', () => { const processor = new OpenAPIInputProcessor(); test('should be able to process OpenAPI 3.0.0 documents', () => { - const parsedObject = {openapi: '3.0.0'}; + const parsedObject = { openapi: '3.0.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); }); test('should be able to process OpenAPI 3.0.1 documents', () => { - const parsedObject = {openapi: '3.0.1'}; + const parsedObject = { openapi: '3.0.1' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); }); test('should be able to process OpenAPI 3.0.2 documents', () => { - const parsedObject = {openapi: '3.0.2'}; + const parsedObject = { openapi: '3.0.2' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); }); test('should be able to process OpenAPI 3.0.3 documents', () => { - const parsedObject = {openapi: '3.0.3'}; + const parsedObject = { openapi: '3.0.3' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); }); test('should not be able to process other OpenAPI docs', () => { - const parsedObject = {openapi: '1.0'}; + const parsedObject = { openapi: '1.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(false); }); test('should not be able to process document without OpenAPI version', () => { @@ -67,15 +74,19 @@ describe('OpenAPIInputProcessor', () => { describe('process()', () => { test('should throw error when trying to process wrong schema', async () => { const processor = new OpenAPIInputProcessor(); - await expect(processor.process({})) - .rejects - .toThrow('Input is not a OpenAPI document so it cannot be processed'); + await expect(processor.process({})).rejects.toThrow( + 'Input is not a OpenAPI document so it cannot be processed' + ); }); test('should process the OpenAPI document accurately', async () => { const processor = new OpenAPIInputProcessor(); const commonInputModel = await processor.process(basicDoc); expect(commonInputModel).toMatchSnapshot(); - expect((OpenAPIInputProcessor.convertToInternalSchema as any as jest.SpyInstance).mock.calls).toMatchSnapshot(); + expect( + ( + OpenAPIInputProcessor.convertToInternalSchema as any as jest.SpyInstance + ).mock.calls + ).toMatchSnapshot(); }); }); }); diff --git a/test/processors/SwaggerInputProcessor.spec.ts b/test/processors/SwaggerInputProcessor.spec.ts index 5489876949..a2ff98b76e 100644 --- a/test/processors/SwaggerInputProcessor.spec.ts +++ b/test/processors/SwaggerInputProcessor.spec.ts @@ -1,28 +1,36 @@ import * as fs from 'fs'; import * as path from 'path'; -import { CommonModel } from '../../src/models'; -import {SwaggerInputProcessor} from '../../src/processors/SwaggerInputProcessor'; -const basicDoc = JSON.parse(fs.readFileSync(path.resolve(__dirname, './SwaggerInputProcessor/basic.json'), 'utf8')); -jest.mock('../../src/interpreter/Interpreter'); -jest.mock('../../src/interpreter/PostInterpreter'); +import { AnyModel, CommonModel } from '../../src/models'; +import { SwaggerInputProcessor } from '../../src/processors/SwaggerInputProcessor'; +const basicDoc = JSON.parse( + fs.readFileSync( + path.resolve(__dirname, './SwaggerInputProcessor/basic.json'), + 'utf8' + ) +); jest.mock('../../src/utils/LoggingInterface'); jest.spyOn(SwaggerInputProcessor, 'convertToInternalSchema'); - const mockedReturnModels = [new CommonModel()]; +const mockedMetaModel = new AnyModel('', undefined); +jest.mock('../../src/helpers/CommonModelToMetaModel', () => { + return { + convertToMetaModel: jest.fn().mockImplementation(() => { + return mockedMetaModel; + }) + }; +}); jest.mock('../../src/interpreter/Interpreter', () => { return { Interpreter: jest.fn().mockImplementation(() => { return { - interpret: jest.fn().mockImplementation(() => {return mockedReturnModels[0];}) + interpret: jest.fn().mockImplementation(() => { + return mockedReturnModels[0]; + }) }; }) }; }); -jest.mock('../../src/interpreter/PostInterpreter', () => { - return { - postInterpretModel: jest.fn().mockImplementation(() => {return mockedReturnModels;}) - }; -}); + describe('SwaggerInputProcessor', () => { afterAll(() => { jest.restoreAllMocks(); @@ -30,11 +38,11 @@ describe('SwaggerInputProcessor', () => { describe('shouldProcess()', () => { const processor = new SwaggerInputProcessor(); test('should be able to process Swagger 2.0', () => { - const parsedObject = {swagger: '2.0'}; + const parsedObject = { swagger: '2.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); }); test('should not be able to process other swagger docs', () => { - const parsedObject = {swagger: '1.0'}; + const parsedObject = { swagger: '1.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(false); }); test('should not be able to process document without swagger version', () => { @@ -55,15 +63,19 @@ describe('SwaggerInputProcessor', () => { describe('process()', () => { test('should throw error when trying to process wrong schema', async () => { const processor = new SwaggerInputProcessor(); - await expect(processor.process({})) - .rejects - .toThrow('Input is not a Swagger document so it cannot be processed.'); + await expect(processor.process({})).rejects.toThrow( + 'Input is not a Swagger document so it cannot be processed.' + ); }); test('should process the swagger document accurately', async () => { const processor = new SwaggerInputProcessor(); const commonInputModel = await processor.process(basicDoc); expect(commonInputModel).toMatchSnapshot(); - expect((SwaggerInputProcessor.convertToInternalSchema as any as jest.SpyInstance).mock.calls).toMatchSnapshot(); + expect( + ( + SwaggerInputProcessor.convertToInternalSchema as any as jest.SpyInstance + ).mock.calls + ).toMatchSnapshot(); }); }); }); diff --git a/test/processors/TemplateInputProcessor.spec.ts b/test/processors/TemplateInputProcessor.spec.ts new file mode 100644 index 0000000000..ebd7c537f9 --- /dev/null +++ b/test/processors/TemplateInputProcessor.spec.ts @@ -0,0 +1,28 @@ +import { TemplateInputProcessor } from '../../src/processors/TemplateInputProcessor'; + +describe('TemplateInputProcessor', () => { + const processor = new TemplateInputProcessor(); + afterAll(() => { + jest.restoreAllMocks(); + }); + describe('shouldProcess()', () => { + test('should be able to process X', () => { + const input = {}; + expect(processor.shouldProcess(input)).toEqual(true); + }); + }); + + describe('process()', () => { + test('should throw error when trying to process wrong schema', async () => { + const processor = new TemplateInputProcessor(); + await expect(processor.process({})).rejects.toThrow( + 'Input is not X and cannot be processed by this input processor.' + ); + }); + test('should process X accurately', async () => { + const input = {}; + const commonInputModel = await processor.process(input); + expect(commonInputModel).toMatchSnapshot(); + }); + }); +}); diff --git a/test/processors/TypeScriptInputProcessor.spec.ts b/test/processors/TypeScriptInputProcessor.spec.ts index ed2665b370..0b26f97a42 100644 --- a/test/processors/TypeScriptInputProcessor.spec.ts +++ b/test/processors/TypeScriptInputProcessor.spec.ts @@ -2,29 +2,24 @@ import * as fs from 'fs'; import * as path from 'path'; import { CommonModel } from '../../src/models'; import { TypeScriptInputProcessor } from '../../src/processors'; - const baseFile = path.resolve(__dirname, './TypeScriptInputProcessor/index.ts'); -const baseFileContents = fs.readFileSync(path.resolve(__dirname, './TypeScriptInputProcessor/index.ts'), 'utf-8'); - -jest.mock('../../src/interpreter/Interpreter'); -jest.mock('../../src/interpreter/PostInterpreter'); +const baseFileContents = fs.readFileSync( + path.resolve(__dirname, './TypeScriptInputProcessor/index.ts'), + 'utf-8' +); jest.mock('../../src/utils/LoggingInterface'); - const mockedReturnModels = [new CommonModel()]; jest.mock('../../src/interpreter/Interpreter', () => { return { Interpreter: jest.fn().mockImplementation(() => { return { - interpret: jest.fn().mockImplementation(() => {return mockedReturnModels[0];}) + interpret: jest.fn().mockImplementation(() => { + return mockedReturnModels[0]; + }) }; }) }; }); -jest.mock('../../src/interpreter/PostInterpreter', () => { - return { - postInterpretModel: jest.fn().mockImplementation(() => {return mockedReturnModels;}) - }; -}); describe('TypeScriptInputProcessor', () => { describe('shouldProcess()', () => { @@ -35,28 +30,36 @@ describe('TypeScriptInputProcessor', () => { test('should process input', () => { const processsor = new TypeScriptInputProcessor(); - expect(processsor.shouldProcess({ fileContents: baseFileContents, baseFile })).toBeTruthy(); + expect( + processsor.shouldProcess({ fileContents: baseFileContents, baseFile }) + ).toBeTruthy(); }); }); describe('process()', () => { test('should throw error when trying to process wrong input format', async () => { const processor = new TypeScriptInputProcessor(); - await expect(processor.process({})) - .rejects - .toThrowError('Input is not of the valid file format'); + await expect(processor.process({})).rejects.toThrowError( + 'Input is not of the valid file format' + ); }); test('should be able to process input', async () => { const processsor = new TypeScriptInputProcessor(); - const commonModel = await processsor.process({ fileContents: baseFileContents, baseFile }); + const commonModel = await processsor.process({ + fileContents: baseFileContents, + baseFile + }); expect(commonModel).toMatchSnapshot(); }); test('should be able to process input with user provided options', async () => { const processor = new TypeScriptInputProcessor(); - const commonModel = await processor.process({ fileContents: baseFileContents, baseFile }, { - typescript: { - uniqueNames: true + const commonModel = await processor.process( + { fileContents: baseFileContents, baseFile }, + { + typescript: { + uniqueNames: true + } } - }); + ); expect(commonModel).toMatchSnapshot(); }); }); diff --git a/test/processors/TypeScriptInputProcessor/index.ts b/test/processors/TypeScriptInputProcessor/index.ts index b980f64b27..801427fc8f 100644 --- a/test/processors/TypeScriptInputProcessor/index.ts +++ b/test/processors/TypeScriptInputProcessor/index.ts @@ -1,7 +1,6 @@ - export type Shape = { size: number; -} +}; export interface InnerData { age: number; diff --git a/test/processors/__snapshots__/AsyncAPIInputProcessor.spec.ts.snap b/test/processors/__snapshots__/AsyncAPIInputProcessor.spec.ts.snap index a74c93d92a..2daf4cfa4a 100644 --- a/test/processors/__snapshots__/AsyncAPIInputProcessor.spec.ts.snap +++ b/test/processors/__snapshots__/AsyncAPIInputProcessor.spec.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`AsyncAPIInputProcessor process() should be able to process parsed objects 1`] = ` -CommonInputModel { +InputMetaModel { "models": Object {}, "originalInput": AsyncAPIDocument { "_json": Object { @@ -21,19 +21,7 @@ CommonInputModel { "type": "object", "x-parser-schema-id": "", }, - "schemaFormat": "application/vnd.aai.asyncapi;version=2.0.0", "x-parser-message-name": "", - "x-parser-message-parsed": true, - "x-parser-original-payload": Object { - "properties": Object { - "email": Object { - "format": "email", - "type": "string", - }, - }, - "type": "object", - }, - "x-parser-original-schema-format": "application/vnd.aai.asyncapi;version=2.0.0", }, }, }, @@ -43,14 +31,83 @@ CommonInputModel { "title": "Signup service example (internal)", "version": "0.1.0", }, + "x-parser-api-version": 1, "x-parser-spec-parsed": true, }, + "_meta": Object { + "asyncapi": Object { + "input": "{ + \\"asyncapi\\": \\"2.0.0\\", + \\"defaultContentType\\": \\"application/json\\", + \\"info\\": { + \\"title\\": \\"Signup service example (internal)\\", + \\"version\\": \\"0.1.0\\" + }, + \\"channels\\": { + \\"/user/signedup\\": { + \\"subscribe\\": { + \\"message\\": { + \\"payload\\": { + \\"type\\": \\"object\\", + \\"properties\\": { + \\"email\\": { + \\"type\\": \\"string\\", + \\"format\\": \\"email\\" + } + } + } + } + } + } + } +}", + "parsed": Object { + "asyncapi": "2.0.0", + "channels": Object { + "/user/signedup": Object { + "subscribe": Object { + "message": Object { + "payload": Object { + "properties": Object { + "email": Object { + "format": "email", + "type": "string", + "x-parser-schema-id": "", + }, + }, + "type": "object", + "x-parser-schema-id": "", + }, + "x-parser-message-name": "", + }, + }, + }, + }, + "defaultContentType": "application/json", + "info": Object { + "title": "Signup service example (internal)", + "version": "0.1.0", + }, + "x-parser-api-version": 1, + "x-parser-spec-parsed": true, + }, + "semver": Object { + "major": 2, + "minor": 0, + "patch": 0, + "rc": undefined, + "version": "2.0.0", + }, + "source": undefined, + }, + "pointer": "/", + }, }, } `; exports[`AsyncAPIInputProcessor process() should be able to process pure object 1`] = ` -CommonInputModel { +InputMetaModel { "models": Object {}, "originalInput": AsyncAPIDocument { "_json": Object { @@ -70,19 +127,7 @@ CommonInputModel { "type": "object", "x-parser-schema-id": "", }, - "schemaFormat": "application/vnd.aai.asyncapi;version=2.0.0", "x-parser-message-name": "", - "x-parser-message-parsed": true, - "x-parser-original-payload": Object { - "properties": Object { - "email": Object { - "format": "email", - "type": "string", - }, - }, - "type": "object", - }, - "x-parser-original-schema-format": "application/vnd.aai.asyncapi;version=2.0.0", }, }, }, @@ -92,8 +137,77 @@ CommonInputModel { "title": "Signup service example (internal)", "version": "0.1.0", }, + "x-parser-api-version": 1, "x-parser-spec-parsed": true, }, + "_meta": Object { + "asyncapi": Object { + "input": Object { + "asyncapi": "2.0.0", + "channels": Object { + "/user/signedup": Object { + "subscribe": Object { + "message": Object { + "payload": Object { + "properties": Object { + "email": Object { + "format": "email", + "type": "string", + }, + }, + "type": "object", + }, + }, + }, + }, + }, + "defaultContentType": "application/json", + "info": Object { + "title": "Signup service example (internal)", + "version": "0.1.0", + }, + }, + "parsed": Object { + "asyncapi": "2.0.0", + "channels": Object { + "/user/signedup": Object { + "subscribe": Object { + "message": Object { + "payload": Object { + "properties": Object { + "email": Object { + "format": "email", + "type": "string", + "x-parser-schema-id": "", + }, + }, + "type": "object", + "x-parser-schema-id": "", + }, + "x-parser-message-name": "", + }, + }, + }, + }, + "defaultContentType": "application/json", + "info": Object { + "title": "Signup service example (internal)", + "version": "0.1.0", + }, + "x-parser-api-version": 1, + "x-parser-spec-parsed": true, + }, + "semver": Object { + "major": 2, + "minor": 0, + "patch": 0, + "rc": undefined, + "version": "2.0.0", + }, + "source": undefined, + }, + "pointer": "/", + }, }, } `; diff --git a/test/processors/__snapshots__/OpenAPIInputProcessor.spec.ts.snap b/test/processors/__snapshots__/OpenAPIInputProcessor.spec.ts.snap index ccc2d225d3..fc09e3799b 100644 --- a/test/processors/__snapshots__/OpenAPIInputProcessor.spec.ts.snap +++ b/test/processors/__snapshots__/OpenAPIInputProcessor.spec.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`OpenAPIInputProcessor process() should process the OpenAPI document accurately 1`] = ` -CommonInputModel { +InputMetaModel { "models": Object {}, "originalInput": Object { "components": Object { diff --git a/test/processors/__snapshots__/SwaggerInputProcessor.spec.ts.snap b/test/processors/__snapshots__/SwaggerInputProcessor.spec.ts.snap index 3d76baa445..c2ddf0d677 100644 --- a/test/processors/__snapshots__/SwaggerInputProcessor.spec.ts.snap +++ b/test/processors/__snapshots__/SwaggerInputProcessor.spec.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`SwaggerInputProcessor process() should process the swagger document accurately 1`] = ` -CommonInputModel { +InputMetaModel { "models": Object {}, "originalInput": Object { "definitions": Object { diff --git a/test/processors/__snapshots__/TypeScriptInputProcessor.spec.ts.snap b/test/processors/__snapshots__/TypeScriptInputProcessor.spec.ts.snap index 77449f8846..6834bd22ca 100644 --- a/test/processors/__snapshots__/TypeScriptInputProcessor.spec.ts.snap +++ b/test/processors/__snapshots__/TypeScriptInputProcessor.spec.ts.snap @@ -1,12 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`TypeScriptInputProcessor process() should be able to process input 1`] = ` -CommonInputModel { +InputMetaModel { "models": Object {}, - "originalInput": " -export type Shape = { + "originalInput": "export type Shape = { size: number; -} +}; export interface InnerData { age: number; @@ -24,12 +23,11 @@ export interface ShapesData { `; exports[`TypeScriptInputProcessor process() should be able to process input with user provided options 1`] = ` -CommonInputModel { +InputMetaModel { "models": Object {}, - "originalInput": " -export type Shape = { + "originalInput": "export type Shape = { size: number; -} +}; export interface InnerData { age: number; diff --git a/test/utils/LoggingInterface.spec.ts b/test/utils/LoggingInterface.spec.ts index f65c1c5417..bc4e730b7b 100644 --- a/test/utils/LoggingInterface.spec.ts +++ b/test/utils/LoggingInterface.spec.ts @@ -1,4 +1,4 @@ -import {ModelLoggingInterface, Logger} from '../../src'; +import { ModelLoggingInterface, Logger } from '../../src'; describe('LoggingInterface', () => { test('should work when no logging interface have been defined', () => { diff --git a/test/utils/Partials.spec.ts b/test/utils/Partials.spec.ts new file mode 100644 index 0000000000..8bdcee258e --- /dev/null +++ b/test/utils/Partials.spec.ts @@ -0,0 +1,103 @@ +import { DeepPartial, mergePartialAndDefault } from '../../src/utils'; + +describe('mergePartialAndDefault', () => { + test('should handle default objects', () => { + interface TestType { + nestedObject: { + nested: string; + }; + testProp: string; + } + const defaultOptions: TestType = { + nestedObject: { + nested: 'test' + }, + testProp: 'test' + }; + const partialOptions: DeepPartial = { + testProp: 'test2' + }; + const realizedOptions = mergePartialAndDefault( + defaultOptions, + partialOptions + ) as TestType; + expect(realizedOptions.testProp).toEqual('test2'); + expect(realizedOptions.nestedObject.nested).toEqual('test'); + }); + test('should handle overwriting nested objects', () => { + interface TestType { + nestedObject: { + nested: string; + }; + } + const defaultOptions: TestType = { + nestedObject: { + nested: 'test' + } + }; + const partialOptions: DeepPartial = { + nestedObject: { + nested: 'test2' + } + }; + const realizedOptions = mergePartialAndDefault( + defaultOptions, + partialOptions + ) as TestType; + expect(realizedOptions.nestedObject.nested).toEqual('test2'); + }); + test('should not overwrite old realized options ', () => { + interface TestType { + nestedObject: { + nested: string; + }; + } + const defaultOptions: TestType = { + nestedObject: { + nested: 'test' + } + }; + const partialOptions: DeepPartial = { + nestedObject: { + nested: 'test2' + } + }; + const partialOptions2: DeepPartial = { + nestedObject: { + nested: 'test3' + } + }; + const realizedOptions = mergePartialAndDefault( + defaultOptions, + partialOptions + ) as TestType; + mergePartialAndDefault(defaultOptions, partialOptions2) as TestType; + expect(realizedOptions.nestedObject.nested).toEqual('test2'); + }); + + test('should keep class types for instances of non-regular objects', () => { + class TestClass { + test() { + return true; + } + } + interface TestType { + nestedObject: TestClass; + testProp: string; + } + const defaultOptions: TestType = { + nestedObject: new TestClass(), + testProp: 'test' + }; + const partialOptions: DeepPartial = { + testProp: 'test2' + }; + const realizedOptions = mergePartialAndDefault( + defaultOptions, + partialOptions + ) as TestType; + expect(realizedOptions.testProp).toEqual('test2'); + expect(realizedOptions.nestedObject instanceof TestClass).toEqual(true); + expect(realizedOptions.nestedObject.test()).toEqual(true); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 9535617ffe..66ab1cf786 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,5 +23,10 @@ "include": [ "src/**/*.ts", ], - "exclude": ["node_modules", "lib", "examples"] + "exclude": ["node_modules", "lib", "examples", "src/generator/template", "src/processors/TemplateInputProcessor"], + "ts-node": { + "compilerOptions": { + "module": "CommonJS" + } + } } \ No newline at end of file