From daaf0bbd7d54c29fa360f44cd07dd0501855fd14 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 22:31:59 -0700 Subject: [PATCH 1/2] chore: bump version 3.0.0-beta.6 (#266) 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/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 +- tests/regression/package.json | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 88f7b917..3ab1d94d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zenstack-v3", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "description": "ZenStack", "packageManager": "pnpm@10.12.1", "scripts": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 17f38a48..7cb2b767 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-beta.5", + "version": "3.0.0-beta.6", "type": "module", "author": { "name": "ZenStack Team" diff --git a/packages/common-helpers/package.json b/packages/common-helpers/package.json index 78f842e2..f6dee8f1 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-beta.5", + "version": "3.0.0-beta.6", "description": "ZenStack Common Helpers", "type": "module", "scripts": { diff --git a/packages/create-zenstack/package.json b/packages/create-zenstack/package.json index adcac380..84a20000 100644 --- a/packages/create-zenstack/package.json +++ b/packages/create-zenstack/package.json @@ -1,6 +1,6 @@ { "name": "create-zenstack", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "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 cc43bcd2..36acf9a4 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-beta.5", + "version": "3.0.0-beta.6", "description": "Kysely dialect for sql.js", "type": "module", "scripts": { diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 5b8ec217..f2e1acd8 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-beta.5", + "version": "3.0.0-beta.6", "type": "module", "private": true, "license": "MIT" diff --git a/packages/language/package.json b/packages/language/package.json index 0754fdd8..96650b4e 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-beta.5", + "version": "3.0.0-beta.6", "license": "MIT", "author": "ZenStack Team", "files": [ diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 561eb33f..8ebad598 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/runtime", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "description": "ZenStack Runtime", "type": "module", "scripts": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index e72757cc..53f06bee 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/sdk", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "description": "ZenStack SDK", "type": "module", "scripts": { diff --git a/packages/tanstack-query/package.json b/packages/tanstack-query/package.json index 4dd4379e..ca54c999 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-beta.5", + "version": "3.0.0-beta.6", "description": "", "main": "index.js", "type": "module", diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 683b6652..4b58e8e3 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/testtools", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "description": "ZenStack Test Tools", "type": "module", "scripts": { diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index 833b3d0d..78a68cb3 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-beta.5", + "version": "3.0.0-beta.6", "private": true, "license": "MIT" } diff --git a/packages/vitest-config/package.json b/packages/vitest-config/package.json index a7a2d8c5..a5f0695d 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-beta.5", + "version": "3.0.0-beta.6", "private": true, "license": "MIT", "exports": { diff --git a/packages/zod/package.json b/packages/zod/package.json index 350be5d2..571b46be 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/zod", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "description": "", "type": "module", "main": "index.js", diff --git a/samples/blog/package.json b/samples/blog/package.json index 8f60eb7f..b3a9157a 100644 --- a/samples/blog/package.json +++ b/samples/blog/package.json @@ -1,6 +1,6 @@ { "name": "sample-blog", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "description": "", "main": "index.js", "scripts": { diff --git a/tests/e2e/package.json b/tests/e2e/package.json index a7184260..e4137b43 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "e2e", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "private": true, "type": "module", "scripts": { diff --git a/tests/regression/package.json b/tests/regression/package.json index c64fd64b..b5b2b88a 100644 --- a/tests/regression/package.json +++ b/tests/regression/package.json @@ -1,6 +1,6 @@ { "name": "regression", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "private": true, "type": "module", "scripts": { From 9bf6d7f7544eff273cc0d47083b87ac52c877fd7 Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Mon, 22 Sep 2025 22:43:14 -0700 Subject: [PATCH 2/2] refactor: move policy zmodel back to stdlib (#265) * refactor: move policy zmodel back to stdlib * update --- packages/language/res/stdlib.zmodel | 59 +++++++++++++++++-- packages/language/test/utils.ts | 8 +-- .../runtime/src/plugins/policy/plugin.zmodel | 43 -------------- packages/runtime/test/scripts/generate.ts | 3 +- packages/runtime/test/utils.ts | 4 +- packages/runtime/tsup.config.ts | 4 -- packages/testtools/src/schema.ts | 11 +--- tests/regression/generate.ts | 4 +- 8 files changed, 61 insertions(+), 75 deletions(-) delete mode 100644 packages/runtime/src/plugins/policy/plugin.zmodel diff --git a/packages/language/res/stdlib.zmodel b/packages/language/res/stdlib.zmodel index c248bde0..e43c389c 100644 --- a/packages/language/res/stdlib.zmodel +++ b/packages/language/res/stdlib.zmodel @@ -116,12 +116,6 @@ function autoincrement(): Int { function dbgenerated(expr: String?): Any { } @@@expressionContext([DefaultValue]) -/** - * Gets entities value before an update. Only valid when used in a "update" policy rule. - */ -function future(): Any { -} @@@expressionContext([AccessPolicy]) - /** * Checks if the field value contains the search string. By default, the search is case-sensitive, and * "LIKE" operator is used to match. If `caseInSensitive` is true, "ILIKE" operator is used if @@ -663,3 +657,56 @@ attribute @meta(_ name: String, _ value: Any) * Marks an attribute as deprecated. */ attribute @@@deprecated(_ message: String) + +/* --- Policy Plugin --- */ + +/** + * Defines an access policy that allows a set of operations when the given condition is true. + * + * @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations. + * @param condition: a boolean expression that controls if the operation should be allowed. + */ +attribute @@allow(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean) + +/** + * Defines an access policy that allows the annotated field to be read or updated. + * You can pass a third argument as `true` to make it override the model-level policies. + * + * @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations. + * @param condition: a boolean expression that controls if the operation should be allowed. + * @param override: a boolean value that controls if the field-level policy should override the model-level policy. + */ +attribute @allow(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean, _ override: Boolean?) + +/** + * Defines an access policy that denies a set of operations when the given condition is true. + * + * @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations. + * @param condition: a boolean expression that controls if the operation should be denied. + */ +attribute @@deny(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean) + +/** + * Defines an access policy that denies the annotated field to be read or updated. + * + * @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations. + * @param condition: a boolean expression that controls if the operation should be denied. + */ +attribute @deny(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean) + +/** + * Checks if the current user can perform the given operation on the given field. + * + * @param field: The field to check access for + * @param operation: The operation to check access for. Can be "read", "create", "update", or "delete". If the operation is not provided, + * it defaults the operation of the containing policy rule. + */ +function check(field: Any, operation: String?): Boolean { +} @@@expressionContext([AccessPolicy]) + +/** + * Gets entities value before an update. Only valid when used in a "update" policy rule. + */ +function future(): Any { +} @@@expressionContext([AccessPolicy]) + diff --git a/packages/language/test/utils.ts b/packages/language/test/utils.ts index b14bdabb..4b60ce42 100644 --- a/packages/language/test/utils.ts +++ b/packages/language/test/utils.ts @@ -1,5 +1,4 @@ import { invariant } from '@zenstackhq/common-helpers'; -import { glob } from 'glob'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; @@ -10,7 +9,7 @@ export async function loadSchema(schema: string) { // create a temp file const tempFile = path.join(os.tmpdir(), `zenstack-schema-${crypto.randomUUID()}.zmodel`); fs.writeFileSync(tempFile, schema); - const r = await loadDocument(tempFile, getPluginModels()); + const r = await loadDocument(tempFile); expect(r).toSatisfy( (r) => r.success, `Failed to load schema: ${(r as any).errors?.map((e) => e.toString()).join(', ')}`, @@ -23,7 +22,7 @@ export async function loadSchemaWithError(schema: string, error: string | RegExp // create a temp file const tempFile = path.join(os.tmpdir(), `zenstack-schema-${crypto.randomUUID()}.zmodel`); fs.writeFileSync(tempFile, schema); - const r = await loadDocument(tempFile, getPluginModels()); + const r = await loadDocument(tempFile); expect(r.success).toBe(false); invariant(!r.success); if (typeof error === 'string') { @@ -38,6 +37,3 @@ export async function loadSchemaWithError(schema: string, error: string | RegExp ); } } -function getPluginModels() { - return glob.sync(path.resolve(__dirname, '../../runtime/src/plugins/**/plugin.zmodel')); -} diff --git a/packages/runtime/src/plugins/policy/plugin.zmodel b/packages/runtime/src/plugins/policy/plugin.zmodel deleted file mode 100644 index 659705ce..00000000 --- a/packages/runtime/src/plugins/policy/plugin.zmodel +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Defines an access policy that allows a set of operations when the given condition is true. - * - * @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations. - * @param condition: a boolean expression that controls if the operation should be allowed. - */ -attribute @@allow(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean) - -/** - * Defines an access policy that allows the annotated field to be read or updated. - * You can pass a third argument as `true` to make it override the model-level policies. - * - * @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations. - * @param condition: a boolean expression that controls if the operation should be allowed. - * @param override: a boolean value that controls if the field-level policy should override the model-level policy. - */ -attribute @allow(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean, _ override: Boolean?) - -/** - * Defines an access policy that denies a set of operations when the given condition is true. - * - * @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations. - * @param condition: a boolean expression that controls if the operation should be denied. - */ -attribute @@deny(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean) - -/** - * Defines an access policy that denies the annotated field to be read or updated. - * - * @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations. - * @param condition: a boolean expression that controls if the operation should be denied. - */ -attribute @deny(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean) - -/** - * Checks if the current user can perform the given operation on the given field. - * - * @param field: The field to check access for - * @param operation: The operation to check access for. Can be "read", "create", "update", or "delete". If the operation is not provided, - * it defaults the operation of the containing policy rule. - */ -function check(field: Any, operation: String?): Boolean { -} @@@expressionContext([AccessPolicy]) diff --git a/packages/runtime/test/scripts/generate.ts b/packages/runtime/test/scripts/generate.ts index 7e5f1293..df405295 100644 --- a/packages/runtime/test/scripts/generate.ts +++ b/packages/runtime/test/scripts/generate.ts @@ -19,8 +19,7 @@ async function generate(schemaPath: string) { const generator = new TsSchemaGenerator(); const outputDir = path.dirname(schemaPath); const tsPath = path.join(outputDir, 'schema.ts'); - const pluginModelFiles = glob.sync(path.resolve(dir, '../../dist/**/plugin.zmodel')); - const result = await loadDocument(schemaPath, pluginModelFiles); + const result = await loadDocument(schemaPath); if (!result.success) { throw new Error(`Failed to load schema from ${schemaPath}: ${result.errors}`); } diff --git a/packages/runtime/test/utils.ts b/packages/runtime/test/utils.ts index b7245062..d5bea549 100644 --- a/packages/runtime/test/utils.ts +++ b/packages/runtime/test/utils.ts @@ -2,7 +2,7 @@ import { invariant } from '@zenstackhq/common-helpers'; import { loadDocument } from '@zenstackhq/language'; import type { Model } from '@zenstackhq/language/ast'; import { PrismaSchemaGenerator } from '@zenstackhq/sdk'; -import { createTestProject, generateTsSchema, getPluginModules } from '@zenstackhq/testtools'; +import { createTestProject, generateTsSchema } from '@zenstackhq/testtools'; import SQLite from 'better-sqlite3'; import { PostgresDialect, SqliteDialect, type LogEvent } from 'kysely'; import { execSync } from 'node:child_process'; @@ -113,7 +113,7 @@ export async function createTestClient( if (options?.usePrismaPush) { invariant(typeof schema === 'string' || schemaFile, 'a schema file must be provided when using prisma db push'); if (!model) { - const r = await loadDocument(path.join(workDir, 'schema.zmodel'), getPluginModules()); + const r = await loadDocument(path.join(workDir, 'schema.zmodel')); if (!r.success) { throw new Error(r.errors.join('\n')); } diff --git a/packages/runtime/tsup.config.ts b/packages/runtime/tsup.config.ts index 795f2b2b..7f7a9348 100644 --- a/packages/runtime/tsup.config.ts +++ b/packages/runtime/tsup.config.ts @@ -1,5 +1,4 @@ import { defineConfig } from 'tsup'; -import fs from 'node:fs'; export default defineConfig({ entry: { @@ -14,7 +13,4 @@ export default defineConfig({ clean: true, dts: true, format: ['cjs', 'esm'], - async onSuccess() { - fs.cpSync('src/plugins/policy/plugin.zmodel', 'dist/plugins/policy/plugin.zmodel'); - }, }); diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index b4f5386e..ee57a0c5 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -1,7 +1,6 @@ import { loadDocument } from '@zenstackhq/language'; import { TsSchemaGenerator } from '@zenstackhq/sdk'; import type { SchemaDef } from '@zenstackhq/sdk/schema'; -import { glob } from 'glob'; import { execSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; @@ -41,8 +40,7 @@ export async function generateTsSchema( const noPrelude = schemaText.includes('datasource '); fs.writeFileSync(zmodelPath, `${noPrelude ? '' : makePrelude(provider, dbUrl)}\n\n${schemaText}`); - const pluginModelFiles = getPluginModules(); - const result = await loadDocument(zmodelPath, pluginModelFiles); + const result = await loadDocument(zmodelPath); if (!result.success) { throw new Error(`Failed to load schema from ${zmodelPath}: ${result.errors}`); } @@ -62,10 +60,6 @@ export async function generateTsSchema( return { ...(await compileAndLoad(workDir)), model: result.model }; } -export function getPluginModules() { - return glob.sync(path.resolve(__dirname, '../../runtime/src/plugins/**/plugin.zmodel')); -} - async function compileAndLoad(workDir: string) { execSync('npx tsc', { cwd: workDir, @@ -84,8 +78,7 @@ export function generateTsSchemaFromFile(filePath: string) { export async function generateTsSchemaInPlace(schemaPath: string) { const workDir = path.dirname(schemaPath); - const pluginModelFiles = glob.sync(path.resolve(__dirname, '../../runtime/src/plugins/**/plugin.zmodel')); - const result = await loadDocument(schemaPath, pluginModelFiles); + const result = await loadDocument(schemaPath); if (!result.success) { throw new Error(`Failed to load schema from ${schemaPath}: ${result.errors}`); } diff --git a/tests/regression/generate.ts b/tests/regression/generate.ts index 86993b96..22959cda 100644 --- a/tests/regression/generate.ts +++ b/tests/regression/generate.ts @@ -18,9 +18,7 @@ async function main() { async function generate(schemaPath: string) { const generator = new TsSchemaGenerator(); const outputDir = path.dirname(schemaPath); - const tsPath = path.join(outputDir, 'schema.ts'); - const pluginModelFiles = glob.sync(path.resolve(dir, '../../packages/runtime/dist/**/plugin.zmodel')); - const result = await loadDocument(schemaPath, pluginModelFiles); + const result = await loadDocument(schemaPath); if (!result.success) { throw new Error(`Failed to load schema from ${schemaPath}: ${result.errors}`); }