From dea286b1e29dcc4b3a50e880a07295c7708c4d40 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:24:30 +0800 Subject: [PATCH 1/3] chore: update zod, add several helpers, CLI loads config from pkg.json --- packages/cli/src/actions/action-utils.ts | 32 +++++++++++++++++ packages/cli/src/actions/generate.ts | 16 +++++++-- packages/common-helpers/src/find-up.ts | 34 +++++++++++++++++++ packages/common-helpers/src/index.ts | 1 + packages/runtime/package.json | 16 +++++++-- packages/runtime/src/client/contract.ts | 8 +++++ packages/runtime/src/client/crud/validator.ts | 2 +- packages/runtime/src/helpers.ts | 1 + packages/runtime/tsup.config.ts | 1 + packages/zod/src/index.ts | 2 +- packages/zod/src/types.ts | 2 +- pnpm-lock.yaml | 23 ++++--------- pnpm-workspace.yaml | 2 +- 13 files changed, 115 insertions(+), 25 deletions(-) create mode 100644 packages/common-helpers/src/find-up.ts create mode 100644 packages/runtime/src/helpers.ts diff --git a/packages/cli/src/actions/action-utils.ts b/packages/cli/src/actions/action-utils.ts index 9f36d53c..2712e9cf 100644 --- a/packages/cli/src/actions/action-utils.ts +++ b/packages/cli/src/actions/action-utils.ts @@ -1,3 +1,4 @@ +import { findUp } from '@zenstackhq/common-helpers'; import { loadDocument } from '@zenstackhq/language'; import { PrismaSchemaGenerator } from '@zenstackhq/sdk'; import colors from 'colors'; @@ -13,6 +14,14 @@ export function getSchemaFile(file?: string) { return file; } + const pkgJsonConfig = getPkgJsonConfig(process.cwd()); + if (pkgJsonConfig.schema) { + if (!fs.existsSync(pkgJsonConfig.schema)) { + throw new CliError(`Schema file not found: ${pkgJsonConfig.schema}`); + } + return pkgJsonConfig.schema; + } + if (fs.existsSync('./zenstack/schema.zmodel')) { return './zenstack/schema.zmodel'; } else if (fs.existsSync('./schema.zmodel')) { @@ -51,3 +60,26 @@ export async function generateTempPrismaSchema(zmodelPath: string) { fs.writeFileSync(prismaSchemaFile, prismaSchema); return prismaSchemaFile; } + +export function getPkgJsonConfig(startPath: string) { + const result: { schema: string | undefined; output: string | undefined } = { schema: undefined, output: undefined }; + const pkgJsonFile = findUp(['package.json'], startPath, false); + + if (!pkgJsonFile) { + return result; + } + + let pkgJson: any = undefined; + try { + pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, 'utf8')); + } catch { + return result; + } + + if (pkgJson.zenstack && typeof pkgJson.zenstack === 'object') { + result.schema = pkgJson.zenstack.schema && path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.schema); + result.output = pkgJson.zenstack.output && path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.output); + } + + return result; +} diff --git a/packages/cli/src/actions/generate.ts b/packages/cli/src/actions/generate.ts index 269f837a..3355d462 100644 --- a/packages/cli/src/actions/generate.ts +++ b/packages/cli/src/actions/generate.ts @@ -4,7 +4,7 @@ import { PrismaSchemaGenerator, TsSchemaGenerator, type CliGenerator } from '@ze import colors from 'colors'; import fs from 'node:fs'; import path from 'node:path'; -import { getSchemaFile, loadSchemaDocument } from './action-utils'; +import { getPkgJsonConfig, getSchemaFile, loadSchemaDocument } from './action-utils'; type Options = { schema?: string; @@ -20,7 +20,7 @@ export async function run(options: Options) { const schemaFile = getSchemaFile(options.schema); const model = await loadSchemaDocument(schemaFile); - const outputPath = options.output ?? path.dirname(schemaFile); + const outputPath = getOutputPath(options, schemaFile); // generate TS schema const tsSchemaFile = path.join(outputPath, 'schema.ts'); @@ -55,6 +55,18 @@ const client = new ZenStackClient(schema, { } } +function getOutputPath(options: Options, schemaFile: string) { + if (options.output) { + return options.output; + } + const pkgJsonConfig = getPkgJsonConfig(process.cwd()); + if (pkgJsonConfig.output) { + return pkgJsonConfig.output; + } else { + return path.dirname(schemaFile); + } +} + async function runPlugins(model: Model, outputPath: string, tsSchemaFile: string) { const plugins = model.declarations.filter(isPlugin); for (const plugin of plugins) { diff --git a/packages/common-helpers/src/find-up.ts b/packages/common-helpers/src/find-up.ts new file mode 100644 index 00000000..fc54cda4 --- /dev/null +++ b/packages/common-helpers/src/find-up.ts @@ -0,0 +1,34 @@ +import fs from 'fs'; +import path from 'path'; + +/** + * A type named FindUp that takes a type parameter e which extends boolean. + */ +export type FindUpResult = Multiple extends true ? string[] | undefined : string | undefined; + +/** + * Find and return file paths by searching parent directories based on the given names list and current working directory (cwd) path. + * Optionally return a single path or multiple paths. + * If multiple allowed, return all paths found. + * If no paths are found, return undefined. + * + * @param names An array of strings representing names to search for within the directory + * @param cwd A string representing the current working directory + * @param multiple A boolean flag indicating whether to search for multiple levels. Useful for finding node_modules directories... + * @param An array of strings representing the accumulated results used in multiple results + * @returns Path(s) to a specific file or folder within the directory or parent directories + */ +export function findUp( + names: string[], + cwd: string = process.cwd(), + multiple: Multiple = false as Multiple, + result: string[] = [], +): FindUpResult { + if (!names.some((name) => !!name)) return undefined; + const target = names.find((name) => fs.existsSync(path.join(cwd, name))); + if (multiple == false && target) return path.join(cwd, target) as FindUpResult; + if (target) result.push(path.join(cwd, target)); + const up = path.resolve(cwd, '..'); + if (up === cwd) return (multiple && result.length > 0 ? result : undefined) as FindUpResult; // it'll fail anyway + return findUp(names, up, multiple, result); +} diff --git a/packages/common-helpers/src/index.ts b/packages/common-helpers/src/index.ts index 7f9c421b..3b0d2d3c 100644 --- a/packages/common-helpers/src/index.ts +++ b/packages/common-helpers/src/index.ts @@ -1,3 +1,4 @@ +export * from './find-up'; export * from './is-plain-object'; export * from './lower-case-first'; export * from './param-case'; diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 5d02759c..c5a6f035 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -39,6 +39,16 @@ "default": "./dist/schema.cjs" } }, + "./helpers": { + "import": { + "types": "./dist/helpers.d.ts", + "default": "./dist/helpers.js" + }, + "require": { + "types": "./dist/helpers.d.cts", + "default": "./dist/helpers.cjs" + } + }, "./plugins/policy": { "import": { "types": "./dist/plugins/policy.d.ts", @@ -63,12 +73,12 @@ "nanoid": "^5.0.9", "ts-pattern": "catalog:", "ulid": "^3.0.0", - "uuid": "^11.0.5" + "uuid": "^11.0.5", + "zod": "catalog:" }, "peerDependencies": { "better-sqlite3": "^11.8.1", - "pg": "^8.13.1", - "zod": "catalog:" + "pg": "^8.13.1" }, "peerDependenciesMeta": { "better-sqlite3": { diff --git a/packages/runtime/src/client/contract.ts b/packages/runtime/src/client/contract.ts index ce58c5d0..531f3fa7 100644 --- a/packages/runtime/src/client/contract.ts +++ b/packages/runtime/src/client/contract.ts @@ -158,6 +158,14 @@ export type ClientContract = { [Key in GetModels as Uncapitalize]: ModelOperations; } & Procedures; +/** + * The contract for a client in a transaction. + */ +export type TransactionClientContract = Omit< + ClientContract, + TransactionUnsupportedMethods +>; + type _TypeMap = { String: string; Int: number; diff --git a/packages/runtime/src/client/crud/validator.ts b/packages/runtime/src/client/crud/validator.ts index d8eea71e..7d1aed7d 100644 --- a/packages/runtime/src/client/crud/validator.ts +++ b/packages/runtime/src/client/crud/validator.ts @@ -2,7 +2,7 @@ import { invariant } from '@zenstackhq/common-helpers'; import Decimal from 'decimal.js'; import stableStringify from 'json-stable-stringify'; import { match, P } from 'ts-pattern'; -import { z, ZodType } from 'zod/v4'; +import { z, ZodType } from 'zod'; import type { BuiltinType, EnumDef, FieldDef, GetModels, SchemaDef } from '../../schema'; import { NUMERIC_FIELD_TYPES } from '../constants'; import { diff --git a/packages/runtime/src/helpers.ts b/packages/runtime/src/helpers.ts new file mode 100644 index 00000000..a58d0d1c --- /dev/null +++ b/packages/runtime/src/helpers.ts @@ -0,0 +1 @@ +export { sql } from 'kysely'; diff --git a/packages/runtime/tsup.config.ts b/packages/runtime/tsup.config.ts index 0f8a9d6f..9278c545 100644 --- a/packages/runtime/tsup.config.ts +++ b/packages/runtime/tsup.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ entry: { index: 'src/index.ts', schema: 'src/schema/index.ts', + helpers: 'src/helpers.ts', 'plugins/policy': 'src/plugins/policy/index.ts', }, outDir: 'dist', diff --git a/packages/zod/src/index.ts b/packages/zod/src/index.ts index 4ca7ade0..a8180b18 100644 --- a/packages/zod/src/index.ts +++ b/packages/zod/src/index.ts @@ -1,6 +1,6 @@ import type { FieldDef, GetModels, SchemaDef } from '@zenstackhq/runtime/schema'; import { match, P } from 'ts-pattern'; -import { z, ZodType } from 'zod/v4'; +import { z, ZodType } from 'zod'; import type { SelectSchema } from './types'; export function makeSelectSchema>( diff --git a/packages/zod/src/types.ts b/packages/zod/src/types.ts index 389178e7..878aef87 100644 --- a/packages/zod/src/types.ts +++ b/packages/zod/src/types.ts @@ -1,5 +1,5 @@ import type { FieldType, GetModels, ScalarFields, SchemaDef } from '@zenstackhq/runtime/schema'; -import type { ZodBoolean, ZodNumber, ZodObject, ZodString, ZodUnknown } from 'zod/v4'; +import type { ZodBoolean, ZodNumber, ZodObject, ZodString, ZodUnknown } from 'zod'; export type SelectSchema> = ZodObject<{ [Key in ScalarFields]: MapScalarType; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0be9341..36af9eb9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,8 +25,8 @@ catalogs: specifier: ^5.0.0 version: 5.8.3 zod: - specifier: ^3.25.67 - version: 3.25.67 + specifier: ^4.0.0 + version: 4.0.5 importers: @@ -127,15 +127,6 @@ importers: specifier: ^0.2.3 version: 0.2.3 - packages/cli/test: - devDependencies: - '@types/tmp': - specifier: ^0.2.6 - version: 0.2.6 - tmp: - specifier: ^0.2.3 - version: 0.2.3 - packages/common-helpers: devDependencies: '@zenstackhq/eslint-config': @@ -253,7 +244,7 @@ importers: version: 11.0.5 zod: specifier: 'catalog:' - version: 3.25.67 + version: 4.0.5 devDependencies: '@types/better-sqlite3': specifier: ^7.0.0 @@ -379,7 +370,7 @@ importers: version: 5.7.1 zod: specifier: 'catalog:' - version: 3.25.67 + version: 4.0.5 devDependencies: '@zenstackhq/eslint-config': specifier: workspace:* @@ -2518,8 +2509,8 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zod@3.25.67: - resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} + zod@4.0.5: + resolution: {integrity: sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==} snapshots: @@ -4481,4 +4472,4 @@ snapshots: yocto-queue@0.1.0: {} - zod@3.25.67: {} + zod@4.0.5: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 50ebbc00..fcdfd392 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,7 +5,7 @@ packages: - tests/** catalog: kysely: ^0.27.6 - zod: ^3.25.67 + zod: ^4.0.0 prisma: ^6.0.0 langium: 3.5.0 langium-cli: 3.5.0 From 95baf0e19257b07fe388dc9c91d28e356fa3d032 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:30:52 +0800 Subject: [PATCH 2/3] add test --- packages/cli/test/generate.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/cli/test/generate.test.ts b/packages/cli/test/generate.test.ts index 4372b869..ad9e0497 100644 --- a/packages/cli/test/generate.test.ts +++ b/packages/cli/test/generate.test.ts @@ -41,4 +41,19 @@ describe('CLI generate command test', () => { runCli('generate --save-prisma-schema "../prisma/schema.prisma"', workDir); expect(fs.existsSync(path.join(workDir, 'prisma/schema.prisma'))).toBe(true); }); + + it('should respect package.json config', () => { + const workDir = createProject(model); + fs.mkdirSync(path.join(workDir, 'foo')); + fs.renameSync(path.join(workDir, 'zenstack/schema.zmodel'), path.join(workDir, 'foo/schema.zmodel')); + fs.rmdirSync(path.join(workDir, 'zenstack')); + const pkgJson = JSON.parse(fs.readFileSync(path.join(workDir, 'package.json'), 'utf8')); + pkgJson.zenstack = { + schema: './foo/schema.zmodel', + output: './bar', + }; + fs.writeFileSync(path.join(workDir, 'package.json'), JSON.stringify(pkgJson, null, 2)); + runCli('generate', workDir); + expect(fs.existsSync(path.join(workDir, 'bar/schema.ts'))).toBe(true); + }); }); From 49b651fff8b906de7fb1f59677232d1b7437405f Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:38:05 +0800 Subject: [PATCH 3/3] addressing comments --- packages/common-helpers/src/find-up.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common-helpers/src/find-up.ts b/packages/common-helpers/src/find-up.ts index fc54cda4..afb2fed9 100644 --- a/packages/common-helpers/src/find-up.ts +++ b/packages/common-helpers/src/find-up.ts @@ -26,7 +26,7 @@ export function findUp( ): FindUpResult { if (!names.some((name) => !!name)) return undefined; const target = names.find((name) => fs.existsSync(path.join(cwd, name))); - if (multiple == false && target) return path.join(cwd, target) as FindUpResult; + if (multiple === false && target) return path.join(cwd, target) as FindUpResult; if (target) result.push(path.join(cwd, target)); const up = path.resolve(cwd, '..'); if (up === cwd) return (multiple && result.length > 0 ? result : undefined) as FindUpResult; // it'll fail anyway