diff --git a/package.json b/package.json index 79dd0d022..c242b4b13 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zenstack-monorepo", - "version": "1.0.0-alpha.53", + "version": "1.0.0-alpha.55", "description": "", "scripts": { "build": "pnpm -r build", diff --git a/packages/language/package.json b/packages/language/package.json index 57e3d4ecf..b9b76bbd8 100644 --- a/packages/language/package.json +++ b/packages/language/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/language", - "version": "1.0.0-alpha.53", + "version": "1.0.0-alpha.55", "displayName": "ZenStack modeling language compiler", "description": "ZenStack modeling language compiler", "homepage": "https://zenstack.dev", diff --git a/packages/next/package.json b/packages/next/package.json index dbc7dfca7..181c36b6a 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/next", - "version": "1.0.0-alpha.53", + "version": "1.0.0-alpha.55", "displayName": "ZenStack Next.js integration", "description": "ZenStack Next.js integration", "homepage": "https://zenstack.dev", diff --git a/packages/plugins/react/package.json b/packages/plugins/react/package.json index f7127dbbf..c8cdeb7b1 100644 --- a/packages/plugins/react/package.json +++ b/packages/plugins/react/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/react", "displayName": "ZenStack plugin and runtime for ReactJS", - "version": "1.0.0-alpha.53", + "version": "1.0.0-alpha.55", "description": "ZenStack plugin and runtime for ReactJS", "main": "index.js", "repository": { diff --git a/packages/plugins/trpc/package.json b/packages/plugins/trpc/package.json index 47c43c059..c92c1aeec 100644 --- a/packages/plugins/trpc/package.json +++ b/packages/plugins/trpc/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/trpc", "displayName": "ZenStack plugin for tRPC", - "version": "1.0.0-alpha.53", + "version": "1.0.0-alpha.55", "description": "ZenStack plugin for tRPC", "main": "index.js", "repository": { diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 748ce2348..87273829e 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/runtime", "displayName": "ZenStack Runtime Library", - "version": "1.0.0-alpha.53", + "version": "1.0.0-alpha.55", "description": "Runtime of ZenStack for both client-side and server-side environments.", "repository": { "type": "git", @@ -29,7 +29,6 @@ "decimal.js": "^10.4.2", "deepcopy": "^2.1.0", "superjson": "^1.11.0", - "swr": "^1.3.0", "tslib": "^2.4.1", "zod": "^3.19.1", "zod-validation-error": "^0.2.1" diff --git a/packages/schema/package.json b/packages/schema/package.json index e47e9d5f1..8f33abf9c 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -3,7 +3,7 @@ "publisher": "zenstack", "displayName": "ZenStack Language Tools", "description": "A toolkit for building secure CRUD apps with Next.js + Typescript", - "version": "1.0.0-alpha.53", + "version": "1.0.0-alpha.55", "author": { "name": "ZenStack Team" }, diff --git a/packages/schema/src/language-server/validator/utils.ts b/packages/schema/src/language-server/validator/utils.ts index a4668fb3d..a29af47e3 100644 --- a/packages/schema/src/language-server/validator/utils.ts +++ b/packages/schema/src/language-server/validator/utils.ts @@ -7,6 +7,7 @@ import { DataModelAttribute, DataModelField, DataModelFieldAttribute, + Expression, ExpressionType, isArrayExpr, isAttribute, @@ -54,10 +55,21 @@ export function getStringLiteral(node: AstNode | undefined): string | undefined } } +const isoDateTimeRegex = /^\d{4}(-\d\d(-\d\d(T\d\d:\d\d(:\d\d)?(\.\d+)?(([+-]\d\d:\d\d)|Z)?)?)?)?$/i; + /** * Determines if the given sourceType is assignable to a destination of destType */ -export function typeAssignable(destType: ExpressionType, sourceType: ExpressionType): boolean { +export function typeAssignable(destType: ExpressionType, sourceType: ExpressionType, sourceExpr?: Expression): boolean { + // implicit conversion from ISO datetime string to datetime + if (destType === 'DateTime' && sourceType === 'String' && sourceExpr && isLiteralExpr(sourceExpr)) { + const literal = getStringLiteral(sourceExpr); + if (literal && isoDateTimeRegex.test(literal)) { + // implicitly convert to DateTime + sourceType = 'DateTime'; + } + } + switch (destType) { case 'Any': return true; @@ -108,6 +120,19 @@ export function assignableToAttributeParam( let dstIsArray = param.type.array; const dstRef = param.type.reference; + // destination is field reference or transitive field reference, check if + // argument is reference or array or reference + if (dstType === 'FieldReference' || dstType === 'TransitiveFieldReference') { + 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); + } + } + if (isEnum(argResolvedType.decl)) { // enum type @@ -127,16 +152,7 @@ export function assignableToAttributeParam( return false; } - if (dstType === 'FieldReference' || dstType === 'TransitiveFieldReference') { - 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 (dstType === 'ContextType') { // attribute parameter type is ContextType, need to infer type from // the attribute's container if (isDataModelField(attr.$container)) { @@ -151,7 +167,8 @@ export function assignableToAttributeParam( } return ( - typeAssignable(dstType, argResolvedType.decl) && (dstType === 'Any' || dstIsArray === argResolvedType.array) + typeAssignable(dstType, argResolvedType.decl, arg.value) && + (dstType === 'Any' || dstIsArray === argResolvedType.array) ); } else { // reference type diff --git a/packages/schema/src/plugins/prisma/prisma-builder.ts b/packages/schema/src/plugins/prisma/prisma-builder.ts index f7edcd671..60a1d9191 100644 --- a/packages/schema/src/plugins/prisma/prisma-builder.ts +++ b/packages/schema/src/plugins/prisma/prisma-builder.ts @@ -277,7 +277,13 @@ export class FunctionCallArg { constructor(public name: string | undefined, public value: any) {} toString(): string { - return this.name ? `${this.name}: ${this.value}` : this.value; + const val = + this.value === null || this.value === undefined + ? 'null' + : typeof this.value === 'string' + ? `"${this.value}"` + : this.value.toString(); + return this.name ? `${this.name}: ${val}` : val; } } diff --git a/packages/schema/tests/generator/prisma-builder.test.ts b/packages/schema/tests/generator/prisma-builder.test.ts index 2e1341167..56f9d090b 100644 --- a/packages/schema/tests/generator/prisma-builder.test.ts +++ b/packages/schema/tests/generator/prisma-builder.test.ts @@ -7,6 +7,7 @@ import { FieldReference, FieldReferenceArg, FunctionCall, + FunctionCallArg, ModelFieldType, PrismaModel, } from '../../src/plugins/prisma/prisma-builder'; @@ -94,6 +95,17 @@ describe('Prisma Builder Tests', () => { post.addField('id', 'String', [new FieldAttribute('@id')]); post.addField('slug', 'String'); post.addField('space', 'String'); + post.addField('tsid', 'String', [ + new FieldAttribute('@default', [ + new AttributeArg( + undefined, + new AttributeArgValue( + 'FunctionCall', + new FunctionCall('dbgenerated', [new FunctionCallArg(undefined, 'timestamp_id()')]) + ) + ), + ]), + ]); post.addAttribute('@@unique', [ new AttributeArg( 'fields', diff --git a/packages/schema/tests/schema/validation/attribute-validation.test.ts b/packages/schema/tests/schema/validation/attribute-validation.test.ts index f08a70487..309d2b5ad 100644 --- a/packages/schema/tests/schema/validation/attribute-validation.test.ts +++ b/packages/schema/tests/schema/validation/attribute-validation.test.ts @@ -81,6 +81,34 @@ describe('Attribute tests', () => { } `) ).toContain(`Attribute "@default" doesn't have a parameter named "foo"`); + + expect( + await loadModelWithError(` + ${prelude} + model M { + id String @id() + dt DateTime @default('2020abc') + } + `) + ).toContain('Value is not assignable to parameter'); + + // auto-convert of string to date time + await loadModel(` + ${prelude} + model M { + id String @id() + dt DateTime @default('2000-01-01T00:00:00Z') + } + `); + + // auto-convert of string to bytes + await loadModel(` + ${prelude} + model M { + id String @id() + dt Bytes @default('abc123') + } + `); }); it('field attribute coverage', async () => { diff --git a/packages/schema/tests/schema/validation/datamodel-validation.test.ts b/packages/schema/tests/schema/validation/datamodel-validation.test.ts index 87bb49e21..fbe7ca4e9 100644 --- a/packages/schema/tests/schema/validation/datamodel-validation.test.ts +++ b/packages/schema/tests/schema/validation/datamodel-validation.test.ts @@ -370,5 +370,27 @@ describe('Data Model Validation Tests', () => { } `) ).toContain(`Could not resolve reference to ReferenceTarget named 'aId'.`); + + // enum as foreign key + await loadModel(` + ${prelude} + + enum Role { + ADMIN + USER + } + + model A { + id String @id + role Role @unique + bs B[] + } + + model B { + id String @id + a A @relation(fields: [aRole], references: [role]) + aRole Role + } + `); }); }); diff --git a/packages/sdk/package.json b/packages/sdk/package.json index d123008a9..6e70a5d88 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/sdk", - "version": "1.0.0-alpha.53", + "version": "1.0.0-alpha.55", "description": "ZenStack plugin development SDK", "main": "index.js", "scripts": { diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 6e9a9dda9..c9afdabdd 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/testtools", - "version": "1.0.0-alpha.53", + "version": "1.0.0-alpha.55", "description": "ZenStack Test Tools", "main": "index.js", "publishConfig": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b0eebf1b..dc51f8f7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -140,7 +140,6 @@ importers: deepcopy: ^2.1.0 rimraf: ^3.0.2 superjson: ^1.11.0 - swr: ^1.3.0 tslib: ^2.4.1 typescript: ^4.9.3 zod: ^3.19.1 @@ -156,7 +155,6 @@ importers: decimal.js: 10.4.2 deepcopy: 2.1.0 superjson: 1.11.0 - swr: 1.3.0_react@18.2.0 tslib: 2.4.1 zod: 3.19.1 zod-validation-error: 0.2.1_zod@3.19.1 @@ -8593,14 +8591,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - /swr/1.3.0_react@18.2.0: - resolution: {integrity: sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==} - peerDependencies: - react: ^16.11.0 || ^17.0.0 || ^18.0.0 - dependencies: - react: 18.2.0 - dev: false - /swr/2.0.3_react@18.2.0: resolution: {integrity: sha512-sGvQDok/AHEWTPfhUWXEHBVEXmgGnuahyhmRQbjl9XBYxT/MSlAzvXEKQpyM++bMPaI52vcWS2HiKNaW7+9OFw==} engines: {pnpm: '7'} diff --git a/tests/integration/test-run/package-lock.json b/tests/integration/test-run/package-lock.json index 3c7d2ee13..74c9c5c8b 100644 --- a/tests/integration/test-run/package-lock.json +++ b/tests/integration/test-run/package-lock.json @@ -126,7 +126,7 @@ }, "../../../packages/runtime/dist": { "name": "@zenstackhq/runtime", - "version": "1.0.0-alpha.53", + "version": "1.0.0-alpha.55", "license": "MIT", "dependencies": { "@types/bcryptjs": "^2.4.2", @@ -138,7 +138,6 @@ "decimal.js": "^10.4.2", "deepcopy": "^2.1.0", "superjson": "^1.11.0", - "swr": "^1.3.0", "tslib": "^2.4.1", "zod": "^3.19.1", "zod-validation-error": "^0.2.1" @@ -157,7 +156,7 @@ }, "../../../packages/schema/dist": { "name": "zenstack", - "version": "1.0.0-alpha.53", + "version": "1.0.8", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -355,7 +354,6 @@ "deepcopy": "^2.1.0", "rimraf": "^3.0.2", "superjson": "^1.11.0", - "swr": "^1.3.0", "tslib": "^2.4.1", "typescript": "^4.9.3", "zod": "^3.19.1",