diff --git a/packages/schema/package.json b/packages/schema/package.json index 3a8118510..552945e13 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -2,7 +2,7 @@ "name": "zenstack", "displayName": "ZenStack CLI and Language Tools", "description": "ZenStack CLI and Language Tools", - "version": "0.1.45", + "version": "0.1.47", "engines": { "vscode": "^1.56.0" }, @@ -48,7 +48,7 @@ "main": "./out/extension.js", "scripts": { "vscode:prepublish": "npm run build && npm run lint", - "build": "tsc && tsc-alias && cp src/language-server/stdlib.zmodel ./out/language-server/ && cp src/generator/*.template.json ./out/generator/", + "build": "npm run langium:generate && tsc && tsc-alias && cp src/language-server/stdlib.zmodel ./out/language-server/ && cp src/generator/*.template.json ./out/generator/", "ts:watch": "tsc --watch", "tsc-alias:watch": "tsc-alias --watch", "lint": "eslint src --ext ts", @@ -63,34 +63,37 @@ "change-case": "^4.1.2", "chevrotain": "^9.1.0", "colors": "^1.4.0", - "commander": "^8.0.0", - "langium": "^0.4.0", - "prisma": "^4.4.0", + "commander": "^8.3.0", + "langium": "^0.5.0", + "pluralize": "^8.0.0", + "prisma": "^4.5.0", "promisify": "^0.0.3", "ts-morph": "^16.0.0", + "uuid": "^9.0.0", "vscode-jsonrpc": "^8.0.2", - "vscode-languageclient": "^7.0.0", - "vscode-languageserver": "^7.0.0", - "vscode-uri": "^3.0.2" + "vscode-languageclient": "^8.0.2", + "vscode-languageserver": "^8.0.2", + "vscode-uri": "^3.0.6" }, "devDependencies": { - "@prisma/internals": "^4.4.0", - "@types/jest": "^29.0.3", - "@types/node": "^14.18.29", + "@prisma/internals": "^4.5.0", + "@types/jest": "^29.2.0", + "@types/node": "^14.18.32", + "@types/pluralize": "^0.0.29", "@types/tmp": "^0.2.3", "@types/uuid": "^8.3.4", - "@types/vscode": "^1.56.0", - "@typescript-eslint/eslint-plugin": "^4.14.1", - "@typescript-eslint/parser": "^4.14.1", + "@types/vscode": "^1.72.0", + "@typescript-eslint/eslint-plugin": "^4.33.0", + "@typescript-eslint/parser": "^4.33.0", "concurrently": "^7.4.0", - "eslint": "^7.19.0", - "jest": "^29.0.3", - "langium-cli": "^0.4.0", + "eslint": "^7.32.0", + "jest": "^29.2.1", + "langium-cli": "^0.5.0", "tmp": "^0.2.1", - "ts-jest": "^29.0.1", + "ts-jest": "^29.0.3", "ts-node": "^10.9.1", "tsc-alias": "^1.7.0", "tsconfig-paths-jest": "^0.0.1", - "typescript": "^4.6.2" + "typescript": "^4.8.4" } } diff --git a/packages/schema/src/cli/index.ts b/packages/schema/src/cli/index.ts index 09abb9973..4dbe5dced 100644 --- a/packages/schema/src/cli/index.ts +++ b/packages/schema/src/cli/index.ts @@ -1,4 +1,5 @@ import { Command, Option } from 'commander'; +import { NodeFileSystem } from 'langium/node'; import { Model } from '../language-server/generated/ast'; import { ZModelLanguageMetaData } from '../language-server/generated/module'; import { createZModelServices } from '../language-server/zmodel-module'; @@ -14,7 +15,7 @@ import path from 'path'; export const generateAction = async (options: { schema: string; }): Promise => { - const services = createZModelServices().ZModel; + const services = createZModelServices(NodeFileSystem).ZModel; const model = await extractAstNode(options.schema, services); const context: Context = { @@ -110,6 +111,8 @@ export default function (): void { ) .addOption(schemaOption) .option('--create-only', 'Create a migration without applying it') + .option('-n --name ', 'Name the migration') + .option('--skip-seed', 'Skip triggering seed') .action(prismaAction('migrate')); migrate diff --git a/packages/schema/src/generator/prisma/expression-writer.ts b/packages/schema/src/generator/prisma/expression-writer.ts index 5ca7ccfc2..19cb9e4d5 100644 --- a/packages/schema/src/generator/prisma/expression-writer.ts +++ b/packages/schema/src/generator/prisma/expression-writer.ts @@ -13,12 +13,10 @@ import { UnaryExpr, } from '@lang/generated/ast'; import { CodeBlockWriter } from 'ts-morph'; +import { GUARD_FIELD_NAME } from '../constants'; import { GeneratorError } from '../types'; -import { TypedNode } from '@lang/types'; import PlainExpressionBuilder from './plain-expression-builder'; -const AUX_GUARD_FIELD = 'zenstack_guard'; - type ComparisonOperator = '==' | '!=' | '>' | '>=' | '<' | '<='; export default class ExpressionWriter { @@ -136,7 +134,7 @@ export default class ExpressionWriter { } private guard(write: () => void) { - this.writer.write(`${AUX_GUARD_FIELD}: `); + this.writer.write(`${GUARD_FIELD_NAME}: `); write(); } @@ -293,7 +291,7 @@ export default class ExpressionWriter { } private isModelTyped(expr: Expression) { - return isDataModel((expr as TypedNode).$resolvedType?.decl); + return isDataModel(expr.$resolvedType?.decl); } mapOperator(operator: '==' | '!=' | '>' | '>=' | '<' | '<=') { diff --git a/packages/schema/src/generator/prisma/prisma-builder.ts b/packages/schema/src/generator/prisma/prisma-builder.ts index ec1d4bbba..88b0a39ea 100644 --- a/packages/schema/src/generator/prisma/prisma-builder.ts +++ b/packages/schema/src/generator/prisma/prisma-builder.ts @@ -199,7 +199,7 @@ export class FieldAttribute { toString() { return ( - `@${this.name}(` + + `${this.name}(` + this.args.map((a) => a.toString()).join(', ') + `)` ); @@ -211,7 +211,7 @@ export class ModelAttribute { toString() { return ( - `@@${this.name}(` + + `${this.name}(` + this.args.map((a) => a.toString()).join(', ') + `)` ); diff --git a/packages/schema/src/generator/prisma/query-gard-generator.ts b/packages/schema/src/generator/prisma/query-gard-generator.ts index a7b7d8553..df9c4942c 100644 --- a/packages/schema/src/generator/prisma/query-gard-generator.ts +++ b/packages/schema/src/generator/prisma/query-gard-generator.ts @@ -86,7 +86,7 @@ export default class QueryGuardGenerator { operation: PolicyOperationKind ) { const attrs = model.attributes.filter( - (attr) => attr.decl.ref?.name === kind + (attr) => attr.decl.ref?.name === `@@${kind}` ); return attrs .filter((attr) => { diff --git a/packages/schema/src/generator/prisma/schema-generator.ts b/packages/schema/src/generator/prisma/schema-generator.ts index d1184f92c..2f99ae586 100644 --- a/packages/schema/src/generator/prisma/schema-generator.ts +++ b/packages/schema/src/generator/prisma/schema-generator.ts @@ -34,16 +34,7 @@ import { ModelFieldType, } from './prisma-builder'; -const supportedProviders = ['postgresql', 'mysql', 'sqlite', 'sqlserver']; -const supportedAttrbutes = [ - 'id', - 'index', - 'relation', - 'default', - 'createdAt', - 'updatedAt', - 'unique', -]; +const excludedAttributes = ['@@allow', '@@deny']; export default class PrismaSchemaGenerator { constructor(private readonly context: Context) {} @@ -90,13 +81,6 @@ export default class PrismaSchemaGenerator { 'Datasource provider must be set to a string' ); } - if (!supportedProviders.includes(provider)) { - throw new GeneratorError( - `Provider ${provider} is not supported. Supported providers: ${supportedProviders.join( - ', ' - )}` - ); - } break; } @@ -157,7 +141,11 @@ export default class PrismaSchemaGenerator { 'client', 'prisma-client-js', path.join('../', this.context.generatedCodeDir, '.prisma'), - ['fieldReference', 'interactiveTransactions'] + [ + 'fieldReference', + 'interactiveTransactions', + 'referentialIntegrity', + ] ); } @@ -169,7 +157,7 @@ export default class PrismaSchemaGenerator { // add an "zenstack_guard" field for dealing with pure auth() related conditions model.addField(GUARD_FIELD_NAME, 'Boolean', [ - new PrismaFieldAttribute('default', [ + new PrismaFieldAttribute('@default', [ new PrismaAttributeArg( undefined, new PrismaAttributeArgValue('Boolean', true) @@ -180,8 +168,8 @@ export default class PrismaSchemaGenerator { // add an "zenstack_transaction" field for tracking records created/updated with nested writes model.addField(TRANSACTION_FIELD_NAME, 'String?'); - for (const attr of decl.attributes.filter((attr) => - supportedAttrbutes.includes(attr.decl.ref?.name!) + for (const attr of decl.attributes.filter( + (attr) => !excludedAttributes.includes(attr.decl.ref?.name!) )) { this.generateModelAttribute(model, attr); } @@ -195,7 +183,9 @@ export default class PrismaSchemaGenerator { ); const attributes = field.attributes - .filter((attr) => supportedAttrbutes.includes(attr.decl.ref?.name!)) + .filter( + (attr) => !excludedAttributes.includes(attr.decl.ref?.name!) + ) .map((attr) => this.makeFieldAttribute(attr)); model.addField(field.name, type, attributes); } diff --git a/packages/schema/src/generator/utils.ts b/packages/schema/src/generator/utils.ts index 52c35139e..6ff5eb3e5 100644 --- a/packages/schema/src/generator/utils.ts +++ b/packages/schema/src/generator/utils.ts @@ -4,6 +4,6 @@ export function extractDataModelsWithAllowRules(model: Model) { return model.declarations.filter( (d) => isDataModel(d) && - !!d.attributes.find((attr) => attr.decl.ref?.name === 'allow') + !!d.attributes.find((attr) => attr.decl.ref?.name === '@@allow') ) as DataModel[]; } diff --git a/packages/schema/src/language-server/constants.ts b/packages/schema/src/language-server/constants.ts new file mode 100644 index 000000000..48a0a92e0 --- /dev/null +++ b/packages/schema/src/language-server/constants.ts @@ -0,0 +1,17 @@ +export const SUPPORTED_PROVIDERS = [ + 'postgresql', + 'mysql', + 'sqlite', + 'sqlserver', +]; + +export const SCALAR_TYPES = [ + 'String', + 'Int', + 'Float', + 'Decimal', + 'BigInt', + 'Boolean', + 'Bytes', + 'DateTime', +]; diff --git a/packages/schema/src/language-server/generated/ast.ts b/packages/schema/src/language-server/generated/ast.ts index 0ebbb400f..326027ba0 100644 --- a/packages/schema/src/language-server/generated/ast.ts +++ b/packages/schema/src/language-server/generated/ast.ts @@ -1,11 +1,11 @@ /****************************************************************************** - * This file was generated by langium-cli 0.4.0. + * This file was generated by langium-cli 0.5.0. * DO NOT EDIT MANUALLY! ******************************************************************************/ /* eslint-disable @typescript-eslint/array-type */ /* eslint-disable @typescript-eslint/no-empty-interface */ -import { AstNode, AstReflection, Reference, isAstNode, TypeMetaData } from 'langium'; +import { AstNode, AstReflection, Reference, ReferenceInfo, isAstNode, TypeMetaData } from 'langium'; export type AbstractDeclaration = Attribute | DataModel | DataSource | Enum | Function; @@ -15,6 +15,14 @@ export function isAbstractDeclaration(item: unknown): item is AbstractDeclaratio return reflection.isInstance(item, AbstractDeclaration); } +export type AttributeName = string; + +export type BuiltinType = 'BigInt' | 'Boolean' | 'Bytes' | 'DateTime' | 'Decimal' | 'Float' | 'Int' | 'Json' | 'String'; + +export type DataModelAttributeName = string; + +export type DataModelFieldAttributeName = string; + export type Expression = ArrayExpr | BinaryExpr | InvocationExpr | LiteralExpr | MemberAccessExpr | NullExpr | ReferenceExpr | ThisExpr | UnaryExpr; export const Expression = 'Expression'; @@ -23,6 +31,8 @@ export function isExpression(item: unknown): item is Expression { return reflection.isInstance(item, Expression); } +export type ExpressionType = 'Any' | 'Boolean' | 'DateTime' | 'Float' | 'Int' | 'Null' | 'String'; + export type ReferenceTarget = DataModelField | EnumField | FunctionParam; export const ReferenceTarget = 'ReferenceTarget'; @@ -64,7 +74,7 @@ export function isArrayExpr(item: unknown): item is ArrayExpr { export interface Attribute extends AstNode { readonly $container: Model; - name: string + name: AttributeName params: Array } @@ -88,8 +98,8 @@ export function isAttributeArg(item: unknown): item is AttributeArg { export interface AttributeParam extends AstNode { readonly $container: Attribute; + default: boolean name: string - positional: boolean type: AttributeParamType } @@ -103,7 +113,8 @@ export interface AttributeParamType extends AstNode { readonly $container: AttributeParam; array: boolean optional: boolean - type: 'Boolean' | 'DateTime' | 'FieldReference' | 'Int' | 'JSON' | 'String' + reference?: Reference + type?: 'ContextType' | 'FieldReference' | ExpressionType } export const AttributeParamType = 'AttributeParamType'; @@ -180,7 +191,7 @@ export interface DataModelFieldType extends AstNode { array: boolean optional: boolean reference?: Reference - type?: 'Boolean' | 'DateTime' | 'Int' | 'JSON' | 'String' + type?: BuiltinType } export const DataModelFieldType = 'DataModelFieldType'; @@ -266,7 +277,7 @@ export interface FunctionParamType extends AstNode { readonly $container: Function | FunctionParam; array: boolean reference?: Reference - type?: 'Boolean' | 'DateTime' | 'Int' | 'JSON' | 'String' + type?: ExpressionType } export const FunctionParamType = 'FunctionParamType'; @@ -380,8 +391,6 @@ export function isUnaryExpr(item: unknown): item is UnaryExpr { export type ZModelAstType = 'AbstractDeclaration' | 'Argument' | 'ArrayExpr' | 'Attribute' | 'AttributeArg' | 'AttributeParam' | 'AttributeParamType' | 'BinaryExpr' | 'DataModel' | 'DataModelAttribute' | 'DataModelField' | 'DataModelFieldAttribute' | 'DataModelFieldType' | 'DataSource' | 'DataSourceField' | 'Enum' | 'EnumField' | 'Expression' | 'Function' | 'FunctionParam' | 'FunctionParamType' | 'InvocationExpr' | 'LiteralExpr' | 'MemberAccessExpr' | 'Model' | 'NullExpr' | 'ReferenceArg' | 'ReferenceExpr' | 'ReferenceTarget' | 'ThisExpr' | 'TypeDeclaration' | 'UnaryExpr'; -export type ZModelAstReference = 'DataModelAttribute:decl' | 'DataModelFieldAttribute:decl' | 'DataModelFieldType:reference' | 'FunctionParamType:reference' | 'InvocationExpr:function' | 'MemberAccessExpr:member' | 'ReferenceExpr:target'; - export class ZModelAstReflection implements AstReflection { getAllTypes(): string[] { @@ -428,8 +437,12 @@ export class ZModelAstReflection implements AstReflection { } } - getReferenceType(referenceId: ZModelAstReference): string { + getReferenceType(refInfo: ReferenceInfo): string { + const referenceId = `${refInfo.container.$type}:${refInfo.property}`; switch (referenceId) { + case 'AttributeParamType:reference': { + return TypeDeclaration; + } case 'DataModelAttribute:decl': { return Attribute; } @@ -479,7 +492,7 @@ export class ZModelAstReflection implements AstReflection { return { name: 'AttributeParam', mandatory: [ - { name: 'positional', type: 'boolean' } + { name: 'default', type: 'boolean' } ] }; } diff --git a/packages/schema/src/language-server/generated/grammar.ts b/packages/schema/src/language-server/generated/grammar.ts index e0a670a29..886cdff50 100644 --- a/packages/schema/src/language-server/generated/grammar.ts +++ b/packages/schema/src/language-server/generated/grammar.ts @@ -1,12 +1,12 @@ /****************************************************************************** - * This file was generated by langium-cli 0.4.0. + * This file was generated by langium-cli 0.5.0. * DO NOT EDIT MANUALLY! ******************************************************************************/ -import { loadGrammar, Grammar } from 'langium'; +import { loadGrammarFromJson, Grammar } from 'langium'; let loadedZModelGrammar: Grammar | undefined; -export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelGrammar = loadGrammar(`{ +export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModelGrammar = loadGrammarFromJson(`{ "$type": "Grammar", "isDeclared": true, "name": "ZModel", @@ -15,7 +15,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "ParserRule", "name": "Model", "entry": true, - "alternatives": { + "definition": { "$type": "Assignment", "feature": "declarations", "operator": "+=", @@ -37,7 +37,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "AbstractDeclaration", - "alternatives": { + "definition": { "$type": "Alternatives", "elements": [ { @@ -87,7 +87,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "DataSource", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -121,7 +121,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG }, "arguments": [] }, - "cardinality": "+" + "cardinality": "*" }, { "$type": "Keyword", @@ -139,7 +139,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "DataSourceField", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -194,7 +194,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "Expression", - "alternatives": { + "definition": { "$type": "RuleCall", "rule": { "$refText": "LogicalExpr" @@ -211,7 +211,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "LiteralExpr", - "alternatives": { + "definition": { "$type": "Assignment", "feature": "value", "operator": "=", @@ -228,7 +228,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "RuleCall", "rule": { - "$refText": "INT" + "$refText": "NUMBER" }, "arguments": [] }, @@ -252,7 +252,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "ArrayExpr", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -315,7 +315,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "ThisExpr", - "alternatives": { + "definition": { "$type": "Assignment", "feature": "value", "operator": "=", @@ -337,7 +337,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "NullExpr", - "alternatives": { + "definition": { "$type": "Assignment", "feature": "value", "operator": "=", @@ -359,7 +359,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "ReferenceExpr", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -415,7 +415,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "ParserRule", "name": "ReferenceArgList", "fragment": true, - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -463,7 +463,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "ReferenceArg", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -509,7 +509,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "InvocationExpr", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -552,7 +552,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "UnaryExpr", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -592,7 +592,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "InferredType", "name": "Expression" }, - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -654,7 +654,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "InferredType", "name": "Expression" }, - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -737,7 +737,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "InferredType", "name": "Expression" }, - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -816,7 +816,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "InferredType", "name": "Expression" }, - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -887,7 +887,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "InferredType", "name": "Expression" }, - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -958,7 +958,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "InferredType", "name": "Expression" }, - "alternatives": { + "definition": { "$type": "Alternatives", "elements": [ { @@ -1043,7 +1043,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "ParserRule", "name": "ArgumentList", "fragment": true, - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -1091,7 +1091,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "Argument", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -1140,7 +1140,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "DataModel", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -1209,7 +1209,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "DataModelField", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -1261,18 +1261,23 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "DataModelFieldType", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { "$type": "Alternatives", "elements": [ { - "$type": "RuleCall", - "rule": { - "$refText": "BuiltinType" - }, - "arguments": [] + "$type": "Assignment", + "feature": "type", + "operator": "=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$refText": "BuiltinType" + }, + "arguments": [] + } }, { "$type": "Assignment", @@ -1327,7 +1332,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "Enum", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -1379,7 +1384,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "EnumField", - "alternatives": { + "definition": { "$type": "Assignment", "feature": "name", "operator": "=", @@ -1401,7 +1406,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "Function", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -1468,6 +1473,10 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "Keyword", "value": ")" }, + { + "$type": "Keyword", + "value": ":" + }, { "$type": "Assignment", "feature": "returnType", @@ -1513,7 +1522,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "FunctionParam", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -1528,6 +1537,10 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "arguments": [] } }, + { + "$type": "Keyword", + "value": ":" + }, { "$type": "Assignment", "feature": "type", @@ -1552,18 +1565,23 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "FunctionParamType", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { "$type": "Alternatives", "elements": [ { - "$type": "RuleCall", - "rule": { - "$refText": "BuiltinType" - }, - "arguments": [] + "$type": "Assignment", + "feature": "type", + "operator": "=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$refText": "ExpressionType" + }, + "arguments": [] + } }, { "$type": "Assignment", @@ -1598,10 +1616,94 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "parameters": [], "wildcard": false }, + { + "$type": "ParserRule", + "name": "DataModelAttributeName", + "dataType": "string", + "definition": { + "$type": "Group", + "elements": [ + { + "$type": "Keyword", + "value": "@@" + }, + { + "$type": "RuleCall", + "rule": { + "$refText": "ID" + }, + "arguments": [] + } + ] + }, + "definesHiddenTokens": false, + "entry": false, + "fragment": false, + "hiddenTokens": [], + "parameters": [], + "wildcard": false + }, + { + "$type": "ParserRule", + "name": "DataModelFieldAttributeName", + "dataType": "string", + "definition": { + "$type": "Group", + "elements": [ + { + "$type": "Keyword", + "value": "@" + }, + { + "$type": "RuleCall", + "rule": { + "$refText": "ID" + }, + "arguments": [] + } + ] + }, + "definesHiddenTokens": false, + "entry": false, + "fragment": false, + "hiddenTokens": [], + "parameters": [], + "wildcard": false + }, + { + "$type": "ParserRule", + "name": "AttributeName", + "dataType": "string", + "definition": { + "$type": "Alternatives", + "elements": [ + { + "$type": "RuleCall", + "rule": { + "$refText": "DataModelAttributeName" + }, + "arguments": [] + }, + { + "$type": "RuleCall", + "rule": { + "$refText": "DataModelFieldAttributeName" + }, + "arguments": [] + } + ] + }, + "definesHiddenTokens": false, + "entry": false, + "fragment": false, + "hiddenTokens": [], + "parameters": [], + "wildcard": false + }, { "$type": "ParserRule", "name": "Attribute", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -1615,7 +1717,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "terminal": { "$type": "RuleCall", "rule": { - "$refText": "ID" + "$refText": "AttributeName" }, "arguments": [] } @@ -1680,17 +1782,18 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "AttributeParam", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { "$type": "Assignment", - "feature": "positional", + "feature": "default", "operator": "?=", "terminal": { "$type": "Keyword", "value": "_" - } + }, + "cardinality": "?" }, { "$type": "Assignment", @@ -1704,6 +1807,10 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "arguments": [] } }, + { + "$type": "Keyword", + "value": ":" + }, { "$type": "Assignment", "feature": "type", @@ -1728,26 +1835,54 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "AttributeParamType", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { "$type": "Alternatives", "elements": [ { - "$type": "RuleCall", - "rule": { - "$refText": "BuiltinType" - }, - "arguments": [] + "$type": "Assignment", + "feature": "type", + "operator": "=", + "terminal": { + "$type": "Alternatives", + "elements": [ + { + "$type": "RuleCall", + "rule": { + "$refText": "ExpressionType" + }, + "arguments": [] + }, + { + "$type": "Keyword", + "value": "FieldReference" + }, + { + "$type": "Keyword", + "value": "ContextType" + } + ] + } }, { "$type": "Assignment", - "feature": "type", + "feature": "reference", "operator": "=", "terminal": { - "$type": "Keyword", - "value": "FieldReference" + "$type": "CrossReference", + "type": { + "$refText": "TypeDeclaration" + }, + "terminal": { + "$type": "RuleCall", + "rule": { + "$refText": "ID" + }, + "arguments": [] + }, + "deprecatedSyntax": false } } ] @@ -1759,7 +1894,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "terminal": { "$type": "Keyword", "value": "[]" - } + }, + "cardinality": "?" }, { "$type": "Assignment", @@ -1768,7 +1904,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "terminal": { "$type": "Keyword", "value": "?" - } + }, + "cardinality": "?" } ] }, @@ -1782,13 +1919,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "DataModelFieldAttribute", - "alternatives": { + "definition": { "$type": "Group", "elements": [ - { - "$type": "Keyword", - "value": "@" - }, { "$type": "Assignment", "feature": "decl", @@ -1798,6 +1931,13 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "type": { "$refText": "Attribute" }, + "terminal": { + "$type": "RuleCall", + "rule": { + "$refText": "DataModelFieldAttributeName" + }, + "arguments": [] + }, "deprecatedSyntax": false } }, @@ -1835,13 +1975,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "DataModelAttribute", - "alternatives": { + "definition": { "$type": "Group", "elements": [ - { - "$type": "Keyword", - "value": "@@" - }, { "$type": "Assignment", "feature": "decl", @@ -1851,6 +1987,13 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "type": { "$refText": "Attribute" }, + "terminal": { + "$type": "RuleCall", + "rule": { + "$refText": "DataModelAttributeName" + }, + "arguments": [] + }, "deprecatedSyntax": false } }, @@ -1889,7 +2032,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "ParserRule", "name": "AttributeArgList", "fragment": true, - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -1937,7 +2080,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "ParserRule", "name": "AttributeArg", - "alternatives": { + "definition": { "$type": "Group", "elements": [ { @@ -1983,42 +2126,98 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "parameters": [], "wildcard": false }, + { + "$type": "ParserRule", + "name": "ExpressionType", + "dataType": "string", + "definition": { + "$type": "Alternatives", + "elements": [ + { + "$type": "Keyword", + "value": "String" + }, + { + "$type": "Keyword", + "value": "Int" + }, + { + "$type": "Keyword", + "value": "Float" + }, + { + "$type": "Keyword", + "value": "Boolean" + }, + { + "$type": "Keyword", + "value": "DateTime" + }, + { + "$type": "Keyword", + "value": "Null" + }, + { + "$type": "Keyword", + "value": "Any" + } + ] + }, + "definesHiddenTokens": false, + "entry": false, + "fragment": false, + "hiddenTokens": [], + "parameters": [], + "wildcard": false + }, { "$type": "ParserRule", "name": "BuiltinType", - "fragment": true, - "alternatives": { - "$type": "Assignment", - "feature": "type", - "operator": "=", - "terminal": { - "$type": "Alternatives", - "elements": [ - { - "$type": "Keyword", - "value": "String" - }, - { - "$type": "Keyword", - "value": "Boolean" - }, - { - "$type": "Keyword", - "value": "Int" - }, - { - "$type": "Keyword", - "value": "DateTime" - }, - { - "$type": "Keyword", - "value": "JSON" - } - ] - } + "dataType": "string", + "definition": { + "$type": "Alternatives", + "elements": [ + { + "$type": "Keyword", + "value": "String" + }, + { + "$type": "Keyword", + "value": "Boolean" + }, + { + "$type": "Keyword", + "value": "Int" + }, + { + "$type": "Keyword", + "value": "BigInt" + }, + { + "$type": "Keyword", + "value": "Float" + }, + { + "$type": "Keyword", + "value": "Decimal" + }, + { + "$type": "Keyword", + "value": "DateTime" + }, + { + "$type": "Keyword", + "value": "Json" + }, + { + "$type": "Keyword", + "value": "Bytes" + } + ] }, "definesHiddenTokens": false, "entry": false, + "fragment": false, "hiddenTokens": [], "parameters": [], "wildcard": false @@ -2027,7 +2226,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "TerminalRule", "hidden": true, "name": "WS", - "terminal": { + "definition": { "$type": "RegexToken", "regex": "\\\\s+" }, @@ -2040,7 +2239,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "ReturnType", "name": "boolean" }, - "terminal": { + "definition": { "$type": "RegexToken", "regex": "true|false" }, @@ -2050,7 +2249,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "TerminalRule", "name": "NULL", - "terminal": { + "definition": { "$type": "CharacterRange", "left": { "$type": "Keyword", @@ -2063,7 +2262,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "TerminalRule", "name": "THIS", - "terminal": { + "definition": { "$type": "CharacterRange", "left": { "$type": "Keyword", @@ -2076,7 +2275,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "TerminalRule", "name": "ID", - "terminal": { + "definition": { "$type": "RegexToken", "regex": "[_a-zA-Z][\\\\w_]*" }, @@ -2086,7 +2285,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG { "$type": "TerminalRule", "name": "STRING", - "terminal": { + "definition": { "$type": "RegexToken", "regex": "\\"[^\\"]*\\"|'[^']*'" }, @@ -2095,14 +2294,14 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG }, { "$type": "TerminalRule", - "name": "INT", + "name": "NUMBER", "type": { "$type": "ReturnType", "name": "number" }, - "terminal": { + "definition": { "$type": "RegexToken", - "regex": "[+-]?[0-9]+" + "regex": "[+-]?[0-9]+(\\\\.[0-9]+)?" }, "fragment": false, "hidden": false @@ -2111,7 +2310,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "TerminalRule", "hidden": true, "name": "ML_COMMENT", - "terminal": { + "definition": { "$type": "RegexToken", "regex": "\\\\/\\\\*[\\\\s\\\\S]*?\\\\*\\\\/" }, @@ -2121,7 +2320,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ||(loadedZModelG "$type": "TerminalRule", "hidden": true, "name": "SL_COMMENT", - "terminal": { + "definition": { "$type": "RegexToken", "regex": "\\\\/\\\\/[^\\\\n\\\\r]*" }, diff --git a/packages/schema/src/language-server/generated/module.ts b/packages/schema/src/language-server/generated/module.ts index 4ce1978b8..2ba861e07 100644 --- a/packages/schema/src/language-server/generated/module.ts +++ b/packages/schema/src/language-server/generated/module.ts @@ -1,5 +1,5 @@ /****************************************************************************** - * This file was generated by langium-cli 0.4.0. + * This file was generated by langium-cli 0.5.0. * DO NOT EDIT MANUALLY! ******************************************************************************/ diff --git a/packages/schema/src/language-server/langium-ext.ts b/packages/schema/src/language-server/langium-ext.ts new file mode 100644 index 000000000..06ccb8e3c --- /dev/null +++ b/packages/schema/src/language-server/langium-ext.ts @@ -0,0 +1,7 @@ +import { ResolvedType } from '@lang/types'; + +declare module 'langium' { + export interface AstNode { + $resolvedType?: ResolvedType; + } +} diff --git a/packages/schema/src/language-server/main.ts b/packages/schema/src/language-server/main.ts index 8abf8ee04..301561643 100644 --- a/packages/schema/src/language-server/main.ts +++ b/packages/schema/src/language-server/main.ts @@ -1,4 +1,5 @@ import { startLanguageServer } from 'langium'; +import { NodeFileSystem } from 'langium/node'; import { createConnection, ProposedFeatures } from 'vscode-languageserver/node'; import { createZModelServices } from './zmodel-module'; @@ -6,7 +7,7 @@ import { createZModelServices } from './zmodel-module'; const connection = createConnection(ProposedFeatures.all); // Inject the shared services and language-specific services -const { shared } = createZModelServices({ connection }); +const { shared } = createZModelServices({ connection, ...NodeFileSystem }); // Start the language server with the shared services startLanguageServer(shared); diff --git a/packages/schema/src/language-server/stdlib.zmodel b/packages/schema/src/language-server/stdlib.zmodel index e8914ece6..3682d3f22 100644 --- a/packages/schema/src/language-server/stdlib.zmodel +++ b/packages/schema/src/language-server/stdlib.zmodel @@ -1,22 +1,23 @@ -enum ReferentialIntegrity { +enum ReferentialAction { Cascade } -function env() String {} -function auth() User {} -function now() DateTime {} -function uuid() String {} -function cuid() String {} +function env(): String {} +function auth(): User {} +function now(): DateTime {} +function uuid(): String {} +function cuid(): String {} function autoincrement() Int {} -attribute id() -attribute index() -attribute relation() -attribute default() -attribute updatedAt() -attribute unique() -attribute allow() -attribute deny() -attribute email() -attribute url() -attribute length() +attribute @id(map: String?) +attribute @default(_ value: ContextType) +attribute @unique(map: String?) +attribute @@unique(_ fields: FieldReference[], name: String?, map: String?) +attribute @@index(_ fields: FieldReference[], map: String?) +attribute @relation(_ name: String?, fields: FieldReference[]?, references: FieldReference[]?, onDelete: ReferentialAction?, onUpdate: ReferentialAction?, map: String?) +attribute @map(_ name: String) +attribute @@map(_ name: String) +attribute @updatedAt() + +attribute @@allow(_ operation: String, _ condition: Boolean) +attribute @@deny(_ operation: String, _ condition: Boolean) diff --git a/packages/schema/src/language-server/types.ts b/packages/schema/src/language-server/types.ts index 950781b6c..ba64ef7d7 100644 --- a/packages/schema/src/language-server/types.ts +++ b/packages/schema/src/language-server/types.ts @@ -1,9 +1,13 @@ -import { AstNode } from 'langium'; -import { AbstractDeclaration } from './generated/ast'; +import { AstNode, ValidationAcceptor } from 'langium'; +import { AbstractDeclaration, ExpressionType } from './generated/ast'; -export type TypedNode = AstNode & { - $resolvedType?: { - decl?: string | AbstractDeclaration; - array?: boolean; - }; +export type ResolvedShape = ExpressionType | AbstractDeclaration; + +export type ResolvedType = { + decl?: ResolvedShape; + array?: boolean; }; + +export interface AstValidator { + validate(node: T, accept: ValidationAcceptor): void; +} diff --git a/packages/schema/src/language-server/validator/attribute-validator.ts b/packages/schema/src/language-server/validator/attribute-validator.ts new file mode 100644 index 000000000..1776a001b --- /dev/null +++ b/packages/schema/src/language-server/validator/attribute-validator.ts @@ -0,0 +1,7 @@ +import { Attribute } from '@lang/generated/ast'; +import { AstValidator } from '@lang/types'; +import { ValidationAcceptor } from 'langium'; + +export default class AttributeValidator implements AstValidator { + validate(attr: Attribute, accept: ValidationAcceptor) {} +} diff --git a/packages/schema/src/language-server/validator/datamodel-validator.ts b/packages/schema/src/language-server/validator/datamodel-validator.ts new file mode 100644 index 000000000..c639ca2a7 --- /dev/null +++ b/packages/schema/src/language-server/validator/datamodel-validator.ts @@ -0,0 +1,307 @@ +import { SCALAR_TYPES } from '@lang/constants'; +import { + ArrayExpr, + AttributeParam, + DataModel, + DataModelAttribute, + DataModelField, + DataModelFieldAttribute, + isDataModel, + isLiteralExpr, + ReferenceExpr, +} from '@lang/generated/ast'; +import { AstValidator } from '@lang/types'; +import { ValidationAcceptor } from 'langium'; +import { + assignableToAttributeParam, + validateDuplicatedDeclarations, +} from './utils'; +import pluralize from 'pluralize'; + +export default class DataModelValidator implements AstValidator { + validate(dm: DataModel, accept: ValidationAcceptor): void { + validateDuplicatedDeclarations(dm.fields, accept); + this.validateFields(dm, accept); + this.validateAttributes(dm, accept); + } + + private validateFields(dm: DataModel, accept: ValidationAcceptor) { + const idFields = dm.fields.filter((f) => + f.attributes.find((attr) => attr.decl.ref?.name === '@id') + ); + if (idFields.length === 0) { + accept('error', 'Model must include a field with @id attribute', { + node: dm, + }); + } else if (idFields.length > 1) { + accept( + 'error', + 'Model can include at most one field with @id attribute', + { + node: dm, + } + ); + } else { + if (idFields[0].type.optional) { + accept( + 'error', + 'Field with @id attribute must not be optional', + { node: idFields[0] } + ); + } + + if ( + idFields[0].type.array || + !idFields[0].type.type || + !SCALAR_TYPES.includes(idFields[0].type.type) + ) { + accept( + 'error', + 'Field with @id attribute must be of scalar type', + { node: idFields[0] } + ); + } + } + + dm.fields.forEach((field) => this.validateField(field, accept)); + } + + private validateField( + field: DataModelField, + accept: ValidationAcceptor + ): void { + if (field.type.array && field.type.optional) { + accept( + 'error', + 'Optional lists are not supported. Use either `Type[]` or `Type?`', + { node: field.type } + ); + } + + field.attributes.forEach((attr) => + this.validateAttributeApplication(attr, accept) + ); + + if (isDataModel(field.type.reference?.ref)) { + this.validateRelationField(field, accept); + } + } + + private validateAttributes(dm: DataModel, accept: ValidationAcceptor) { + dm.attributes.forEach((attr) => { + this.validateAttributeApplication(attr, accept); + }); + } + + private validateAttributeApplication( + attr: DataModelAttribute | DataModelFieldAttribute, + accept: ValidationAcceptor + ) { + const decl = attr.decl.ref; + if (!decl) { + throw new Error(`Reference unresolved: ${attr.decl.$refText}`); + } + + const filledParams = new Set(); + + for (const arg of attr.args) { + let paramDecl: AttributeParam | undefined; + if (!arg.name) { + paramDecl = decl.params.find( + (p) => p.default && !filledParams.has(p) + ); + if (!paramDecl) { + accept('error', `Unexpected unnamed argument`, { + node: arg, + }); + return false; + } + } else { + paramDecl = decl.params.find((p) => p.name === arg.name); + if (!paramDecl) { + accept( + 'error', + `Attribute "${decl.name}" doesn't have a parameter named "${arg.name}"`, + { + node: arg, + } + ); + return false; + } + } + + if (!assignableToAttributeParam(arg, paramDecl, attr)) { + accept('error', `Value is not assignable to parameter`, { + node: arg, + }); + return false; + } + + if (filledParams.has(paramDecl)) { + accept( + 'error', + `Parameter "${paramDecl.name}" is already provided`, + { node: arg } + ); + return false; + } + filledParams.add(paramDecl); + } + + const missingParams = decl.params.filter( + (p) => !p.type.optional && !filledParams.has(p) + ); + if (missingParams.length > 0) { + accept( + 'error', + `Required ${pluralize( + 'parameter', + missingParams.length + )} not provided: ${missingParams + .map((p) => p.name) + .join(', ')}`, + { node: attr } + ); + return false; + } + + return true; + } + + private parseRelation(field: DataModelField, accept?: ValidationAcceptor) { + const relAttr = field.attributes.find( + (attr) => attr.decl.ref?.name === '@relation' + ); + + let name: string | undefined; + let fields: ReferenceExpr[] | undefined; + let references: ReferenceExpr[] | undefined; + let valid = true; + + if (!relAttr) { + return { attr: relAttr, name, fields, references, valid: true }; + } + + for (const arg of relAttr.args) { + if (!arg.name || arg.name === 'name') { + if (isLiteralExpr(arg.value)) { + name = arg.value.value as string; + } + } else if (arg.name === 'fields') { + fields = (arg.value as ArrayExpr).items as ReferenceExpr[]; + if (fields.length === 0) { + if (accept) { + accept('error', `"fields" value cannot be emtpy`, { + node: arg, + }); + } + valid = false; + } + } else if (arg.name === 'references') { + references = (arg.value as ArrayExpr).items as ReferenceExpr[]; + if (references.length === 0) { + if (accept) { + accept('error', `"references" value cannot be emtpy`, { + node: arg, + }); + } + valid = false; + } + } + } + + return { attr: relAttr, name, fields, references, valid }; + } + + private validateRelationField( + field: DataModelField, + accept: ValidationAcceptor + ) { + const thisRelation = this.parseRelation(field, accept); + if (!thisRelation.valid) { + return; + } + + const oppositeModel = field.type.reference!.ref! as DataModel; + + let oppositeFields = oppositeModel.fields.filter( + (f) => f.type.reference?.ref === field.$container + ); + oppositeFields = oppositeFields.filter((f) => { + const fieldRel = this.parseRelation(f); + return fieldRel.valid && fieldRel.name === thisRelation.name; + }); + + if (oppositeFields.length === 0) { + accept( + 'error', + `The relation field "${field.name}" on model "${field.$container.name}" is missing an opposite relation field on model "${oppositeModel.name}"`, + { node: field } + ); + return; + } else if (oppositeFields.length > 1) { + oppositeFields.forEach((f) => + accept( + 'error', + `Fields ${oppositeFields + .map((f) => '"' + f.name + '"') + .join(', ')} on model ${ + oppositeModel.name + } refer to the same relation to model "${ + field.$container.name + }"`, + { node: f } + ) + ); + return; + } + + const oppositeField = oppositeFields[0]; + const oppositeRelation = this.parseRelation(oppositeField); + + let relationOwner: DataModelField; + + if (thisRelation?.references?.length && thisRelation.fields?.length) { + if (oppositeRelation?.references || oppositeRelation?.fields) { + accept( + 'error', + '"fields" and "references" must be provided only on one side of relation field', + { node: oppositeField } + ); + return; + } else { + relationOwner = oppositeField; + } + } else if ( + oppositeRelation?.references?.length && + oppositeRelation.fields?.length + ) { + if (thisRelation?.references || thisRelation?.fields) { + accept( + 'error', + '"fields" and "references" must be provided only on one side of relation field', + { node: field } + ); + return; + } else { + relationOwner = field; + } + } else { + [field, oppositeField].forEach((f) => + accept( + 'error', + 'Field for one side of relation must carry @relation attribute with both "fields" and "references" fields', + { node: f } + ) + ); + return; + } + + if (!relationOwner.type.array && !relationOwner.type.optional) { + accept('error', 'Relation field needs to be list or optional', { + node: relationOwner, + }); + return; + } + } +} diff --git a/packages/schema/src/language-server/validator/datasource-validator.ts b/packages/schema/src/language-server/validator/datasource-validator.ts new file mode 100644 index 000000000..5fa4ff78d --- /dev/null +++ b/packages/schema/src/language-server/validator/datasource-validator.ts @@ -0,0 +1,99 @@ +import { DataSource, isInvocationExpr } from '@lang/generated/ast'; +import { AstValidator } from '@lang/types'; +import { ValidationAcceptor } from 'langium'; +import { getStringLiteral, validateDuplicatedDeclarations } from './utils'; +import { SUPPORTED_PROVIDERS } from '../constants'; + +const supportedFields = [ + 'provider', + 'url', + 'shadowDatabaseUrl', + 'relationMode', +]; + +export default class DataSourceValidator implements AstValidator { + validate(ds: DataSource, accept: ValidationAcceptor): void { + validateDuplicatedDeclarations(ds.fields, accept); + this.validateFields(ds, accept); + this.validateProvider(ds, accept); + this.validateUrl(ds, accept); + this.validateRelationMode(ds, accept); + } + + private validateFields(ds: DataSource, accept: ValidationAcceptor) { + ds.fields.forEach((f) => { + if (!supportedFields.includes(f.name)) { + accept('error', `Unexpected field "${f.name}"`, { node: f }); + } + }); + } + + private validateProvider(ds: DataSource, accept: ValidationAcceptor) { + const provider = ds.fields.find((f) => f.name === 'provider'); + if (!provider) { + accept('error', 'datasource must include a "provider" field', { + node: ds, + }); + return; + } + + const value = getStringLiteral(provider.value); + if (!value) { + accept('error', '"provider" must be set to a string literal', { + node: provider.value, + }); + } else if (!SUPPORTED_PROVIDERS.includes(value)) { + accept( + 'error', + `Provider "${value}" is not supported. Choose from ${SUPPORTED_PROVIDERS.map( + (p) => '"' + p + '"' + ).join(' | ')}.`, + { node: provider.value } + ); + } + } + + private validateUrl(ds: DataSource, accept: ValidationAcceptor) { + const url = ds.fields.find((f) => f.name === 'url'); + if (!url) { + accept('error', 'datasource must include a "url" field', { + node: ds, + }); + } + + for (const fieldName of ['url', 'shadowDatabaseUrl']) { + const field = ds.fields.find((f) => f.name === fieldName); + if (!field) { + continue; + } + const value = getStringLiteral(field.value); + if ( + !value && + !( + isInvocationExpr(field.value) && + field.value.function.ref?.name === 'env' + ) + ) { + accept( + 'error', + `"${fieldName}" must be set to a string literal or an invocation of "env" function`, + { node: field.value } + ); + } + } + } + + private validateRelationMode(ds: DataSource, accept: ValidationAcceptor) { + const field = ds.fields.find((f) => f.name === 'relationMode'); + if (field) { + const val = getStringLiteral(field.value); + if (!val || !['foreignKeys', 'prisma'].includes(val)) { + accept( + 'error', + '"relationMode" must be set to "foreignKeys" or "prisma"', + { node: field.value } + ); + } + } + } +} diff --git a/packages/schema/src/language-server/validator/enum-validator.ts b/packages/schema/src/language-server/validator/enum-validator.ts new file mode 100644 index 000000000..da42d8c7a --- /dev/null +++ b/packages/schema/src/language-server/validator/enum-validator.ts @@ -0,0 +1,10 @@ +import { Enum } from '@lang/generated/ast'; +import { AstValidator } from '@lang/types'; +import { ValidationAcceptor } from 'langium'; +import { validateDuplicatedDeclarations } from './utils'; + +export default class EnumValidator implements AstValidator { + validate(_enum: Enum, accept: ValidationAcceptor) { + validateDuplicatedDeclarations(_enum.fields, accept); + } +} diff --git a/packages/schema/src/language-server/validator/schema-validator.ts b/packages/schema/src/language-server/validator/schema-validator.ts new file mode 100644 index 000000000..176081266 --- /dev/null +++ b/packages/schema/src/language-server/validator/schema-validator.ts @@ -0,0 +1,24 @@ +import { isDataSource, Model } from '@lang/generated/ast'; +import { AstValidator } from '@lang/types'; +import { ValidationAcceptor } from 'langium'; +import { validateDuplicatedDeclarations } from './utils'; + +export default class SchemaValidator implements AstValidator { + validate(model: Model, accept: ValidationAcceptor): void { + validateDuplicatedDeclarations(model.declarations, accept); + this.validateDataSources(model, accept); + } + + private validateDataSources(model: Model, accept: ValidationAcceptor) { + const dataSources = model.declarations.filter((d) => isDataSource(d)); + if (dataSources.length === 0) { + accept('error', 'Model must define a datasource', { node: model }); + } else if (dataSources.length > 1) { + accept( + 'error', + 'Multiple datasource declarations are not allowed', + { node: dataSources[1] } + ); + } + } +} diff --git a/packages/schema/src/language-server/validator/utils.ts b/packages/schema/src/language-server/validator/utils.ts new file mode 100644 index 000000000..54af3f182 --- /dev/null +++ b/packages/schema/src/language-server/validator/utils.ts @@ -0,0 +1,137 @@ +import { + AttributeArg, + AttributeParam, + BuiltinType, + DataModelAttribute, + DataModelFieldAttribute, + ExpressionType, + isArrayExpr, + isDataModelField, + isLiteralExpr, + isReferenceExpr, +} from '@lang/generated/ast'; +import { AstNode, ValidationAcceptor } from 'langium'; + +export function validateDuplicatedDeclarations( + decls: Array, + accept: ValidationAcceptor +) { + const groupByName = decls.reduce((group, decl) => { + group[decl.name] = group[decl.name] ?? []; + group[decl.name].push(decl); + return group; + }, {}); + + for (const [name, decls] of Object.entries(groupByName)) { + if (decls.length > 1) { + accept('error', `Duplicated declaration name "${name}"`, { + node: decls[1], + }); + } + } +} + +export function getStringLiteral(node: AstNode) { + if (isLiteralExpr(node) && typeof node.value === 'string') { + return node.value; + } else { + return undefined; + } +} + +export function typeAssignable( + destType: ExpressionType, + sourceType: ExpressionType +) { + switch (destType) { + case 'Any': + return true; + case 'Float': + return sourceType === 'Int' || sourceType === 'Float'; + default: + return sourceType === destType; + } +} + +export function mapBuiltinTypeToExpressionType( + type: BuiltinType | 'Any' | 'Null' +): ExpressionType | 'Any' { + switch (type) { + case 'Any': + case 'Boolean': + case 'String': + case 'DateTime': + case 'Int': + case 'Float': + case 'Null': + return type; + case 'BigInt': + return 'Int'; + case 'Decimal': + return 'Float'; + case 'Json': + case 'Bytes': + return 'Any'; + } +} + +export function assignableToAttributeParam( + arg: AttributeArg, + param: AttributeParam, + attr: DataModelAttribute | DataModelFieldAttribute +) { + const argResolvedType = arg.$resolvedType; + if (!argResolvedType) { + return false; + } + + let dstType = param.type.type; + const dstIsArray = param.type.array; + const dstRef = param.type.reference; + + if (dstType) { + if (typeof argResolvedType?.decl !== 'string') { + // destination type is not a reference, so argument type must be a plain expression + return false; + } + + if (dstType === 'FieldReference') { + if (dstIsArray) { + return ( + isArrayExpr(arg.value) && + !arg.value.items.find( + (item) => + !isReferenceExpr(item) || + !isDataModelField(item.target.ref) + ) + ); + } else { + return ( + isReferenceExpr(arg.value) && + isDataModelField(arg.value.target.ref) + ); + } + } else if (dstType === 'ContextType') { + if (isDataModelField(attr.$container)) { + if (!attr.$container?.type?.type) { + return false; + } + dstType = mapBuiltinTypeToExpressionType( + attr.$container.type.type + ); + } else { + dstType = 'Any'; + } + } + + return ( + typeAssignable(dstType, argResolvedType.decl) && + dstIsArray === argResolvedType.array + ); + } else { + return ( + dstRef!.ref === argResolvedType.decl && + dstIsArray === argResolvedType.array + ); + } +} diff --git a/packages/schema/src/language-server/validator/zmodel-validator.ts b/packages/schema/src/language-server/validator/zmodel-validator.ts new file mode 100644 index 000000000..a84b38e33 --- /dev/null +++ b/packages/schema/src/language-server/validator/zmodel-validator.ts @@ -0,0 +1,62 @@ +import { + ValidationAcceptor, + ValidationChecks, + ValidationRegistry, +} from 'langium'; +import { + Attribute, + DataModel, + DataSource, + Enum, + Model, + ZModelAstType, +} from '../generated/ast'; +import type { ZModelServices } from '../zmodel-module'; +import SchemaValidator from './schema-validator'; +import DataSourceValidator from './datasource-validator'; +import DataModelValidator from './datamodel-validator'; +import AttributeValidator from './attribute-validator'; +import EnumValidator from './enum-validator'; + +/** + * Registry for validation checks. + */ +export class ZModelValidationRegistry extends ValidationRegistry { + constructor(services: ZModelServices) { + super(services); + const validator = services.validation.ZModelValidator; + const checks: ValidationChecks = { + Model: validator.checkModel, + DataSource: validator.checkDataSource, + DataModel: validator.checkDataModel, + Enum: validator.checkEnum, + Attribute: validator.checkAttribute, + }; + this.register(checks, validator); + } +} + +/** + * Implementation of custom validations. + */ +export class ZModelValidator { + checkModel(node: Model, accept: ValidationAcceptor) { + new SchemaValidator().validate(node, accept); + } + + checkDataSource(node: DataSource, accept: ValidationAcceptor) { + new DataSourceValidator().validate(node, accept); + } + + checkDataModel(node: DataModel, accept: ValidationAcceptor) { + new DataModelValidator().validate(node, accept); + } + + checkEnum(node: Enum, accept: ValidationAcceptor) { + new EnumValidator().validate(node, accept); + } + + checkAttribute(node: Attribute, accept: ValidationAcceptor) { + new AttributeValidator().validate(node, accept); + } +} diff --git a/packages/schema/src/language-server/zmodel-index.ts b/packages/schema/src/language-server/zmodel-index.ts deleted file mode 100644 index 355e744bb..000000000 --- a/packages/schema/src/language-server/zmodel-index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { - AstNodeDescription, - DefaultAstNodeDescriptionProvider, - interruptAndCheck, - LangiumDocument, - LangiumServices, - streamAllContents, -} from 'langium'; -import { CancellationToken } from 'vscode-languageserver'; -import { isEnumField } from './generated/ast'; - -export class ZModelDescriptionProvider extends DefaultAstNodeDescriptionProvider { - constructor(services: LangiumServices) { - super(services); - } - - async createDescriptions( - document: LangiumDocument, - cancelToken = CancellationToken.None - ): Promise { - const descr = await super.createDescriptions(document, cancelToken); - // add enum fields so they can be globally resolved across modules - for (const node of streamAllContents(document.parseResult.value)) { - await interruptAndCheck(cancelToken); - if (isEnumField(node)) { - await descr.push( - this.createDescription(node, node.name, document) - ); - } - } - return descr; - } -} diff --git a/packages/schema/src/language-server/zmodel-linker.ts b/packages/schema/src/language-server/zmodel-linker.ts index 9931caa24..1cd06cadc 100644 --- a/packages/schema/src/language-server/zmodel-linker.ts +++ b/packages/schema/src/language-server/zmodel-linker.ts @@ -14,7 +14,6 @@ import { } from 'langium'; import { CancellationToken } from 'vscode-jsonrpc'; import { - AbstractDeclaration, isDataModel, Function, FunctionParamType, @@ -33,8 +32,10 @@ import { FunctionParam, ThisExpr, NullExpr, + AttributeArg, } from './generated/ast'; -import { TypedNode } from './types'; +import { ResolvedShape } from './types'; +import { mapBuiltinTypeToExpressionType } from './validator/utils'; interface DefaultReference extends Reference { _ref?: AstNode | LinkingError; @@ -116,6 +117,10 @@ export class ZModelLinker extends DefaultLinker { document: LangiumDocument, extraScopes: ScopeProvider[] = [] ) { + if (node.$resolvedType) { + return; + } + switch (node.$type) { case LiteralExpr: this.resolveLiteral(node as LiteralExpr); @@ -165,13 +170,21 @@ export class ZModelLinker extends DefaultLinker { this.resolveNull(node as NullExpr, document, extraScopes); break; + case AttributeArg: + this.resolveAttributeArg( + node as AttributeArg, + document, + extraScopes + ); + break; + default: this.resolveDefault(node, document, extraScopes); break; } } - resolveBinary( + private resolveBinary( node: BinaryExpr, document: LangiumDocument, extraScopes: ScopeProvider[] @@ -211,18 +224,16 @@ export class ZModelLinker extends DefaultLinker { } } - resolveUnary( + private resolveUnary( node: UnaryExpr, document: LangiumDocument, extraScopes: ScopeProvider[] ) { this.resolve(node.operand, document, extraScopes); - (node as TypedNode).$resolvedType = ( - node.operand as TypedNode - ).$resolvedType; + node.$resolvedType = node.operand.$resolvedType; } - resolveReference( + private resolveReference( node: ReferenceExpr, document: LangiumDocument, extraScopes: ScopeProvider[] @@ -246,20 +257,20 @@ export class ZModelLinker extends DefaultLinker { } } - resolveArray( + private resolveArray( node: ArrayExpr, document: LangiumDocument, extraScopes: ScopeProvider[] ) { node.items.forEach((item) => this.resolve(item, document, extraScopes)); - const itemType = (node.items[0] as TypedNode).$resolvedType; + const itemType = node.items[0].$resolvedType; if (itemType?.decl) { this.resolveToBuiltinTypeOrDecl(node, itemType.decl, true); } } - resolveInvocation( + private resolveInvocation( node: InvocationExpr, document: LangiumDocument, extraScopes: ScopeProvider[] @@ -270,7 +281,7 @@ export class ZModelLinker extends DefaultLinker { this.resolveToDeclaredType(node, funcDecl.returnType); } - resolveLiteral(node: LiteralExpr) { + private resolveLiteral(node: LiteralExpr) { const type = typeof node.value === 'string' ? 'String' @@ -285,13 +296,13 @@ export class ZModelLinker extends DefaultLinker { } } - resolveMemberAccess( + private resolveMemberAccess( node: MemberAccessExpr, document: LangiumDocument, extraScopes: ScopeProvider[] ) { this.resolve(node.operand, document, extraScopes); - const operandResolved = (node.operand as TypedNode).$resolvedType; + const operandResolved = node.operand.$resolvedType; if ( operandResolved && @@ -310,14 +321,14 @@ export class ZModelLinker extends DefaultLinker { } } - resolveCollectionPredicate( + private resolveCollectionPredicate( node: BinaryExpr, document: LangiumDocument, extraScopes: ScopeProvider[] ) { this.resolve(node.left, document, extraScopes); - const resolvedType = this.getResolvedType(node.left); + const resolvedType = node.left.$resolvedType; if ( resolvedType && isDataModel(resolvedType.decl) && @@ -335,7 +346,7 @@ export class ZModelLinker extends DefaultLinker { } } - resolveThis( + private resolveThis( node: ThisExpr, document: LangiumDocument, extraScopes: ScopeProvider[] @@ -351,7 +362,7 @@ export class ZModelLinker extends DefaultLinker { } } - resolveNull( + private resolveNull( node: NullExpr, document: LangiumDocument, extraScopes: ScopeProvider[] @@ -360,7 +371,16 @@ export class ZModelLinker extends DefaultLinker { this.resolveToBuiltinTypeOrDecl(node, 'Null'); } - resolveDefault( + private resolveAttributeArg( + node: AttributeArg, + document: LangiumDocument, + extraScopes: ScopeProvider[] + ) { + this.resolve(node.value, document, extraScopes); + node.$resolvedType = node.value.$resolvedType; + } + + private resolveDefault( node: AstNode, document: LangiumDocument, extraScopes: ScopeProvider[] @@ -380,30 +400,26 @@ export class ZModelLinker extends DefaultLinker { // utils - getResolvedType(node: AstNode) { - return (node as TypedNode).$resolvedType; - } - - resolveToDeclaredType( + private resolveToDeclaredType( node: AstNode, type: FunctionParamType | DataModelFieldType ) { - const _node: TypedNode = node; if (type.type) { - _node.$resolvedType = { decl: type.type, array: type.array }; + const mappedType = mapBuiltinTypeToExpressionType(type.type); + node.$resolvedType = { decl: mappedType, array: type.array }; } else if (type.reference) { - _node.$resolvedType = { + node.$resolvedType = { decl: type.reference.ref, array: type.array, }; } } - resolveToBuiltinTypeOrDecl( + private resolveToBuiltinTypeOrDecl( node: AstNode, - type: string | AbstractDeclaration, + type: ResolvedShape, array = false ) { - (node as TypedNode).$resolvedType = { decl: type, array }; + node.$resolvedType = { decl: type, array }; } } diff --git a/packages/schema/src/language-server/zmodel-module.ts b/packages/schema/src/language-server/zmodel-module.ts index 20603db5e..16c24f6f3 100644 --- a/packages/schema/src/language-server/zmodel-module.ts +++ b/packages/schema/src/language-server/zmodel-module.ts @@ -12,10 +12,12 @@ import { ZModelGeneratedModule, ZModelGeneratedSharedModule, } from './generated/module'; -import { ZModelDescriptionProvider } from './zmodel-index'; import { ZModelLinker } from './zmodel-linker'; import { ZModelScopeComputation } from './zmodel-scope'; -import { ZModelValidationRegistry, ZModelValidator } from './zmodel-validator'; +import { + ZModelValidationRegistry, + ZModelValidator, +} from './validator/zmodel-validator'; /** * Declaration of custom services - add your own service classes here. @@ -44,17 +46,12 @@ export const ZModelModule: Module< references: { ScopeComputation: (services) => new ZModelScopeComputation(services), Linker: (services) => new ZModelLinker(services), - // NameProvider: () => new ZModelNameProvider(), }, validation: { ValidationRegistry: (services) => new ZModelValidationRegistry(services), ZModelValidator: () => new ZModelValidator(), }, - workspace: { - AstNodeDescriptionProvider: (services) => - new ZModelDescriptionProvider(services), - }, }; /** @@ -72,7 +69,7 @@ export const ZModelModule: Module< * @param context Optional module context with the LSP connection * @returns An object wrapping the shared services and the language-specific services */ -export function createZModelServices(context?: DefaultSharedModuleContext): { +export function createZModelServices(context: DefaultSharedModuleContext): { shared: LangiumSharedServices; ZModel: ZModelServices; } { diff --git a/packages/schema/src/language-server/zmodel-scope.ts b/packages/schema/src/language-server/zmodel-scope.ts index a6fd1a28c..7b4773aaf 100644 --- a/packages/schema/src/language-server/zmodel-scope.ts +++ b/packages/schema/src/language-server/zmodel-scope.ts @@ -1,21 +1,41 @@ import { + AstNode, DefaultScopeComputation, + interruptAndCheck, LangiumDocument, LangiumServices, - PrecomputedScopes, + streamAllContents, } from 'langium'; import { CancellationToken } from 'vscode-jsonrpc'; +import { isEnumField } from './generated/ast'; export class ZModelScopeComputation extends DefaultScopeComputation { - constructor(services: LangiumServices) { + constructor(private readonly services: LangiumServices) { super(services); } - async computeScope( - document: LangiumDocument, - cancelToken = CancellationToken.None - ): Promise { - // Dummy for now - return super.computeScope(document, cancelToken); + async computeExports( + document: LangiumDocument, + cancelToken?: CancellationToken | undefined + ) { + const result = await super.computeExports(document, cancelToken); + + // add enum fields so they can be globally resolved across modules + for (const node of streamAllContents(document.parseResult.value)) { + if (cancelToken) { + await interruptAndCheck(cancelToken); + } + if (isEnumField(node)) { + const desc = + this.services.workspace.AstNodeDescriptionProvider.createDescription( + node, + node.name, + document + ); + result.push(desc); + } + } + + return result; } } diff --git a/packages/schema/src/language-server/zmodel-validator.ts b/packages/schema/src/language-server/zmodel-validator.ts deleted file mode 100644 index e7312c818..000000000 --- a/packages/schema/src/language-server/zmodel-validator.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - // ValidationAcceptor, - ValidationChecks, - ValidationRegistry, -} from 'langium'; -import { ZModelAstType } from './generated/ast'; -import type { ZModelServices } from './zmodel-module'; - -/** - * Registry for validation checks. - */ -export class ZModelValidationRegistry extends ValidationRegistry { - constructor(services: ZModelServices) { - super(services); - const validator = services.validation.ZModelValidator; - const checks: ValidationChecks = { - // Person: validator.checkPersonStartsWithCapital - }; - this.register(checks, validator); - } -} - -/** - * Implementation of custom validations. - */ -export class ZModelValidator { - // checkPersonStartsWithCapital(person: Person, accept: ValidationAcceptor): void { - // if (person.name) { - // const firstChar = person.name.substring(0, 1); - // if (firstChar.toUpperCase() !== firstChar) { - // accept('warning', 'Person name should start with a capital.', { node: person, property: 'name' }); - // } - // } - // } -} diff --git a/packages/schema/src/language-server/zmodel.langium b/packages/schema/src/language-server/zmodel.langium index 053e798b1..b022ec469 100644 --- a/packages/schema/src/language-server/zmodel.langium +++ b/packages/schema/src/language-server/zmodel.langium @@ -10,7 +10,7 @@ AbstractDeclaration: // datasource DataSource: - 'datasource' name=ID '{' (fields+=DataSourceField)+ '}'; + 'datasource' name=ID '{' (fields+=DataSourceField)* '}'; DataSourceField: (name=ID '=' value=(LiteralExpr|InvocationExpr)); @@ -20,7 +20,7 @@ Expression: LogicalExpr; LiteralExpr: - value=(BOOLEAN | INT | STRING); + value=(BOOLEAN | NUMBER | STRING); ArrayExpr: '[' (items+=Expression (',' items+=Expression)*)? ']'; @@ -129,7 +129,7 @@ DataModelField: name=ID type=DataModelFieldType (attributes+=DataModelFieldAttribute)*; DataModelFieldType: - (BuiltinType | reference=[TypeDeclaration:ID]) (array?='[]')? (optional?='?')?; + (type=BuiltinType | reference=[TypeDeclaration:ID]) (array?='[]')? (optional?='?')?; // enum Enum: @@ -140,31 +140,39 @@ EnumField: // function Function: - 'function' name=ID '(' (params+=FunctionParam (',' params+=FunctionParam)*)? ')' returnType=FunctionParamType '{' (expression=Expression)? '}'; + 'function' name=ID '(' (params+=FunctionParam (',' params+=FunctionParam)*)? ')' ':' returnType=FunctionParamType '{' (expression=Expression)? '}'; FunctionParam: - name=ID type=FunctionParamType; + name=ID ':' type=FunctionParamType; FunctionParamType: - (BuiltinType | reference=[TypeDeclaration]) (array?='[]')?; + (type=ExpressionType | reference=[TypeDeclaration]) (array?='[]')?; + +DataModelAttributeName returns string: + '@@' ID; +DataModelFieldAttributeName returns string: + '@' ID; + +AttributeName returns string: + DataModelAttributeName | DataModelFieldAttributeName; // attribute Attribute: - 'attribute' name=ID '(' (params+=AttributeParam (',' params+=AttributeParam)*)? ')'; + 'attribute' name=AttributeName '(' (params+=AttributeParam (',' params+=AttributeParam)*)? ')'; AttributeParam: - positional?='_' name=ID type=AttributeParamType; + (default?='_')? name=ID ':' type=AttributeParamType; AttributeParamType: - (BuiltinType | type='FieldReference') array?='[]' optional?='?'; + (type=(ExpressionType | 'FieldReference' | 'ContextType') | reference=[TypeDeclaration:ID]) (array?='[]')? (optional?='?')?; type TypeDeclaration = DataModel | Enum; DataModelFieldAttribute: - '@' decl=[Attribute] ('(' AttributeArgList? ')')?; + decl=[Attribute:DataModelFieldAttributeName] ('(' AttributeArgList? ')')?; DataModelAttribute: - '@@' decl=[Attribute] ('(' AttributeArgList? ')')?; + decl=[Attribute:DataModelAttributeName] ('(' AttributeArgList? ')')?; fragment AttributeArgList: args+=AttributeArg (',' args+=AttributeArg)*; @@ -172,8 +180,11 @@ fragment AttributeArgList: AttributeArg: (name=ID ':')? value=Expression; -fragment BuiltinType: - type=('String'|'Boolean'|'Int'|'DateTime'|'JSON'); +ExpressionType returns string: + 'String' | 'Int' | 'Float' | 'Boolean' | 'DateTime' | 'Null' | 'Any'; + +BuiltinType returns string: + 'String'|'Boolean'|'Int'|'BigInt'|'Float'|'Decimal'|'DateTime'|'Json'|'Bytes'; hidden terminal WS: /\s+/; terminal BOOLEAN returns boolean: /true|false/; @@ -181,6 +192,6 @@ terminal NULL: 'null'; terminal THIS: 'this'; terminal ID: /[_a-zA-Z][\w_]*/; terminal STRING: /"[^"]*"|'[^']*'/; -terminal INT returns number: /[+-]?[0-9]+/; +terminal NUMBER returns number: /[+-]?[0-9]+(\.[0-9]+)?/; hidden terminal ML_COMMENT: /\/\*[\s\S]*?\*\//; hidden terminal SL_COMMENT: /\/\/[^\n\r]*/; diff --git a/packages/schema/syntaxes/zmodel.tmLanguage.json b/packages/schema/syntaxes/zmodel.tmLanguage.json index 5e3743470..433322aa6 100644 --- a/packages/schema/syntaxes/zmodel.tmLanguage.json +++ b/packages/schema/syntaxes/zmodel.tmLanguage.json @@ -10,7 +10,7 @@ }, { "name": "keyword.control.zmodel", - "match": "\\b(Asc|attribute|Boolean|datasource|DateTime|Desc|enum|FieldReference|function|Int|JSON|model|sort|String)\\b" + "match": "\\b(Any|Asc|attribute|BigInt|Boolean|Bytes|ContextType|datasource|DateTime|Decimal|Desc|enum|FieldReference|Float|function|Int|Json|model|Null|sort|String)\\b" }, { "name": "string.quoted.double.zmodel", diff --git a/packages/schema/tests/generator/expression-writer.test.ts b/packages/schema/tests/generator/expression-writer.test.ts index fb2936780..f0305550e 100644 --- a/packages/schema/tests/generator/expression-writer.test.ts +++ b/packages/schema/tests/generator/expression-writer.test.ts @@ -11,90 +11,12 @@ import * as tmp from 'tmp'; import { GUARD_FIELD_NAME } from '../../src/generator/constants'; import expressionWriter from '../../src/generator/prisma/expression-writer'; -async function check( - schema: string, - getExpr: (model: DataModel) => Expression, - expected: string -) { - const model = await loadModel(schema); - const expr = getExpr( - model.declarations.find( - (d) => isDataModel(d) && d.name === 'Test' - ) as DataModel - ); - - const project = new Project(); - - const { name: sourcePath } = tmp.fileSync({ postfix: '.ts' }); - const sf = project.createSourceFile(sourcePath, undefined, { - overwrite: true, - }); - - // inject user variable - sf.addVariableStatement({ - declarationKind: VariableDeclarationKind.Const, - declarations: [{ name: 'user', initializer: '{ id: "user1" }' }], - }); - - // inject enums - model.declarations - .filter((d) => isEnum(d)) - .map((e) => { - sf.addVariableStatement({ - declarationKind: VariableDeclarationKind.Const, - declarations: [ - { - name: e.name, - initializer: ` - { - ${(e as Enum).fields - .map((f) => `${f.name}: "${f.name}"`) - .join(',\n')} - } - `, - }, - ], - }); - }); - - sf.addVariableStatement({ - declarationKind: VariableDeclarationKind.Const, - declarations: [ - { - name: 'expr', - initializer: (writer) => - new expressionWriter(writer).write(expr), - }, - ], - }); - sf.formatText(); - - await project.save(); - - if (project.getPreEmitDiagnostics().length > 0) { - for (const d of project.getPreEmitDiagnostics()) { - console.warn(`${d.getLineNumber()}: ${d.getMessageText()}`); - } - console.log(`Generated source: ${sourcePath}`); - throw new Error('Compilation errors occurred'); - } - - const outExpr = sf.getVariableDeclaration('expr'); - // console.log('Generated expr:\n', outExpr?.getText()); - - if (expected) { - const generatedExpr = outExpr!.getInitializer()!.getText(); - expect(generatedExpr.replace(/\s+/g, '')).toBe( - expected.replace(/\s+/g, '') - ); - } -} - describe('Expression Writer Tests', () => { it('boolean literal', async () => { await check( ` model Test { + id String @id @@allow('all', true) } `, @@ -105,6 +27,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id @@allow('all', false) } `, @@ -117,6 +40,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id flag Boolean @@allow('all', flag) } @@ -128,6 +52,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id flag Boolean @@allow('all', !flag) } @@ -144,7 +69,9 @@ describe('Expression Writer Tests', () => { USER ADMIN } + model Test { + id String @id role Role @@allow('all', role == ADMIN) } @@ -158,6 +85,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id x Int @@allow('all', x > 0) } @@ -173,6 +101,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id label String @@allow('all', label == 'thing') } @@ -222,6 +151,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id x Int @@allow('all', this.x > 0) } @@ -239,6 +169,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id x Int @@allow('all', x > 0 && x > 1) } @@ -265,6 +196,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id x Int @@allow('all', x > 0 || x > 1) } @@ -291,6 +223,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id x Int @@allow('all', x > 0 && x > 1 || x > 2) } @@ -328,6 +261,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id x Int @@allow('all', !(x > 0 && x > 1 || !x > 2)) } @@ -373,11 +307,15 @@ describe('Expression Writer Tests', () => { await check( ` model Foo { + id String @id x Int + t Test? } model Test { - foo Foo + id String @id + foo Foo @relation(fields: [fooId], references: [id]) + fooId String @@deny('all', foo.x <= 0) } `, @@ -396,11 +334,15 @@ describe('Expression Writer Tests', () => { await check( ` model Foo { + id String @id + t Test? x Int } model Test { - foo Foo + id String @id + foo Foo @relation(fields: [fooId], references: [id]) + fooId String @@deny('all', !(foo.x > 0)) } `, @@ -422,11 +364,15 @@ describe('Expression Writer Tests', () => { await check( ` model Foo { + id String @id + t Test? x Boolean } model Test { - foo Foo + id String @id + foo Foo @relation(fields: [fooId], references: [id]) + fooId String @@deny('all', !foo.x) } `, @@ -445,15 +391,22 @@ describe('Expression Writer Tests', () => { await check( ` model Foo { - bar Bar + id String @id + bar Bar? + t Test? } model Bar { + id String @id x Int + foo Foo @relation(fields: [fooId], references: [id]) + fooId String } model Test { - foo Foo + id String @id + foo Foo @relation(fields: [fooId], references: [id]) + fooId String @@deny('all', foo.bar.x <= 0) } `, @@ -478,10 +431,14 @@ describe('Expression Writer Tests', () => { await check( ` model Foo { + id String @id + t Test @relation(fields: [tId], references: [id]) + tId String x Int } model Test { + id String @id foos Foo[] @@deny('all', foos?[x <= 0]) } @@ -501,10 +458,14 @@ describe('Expression Writer Tests', () => { await check( ` model Foo { + id String @id + t Test @relation(fields: [tId], references: [id]) + tId String x Int } model Test { + id String @id foos Foo[] @@deny('all', foos![x <= 0]) } @@ -524,10 +485,14 @@ describe('Expression Writer Tests', () => { await check( ` model Foo { + id String @id + t Test @relation(fields: [tId], references: [id]) + tId String x Int } model Test { + id String @id foos Foo[] @@deny('all', foos^[x <= 0]) } @@ -547,15 +512,22 @@ describe('Expression Writer Tests', () => { await check( ` model Foo { + id String @id bars Bar[] + t Test @relation(fields: [tId], references: [id]) + tId String } model Bar { + id String @id + foo Foo @relation(fields: [fooId], references: [id]) + fooId String x Int } model Test { - foo Foo + id String @id + foo Foo? @@deny('all', foo.bars?[x <= 0]) } `, @@ -580,6 +552,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id @@deny('all', auth() == null) } `, @@ -590,6 +563,7 @@ describe('Expression Writer Tests', () => { await check( ` model Test { + id String @id @@allow('all', auth() != null) } `, @@ -603,11 +577,13 @@ describe('Expression Writer Tests', () => { ` model User { id String @id + t Test? } model Test { id String @id - owner User + owner User @relation(fields: [ownerId], references: [id]) + ownerId String @@allow('all', auth() == owner) } `, @@ -627,11 +603,13 @@ describe('Expression Writer Tests', () => { ` model User { id String @id + t Test? } model Test { id String @id - owner User + owner User @relation(fields: [ownerId], references: [id]) + ownerId String @@deny('all', auth() != owner) } `, @@ -653,11 +631,13 @@ describe('Expression Writer Tests', () => { ` model User { id String @id + t Test? } model Test { id String @id - owner User + owner User @relation(fields: [ownerId], references: [id]) + ownerId String @@allow('all', auth().id == owner.id) } `, @@ -674,3 +654,92 @@ describe('Expression Writer Tests', () => { ); }); }); + +async function check( + schema: string, + getExpr: (model: DataModel) => Expression, + expected: string +) { + if (!schema.includes('datasource ')) { + schema = + ` + datasource db { + provider = 'postgresql' + url = 'dummy' + } + ` + schema; + } + + const model = await loadModel(schema); + const expr = getExpr( + model.declarations.find( + (d) => isDataModel(d) && d.name === 'Test' + ) as DataModel + ); + + const project = new Project(); + + const { name: sourcePath } = tmp.fileSync({ postfix: '.ts' }); + const sf = project.createSourceFile(sourcePath, undefined, { + overwrite: true, + }); + + // inject user variable + sf.addVariableStatement({ + declarationKind: VariableDeclarationKind.Const, + declarations: [{ name: 'user', initializer: '{ id: "user1" }' }], + }); + + // inject enums + model.declarations + .filter((d) => isEnum(d)) + .map((e) => { + sf.addVariableStatement({ + declarationKind: VariableDeclarationKind.Const, + declarations: [ + { + name: e.name, + initializer: ` + { + ${(e as Enum).fields + .map((f) => `${f.name}: "${f.name}"`) + .join(',\n')} + } + `, + }, + ], + }); + }); + + sf.addVariableStatement({ + declarationKind: VariableDeclarationKind.Const, + declarations: [ + { + name: 'expr', + initializer: (writer) => + new expressionWriter(writer).write(expr), + }, + ], + }); + sf.formatText(); + + await project.save(); + + if (project.getPreEmitDiagnostics().length > 0) { + for (const d of project.getPreEmitDiagnostics()) { + console.warn(`${d.getLineNumber()}: ${d.getMessageText()}`); + } + console.log(`Generated source: ${sourcePath}`); + throw new Error('Compilation errors occurred'); + } + + const outExpr = sf.getVariableDeclaration('expr'); + // console.log('Generated expr:\n', outExpr?.getText()); + + if (expected) { + const generatedExpr = outExpr!.getInitializer()!.getText(); + expect(generatedExpr.replace(/\s+/g, '')).toBe( + expected.replace(/\s+/g, '') + ); + } +} diff --git a/packages/schema/tests/generator/prisma-builder.test.ts b/packages/schema/tests/generator/prisma-builder.test.ts index 06c530c3e..928bd846c 100644 --- a/packages/schema/tests/generator/prisma-builder.test.ts +++ b/packages/schema/tests/generator/prisma-builder.test.ts @@ -58,9 +58,9 @@ describe('Prisma Builder Tests', () => { it('model', async () => { let model = new PrismaModel(); const dm = model.addModel('User'); - dm.addField('id', 'String', [new FieldAttribute('id')]); + dm.addField('id', 'String', [new FieldAttribute('@id')]); dm.addField('createdAt', 'DateTime', [ - new FieldAttribute('default', [ + new FieldAttribute('@default', [ new AttributeArg( undefined, new AttributeArgValue( @@ -76,13 +76,13 @@ describe('Prisma Builder Tests', () => { it('relation', async () => { let model = new PrismaModel(); const user = model.addModel('User'); - user.addField('id', 'String', [new FieldAttribute('id')]); + user.addField('id', 'String', [new FieldAttribute('@id')]); user.addField('posts', new ModelFieldType('Post', true)); const post = model.addModel('Post'); - post.addField('id', 'String', [new FieldAttribute('id')]); + post.addField('id', 'String', [new FieldAttribute('@id')]); post.addField('user', 'User', [ - new FieldAttribute('relation', [ + new FieldAttribute('@relation', [ new AttributeArg( 'fields', new AttributeArgValue('Array', [ @@ -112,10 +112,10 @@ describe('Prisma Builder Tests', () => { it('model attribute', async () => { let model = new PrismaModel(); const post = model.addModel('Post'); - post.addField('id', 'String', [new FieldAttribute('id')]); + post.addField('id', 'String', [new FieldAttribute('@id')]); post.addField('slug', 'String'); post.addField('space', 'String'); - post.addAttribute('unique', [ + post.addAttribute('@@unique', [ new AttributeArg( 'fields', new AttributeArgValue('Array', [ diff --git a/packages/schema/tests/schema/parser.test.ts b/packages/schema/tests/schema/parser.test.ts index c4efb76b5..8f65a2ff0 100644 --- a/packages/schema/tests/schema/parser.test.ts +++ b/packages/schema/tests/schema/parser.test.ts @@ -13,7 +13,7 @@ import { } from '../../src/language-server/generated/ast'; import { loadModel } from '../utils'; -describe('Basic Tests', () => { +describe('Parsing Tests', () => { it('data source', async () => { const content = ` datasource db { @@ -21,7 +21,7 @@ describe('Basic Tests', () => { url = env('DATABASE_URL') } `; - const doc = await loadModel(content); + const doc = await loadModel(content, false); expect(doc.declarations).toHaveLength(1); const ds = doc.declarations[0] as DataSource; @@ -51,10 +51,11 @@ describe('Basic Tests', () => { } model User { + id String @id role UserRole @default(USER) } `; - const doc = await loadModel(content); + const doc = await loadModel(content, false); const enumDecl = doc.declarations[0]; expect(enumDecl.name).toBe('UserRole'); expect((enumDecl as Enum).fields.map((f) => f.name)).toEqual( @@ -62,32 +63,39 @@ describe('Basic Tests', () => { ); const model = doc.declarations[1] as DataModel; - expect(model.fields[0].type.reference?.ref?.name).toBe('UserRole'); + expect(model.fields[1].type.reference?.ref?.name).toBe('UserRole'); - const attrVal = model.fields[0].attributes[0].args[0] as AttributeArg; + const attrVal = model.fields[1].attributes[0].args[0] as AttributeArg; expect((attrVal.value as ReferenceExpr).target.ref?.name).toBe('USER'); }); it('model field types', async () => { const content = ` model User { - id String + id String @id age Int + serial BigInt + height Float + salary Decimal activated Boolean createdAt DateTime - metadata JSON + metadata Json + content Bytes } `; - const doc = await loadModel(content); + const doc = await loadModel(content, false); const model = doc.declarations[0] as DataModel; - expect(model.fields).toHaveLength(5); expect(model.fields.map((f) => f.type.type)).toEqual( expect.arrayContaining([ 'String', 'Int', + 'BigInt', + 'Float', + 'Decimal', 'Boolean', - 'JSON', + 'Json', 'DateTime', + 'Bytes', ]) ); }); @@ -95,14 +103,15 @@ describe('Basic Tests', () => { it('model field modifiers', async () => { const content = ` model User { + id String @id name String? tags String[] } `; - const doc = await loadModel(content); + const doc = await loadModel(content, false); const model = doc.declarations[0] as DataModel; - expect(model.fields[0].type.optional).toBeTruthy(); - expect(model.fields[1].type.array).toBeTruthy(); + expect(model.fields[1].type.optional).toBeTruthy(); + expect(model.fields[2].type.array).toBeTruthy(); }); it('model field attributes', async () => { @@ -112,18 +121,19 @@ describe('Basic Tests', () => { activated Boolean @default(false) @unique } `; - const doc = await loadModel(content); + const doc = await loadModel(content, false); const model = doc.declarations[0] as DataModel; - expect(model.fields[0].attributes[0].decl.ref?.name).toBe('id'); + expect(model.fields[0].attributes[0].decl.ref?.name).toBe('@id'); expect(model.fields[1].attributes[0].args[0].value.$type).toBe( LiteralExpr ); - expect(model.fields[1].attributes[1].decl.ref?.name).toBe('unique'); + expect(model.fields[1].attributes[1].decl.ref?.name).toBe('@unique'); }); it('model attributes', async () => { const content = ` model Model { + id String @id a String b String @@unique([a, b]) @@ -131,10 +141,10 @@ describe('Basic Tests', () => { @@unique(b(sort: Desc)) } `; - const doc = await loadModel(content); + const doc = await loadModel(content, false); const model = doc.declarations[0] as DataModel; expect(model.attributes).toHaveLength(3); - expect(model.attributes[0].decl.ref?.name).toBe('unique'); + expect(model.attributes[0].decl.ref?.name).toBe('@@unique'); expect( (model.attributes[0].args[0].value as ArrayExpr).items.map( (item) => (item as ReferenceExpr).target.ref?.name @@ -170,16 +180,16 @@ describe('Basic Tests', () => { it('model relation', async () => { const content = ` model User { - id String + id String @id posts Post[] } model Post { - id String + id String @id owner User @relation(references: [id], onDelete: Cascade, onUpdate: Cascade) } `; - const doc = await loadModel(content); + const doc = await loadModel(content, false); const models = doc.declarations as DataModel[]; expect(models[0].fields[1].type.reference?.ref?.name === 'Post'); expect(models[1].fields[1].type.reference?.ref?.name === 'User'); @@ -188,6 +198,7 @@ describe('Basic Tests', () => { it('policy expressions', async () => { const content = ` model Model { + id String @id a Int b Int c Boolean @@ -197,7 +208,7 @@ describe('Basic Tests', () => { // @@deny(a + b < 10) } `; - const doc = await loadModel(content); + const doc = await loadModel(content, false); const model = doc.declarations[0] as DataModel; const attrs = model.attributes; @@ -231,6 +242,7 @@ describe('Basic Tests', () => { it('policy expression precedence', async () => { const content = ` model Model { + id String @id a Int b Int // @@deny(a + b * 2 > 0) @@ -241,9 +253,9 @@ describe('Basic Tests', () => { } `; - await loadModel(content); + await loadModel(content, false); - // const doc = await loadModel(content); + // const doc = await loadModel(content, false); // const attrs = (doc.declarations[0] as DataModel).attributes; // expect(attrs[0].args[0].value.$type).toBe(BinaryExpr); @@ -332,6 +344,7 @@ describe('Basic Tests', () => { it('function', async () => { const content = ` model M { + id String @id a Int b Int c N[] @@ -340,6 +353,7 @@ describe('Basic Tests', () => { } model N { + id String @id x Int } @@ -349,7 +363,7 @@ describe('Basic Tests', () => { function bar(items N[]) Boolean { } `; - const doc = await loadModel(content); + const doc = await loadModel(content, false); const model = doc.declarations[0] as DataModel; const foo = doc.declarations[2] as Function; const bar = doc.declarations[3] as Function; @@ -369,16 +383,19 @@ describe('Basic Tests', () => { it('member access', async () => { const content = ` model M { + id String @id a N @@deny('all', a.x.y < 0) @@deny('all', foo(a)) } model N { + id String @id x P } model P { + id String @id y Int } @@ -386,21 +403,23 @@ describe('Basic Tests', () => { n.x < 0 } `; - await loadModel(content); + await loadModel(content, false); }); it('collection predicate', async () => { const content = ` model M { + id String @id n N[] @@deny('all', n?[x < 0]) } model N { + id String @id x Int } `; - await loadModel(content); + await loadModel(content, false); }); it('collection predicate chained', async () => { @@ -418,6 +437,6 @@ describe('Basic Tests', () => { x Int } `; - await loadModel(content); + await loadModel(content, false); }); }); diff --git a/packages/schema/tests/schema/stdlib.test.ts b/packages/schema/tests/schema/stdlib.test.ts new file mode 100644 index 000000000..aa376611e --- /dev/null +++ b/packages/schema/tests/schema/stdlib.test.ts @@ -0,0 +1,36 @@ +import { NodeFileSystem } from 'langium/node'; +import path from 'path'; +import { URI } from 'vscode-uri'; +import { createZModelServices } from '../../src/language-server/zmodel-module'; +import { SchemaLoadingError } from '../utils'; + +describe('Stdlib Tests', () => { + it('stdlib', async () => { + const { shared } = createZModelServices(NodeFileSystem); + const stdLib = shared.workspace.LangiumDocuments.getOrCreateDocument( + URI.file(path.resolve('src/language-server/stdlib.zmodel')) + ); + await shared.workspace.DocumentBuilder.build([stdLib], { + validationChecks: 'none', + }); + + const validationErrors = (stdLib.diagnostics ?? []).filter( + (e) => e.severity === 1 + ); + if (validationErrors.length > 0) { + for (const validationError of validationErrors) { + const range = stdLib.textDocument.getText( + validationError.range + ); + console.error( + `line ${validationError.range.start.line + 1}: ${ + validationError.message + }${range ? ' [' + range + ']' : ''}` + ); + } + throw new SchemaLoadingError( + validationErrors.map((e) => e.message) + ); + } + }); +}); diff --git a/packages/schema/tests/schema/validation/datamodel-validation.test.ts b/packages/schema/tests/schema/validation/datamodel-validation.test.ts new file mode 100644 index 000000000..0f9777a1d --- /dev/null +++ b/packages/schema/tests/schema/validation/datamodel-validation.test.ts @@ -0,0 +1,467 @@ +import { loadModel, loadModelWithError } from '../../utils'; + +describe('Data Model Validation Tests', () => { + const prelude = ` + datasource db { + provider = "postgresql" + url = "url" + } + `; + + it('duplicated fields', async () => { + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id + x Int + x String + } + `) + ).toContain('Duplicated declaration name "x"'); + }); + + it('scalar types', async () => { + await loadModel(` + ${prelude} + model M { + id String @id + a String + b Boolean? + c Int[] + d BigInt + e Float + f Decimal + g DateTime + h Json + i Bytes + } + `); + }); + + it('mix array and optional', async () => { + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id + x Int[]? + } + `) + ).toContain( + 'Optional lists are not supported. Use either `Type[]` or `Type?`' + ); + }); + + it('unresolved field type', async () => { + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id + x Integer + } + `) + ).toContain( + `Could not resolve reference to TypeDeclaration named 'Integer'.` + ); + + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id + x Integer[] + } + `) + ).toContain( + `Could not resolve reference to TypeDeclaration named 'Integer'.` + ); + + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id + x Integer? + } + `) + ).toContain( + `Could not resolve reference to TypeDeclaration named 'Integer'.` + ); + }); + + it('id field', async () => { + expect( + await loadModelWithError(` + ${prelude} + model M { + x Int + } + `) + ).toContain(`Model must include a field with @id attribute`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + x Int @id + y Int @id + } + `) + ).toContain(`Model can include at most one field with @id attribute`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + x Int? @id + } + `) + ).toContain(`Field with @id attribute must not be optional`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + x Int[] @id + } + `) + ).toContain(`Field with @id attribute must be of scalar type`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + x Json @id + } + `) + ).toContain(`Field with @id attribute must be of scalar type`); + + expect( + await loadModelWithError(` + ${prelude} + model Id { + id String @id + } + model M { + myId Id @id + } + `) + ).toContain(`Field with @id attribute must be of scalar type`); + }); + + it('builtin field attributes', async () => { + await loadModel(` + ${prelude} + model M { + x String @id @default("abc") @unique @map("_id") @updatedAt + } + `); + }); + + it('field attribute type checking', async () => { + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id(123) + } + `) + ).toContain(`Unexpected unnamed argument`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id() @default(value:'def', 'abc') + } + `) + ).toContain(`Unexpected unnamed argument`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id() @default('abc', value:'def') + } + `) + ).toContain(`Parameter \"value\" is already provided`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id() @default(123) + } + `) + ).toContain(`Value is not assignable to parameter`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id() @default() + } + `) + ).toContain(`Required parameter not provided: value`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id() @default('abc', value: 'def') + } + `) + ).toContain(`Parameter "value" is already provided`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id() @default(foo: 'abc') + } + `) + ).toContain( + `Attribute "@default" doesn't have a parameter named "foo"` + ); + }); + + it('field attribute coverage', async () => { + await loadModel(` + ${prelude} + model A { + id String @id + } + + model B { + id String @id() + } + + model C { + id String @id(map: "__id") + } + + model D { + id String @id + x String @default("x") + } + + model E { + id String @id + x String @default(value: "x") + } + + model F { + id String @id + x String @default(uuid()) + } + + model G { + id String @id + x Int @default(autoincrement()) + } + + model H { + id String @id + x String @unique() + } + `); + }); + + it('model attribute coverage', async () => { + await loadModel(` + ${prelude} + model A { + id String @id + x Int + y String + @@unique([x, y]) + } + `); + + await loadModel(` + ${prelude} + model A { + id String @id + x Int + y String + @@unique(fields: [x, y]) + } + `); + + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + x Int + y String + @@unique([x, z]) + } + `) + ).toContain( + `Could not resolve reference to ReferenceTarget named 'z'.` + ); + + await loadModel(` + ${prelude} + model A { + id String @id + x Int + y String + @@index([x, y]) + } + `); + + await loadModel(` + ${prelude} + model A { + id String @id + x Int + y String + @@map("__A") + } + `); + }); + + it('relation', async () => { + // one-to-one + await loadModel(` + ${prelude} + model A { + id String @id + b B? + } + + model B { + id String @id + a A @relation(fields: [foreignId], references: [id], onUpdate: Cascade, onDelete: Cascade) + foreignId String + } + `); + + // one-to-many + await loadModel(` + ${prelude} + model A { + id String @id + b B[] + } + + model B { + id String @id + a A @relation(fields: [foreignId], references: [id]) + foreignId String + } + `); + + // one-to-one incomplete + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + b B? + } + + model B { + id String @id + } + `) + ).toContain( + `The relation field "b" on model "A" is missing an opposite relation field on model "B"` + ); + + // one-to-one ambiguous + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + b B? + } + + model B { + id String @id + a A + a1 A + } + `) + ).toContain( + `Fields "a", "a1" on model B refer to the same relation to model "A"` + ); + + // one-to-one inconsistent attribute + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + b B? @relation(references: [id]) + } + + model B { + id String @id + a A @relation(fields: [aId], references: [id]) + aId String + } + `) + ).toContain( + `"fields" and "references" must be provided only on one side of relation field` + ); + + // missing @relation + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + b B? + } + + model B { + id String @id + a A + } + `) + ).toContain( + `Field for one side of relation must carry @relation attribute with both "fields" and "references" fields` + ); + + // wrong relation owner field type + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + b B + } + + model B { + id String @id + a A @relation(fields: [aId], references: [id]) + aId String + } + `) + ).toContain(`Relation field needs to be list or optional`); + + // unresolved field + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + b B? + } + + model B { + id String @id + a A @relation(fields: [aId], references: [id]) + } + `) + ).toContain( + `Could not resolve reference to ReferenceTarget named 'aId'.` + ); + }); +}); diff --git a/packages/schema/tests/schema/validation/datasource-validation.test.ts b/packages/schema/tests/schema/validation/datasource-validation.test.ts new file mode 100644 index 000000000..ff1e14ed7 --- /dev/null +++ b/packages/schema/tests/schema/validation/datasource-validation.test.ts @@ -0,0 +1,128 @@ +import { loadModel, loadModelWithError } from '../../utils'; + +describe('Datasource Validation Tests', () => { + it('missing fields', async () => { + expect( + await loadModelWithError(` + datasource db { + } + `) + ).toEqual( + expect.arrayContaining([ + 'datasource must include a "provider" field', + 'datasource must include a "url" field', + ]) + ); + }); + + it('dup fields', async () => { + expect( + await loadModelWithError(` + datasource db { + provider = 'abc' + provider = 'abc' + } + `) + ).toContain('Duplicated declaration name "provider"'); + }); + + it('unknown fields', async () => { + expect( + await loadModelWithError(` + datasource db { + x = 1 + } + `) + ).toContain('Unexpected field "x"'); + }); + + it('invalid provider value', async () => { + expect( + await loadModelWithError(` + datasource db { + provider = 123 + } + `) + ).toContain('"provider" must be set to a string literal'); + + expect( + await loadModelWithError(` + datasource db { + provider = 'abc' + } + `) + ).toContain( + 'Provider "abc" is not supported. Choose from "postgresql" | "mysql" | "sqlite" | "sqlserver".' + ); + }); + + it('invalid url value', async () => { + expect( + await loadModelWithError(` + datasource db { + url = 123 + } + `) + ).toContain( + '"url" must be set to a string literal or an invocation of "env" function' + ); + + expect( + await loadModelWithError(` + datasource db { + url = uuid() + } + `) + ).toContain( + '"url" must be set to a string literal or an invocation of "env" function' + ); + + expect( + await loadModelWithError(` + datasource db { + shadowDatabaseUrl = 123 + } + `) + ).toContain( + '"shadowDatabaseUrl" must be set to a string literal or an invocation of "env" function' + ); + }); + + it('invalid relationMode value', async () => { + expect( + await loadModelWithError(` + datasource db { + relationMode = 123 + } + `) + ).toContain('"relationMode" must be set to "foreignKeys" or "prisma"'); + + expect( + await loadModelWithError(` + datasource db { + relationMode = "foo" + } + `) + ).toContain('"relationMode" must be set to "foreignKeys" or "prisma"'); + }); + + it('success', async () => { + await loadModel(` + datasource db { + provider = "postgresql" + url = "url" + shadowDatabaseUrl = "shadow" + relationMode = "prisma" + } + `); + + await loadModel(` + datasource db { + provider = "postgresql" + url = env("url") + shadowDatabaseUrl = env("shadowUrl") + relationMode = "foreignKeys" + } + `); + }); +}); diff --git a/packages/schema/tests/schema/validation/schema-validation.test.ts b/packages/schema/tests/schema/validation/schema-validation.test.ts new file mode 100644 index 000000000..06dda524a --- /dev/null +++ b/packages/schema/tests/schema/validation/schema-validation.test.ts @@ -0,0 +1,33 @@ +import { loadModelWithError } from '../../utils'; + +describe('Toplevel Schema Validation Tests', () => { + it('no datasource', async () => { + expect(await loadModelWithError('')).toContain( + 'Model must define a datasource' + ); + }); + + it('too many datasources', async () => { + expect( + await loadModelWithError(` + datasource db1 { + provider = 'postgresql' + url = env('DATABASE_URL') + } + datasource db2 { + provider = 'postgresql' + url = env('DATABASE_URL') + } + `) + ).toContain('Multiple datasource declarations are not allowed'); + }); + + it('duplicated declaration names', async () => { + expect( + await loadModelWithError(` + model X {} + model X {} + `) + ).toContain('Duplicated declaration name "X"'); + }); +}); diff --git a/packages/schema/tests/utils.ts b/packages/schema/tests/utils.ts index bb4d19489..cf68a62e8 100644 --- a/packages/schema/tests/utils.ts +++ b/packages/schema/tests/utils.ts @@ -2,13 +2,24 @@ import { createZModelServices } from '../src/language-server/zmodel-module'; import { URI } from 'vscode-uri'; import * as fs from 'fs'; import * as path from 'path'; +import { NodeFileSystem } from 'langium/node'; import { Model } from '../src/language-server/generated/ast'; import * as tmp from 'tmp'; -export async function loadModel(content: string) { +export class SchemaLoadingError extends Error { + constructor(public readonly errors: string[]) { + super('Schema error'); + } +} + +export async function loadModel( + content: string, + validate = true, + verbose = true +) { const { name: docPath } = tmp.fileSync({ postfix: '.zmodel' }); fs.writeFileSync(docPath, content); - const { shared } = createZModelServices(); + const { shared } = createZModelServices(NodeFileSystem); const stdLib = shared.workspace.LangiumDocuments.getOrCreateDocument( URI.file(path.resolve('src/language-server/stdlib.zmodel')) ); @@ -16,7 +27,7 @@ export async function loadModel(content: string) { URI.file(docPath) ); await shared.workspace.DocumentBuilder.build([stdLib, doc], { - validationChecks: 'all', + validationChecks: validate ? 'all' : 'none', }); const validationErrors = (doc.diagnostics ?? []).filter( @@ -24,15 +35,28 @@ export async function loadModel(content: string) { ); if (validationErrors.length > 0) { for (const validationError of validationErrors) { - console.error( - `line ${validationError.range.start.line + 1}: ${ - validationError.message - } [${doc.textDocument.getText(validationError.range)}]` - ); + if (verbose) { + const range = doc.textDocument.getText(validationError.range); + console.error( + `line ${validationError.range.start.line + 1}: ${ + validationError.message + }${range ? ' [' + range + ']' : ''}` + ); + } } - throw new Error('Validation error'); + throw new SchemaLoadingError(validationErrors.map((e) => e.message)); } const model = (await doc.parseResult.value) as Model; return model; } + +export async function loadModelWithError(content: string, verbose = false) { + try { + await loadModel(content, true, verbose); + } catch (err) { + expect(err).toBeInstanceOf(SchemaLoadingError); + return (err as SchemaLoadingError).errors; + } + throw new Error('No error is thrown'); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d445bdcc..fbc32a62a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,70 +56,76 @@ importers: packages/schema: specifiers: - '@prisma/internals': ^4.4.0 - '@types/jest': ^29.0.3 - '@types/node': ^14.18.29 + '@prisma/internals': ^4.5.0 + '@types/jest': ^29.2.0 + '@types/node': ^14.18.32 + '@types/pluralize': ^0.0.29 '@types/tmp': ^0.2.3 '@types/uuid': ^8.3.4 - '@types/vscode': ^1.56.0 - '@typescript-eslint/eslint-plugin': ^4.14.1 - '@typescript-eslint/parser': ^4.14.1 + '@types/vscode': ^1.72.0 + '@typescript-eslint/eslint-plugin': ^4.33.0 + '@typescript-eslint/parser': ^4.33.0 '@zenstackhq/internal': workspace:* change-case: ^4.1.2 chevrotain: ^9.1.0 colors: ^1.4.0 - commander: ^8.0.0 + commander: ^8.3.0 concurrently: ^7.4.0 - eslint: ^7.19.0 - jest: ^29.0.3 - langium: ^0.4.0 - langium-cli: ^0.4.0 - prisma: ^4.4.0 + eslint: ^7.32.0 + jest: ^29.2.1 + langium: ^0.5.0 + langium-cli: ^0.5.0 + pluralize: ^8.0.0 + prisma: ^4.5.0 promisify: ^0.0.3 tmp: ^0.2.1 - ts-jest: ^29.0.1 + ts-jest: ^29.0.3 ts-morph: ^16.0.0 ts-node: ^10.9.1 tsc-alias: ^1.7.0 tsconfig-paths-jest: ^0.0.1 - typescript: ^4.6.2 + typescript: ^4.8.4 + uuid: ^9.0.0 vscode-jsonrpc: ^8.0.2 - vscode-languageclient: ^7.0.0 - vscode-languageserver: ^7.0.0 - vscode-uri: ^3.0.2 + vscode-languageclient: ^8.0.2 + vscode-languageserver: ^8.0.2 + vscode-uri: ^3.0.6 dependencies: '@zenstackhq/internal': link:../internal change-case: 4.1.2 chevrotain: 9.1.0 colors: 1.4.0 commander: 8.3.0 - langium: 0.4.0 - prisma: 4.4.0 + langium: 0.5.0 + pluralize: 8.0.0 + prisma: 4.5.0 promisify: 0.0.3 ts-morph: 16.0.0 + uuid: 9.0.0 vscode-jsonrpc: 8.0.2 - vscode-languageclient: 7.0.0 - vscode-languageserver: 7.0.0 + vscode-languageclient: 8.0.2 + vscode-languageserver: 8.0.2 vscode-uri: 3.0.6 devDependencies: - '@prisma/internals': 4.4.0 - '@types/jest': 29.0.3 - '@types/node': 14.18.29 + '@prisma/internals': 4.5.0 + '@types/jest': 29.2.0 + '@types/node': 14.18.32 + '@types/pluralize': 0.0.29 '@types/tmp': 0.2.3 '@types/uuid': 8.3.4 - '@types/vscode': 1.71.0 - '@typescript-eslint/eslint-plugin': 4.33.0_qkm6m2dvh7633pj2jigehwm774 - '@typescript-eslint/parser': 4.33.0_dyxdave6dwjbccc5dgiifcmuza + '@types/vscode': 1.72.0 + '@typescript-eslint/eslint-plugin': 4.33.0_k4l66av2tbo6kxzw52jzgbfzii + '@typescript-eslint/parser': 4.33.0_3rubbgt5ekhqrcgx4uwls3neim concurrently: 7.4.0 eslint: 7.32.0 - jest: 29.0.3_johvxhudwcpndp4mle25vwrlq4 - langium-cli: 0.4.0 + jest: 29.2.1_4f2ldd7um3b3u4eyvetyqsphze + langium-cli: 0.5.0 tmp: 0.2.1 - ts-jest: 29.0.1_poggjixajg6vd6yquly7s7dsj4 - ts-node: 10.9.1_ck2axrxkiif44rdbzjywaqjysa + ts-jest: 29.0.3_hx3pnmlivexg2zscxcsqm4rf7i + ts-node: 10.9.1_jcmx33t3olsvcxopqdljsohpme tsc-alias: 1.7.0 tsconfig-paths-jest: 0.0.1 - typescript: 4.8.3 + typescript: 4.8.4 tests/integration: specifiers: @@ -141,10 +147,10 @@ importers: '@types/supertest': 2.0.12 '@types/tmp': 0.2.3 jest: 29.0.3_johvxhudwcpndp4mle25vwrlq4 - next: 12.3.1_6tziyx3dehkoeijunclpkpolha + next: 12.3.1_qtpcxnaaarbm4ws7ughq6oxfve supertest: 6.3.0 tmp: 0.2.1 - ts-jest: 29.0.1_poggjixajg6vd6yquly7s7dsj4 + ts-jest: 29.0.1_t3cec5bure72u77t3utxqeumoa ts-node: 10.9.1_ck2axrxkiif44rdbzjywaqjysa typescript: 4.8.3 @@ -155,7 +161,7 @@ packages: engines: {node: '>=6.0.0'} dependencies: '@jridgewell/gen-mapping': 0.1.1 - '@jridgewell/trace-mapping': 0.3.16 + '@jridgewell/trace-mapping': 0.3.17 /@babel/code-frame/7.12.11: resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} @@ -195,6 +201,29 @@ packages: transitivePeerDependencies: - supports-color + /@babel/core/7.19.6: + resolution: {integrity: sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.0 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.19.6 + '@babel/helper-compilation-targets': 7.19.3_@babel+core@7.19.6 + '@babel/helper-module-transforms': 7.19.6 + '@babel/helpers': 7.19.4 + '@babel/parser': 7.19.6 + '@babel/template': 7.18.10 + '@babel/traverse': 7.19.6 + '@babel/types': 7.19.4 + convert-source-map: 1.9.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/generator/7.19.5: resolution: {integrity: sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg==} engines: {node: '>=6.9.0'} @@ -203,6 +232,14 @@ packages: '@jridgewell/gen-mapping': 0.3.2 jsesc: 2.5.2 + /@babel/generator/7.19.6: + resolution: {integrity: sha512-oHGRUQeoX1QrKeJIKVe0hwjGqNnVYsM5Nep5zo0uE0m42sLH+Fsd2pStJ5sRM1bNyTUUoz0pe2lTeMJrb/taTA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.19.4 + '@jridgewell/gen-mapping': 0.3.2 + jsesc: 2.5.2 + /@babel/helper-compilation-targets/7.19.3_@babel+core@7.19.3: resolution: {integrity: sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==} engines: {node: '>=6.9.0'} @@ -215,6 +252,19 @@ packages: browserslist: 4.21.4 semver: 6.3.0 + /@babel/helper-compilation-targets/7.19.3_@babel+core@7.19.6: + resolution: {integrity: sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.19.4 + '@babel/core': 7.19.6 + '@babel/helper-validator-option': 7.18.6 + browserslist: 4.21.4 + semver: 6.3.0 + dev: true + /@babel/helper-environment-visitor/7.18.9: resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} engines: {node: '>=6.9.0'} @@ -253,6 +303,22 @@ packages: transitivePeerDependencies: - supports-color + /@babel/helper-module-transforms/7.19.6: + resolution: {integrity: sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-simple-access': 7.19.4 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.19.1 + '@babel/template': 7.18.10 + '@babel/traverse': 7.19.6 + '@babel/types': 7.19.4 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/helper-plugin-utils/7.19.0: resolution: {integrity: sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==} engines: {node: '>=6.9.0'} @@ -264,6 +330,13 @@ packages: dependencies: '@babel/types': 7.19.4 + /@babel/helper-simple-access/7.19.4: + resolution: {integrity: sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.19.4 + dev: true + /@babel/helper-split-export-declaration/7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} @@ -287,7 +360,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.18.10 - '@babel/traverse': 7.19.4 + '@babel/traverse': 7.19.6 '@babel/types': 7.19.4 transitivePeerDependencies: - supports-color @@ -307,6 +380,13 @@ packages: dependencies: '@babel/types': 7.19.4 + /@babel/parser/7.19.6: + resolution: {integrity: sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.19.4 + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.19.3: resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -316,6 +396,15 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.19.6: + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.19.3: resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: @@ -325,6 +414,15 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.19.3: resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: @@ -334,6 +432,15 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.19.6: + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.19.3: resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: @@ -343,6 +450,15 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.19.6: + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.19.3: resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: @@ -352,6 +468,15 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.19.3: resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} engines: {node: '>=6.9.0'} @@ -362,6 +487,16 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.19.6: + resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.19.3: resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: @@ -371,6 +506,15 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.19.6: + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.19.3: resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: @@ -380,6 +524,15 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.19.3: resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: @@ -389,6 +542,15 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.19.6: + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.19.3: resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: @@ -398,6 +560,15 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.19.3: resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: @@ -407,6 +578,15 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.19.3: resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: @@ -416,6 +596,15 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.19.3: resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} @@ -426,6 +615,16 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.19.6: + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-typescript/7.18.6_@babel+core@7.19.3: resolution: {integrity: sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==} engines: {node: '>=6.9.0'} @@ -436,12 +635,22 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true + /@babel/plugin-syntax-typescript/7.18.6_@babel+core@7.19.6: + resolution: {integrity: sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/template/7.18.10: resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.18.6 - '@babel/parser': 7.19.4 + '@babel/parser': 7.19.6 '@babel/types': 7.19.4 /@babel/traverse/7.19.4: @@ -461,6 +670,23 @@ packages: transitivePeerDependencies: - supports-color + /@babel/traverse/7.19.6: + resolution: {integrity: sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.19.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.19.6 + '@babel/types': 7.19.4 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + /@babel/types/7.19.4: resolution: {integrity: sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==} engines: {node: '>=6.9.0'} @@ -546,6 +772,18 @@ packages: slash: 3.0.0 dev: true + /@jest/console/29.2.1: + resolution: {integrity: sha512-MF8Adcw+WPLZGBiNxn76DOuczG3BhODTcMlDCA4+cFi41OkaY/lyI0XUUhi73F88Y+7IHoGmD80pN5CtxQUdSw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.2.1 + '@types/node': 14.18.32 + chalk: 4.1.2 + jest-message-util: 29.2.1 + jest-util: 29.2.1 + slash: 3.0.0 + dev: true + /@jest/core/29.0.3_ts-node@10.9.1: resolution: {integrity: sha512-1d0hLbOrM1qQE3eP3DtakeMbKTcXiXP3afWxqz103xPyddS2NhnNghS7MaXx1dcDt4/6p4nlhmeILo2ofgi8cQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -588,6 +826,48 @@ packages: - ts-node dev: true + /@jest/core/29.2.1_ts-node@10.9.1: + resolution: {integrity: sha512-kuLKYqnqgerXkBUwlHVxeSuhSnd+JMnMCLfU98bpacBSfWEJPegytDh3P2m15/JHzet32hGGld4KR4OzMb6/Tg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.2.1 + '@jest/reporters': 29.2.1 + '@jest/test-result': 29.2.1 + '@jest/transform': 29.2.1 + '@jest/types': 29.2.1 + '@types/node': 14.18.32 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.5.0 + exit: 0.1.2 + graceful-fs: 4.2.10 + jest-changed-files: 29.2.0 + jest-config: 29.2.1_4f2ldd7um3b3u4eyvetyqsphze + jest-haste-map: 29.2.1 + jest-message-util: 29.2.1 + jest-regex-util: 29.2.0 + jest-resolve: 29.2.1 + jest-resolve-dependencies: 29.2.1 + jest-runner: 29.2.1 + jest-runtime: 29.2.1 + jest-snapshot: 29.2.1 + jest-util: 29.2.1 + jest-validate: 29.2.1 + jest-watcher: 29.2.1 + micromatch: 4.0.5 + pretty-format: 29.2.1 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + /@jest/environment/29.0.3: resolution: {integrity: sha512-iKl272NKxYNQNqXMQandAIwjhQaGw5uJfGXduu8dS9llHi8jV2ChWrtOAVPnMbaaoDhnI3wgUGNDvZgHeEJQCA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -598,6 +878,16 @@ packages: jest-mock: 29.0.3 dev: true + /@jest/environment/29.2.1: + resolution: {integrity: sha512-EutqA7T/X6zFjw6mAWRHND+ZkTPklmIEWCNbmwX6uCmOrFrWaLbDZjA+gePHJx6fFMMRvNfjXcvzXEtz54KPlg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.2.1 + '@jest/types': 29.2.1 + '@types/node': 14.18.32 + jest-mock: 29.2.1 + dev: true + /@jest/expect-utils/29.0.3: resolution: {integrity: sha512-i1xUkau7K/63MpdwiRqaxgZOjxYs4f0WMTGJnYwUKubsNRZSeQbLorS7+I4uXVF9KQ5r61BUPAUMZ7Lf66l64Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -605,6 +895,13 @@ packages: jest-get-type: 29.0.0 dev: true + /@jest/expect-utils/29.2.1: + resolution: {integrity: sha512-yr4aHNg5Z1CjKby5ozm7sKjgBlCOorlAoFcvrOQ/4rbZRfgZQdnmh7cth192PYIgiPZo2bBXvqdOApnAMWFJZg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.2.0 + dev: true + /@jest/expect/29.0.3: resolution: {integrity: sha512-6W7K+fsI23FQ01H/BWccPyDZFrnU9QlzDcKOjrNVU5L8yUORFAJJIpmyxWPW70+X624KUNqzZwPThPMX28aXEQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -615,6 +912,16 @@ packages: - supports-color dev: true + /@jest/expect/29.2.1: + resolution: {integrity: sha512-o14R2t2tHHHudwji43UKkzmmH49xfF5T++FQBK2tl88qwuBWQOcx7fNUYl+mA/9TPNAN0FkQ3usnpyS8FUwsvQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.2.1 + jest-snapshot: 29.2.1 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/fake-timers/29.0.3: resolution: {integrity: sha512-tmbUIo03x0TdtcZCESQ0oQSakPCpo7+s6+9mU19dd71MptkP4zCwoeZqna23//pgbhtT1Wq02VmA9Z9cNtvtCQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -627,6 +934,18 @@ packages: jest-util: 29.0.3 dev: true + /@jest/fake-timers/29.2.1: + resolution: {integrity: sha512-KWil+8fef7Uj/P/PTZlPKk1Pw117wAmr71VWFV8ZDtRtkwmTG8oY4IRf0Ss44J2y5CYRy8d/zLOhxyoGRENjvA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.2.1 + '@sinonjs/fake-timers': 9.1.2 + '@types/node': 14.18.32 + jest-message-util: 29.2.1 + jest-mock: 29.2.1 + jest-util: 29.2.1 + dev: true + /@jest/globals/29.0.3: resolution: {integrity: sha512-YqGHT65rFY2siPIHHFjuCGUsbzRjdqkwbat+Of6DmYRg5shIXXrLdZoVE/+TJ9O1dsKsFmYhU58JvIbZRU1Z9w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -639,6 +958,18 @@ packages: - supports-color dev: true + /@jest/globals/29.2.1: + resolution: {integrity: sha512-Z4EejYPP1OPVq2abk1+9urAwJqkgw5jB2UJGlPjb5ZwzPQF8WLMcigKEfFzZb2OHhEVPP0RZD0/DbVTY1R6iQA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.2.1 + '@jest/expect': 29.2.1 + '@jest/types': 29.2.1 + jest-mock: 29.2.1 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/reporters/29.0.3: resolution: {integrity: sha512-3+QU3d4aiyOWfmk1obDerie4XNCaD5Xo1IlKNde2yGEi02WQD+ZQD0i5Hgqm1e73sMV7kw6pMlCnprtEwEVwxw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -677,11 +1008,48 @@ packages: - supports-color dev: true + /@jest/reporters/29.2.1: + resolution: {integrity: sha512-sCsfUKM/yIF4nNed3e/rIgVIS58EiASGMDEPWqItfLZ9UO1ALW2ASDNJzdWkxEt0T8o2Ztj619G0KKrvK+McAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.2.1 + '@jest/test-result': 29.2.1 + '@jest/transform': 29.2.1 + '@jest/types': 29.2.1 + '@jridgewell/trace-mapping': 0.3.17 + '@types/node': 14.18.32 + chalk: 4.1.2 + collect-v8-coverage: 1.0.1 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.10 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.0 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.5 + jest-message-util: 29.2.1 + jest-util: 29.2.1 + jest-worker: 29.2.1 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.0.1 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/schemas/29.0.0: resolution: {integrity: sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@sinclair/typebox': 0.24.42 + '@sinclair/typebox': 0.24.47 dev: true /@jest/source-map/29.0.0: @@ -693,6 +1061,15 @@ packages: graceful-fs: 4.2.10 dev: true + /@jest/source-map/29.2.0: + resolution: {integrity: sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.17 + callsites: 3.1.0 + graceful-fs: 4.2.10 + dev: true + /@jest/test-result/29.0.3: resolution: {integrity: sha512-vViVnQjCgTmbhDKEonKJPtcFe9G/CJO4/Np4XwYJah+lF2oI7KKeRp8t1dFvv44wN2NdbDb/qC6pi++Vpp0Dlg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -703,6 +1080,16 @@ packages: collect-v8-coverage: 1.0.1 dev: true + /@jest/test-result/29.2.1: + resolution: {integrity: sha512-lS4+H+VkhbX6z64tZP7PAUwPqhwj3kbuEHcaLuaBuB+riyaX7oa1txe0tXgrFj5hRWvZKvqO7LZDlNWeJ7VTPA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.2.1 + '@jest/types': 29.2.1 + '@types/istanbul-lib-coverage': 2.0.4 + collect-v8-coverage: 1.0.1 + dev: true + /@jest/test-sequencer/29.0.3: resolution: {integrity: sha512-Hf4+xYSWZdxTNnhDykr8JBs0yBN/nxOXyUQWfotBUqqy0LF9vzcFB0jm/EDNZCx587znLWTIgxcokW7WeZMobQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -713,6 +1100,16 @@ packages: slash: 3.0.0 dev: true + /@jest/test-sequencer/29.2.1: + resolution: {integrity: sha512-O/pnk0/xGj3lxPVNwB6HREJ7AYvUdyP2xo/s14/9Dtf091HoOeyIhWLKQE/4HzB8lNQBMo6J5mg0bHz/uCWK7w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.2.1 + graceful-fs: 4.2.10 + jest-haste-map: 29.2.1 + slash: 3.0.0 + dev: true + /@jest/transform/29.0.3: resolution: {integrity: sha512-C5ihFTRYaGDbi/xbRQRdbo5ddGtI4VSpmL6AIcZxdhwLbXMa7PcXxxqyI91vGOFHnn5aVM3WYnYKCHEqmLVGzg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -736,6 +1133,29 @@ packages: - supports-color dev: true + /@jest/transform/29.2.1: + resolution: {integrity: sha512-xup+iEuaIRSQabQaeqxaQyN0vg1Dctrp9oTObQsNf3sZEowTIa5cANYuoyi8Tqhg4GCqEVLTf18KW7ii0UeFVA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.19.6 + '@jest/types': 29.2.1 + '@jridgewell/trace-mapping': 0.3.17 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 1.9.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.10 + jest-haste-map: 29.2.1 + jest-regex-util: 29.2.0 + jest-util: 29.2.1 + micromatch: 4.0.5 + pirates: 4.0.5 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/types/29.0.3: resolution: {integrity: sha512-coBJmOQvurXjN1Hh5PzF7cmsod0zLIOXpP8KD161mqNlroMhLcwpODiEzi7ZsRl5Z/AIuxpeNm8DCl43F4kz8A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -748,6 +1168,18 @@ packages: chalk: 4.1.2 dev: true + /@jest/types/29.2.1: + resolution: {integrity: sha512-O/QNDQODLnINEPAI0cl9U6zUIDXEWXt6IC1o2N2QENuos7hlGUIthlKyV4p6ki3TvXFX071blj8HUhgLGquPjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.0.0 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 14.18.32 + '@types/yargs': 17.0.13 + chalk: 4.1.2 + dev: true + /@jridgewell/gen-mapping/0.1.1: resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} engines: {node: '>=6.0.0'} @@ -761,7 +1193,7 @@ packages: dependencies: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.16 + '@jridgewell/trace-mapping': 0.3.17 /@jridgewell/resolve-uri/3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} @@ -779,6 +1211,13 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@jridgewell/trace-mapping/0.3.17: + resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 /@jridgewell/trace-mapping/0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -955,8 +1394,8 @@ packages: engines: {node: '>=14'} dev: true - /@prisma/debug/4.4.0: - resolution: {integrity: sha512-tpJqrvmA8VlQuaVAmkFzIU7Of6xk3kQ2DYV6bPJukDZ6xmnufT27EpU8TSIra4jGdzz7y/R0rxmuXSBp24ew5w==} + /@prisma/debug/4.5.0: + resolution: {integrity: sha512-zTBisqSCipBN7veltdhuHU89t98BHQWH4qb6rJAla39AulLtsjCOUu5QEBUmXEuND5SChjYP/S9rJ4mVHkcTdg==} dependencies: '@types/debug': 4.1.7 debug: 4.3.4 @@ -965,15 +1404,15 @@ packages: - supports-color dev: true - /@prisma/engine-core/4.4.0: - resolution: {integrity: sha512-SwwfaBkQhw6EpwDK2X6+2w4Z9cfdQiZWC2Rmdp26JC4R1Q3i3Y/7GPgMSrwCJvSF9y0+nJ93Kyx7uq9xuIlSZQ==} + /@prisma/engine-core/4.5.0: + resolution: {integrity: sha512-pOiJPsXwvy5HHVvzCyb2gVs+yT2/KEkH2KrRF7szQrmaRhsh44ollv05u0VMM8xKy79n15L3ZXz4nPS0LxPK4Q==} dependencies: '@opentelemetry/api': 1.2.0 '@opentelemetry/sdk-trace-base': 1.7.0_@opentelemetry+api@1.2.0 - '@prisma/debug': 4.4.0 - '@prisma/engines': 4.4.0 - '@prisma/generator-helper': 4.4.0 - '@prisma/get-platform': 4.4.0 + '@prisma/debug': 4.5.0 + '@prisma/engines': 4.5.0 + '@prisma/generator-helper': 4.5.0 + '@prisma/get-platform': 4.5.0 chalk: 4.1.2 execa: 5.1.1 get-stream: 6.0.1 @@ -986,15 +1425,15 @@ packages: - supports-color dev: true - /@prisma/engines/4.4.0: - resolution: {integrity: sha512-Fpykccxlt9MHrAs/QpPGpI2nOiRxuLA+LiApgA59ibbf24YICZIMWd3SI2YD+q0IAIso0jCGiHhirAIbxK3RyQ==} + /@prisma/engines/4.5.0: + resolution: {integrity: sha512-4t9ir2SbQQr/wMCNU4YpHWp5hU14J2m3wHUZnGJPpmBF8YtkisxyVyQsKd1e6FyLTaGq8LOLhm6VLYHKqKNm+g==} requiresBuild: true - /@prisma/fetch-engine/4.4.0: - resolution: {integrity: sha512-3a+f/HPvJl9XYj8IuX57/rHM8cYZuqS+R+jXx/ZPRwvELVlvVeE81GTTSMvtXguyfHXgKW7wKjiJqZm7tGw/WA==} + /@prisma/fetch-engine/4.5.0: + resolution: {integrity: sha512-IIJj+7PIfQj65OfkkPv4hyd4O3flE1DfUhdHLa7v2+XZrzoKOC+Dj6ksAeXKhZSj60Tgk0Ed1SPPIczrvN8e6Q==} dependencies: - '@prisma/debug': 4.4.0 - '@prisma/get-platform': 4.4.0 + '@prisma/debug': 4.5.0 + '@prisma/get-platform': 4.5.0 chalk: 4.1.2 execa: 5.1.1 find-cache-dir: 3.3.2 @@ -1015,10 +1454,10 @@ packages: - supports-color dev: true - /@prisma/generator-helper/4.4.0: - resolution: {integrity: sha512-6z5Tl+Cjk/WDWUMvpCOzhsK4Nkcb64zGoU/NePl3Z0tFR2RkfnBfnmJbpWdHr9HQYz7jO7LsTAyS5aWPjqeHKg==} + /@prisma/generator-helper/4.5.0: + resolution: {integrity: sha512-4Ky6sIvTSylLkWQmwVezaw8bHE/TfsnoFyPHDphBOl5r/l3X2I1yy1g2kVAqNQ9phkEDzRX7ZIIn6w9jCGtOLg==} dependencies: - '@prisma/debug': 4.4.0 + '@prisma/debug': 4.5.0 '@types/cross-spawn': 6.0.2 chalk: 4.1.2 cross-spawn: 7.0.3 @@ -1026,24 +1465,24 @@ packages: - supports-color dev: true - /@prisma/get-platform/4.4.0: - resolution: {integrity: sha512-3m8Y07h9bZlfS98dh5/e2wS+5iJ3NLBOy3bv7zjGa2GI68EW5q3ncbtHHw9vk5G6epTO7YrM/PBFqQWc4fKvNA==} + /@prisma/get-platform/4.5.0: + resolution: {integrity: sha512-ndamUoGPzstoirM1MYbbzQ5j4MgBETUuX5HzP/IlewJ8t3AkI18aONfM88bWsbDQaT7vP6I2FEqpwYECP/XXFw==} dependencies: - '@prisma/debug': 4.4.0 + '@prisma/debug': 4.5.0 transitivePeerDependencies: - supports-color dev: true - /@prisma/internals/4.4.0: - resolution: {integrity: sha512-DvJ78z+HI6+5qA4SSHS/BRxR8m0aTIpJIhBbV/cY6m6kYgOvBjuf9slp+MqHpRR28eC5rgOS+e0DlN/tU+cxLg==} + /@prisma/internals/4.5.0: + resolution: {integrity: sha512-PwvxeMWBMIJK3VykXsXlR4KFVUX6KxAqALIZQA+Bib71bDS+lqIlHRq852mVaPSNZ5QD2fSP/wA7gQ4T+ZJQ6g==} dependencies: - '@prisma/debug': 4.4.0 - '@prisma/engine-core': 4.4.0 - '@prisma/engines': 4.4.0 - '@prisma/fetch-engine': 4.4.0 - '@prisma/generator-helper': 4.4.0 - '@prisma/get-platform': 4.4.0 - '@prisma/prisma-fmt-wasm': 4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6 + '@prisma/debug': 4.5.0 + '@prisma/engine-core': 4.5.0 + '@prisma/engines': 4.5.0 + '@prisma/fetch-engine': 4.5.0 + '@prisma/generator-helper': 4.5.0 + '@prisma/get-platform': 4.5.0 + '@prisma/prisma-fmt-wasm': 4.5.0-43.0362da9eebca54d94c8ef5edd3b2e90af99ba452 archiver: 5.3.1 arg: 5.0.2 chalk: 4.1.2 @@ -1086,12 +1525,12 @@ packages: - supports-color dev: true - /@prisma/prisma-fmt-wasm/4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6: - resolution: {integrity: sha512-Hc2i5nfAt3nLDUkQNWJcKFJaA9Avd5zz6t85w9SW7P0vGtFXScQ+xIu6znbULr9bc0pgTWejY1We2u/7EMxHWw==} + /@prisma/prisma-fmt-wasm/4.5.0-43.0362da9eebca54d94c8ef5edd3b2e90af99ba452: + resolution: {integrity: sha512-MYWUyB+sk3AL/dJFdAzoGbmcYQKA3F8SzsdPUCVfH3I0FujdwbR+pabIXogOHVt8eZySiJWW7+yAWOD2GkBtoA==} dev: true - /@sinclair/typebox/0.24.42: - resolution: {integrity: sha512-d+2AtrHGyWek2u2ITF0lHRIv6Tt7X0dEHW+0rP+5aDCEjC3fiN2RBjrLD0yU0at52BcZbRGxLbAtXiR0hFCjYw==} + /@sinclair/typebox/0.24.47: + resolution: {integrity: sha512-J4Xw0xYK4h7eC34MNOPQi6IkNxGRck6n4VJpWDzXIFVTW8I/D43Gf+NfWz/v/7NHlzWOPd3+T4PJ4OqklQ2u7A==} dev: true /@sinonjs/commons/1.8.3: @@ -1144,11 +1583,11 @@ packages: /@types/babel__core/7.1.19: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} dependencies: - '@babel/parser': 7.19.4 + '@babel/parser': 7.19.6 '@babel/types': 7.19.4 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 - '@types/babel__traverse': 7.18.1 + '@types/babel__traverse': 7.18.2 dev: true /@types/babel__generator/7.6.4: @@ -1160,7 +1599,7 @@ packages: /@types/babel__template/7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.19.4 + '@babel/parser': 7.19.6 '@babel/types': 7.19.4 dev: true @@ -1170,6 +1609,12 @@ packages: '@babel/types': 7.19.4 dev: true + /@types/babel__traverse/7.18.2: + resolution: {integrity: sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==} + dependencies: + '@babel/types': 7.19.4 + dev: true + /@types/bcryptjs/2.4.2: resolution: {integrity: sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==} @@ -1180,7 +1625,7 @@ packages: /@types/cross-spawn/6.0.2: resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==} dependencies: - '@types/node': 16.11.62 + '@types/node': 14.18.32 dev: true /@types/debug/4.1.7: @@ -1192,7 +1637,7 @@ packages: /@types/graceful-fs/4.1.5: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: - '@types/node': 16.11.62 + '@types/node': 14.18.32 dev: true /@types/istanbul-lib-coverage/2.0.4: @@ -1218,6 +1663,13 @@ packages: pretty-format: 29.0.3 dev: true + /@types/jest/29.2.0: + resolution: {integrity: sha512-KO7bPV21d65PKwv3LLsD8Jn3E05pjNjRZvkm+YTacWhVmykAb07wW6IkZUmQAltwQafNcDUEUrMO2h3jeBSisg==} + dependencies: + expect: 29.2.1 + pretty-format: 29.2.1 + dev: true + /@types/json-schema/7.0.11: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true @@ -1229,6 +1681,10 @@ packages: /@types/node/14.18.29: resolution: {integrity: sha512-LhF+9fbIX4iPzhsRLpK5H7iPdvW8L4IwGciXQIOEcuF62+9nw/VQVsOViAOOGxY3OlOKGLFv0sWwJXdwQeTn6A==} + /@types/node/14.18.32: + resolution: {integrity: sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow==} + dev: true + /@types/node/16.11.62: resolution: {integrity: sha512-K/ggecSdwAAy2NUW4WKmF4Rc03GKbsfP+k326UWgckoS+Rzd2PaWbjk76dSmqdLQvLTJAO9axiTUJ6488mFsYQ==} dev: true @@ -1237,10 +1693,18 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true + /@types/pluralize/0.0.29: + resolution: {integrity: sha512-BYOID+l2Aco2nBik+iYS4SZX0Lf20KPILP5RGmM1IgzdwNdTs0eebiFriOPcej1sX9mLnSoiNte5zcFxssgpGA==} + dev: true + /@types/prettier/2.7.0: resolution: {integrity: sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==} dev: true + /@types/prettier/2.7.1: + resolution: {integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==} + dev: true + /@types/retry/0.12.0: resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} dev: true @@ -1270,8 +1734,8 @@ packages: resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} dev: true - /@types/vscode/1.71.0: - resolution: {integrity: sha512-nB50bBC9H/x2CpwW9FzRRRDrTZ7G0/POttJojvN/LiVfzTGfLyQIje1L1QRMdFXK9G41k5UJN/1B9S4of7CSzA==} + /@types/vscode/1.72.0: + resolution: {integrity: sha512-WvHluhUo+lQvE3I4wUagRpnkHuysB4qSyOQUyIAS9n9PYMJjepzTUD8Jyks0YeXoPD0UGctjqp2u84/b3v6Ydw==} dev: true /@types/yargs-parser/21.0.0: @@ -1284,7 +1748,13 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@typescript-eslint/eslint-plugin/4.33.0_qkm6m2dvh7633pj2jigehwm774: + /@types/yargs/17.0.13: + resolution: {integrity: sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==} + dependencies: + '@types/yargs-parser': 21.0.0 + dev: true + + /@typescript-eslint/eslint-plugin/4.33.0_k4l66av2tbo6kxzw52jzgbfzii: resolution: {integrity: sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -1295,22 +1765,22 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/experimental-utils': 4.33.0_dyxdave6dwjbccc5dgiifcmuza - '@typescript-eslint/parser': 4.33.0_dyxdave6dwjbccc5dgiifcmuza + '@typescript-eslint/experimental-utils': 4.33.0_3rubbgt5ekhqrcgx4uwls3neim + '@typescript-eslint/parser': 4.33.0_3rubbgt5ekhqrcgx4uwls3neim '@typescript-eslint/scope-manager': 4.33.0 debug: 4.3.4 eslint: 7.32.0 functional-red-black-tree: 1.0.1 ignore: 5.2.0 regexpp: 3.2.0 - semver: 7.3.7 - tsutils: 3.21.0_typescript@4.8.3 - typescript: 4.8.3 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.8.4 + typescript: 4.8.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/experimental-utils/4.33.0_dyxdave6dwjbccc5dgiifcmuza: + /@typescript-eslint/experimental-utils/4.33.0_3rubbgt5ekhqrcgx4uwls3neim: resolution: {integrity: sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -1319,7 +1789,7 @@ packages: '@types/json-schema': 7.0.11 '@typescript-eslint/scope-manager': 4.33.0 '@typescript-eslint/types': 4.33.0 - '@typescript-eslint/typescript-estree': 4.33.0_typescript@4.8.3 + '@typescript-eslint/typescript-estree': 4.33.0_typescript@4.8.4 eslint: 7.32.0 eslint-scope: 5.1.1 eslint-utils: 3.0.0_eslint@7.32.0 @@ -1328,7 +1798,7 @@ packages: - typescript dev: true - /@typescript-eslint/parser/4.33.0_dyxdave6dwjbccc5dgiifcmuza: + /@typescript-eslint/parser/4.33.0_3rubbgt5ekhqrcgx4uwls3neim: resolution: {integrity: sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -1340,10 +1810,10 @@ packages: dependencies: '@typescript-eslint/scope-manager': 4.33.0 '@typescript-eslint/types': 4.33.0 - '@typescript-eslint/typescript-estree': 4.33.0_typescript@4.8.3 + '@typescript-eslint/typescript-estree': 4.33.0_typescript@4.8.4 debug: 4.3.4 eslint: 7.32.0 - typescript: 4.8.3 + typescript: 4.8.4 transitivePeerDependencies: - supports-color dev: true @@ -1361,7 +1831,7 @@ packages: engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} dev: true - /@typescript-eslint/typescript-estree/4.33.0_typescript@4.8.3: + /@typescript-eslint/typescript-estree/4.33.0_typescript@4.8.4: resolution: {integrity: sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -1375,9 +1845,9 @@ packages: debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.3.7 - tsutils: 3.21.0_typescript@4.8.3 - typescript: 4.8.3 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.8.4 + typescript: 4.8.4 transitivePeerDependencies: - supports-color dev: true @@ -1581,6 +2051,24 @@ packages: - supports-color dev: true + /babel-jest/29.2.1_@babel+core@7.19.6: + resolution: {integrity: sha512-gQJwArok0mqoREiCYhXKWOgUhElJj9DpnssW6GL8dG7ARYqHEhrM9fmPHTjdqEGRVXZAd6+imo3/Vwa8TjLcsw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.19.6 + '@jest/transform': 29.2.1 + '@types/babel__core': 7.1.19 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.2.0_@babel+core@7.19.6 + chalk: 4.1.2 + graceful-fs: 4.2.10 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-istanbul/6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} @@ -1588,7 +2076,7 @@ packages: '@babel/helper-plugin-utils': 7.19.0 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.0 + istanbul-lib-instrument: 5.2.1 test-exclude: 6.0.0 transitivePeerDependencies: - supports-color @@ -1604,6 +2092,16 @@ packages: '@types/babel__traverse': 7.18.1 dev: true + /babel-plugin-jest-hoist/29.2.0: + resolution: {integrity: sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.18.10 + '@babel/types': 7.19.4 + '@types/babel__core': 7.1.19 + '@types/babel__traverse': 7.18.2 + dev: true + /babel-preset-current-node-syntax/1.0.1_@babel+core@7.19.3: resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: @@ -1624,6 +2122,26 @@ packages: '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.19.3 dev: true + /babel-preset-current-node-syntax/1.0.1_@babel+core@7.19.6: + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.19.6 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.19.6 + '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.19.6 + '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.19.6 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.19.6 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.19.6 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.19.6 + dev: true + /babel-preset-jest/29.0.2_@babel+core@7.19.3: resolution: {integrity: sha512-BeVXp7rH5TK96ofyEnHjznjLMQ2nAeDJ+QzxKnHAAMs0RgrQsCywjAN8m4mOm5Di0pxU//3AoEeJJrerMH5UeA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1635,6 +2153,17 @@ packages: babel-preset-current-node-syntax: 1.0.1_@babel+core@7.19.3 dev: true + /babel-preset-jest/29.2.0_@babel+core@7.19.6: + resolution: {integrity: sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.19.6 + babel-plugin-jest-hoist: 29.2.0 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.19.6 + dev: true + /balanced-match/1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1681,10 +2210,10 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001409 - electron-to-chromium: 1.4.257 + caniuse-lite: 1.0.30001422 + electron-to-chromium: 1.4.284 node-releases: 2.0.6 - update-browserslist-db: 1.0.9_browserslist@4.21.4 + update-browserslist-db: 1.0.10_browserslist@4.21.4 /bs-logger/0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} @@ -1746,6 +2275,9 @@ packages: /caniuse-lite/1.0.30001409: resolution: {integrity: sha512-V0mnJ5dwarmhYv8/MzhJ//aW68UpvnQBXv8lJ2QUsvn2pHcmAuNtu8hQEDz37XnA1iE+lRR9CIfGWWpgJ5QedQ==} + /caniuse-lite/1.0.30001422: + resolution: {integrity: sha512-hSesn02u1QacQHhaxl/kNMZwqVG35Sz/8DgvmgedxSH8z9UUpcDYSPYgsj3x5dQNRcNp6BwpSfQfVzYUTm+fog==} + /capital-case/1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} dependencies: @@ -1836,6 +2368,10 @@ packages: resolution: {integrity: sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==} dev: true + /ci-info/3.5.0: + resolution: {integrity: sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==} + dev: true + /cjs-module-lexer/1.2.2: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} dev: true @@ -1873,6 +2409,15 @@ packages: wrap-ansi: 7.0.0 dev: true + /cliui/8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + /clone/1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} @@ -1913,6 +2458,7 @@ packages: /colors/1.4.0: resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} engines: {node: '>=0.1.90'} + dev: false /combined-stream/1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} @@ -1965,12 +2511,12 @@ packages: chalk: 4.1.2 date-fns: 2.29.3 lodash: 4.17.21 - rxjs: 7.5.6 - shell-quote: 1.7.3 + rxjs: 7.5.7 + shell-quote: 1.7.4 spawn-command: 0.0.2-1 supports-color: 8.1.1 tree-kill: 1.2.2 - yargs: 17.5.1 + yargs: 17.6.0 dev: true /constant-case/3.0.4: @@ -2059,8 +2605,8 @@ packages: engines: {node: '>=0.10.0'} dev: true - /defaults/1.0.3: - resolution: {integrity: sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==} + /defaults/1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: clone: 1.0.4 dev: true @@ -2101,6 +2647,11 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /diff-sequences/29.2.0: + resolution: {integrity: sha512-413SY5JpYeSBZxmenGEmCVQ8mCgtFJF0w9PROdaS6z987XC2Pd2GOKqOITLtMftmyFZqgtCOb/QA7/Z3ZXfzIw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /diff/4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -2132,8 +2683,8 @@ packages: engines: {node: '>=12'} dev: true - /electron-to-chromium/1.4.257: - resolution: {integrity: sha512-C65sIwHqNnPC2ADMfse/jWTtmhZMII+x6ADI9gENzrOiI7BpxmfKFE84WkIEl5wEg+7+SfIkwChDlsd1Erju2A==} + /electron-to-chromium/1.4.284: + resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} /emittery/0.10.2: resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} @@ -2260,7 +2811,7 @@ packages: optionator: 0.9.1 progress: 2.0.3 regexpp: 3.2.0 - semver: 7.3.7 + semver: 7.3.8 strip-ansi: 6.0.1 strip-json-comments: 3.1.1 table: 6.8.0 @@ -2345,6 +2896,17 @@ packages: jest-util: 29.0.3 dev: true + /expect/29.2.1: + resolution: {integrity: sha512-BJtA754Fba0YWRWHgjKUMTA3ltWarKgITXHQnbZ2mTxTXC4yMQlR0FI7HkB3fJYkhWBf4qjNiqvg3LDtXCcVRQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.2.1 + jest-get-type: 29.2.0 + jest-matcher-utils: 29.2.1 + jest-message-util: 29.2.1 + jest-util: 29.2.1 + dev: true + /fast-deep-equal/3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -2733,8 +3295,8 @@ packages: binary-extensions: 2.2.0 dev: true - /is-core-module/2.10.0: - resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==} + /is-core-module/2.11.0: + resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 dev: true @@ -2832,6 +3394,19 @@ packages: - supports-color dev: true + /istanbul-lib-instrument/5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.19.6 + '@babel/parser': 7.19.6 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.0 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /istanbul-lib-report/3.0.0: resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} engines: {node: '>=8'} @@ -2868,6 +3443,14 @@ packages: p-limit: 3.1.0 dev: true + /jest-changed-files/29.2.0: + resolution: {integrity: sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + p-limit: 3.1.0 + dev: true + /jest-circus/29.0.3: resolution: {integrity: sha512-QeGzagC6Hw5pP+df1+aoF8+FBSgkPmraC1UdkeunWh0jmrp7wC0Hr6umdUAOELBQmxtKAOMNC3KAdjmCds92Zg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2895,6 +3478,33 @@ packages: - supports-color dev: true + /jest-circus/29.2.1: + resolution: {integrity: sha512-W+ZQQ5ln4Db2UZNM4NJIeasnhCdDhSuYW4eLgNAUi0XiSSpF634Kc5wiPvGiHvTgXMFVn1ZgWIijqhi9+kLNLg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.2.1 + '@jest/expect': 29.2.1 + '@jest/test-result': 29.2.1 + '@jest/types': 29.2.1 + '@types/node': 14.18.32 + chalk: 4.1.2 + co: 4.6.0 + dedent: 0.7.0 + is-generator-fn: 2.1.0 + jest-each: 29.2.1 + jest-matcher-utils: 29.2.1 + jest-message-util: 29.2.1 + jest-runtime: 29.2.1 + jest-snapshot: 29.2.1 + jest-util: 29.2.1 + p-limit: 3.1.0 + pretty-format: 29.2.1 + slash: 3.0.0 + stack-utils: 2.0.5 + transitivePeerDependencies: + - supports-color + dev: true + /jest-cli/29.0.3_johvxhudwcpndp4mle25vwrlq4: resolution: {integrity: sha512-aUy9Gd/Kut1z80eBzG10jAn6BgS3BoBbXyv+uXEqBJ8wnnuZ5RpNfARoskSrTIy1GY4a8f32YGuCMwibtkl9CQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2923,6 +3533,34 @@ packages: - ts-node dev: true + /jest-cli/29.2.1_4f2ldd7um3b3u4eyvetyqsphze: + resolution: {integrity: sha512-UIMD5aNqvPKpdlJSaeUAoLfxsh9TZvOkaMETx5qXnkboc317bcbb0eLHbIj8sFBHdcJAIAM+IRKnIU7Wi61MBw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.2.1_ts-node@10.9.1 + '@jest/test-result': 29.2.1 + '@jest/types': 29.2.1 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.10 + import-local: 3.1.0 + jest-config: 29.2.1_4f2ldd7um3b3u4eyvetyqsphze + jest-util: 29.2.1 + jest-validate: 29.2.1 + prompts: 2.4.2 + yargs: 17.6.0 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + /jest-config/29.0.3_johvxhudwcpndp4mle25vwrlq4: resolution: {integrity: sha512-U5qkc82HHVYe3fNu2CRXLN4g761Na26rWKf7CjM8LlZB3In1jadEkZdMwsE37rd9RSPV0NfYaCjHdk/gu3v+Ew==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3003,6 +3641,46 @@ packages: - supports-color dev: true + /jest-config/29.2.1_4f2ldd7um3b3u4eyvetyqsphze: + resolution: {integrity: sha512-EV5F1tQYW/quZV2br2o88hnYEeRzG53Dfi6rSG3TZBuzGQ6luhQBux/RLlU5QrJjCdq3LXxRRM8F1LP6DN1ycA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.19.6 + '@jest/test-sequencer': 29.2.1 + '@jest/types': 29.2.1 + '@types/node': 14.18.32 + babel-jest: 29.2.1_@babel+core@7.19.6 + chalk: 4.1.2 + ci-info: 3.5.0 + deepmerge: 4.2.2 + glob: 7.2.3 + graceful-fs: 4.2.10 + jest-circus: 29.2.1 + jest-environment-node: 29.2.1 + jest-get-type: 29.2.0 + jest-regex-util: 29.2.0 + jest-resolve: 29.2.1 + jest-runner: 29.2.1 + jest-util: 29.2.1 + jest-validate: 29.2.1 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.2.1 + slash: 3.0.0 + strip-json-comments: 3.1.1 + ts-node: 10.9.1_jcmx33t3olsvcxopqdljsohpme + transitivePeerDependencies: + - supports-color + dev: true + /jest-diff/29.0.3: resolution: {integrity: sha512-+X/AIF5G/vX9fWK+Db9bi9BQas7M9oBME7egU7psbn4jlszLFCu0dW63UgeE6cs/GANq4fLaT+8sGHQQ0eCUfg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3013,6 +3691,16 @@ packages: pretty-format: 29.0.3 dev: true + /jest-diff/29.2.1: + resolution: {integrity: sha512-gfh/SMNlQmP3MOUgdzxPOd4XETDJifADpT937fN1iUGz+9DgOu2eUPHH25JDkLVcLwwqxv3GzVyK4VBUr9fjfA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.2.0 + jest-get-type: 29.2.0 + pretty-format: 29.2.1 + dev: true + /jest-docblock/29.0.0: resolution: {integrity: sha512-s5Kpra/kLzbqu9dEjov30kj1n4tfu3e7Pl8v+f8jOkeWNqM6Ds8jRaJfZow3ducoQUrf2Z4rs2N5S3zXnb83gw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3020,6 +3708,13 @@ packages: detect-newline: 3.1.0 dev: true + /jest-docblock/29.2.0: + resolution: {integrity: sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + /jest-each/29.0.3: resolution: {integrity: sha512-wILhZfESURHHBNvPMJ0lZlYZrvOQJxAo3wNHi+ycr90V7M+uGR9Gh4+4a/BmaZF0XTyZsk4OiYEf3GJN7Ltqzg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3031,6 +3726,17 @@ packages: pretty-format: 29.0.3 dev: true + /jest-each/29.2.1: + resolution: {integrity: sha512-sGP86H/CpWHMyK3qGIGFCgP6mt+o5tu9qG4+tobl0LNdgny0aitLXs9/EBacLy3Bwqy+v4uXClqJgASJWcruYw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.2.1 + chalk: 4.1.2 + jest-get-type: 29.2.0 + jest-util: 29.2.1 + pretty-format: 29.2.1 + dev: true + /jest-environment-node/29.0.3: resolution: {integrity: sha512-cdZqRCnmIlTXC+9vtvmfiY/40Cj6s2T0czXuq1whvQdmpzAnj4sbqVYuZ4zFHk766xTTJ+Ij3uUqkk8KCfXoyg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3043,11 +3749,28 @@ packages: jest-util: 29.0.3 dev: true + /jest-environment-node/29.2.1: + resolution: {integrity: sha512-PulFKwEMz6nTAdLUwglFKei3b/LixwlRiqTN6nvPE1JtrLtlnpd6LXnFI1NFHYJGlTmIWilMP2n9jEtPPKX50g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.2.1 + '@jest/fake-timers': 29.2.1 + '@jest/types': 29.2.1 + '@types/node': 14.18.32 + jest-mock: 29.2.1 + jest-util: 29.2.1 + dev: true + /jest-get-type/29.0.0: resolution: {integrity: sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /jest-get-type/29.2.0: + resolution: {integrity: sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /jest-haste-map/29.0.3: resolution: {integrity: sha512-uMqR99+GuBHo0RjRhOE4iA6LmsxEwRdgiIAQgMU/wdT2XebsLDz5obIwLZm/Psj+GwSEQhw9AfAVKGYbh2G55A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3067,6 +3790,25 @@ packages: fsevents: 2.3.2 dev: true + /jest-haste-map/29.2.1: + resolution: {integrity: sha512-wF460rAFmYc6ARcCFNw4MbGYQjYkvjovb9GBT+W10Um8q5nHq98jD6fHZMDMO3tA56S8XnmNkM8GcA8diSZfnA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.2.1 + '@types/graceful-fs': 4.1.5 + '@types/node': 14.18.32 + anymatch: 3.1.2 + fb-watchman: 2.0.2 + graceful-fs: 4.2.10 + jest-regex-util: 29.2.0 + jest-util: 29.2.1 + jest-worker: 29.2.1 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /jest-leak-detector/29.0.3: resolution: {integrity: sha512-YfW/G63dAuiuQ3QmQlh8hnqLDe25WFY3eQhuc/Ev1AGmkw5zREblTh7TCSKLoheyggu6G9gxO2hY8p9o6xbaRQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3075,6 +3817,14 @@ packages: pretty-format: 29.0.3 dev: true + /jest-leak-detector/29.2.1: + resolution: {integrity: sha512-1YvSqYoiurxKOJtySc+CGVmw/e1v4yNY27BjWTVzp0aTduQeA7pdieLiW05wTYG/twlKOp2xS/pWuikQEmklug==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.2.0 + pretty-format: 29.2.1 + dev: true + /jest-matcher-utils/29.0.3: resolution: {integrity: sha512-RsR1+cZ6p1hDV4GSCQTg+9qjeotQCgkaleIKLK7dm+U4V/H2bWedU3RAtLm8+mANzZ7eDV33dMar4pejd7047w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3085,6 +3835,16 @@ packages: pretty-format: 29.0.3 dev: true + /jest-matcher-utils/29.2.1: + resolution: {integrity: sha512-hUTBh7H/Mnb6GTpihbLh8uF5rjAMdekfW/oZNXUMAXi7bbmym2HiRpzgqf/zzkjgejMrVAkPdVSQj+32enlUww==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.2.1 + jest-get-type: 29.2.0 + pretty-format: 29.2.1 + dev: true + /jest-message-util/29.0.3: resolution: {integrity: sha512-7T8JiUTtDfppojosORAflABfLsLKMLkBHSWkjNQrjIltGoDzNGn7wEPOSfjqYAGTYME65esQzMJxGDjuLBKdOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3100,6 +3860,21 @@ packages: stack-utils: 2.0.5 dev: true + /jest-message-util/29.2.1: + resolution: {integrity: sha512-Dx5nEjw9V8C1/Yj10S/8ivA8F439VS8vTq1L7hEgwHFn9ovSKNpYW/kwNh7UglaEgXO42XxzKJB+2x0nSglFVw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.18.6 + '@jest/types': 29.2.1 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.10 + micromatch: 4.0.5 + pretty-format: 29.2.1 + slash: 3.0.0 + stack-utils: 2.0.5 + dev: true + /jest-mock/29.0.3: resolution: {integrity: sha512-ort9pYowltbcrCVR43wdlqfAiFJXBx8l4uJDsD8U72LgBcetvEp+Qxj1W9ZYgMRoeAo+ov5cnAGF2B6+Oth+ww==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3108,6 +3883,15 @@ packages: '@types/node': 16.11.62 dev: true + /jest-mock/29.2.1: + resolution: {integrity: sha512-NDphaY/GqyQpTfnTZiTqqpMaw4Z0I7XnB7yBgrT6IwYrLGxpOhrejYr4ANY4YvO2sEGdd8Tx/6D0+WLQy7/qDA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.2.1 + '@types/node': 14.18.32 + jest-util: 29.2.1 + dev: true + /jest-pnp-resolver/1.2.2_jest-resolve@29.0.3: resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} engines: {node: '>=6'} @@ -3120,11 +3904,28 @@ packages: jest-resolve: 29.0.3 dev: true + /jest-pnp-resolver/1.2.2_jest-resolve@29.2.1: + resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.2.1 + dev: true + /jest-regex-util/29.0.0: resolution: {integrity: sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /jest-regex-util/29.2.0: + resolution: {integrity: sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /jest-resolve-dependencies/29.0.3: resolution: {integrity: sha512-KzuBnXqNvbuCdoJpv8EanbIGObk7vUBNt/PwQPPx2aMhlv/jaXpUJsqWYRpP/0a50faMBY7WFFP8S3/CCzwfDw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3135,6 +3936,16 @@ packages: - supports-color dev: true + /jest-resolve-dependencies/29.2.1: + resolution: {integrity: sha512-o3mUGX2j08usj1jIAIE8KmUVpqVAn54k80kI27ldbZf2oJn6eghhB6DvJxjrcH40va9CQgWTfU5f2Ag/MoUqgQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.2.0 + jest-snapshot: 29.2.1 + transitivePeerDependencies: + - supports-color + dev: true + /jest-resolve/29.0.3: resolution: {integrity: sha512-toVkia85Y/BPAjJasTC9zIPY6MmVXQPtrCk8SmiheC4MwVFE/CMFlOtMN6jrwPMC6TtNh8+sTMllasFeu1wMPg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3150,6 +3961,21 @@ packages: slash: 3.0.0 dev: true + /jest-resolve/29.2.1: + resolution: {integrity: sha512-1dJTW76Z9622Viq4yRcwBuEXuzGtE9B2kdl05RC8Om/lAzac9uEgC+M8Q5osVidbuBPmxm8wSrcItYhca2ZAtQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.10 + jest-haste-map: 29.2.1 + jest-pnp-resolver: 1.2.2_jest-resolve@29.2.1 + jest-util: 29.2.1 + jest-validate: 29.2.1 + resolve: 1.22.1 + resolve.exports: 1.1.0 + slash: 3.0.0 + dev: true + /jest-runner/29.0.3: resolution: {integrity: sha512-Usu6VlTOZlCZoNuh3b2Tv/yzDpKqtiNAetG9t3kJuHfUyVMNW7ipCCJOUojzKkjPoaN7Bl1f7Buu6PE0sGpQxw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3179,6 +4005,35 @@ packages: - supports-color dev: true + /jest-runner/29.2.1: + resolution: {integrity: sha512-PojFI+uVhQ4u4YZKCN/a3yU0/l/pJJXhq1sW3JpCp8CyvGBYGddRFPKZ1WihApusxqWRTHjBJmGyPWv6Av2lWA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.2.1 + '@jest/environment': 29.2.1 + '@jest/test-result': 29.2.1 + '@jest/transform': 29.2.1 + '@jest/types': 29.2.1 + '@types/node': 14.18.32 + chalk: 4.1.2 + emittery: 0.10.2 + graceful-fs: 4.2.10 + jest-docblock: 29.2.0 + jest-environment-node: 29.2.1 + jest-haste-map: 29.2.1 + jest-leak-detector: 29.2.1 + jest-message-util: 29.2.1 + jest-resolve: 29.2.1 + jest-runtime: 29.2.1 + jest-util: 29.2.1 + jest-watcher: 29.2.1 + jest-worker: 29.2.1 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + dev: true + /jest-runtime/29.0.3: resolution: {integrity: sha512-12gZXRQ7ozEeEHKTY45a+YLqzNDR/x4c//X6AqwKwKJPpWM8FY4vwn4VQJOcLRS3Nd1fWwgP7LU4SoynhuUMHQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3209,6 +4064,36 @@ packages: - supports-color dev: true + /jest-runtime/29.2.1: + resolution: {integrity: sha512-PSQ880OoIW9y8E6/jjhGn3eQNgNc6ndMzCZaKqy357bv7FqCfSyYepu3yDC6Sp1Vkt+GhP2M/PVgldS2uZSFZg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.2.1 + '@jest/fake-timers': 29.2.1 + '@jest/globals': 29.2.1 + '@jest/source-map': 29.2.0 + '@jest/test-result': 29.2.1 + '@jest/transform': 29.2.1 + '@jest/types': 29.2.1 + '@types/node': 14.18.32 + chalk: 4.1.2 + cjs-module-lexer: 1.2.2 + collect-v8-coverage: 1.0.1 + glob: 7.2.3 + graceful-fs: 4.2.10 + jest-haste-map: 29.2.1 + jest-message-util: 29.2.1 + jest-mock: 29.2.1 + jest-regex-util: 29.2.0 + jest-resolve: 29.2.1 + jest-snapshot: 29.2.1 + jest-util: 29.2.1 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /jest-snapshot/29.0.3: resolution: {integrity: sha512-52q6JChm04U3deq+mkQ7R/7uy7YyfVIrebMi6ZkBoDJ85yEjm/sJwdr1P0LOIEHmpyLlXrxy3QP0Zf5J2kj0ew==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3241,6 +4126,38 @@ packages: - supports-color dev: true + /jest-snapshot/29.2.1: + resolution: {integrity: sha512-KZdLD7iEz5M4ZYd+ezZ/kk73z+DtNbk/yJ4Qx7408Vb0CCuclJIZPa/HmIwSsCfIlOBNcYTKufr7x/Yv47oYlg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.19.6 + '@babel/generator': 7.19.6 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.19.6 + '@babel/plugin-syntax-typescript': 7.18.6_@babel+core@7.19.6 + '@babel/traverse': 7.19.6 + '@babel/types': 7.19.4 + '@jest/expect-utils': 29.2.1 + '@jest/transform': 29.2.1 + '@jest/types': 29.2.1 + '@types/babel__traverse': 7.18.2 + '@types/prettier': 2.7.1 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.19.6 + chalk: 4.1.2 + expect: 29.2.1 + graceful-fs: 4.2.10 + jest-diff: 29.2.1 + jest-get-type: 29.2.0 + jest-haste-map: 29.2.1 + jest-matcher-utils: 29.2.1 + jest-message-util: 29.2.1 + jest-util: 29.2.1 + natural-compare: 1.4.0 + pretty-format: 29.2.1 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + dev: true + /jest-util/29.0.3: resolution: {integrity: sha512-Q0xaG3YRG8QiTC4R6fHjHQPaPpz9pJBEi0AeOE4mQh/FuWOijFjGXMMOfQEaU9i3z76cNR7FobZZUQnL6IyfdQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3253,6 +4170,18 @@ packages: picomatch: 2.3.1 dev: true + /jest-util/29.2.1: + resolution: {integrity: sha512-P5VWDj25r7kj7kl4pN2rG/RN2c1TLfYYYZYULnS/35nFDjBai+hBeo3MDrYZS7p6IoY3YHZnt2vq4L6mKnLk0g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.2.1 + '@types/node': 14.18.32 + chalk: 4.1.2 + ci-info: 3.5.0 + graceful-fs: 4.2.10 + picomatch: 2.3.1 + dev: true + /jest-validate/29.0.3: resolution: {integrity: sha512-OebiqqT6lK8cbMPtrSoS3aZP4juID762lZvpf1u+smZnwTEBCBInan0GAIIhv36MxGaJvmq5uJm7dl5gVt+Zrw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3265,6 +4194,18 @@ packages: pretty-format: 29.0.3 dev: true + /jest-validate/29.2.1: + resolution: {integrity: sha512-DZVX5msG6J6DL5vUUw+++6LEkXUsPwB5R7fsfM7BXdz2Ipr0Ib046ak+8egrwAR++pvSM/5laxLK977ieIGxkQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.2.1 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.2.0 + leven: 3.1.0 + pretty-format: 29.2.1 + dev: true + /jest-watcher/29.0.3: resolution: {integrity: sha512-tQX9lU91A+9tyUQKUMp0Ns8xAcdhC9fo73eqA3LFxP2bSgiF49TNcc+vf3qgGYYK9qRjFpXW9+4RgF/mbxyOOw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3279,6 +4220,20 @@ packages: string-length: 4.0.2 dev: true + /jest-watcher/29.2.1: + resolution: {integrity: sha512-7jFaHUaRq50l4w/f6RuY713bvI5XskMmjWCE54NGYcY74fLkShS8LucXJke1QfGnwDSCoIqGnGGGKPwdaBYz2Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.2.1 + '@jest/types': 29.2.1 + '@types/node': 14.18.32 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.10.2 + jest-util: 29.2.1 + string-length: 4.0.2 + dev: true + /jest-worker/29.0.3: resolution: {integrity: sha512-Tl/YWUugQOjoTYwjKdfJWkSOfhufJHO5LhXTSZC3TRoQKO+fuXnZAdoXXBlpLXKGODBL3OvdUasfDD4PcMe6ng==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3288,6 +4243,16 @@ packages: supports-color: 8.1.1 dev: true + /jest-worker/29.2.1: + resolution: {integrity: sha512-ROHTZ+oj7sBrgtv46zZ84uWky71AoYi0vEV9CdEtc1FQunsoAGe5HbQmW76nI5QWdvECVPrSi1MCVUmizSavMg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@types/node': 14.18.32 + jest-util: 29.2.1 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + /jest/29.0.3_johvxhudwcpndp4mle25vwrlq4: resolution: {integrity: sha512-ElgUtJBLgXM1E8L6K1RW1T96R897YY/3lRYqq9uVcPWtP2AAl/nQ16IYDh/FzQOOQ12VEuLdcPU83mbhG2C3PQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3308,6 +4273,26 @@ packages: - ts-node dev: true + /jest/29.2.1_4f2ldd7um3b3u4eyvetyqsphze: + resolution: {integrity: sha512-K0N+7rx+fv3Us3KhuwRSJt55MMpZPs9Q3WSO/spRZSnsalX8yEYOTQ1PiSN7OvqzoRX4JEUXCbOJRlP4n8m5LA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.2.1_ts-node@10.9.1 + '@jest/types': 29.2.1 + import-local: 3.1.0 + jest-cli: 29.2.1_4f2ldd7um3b3u4eyvetyqsphze + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + /js-tokens/4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3362,25 +4347,25 @@ packages: engines: {node: '>=6'} dev: true - /langium-cli/0.4.0: - resolution: {integrity: sha512-4Q5QX7ChLbBRRqZwxdCxvYkKTpzDc1vKx/WdfooCssW2EWKdowRkLsGUXucMPvfegnMKkhchzoElPHQM0LU6RA==} + /langium-cli/0.5.0: + resolution: {integrity: sha512-HhJOGuEyTnaaU5oE7X6OoeAWhJw6AsaZGOyNUYUukpP75/m/NvAfMBSSrbY21Os5eXaO8X+xad5lRC3ld6TBWQ==} engines: {node: '>=12.0.0'} hasBin: true dependencies: - colors: 1.4.0 + chalk: 4.1.2 commander: 7.2.0 fs-extra: 9.1.0 jsonschema: 1.4.1 - langium: 0.4.0 + langium: 0.5.0 lodash: 4.17.21 dev: true - /langium/0.4.0: - resolution: {integrity: sha512-VDwbcSdd4Uzektou73gcuqSt1eJcizHWJic7ZGIkTQyBIiRTP66YvMlxLUUE9T9p4/lCZGwf9D8CCBc7RiXZ7A==} + /langium/0.5.0: + resolution: {integrity: sha512-7mheQQtyyPm8r9R+8sPZopcdzEcHtCC+M52m8ZbotzNql8RPLs3+QbDY6EZ98ejKtJCGt9RviTkfZEt1LxX86w==} engines: {node: '>=12.0.0'} dependencies: chevrotain: 9.1.0 - vscode-languageserver: 7.0.0 + vscode-languageserver: 8.0.2 vscode-languageserver-textdocument: 1.0.7 vscode-uri: 3.0.6 @@ -3635,6 +4620,52 @@ packages: transitivePeerDependencies: - '@babel/core' - babel-plugin-macros + dev: false + + /next/12.3.1_qtpcxnaaarbm4ws7ughq6oxfve: + resolution: {integrity: sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw==} + engines: {node: '>=12.22.0'} + hasBin: true + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^6.0.0 || ^7.0.0 + react: ^17.0.2 || ^18.0.0-0 + react-dom: ^17.0.2 || ^18.0.0-0 + sass: ^1.3.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + dependencies: + '@next/env': 12.3.1 + '@swc/helpers': 0.4.11 + caniuse-lite: 1.0.30001409 + postcss: 8.4.14 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + styled-jsx: 5.0.7_otspjrsspon4ofp37rshhlhp2y + use-sync-external-store: 1.2.0_react@18.2.0 + optionalDependencies: + '@next/swc-android-arm-eabi': 12.3.1 + '@next/swc-android-arm64': 12.3.1 + '@next/swc-darwin-arm64': 12.3.1 + '@next/swc-darwin-x64': 12.3.1 + '@next/swc-freebsd-x64': 12.3.1 + '@next/swc-linux-arm-gnueabihf': 12.3.1 + '@next/swc-linux-arm64-gnu': 12.3.1 + '@next/swc-linux-arm64-musl': 12.3.1 + '@next/swc-linux-x64-gnu': 12.3.1 + '@next/swc-linux-x64-musl': 12.3.1 + '@next/swc-win32-arm64-msvc': 12.3.1 + '@next/swc-win32-ia32-msvc': 12.3.1 + '@next/swc-win32-x64-msvc': 12.3.1 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: true /no-case/3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} @@ -3880,12 +4911,17 @@ packages: find-up: 4.1.0 dev: true - /plimit-lit/1.4.0: - resolution: {integrity: sha512-e4VKIdzc5+vvTuNZeWDTWHCNPLZ1+jXeQ9HqZhAS+vlAodNr8A26IcTA9OudjLXj8fV+KRW/ZzsoyrTZzoWD/A==} + /plimit-lit/1.4.1: + resolution: {integrity: sha512-bK14ePAod0XWhXwjT6XvYfjcQ9PbCUkZXnDCAKRMZTJCaDIV9VFya1S/I+3WSbpdR8uBhCDh8TS4lQ/JQvhNFA==} dependencies: queue-lit: 1.4.0 dev: true + /pluralize/8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: false + /postcss/8.4.14: resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==} engines: {node: ^10 || ^12 || >=14} @@ -3908,13 +4944,22 @@ packages: react-is: 18.2.0 dev: true - /prisma/4.4.0: - resolution: {integrity: sha512-l/QKLmLcKJQFuc+X02LyICo0NWTUVaNNZ00jKJBqwDyhwMAhboD1FWwYV50rkH4Wls0RviAJSFzkC2ZrfawpfA==} + /pretty-format/29.2.1: + resolution: {integrity: sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.0.0 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /prisma/4.5.0: + resolution: {integrity: sha512-9Aeg4qiKlv9Wsjz4NO8k2CzRzlvS3A4FYVJ5+28sBBZ0eEwbiVOE/Jj7v6rZC1tFW2s4GSICQOAyuOjc6WsNew==} engines: {node: '>=14.17'} hasBin: true requiresBuild: true dependencies: - '@prisma/engines': 4.4.0 + '@prisma/engines': 4.5.0 dev: false /process-nextick-args/2.0.1: @@ -4085,7 +5130,7 @@ packages: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true dependencies: - is-core-module: 2.10.0 + is-core-module: 2.11.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: true @@ -4119,8 +5164,8 @@ packages: dependencies: queue-microtask: 1.2.3 - /rxjs/7.5.6: - resolution: {integrity: sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==} + /rxjs/7.5.7: + resolution: {integrity: sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==} dependencies: tslib: 2.4.0 dev: true @@ -4153,6 +5198,14 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 + dev: true + + /semver/7.3.8: + resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 /sentence-case/3.0.4: resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} @@ -4174,8 +5227,8 @@ packages: engines: {node: '>=8'} dev: true - /shell-quote/1.7.3: - resolution: {integrity: sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==} + /shell-quote/1.7.4: + resolution: {integrity: sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==} dev: true /side-channel/1.0.4: @@ -4350,6 +5403,24 @@ packages: dependencies: '@babel/core': 7.19.3 react: 18.2.0 + dev: false + + /styled-jsx/5.0.7_otspjrsspon4ofp37rshhlhp2y: + resolution: {integrity: sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + dependencies: + '@babel/core': 7.19.6 + react: 18.2.0 + dev: true /superagent/8.0.2: resolution: {integrity: sha512-QtYZ9uaNAMexI7XWl2vAXAh0j4q9H7T0WVEI/y5qaUB3QLwxo+voUgCQ217AokJzUTIVOp0RTo7fhZrwhD7A2Q==} @@ -4560,6 +5631,74 @@ packages: yargs-parser: 21.1.1 dev: true + /ts-jest/29.0.1_t3cec5bure72u77t3utxqeumoa: + resolution: {integrity: sha512-htQOHshgvhn93QLxrmxpiQPk69+M1g7govO1g6kf6GsjCv4uvRV0znVmDrrvjUrVCnTYeY4FBxTYYYD4airyJA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.19.6 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.0.3_johvxhudwcpndp4mle25vwrlq4 + jest-util: 29.0.3 + json5: 2.2.1 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.3.7 + typescript: 4.8.3 + yargs-parser: 21.1.1 + dev: true + + /ts-jest/29.0.3_hx3pnmlivexg2zscxcsqm4rf7i: + resolution: {integrity: sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.19.6 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.2.1_4f2ldd7um3b3u4eyvetyqsphze + jest-util: 29.2.1 + json5: 2.2.1 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.3.8 + typescript: 4.8.4 + yargs-parser: 21.1.1 + dev: true + /ts-morph/16.0.0: resolution: {integrity: sha512-jGNF0GVpFj0orFw55LTsQxVYEUOCWBAbR5Ls7fTYE5pQsbW18ssTb/6UXx/GYAEjS+DQTp8VoTw0vqYMiaaQuw==} dependencies: @@ -4598,6 +5737,37 @@ packages: yn: 3.1.1 dev: true + /ts-node/10.9.1_jcmx33t3olsvcxopqdljsohpme: + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.3 + '@types/node': 14.18.32 + acorn: 8.8.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.8.4 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + /ts-pattern/4.0.5: resolution: {integrity: sha512-Bq44KCEt7JVaNLa148mBCJkcQf4l7jtLEBDuDdeuLynWDA+1a60P4D0rMkqSM9mOKLQbIWUddE9h3XKyKwBeqA==} dev: true @@ -4611,7 +5781,7 @@ packages: globby: 11.1.0 mylas: 2.1.13 normalize-path: 3.0.0 - plimit-lit: 1.4.0 + plimit-lit: 1.4.1 dev: true /tsconfig-paths-jest/0.0.1: @@ -4625,14 +5795,14 @@ packages: /tslib/2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} - /tsutils/3.21.0_typescript@4.8.3: + /tsutils/3.21.0_typescript@4.8.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} 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' dependencies: tslib: 1.14.1 - typescript: 4.8.3 + typescript: 4.8.4 dev: true /type-check/0.4.0: @@ -4677,6 +5847,12 @@ packages: hasBin: true dev: true + /typescript/4.8.4: + resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + /undici/5.10.0: resolution: {integrity: sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==} engines: {node: '>=12.18'} @@ -4694,8 +5870,8 @@ packages: engines: {node: '>= 10.0.0'} dev: true - /update-browserslist-db/1.0.9_browserslist@4.21.4: - resolution: {integrity: sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==} + /update-browserslist-db/1.0.10_browserslist@4.21.4: + resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -4761,7 +5937,7 @@ packages: resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==} engines: {node: '>=10.12.0'} dependencies: - '@jridgewell/trace-mapping': 0.3.16 + '@jridgewell/trace-mapping': 0.3.17 '@types/istanbul-lib-coverage': 2.0.4 convert-source-map: 1.9.0 dev: true @@ -4773,41 +5949,36 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /vscode-jsonrpc/6.0.0: - resolution: {integrity: sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==} - engines: {node: '>=8.0.0 || >=10.0.0'} - /vscode-jsonrpc/8.0.2: resolution: {integrity: sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==} engines: {node: '>=14.0.0'} - dev: false - /vscode-languageclient/7.0.0: - resolution: {integrity: sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==} - engines: {vscode: ^1.52.0} + /vscode-languageclient/8.0.2: + resolution: {integrity: sha512-lHlthJtphG9gibGb/y72CKqQUxwPsMXijJVpHEC2bvbFqxmkj9LwQ3aGU9dwjBLqsX1S4KjShYppLvg1UJDF/Q==} + engines: {vscode: ^1.67.0} dependencies: minimatch: 3.1.2 - semver: 7.3.7 - vscode-languageserver-protocol: 3.16.0 + semver: 7.3.8 + vscode-languageserver-protocol: 3.17.2 dev: false - /vscode-languageserver-protocol/3.16.0: - resolution: {integrity: sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==} + /vscode-languageserver-protocol/3.17.2: + resolution: {integrity: sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==} dependencies: - vscode-jsonrpc: 6.0.0 - vscode-languageserver-types: 3.16.0 + vscode-jsonrpc: 8.0.2 + vscode-languageserver-types: 3.17.2 /vscode-languageserver-textdocument/1.0.7: resolution: {integrity: sha512-bFJH7UQxlXT8kKeyiyu41r22jCZXG8kuuVVA33OEJn1diWOZK5n8zBSPZFHVBOu8kXZ6h0LIRhf5UnCo61J4Hg==} - /vscode-languageserver-types/3.16.0: - resolution: {integrity: sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==} + /vscode-languageserver-types/3.17.2: + resolution: {integrity: sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==} - /vscode-languageserver/7.0.0: - resolution: {integrity: sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==} + /vscode-languageserver/8.0.2: + resolution: {integrity: sha512-bpEt2ggPxKzsAOZlXmCJ50bV7VrxwCS5BI4+egUmure/oI/t4OlFzi/YNtVvY24A2UDOZAgwFGgnZPwqSJubkA==} hasBin: true dependencies: - vscode-languageserver-protocol: 3.16.0 + vscode-languageserver-protocol: 3.17.2 /vscode-uri/3.0.6: resolution: {integrity: sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ==} @@ -4821,7 +5992,7 @@ packages: /wcwidth/1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: - defaults: 1.0.3 + defaults: 1.0.4 dev: true /webidl-conversions/3.0.1: @@ -4899,6 +6070,19 @@ packages: yargs-parser: 21.1.1 dev: true + /yargs/17.6.0: + resolution: {integrity: sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==} + engines: {node: '>=12'} + 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.8 + yargs-parser: 21.1.1 + dev: true + /yn/3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} diff --git a/samples/todo/zenstack/schema.zmodel b/samples/todo/zenstack/schema.zmodel index 0919be87a..2b0b48802 100644 --- a/samples/todo/zenstack/schema.zmodel +++ b/samples/todo/zenstack/schema.zmodel @@ -16,8 +16,8 @@ model Space { id String @id @default(uuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - name String @length(1, 100) - slug String @unique @length(1, 20) + name String + slug String @unique members SpaceUser[] lists List[] @@ -60,12 +60,12 @@ model User { id String @id @default(uuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - email String @unique @email + email String @unique emailVerified DateTime? password String? - name String? @length(1, 100) + name String? spaces SpaceUser[] - image String? @url + image String? lists List[] todos Todo[] @@ -91,7 +91,7 @@ model List { spaceId String owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade) ownerId String - title String @length(1, 20) + title String private Boolean @default(false) todos Todo[] @@ -154,7 +154,7 @@ model Session { model VerificationToken { identifier String - token String @unique + token String @id expires DateTime @@unique([identifier, token]) diff --git a/tests/integration/tests/todo.zmodel b/tests/integration/tests/todo.zmodel index 795cfee4a..2b2dd6347 100644 --- a/tests/integration/tests/todo.zmodel +++ b/tests/integration/tests/todo.zmodel @@ -11,8 +11,8 @@ model Space { id String @id @default(uuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - name String @length(1, 100) - slug String @unique @length(1, 20) + name String + slug String @unique owner User? @relation(fields: [ownerId], references: [id]) ownerId String? @@ -59,15 +59,15 @@ model User { id String @id @default(uuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - email String @unique @email + email String @unique emailVerified DateTime? password String? - name String? @length(1, 100) + name String? ownedSpaces Space[] spaces SpaceUser[] - image String? @url + image String? lists List[] todos Todo[] @@ -89,7 +89,7 @@ model List { spaceId String owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade) ownerId String - title String @length(1, 20) + title String private Boolean @default(false) todos Todo[] diff --git a/tests/integration/tests/utils.ts b/tests/integration/tests/utils.ts index 7304ec004..b9b20d760 100644 --- a/tests/integration/tests/utils.ts +++ b/tests/integration/tests/utils.ts @@ -19,10 +19,10 @@ export async function setup(schemaFile: string) { if (fs.existsSync(workDir)) { fs.rmSync(workDir, { recursive: true, force: true }); } - fs.mkdirSync(workDir); + fs.mkdirSync(path.join(workDir, 'zenstack'), { recursive: true }); process.chdir(workDir); - const targetSchema = path.join(workDir, 'schema.zmodel'); + const targetSchema = path.join(workDir, 'zenstack', 'schema.zmodel'); fs.copyFileSync(path.join(origDir, schemaFile), targetSchema); // install dependencies @@ -40,8 +40,8 @@ export async function setup(schemaFile: string) { run(`npm i ${dependencies.join(' ')}`); // code generation - run(`npx zenstack generate ./schema.zmodel`); - run(`npx prisma migrate dev --schema ./zenstack/schema.prisma -n init`); + run(`npx zenstack generate`); + run(`npx zenstack migrate dev -n init`); fs.writeFileSync( 'handler.ts',