Skip to content

Conversation

@ymc9
Copy link
Member

@ymc9 ymc9 commented Jun 28, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a new common helpers package with utility functions for string manipulation, assertions, and object checks.
    • Enhanced the client API with strongly typed CRUD and aggregation methods for each model, including detailed method documentation.
    • Improved the plugin system to support granular query interception per model and operation, with new helper for plugin definition.
    • Added a new workflow for automated version bumping and pull request creation.
    • Added Prettier configuration for consistent code formatting.
  • Bug Fixes

    • Computed fields are now correctly excluded from database schema generation.
  • Refactor

    • Migrated to internal helper utilities and updated development dependencies for improved maintainability.
    • Updated formatting and code style across the codebase for readability and consistency.
    • Removed asynchronous behavior from schema validation for improved performance.
  • Chores

    • Upgraded and reorganized dependencies, scripts, and configurations for better workspace management.
    • Added and updated ESLint configurations for unified linting rules across packages.
  • Documentation

    • Updated and reformatted documentation files for clarity and consistency.
    • Removed outdated or redundant markdown documentation files.

ymc9 and others added 15 commits June 21, 2025 20:53
* chore: set up turborepo

* fix build

* fix

* update lock file
* feat: make plugins compatible with prisma client extensions

* chore: bump version, fix lint

* update
* fix: incorrect computed field typing for optional fields

fixes #33

* fix tests
* chore: remove unused spaces

* chore(sdk): move libraries from v2

* chore: remove some libraries, use local-helpers from sdk

---------

Co-authored-by: Yiming Cao <yiming@whimslab.io>
* chore: remove unused spaces

* chore(sdk): move libraries from v2

* chore: remove some libraries, use local-helpers from sdk

* fix: PR merge issues

* update

* update

* update

* update

---------

Co-authored-by: FTB_lag <tabolskyy.git@gmail.com>
* chore: add script for bumping version

* update lock file

* update packages
@coderabbitai
Copy link

coderabbitai bot commented Jun 28, 2025

Walkthrough

This update introduces a comprehensive refactor and enhancement across the codebase. It adds new utility packages, restructures ESLint and TypeScript configurations, updates build and test workflows, and reformats code for consistency. Major improvements are made to the runtime client contract, plugin system, and CRUD operation typings, with expanded documentation, new scripts, and enhanced package management for better modularity and maintainability.

Changes

File(s) / Area Change Summary
.eslintignore, .eslintrc.json Removed ignore pattern for dist directories and deleted ESLint config.
.github/workflows/build-test.yml, .github/workflows/bump-version.yml Renamed workflow, removed redundant install step, and added a new manual "Bump Version" workflow.
.prettierrc, TODO.md, README.md, NEW-FEATURES.md, packages/ide/vscode/README.md Added Prettier config, reformatted TODO and README, deleted new features and quickstart docs, adjusted markdown formatting.
package.json, packages/*/package.json Updated scripts to use Turbo, added formatting/version bump scripts, updated dependencies, migrated to workspace/internal packages, and incremented versions.
packages/common-helpers/* New package: Added utility functions (isPlainObject, lowerCaseFirst, paramCase, sleep, tiny-invariant, upperCaseFirst), index, ESLint/TS config, and build setup.
packages/eslint-config/* New package: Added shared ESLint config with TypeScript support and rule overrides.
packages/cli/*, packages/create-zenstack/*, packages/ide/vscode/*, packages/language/*, packages/runtime/* Updated ESLint and TypeScript configs to use new internal packages, reformatted code for consistency, and updated imports to new helpers.
packages/language/res/stdlib.zmodel, packages/language/src/*, packages/language/tsconfig.json, packages/language/langium-quickstart.md Reformatted code, removed unused comments, improved consistency, deleted quickstart doc, and updated base TS config.
packages/runtime/src/client/contract.ts, packages/runtime/src/client/client-impl.ts, packages/runtime/src/client/plugin.ts, packages/runtime/src/client/crud-types.ts Major refactor: Expanded client contract with full CRUD/aggregation typings, improved plugin system with per-model/operation hooks, and updated documentation/comments.
packages/runtime/src/client/crud/*, packages/runtime/src/client/functions.ts, packages/runtime/src/client/helpers/schema-db-pusher.ts, packages/runtime/src/client/index.ts Refactored for code style, updated imports to new helpers, improved handling of computed fields, enhanced plugin and error handling, and updated exports.
packages/runtime/src/plugins/policy/* Reformatted code, updated imports, and improved documentation comments.
.vscode/tasks.json Replaced and expanded VSCode tasks for build, lint, and test operations.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Client
    participant Plugin
    participant DB

    User->>Client: Call Model CRUD Operation (e.g., create, findMany)
    Client->>Plugin: (If present) Run onQuery hooks per model/operation
    Plugin-->>Client: Optionally intercept/modify query
    Client->>DB: Execute query (possibly via plugin chain)
    DB-->>Client: Return result
    Client-->>User: Return processed result
Loading
sequenceDiagram
    participant Dev
    participant GitHub Actions

    Dev->>GitHub Actions: Trigger "Bump Version" workflow (manual)
    GitHub Actions->>Repo: Checkout dev branch
    GitHub Actions->>Node: Setup Node.js and pnpm
    GitHub Actions->>Repo: Install dependencies
    GitHub Actions->>Repo: Run custom bump-version script
    GitHub Actions->>Repo: Create PR with version bump
Loading

Possibly related PRs

  • zenstackhq/zenstack-v3#17: Inverse operation on ESLint configuration files; this PR removes .eslintignore and .eslintrc.json while the related PR adds them.
  • zenstackhq/zenstack-v3#22: Both PRs involve changes to client CRUD operations and typings, especially around updateManyAndReturn.
  • zenstackhq/zenstack-v3#9: Both PRs modify BaseCrudDialect and subclasses in the runtime client, specifically concerning dialect capabilities and method declarations.

Poem

A rabbit hopped through code so neat,
Refactored helpers—oh, what a treat!
Plugins now dance with every CRUD,
Type safety blooms where old bugs once stood.
Scripts and configs in harmony sing,
With Turbo and Prettier, what joy they bring!
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/cli/eslint.config.js

Oops! Something went wrong! :(

ESLint: 9.29.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

packages/cli/src/actions/db.ts

Oops! Something went wrong! :(

ESLint: 9.29.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

packages/cli/src/actions/action-utils.ts

Oops! Something went wrong! :(

ESLint: 9.29.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

  • 77 others
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ymc9 ymc9 added this pull request to the merge queue Jun 28, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🔭 Outside diff range comments (11)
packages/language/src/validators/function-invocation-validator.ts (1)

106-127: Validate both missing and extra arguments & point diagnostics to the right node

validateArgs correctly flags missing parameters, but:

  1. The diagnostic is associated with funcDecl, not the offending param, making the error highlight less helpful.
  2. Extra arguments are silently ignored (see the TODO). This can hide bugs in author-supplied code.
-                    accept('error', `missing argument for parameter "${param.name}"`, { node: funcDecl });
+                    accept('error', `missing argument for parameter "${param.name}"`, { node: param });
@@
-        // TODO: do we need to complain for extra arguments?
-        return success;
+        if (args.length > funcDecl.params.length) {
+            accept(
+                'error',
+                'too many arguments supplied to function',
+                { node: args[funcDecl.params.length] }
+            );
+            success = false;
+        }
+        return success;

This keeps authors honest about a function’s arity and produces focused diagnostics.

packages/cli/src/actions/db.ts (1)

23-35: Guard against shell‐injection & quoting issues in runPush

runPush in packages/cli/src/actions/db.ts builds a single command string that’s passed directly into execPackage (which uses child_process.execSync). If prismaSchemaFile contains spaces, quotes or shell metacharacters, the command will break or—worse—open injection vectors.

Recommendations:

• Properly quote/escape the schema path before interpolation:

function quoteArg(arg: string): string {
  return `'${arg.replace(/'/g, `'\\"'`)}'`;
}

const cmd = `prisma db push --schema ${quoteArg(prismaSchemaFile)}${
  options.acceptDataLoss ? ' --accept-data-loss' : ''
}${options.forceReset ? ' --force-reset' : ''} --skip-generate`;

Better: refactor execPackage to accept args: string[] and use child_process.spawnSync (or execFileSync) instead of execSync(command). For example:

// exec-utils.ts
export function execPackage(
  bin: string,
  args: string[],
  options?: SpawnSyncOptions,
) { return spawnSync(bin, args, { stdio: 'inherit', ...options }); }

// db.ts
await execPackage(
  process.versions.bun ? 'bunx' : 'npx',
  [
    'prisma', 'db', 'push',
    '--schema', prismaSchemaFile,
    ...(options.acceptDataLoss ? ['--accept-data-loss'] : []),
    ...(options.forceReset   ? ['--force-reset']    : []),
    '--skip-generate',
  ],
);

• Audit and apply the same fix in packages/cli/src/actions/migrate.ts, where identical patterns exist.

packages/language/src/ast.ts (1)

70-76: Remove the duplicated InheritableNode declaration

The interface is declared twice back-to-back with identical content. Although TypeScript will merge them, it’s redundant noise.

-export interface InheritableNode extends AstNode {
-    $inheritedFrom?: DataModel;
-}
packages/runtime/src/client/crud/operations/aggregate.ts (1)

70-82: Precision loss for Decimal _avg / _sum results

parseFloat converts high-precision decimals to IEEE-754, losing accuracy.
When the Prisma schema field is Decimal, return a Decimal instance instead.

-    .with('Decimal', () => parseFloat(value))
+    .with('Decimal', () => new Decimal(value))
packages/runtime/src/client/crud/operations/delete.ts (1)

15-26: Deletion & read should be wrapped in a single transaction

existing is read first and then the actual DELETE is executed outside of the same DB transaction. A concurrent delete between the two calls can lead to a race where:

  1. existing is found and returned.
  2. Another session deletes the row.
  3. this.delete returns count === 0, causing a second NotFoundError, which masks the real concurrency issue.

Wrapping the read + delete in a single transaction (or using DELETE … RETURNING *) removes the race completely and saves one round‐trip.

-const existing = await this.readUnique(this.kysely, this.model, { ... });
-...
-const result = await this.delete(this.kysely, this.model, args.where, undefined, false);
+const existing = await this.safeTransaction(async (tx) => {
+    const row = await this.readUnique(tx, this.model, { ... });
+    if (!row) throw new NotFoundError(this.model);
+    const { count } = await this.delete(tx, this.model, args.where, undefined, false);
+    if (count === 0) throw new NotFoundError(this.model);
+    return row;
+});
packages/cli/src/actions/generate.ts (1)

50-63: Harden dynamic plugin loading

await import(useProvider) will throw an unhandled exception if the module is missing or has no default export, aborting the whole generation pipeline.

-        const generator = (await import(useProvider)).default as CliGenerator;
-        console.log('Running generator:', provider);
-        await generator({ model, outputPath, tsSchemaFile });
+        let mod: unknown;
+        try {
+            mod = await import(useProvider);
+        } catch (e) {
+            throw new Error(`Failed to load generator "${provider}": ${String(e)}`);
+        }
+
+        const generator = (mod as { default?: unknown }).default;
+        invariant(typeof generator === 'function', `Generator "${provider}" does not export a default function`);
+
+        console.log('Running generator:', provider);
+        await (generator as CliGenerator)({ model, outputPath, tsSchemaFile });

Provides clearer error messages and guards against incorrect plugin shapes.

packages/language/src/utils.ts (1)

464-474: require.resolve will explode under ESM – use createRequire

require is undefined when the package is published as pure ESM.
Consider delegating to import.meta.resolve (Node ≥20) or create a scoped require:

-        const resolvedPath = require.resolve(name, { paths: [cwd] });
+        /* eslint-disable @typescript-eslint/no-var-requires */
+        const { createRequire } = await import('node:module');
+        const resolvedPath = createRequire(import.meta.url).resolve(name, { paths: [cwd] });

This keeps the function working regardless of the module system.

packages/language/src/zmodel-linker.ts (1)

270-278: resolveArray infers element type from index 0 only

If an array literal mixes element types ([1, 'two']) the resolver silently assigns the first element’s type to the whole array. This may hide errors that should be surfaced during validation.

Consider detecting heterogeneous arrays:

-        if (node.items.length > 0) {
-            const itemType = node.items[0]!.$resolvedType;
-            if (itemType?.decl) {
-                this.resolveToBuiltinTypeOrDecl(node, itemType.decl, true);
-            }
-        } else {
+        if (node.items.length === 0) {
             this.resolveToBuiltinTypeOrDecl(node, 'Any', true);
+            return;
+        }
+
+        const first = node.items[0]!.$resolvedType;
+        const homogeneous = node.items.every(
+            (it) => it.$resolvedType?.decl === first?.decl && it.$resolvedType?.array === first?.array,
+        );
+        if (!homogeneous) {
+            // Mark as error – validation pass can surface it
+            this.resolveToBuiltinTypeOrDecl(node, 'Any', true);
+        } else if (first?.decl) {
+            this.resolveToBuiltinTypeOrDecl(node, first.decl, true);
         }
packages/runtime/src/client/crud/dialects/postgresql.ts (1)

289-295: Possible SQL-injection / escaping bug in buildArrayLiteralSQL

String elements are embedded directly into the SQL string with template-interpolation:

return `ARRAY[${values.map((v) => (typeof v === 'string' ? `'${v}'` : v))}]`;

If v contains a single quote or backslash the generated SQL becomes invalid or exploitable.
Prefer parameterised builders or at least pg-format-style escaping.

-import { sql } from 'kysely';
+import { sql } from 'kysely';

...
-return `ARRAY[${values.map((v) => (typeof v === 'string' ? `'${v}'` : v))}]`;
+return sql`ARRAY[${sql.join(values.map((v) => sql`${v}`))}]`.toOperationNode().toString();

(or expose a helper in the dialect that safely serialises each element).

packages/runtime/src/client/crud/operations/base.ts (1)

432-440: Performance: Avoid spread syntax in reduce accumulators.

The spread syntax in .reduce() creates a new object on each iteration, resulting in O(n²) time complexity. Consider using direct property assignment instead.

Apply this pattern to fix the performance issues:

-                            .set(
-                                keyPairs.reduce(
-                                    (acc, { fk, pk }) => ({
-                                        ...acc,
-                                        [fk]: entity[pk],
-                                    }),
-                                    {} as any,
-                                ),
-                            )
+                            .set(
+                                keyPairs.reduce(
+                                    (acc, { fk, pk }) => {
+                                        acc[fk] = entity[pk];
+                                        return acc;
+                                    },
+                                    {} as any,
+                                ),
+                            )

This applies to all similar occurrences in the file at lines 434-438, 1383-1387, 1422-1425, 1545, 1560, 1565, 1627-1629, 1644, and 1661-1663.

Also applies to: 1381-1389, 1420-1427, 1545-1545, 1560-1560, 1565-1565, 1625-1631, 1644-1644, 1659-1666

packages/runtime/src/plugins/policy/policy-handler.ts (1)

86-109: Read-back policy violations are surfaced after the transaction commits

readBackError is populated inside the transaction callback, but the thrown RejectedByPolicyError happens after the callback has resolved.
With both Kysely and most SQL drivers, a callback’s successful resolution commits the transaction.
Consequently, if the read-back check fails the mutation is still persisted even though the client receives an error – an integrity/UX nightmare.

-        const result = await transaction(async (txProceed) => {
+        const result = await transaction(async (txProceed) => {
             …
             if (!this.onlyReturningId(node)) {
                 const readBackResult = await this.processReadBack(node, result, txProceed);
                 if (readBackResult.rows.length !== result.rows.length) {
-                    readBackError = true;
+                    // abort the tx immediately
+                    throw new RejectedByPolicyError(mutationModel, 'result is not allowed to be read back');
                 }
                 return readBackResult;
             } else {
                 return result;
             }
         });
-
-        if (readBackError) {
-            throw new RejectedByPolicyError(mutationModel, 'result is not allowed to be read back');
-        }

Throwing inside the callback guarantees an automatic rollback.
If a rollback is not desired, please clarify the intent and add a compensating mechanism.

♻️ Duplicate comments (2)
packages/create-zenstack/tsconfig.json (1)

2-2: Same note as above – confirm resolver can find the shared config

Nothing else to flag here.
Marking as duplicate of the previous remark.

packages/language/tsconfig.json (1)

2-2: Extends path change – see earlier comment

The comment on ensuring the shared config package is reachable applies here as well.

🧹 Nitpick comments (40)
packages/language/src/constants.ts (1)

16-16: Consider adding a trailing comma and as const for safer, cleaner maintenance

Trailing commas make diffs cleaner when new scalars are appended, and as const preserves literal types:

-export const SCALAR_TYPES = ['String', 'Int', 'Float', 'Decimal', 'BigInt', 'Boolean', 'Bytes', 'DateTime'];
+export const SCALAR_TYPES = [
+    'String',
+    'Int',
+    'Float',
+    'Decimal',
+    'BigInt',
+    'Boolean',
+    'Bytes',
+    'DateTime',
+] as const;
packages/language/src/validators/typedef-validator.ts (1)

24-26: Minor naming nitpick

attr is perfectly valid, but a more specific name (e.g. fieldAttr) would avoid overload with other attr variables in nearby scopes and aid quick grepping.

packages/language/src/validators/attribute-validator.ts (1)

11-12: Shadowed parameter may impair readability

The inner arrow parameter attr shadows the outer method parameter attr. Consider renaming the inner one:

-attr.attributes.forEach((attr) => validateAttributeApplication(attr, accept));
+attr.attributes.forEach((childAttr) => validateAttributeApplication(childAttr, accept));
packages/language/src/validators/enum-validator.ts (1)

19-24: Consistent naming

Same shadowing pattern as in AttributeValidator. Renaming the arrow arg (e.g. enumAttr / fieldAttr) would improve clarity.

packages/ide/vscode/README.md (2)

9-33: Fix markdown list indentation to satisfy MD007

Current four-space indentation trips markdownlint. Two spaces is the conventional level for nested lists:

-    - In case the schema file is not recognized automatically, add the following to your settings.json file:
+  - In case the schema file is not recognized automatically, add the following to your settings.json file:
...
-    - To automatically format on save, add the following to your settings.json file:
+  - To automatically format on save, add the following to your settings.json file:
...
-    - To enable formatting in combination with prettier, add the following to your settings.json file:
+  - To enable formatting in combination with prettier, add the following to your settings.json file:

This satisfies markdownlint (MD007) and keeps the list structure intact.


41-45: Optional: alphabetise external links

Putting the links in alpha order (Blog, Community chat, Documentation, Home, Twitter) makes scanning quicker, but totally discretionary.

packages/language/src/validators/datasource-validator.ts (2)

57-61: Minor readability gain; consider extracting ‘env’ check.

Current predicate is fine, but a helper like isEnvCall(value) could improve reuse across validators.


70-71: Hard-coded literals: candidate for enum/const.

'foreignKeys' | 'prisma' appears in multiple places; elevating to const ALLOWED_RELATION_MODES avoids drift.

packages/language/src/validators/function-invocation-validator.ts (1)

88-96: Reuse immutable data instead of reallocating it

allCasing is recreated on every validation run, even though its value never changes. Hoist it once at module scope:

-            const allCasing = ['original', 'upper', 'lower', 'capitalize', 'uncapitalize'];
+const ALL_CASING = ['original', 'upper', 'lower', 'capitalize', 'uncapitalize'] as const;
...
-            const arg = getLiteral<string>(expr.args[0]?.value);
-            if (arg && !allCasing.includes(arg)) {
+            const arg = getLiteral<string>(expr.args[0]?.value);
+            if (arg && !ALL_CASING.includes(arg)) {

Saves a tiny amount of work in large ASTs and communicates intent (constant set).

packages/common-helpers/tsconfig.json (1)

1-7: Good first cut – consider emitting declaration maps

Since this package will likely be consumed by many others, adding

"declarationMap": true

under compilerOptions improves DX when debugging into the helpers.

packages/common-helpers/src/sleep.ts (1)

1-5: Add explicit return type & optional abort-signal

sleep is tiny but widely reused; a couple of small improvements increase safety:

  1. Declare the return type explicitly for stronger typing.
  2. Accept an optional AbortSignal so callers can cancel the delay (handy in tests and long-running tasks).
-export function sleep(timeout: number) {
-    return new Promise<void>((resolve) => {
-        setTimeout(() => resolve(), timeout);
-    });
+export function sleep(timeout: number, signal?: AbortSignal): Promise<void> {
+    return new Promise<void>((resolve, reject) => {
+        if (signal?.aborted) {
+            return reject(new DOMException('Aborted', 'AbortError'));
+        }
+        const timer = setTimeout(resolve, timeout);
+        signal?.addEventListener(
+            'abort',
+            () => {
+                clearTimeout(timer);
+                reject(new DOMException('Aborted', 'AbortError'));
+            },
+            { once: true }
+        );
+    });
 }

If the extra complexity isn’t necessary right now, at minimum add : Promise<void> to the signature.

.github/workflows/build-test.yml (2)

17-22: Pin the Postgres image version

image: postgres pulls the latest tag on every run, which can introduce breaking changes overnight.
Recommend pinning to an explicit major/minor version (e.g. postgres:15-alpine) to keep CI deterministic.

-                image: postgres
+                image: postgres:15-alpine

30-34: Consider adding the current LTS Node release to the matrix

Only testing 20.x skips the active LTS (18.x) that many users still rely on.
Expanding the matrix costs little thanks to caching and guards against unexpected breakage.

-                node-version: [20.x]
+                node-version: [18.x, 20.x]
packages/runtime/src/plugins/policy/errors.ts (1)

5-10: Minor clarity tweak: use this.model inside the message

Using the parameter shadow (model) works, but referencing the property makes the intent clearer after the parameter is assigned.

-        super(reason ?? `Operation rejected by policy${model ? ': ' + model : ''}`);
+        super(reason ?? `Operation rejected by policy${this.model ? ': ' + this.model : ''}`);
packages/language/src/ast.ts (1)

21-38: Optional: tighten the operator-priority map typing

Using the new TS5 satisfies operator (plus as const) preserves literal keys and prevents drift without widening the type:

-export const BinaryExprOperatorPriority: Record<BinaryExpr['operator'], number> = {
+export const BinaryExprOperatorPriority satisfies Record<BinaryExpr['operator'], number> = {
     /* … */
-};
+} as const;
packages/runtime/eslint.config.js (1)

5-9: Confirm rule override necessity

Disabling @typescript-eslint/no-unused-expressions is common for chai/should style assertions.
Double-check that it’s really required; if not, consider keeping the rule on and adding selective // eslint-disable-next-line comments where necessary to avoid masking genuine issues.

packages/ide/vscode/src/extension/main.ts (1)

49-51: Avoid shadowing the outer client variable

Inside startLanguageClient a const client shadows the module-level let client.
Renaming prevents cognitive overhead and accidental misuse.

-    const client = new LanguageClient('zmodel', 'ZModel', serverOptions, clientOptions);
+    const languageClient = new LanguageClient('zmodel', 'ZModel', serverOptions, clientOptions);
 ...
-    client.start();
-    return client;
+    languageClient.start();
+    return languageClient;
packages/runtime/src/client/result-processor.ts (2)

99-104: Invariant message can mislead – Decimal is filtered out already

The instanceof Decimal guard above guarantees the invariant block never receives a Decimal, yet the error talks about it.
Trim the message to avoid confusion and shorten the critical path:

-invariant(
-    typeof value === 'string' || typeof value === 'number' || value instanceof Decimal,
-    `Expected string, number or Decimal, got ${typeof value}`,
-);
+invariant(
+    typeof value === 'string' || typeof value === 'number',
+    `Expected string or number, got ${typeof value}`,
+);

135-140: Micro-opt: early-exit before reverse traversal

You already calculate the need to reverse. Re-ordering the checks avoids the cost of Array.isArray on non-arrays and short-circuits faster:

-if (Array.isArray(data) && typeof args === 'object' && args && args.take !== undefined && args.take < 0) {
+if (typeof args === 'object' && args?.take !== undefined && args.take < 0 && Array.isArray(data)) {
     data.reverse();
 }
packages/common-helpers/tsup.config.ts (1)

3-13: Consider enabling minification for published bundles

tsup’s default leaves code un-minified. For helpers that ship to npm, minify: true (and maybe target: 'es2019') trims bundle size without affecting DX.

     dts: true,
-    format: ['cjs', 'esm'],
+    format: ['cjs', 'esm'],
+    minify: true,
packages/runtime/src/client/crud/operations/find.ts (1)

18-21: Minor: avoid double allocation for single-row queries

For findUnique / findFirst we read the whole array just to keep [0].
A tiny tweak in the reader to request take 1 and skip array wrapping would save allocations, but it’s purely micro-perf – feel free to ignore.

.vscode/tasks.json (2)

19-30: Background task lacks isBackground & problem-matcher

turbo watch build never exits, so VSCode treats the task as hanging unless isBackground: true and a simple problem-matcher are provided.
Sample patch:

 "type": "shell",
+"isBackground": true,
 "group": {
     "kind": "build"
 },
 "problemMatcher": "$tsc-watch"

31-41: Lint all task could surface diagnostics inline

Attaching the default ESLint matcher lets VSCode underline issues without running the separate Problems panel.

-"problemMatcher": []
+"problemMatcher": ["$eslint-stylish"]
packages/runtime/src/client/crud/operations/delete.ts (1)

32-34: Optional: remove the | undefined in the argument type

validateDeleteManyArgs always returns a defined object (empty object when no args are provided). Therefore runDeleteMany never receives undefined. Narrowing the type simplifies downstream null-checks.

-async runDeleteMany(args: DeleteManyArgs<Schema, Extract<keyof Schema['models'], string>> | undefined) {
+async runDeleteMany(args: DeleteManyArgs<Schema, Extract<keyof Schema['models'], string>>) {
packages/cli/src/utils/version-utils.ts (1)

10-12: Consider logging the error for debugging purposes.

While the simplified error handling works, consider logging the actual error to help with debugging in development environments.

    } catch {
-        return undefined;
+        return undefined;
    }

You could optionally log the error:

-    } catch {
+    } catch (error) {
+        // Optional: log error in development
+        if (process.env.NODE_ENV === 'development') {
+            console.warn('Failed to read package.json version:', error);
+        }
         return undefined;
     }
packages/runtime/src/client/query-utils.ts (2)

160-166: Replace banned Function type with explicit signature

Function is too permissive and flagged by Biome. Narrow the type to the expected callable shape.

-        let computer: Function | undefined;
+        let computer: ((eb: ExpressionBuilder<any, any>) => unknown) | undefined;

205-208: Guard against missing ID fields

getIdFields may return undefined; map will then crash.
Either reuse requireIdFields or add an explicit check.

-    const idFields = getIdFields(schema, model);
-    return idFields.map((f) => ({ [f]: 'asc' }) as OrderBy<Schema, GetModels<Schema>, true, false>);
+    const idFields = requireIdFields(schema, model);
+    return idFields.map((f) => ({ [f]: 'asc' }) as OrderBy<Schema, GetModels<Schema>, true, false>);
packages/runtime/src/plugins/policy/expression-evaluator.ts (1)

40-46: Unsupported call paths throw generic Error

Consider throwing a domain-specific error (QueryError or custom) to keep error handling consistent across the runtime.

packages/runtime/src/client/executor/zenstack-driver.ts (1)

181-186: Time precision fallback may skew latency metrics

Date.now() only has millisecond precision whereas performance.now() provides sub-ms. If high-resolution timing is critical, consider importing perf_hooks’s performance polyfill when running under Node:

export function performanceNow() {
-    if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
-        return performance.now();
-    } else {
-        return Date.now();
-    }
+    if (typeof performance !== 'undefined' && typeof performance.now === 'function')
+        return performance.now();
+    return (await import('node:perf_hooks')).performance.timeOrigin
+        ? (await import('node:perf_hooks')).performance.now()
+        : Date.now();
 }

Optional, depending on accuracy requirements.

packages/language/src/utils.ts (2)

64-67: isFromStdlib is case-sensitive – may mis-detect on Windows / macOS

model.$document.uri.path.endsWith(STD_LIB_MODULE_NAME) will fail if the file-system is case-insensitive and the path casing differs (e.g. stdlib.zmodel vs STD_LIB.zmodel).
A small normalisation avoids surprising false negatives:

-    return !!model && !!model.$document && model.$document.uri.path.endsWith(STD_LIB_MODULE_NAME);
+    return (
+        !!model &&
+        !!model.$document &&
+        model.$document.uri.path.toLowerCase().endsWith(STD_LIB_MODULE_NAME.toLowerCase())
+    );

59-61: Guard against nodes that don’t carry attributes

getAttribute unconditionally casts decl to DataModelAttribute | DataModelFieldAttribute and then accesses .attributes.
If the helper is (mis)used on a declaration that does not own an attributes array this will throw at runtime. A cheap type-guard prevents accidental crashes:

-    return (decl.attributes as (DataModelAttribute | DataModelFieldAttribute)[]).find(
+    if (!('attributes' in decl)) return undefined;
+    return (decl.attributes as (DataModelAttribute | DataModelFieldAttribute)[]).find(
packages/language/src/validators/datamodel-validator.ts (1)

60-62: Grammar nit: “criterion” vs “criteria”

"Model must have at least one unique criteria" – “criteria” is plural; here you’re referring to a single requirement.

-                'Model must have at least one unique criteria. Either mark a single field with `@id`, `@unique` or add a multi field criterion with `@@id([])` or `@@unique([])` to the model.',
+                'Model must have at least one unique criterion. Either mark a single field with `@id`, `@unique`, or add a multi-field criterion with `@@id([])` or `@@unique([])`.',
packages/language/src/validators/attribute-application-validator.ts (1)

342-345: Redundant comparison against dstType === 'Any'

Inside the final else branch dstType is guaranteed to be undefined, so

dstType === 'Any'

is always false and can be removed to reduce cognitive load:

-        return (dstRef?.ref === argResolvedType.decl || dstType === 'Any') && dstIsArray === argResolvedType.array;
+        return dstRef?.ref === argResolvedType.decl && dstIsArray === argResolvedType.array;
README.md (1)

27-34: Bullet markers removed – Markdown list is now plain text

The leading dashes were dropped, so GitHub no longer renders a bullet list. If that was unintentional, restore the markers:

- A modern schema-first ORM ...
+ - A modern schema-first ORM ...

Same applies to the later “Writing ZModel schema” and “Database migration” sections.

packages/runtime/src/plugins/policy/expression-transformer.ts (1)

102-107: Numeric literals are always coerced to Int – widen mapping

_literal maps every non-string/boolean literal to 'Int'.
This silently mis-types floats, bigints, decimals, etc., which will later reach the dialect layer with the wrong Prisma/ZenStack built-in type.

-            typeof expr.value === 'string' ? 'String' : typeof expr.value === 'boolean' ? 'Boolean' : 'Int',
+            typeof expr.value === 'string'
+                ? 'String'
+                : typeof expr.value === 'boolean'
+                  ? 'Boolean'
+                  : Number.isInteger(expr.value)
+                    ? 'Int'
+                    : 'Float',

(You could further refine for bigint, Decimal, etc.)
Leaving it as-is risks precision loss and wrong query plans when the DB column is non-integer.

packages/runtime/src/client/crud/dialects/postgresql.ts (1)

197-205: Minor: replace flatMap(...=>v) with plain flat()

Both locations build an array of tuples and immediately flatten it:

...map(...).flatMap((v) => v)

Using flat() is clearer and avoids an unnecessary callback.

-    .map(/* ... */).flatMap((v) => v)
+    .map(/* ... */).flat()

Also applies to: 222-225

packages/language/src/zmodel-workspace-manager.ts (1)

48-52: Use a logger instead of console.log inside library code

Raw console.log calls pollute user terminals and cannot be silenced/redirected easily.
Consider injecting a logger or using VS Code’s output channel so consumers can control verbosity.

packages/runtime/src/client/client-impl.ts (1)

271-291: Enhanced plugin architecture looks good.

The nested plugin hook structure with model and operation-specific handlers provides better granularity. The case-insensitive model matching and wildcard support ($allModels, $allOperations) are useful additions.

Would you like me to generate documentation or examples for the new plugin hook structure to help developers understand how to use these enhanced capabilities?

packages/runtime/src/plugins/policy/policy-handler.ts (1)

178-205: Possible SQL-injection when serialising array literals

unwrapCreateValueRow injects user-provided arrays via RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)).
Unless buildArrayLiteralSQL fully escapes every element, malicious content could reach the DB un-parametrised.

Consider:

-                        node: RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
+                        node: ValueNode.create(
+                            this.dialect.transformPrimitive(
+                                value,
+                                fieldDef.type as BuiltinType,
+                            ),
+                        ),

or add a dedicated, well-tested helper that returns a ValueNode/bind parameter rather than raw SQL.

packages/runtime/src/client/crud-types.ts (1)

895-901: Avoid banned {} type – replace with a shaped object

Biome flags the use of {} (lines 898-900).
A safer, more precise alternative keeps the intent (object ≠ true) while silencing the linter:

-type AggCommonOutput<Input> = Input extends true
-    ? number
-    : Input extends {}
+type AggCommonOutput<Input> = Input extends true
+    ? number
+    : Input extends Record<string, unknown>
         ? {
             [Key in keyof Input]: number;
         }
         : never;

This keeps the conditional logic unchanged and eliminates the noBannedTypes error.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2db2993 and c7e9057.

⛔ Files ignored due to path filters (4)
  • packages/language/src/generated/ast.ts is excluded by !**/generated/**
  • packages/language/src/generated/grammar.ts is excluded by !**/generated/**
  • packages/language/src/generated/module.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (107)
  • .eslintignore (0 hunks)
  • .eslintrc.json (0 hunks)
  • .github/workflows/build-test.yml (1 hunks)
  • .github/workflows/bump-version.yml (1 hunks)
  • .prettierrc (1 hunks)
  • .vscode/tasks.json (1 hunks)
  • NEW-FEATURES.md (0 hunks)
  • README.md (4 hunks)
  • TODO.md (1 hunks)
  • package.json (2 hunks)
  • packages/cli/eslint.config.js (1 hunks)
  • packages/cli/package.json (2 hunks)
  • packages/cli/src/actions/action-utils.ts (2 hunks)
  • packages/cli/src/actions/db.ts (1 hunks)
  • packages/cli/src/actions/generate.ts (2 hunks)
  • packages/cli/src/actions/info.ts (4 hunks)
  • packages/cli/src/actions/init.ts (2 hunks)
  • packages/cli/src/actions/migrate.ts (2 hunks)
  • packages/cli/src/index.ts (3 hunks)
  • packages/cli/src/utils/exec-utils.ts (2 hunks)
  • packages/cli/src/utils/version-utils.ts (1 hunks)
  • packages/cli/tsconfig.json (1 hunks)
  • packages/common-helpers/eslint.config.js (1 hunks)
  • packages/common-helpers/package.json (1 hunks)
  • packages/common-helpers/src/index.ts (1 hunks)
  • packages/common-helpers/src/is-plain-object.ts (1 hunks)
  • packages/common-helpers/src/lower-case-first.ts (1 hunks)
  • packages/common-helpers/src/param-case.ts (1 hunks)
  • packages/common-helpers/src/sleep.ts (1 hunks)
  • packages/common-helpers/src/tiny-invariant.ts (1 hunks)
  • packages/common-helpers/src/upper-case-first.ts (1 hunks)
  • packages/common-helpers/tsconfig.json (1 hunks)
  • packages/common-helpers/tsup.config.ts (1 hunks)
  • packages/create-zenstack/eslint.config.js (1 hunks)
  • packages/create-zenstack/package.json (2 hunks)
  • packages/create-zenstack/src/index.ts (3 hunks)
  • packages/create-zenstack/tsconfig.json (1 hunks)
  • packages/eslint-config/base.js (1 hunks)
  • packages/eslint-config/package.json (1 hunks)
  • packages/ide/vscode/README.md (1 hunks)
  • packages/ide/vscode/eslint.config.mjs (1 hunks)
  • packages/ide/vscode/package.json (1 hunks)
  • packages/ide/vscode/src/extension/main.ts (3 hunks)
  • packages/ide/vscode/src/language-server/main.ts (1 hunks)
  • packages/ide/vscode/tsconfig.json (1 hunks)
  • packages/language/eslint.config.js (1 hunks)
  • packages/language/langium-quickstart.md (0 hunks)
  • packages/language/package.json (2 hunks)
  • packages/language/res/stdlib.zmodel (7 hunks)
  • packages/language/src/ast.ts (2 hunks)
  • packages/language/src/constants.ts (1 hunks)
  • packages/language/src/index.ts (2 hunks)
  • packages/language/src/module.ts (4 hunks)
  • packages/language/src/utils.ts (24 hunks)
  • packages/language/src/validator.ts (3 hunks)
  • packages/language/src/validators/attribute-application-validator.ts (15 hunks)
  • packages/language/src/validators/attribute-validator.ts (1 hunks)
  • packages/language/src/validators/common.ts (2 hunks)
  • packages/language/src/validators/datamodel-validator.ts (17 hunks)
  • packages/language/src/validators/datasource-validator.ts (3 hunks)
  • packages/language/src/validators/enum-validator.ts (1 hunks)
  • packages/language/src/validators/expression-validator.ts (8 hunks)
  • packages/language/src/validators/function-decl-validator.ts (1 hunks)
  • packages/language/src/validators/function-invocation-validator.ts (13 hunks)
  • packages/language/src/validators/schema-validator.ts (3 hunks)
  • packages/language/src/validators/typedef-validator.ts (1 hunks)
  • packages/language/src/zmodel-linker.ts (19 hunks)
  • packages/language/src/zmodel-scope.ts (8 hunks)
  • packages/language/src/zmodel-workspace-manager.ts (3 hunks)
  • packages/language/src/zmodel.langium (3 hunks)
  • packages/language/tsconfig.json (1 hunks)
  • packages/runtime/eslint.config.js (1 hunks)
  • packages/runtime/package.json (3 hunks)
  • packages/runtime/src/client/client-impl.ts (17 hunks)
  • packages/runtime/src/client/contract.ts (3 hunks)
  • packages/runtime/src/client/crud-types.ts (34 hunks)
  • packages/runtime/src/client/crud/dialects/base.ts (40 hunks)
  • packages/runtime/src/client/crud/dialects/index.ts (1 hunks)
  • packages/runtime/src/client/crud/dialects/postgresql.ts (15 hunks)
  • packages/runtime/src/client/crud/dialects/sqlite.ts (11 hunks)
  • packages/runtime/src/client/crud/operations/aggregate.ts (5 hunks)
  • packages/runtime/src/client/crud/operations/base.ts (80 hunks)
  • packages/runtime/src/client/crud/operations/count.ts (2 hunks)
  • packages/runtime/src/client/crud/operations/create.ts (3 hunks)
  • packages/runtime/src/client/crud/operations/delete.ts (2 hunks)
  • packages/runtime/src/client/crud/operations/find.ts (1 hunks)
  • packages/runtime/src/client/crud/operations/group-by.ts (5 hunks)
  • packages/runtime/src/client/crud/operations/update.ts (3 hunks)
  • packages/runtime/src/client/crud/validator.ts (46 hunks)
  • packages/runtime/src/client/executor/name-mapper.ts (11 hunks)
  • packages/runtime/src/client/executor/zenstack-driver.ts (8 hunks)
  • packages/runtime/src/client/executor/zenstack-query-executor.ts (17 hunks)
  • packages/runtime/src/client/functions.ts (11 hunks)
  • packages/runtime/src/client/helpers/schema-db-pusher.ts (9 hunks)
  • packages/runtime/src/client/index.ts (1 hunks)
  • packages/runtime/src/client/options.ts (4 hunks)
  • packages/runtime/src/client/plugin.ts (5 hunks)
  • packages/runtime/src/client/promise.ts (1 hunks)
  • packages/runtime/src/client/query-builder.ts (2 hunks)
  • packages/runtime/src/client/query-utils.ts (12 hunks)
  • packages/runtime/src/client/result-processor.ts (7 hunks)
  • packages/runtime/src/plugins/policy/errors.ts (1 hunks)
  • packages/runtime/src/plugins/policy/expression-evaluator.ts (5 hunks)
  • packages/runtime/src/plugins/policy/expression-transformer.ts (20 hunks)
  • packages/runtime/src/plugins/policy/plugin.ts (2 hunks)
  • packages/runtime/src/plugins/policy/plugin.zmodel (2 hunks)
  • packages/runtime/src/plugins/policy/policy-handler.ts (22 hunks)
⛔ Files not processed due to max files limit (18)
  • packages/runtime/src/plugins/policy/utils.ts
  • packages/runtime/src/schema/auth.ts
  • packages/runtime/src/schema/expression.ts
  • packages/runtime/src/utils/clone.ts
  • packages/runtime/src/utils/default-operation-node-visitor.ts
  • packages/runtime/src/utils/object-utils.ts
  • packages/runtime/src/utils/type-utils.ts
  • packages/runtime/test/client-api/aggregate.test.ts
  • packages/runtime/test/client-api/client-specs.ts
  • packages/runtime/test/client-api/compound-id.test.ts
  • packages/runtime/test/client-api/computed-fields.test.ts
  • packages/runtime/test/client-api/count.test.ts
  • packages/runtime/test/client-api/create-many-and-return.test.ts
  • packages/runtime/test/client-api/create-many.test.ts
  • packages/runtime/test/client-api/create.test.ts
  • packages/runtime/test/client-api/default-values.test.ts
  • packages/runtime/test/client-api/delete-many.test.ts
  • packages/runtime/test/client-api/delete.test.ts
💤 Files with no reviewable changes (4)
  • NEW-FEATURES.md
  • .eslintignore
  • packages/language/langium-quickstart.md
  • .eslintrc.json
🧰 Additional context used
🧬 Code Graph Analysis (34)
packages/language/src/validators/attribute-validator.ts (1)
packages/language/src/validators/attribute-application-validator.ts (1)
  • validateAttributeApplication (406-408)
packages/language/src/validators/enum-validator.ts (2)
packages/language/src/validators/attribute-application-validator.ts (1)
  • validateAttributeApplication (406-408)
packages/language/src/generated/ast.ts (2)
  • EnumField (444-450)
  • EnumField (452-452)
packages/language/src/ast.ts (1)
packages/language/src/generated/ast.ts (2)
  • BinaryExpr (264-270)
  • BinaryExpr (272-272)
packages/language/src/validators/datasource-validator.ts (2)
packages/language/src/constants.ts (1)
  • SUPPORTED_PROVIDERS (4-11)
packages/language/src/generated/ast.ts (1)
  • isInvocationExpr (550-552)
packages/cli/src/actions/init.ts (2)
packages/cli/src/cli-error.ts (1)
  • CliError (4-4)
packages/cli/src/actions/templates.ts (1)
  • STARTER_ZMODEL (1-27)
packages/runtime/src/client/crud/dialects/index.ts (1)
packages/runtime/src/client/options.ts (1)
  • ClientOptions (30-65)
packages/language/src/validators/function-decl-validator.ts (3)
packages/language/src/validators/common.ts (1)
  • AstValidator (7-12)
packages/language/src/generated/ast.ts (2)
  • FunctionDecl (471-479)
  • FunctionDecl (481-481)
packages/language/src/validators/attribute-application-validator.ts (1)
  • validateAttributeApplication (406-408)
packages/language/src/validators/common.ts (2)
packages/language/src/ast.ts (2)
  • AstNode (4-4)
  • AstNode (79-84)
packages/language/src/generated/ast.ts (1)
  • isDataModelField (383-385)
packages/language/src/validators/typedef-validator.ts (2)
packages/language/src/validators/attribute-application-validator.ts (1)
  • validateAttributeApplication (406-408)
packages/language/src/generated/ast.ts (4)
  • TypeDef (733-740)
  • TypeDef (742-742)
  • TypeDefField (748-755)
  • TypeDefField (757-757)
packages/runtime/src/plugins/policy/plugin.ts (2)
packages/sdk/src/schema/schema.ts (1)
  • SchemaDef (10-17)
packages/runtime/src/client/plugin.ts (2)
  • RuntimePlugin (108-155)
  • OnKyselyQueryArgs (94-101)
packages/runtime/src/client/result-processor.ts (1)
packages/sdk/src/schema/schema.ts (1)
  • GetModels (84-84)
packages/runtime/src/client/crud/operations/aggregate.ts (1)
packages/sdk/src/schema/schema.ts (1)
  • SchemaDef (10-17)
packages/runtime/src/client/executor/name-mapper.ts (3)
packages/runtime/src/client/errors.ts (1)
  • InternalError (7-11)
packages/runtime/src/client/crud/operations/base.ts (1)
  • requireModel (109-111)
packages/runtime/src/client/query-utils.ts (1)
  • requireModel (17-23)
packages/runtime/src/client/crud/operations/count.ts (1)
packages/sdk/src/schema/schema.ts (1)
  • SchemaDef (10-17)
packages/language/src/module.ts (1)
packages/language/src/generated/module.ts (2)
  • ZModelGeneratedSharedModule (17-19)
  • ZModelGeneratedModule (21-25)
packages/runtime/src/client/crud/operations/find.ts (3)
packages/sdk/src/schema/schema.ts (2)
  • SchemaDef (10-17)
  • GetModels (84-84)
packages/runtime/src/client/crud/operations/base.ts (1)
  • CrudOperation (52-67)
packages/runtime/src/client/crud-types.ts (1)
  • FindArgs (495-514)
packages/runtime/src/client/query-utils.ts (5)
packages/runtime/test/test-schema.ts (1)
  • schema (3-310)
samples/blog/zenstack/schema.ts (1)
  • schema (9-166)
packages/sdk/src/schema/schema.ts (2)
  • SchemaDef (10-17)
  • GetModels (84-84)
packages/runtime/src/client/errors.ts (2)
  • InternalError (7-11)
  • QueryError (1-5)
packages/runtime/src/client/crud-types.ts (1)
  • OrderBy (260-294)
packages/runtime/src/client/crud/operations/delete.ts (3)
packages/sdk/src/schema/schema.ts (1)
  • SchemaDef (10-17)
packages/runtime/src/client/crud-types.ts (2)
  • DeleteArgs (786-791)
  • DeleteManyArgs (793-796)
packages/runtime/src/client/errors.ts (1)
  • NotFoundError (13-17)
packages/language/src/validators/expression-validator.ts (3)
packages/language/src/generated/ast.ts (7)
  • isEnum (440-442)
  • isNullExpr (599-601)
  • isThisExpr (729-731)
  • BinaryExpr (264-270)
  • BinaryExpr (272-272)
  • isDataModelAttribute (368-370)
  • isArrayExpr (201-203)
packages/runtime/src/client/query-utils.ts (1)
  • isEnum (177-179)
packages/language/src/utils.ts (3)
  • isDataModelFieldReference (142-144)
  • typeAssignable (85-103)
  • findUpAst (284-293)
packages/language/src/index.ts (1)
packages/language/src/constants.ts (1)
  • STD_LIB_MODULE_NAME (21-21)
packages/language/src/validators/datamodel-validator.ts (4)
packages/language/src/constants.ts (1)
  • SCALAR_TYPES (16-16)
packages/language/src/generated/ast.ts (8)
  • isEnum (440-442)
  • DataModelField (372-379)
  • DataModelField (381-381)
  • isStringLiteral (717-719)
  • isDataModel (355-357)
  • isTypeDef (744-746)
  • DataModel (341-351)
  • DataModel (353-353)
packages/language/src/validators/attribute-application-validator.ts (1)
  • validateAttributeApplication (406-408)
packages/language/src/utils.ts (4)
  • hasAttribute (54-56)
  • getModelFieldsWithBases (184-190)
  • findUpInheritance (171-182)
  • isDelegateModel (157-159)
packages/cli/src/actions/generate.ts (2)
packages/language/src/generated/ast.ts (1)
  • isPlugin (636-638)
packages/common-helpers/src/tiny-invariant.ts (1)
  • invariant (4-14)
packages/language/src/validators/attribute-application-validator.ts (3)
packages/language/src/generated/ast.ts (25)
  • DataModelAttribute (359-364)
  • DataModelAttribute (366-366)
  • DataModelFieldAttribute (387-392)
  • DataModelFieldAttribute (394-394)
  • InternalAttribute (528-533)
  • InternalAttribute (535-535)
  • isDataModelField (383-385)
  • isTypeDefField (759-761)
  • isTypeDef (744-746)
  • AttributeParam (233-241)
  • AttributeParam (243-243)
  • isDataModel (355-357)
  • DataModelField (372-379)
  • DataModelField (381-381)
  • AttributeArg (220-225)
  • AttributeArg (227-227)
  • isArrayExpr (201-203)
  • isReferenceExpr (705-707)
  • isEnum (440-442)
  • Attribute (205-212)
  • Attribute (214-214)
  • ArrayExpr (193-197)
  • ArrayExpr (199-199)
  • ReferenceExpr (696-701)
  • ReferenceExpr (703-703)
packages/language/src/validators/common.ts (1)
  • AstValidator (7-12)
packages/language/src/utils.ts (8)
  • hasAttribute (54-56)
  • getStringLiteral (76-78)
  • isFutureExpr (153-155)
  • isDataModelFieldReference (142-144)
  • isDelegateModel (157-159)
  • resolved (161-166)
  • mapBuiltinTypeToExpressionType (108-132)
  • typeAssignable (85-103)
packages/runtime/src/client/query-builder.ts (2)
packages/sdk/src/schema/schema.ts (8)
  • SchemaDef (10-17)
  • GetModels (84-84)
  • ScalarFields (109-123)
  • ForeignKeyFields (125-129)
  • GetFields (92-95)
  • GetFieldType (103-107)
  • FieldIsOptional (151-155)
  • FieldHasDefault (175-183)
packages/runtime/src/utils/type-utils.ts (1)
  • MapBaseType (15-29)
packages/runtime/src/plugins/policy/expression-evaluator.ts (3)
packages/runtime/src/schema/expression.ts (1)
  • ExpressionUtils (16-112)
packages/sdk/src/schema/expression.ts (6)
  • CallExpression (46-50)
  • UnaryExpression (33-37)
  • MemberExpression (27-31)
  • FieldExpression (22-25)
  • ArrayExpression (17-20)
  • BinaryExpression (39-44)
packages/common-helpers/src/tiny-invariant.ts (1)
  • invariant (4-14)
packages/language/src/zmodel-workspace-manager.ts (1)
packages/language/src/constants.ts (1)
  • STD_LIB_MODULE_NAME (21-21)
packages/runtime/src/client/crud/operations/group-by.ts (1)
packages/sdk/src/schema/schema.ts (1)
  • SchemaDef (10-17)
packages/language/src/validator.ts (8)
packages/language/src/generated/ast.ts (12)
  • Model (567-571)
  • Model (573-573)
  • DataSource (416-421)
  • DataSource (423-423)
  • DataModel (341-351)
  • DataModel (353-353)
  • Expression (115-115)
  • Expression (117-117)
  • InvocationExpr (541-546)
  • InvocationExpr (548-548)
  • FunctionDecl (471-479)
  • FunctionDecl (481-481)
packages/language/src/validators/schema-validator.ts (1)
  • SchemaValidator (10-58)
packages/language/src/validators/datasource-validator.ts (1)
  • DataSourceValidator (10-74)
packages/language/src/validators/datamodel-validator.ts (1)
  • DataModelValidator (32-464)
packages/language/src/validators/attribute-validator.ts (1)
  • AttributeValidator (9-13)
packages/language/src/validators/expression-validator.ts (1)
  • ExpressionValidator (30-262)
packages/language/src/validators/function-invocation-validator.ts (1)
  • FunctionInvocationValidator (42-249)
packages/language/src/validators/function-decl-validator.ts (1)
  • FunctionDeclValidator (9-13)
packages/language/src/validators/schema-validator.ts (3)
packages/language/src/generated/ast.ts (3)
  • Model (567-571)
  • Model (573-573)
  • isDataSource (425-427)
packages/language/src/validators/common.ts (1)
  • validateDuplicatedDeclarations (17-43)
packages/language/src/utils.ts (3)
  • resolveTransitiveImports (390-392)
  • getAllDeclarationsIncludingImports (488-491)
  • resolveImport (421-439)
packages/runtime/src/client/functions.ts (4)
packages/runtime/src/client/options.ts (2)
  • ZModelFunction (21-25)
  • ZModelFunctionContext (15-19)
packages/common-helpers/src/tiny-invariant.ts (1)
  • invariant (4-14)
packages/common-helpers/src/upper-case-first.ts (1)
  • upperCaseFirst (1-3)
packages/common-helpers/src/lower-case-first.ts (1)
  • lowerCaseFirst (1-3)
packages/runtime/src/client/plugin.ts (4)
packages/sdk/src/schema/schema.ts (2)
  • SchemaDef (10-17)
  • GetModels (84-84)
packages/runtime/src/client/contract.ts (2)
  • ClientContract (35-95)
  • ModelOperations (146-698)
packages/runtime/src/utils/type-utils.ts (1)
  • MaybePromise (64-64)
packages/runtime/src/client/crud/operations/base.ts (1)
  • CrudOperation (52-67)
packages/runtime/src/client/crud-types.ts (5)
packages/sdk/src/schema/schema.ts (22)
  • SchemaDef (10-17)
  • GetModels (84-84)
  • NonRelationFields (131-133)
  • RelationFields (135-137)
  • RelationFieldType (145-149)
  • FieldIsArray (163-167)
  • FieldIsOptional (151-155)
  • GetFieldType (103-107)
  • GetEnums (88-88)
  • BuiltinType (76-76)
  • GetModel (86-86)
  • FieldDef (54-66)
  • GetFields (92-95)
  • GetField (97-101)
  • GetEnum (90-90)
  • FieldHasDefault (175-183)
  • FieldIsRelationArray (185-189)
  • FieldType (139-143)
  • RelationInfo (45-52)
  • ScalarFields (109-123)
  • ForeignKeyFields (125-129)
  • FieldIsRelation (157-161)
packages/language/src/generated/ast.ts (3)
  • Model (567-571)
  • Model (573-573)
  • BuiltinType (101-101)
packages/sdk/src/prisma/prisma-builder.ts (1)
  • Model (115-164)
packages/runtime/src/utils/type-utils.ts (8)
  • Optional (3-3)
  • WrapType (9-13)
  • OrArray (42-42)
  • AtLeast (52-56)
  • MapBaseType (15-29)
  • XOR (60-60)
  • NonEmptyArray (44-44)
  • ValueOfPotentialTuple (46-46)
packages/runtime/src/client/query-builder.ts (2)
  • ToKyselySchema (14-16)
  • MapBaseType (28-40)
packages/runtime/src/client/crud/operations/create.ts (3)
packages/runtime/src/client/query-utils.ts (1)
  • getIdValues (141-147)
packages/runtime/src/client/crud-types.ts (2)
  • WhereInput (158-183)
  • CreateManyAndReturnArgs (536-542)
packages/runtime/src/plugins/policy/errors.ts (1)
  • RejectedByPolicyError (4-11)
packages/language/src/zmodel-scope.ts (3)
packages/language/src/generated/ast.ts (8)
  • isEnumField (454-456)
  • isModel (575-577)
  • isMemberAccessExpr (563-565)
  • isReferenceExpr (705-707)
  • isDataModelField (383-385)
  • isTypeDefField (759-761)
  • isInvocationExpr (550-552)
  • isDataModel (355-357)
packages/language/src/utils.ts (3)
  • resolveImportUri (441-462)
  • getModelFieldsWithBases (184-190)
  • isCollectionPredicate (505-507)
packages/language/src/constants.ts (2)
  • STD_LIB_MODULE_NAME (21-21)
  • PLUGIN_MODULE_NAME (26-26)
🪛 LanguageTool
packages/ide/vscode/README.md

[uncategorized] ~19-~19: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ... "*.zmodel": "zmodel" }, ``` - Auto formatting - To automatically format on save,...

(AUTO_HYPHEN)

🪛 markdownlint-cli2 (0.17.2)
packages/ide/vscode/README.md

11-11: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


21-21: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


27-27: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)

🪛 Biome (1.9.4)
packages/runtime/src/client/executor/name-mapper.ts

[error] 160-160: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

packages/runtime/src/client/crud/dialects/sqlite.ts

[error] 143-150: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)


[error] 155-175: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)

packages/runtime/src/client/query-utils.ts

[error] 146-146: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 161-161: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

packages/language/src/validators/datamodel-validator.ts

[error] 411-411: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/runtime/src/client/crud-types.ts

[error] 109-109: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 383-383: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

packages/runtime/src/client/client-impl.ts

[error] 110-110: The constructor should not return a value.

The constructor is here:

Returning a value from a constructor is ignored.

(lint/correctness/noConstructorReturn)

packages/runtime/src/plugins/policy/expression-transformer.ts

[error] 109-109: This parameter is used before its declaration.

The parameter is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 115-115: This parameter is used before its declaration.

The parameter is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 281-281: This parameter is used before its declaration.

The parameter is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)

packages/runtime/src/client/crud/dialects/postgresql.ts

[error] 197-204: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)


[error] 222-225: Avoid unnecessary callback in flatMap call.

You can just use flat to flatten the array.
Safe fix: Replace unnecessary flatMap call to flat instead.

(lint/correctness/noFlatMapIdentity)

packages/runtime/src/client/contract.ts

[error] 120-120: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

packages/runtime/src/client/crud/operations/base.ts

[error] 1405-1405: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 1545-1545: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 1565-1565: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 1627-1627: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 1644-1644: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-test (20.x)
🔇 Additional comments (96)
packages/runtime/src/client/crud/dialects/index.ts (1)

8-11: Consistent trailing-comma style looks good

The only modification is the trailing comma after options, aligning this file with the updated lint/format rules. No functional impact detected. 👍

packages/language/src/validators/typedef-validator.ts (1)

17-17: LGTM – readability improved

Switching to single-line arrows keeps the validator concise without altering behaviour.

packages/cli/src/actions/action-utils.ts (2)

20-21: LGTM – purely cosmetic change.

Message text and runtime semantics remain untouched; adding the trailing comma is innocuous.


38-38: Single-line if keeps semantics intact.

No objection—readability is arguably better and branch count unchanged.

packages/language/src/validators/function-decl-validator.ts (1)

9-12: No functional delta – validator still iterates attributes.

The arrow-function re-format is safe; no side-effects introduced.

packages/language/res/stdlib.zmodel (1)

28-28: Whitespace / comment tidy-up acknowledged.

All edits are confined to comments or blank lines; DSL semantics stay intact.

Also applies to: 106-110, 174-180, 189-195, 526-533, 541-543, 666-671

packages/language/src/zmodel.langium (1)

167-168: Whitespace trim only – grammar unaffected.

No grammar tokens or rules changed; parser behavior unchanged.

Also applies to: 196-198, 211-212

packages/language/src/validators/datasource-validator.ts (1)

35-38: String interpolation compacted – still correct.

Template-literal remains accurate; no injection vectors introduced.

TODO.md (1)

1-83: LGTM! Well-organized TODO list updates.

The reformatting improves readability with consistent indentation, and the new TODO items reflect ongoing development priorities. Moving "Prisma client extension" to completed status aligns with recent runtime enhancements.

packages/common-helpers/src/tiny-invariant.ts (1)

1-14: LGTM! Clean invariant utility implementation.

The implementation correctly handles environment-based error messaging and uses TypeScript's asserts keyword appropriately for type narrowing. This provides a good internal replacement for the external tiny-invariant dependency.

packages/language/src/validators/common.ts (1)

20-32: LGTM! Clean stylistic improvements.

The formatting changes improve code consistency with trailing commas and single-line expressions while preserving the validation logic.

packages/language/src/validators/expression-validator.ts (1)

39-259: LGTM! Consistent formatting improvements.

The stylistic changes improve code readability by consolidating multiline statements and removing redundant syntax while preserving all validation logic and error handling.

packages/eslint-config/base.js (1)

1-17: LGTM! Well-structured shared ESLint configuration.

The configuration follows current best practices with the flat config format and makes reasonable rule customizations for a TypeScript monorepo. The rule choices balance code quality with development practicality.

.prettierrc (1)

1-6: Prettier baseline looks good

The new configuration matches the formatting observed in the refactor. No concerns.

packages/ide/vscode/src/language-server/main.ts (1)

4-4: Import consolidation LGTM

Purely stylistic; logic untouched.

packages/eslint-config/package.json (1)

1-7: Config package scaffold accepted

Package json is minimal yet sufficient for an internal shared config.

packages/ide/vscode/tsconfig.json (1)

2-2: Centralized base config looks good – double-check package availability

Migrating to @zenstackhq/typescript-config/base.json is beneficial for consistency.
Please verify that the package is published/linked in every CI job and local dev environment; otherwise tsc will fail to resolve the path.

packages/runtime/src/plugins/policy/plugin.zmodel (1)

3-3: Formatting cleanup looks good

Whitespace normalisation improves doc-comment readability without altering semantics.

packages/create-zenstack/src/index.ts (1)

60-61: LGTM on JSON formatting

Trailing-comma adjustment matches Prettier defaults and reduces future diff noise.

Also applies to: 95-96

packages/cli/tsconfig.json (1)

2-2: Config update approved

Extending the shared base config promotes consistency across packages.

packages/cli/eslint.config.js (1)

1-4: LGTM! Clean implementation of centralized ESLint config.

The implementation correctly imports and re-exports the base ESLint configuration with proper type annotation. This aligns well with the centralized configuration strategy across packages.

packages/runtime/src/client/index.ts (1)

6-6: LGTM! Appropriate addition to the public API.

The export of definePlugin enhances the plugin system's public interface and follows the established export pattern in this module.

packages/common-helpers/eslint.config.js (1)

1-4: LGTM! Consistent with centralized ESLint configuration strategy.

The implementation correctly follows the same pattern as other packages, importing and re-exporting the base ESLint configuration with proper type annotation.

packages/common-helpers/src/lower-case-first.ts (1)

1-3: LGTM! Simple and correct utility implementation.

The function correctly converts the first character to lowercase. The implementation handles edge cases appropriately (empty strings return empty strings).

packages/common-helpers/src/upper-case-first.ts (1)

1-3: LGTM! Consistent and correct utility implementation.

The function correctly converts the first character to uppercase and follows the same implementation pattern as lowerCaseFirst, ensuring consistency across the utility functions.

packages/ide/vscode/eslint.config.mjs (1)

1-4: Minimal pass-through config looks good

Straightforward re-export of the shared ESLint rules for the VS Code extension. No concerns.

packages/create-zenstack/package.json (1)

38-40: Good move to workspace-scoped tooling deps

Picking up @zenstackhq/*-config via workspace:* keeps the versions in lock-step across the monorepo. 👍

packages/runtime/src/plugins/policy/plugin.ts (1)

18-21: Method signature complies with the runtime contract

onKyselyQuery forwards to PolicyHandler and returns its promise; interface implementation still matches. No action needed.

packages/create-zenstack/eslint.config.js (1)

1-4: Consistent lint config re-export – looks good

Matches the pattern used in other packages; keeps tooling simple.

packages/language/eslint.config.js (1)

5-7: ESLint flat-config looks good

tseslint.config is used correctly and ignores is the proper top-level key for flat configs in ESLint ≥ 9. No issues spotted.

packages/common-helpers/src/index.ts (1)

1-6: Simple barrel file – LGTM

Straightforward re-exports with no side effects; fine as-is.

packages/language/package.json (1)

46-49: Verify the custom version specifier catalog:

The catalog: protocol isn’t recognised by npm/Yarn/PNPM out of the box.
Ensure your package-manager plugins (e.g. pnpm-catalog) are present in CI/CD; otherwise installs will fail.

"langium": "catalog:",
"ts-pattern": "catalog:"

If this is intentional and tooling is in place, feel free to ignore.

packages/runtime/src/client/promise.ts (1)

5-6: LGTM – signature compression only

No behavioural change; the promise remains a thenable and still satisfies PromiseLike<T>.

packages/cli/src/actions/migrate.ts (1)

55-58: runReset expects { force: boolean } but receives CommonOptions

runReset(prismaSchemaFile, options as any) bypasses type-checking and may send undefined for force, resulting in the CLI prompting the user even when --force was intended.

Consider narrowing the type upfront:

-case 'reset':
-    await runReset(prismaSchemaFile, options as any);
+case 'reset': {
+    const { force = false } = options as { force?: boolean };
+    await runReset(prismaSchemaFile, { force });
+    break;
+}

Likely an incorrect or invalid review comment.

packages/runtime/src/client/crud/operations/update.ts (1)

41-43: Confirm policy checks for updateMany

Unlike runUpdate / runUpsert, the runUpdateMany path doesn’t re-check the read result against policies. If policies can deny reading updated rows, this could leak data through the count returned here.

If policy enforcement is handled elsewhere, please ignore; otherwise consider mirroring the pattern used in runUpdate.

packages/cli/src/utils/version-utils.ts (2)

1-3: Good modernization to native Node.js modules.

The migration from require to native Node.js modules (fs, path, url) is a positive improvement for future compatibility and consistency.


7-9: Isomorphic __dirname implementation is correct.

The implementation correctly handles both CommonJS (__dirname available) and ES modules (import.meta.url available) contexts. The path resolution logic is sound.

packages/cli/src/actions/init.ts (3)

31-31: Good formatting improvement for error handling.

Consolidating the multi-line error throw to a single line improves readability while maintaining the same functionality.


52-56: Consistent formatting for file operations and logging.

The consolidation of the file existence check, write operation, and console logging into single-line statements improves code consistency and readability.


59-60: Well-formatted console output messages.

The single-line formatting of console messages is more readable and maintains consistent styling with the rest of the codebase.

packages/ide/vscode/package.json (3)

12-17: Script simplification aligns with build system modernization.

The removal of watch-related scripts (watch, watch:tsc, watch:tsup) aligns with the migration to Turbo for task running mentioned in the AI summary. This consolidation should improve build consistency across the monorepo.


33-37: Good dependency management improvements.

The updates demonstrate better monorepo dependency management:

  • Using workspace:* for internal packages
  • Using catalog: for external dependencies
  • Removing duplicate entries

These changes should improve dependency resolution and version consistency.


39-42: Addition of shared configuration packages.

Adding @zenstackhq/eslint-config and @zenstackhq/typescript-config as workspace dependencies supports the standardization of linting and TypeScript configurations across the monorepo.

packages/cli/src/utils/exec-utils.ts (2)

6-6: Good formatting improvement for function signature.

Consolidating the multi-line function signature to a single line improves readability while maintaining the same type safety and functionality.


22-22: Good addition of trailing comma.

Adding the trailing comma is a good practice that makes future parameter additions cleaner and reduces diff noise.

packages/cli/src/index.ts (4)

7-9: Clean formatting for action function.

The single-line arrow function format improves readability while maintaining the same async functionality.


37-39: Improved formatting for program description.

The consolidated description formatting makes the CLI help text more readable while preserving the same functionality and styling.


45-46: Consistent single-line option descriptions.

The formatting improvements for option descriptions enhance code readability and maintain consistency across the CLI interface.


52-53: Excellent formatting consistency across command definitions.

The consolidation of multi-line command descriptions and option additions into single-line statements creates a much more readable and consistent command definition structure throughout the CLI.

Also applies to: 61-62, 69-70, 75-76, 90-92, 96-97

packages/runtime/src/client/executor/name-mapper.ts (1)

35-38: LGTM: Formatting improvements enhance readability.

The consolidation of multi-line statements into single lines improves code readability while preserving all original logic and functionality.

Also applies to: 71-71, 89-91, 116-116, 129-129, 143-143, 153-155, 162-177, 188-194, 197-204, 233-233, 247-247, 261-261

packages/common-helpers/src/is-plain-object.ts (1)

1-23: LGTM: Solid implementation of plain object detection.

The function correctly implements the standard approach for detecting plain JavaScript objects by checking the object's internal class string, constructor, prototype, and built-in methods. This is a well-established pattern that handles edge cases properly.

packages/runtime/src/client/crud/operations/create.ts (1)

4-4: LGTM: Formatting improvements enhance code readability.

The consolidation of import statements, method signatures, and type annotations improves code readability while maintaining all original functionality and type safety.

Also applies to: 8-9, 11-11, 13-13, 17-17, 31-35, 40-40, 53-53, 60-60, 65-65

packages/common-helpers/src/param-case.ts (1)

1-18: LGTM: Efficient param-case conversion implementation.

The function correctly handles camelCase and PascalCase boundaries using well-designed regex patterns. The use of null character delimiters and proper trimming ensures clean output for various input formats.

packages/runtime/src/client/crud/operations/count.ts (1)

5-5: LGTM: Formatting improvements enhance readability.

The consolidation of method calls and query building expressions improves code readability while preserving all counting logic and functionality.

Also applies to: 7-7, 14-15, 25-27, 32-32

packages/language/src/module.ts (1)

37-76: Reformat looks good

Only stylistic consolidation; no issues spotted with typing or DI wiring.

packages/language/src/index.ts (1)

48-52: LGTM – robust std-lib resolution

Cross-platform __dirname fallback and URI resolution look solid.

packages/runtime/src/client/query-builder.ts (2)

20-26: Use PascalCase for type aliases to stay consistent with TS conventions

toKyselyFieldType starts with a lowercase letter, which is counter-intuitive for a type alias and inconsistent with the rest of this file (ToKyselySchema, ToKyselyTable, …).
Renaming makes the API clearer and avoids the impression that this is a value-level helper.

-type ToKyselyTable<Schema extends SchemaDef, Model extends GetModels<Schema>> = {
-    [Field in ScalarFields<Schema, Model, false> | ForeignKeyFields<Schema, Model>]: toKyselyFieldType<
+type ToKyselyTable<Schema extends SchemaDef, Model extends GetModels<Schema>> = {
+    [Field in ScalarFields<Schema, Model, false> | ForeignKeyFields<Schema, Model>]: ToKyselyFieldType<
         Schema,
         Model,
         Field
     >;
 };
@@
-type toKyselyFieldType<
+type ToKyselyFieldType<

[ suggest_nitpick ]


28-40: Duplicated MapBaseType diverges from the canonical definition – risk of inconsistent typing

A MapBaseType with the same name already exists in packages/runtime/src/utils/type-utils.ts, but:

  • That canonical version maps 'DateTime'Date (here it is string).
  • The canonical version also maps 'Json'JsonValue (missing here).

Maintaining two differing definitions will silently break assignability checks across the codebase.

Action items:

  1. Delete the local alias and re-export the one from type-utils, or
  2. Bring this copy in sync with the canonical mapping.

Failing to do so can introduce subtle type mismatches between query-builder code and the rest of the runtime.
[ raise_critical_issue ][ request_verification ]

packages/runtime/src/client/options.ts (1)

9-14: LGTM – type-only formatting, no functional change

The refactor is purely cosmetic; typings still evaluate correctly.
[ approve_code_changes ]

packages/runtime/src/client/crud/dialects/sqlite.ts (2)

1-1: Verify behavioural parity of the new @zenstackhq/common-helpers invariant

invariant was switched from tiny-invariant to a workspace helper.
Please confirm that the new helper:

  • throws Error (or a compatible subclass) with the same message
  • keeps the assertion-signature (asserts condition).

A silent semantic drift here will surface only at runtime.
[ request_verification ]


136-152: Drop identity flatMap – use flat() for clarity and micro-perf

flatMap((v) => v) allocates an unnecessary callback. Static analysis already flags this.

-    objArgs.push(...someArray.flatMap((v) => v));
+    objArgs.push(...someArray.flat());

Apply the same change to the two other occurrences in this method.
No behaviour change, but the intent is clearer and marginally faster.
[ suggest_optional_refactor ]

Also applies to: 155-176, 179-195

packages/language/src/validator.ts (1)

67-102: expr && side-effect() pattern hurts debuggability

Using this.shouldCheck(node) && new X().validate(node, accept) relies on short-circuit side effects and required the global no-unused-expressions override.

Pros:

  • Fewer lines.

Cons:

  • Harder to set break-points.
  • Lint rule suppression hints at reduced readability.

Consider reverting to explicit if statements – especially inside a validator that is expected to be maintained by multiple contributors.
[ suggest_nitpick ]

packages/runtime/package.json (1)

68-76: catalog:/workspace: version specifiers may break external consumers

Dependencies such as "kysely": "catalog:" and "ts-pattern": "catalog:" are understood only inside your monorepo tooling.
If @zenstackhq/runtime is published to npm, these placeholders will be preserved in the published package.json, causing install failures for end-users.

Please ensure that the publish pipeline rewrites these specifiers to semver ranges (or mark the package private).
[ request_verification ]

packages/runtime/src/client/executor/zenstack-driver.ts (5)

108-109: Logging condition looks good

Switching to a single boolean expression keeps the intent clear and yields identical behaviour.
No further action required.


120-135: Defensive note: double-patching guard already handled

#addLogging captures the original executeQuery before monkey-patching, avoiding recursion – good. The WeakSet guard earlier prevents double patching, so the change here is safe.

No issues spotted.


137-154: Stream patch matches the sync variant – LGTM

Mirrors the error/latency logging pattern from executeQuery. Implementation is correct and preserves this binding via .call.


157-164: Good: structured error payload

The error logger now includes error and timing – useful for observability.


166-173: Good: optional isStream flag

Adds clarity in query logs without changing public surface area.

packages/cli/src/actions/generate.ts (2)

1-3: Consolidated imports – looks fine

Moving invariant into the new shared helper package and updating generator typings keeps the dependency graph clean.


43-44: Minor formatting only

Whitespace tweak in the template string – no functional impact.

packages/cli/package.json (1)

6-50: Verify unconventional version specifiers and build prerequisites

  • "langium": "catalog:", "ts-pattern": "catalog:", and peer "prisma": "catalog:" are not standard semver ranges. Ensure your private registry or pnpm “catalog” resolver is configured on all CI/consumer environments; otherwise installs will fail.
  • typescript is no longer a devDependency, but tsup-node relies on the compiler at build time. Confirm that the workspace root provides it transitively or add it back locally.
packages/common-helpers/package.json (1)

1-34: Initial package scaffold looks solid

Exports map covers both ESM and CJS, scripts align with other workspace packages. No critical issues spotted.

.github/workflows/bump-version.yml (1)

1-45: Workflow LGTM – remember to protect against stale lockfiles

The job is straightforward and targets the dev branch correctly. Consider adding pnpm install --frozen-lockfile --prefer-offline to speed up caching, but not required.

packages/language/src/validators/datamodel-validator.ts (1)

228-233: $resolvedType equality check can be simplified with optional chaining

Biome flagged this – the verbose comparison can be condensed:

-                    if (
-                        fieldRef.$resolvedType?.decl !== references[i]?.$resolvedType?.decl ||
-                        fieldRef.$resolvedType?.array !== references[i]?.$resolvedType?.array
-                    ) {
+                    if (
+                        fieldRef.$resolvedType?.decl !== references[i]?.$resolvedType?.decl ||
+                        fieldRef.$resolvedType?.array !== references[i]?.$resolvedType?.array
+                    ) {

(No functional change – purely readability.)

packages/language/src/validators/attribute-application-validator.ts (1)

298-305: FieldReference / TransitiveFieldReference array check misses heterogeneous arrays

The predicate only verifies that every item is a reference to a DataModelField, but it doesn’t check that all referenced fields share the same type – an attribute argument like [userId, createdAt] would pass even if the fields are String and DateTime.

If that type-safety is desired, extend the check:

const firstType = (arg.value.items[0] as ReferenceExpr).target.ref?.type;
if (
    arg.value.items.some(
        (item) =>
            !isReferenceExpr(item) ||
            !isDataModelField(item.target.ref) ||
            item.target.ref.type.type !== firstType?.type
    )
) {
    return false;
}
packages/runtime/src/client/helpers/schema-db-pusher.ts (3)

1-1: LGTM: Import migration aligns with project-wide consolidation.

The migration from tiny-invariant to @zenstackhq/common-helpers is consistent with the broader effort to consolidate common utilities across the codebase.


45-48: Well-implemented computed field handling.

The logic correctly identifies and skips computed fields during table column creation. The isComputedField method properly checks for the @computed attribute, which aligns with the expected behavior for derived fields that shouldn't be persisted in the database.

Also applies to: 57-60


103-136: Clean refactoring of createModelField method.

The reformatting improves code readability while maintaining the same logic. The simplified default value handling by combining conditions is a nice improvement.

packages/language/src/validators/schema-validator.ts (1)

13-18: All operations are synchronous
Both resolveTransitiveImports (packages/language/src/utils.ts:390) and resolveImport (packages/language/src/utils.ts:421) contain no async/await or Promise usage. Removing async/await from validate is safe.

packages/runtime/src/client/functions.ts (1)

10-18: Excellent addition of parameter validation.

The explicit parameter checks with clear error messages significantly improve the developer experience by catching missing parameters early and providing helpful error messages.

Also applies to: 26-33, 37-44, 48-55, 59-66, 71-77

packages/runtime/src/client/executor/zenstack-query-executor.ts (1)

60-60: Good use of TypeScript's native Awaited utility type.

Replacing the custom PromiseType with TypeScript's native Awaited utility improves code maintainability and leverages standard language features.

packages/runtime/src/client/plugin.ts (2)

157-174: Excellent plugin hook architecture design.

The new OnQueryHooks structure provides a flexible and type-safe way to intercept queries at different granularities:

  • Model-specific hooks with uncapitalized model names
  • Operation-specific hooks per model
  • $allModels and $allOperations catch-alls for general interception

This design significantly improves the plugin system's extensibility and type safety.


217-219: Nice addition of the definePlugin helper.

The definePlugin helper function provides a clean API for plugin authors with proper TypeScript typing, improving the developer experience.

packages/language/src/zmodel-scope.ts (1)

52-121: Code formatting looks clean and consistent.

The formatting improvements enhance readability without altering functionality. Method signatures and filtering logic have been properly condensed.

packages/runtime/src/client/client-impl.ts (1)

184-189: Good addition for plugin management.

The $unuse method provides a clean way to remove specific plugins by ID while maintaining immutability.

packages/runtime/src/client/crud/validator.ts (2)

25-1180: Zod type migration looks correct.

The systematic replacement of ZodSchema with ZodType throughout the file is appropriate for Zod v4 compatibility. The validation logic remains unchanged.


4-4: All Zod imports consistently use zod/v4
Verification shows no occurrences of import { … } from 'zod'; all imports reference zod/v4, and package definitions don’t pull in conflicting Zod versions. No further action required.

package.json (1)

1-39: Build tooling modernization looks good.

The migration to Turbo for task orchestration and the addition of formatting/version management scripts improve the development workflow. The dependency updates align well with modern tooling standards.

packages/runtime/src/client/crud/dialects/base.ts (1)

1-1: Good consolidation of helper imports.

Using the internal @zenstackhq/common-helpers package reduces external dependencies and improves maintainability.

packages/runtime/src/client/crud/operations/base.ts (2)

2-2: LGTM! Good consolidation of common utilities.

The migration from tiny-invariant to the centralized @zenstackhq/common-helpers package aligns well with the broader effort to consolidate common utilities.


125-1903: Excellent formatting improvements throughout the file.

The consistent formatting changes (multi-line to single-line conversions, trailing commas) improve code readability and maintainability without altering the core functionality.

packages/runtime/src/client/contract.ts (5)

5-28: LGTM! Well-organized imports for CRUD types.

The detailed import list provides better clarity on the specific types being used.


73-77: Excellent addition of the $unuse method.

This complements the existing $use and $unuseAll methods, providing fine-grained control over plugin management.


97-112: Clean redesign of the MapType utility.

The _TypeMap object provides a clear and maintainable mapping between schema primitive types and TypeScript types.


146-698: Outstanding addition of the ModelOperations interface!

This is an exceptional improvement to the client contract:

  • Comprehensive coverage of all CRUD operations
  • Excellent JSDoc documentation with practical examples
  • Strong typing for all operation arguments and return types
  • Clear parameter explanations and usage patterns

The extensive examples for each method will greatly help developers understand the API.


136-136: Good simplification of the ClientConstructor interface.

Removing the constructor overloads in favor of a single, clear signature improves API clarity.

Comment on lines 113 to 115
function installPackage(pkg: { name: string; dev: boolean }) {
runCommand(
`${agent} install ${pkg.name} ${pkg.dev ? saveDev : ''}`,
`Installing "${pkg.name}"`
);
runCommand(`${agent} install ${pkg.name} ${pkg.dev ? saveDev : ''}`, `Installing "${pkg.name}"`);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

install vs add: Yarn/PNPM/Bun will ignore the package name

yarn install foo (and likewise for pnpm/bun) installs nothing; it merely runs the install step.
Use the ecosystem-specific sub-command (add) to ensure dependencies are actually added.

-function installPackage(pkg: { name: string; dev: boolean }) {
-    runCommand(`${agent} install ${pkg.name} ${pkg.dev ? saveDev : ''}`, `Installing "${pkg.name}"`);
+function installPackage(pkg: { name: string; dev: boolean }) {
+    const subCmd = agent === 'npm' ? 'install' : 'add';
+    const devFlag = pkg.dev ? saveDev : '';
+    runCommand(`${agent} ${subCmd} ${pkg.name} ${devFlag}`.trim(), `Installing "${pkg.name}"`);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function installPackage(pkg: { name: string; dev: boolean }) {
runCommand(
`${agent} install ${pkg.name} ${pkg.dev ? saveDev : ''}`,
`Installing "${pkg.name}"`
);
runCommand(`${agent} install ${pkg.name} ${pkg.dev ? saveDev : ''}`, `Installing "${pkg.name}"`);
}
function installPackage(pkg: { name: string; dev: boolean }) {
const subCmd = agent === 'npm' ? 'install' : 'add';
const devFlag = pkg.dev ? saveDev : '';
runCommand(`${agent} ${subCmd} ${pkg.name} ${devFlag}`.trim(), `Installing "${pkg.name}"`);
}
🤖 Prompt for AI Agents
In packages/create-zenstack/src/index.ts around lines 113 to 115, the command
uses 'install' to add packages, but for Yarn, PNPM, and Bun, 'install' does not
add packages and only runs the install step. Replace the 'install' sub-command
with the appropriate 'add' sub-command to ensure the package is actually added
as a dependency.

Comment on lines 34 to 39

if (key === '_count') {
// underlying database provider may return string for count
data[key] =
typeof value === 'string' ? JSON.parse(value) : value;
data[key] = typeof value === 'string' ? JSON.parse(value) : value;
continue;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

JSON.parse on un-validated _count strings can blow up at runtime

If the provider ever returns a malformed string (or simply "NaN"), JSON.parse will throw and bring down the whole request.
A safer conversion does the minimal work needed (number or object) and falls back gracefully:

-data[key] = typeof value === 'string' ? JSON.parse(value) : value;
+if (typeof value === 'string') {
+    try {
+        data[key] = JSON.parse(value);
+    } catch {
+        // most providers just send numeric strings; convert them silently
+        const num = Number(value);
+        data[key] = Number.isNaN(num) ? value : num;
+    }
+}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/runtime/src/client/result-processor.ts around lines 34 to 39, the
use of JSON.parse on the _count string value can throw an error if the string is
malformed or not valid JSON. Replace JSON.parse with a safer conversion that
attempts to parse the string as a number or object but falls back gracefully
without throwing, ensuring the request does not fail due to invalid _count
strings.

Comment on lines 46 to 60
case '_count': {
if (value === true) {
query = query.select((eb) =>
eb.cast(eb.fn.countAll(), 'integer').as('_count')
);
query = query.select((eb) => eb.cast(eb.fn.countAll(), 'integer').as('_count'));
} else {
Object.entries(value).forEach(([field, val]) => {
if (val === true) {
if (field === '_all') {
query = query.select((eb) =>
eb
.cast(eb.fn.countAll(), 'integer')
.as(`_count._all`)
eb.cast(eb.fn.countAll(), 'integer').as(`_count._all`),
);
} else {
query = query.select((eb) =>
eb
.cast(
eb.fn.count(
sql.ref(`$sub.${field}`)
),
'integer'
)
.as(`${key}.${field}`)
eb.cast(eb.fn.count(sql.ref(`$sub.${field}`)), 'integer').as(`${key}.${field}`),
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

SQL alias contains dots – breaks MySQL & quoted identifiers

Aliases like '_count._all' and ${key}.${field} are invalid in MySQL and need quoting in Postgres.
Use an underscore delimiter (or quote) to stay portable:

-    .as(`_count._all`)
+    .as(`_count__all`)
...
-    .as(`${key}.${field}`)
+    .as(`${key}__${field}`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case '_count': {
if (value === true) {
query = query.select((eb) =>
eb.cast(eb.fn.countAll(), 'integer').as('_count')
);
query = query.select((eb) => eb.cast(eb.fn.countAll(), 'integer').as('_count'));
} else {
Object.entries(value).forEach(([field, val]) => {
if (val === true) {
if (field === '_all') {
query = query.select((eb) =>
eb
.cast(eb.fn.countAll(), 'integer')
.as(`_count._all`)
eb.cast(eb.fn.countAll(), 'integer').as(`_count._all`),
);
} else {
query = query.select((eb) =>
eb
.cast(
eb.fn.count(
sql.ref(`$sub.${field}`)
),
'integer'
)
.as(`${key}.${field}`)
eb.cast(eb.fn.count(sql.ref(`$sub.${field}`)), 'integer').as(`${key}.${field}`),
);
}
case '_count': {
if (value === true) {
query = query.select((eb) => eb.cast(eb.fn.countAll(), 'integer').as('_count'));
} else {
Object.entries(value).forEach(([field, val]) => {
if (val === true) {
if (field === '_all') {
query = query.select((eb) =>
eb.cast(eb.fn.countAll(), 'integer').as(`_count__all`),
);
} else {
query = query.select((eb) =>
eb.cast(eb.fn.count(sql.ref(`$sub.${field}`)), 'integer').as(`${key}__${field}`),
);
}
}
});
}
break;
}
🤖 Prompt for AI Agents
In packages/runtime/src/client/crud/operations/aggregate.ts between lines 46 and
60, the SQL aliases use dots (e.g., '_count._all' and `${key}.${field}`), which
are invalid in MySQL and cause issues with quoted identifiers in Postgres. To
fix this, replace the dots in the alias names with underscores or another
delimiter that is compatible across databases to ensure portability and prevent
SQL errors.

Comment on lines 8 to 13
const packages = await getZenStackPackages(projectPath);
if (!packages) {
console.error(
'Unable to locate package.json. Are you in a valid project directory?'
);
console.error('Unable to locate package.json. Are you in a valid project directory?');
return;
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

getZenStackPackages now returns [], breaking the “no package.json” guard

run() still relies on a falsy value to detect the absence of package.json, but getZenStackPackages was changed to return an empty array instead of undefined.
As a result, the error message is never printed and the user just sees an empty list.

-const packages = await getZenStackPackages(projectPath);
-if (!packages) {
+const packages = await getZenStackPackages(projectPath);
+if (packages.length === 0) {
     console.error('Unable to locate package.json. Are you in a valid project directory?');
     return;
 }

…and keep getZenStackPackages’s catch branch returning [] as it is.

This restores the intended UX and avoids changing the public contract of getZenStackPackages.

Also applies to: 40-41

🤖 Prompt for AI Agents
In packages/cli/src/actions/info.ts around lines 8 to 13 and also lines 40 to
41, the check for the absence of package.json incorrectly relies on a falsy
value from getZenStackPackages, but it now returns an empty array instead of
undefined. Update the condition to check if the returned packages array is empty
(length === 0) to detect the missing package.json and print the error message
accordingly, preserving the existing behavior of getZenStackPackages returning
an empty array on errors.

Comment on lines 158 to +162
if (isSelectAll) {
if (!selectAllFromModel) {
continue;
} else {
const scalarFields = this.getModelScalarFields(
contextNode,
selectAllFromModel
);
const fromModelDef = requireModel(
this.schema,
selectAllFromModel
);
const mappedTableName =
this.getMappedName(fromModelDef) ?? selectAllFromModel;
const scalarFields = this.getModelScalarFields(contextNode, selectAllFromModel);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unnecessary continue statement.

The continue statement is redundant since the else block won't execute when !selectAllFromModel is true, and the loop will naturally continue to the next iteration.

 if (isSelectAll) {
     if (!selectAllFromModel) {
-        continue;
+        // Skip processing when no model context
     } else {
         const scalarFields = this.getModelScalarFields(contextNode, selectAllFromModel);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (isSelectAll) {
if (!selectAllFromModel) {
continue;
} else {
const scalarFields = this.getModelScalarFields(
contextNode,
selectAllFromModel
);
const fromModelDef = requireModel(
this.schema,
selectAllFromModel
);
const mappedTableName =
this.getMappedName(fromModelDef) ?? selectAllFromModel;
const scalarFields = this.getModelScalarFields(contextNode, selectAllFromModel);
if (isSelectAll) {
if (!selectAllFromModel) {
// Skip processing when no model context
} else {
const scalarFields = this.getModelScalarFields(contextNode, selectAllFromModel);
🧰 Tools
🪛 Biome (1.9.4)

[error] 160-160: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

🤖 Prompt for AI Agents
In packages/runtime/src/client/executor/name-mapper.ts around lines 158 to 162,
remove the unnecessary continue statement inside the if (isSelectAll) block.
Since the else block only executes when selectAllFromModel is truthy, the
continue is redundant and can be safely deleted to simplify the code flow.

Comment on lines +68 to 72
async beginTransaction(connection: DatabaseConnection, settings: TransactionSettings): Promise<void> {
const result = await this.#driver.beginTransaction(connection, settings);
this.txConnection = connection;
return result;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Potential race condition & incorrect return type in beginTransaction

txConnection is a single shared property. If the same ZenStackDriver instance is used for concurrent transactions (a realistic scenario for pooled drivers), this will be overwritten by whichever transaction starts last, breaking the “active-transaction” invariant for the earlier one.
In addition, the method is declared to return Promise<void> but currently propagates the (void) result of the underlying driver – the extra return result; is redundant and may mis-lead future refactors.

-    async beginTransaction(connection: DatabaseConnection, settings: TransactionSettings): Promise<void> {
-        const result = await this.#driver.beginTransaction(connection, settings);
-        this.txConnection = connection;
-        return result;
+    async beginTransaction(connection: DatabaseConnection, settings: TransactionSettings): Promise<void> {
+        await this.#driver.beginTransaction(connection, settings);
+
+        // Track multiple concurrent transactions safely
+        (this.#activeTxConnections ??= new WeakSet()).add(connection);
+        this.txConnection = connection; // keep the shortcut for backwards-compat if really needed
+    }

You’ll also need to:

#activeTxConnections?: WeakSet<DatabaseConnection>;

and clean the set in commitTransaction/rollbackTransaction.

🤖 Prompt for AI Agents
In packages/runtime/src/client/executor/zenstack-driver.ts around lines 68 to
72, the beginTransaction method incorrectly uses a single shared property
txConnection for all transactions, causing race conditions in concurrent
scenarios, and it returns a redundant result despite being declared as
Promise<void>. To fix this, remove the txConnection assignment and the return
statement from beginTransaction, declare a private WeakSet named
#activeTxConnections to track active connections, and update commitTransaction
and rollbackTransaction methods to remove connections from this set to properly
manage transaction state without conflicts.

Comment on lines +112 to 116
const provider = this.getDataSourceProvider(AstUtils.getContainerOfType(field, isModel)!);
if (provider === 'sqlite') {
accept(
'error',
`Array type is not supported for "${provider}" provider.`,
{ node: field.type }
);
accept('error', `Array type is not supported for "${provider}" provider.`, { node: field.type });
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Hard-coding provider check to 'sqlite' is brittle

You only guard array types for sqlite, but MySQL & MariaDB share the same limitation.
Either compare against a set or reuse Prisma’s allowed list:

-            const provider = this.getDataSourceProvider(AstUtils.getContainerOfType(field, isModel)!);
-            if (provider === 'sqlite') {
+            const provider = this.getDataSourceProvider(AstUtils.getContainerOfType(field, isModel)!);
+            if (provider === 'sqlite' || provider === 'mysql' || provider === 'mariadb') {

Alternatively extract to const ARRAY_UNSUPPORTED = new Set(['sqlite', 'mysql', 'mariadb']).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const provider = this.getDataSourceProvider(AstUtils.getContainerOfType(field, isModel)!);
if (provider === 'sqlite') {
accept(
'error',
`Array type is not supported for "${provider}" provider.`,
{ node: field.type }
);
accept('error', `Array type is not supported for "${provider}" provider.`, { node: field.type });
}
}
const provider = this.getDataSourceProvider(AstUtils.getContainerOfType(field, isModel)!);
if (provider === 'sqlite' || provider === 'mysql' || provider === 'mariadb') {
accept('error', `Array type is not supported for "${provider}" provider.`, { node: field.type });
}
🤖 Prompt for AI Agents
In packages/language/src/validators/datamodel-validator.ts around lines 112 to
116, the code currently checks only for 'sqlite' when validating array type
support, which is brittle because MySQL and MariaDB also do not support array
types. To fix this, replace the single string comparison with a check against a
set of unsupported providers, for example by defining a constant set like
ARRAY_UNSUPPORTED containing 'sqlite', 'mysql', and 'mariadb', and then check if
the provider is in this set before raising the error.

Comment on lines 283 to 287
eb: ExpressionBuilder<any, any>,
array: Expression<unknown>
array: Expression<unknown>,
): ExpressionWrapper<any, any, number> {
return eb.fn('array_length', [array]);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

array_length is called with a single argument – this will fail on PostgreSQL

PostgreSQL requires the dimension parameter: array_length(anyarray, int).
Current call produces ERROR: function array_length(anyarray) does not exist.

-return eb.fn('array_length', [array]);
+// 1 = first dimension
+return eb.fn('array_length', [array, eb.val(1)]);

Please add the dimension literal (usually 1).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
eb: ExpressionBuilder<any, any>,
array: Expression<unknown>
array: Expression<unknown>,
): ExpressionWrapper<any, any, number> {
return eb.fn('array_length', [array]);
}
eb: ExpressionBuilder<any, any>,
array: Expression<unknown>,
): ExpressionWrapper<any, any, number> {
// 1 = first dimension
return eb.fn('array_length', [array, eb.val(1)]);
}
🤖 Prompt for AI Agents
In packages/runtime/src/client/crud/dialects/postgresql.ts around lines 283 to
287, the call to the PostgreSQL function array_length is missing the required
second argument for the array dimension. Fix this by adding the dimension
literal 1 as the second argument in the eb.fn call, so it correctly calls
array_length with two parameters: the array and the dimension.

Comment on lines +38 to 45
private async validateDataSources(model: Model, accept: ValidationAcceptor) {
const dataSources = (await getAllDeclarationsIncludingImports(this.documents, model)).filter((d) =>
isDataSource(d),
);
if (dataSources.length > 1) {
accept(
'error',
'Multiple datasource declarations are not allowed',
{ node: dataSources[1]! }
);
accept('error', 'Multiple datasource declarations are not allowed', { node: dataSources[1]! });
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistency: async validateDataSources called from sync validate method.

The validateDataSources method remains async and uses await, but it's called from the now-synchronous validate method without await. This will result in an unhandled promise and the validation won't complete properly.

Apply this diff to fix the issue:

-    private async validateDataSources(model: Model, accept: ValidationAcceptor) {
-        const dataSources = (await getAllDeclarationsIncludingImports(this.documents, model)).filter((d) =>
-            isDataSource(d),
-        );
+    private validateDataSources(model: Model, accept: ValidationAcceptor) {
+        const dataSources = getAllDeclarationsIncludingImports(this.documents, model).filter((d) =>
+            isDataSource(d),
+        );

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/language/src/validators/schema-validator.ts around lines 38 to 45,
the async method validateDataSources is called from a synchronous validate
method without awaiting, causing unhandled promises and incomplete validation.
To fix this, make the validate method async and await the call to
validateDataSources, ensuring proper asynchronous flow and completion of
validation.

this.kysely = new Kysely(this.kyselyProps);

return createClientProxy(this as unknown as ClientContract<Schema>);
return createClientProxy(this);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove the return statement from the constructor.

Constructors should not return values in JavaScript/TypeScript. The return value will be ignored, which could lead to confusion. The proxy wrapping should be handled differently.

Consider refactoring to use a factory pattern instead:

-export const ZenStackClient = function <Schema extends SchemaDef>(
+export function createZenStackClient<Schema extends SchemaDef>(
+    schema: Schema,
+    options: ClientOptions<Schema>,
+) {
+    const client = new ClientImpl<Schema>(schema, options);
+    return createClientProxy(client);
+}
+
+export const ZenStackClient = createZenStackClient as unknown as ClientConstructor;

And remove the return statement from the constructor:

-        return createClientProxy(this);
+        // Remove this line
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return createClientProxy(this);
// Replace the original `export const ZenStackClient = function…` definition
export function createZenStackClient<Schema extends SchemaDef>(
schema: Schema,
options: ClientOptions<Schema>,
) {
const client = new ClientImpl<Schema>(schema, options);
return createClientProxy(client);
}
export const ZenStackClient = createZenStackClient as unknown as ClientConstructor;
// Inside ClientImpl (packages/runtime/src/client/client-impl.ts)
class ClientImpl<Schema extends SchemaDef> {
constructor(schema: Schema, options: ClientOptions<Schema>) {
// …existing initialization logic…
// Note: we no longer return from the constructor—proxying happens in the factory.
}
// …other methods…
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 110-110: The constructor should not return a value.

The constructor is here:

Returning a value from a constructor is ignored.

(lint/correctness/noConstructorReturn)

🤖 Prompt for AI Agents
In packages/runtime/src/client/client-impl.ts at line 110, remove the return
statement from the constructor because constructors should not return values in
JavaScript/TypeScript. Instead, refactor the code to implement a factory
function that creates and returns the proxy-wrapped client instance, and keep
the constructor solely for initializing the instance without returning anything.

Merged via the queue into main with commit ca05f6d Jun 28, 2025
5 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jul 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants