diff --git a/packages/schema/package.json b/packages/schema/package.json index 0de5ea6a2..41f993a68 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -59,13 +59,13 @@ "configuration": { "title": "ZenStack", "properties": { - "zmodel.format.usePrismaStyle": { - "type": "boolean", - "default": true, - "description": "Use Prisma style indentation." - } + "zmodel.format.usePrismaStyle": { + "type": "boolean", + "default": true, + "description": "Use Prisma style indentation." + } } - } + } }, "activationEvents": [ "onLanguage:zmodel" @@ -90,6 +90,7 @@ }, "dependencies": { "@paralleldrive/cuid2": "^2.2.0", + "@types/node": "^20.12.7", "@zenstackhq/language": "workspace:*", "@zenstackhq/sdk": "workspace:*", "async-exit-hook": "^2.0.1", diff --git a/packages/schema/src/plugins/zod/transformer.ts b/packages/schema/src/plugins/zod/transformer.ts index f4c8a167d..86829f1ca 100644 --- a/packages/schema/src/plugins/zod/transformer.ts +++ b/packages/schema/src/plugins/zod/transformer.ts @@ -117,6 +117,8 @@ export default class Transformer { return result; } + // TODO: unify the following with `schema-gen.ts` + if (inputType.type === 'String') { result.push(this.wrapWithZodValidators('z.string()', field, inputType)); } else if (inputType.type === 'Int' || inputType.type === 'Float') { @@ -131,7 +133,13 @@ export default class Transformer { } else if (inputType.type === 'DateTime') { result.push(this.wrapWithZodValidators(['z.date()', 'z.string().datetime()'], field, inputType)); } else if (inputType.type === 'Bytes') { - result.push(this.wrapWithZodValidators(`z.instanceof(Uint8Array)`, field, inputType)); + result.push( + this.wrapWithZodValidators( + `z.custom(data => data instanceof Uint8Array)`, + field, + inputType + ) + ); } else if (inputType.type === 'Json') { this.hasJson = true; result.push(this.wrapWithZodValidators('jsonSchema', field, inputType)); diff --git a/packages/schema/src/plugins/zod/utils/schema-gen.ts b/packages/schema/src/plugins/zod/utils/schema-gen.ts index 9f79a3c66..e6a335221 100644 --- a/packages/schema/src/plugins/zod/utils/schema-gen.ts +++ b/packages/schema/src/plugins/zod/utils/schema-gen.ts @@ -186,7 +186,7 @@ function makeZodSchema(field: DataModelField) { schema = 'z.coerce.date()'; break; case 'Bytes': - schema = 'z.union([z.string(), z.instanceof(Uint8Array)])'; + schema = 'z.union([z.string(), z.custom(data => data instanceof Uint8Array)])'; break; default: schema = 'z.any()'; diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index 76b2d83da..7249e6c4a 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -101,12 +101,12 @@ generator js { plugin enhancer { provider = '@core/enhancer' - preserveTsFiles = true + ${options.preserveTsFiles ? 'preserveTsFiles = true' : ''} } plugin zod { provider = '@core/zod' - // preserveTsFiles = true + ${options.preserveTsFiles ? 'preserveTsFiles = true' : ''} modelOnly = ${!options.fullZod} } `; @@ -130,6 +130,7 @@ export type SchemaLoadOptions = { enhanceOptions?: Partial; extraSourceFiles?: { name: string; content: string }[]; projectDir?: string; + preserveTsFiles?: boolean; }; const defaultOptions: SchemaLoadOptions = { @@ -140,6 +141,7 @@ const defaultOptions: SchemaLoadOptions = { compile: false, logPrismaQuery: false, provider: 'sqlite', + preserveTsFiles: false, }; export async function loadSchemaFromFile(schemaFile: string, options?: SchemaLoadOptions) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a86c803b7..8af4e228d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -465,6 +465,9 @@ importers: '@paralleldrive/cuid2': specifier: ^2.2.0 version: 2.2.0 + '@types/node': + specifier: ^20.12.7 + version: 20.12.7 '@zenstackhq/language': specifier: workspace:* version: link:../language/dist @@ -4715,7 +4718,7 @@ packages: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 18.0.0 + '@types/node': 20.10.2 dev: true /@types/chai-subset@1.3.3: @@ -4768,7 +4771,7 @@ packages: resolution: {integrity: sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==} dependencies: '@types/jsonfile': 6.1.1 - '@types/node': 18.0.0 + '@types/node': 20.10.2 dev: true /@types/graceful-fs@4.1.6: @@ -4860,6 +4863,7 @@ packages: /@types/node@18.0.0: resolution: {integrity: sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==} + dev: false /@types/node@20.10.2: resolution: {integrity: sha512-37MXfxkb0vuIlRKHNxwCkb60PNBpR94u4efQuN4JgIAm66zfCDXGSAFCef9XUWFovX2R1ok6Z7MHhtdVXXkkIw==} @@ -4867,6 +4871,11 @@ packages: undici-types: 5.26.5 dev: true + /@types/node@20.12.7: + resolution: {integrity: sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==} + dependencies: + undici-types: 5.26.5 + /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true @@ -4874,7 +4883,7 @@ packages: /@types/pg@8.10.2: resolution: {integrity: sha512-MKFs9P6nJ+LAeHLU3V0cODEOgyThJ3OAnmOlsZsxux6sfQs3HRXR5bBn7xG5DjckEFhTAxsXi7k7cd0pCMxpJw==} dependencies: - '@types/node': 18.0.0 + '@types/node': 20.10.2 pg-protocol: 1.6.0 pg-types: 4.0.1 dev: true @@ -9766,7 +9775,7 @@ packages: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 18.0.0 + '@types/node': 20.10.2 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -14418,7 +14427,6 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: true /undici@5.22.1: resolution: {integrity: sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==} @@ -14729,7 +14737,7 @@ packages: engines: {node: '>= 0.8'} dev: true - /vite-node@0.29.7(@types/node@18.0.0): + /vite-node@0.29.7(@types/node@20.12.7): resolution: {integrity: sha512-PakCZLvz37yFfUPWBnLa1OYHPCGm5v4pmRrTcFN4V/N/T3I6tyP3z07S//9w+DdeL7vVd0VSeyMZuAh+449ZWw==} engines: {node: '>=v14.16.0'} hasBin: true @@ -14739,7 +14747,7 @@ packages: mlly: 1.4.0 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.3.9(@types/node@18.0.0) + vite: 4.3.9(@types/node@20.12.7) transitivePeerDependencies: - '@types/node' - less @@ -14825,7 +14833,7 @@ packages: vscode-uri: 3.0.7 dev: true - /vite@4.3.9(@types/node@18.0.0): + /vite@4.3.9(@types/node@20.12.7): resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -14850,7 +14858,7 @@ packages: terser: optional: true dependencies: - '@types/node': 18.0.0 + '@types/node': 20.12.7 esbuild: 0.17.19 postcss: 8.4.24 rollup: 3.25.3 @@ -14935,7 +14943,7 @@ packages: dependencies: '@types/chai': 4.3.5 '@types/chai-subset': 1.3.3 - '@types/node': 18.0.0 + '@types/node': 20.12.7 '@vitest/expect': 0.29.7 '@vitest/runner': 0.29.7 '@vitest/spy': 0.29.7 @@ -14954,8 +14962,8 @@ packages: tinybench: 2.5.0 tinypool: 0.4.0 tinyspy: 1.1.1 - vite: 4.3.9(@types/node@18.0.0) - vite-node: 0.29.7(@types/node@18.0.0) + vite: 4.3.9(@types/node@20.12.7) + vite-node: 0.29.7(@types/node@20.12.7) why-is-node-running: 2.2.2 transitivePeerDependencies: - less diff --git a/script/test-scaffold.ts b/script/test-scaffold.ts index ddf3c999a..4c7a81b51 100644 --- a/script/test-scaffold.ts +++ b/script/test-scaffold.ts @@ -19,6 +19,6 @@ function run(cmd: string) { } run('npm init -y'); -run('npm i --no-audit --no-fund typescript prisma @prisma/client zod decimal.js'); +run('npm i --no-audit --no-fund typescript prisma @prisma/client zod decimal.js @types/node'); console.log('Test scaffold setup complete.'); diff --git a/tests/regression/tests/issue-1268.test.ts b/tests/regression/tests/issue-1268.test.ts new file mode 100644 index 000000000..b51d954f7 --- /dev/null +++ b/tests/regression/tests/issue-1268.test.ts @@ -0,0 +1,32 @@ +import { loadSchema } from '@zenstackhq/testtools'; + +describe('issue 1268', () => { + it('regression', async () => { + const { zodSchemas } = await loadSchema( + ` + model Model { + id String @id @default(uuid()) + bytes Bytes + } + `, + { + fullZod: true, + pushDb: false, + compile: true, + extraSourceFiles: [ + { + name: 'test.ts', + content: ` +import { ModelCreateInputObjectSchema } from '.zenstack/zod/objects'; +ModelCreateInputObjectSchema.parse({ bytes: new Uint8Array(0) }); + `, + }, + ], + } + ); + + expect( + zodSchemas.objects.ModelCreateInputObjectSchema.safeParse({ bytes: new Uint8Array(0) }).success + ).toBeTruthy(); + }); +});