From b2ee223d39f8c6dbd98d22364eb90ac824f187bd Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Thu, 14 Aug 2025 21:58:59 +0800 Subject: [PATCH 1/4] chore: disable query context injection (#170) --- packages/runtime/src/client/crud/operations/base.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/runtime/src/client/crud/operations/base.ts b/packages/runtime/src/client/crud/operations/base.ts index 237b3157..24874d69 100644 --- a/packages/runtime/src/client/crud/operations/base.ts +++ b/packages/runtime/src/client/crud/operations/base.ts @@ -23,7 +23,7 @@ import { ExpressionUtils, type GetModels, type ModelDef, type SchemaDef } from ' import { clone } from '../../../utils/clone'; import { enumerate } from '../../../utils/enumerate'; import { extractFields, fieldsToSelectObject } from '../../../utils/object-utils'; -import { CONTEXT_COMMENT_PREFIX, NUMERIC_FIELD_TYPES } from '../../constants'; +import { NUMERIC_FIELD_TYPES } from '../../constants'; import type { CRUD } from '../../contract'; import type { FindArgs, SelectIncludeOmit, SortOrder, WhereInput } from '../../crud-types'; import { InternalError, NotFoundError, QueryError } from '../../errors'; @@ -1303,8 +1303,9 @@ export abstract class BaseOperationHandler { return NUMERIC_FIELD_TYPES.includes(fieldDef.type) && !fieldDef.array; } - private makeContextComment(context: { model: GetModels; operation: CRUD }) { - return sql.raw(`${CONTEXT_COMMENT_PREFIX}${JSON.stringify(context)}`); + private makeContextComment(_context: { model: GetModels; operation: CRUD }) { + return sql``; + // return sql.raw(`${CONTEXT_COMMENT_PREFIX}${JSON.stringify(context)}`); } protected async updateMany< From edb0255bc0794f8e662be6b123a0f923598dd722 Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Fri, 15 Aug 2025 16:08:53 +0800 Subject: [PATCH 2/4] fix: error when selecting relation that contains computed fields (#172) * fix: error when selecting relation that contains computed fields * update --- packages/cli/package.json | 2 +- packages/cli/tsconfig.json | 2 +- packages/common-helpers/package.json | 2 +- packages/common-helpers/tsconfig.json | 2 +- packages/create-zenstack/package.json | 2 +- packages/create-zenstack/tsconfig.json | 2 +- packages/dialects/sql.js/package.json | 2 +- packages/dialects/sql.js/tsconfig.json | 4 +- packages/ide/vscode/tsconfig.json | 2 +- packages/language/package.json | 2 +- .../src/validators/typedef-validator.ts | 4 +- packages/language/tsconfig.json | 2 +- packages/runtime/package.json | 2 +- .../runtime/src/client/crud/dialects/base.ts | 10 +- .../src/client/crud/dialects/postgresql.ts | 6 +- .../src/client/crud/dialects/sqlite.ts | 7 +- packages/runtime/src/client/query-utils.ts | 4 + .../test/client-api/computed-fields.test.ts | 284 +++++++++++------- packages/runtime/tsconfig.build.json | 7 + packages/runtime/tsconfig.json | 2 +- packages/sdk/package.json | 2 +- packages/sdk/tsconfig.json | 2 +- packages/tanstack-query/package.json | 2 +- packages/tanstack-query/tsconfig.json | 2 +- packages/testtools/package.json | 2 +- packages/testtools/tsconfig.json | 2 +- packages/zod/package.json | 2 +- packages/zod/tsconfig.json | 2 +- 28 files changed, 223 insertions(+), 143 deletions(-) create mode 100644 packages/runtime/tsconfig.build.json diff --git a/packages/cli/package.json b/packages/cli/package.json index ecb796a7..3f38017e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -22,7 +22,7 @@ "zenstack": "bin/cli" }, "scripts": { - "build": "tsup-node", + "build": "tsc --noEmit && tsup-node", "watch": "tsup-node --watch", "lint": "eslint src --ext ts", "test": "vitest run", diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 7b9efb7a..bd22b363 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { - "outDir": "dist" + "baseUrl": "." }, "include": ["src/**/*.ts"] } diff --git a/packages/common-helpers/package.json b/packages/common-helpers/package.json index bb2bbb72..b3eb5964 100644 --- a/packages/common-helpers/package.json +++ b/packages/common-helpers/package.json @@ -4,7 +4,7 @@ "description": "ZenStack Common Helpers", "type": "module", "scripts": { - "build": "tsup-node", + "build": "tsc --noEmit && tsup-node", "watch": "tsup-node --watch", "lint": "eslint src --ext ts", "pack": "pnpm pack" diff --git a/packages/common-helpers/tsconfig.json b/packages/common-helpers/tsconfig.json index 7b9efb7a..bd22b363 100644 --- a/packages/common-helpers/tsconfig.json +++ b/packages/common-helpers/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { - "outDir": "dist" + "baseUrl": "." }, "include": ["src/**/*.ts"] } diff --git a/packages/create-zenstack/package.json b/packages/create-zenstack/package.json index bba1dd12..5c364042 100644 --- a/packages/create-zenstack/package.json +++ b/packages/create-zenstack/package.json @@ -4,7 +4,7 @@ "description": "Create a new ZenStack project", "type": "module", "scripts": { - "build": "tsup-node", + "build": "tsc --noEmit && tsup-node", "lint": "eslint src --ext ts", "pack": "pnpm pack" }, diff --git a/packages/create-zenstack/tsconfig.json b/packages/create-zenstack/tsconfig.json index 7b9efb7a..bd22b363 100644 --- a/packages/create-zenstack/tsconfig.json +++ b/packages/create-zenstack/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { - "outDir": "dist" + "baseUrl": "." }, "include": ["src/**/*.ts"] } diff --git a/packages/dialects/sql.js/package.json b/packages/dialects/sql.js/package.json index 7302a814..1a722f72 100644 --- a/packages/dialects/sql.js/package.json +++ b/packages/dialects/sql.js/package.json @@ -4,7 +4,7 @@ "description": "Kysely dialect for sql.js", "type": "module", "scripts": { - "build": "tsup-node", + "build": "tsc --noEmit && tsup-node", "watch": "tsup-node --watch", "lint": "eslint src --ext ts", "pack": "pnpm pack" diff --git a/packages/dialects/sql.js/tsconfig.json b/packages/dialects/sql.js/tsconfig.json index 2125902f..7b457d06 100644 --- a/packages/dialects/sql.js/tsconfig.json +++ b/packages/dialects/sql.js/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { - "outDir": "dist" + "baseUrl": "." }, - "include": ["src/**/*", "test/**/*"] + "include": ["src/**/*"] } diff --git a/packages/ide/vscode/tsconfig.json b/packages/ide/vscode/tsconfig.json index 7b9efb7a..bd22b363 100644 --- a/packages/ide/vscode/tsconfig.json +++ b/packages/ide/vscode/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { - "outDir": "dist" + "baseUrl": "." }, "include": ["src/**/*.ts"] } diff --git a/packages/language/package.json b/packages/language/package.json index e744294d..2a1fd6fb 100644 --- a/packages/language/package.json +++ b/packages/language/package.json @@ -10,7 +10,7 @@ ], "type": "module", "scripts": { - "build": "pnpm langium:generate && tsup-node", + "build": "pnpm langium:generate && tsc --noEmit && tsup-node", "lint": "eslint src --ext ts", "langium:generate": "langium generate", "langium:generate:production": "langium generate --mode=production", diff --git a/packages/language/src/validators/typedef-validator.ts b/packages/language/src/validators/typedef-validator.ts index 259608e5..d029d8ba 100644 --- a/packages/language/src/validators/typedef-validator.ts +++ b/packages/language/src/validators/typedef-validator.ts @@ -1,5 +1,5 @@ import type { ValidationAcceptor } from 'langium'; -import type { TypeDef, TypeDefField } from '../generated/ast'; +import type { DataField, TypeDef } from '../generated/ast'; import { validateAttributeApplication } from './attribute-application-validator'; import { validateDuplicatedDeclarations, type AstValidator } from './common'; @@ -21,7 +21,7 @@ export default class TypeDefValidator implements AstValidator { typeDef.fields.forEach((field) => this.validateField(field, accept)); } - private validateField(field: TypeDefField, accept: ValidationAcceptor): void { + private validateField(field: DataField, accept: ValidationAcceptor): void { field.attributes.forEach((attr) => validateAttributeApplication(attr, accept)); } } diff --git a/packages/language/tsconfig.json b/packages/language/tsconfig.json index 7b9efb7a..bd22b363 100644 --- a/packages/language/tsconfig.json +++ b/packages/language/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { - "outDir": "dist" + "baseUrl": "." }, "include": ["src/**/*.ts"] } diff --git a/packages/runtime/package.json b/packages/runtime/package.json index a24e2ec7..5648e106 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -4,7 +4,7 @@ "description": "ZenStack Runtime", "type": "module", "scripts": { - "build": "tsup-node && pnpm test:generate", + "build": "tsc --project tsconfig.build.json --noEmit && tsup-node && pnpm test:generate", "watch": "tsup-node --watch", "lint": "eslint src --ext ts", "test": "vitest run && pnpm test:typecheck", diff --git a/packages/runtime/src/client/crud/dialects/base.ts b/packages/runtime/src/client/crud/dialects/base.ts index e3d6e3d0..2c1738cd 100644 --- a/packages/runtime/src/client/crud/dialects/base.ts +++ b/packages/runtime/src/client/crud/dialects/base.ts @@ -993,8 +993,14 @@ export abstract class BaseCrudDialect { return eb.not(this.and(eb, ...args)); } - fieldRef(model: string, field: string, eb: ExpressionBuilder, modelAlias?: string) { - return buildFieldRef(this.schema, model, field, this.options, eb, modelAlias); + fieldRef( + model: string, + field: string, + eb: ExpressionBuilder, + modelAlias?: string, + inlineComputedField = true, + ) { + return buildFieldRef(this.schema, model, field, this.options, eb, modelAlias, inlineComputedField); } // #endregion diff --git a/packages/runtime/src/client/crud/dialects/postgresql.ts b/packages/runtime/src/client/crud/dialects/postgresql.ts index 65ff3988..08d07950 100644 --- a/packages/runtime/src/client/crud/dialects/postgresql.ts +++ b/packages/runtime/src/client/crud/dialects/postgresql.ts @@ -226,13 +226,13 @@ export class PostgresCrudDialect extends BaseCrudDiale ...Object.entries(relationModelDef.fields) .filter(([, value]) => !value.relation) .filter(([name]) => !(typeof payload === 'object' && (payload.omit as any)?.[name] === true)) - .map(([field]) => [sql.lit(field), this.fieldRef(relationModel, field, eb)]) + .map(([field]) => [sql.lit(field), this.fieldRef(relationModel, field, eb, undefined, false)]) .flatMap((v) => v), ); } else if (payload.select) { // select specific fields objArgs.push( - ...Object.entries(payload.select) + ...Object.entries(payload.select) .filter(([, value]) => value) .map(([field, value]) => { if (field === '_count') { @@ -249,7 +249,7 @@ export class PostgresCrudDialect extends BaseCrudDiale ? // reference the synthesized JSON field eb.ref(`${parentAlias}$${relationField}$${field}.$j`) : // reference a plain field - this.fieldRef(relationModel, field, eb); + this.fieldRef(relationModel, field, eb, undefined, false); return [sql.lit(field), fieldValue]; } }) diff --git a/packages/runtime/src/client/crud/dialects/sqlite.ts b/packages/runtime/src/client/crud/dialects/sqlite.ts index 127a13b4..3a2a4868 100644 --- a/packages/runtime/src/client/crud/dialects/sqlite.ts +++ b/packages/runtime/src/client/crud/dialects/sqlite.ts @@ -170,7 +170,7 @@ export class SqliteCrudDialect extends BaseCrudDialect ...Object.entries(relationModelDef.fields) .filter(([, value]) => !value.relation) .filter(([name]) => !(typeof payload === 'object' && (payload.omit as any)?.[name] === true)) - .map(([field]) => [sql.lit(field), this.fieldRef(relationModel, field, eb)]) + .map(([field]) => [sql.lit(field), this.fieldRef(relationModel, field, eb, undefined, false)]) .flatMap((v) => v), ); } else if (payload.select) { @@ -199,7 +199,10 @@ export class SqliteCrudDialect extends BaseCrudDialect ); return [sql.lit(field), subJson]; } else { - return [sql.lit(field), this.fieldRef(relationModel, field, eb) as ArgsType]; + return [ + sql.lit(field), + this.fieldRef(relationModel, field, eb, undefined, false) as ArgsType, + ]; } } }) diff --git a/packages/runtime/src/client/query-utils.ts b/packages/runtime/src/client/query-utils.ts index d2c7b649..ff690fe1 100644 --- a/packages/runtime/src/client/query-utils.ts +++ b/packages/runtime/src/client/query-utils.ts @@ -161,11 +161,15 @@ export function buildFieldRef( options: ClientOptions, eb: ExpressionBuilder, modelAlias?: string, + inlineComputedField = true, ): ExpressionWrapper { const fieldDef = requireField(schema, model, field); if (!fieldDef.computed) { return eb.ref(modelAlias ? `${modelAlias}.${field}` : field); } else { + if (!inlineComputedField) { + return eb.ref(modelAlias ? `${modelAlias}.${field}` : field); + } let computer: Function | undefined; if ('computedFields' in options) { const computedFields = options.computedFields as Record; diff --git a/packages/runtime/test/client-api/computed-fields.test.ts b/packages/runtime/test/client-api/computed-fields.test.ts index 353f495f..85897452 100644 --- a/packages/runtime/test/client-api/computed-fields.test.ts +++ b/packages/runtime/test/client-api/computed-fields.test.ts @@ -1,107 +1,121 @@ -import { describe, expect, it } from 'vitest'; +import { afterEach, describe, expect, it } from 'vitest'; import { createTestClient } from '../utils'; -describe('Computed fields tests', () => { - it('works with non-optional fields', async () => { - const db = await createTestClient( - ` +const TEST_DB = 'client-api-computed-fields'; + +describe.each([{ provider: 'sqlite' as const }, { provider: 'postgresql' as const }])( + 'Computed fields tests', + ({ provider }) => { + let db: any; + + afterEach(async () => { + await db?.$disconnect(); + }); + + it('works with non-optional fields', async () => { + db = await createTestClient( + ` model User { id Int @id @default(autoincrement()) name String upperName String @computed } `, - { - computedFields: { - User: { - upperName: (eb: any) => eb.fn('upper', ['name']), + { + provider, + dbName: TEST_DB, + computedFields: { + User: { + upperName: (eb: any) => eb.fn('upper', ['name']), + }, }, - }, - } as any, - ); - - await expect( - db.user.create({ - data: { id: 1, name: 'Alex' }, - }), - ).resolves.toMatchObject({ - upperName: 'ALEX', - }); + } as any, + ); - await expect( - db.user.findUnique({ - where: { id: 1 }, - select: { upperName: true }, - }), - ).resolves.toMatchObject({ - upperName: 'ALEX', - }); + await expect( + db.user.create({ + data: { id: 1, name: 'Alex' }, + }), + ).resolves.toMatchObject({ + upperName: 'ALEX', + }); - await expect( - db.user.findFirst({ - where: { upperName: 'ALEX' }, - }), - ).resolves.toMatchObject({ - upperName: 'ALEX', - }); + await expect( + db.user.findUnique({ + where: { id: 1 }, + select: { upperName: true }, + }), + ).resolves.toMatchObject({ + upperName: 'ALEX', + }); - await expect( - db.user.findFirst({ - where: { upperName: 'Alex' }, - }), - ).toResolveNull(); - - await expect( - db.user.findFirst({ - orderBy: { upperName: 'desc' }, - }), - ).resolves.toMatchObject({ - upperName: 'ALEX', - }); + await expect( + db.user.findFirst({ + where: { upperName: 'ALEX' }, + }), + ).resolves.toMatchObject({ + upperName: 'ALEX', + }); - await expect( - db.user.findFirst({ - orderBy: { upperName: 'desc' }, - take: -1, - }), - ).resolves.toMatchObject({ - upperName: 'ALEX', - }); + await expect( + db.user.findFirst({ + where: { upperName: 'Alex' }, + }), + ).toResolveNull(); - await expect( - db.user.aggregate({ - _count: { upperName: true }, - }), - ).resolves.toMatchObject({ - _count: { upperName: 1 }, - }); + await expect( + db.user.findFirst({ + orderBy: { upperName: 'desc' }, + }), + ).resolves.toMatchObject({ + upperName: 'ALEX', + }); + + await expect( + db.user.findFirst({ + orderBy: { upperName: 'desc' }, + take: -1, + }), + ).resolves.toMatchObject({ + upperName: 'ALEX', + }); - await expect( - db.user.groupBy({ - by: ['upperName'], - _count: { upperName: true }, - _max: { upperName: true }, - }), - ).resolves.toEqual([ - expect.objectContaining({ + await expect( + db.user.aggregate({ + _count: { upperName: true }, + }), + ).resolves.toMatchObject({ _count: { upperName: 1 }, - _max: { upperName: 'ALEX' }, - }), - ]); - }); + }); + + await expect( + db.user.groupBy({ + by: ['upperName'], + _count: { upperName: true }, + _max: { upperName: true }, + }), + ).resolves.toEqual([ + expect.objectContaining({ + _count: { upperName: 1 }, + _max: { upperName: 'ALEX' }, + }), + ]); + }); - it('is typed correctly for non-optional fields', async () => { - await createTestClient( - ` + it('is typed correctly for non-optional fields', async () => { + db = await createTestClient( + ` model User { id Int @id @default(autoincrement()) name String upperName String @computed } `, - { - extraSourceFiles: { - main: ` + { + provider, + dbName: TEST_DB, + extraSourceFiles: { + main: ` import { ZenStackClient } from '@zenstackhq/runtime'; import { schema } from './schema'; @@ -125,50 +139,54 @@ async function main() { main(); `, + }, }, - }, - ); - }); + ); + }); - it('works with optional fields', async () => { - const db = await createTestClient( - ` + it('works with optional fields', async () => { + db = await createTestClient( + ` model User { id Int @id @default(autoincrement()) name String upperName String? @computed } `, - { - computedFields: { - User: { - upperName: (eb: any) => eb.lit(null), + { + provider, + dbName: TEST_DB, + computedFields: { + User: { + upperName: (eb: any) => eb.lit(null), + }, }, - }, - } as any, - ); - - await expect( - db.user.create({ - data: { id: 1, name: 'Alex' }, - }), - ).resolves.toMatchObject({ - upperName: null, + } as any, + ); + + await expect( + db.user.create({ + data: { id: 1, name: 'Alex' }, + }), + ).resolves.toMatchObject({ + upperName: null, + }); }); - }); - it('is typed correctly for optional fields', async () => { - await createTestClient( - ` + it('is typed correctly for optional fields', async () => { + db = await createTestClient( + ` model User { id Int @id @default(autoincrement()) name String upperName String? @computed } `, - { - extraSourceFiles: { - main: ` + { + provider, + dbName: TEST_DB, + extraSourceFiles: { + main: ` import { ZenStackClient } from '@zenstackhq/runtime'; import { schema } from './schema'; @@ -191,8 +209,50 @@ async function main() { main(); `, + }, }, - }, - ); - }); -}); + ); + }); + + it('works with read from a relation', async () => { + db = await createTestClient( + ` +model User { + id Int @id @default(autoincrement()) + name String + posts Post[] + postCount Int @computed +} + +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId Int +} +`, + { + provider, + dbName: TEST_DB, + computedFields: { + User: { + postCount: (eb: any) => + eb + .selectFrom('Post') + .whereRef('Post.authorId', '=', 'User.id') + .select(() => eb.fn.countAll().as('count')), + }, + }, + } as any, + ); + + await db.user.create({ + data: { id: 1, name: 'Alex', posts: { create: { title: 'Post1' } } }, + }); + + await expect(db.post.findFirst({ select: { id: true, author: true } })).resolves.toMatchObject({ + author: expect.objectContaining({ postCount: 1 }), + }); + }); + }, +); diff --git a/packages/runtime/tsconfig.build.json b/packages/runtime/tsconfig.build.json new file mode 100644 index 00000000..aacb3723 --- /dev/null +++ b/packages/runtime/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "@zenstackhq/typescript-config/base.json", + "compilerOptions": { + "rootDir": "." + }, + "include": ["src/**/*"] +} diff --git a/packages/runtime/tsconfig.json b/packages/runtime/tsconfig.json index 2125902f..6056fb01 100644 --- a/packages/runtime/tsconfig.json +++ b/packages/runtime/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { - "outDir": "dist" + "rootDir": "." }, "include": ["src/**/*", "test/**/*"] } diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 73120876..3cd7688b 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -4,7 +4,7 @@ "description": "ZenStack SDK", "type": "module", "scripts": { - "build": "tsup-node", + "build": "tsc --noEmit && tsup-node", "watch": "tsup-node --watch", "lint": "eslint src --ext ts", "pack": "pnpm pack" diff --git a/packages/sdk/tsconfig.json b/packages/sdk/tsconfig.json index 71637e39..07f91d53 100644 --- a/packages/sdk/tsconfig.json +++ b/packages/sdk/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { - "outDir": "dist", + "baseUrl": ".", "noUnusedLocals": false }, "include": ["src/**/*.ts"] diff --git a/packages/tanstack-query/package.json b/packages/tanstack-query/package.json index 08f82e6f..8e955bd4 100644 --- a/packages/tanstack-query/package.json +++ b/packages/tanstack-query/package.json @@ -6,7 +6,7 @@ "type": "module", "private": true, "scripts": { - "build": "tsup-node", + "build": "tsc --noEmit && tsup-node", "lint": "eslint src --ext ts" }, "keywords": [], diff --git a/packages/tanstack-query/tsconfig.json b/packages/tanstack-query/tsconfig.json index 3df1d231..a64b0eb5 100644 --- a/packages/tanstack-query/tsconfig.json +++ b/packages/tanstack-query/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { - "outDir": "dist" + "baseUrl": "." }, "include": ["src/**/*.ts", "test/**/*.ts"] } diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 9bd7778a..8b5d1cf2 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -4,7 +4,7 @@ "description": "ZenStack Test Tools", "type": "module", "scripts": { - "build": "tsup-node", + "build": "tsc --noEmit && tsup-node", "watch": "tsup-node --watch", "lint": "eslint src --ext ts", "pack": "pnpm pack" diff --git a/packages/testtools/tsconfig.json b/packages/testtools/tsconfig.json index 3df1d231..a64b0eb5 100644 --- a/packages/testtools/tsconfig.json +++ b/packages/testtools/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { - "outDir": "dist" + "baseUrl": "." }, "include": ["src/**/*.ts", "test/**/*.ts"] } diff --git a/packages/zod/package.json b/packages/zod/package.json index c80efbc3..fed86620 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -6,7 +6,7 @@ "main": "index.js", "private": true, "scripts": { - "build": "tsup-node", + "build": "tsc --noEmit && tsup-node", "lint": "eslint src --ext ts" }, "keywords": [], diff --git a/packages/zod/tsconfig.json b/packages/zod/tsconfig.json index 3df1d231..a64b0eb5 100644 --- a/packages/zod/tsconfig.json +++ b/packages/zod/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { - "outDir": "dist" + "baseUrl": "." }, "include": ["src/**/*.ts", "test/**/*.ts"] } From 904a8aa8f5478e26871b9740eb8eb909b8da5448 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 15 Aug 2025 16:15:27 +0800 Subject: [PATCH 3/4] chore: bump version 3.0.0-alpha.25 (#173) Co-authored-by: ymc9 <104139426+ymc9@users.noreply.github.com> --- package.json | 2 +- packages/cli/package.json | 2 +- packages/common-helpers/package.json | 2 +- packages/create-zenstack/package.json | 2 +- packages/dialects/sql.js/package.json | 2 +- packages/eslint-config/package.json | 2 +- packages/ide/vscode/package.json | 2 +- packages/language/package.json | 2 +- packages/runtime/package.json | 2 +- packages/sdk/package.json | 2 +- packages/tanstack-query/package.json | 2 +- packages/testtools/package.json | 2 +- packages/typescript-config/package.json | 2 +- packages/vitest-config/package.json | 2 +- packages/zod/package.json | 2 +- samples/blog/package.json | 2 +- tests/e2e/package.json | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 237c9d63..c88ee776 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zenstack-v3", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "description": "ZenStack", "packageManager": "pnpm@10.12.1", "scripts": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 3f38017e..69214b57 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -3,7 +3,7 @@ "publisher": "zenstack", "displayName": "ZenStack CLI", "description": "FullStack database toolkit with built-in access control and automatic API generation.", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "type": "module", "author": { "name": "ZenStack Team" diff --git a/packages/common-helpers/package.json b/packages/common-helpers/package.json index b3eb5964..9e4fae3e 100644 --- a/packages/common-helpers/package.json +++ b/packages/common-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/common-helpers", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "description": "ZenStack Common Helpers", "type": "module", "scripts": { diff --git a/packages/create-zenstack/package.json b/packages/create-zenstack/package.json index 5c364042..e1fb4fa5 100644 --- a/packages/create-zenstack/package.json +++ b/packages/create-zenstack/package.json @@ -1,6 +1,6 @@ { "name": "create-zenstack", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "description": "Create a new ZenStack project", "type": "module", "scripts": { diff --git a/packages/dialects/sql.js/package.json b/packages/dialects/sql.js/package.json index 1a722f72..df6765ae 100644 --- a/packages/dialects/sql.js/package.json +++ b/packages/dialects/sql.js/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/kysely-sql-js", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "description": "Kysely dialect for sql.js", "type": "module", "scripts": { diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 36d820f7..d061db42 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/eslint-config", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "type": "module", "private": true, "license": "MIT" diff --git a/packages/ide/vscode/package.json b/packages/ide/vscode/package.json index 92ab6b1a..d8472cc3 100644 --- a/packages/ide/vscode/package.json +++ b/packages/ide/vscode/package.json @@ -1,7 +1,7 @@ { "name": "zenstack", "publisher": "zenstack", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "displayName": "ZenStack Language Tools", "description": "VSCode extension for ZenStack ZModel language", "private": true, diff --git a/packages/language/package.json b/packages/language/package.json index 2a1fd6fb..8e0c1585 100644 --- a/packages/language/package.json +++ b/packages/language/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/language", "description": "ZenStack ZModel language specification", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "license": "MIT", "author": "ZenStack Team", "files": [ diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 5648e106..ab3a8b1c 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/runtime", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "description": "ZenStack Runtime", "type": "module", "scripts": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 3cd7688b..21dec84f 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/sdk", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "description": "ZenStack SDK", "type": "module", "scripts": { diff --git a/packages/tanstack-query/package.json b/packages/tanstack-query/package.json index 8e955bd4..c680ae18 100644 --- a/packages/tanstack-query/package.json +++ b/packages/tanstack-query/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/tanstack-query", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "description": "", "main": "index.js", "type": "module", diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 8b5d1cf2..80d9bcf1 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/testtools", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "description": "ZenStack Test Tools", "type": "module", "scripts": { diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index 31e6f850..5fd89dd4 100644 --- a/packages/typescript-config/package.json +++ b/packages/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/typescript-config", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "private": true, "license": "MIT" } diff --git a/packages/vitest-config/package.json b/packages/vitest-config/package.json index cfa33a8a..d259c676 100644 --- a/packages/vitest-config/package.json +++ b/packages/vitest-config/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/vitest-config", "type": "module", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "private": true, "license": "MIT", "exports": { diff --git a/packages/zod/package.json b/packages/zod/package.json index fed86620..066824d6 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/zod", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "description": "", "type": "module", "main": "index.js", diff --git a/samples/blog/package.json b/samples/blog/package.json index 340dd20e..469161b5 100644 --- a/samples/blog/package.json +++ b/samples/blog/package.json @@ -1,6 +1,6 @@ { "name": "sample-blog", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "description": "", "main": "index.js", "scripts": { diff --git a/tests/e2e/package.json b/tests/e2e/package.json index dc21249c..a08dfe41 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "e2e", - "version": "3.0.0-alpha.24", + "version": "3.0.0-alpha.25", "private": true, "type": "module", "scripts": { From 1ac89c1cba94920dee89a0ef3fb0972be03a46ff Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Fri, 15 Aug 2025 17:00:08 +0800 Subject: [PATCH 4/4] feat: add "migrate resolve" command to CLI (#171) * feat: add "migrate resolve" command to CLI * Update packages/cli/src/index.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * add test * update test --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- packages/cli/src/actions/migrate.ts | 29 +++++++++++++++++++++++++++ packages/cli/src/index.ts | 11 +++++++++- packages/cli/test/migrate.test.ts | 31 +++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 77bdfe1f..db969d96 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ npm install -D @types/better-sqlite3 For Postgres: ```bash -npm install pg pg-connection-string +npm install pg npm install -D @types/pg ``` diff --git a/packages/cli/src/actions/migrate.ts b/packages/cli/src/actions/migrate.ts index 6a667a16..896cc991 100644 --- a/packages/cli/src/actions/migrate.ts +++ b/packages/cli/src/actions/migrate.ts @@ -1,5 +1,6 @@ import fs from 'node:fs'; import path from 'node:path'; +import { CliError } from '../cli-error'; import { execPackage } from '../utils/exec-utils'; import { generateTempPrismaSchema, getSchemaFile } from './action-utils'; @@ -21,6 +22,11 @@ type DeployOptions = CommonOptions; type StatusOptions = CommonOptions; +type ResolveOptions = CommonOptions & { + applied?: string; + rolledBack?: string; +}; + /** * CLI action for migration-related commands */ @@ -46,6 +52,10 @@ export async function run(command: string, options: CommonOptions) { case 'status': await runStatus(prismaSchemaFile, options as StatusOptions); break; + + case 'resolve': + await runResolve(prismaSchemaFile, options as ResolveOptions); + break; } } finally { if (fs.existsSync(prismaSchemaFile)) { @@ -100,6 +110,25 @@ async function runStatus(prismaSchemaFile: string, _options: StatusOptions) { } } +async function runResolve(prismaSchemaFile: string, options: ResolveOptions) { + if (!options.applied && !options.rolledBack) { + throw new CliError('Either --applied or --rolled-back option must be provided'); + } + + try { + const cmd = [ + 'prisma migrate resolve', + ` --schema "${prismaSchemaFile}"`, + options.applied ? ` --applied ${options.applied}` : '', + options.rolledBack ? ` --rolled-back ${options.rolledBack}` : '', + ].join(''); + + await execPackage(cmd); + } catch (err) { + handleSubProcessError(err); + } +} + function handleSubProcessError(err: unknown) { if (err instanceof Error && 'status' in err && typeof err.status === 'number') { process.exit(err.status); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index fc154121..fd5ad01e 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -95,9 +95,18 @@ export function createProgram() { .command('status') .addOption(schemaOption) .addOption(migrationsOption) - .description('check the status of your database migrations.') + .description('Check the status of your database migrations.') .action((options) => migrateAction('status', options)); + migrateCommand + .command('resolve') + .addOption(schemaOption) + .addOption(migrationsOption) + .addOption(new Option('--applied ', 'record a specific migration as applied')) + .addOption(new Option('--rolled-back ', 'record a specific migration as rolled back')) + .description('Resolve issues with database migrations in deployment databases') + .action((options) => migrateAction('resolve', options)); + const dbCommand = program.command('db').description('Manage your database schema during development.'); dbCommand diff --git a/packages/cli/test/migrate.test.ts b/packages/cli/test/migrate.test.ts index 85c2a928..56a0fec8 100644 --- a/packages/cli/test/migrate.test.ts +++ b/packages/cli/test/migrate.test.ts @@ -38,4 +38,35 @@ describe('CLI migrate commands test', () => { runCli('migrate dev --name init', workDir); runCli('migrate status', workDir); }); + + it('supports migrate resolve', () => { + const workDir = createProject(model); + runCli('migrate dev --name init', workDir); + + // find the migration record "timestamp_init" + const migrationRecords = fs.readdirSync(path.join(workDir, 'zenstack/migrations')); + const migration = migrationRecords.find((f) => f.endsWith('_init')); + + // force a migration failure + fs.writeFileSync(path.join(workDir, 'zenstack/migrations', migration!, 'migration.sql'), 'invalid content'); + + // redeploy the migration, which will fail + fs.rmSync(path.join(workDir, 'zenstack/dev.db'), { force: true }); + try { + runCli('migrate deploy', workDir); + } catch { + // noop + } + + // --rolled-back + runCli(`migrate resolve --rolled-back ${migration}`, workDir); + + // --applied + runCli(`migrate resolve --applied ${migration}`, workDir); + }); + + it('should throw error when neither applied nor rolled-back is provided', () => { + const workDir = createProject(model); + expect(() => runCli('migrate resolve', workDir)).toThrow(); + }); });