diff --git a/package.json b/package.json index 27ac1fad2..1480b00f7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "zenstack-v3", "displayName": "ZenStack", "description": "ZenStack", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/auth-adapters/better-auth/package.json b/packages/auth-adapters/better-auth/package.json index 871764533..5f153ee98 100644 --- a/packages/auth-adapters/better-auth/package.json +++ b/packages/auth-adapters/better-auth/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/better-auth", "displayName": "ZenStack Better Auth Adapter", "description": "ZenStack Better Auth Adapter. This adapter is modified from better-auth's Prisma adapter.", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", @@ -39,6 +39,16 @@ "default": "./dist/index.cjs" } }, + "./schema-generator": { + "import": { + "types": "./dist/schema-generator.d.mts", + "default": "./dist/schema-generator.mjs" + }, + "require": { + "types": "./dist/schema-generator.d.cts", + "default": "./dist/schema-generator.cjs" + } + }, "./package.json": { "import": "./package.json", "require": "./package.json" diff --git a/packages/auth-adapters/better-auth/src/adapter.ts b/packages/auth-adapters/better-auth/src/adapter.ts index 5e1138f63..cc5d850c3 100644 --- a/packages/auth-adapters/better-auth/src/adapter.ts +++ b/packages/auth-adapters/better-auth/src/adapter.ts @@ -8,7 +8,6 @@ import { type AdapterFactoryCustomizeAdapterCreator, type AdapterFactoryOptions, } from 'better-auth/adapters'; -import { generateSchema } from './schema-generator'; /** * Options for the ZenStack adapter factory. @@ -209,6 +208,7 @@ export const zenstackAdapter = (db: ClientContract { + const generateSchema = (await import('./schema-generator')).generateSchema; return generateSchema(file, tables, config, options); }, }; diff --git a/packages/auth-adapters/better-auth/tsdown.config.ts b/packages/auth-adapters/better-auth/tsdown.config.ts index e0a6d5624..28af6aa31 100644 --- a/packages/auth-adapters/better-auth/tsdown.config.ts +++ b/packages/auth-adapters/better-auth/tsdown.config.ts @@ -1,3 +1,3 @@ import { createConfig } from '@zenstackhq/tsdown-config'; -export default createConfig({ entry: { index: 'src/index.ts' } }); +export default createConfig({ entry: { index: 'src/index.ts', 'schema-generator': 'src/schema-generator.ts' } }); diff --git a/packages/cli/package.json b/packages/cli/package.json index e64209882..8d6b5285a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/cli", "displayName": "ZenStack CLI", "description": "FullStack database toolkit with built-in access control and automatic API generation.", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/clients/client-helpers/package.json b/packages/clients/client-helpers/package.json index 89072cc08..7f44c344b 100644 --- a/packages/clients/client-helpers/package.json +++ b/packages/clients/client-helpers/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/client-helpers", "displayName": "ZenStack Client Helpers", "description": "Helpers for implementing clients that consume ZenStack's CRUD service", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/clients/tanstack-query/package.json b/packages/clients/tanstack-query/package.json index a7ab4c284..ee60b85bf 100644 --- a/packages/clients/tanstack-query/package.json +++ b/packages/clients/tanstack-query/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/tanstack-query", "displayName": "ZenStack TanStack Query Integration", "description": "TanStack Query Client for consuming ZenStack v3's CRUD service", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/common-helpers/package.json b/packages/common-helpers/package.json index dd7f7ae9c..02e89668b 100644 --- a/packages/common-helpers/package.json +++ b/packages/common-helpers/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/common-helpers", "displayName": "ZenStack Common Helpers", "description": "ZenStack Common Helpers", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/config/eslint-config/package.json b/packages/config/eslint-config/package.json index a613d5a35..3ccf99d16 100644 --- a/packages/config/eslint-config/package.json +++ b/packages/config/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/eslint-config", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "private": true, "license": "MIT" diff --git a/packages/config/tsdown-config/package.json b/packages/config/tsdown-config/package.json index efcb91f61..3e1700ae0 100644 --- a/packages/config/tsdown-config/package.json +++ b/packages/config/tsdown-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/tsdown-config", - "version": "3.6.2", + "version": "3.6.3", "private": true, "type": "module", "license": "MIT", diff --git a/packages/config/typescript-config/package.json b/packages/config/typescript-config/package.json index 5f68a2ac7..3481351f1 100644 --- a/packages/config/typescript-config/package.json +++ b/packages/config/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/typescript-config", - "version": "3.6.2", + "version": "3.6.3", "private": true, "license": "MIT" } diff --git a/packages/config/vitest-config/package.json b/packages/config/vitest-config/package.json index 9410b5ca5..4d00103b8 100644 --- a/packages/config/vitest-config/package.json +++ b/packages/config/vitest-config/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/vitest-config", "type": "module", - "version": "3.6.2", + "version": "3.6.3", "private": true, "license": "MIT", "exports": { diff --git a/packages/create-zenstack/package.json b/packages/create-zenstack/package.json index ca05b1f13..b581504e6 100644 --- a/packages/create-zenstack/package.json +++ b/packages/create-zenstack/package.json @@ -2,7 +2,7 @@ "name": "create-zenstack", "displayName": "Create ZenStack", "description": "Create a new ZenStack project", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/ide/vscode/package.json b/packages/ide/vscode/package.json index a3b58f891..698845ccd 100644 --- a/packages/ide/vscode/package.json +++ b/packages/ide/vscode/package.json @@ -1,7 +1,7 @@ { "name": "zenstack-v3", "publisher": "zenstack", - "version": "3.6.2", + "version": "3.6.3", "displayName": "ZenStack V3 Language Tools", "description": "VSCode extension for ZenStack (v3) ZModel language", "private": true, diff --git a/packages/language/package.json b/packages/language/package.json index ca0439abd..a958c6687 100644 --- a/packages/language/package.json +++ b/packages/language/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/language", "displayName": "ZenStack Language Tooling", "description": "ZenStack ZModel language specification", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/orm/package.json b/packages/orm/package.json index 4ef11ed73..ce5181bd8 100644 --- a/packages/orm/package.json +++ b/packages/orm/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/orm", "displayName": "ZenStack ORM", "description": "ZenStack ORM", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/plugins/policy/package.json b/packages/plugins/policy/package.json index 40879ecb0..74a4d5ded 100644 --- a/packages/plugins/policy/package.json +++ b/packages/plugins/policy/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/plugin-policy", "displayName": "ZenStack Access Policy Plugin", "description": "ZenStack plugin that enforces access control policies defined in the schema", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/plugins/policy/src/policy-handler.ts b/packages/plugins/policy/src/policy-handler.ts index 2bbdfadd9..93c6c334a 100644 --- a/packages/plugins/policy/src/policy-handler.ts +++ b/packages/plugins/policy/src/policy-handler.ts @@ -205,24 +205,33 @@ export class PolicyHandler extends OperationNodeTransf // filter combining model-level update policy and update where const updateFilter = conjunction(this.dialect, [modelLevelFilter, node.where?.where ?? trueNode(this.dialect)]); - // build a query to count rows that will be rejected by field-level policies - // `SELECT COALESCE(SUM((not ) as integer), 0) AS $filteredCount WHERE AND ` - const preUpdateCheckQuery = this.eb + // check if any rows violate field-level policies: satisfying update filter but not field-level filter: + // `SELECT 1 FROM WHERE AND NOT ` + const violatingRowsQuery = this.eb .selectFrom(mutationModel) - .select((eb) => - eb.fn - .coalesce( - eb.fn.sum( - this.dialect.castInt(new ExpressionWrapper(logicalNot(this.dialect, fieldLevelFilter))), - ), - eb.lit(0), - ) - .as('$filteredCount'), - ) - .where(() => new ExpressionWrapper(updateFilter)); + .select(this.eb.lit(1).as('_')) + .where( + () => + new ExpressionWrapper( + conjunction(this.dialect, [updateFilter, logicalNot(this.dialect, fieldLevelFilter)]), + ), + ); - const preUpdateResult = await proceed(preUpdateCheckQuery.toOperationNode()); - if (preUpdateResult.rows[0].$filteredCount > 0) { + const preUpdateResult = await proceed( + // `SELECT EXISTS(violatingRowsQuery) AS $condition` + { + kind: 'SelectQueryNode', + selections: [ + SelectionNode.create( + AliasNode.create( + this.eb.exists(violatingRowsQuery).toOperationNode(), + IdentifierNode.create('$condition'), + ), + ), + ], + } satisfies SelectQueryNode, + ); + if (preUpdateResult.rows[0].$condition) { throw createRejectedByPolicyError( mutationModel, RejectedByPolicyReason.NO_ACCESS, @@ -918,12 +927,28 @@ export class PolicyHandler extends OperationNodeTransf const filter = this.buildPolicyFilter(model, undefined, 'create'); - const preCreateCheck = this.eb + // check if the provided values satisfy the create policy + + // `SELECT 1 FROM (VALUES (...)) AS t(column1, column2, ...) WHERE ` + const preCreateInner = this.eb .selectFrom(valuesTable.as(model)) - .select(this.eb(this.eb.fn.count(this.eb.lit(1)), '>', 0).as('$condition')) + .select(this.eb.lit(1).as('_')) .where(() => new ExpressionWrapper(filter)); - const result = await proceed(preCreateCheck.toOperationNode()); + const result = await proceed( + // `SELECT EXISTS(preCreateInner) AS $condition` + { + kind: 'SelectQueryNode', + selections: [ + SelectionNode.create( + AliasNode.create( + this.eb.exists(preCreateInner).toOperationNode(), + IdentifierNode.create('$condition'), + ), + ), + ], + } satisfies SelectQueryNode, + ); if (!result.rows[0]?.$condition) { throw createRejectedByPolicyError(model, RejectedByPolicyReason.NO_ACCESS); } diff --git a/packages/schema/package.json b/packages/schema/package.json index 7cdfc2bd5..e4e51f1b2 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/schema", "displayName": "ZenStack Schema Object Model", "description": "TypeScript representation of ZModel schema", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 3a5fc49e1..6dea9165b 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/sdk", "displayName": "ZenStack SDK", "description": "Utilities for building ZenStack plugins", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/server/package.json b/packages/server/package.json index 39da83be8..06d51b533 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/server", "displayName": "ZenStack Automatic CRUD Server", "description": "ZenStack automatic CRUD API handlers and server adapters for popular frameworks", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 652a03384..7d261bed8 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/testtools", "displayName": "ZenStack Test Tools", "description": "ZenStack Test Tools", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/zod/package.json b/packages/zod/package.json index 87545ee4e..8f3d088d7 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/zod", "displayName": "ZenStack Zod Integration", "description": "Automatically deriving Zod schemas from ZModel schemas", - "version": "3.6.2", + "version": "3.6.3", "type": "module", "author": { "name": "ZenStack Team", diff --git a/samples/orm/package.json b/samples/orm/package.json index 75047ed7a..0868eb967 100644 --- a/samples/orm/package.json +++ b/samples/orm/package.json @@ -1,6 +1,6 @@ { "name": "sample-orm", - "version": "3.6.2", + "version": "3.6.3", "description": "", "main": "index.js", "private": true, diff --git a/tests/e2e/package.json b/tests/e2e/package.json index 3d8897d70..931f99380 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "e2e", - "version": "3.6.2", + "version": "3.6.3", "private": true, "type": "module", "scripts": { diff --git a/tests/regression/package.json b/tests/regression/package.json index f4be0b0d0..3907431f5 100644 --- a/tests/regression/package.json +++ b/tests/regression/package.json @@ -1,6 +1,6 @@ { "name": "regression", - "version": "3.6.2", + "version": "3.6.3", "private": true, "type": "module", "scripts": { diff --git a/tests/runtimes/bun/package.json b/tests/runtimes/bun/package.json index c37cf387d..d30c900bd 100644 --- a/tests/runtimes/bun/package.json +++ b/tests/runtimes/bun/package.json @@ -1,6 +1,6 @@ { "name": "bun-e2e", - "version": "3.6.2", + "version": "3.6.3", "private": true, "type": "module", "scripts": { diff --git a/tests/runtimes/edge-runtime/package.json b/tests/runtimes/edge-runtime/package.json index 21efb03a4..871decac1 100644 --- a/tests/runtimes/edge-runtime/package.json +++ b/tests/runtimes/edge-runtime/package.json @@ -1,6 +1,6 @@ { "name": "edge-runtime-e2e", - "version": "3.6.2", + "version": "3.6.3", "private": true, "type": "module", "scripts": {