Skip to content

Conversation

@ymc9
Copy link
Member

@ymc9 ymc9 commented Dec 29, 2025

Summary by CodeRabbit

  • New Features

    • New client-helpers package: typed fetcher, SuperJSON serialization, cache invalidation, optimistic-update helpers.
    • Formattable ID generation support (uuid/cuid/nanoid/ulid).
    • New Nuxt and SvelteKit sample apps showcasing configurable fetch, optimistic updates, and UI controls.
  • Improvements

    • TanStack Query integration: optional root query context (endpoint/fetch/logging), reactive/configurable hooks, optimistic-update flows.
    • Node.js minimum bumped to v22.
  • Bug Fixes

    • Improved enum handling in query comparisons.

✏️ Tip: You can customize this high-level summary in your review settings.

ymc9 and others added 10 commits December 24, 2025 18:05
* chore: update README badge

* update
…509)

* Add support for generated identifier format strings (ID prefixing).

* Add tests.

* Add missing semicolon.

* Cleanup logic.

* Fix typo.

* Use `replaceAll` instead.

* Add language support and tests.

* Simplify logic.

* allow using '\\%s' to escape replacement pattern, improve tests

* Shorten some test names.

---------

Co-authored-by: ymc9 <[email protected]>
- Updated GitHub Actions workflow to accept version_type input (patch/minor)
- Modified bump-version.ts script to support both patch and minor version increments
  - Patch: increments patch version (e.g., 3.0.5 → 3.0.6)
  - Minor: increments minor version and resets patch to 0 (e.g., 3.0.5 → 3.1.0)
- Replaced single bump-version command with two separate commands:
  - bump-patch: triggers workflow with version_type=patch
  - bump-minor: triggers workflow with version_type=minor

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 4.5 <[email protected]>
* fix: do not accept empty strings, check for escapement

* chore: add more edge cases, remove invalid test
* feat(orm): support where enum

* fix(orm): super transform for where

* feat(orm): support nested enum in where and/or

* fix: resolve field's model from scope instead of relying on table qualification

---------

Co-authored-by: ymc9 <[email protected]>
Copilot AI review requested due to automatic review settings December 29, 2025 13:21
@claude
Copy link

claude bot commented Dec 29, 2025

Claude finished @ymc9's task —— View job


PR Review: dev → main

This is a comprehensive refactoring PR that introduces significant improvements to the client architecture. Here's my detailed review:

Review Checklist

  • Analyze overall PR scope and changes
  • Review new client-helpers package
  • Review tanstack-query refactoring
  • Check version bumps and package.json changes
  • Review new samples (Nuxt)
  • Assess test coverage
  • Check for potential bugs and security issues
  • Evaluate performance considerations
  • Provide final recommendations

Code Quality & Architecture

Excellent refactoring approach:

  • Smart extraction: The new @zenstackhq/client-helpers package properly extracts shared functionality from tanstack-query
  • Clean separation: Clear boundaries between generic helpers and framework-specific implementations
  • Type safety: Strong TypeScript typing throughout, proper generic constraints
  • Modular design: Well-structured exports and logical module organization

Best practices followed:

  • Consistent error handling patterns in packages/clients/client-helpers/src/fetch.ts:15-36
  • Proper use of SuperJSON for serialization with Decimal.js support
  • Clean async/await patterns without callback hell

Test Coverage - Outstanding

Comprehensive testing strategy:

  • 7 new test files with extensive coverage:
    • fetch.test.ts: 423 lines - serialization, HTTP error handling
    • invalidation.test.ts: 602 lines - query cache invalidation logic
    • mutator.test.ts: 1,533 lines - optimistic updates and mutations
    • nested-read-visitor.test.ts: 949 lines - data traversal
    • nested-write-visitor.test.ts: 1,244 lines - nested write operations
    • optimistic.test.ts: 743 lines - optimistic update scenarios
    • query-analysis.test.ts: 1,399 lines - query relationship analysis

Total: ~6,000 lines of test code - exceptional coverage for new functionality.

Security Assessment

No security concerns found:

  • ❌ No use of eval(), Function(), or innerHTML
  • ✅ Proper URL encoding in makeUrl() function: packages/clients/client-helpers/src/fetch.ts:49
  • ✅ Safe error handling without exposing sensitive data
  • ✅ Controlled console logging only for debugging: packages/clients/client-helpers/src/logging.ts:13
  • ✅ No hardcoded secrets or credentials

Performance Considerations

Well-designed performance features:

  • Intelligent invalidation: packages/clients/client-helpers/src/invalidation.ts - smart query cache invalidation based on data relationships
  • Optimistic updates: packages/clients/client-helpers/src/optimistic.ts - reduces perceived latency
  • Efficient serialization: Uses SuperJSON for complex types like Decimal.js
  • Lazy evaluation: Query functions use reactive arguments in framework integrations

Package Management

Clean dependency management:

  • All packages properly bumped to 3.1.0
  • New client-helpers package has minimal, focused dependencies
  • Proper workspace dependencies using workspace:*
  • Catalog dependencies for consistency

New Nuxt Sample

Great addition:

  • samples/nuxt/app/app.vue demonstrates proper Vue 3 + Nuxt integration
  • Shows optimistic updates with visual feedback
  • Good example of the new client architecture in action

⚠️ Minor Issues Identified

  1. Console output in production code: packages/clients/client-helpers/src/fetch.ts:34 - should use logger parameter instead
  2. Missing TypeScript strict checks: Some any types could be more specific in mutation handling
  3. Error message clarity: Generic error message in fetcher() could be more descriptive

Breaking Changes Assessment

Well-managed refactoring:

  • Public APIs remain stable - this is primarily internal restructuring
  • Tanstack-query exports maintain compatibility
  • New client-helpers package adds functionality without removing existing features

Final Recommendation: ✅ APPROVE

This is an excellent refactoring PR that significantly improves the codebase:

Strengths:

  • 🎯 Exceptional test coverage - 6,000+ lines of comprehensive tests
  • 🏗️ Clean architecture - proper separation of concerns
  • 🔒 Security conscious - no security anti-patterns found
  • 🚀 Performance focused - intelligent caching and optimistic updates
  • 📦 Well-structured - clean package boundaries and dependencies

Impact: This refactoring sets up a solid foundation for future client implementations while maintaining backward compatibility.

The only minor issues are cosmetic and don't affect functionality. This PR demonstrates high engineering standards and thorough attention to detail.

@coderabbitai
Copy link

coderabbitai bot commented Dec 29, 2025

📝 Walkthrough

Walkthrough

This PR upgrades CI Node to 22.x, adds a new @zenstackhq/client-helpers package (fetch/serialization/invalidation/optimistic/mutator utilities) with tests, refactors TanStack Query React/Vue/Svelte integrations to use QueryContext, adds Nuxt/SvelteKit samples, introduces ID format support, and bumps many package versions to 3.1.0.

Changes

Cohort / File(s) Summary
CI & release tooling
​.github/workflows/*, vitest.config.ts, scripts/bump-version.ts, pnpm-workspace.yaml, package.json
Node matrix moved to 22.x; bump workflow gained version_type input; scripts split to bump-patch/bump-minor; vitest discovery narrowed; workspace catalog entries updated.
Root manifest & ignores
\.gitignore, package.json
Added coverage ignore; root package.json version and scripts updated (test:coverage, bump-*), pnpm onlyBuiltDependencies extended, new devDeps added.
New package: client-helpers
packages/clients/client-helpers/*
New package with constants, fetcher (SuperJSON + Decimal), marshal/unmarshal, makeUrl, invalidation, optimistic updater, mutator, nested-read/write visitors, logging, types, tsconfigs, build/test configs, and comprehensive tests.
TanStack Query refactor
packages/clients/tanstack-query/src/*, packages/clients/tanstack-query/package.json, tsconfig.*, tsup.config.ts
Moved/removed legacy utils; added common/query-key and common/client helpers; hooks now accept optional QueryContext/root options; react/vue/svelte hook implementations updated to merge root options and apply optimistic/invalidation flows; Svelte build/exports adjusted.
Client utilities moved
packages/clients/tanstack-query/src/utils/*packages/clients/client-helpers/src/*
Serialization, fetch, (de)marshal, invalidation, optimistic-update, and related types removed from tanstack-query and reimplemented in new client-helpers package; tanstack-query imports/use new helpers.
Samples: Nuxt, SvelteKit, Next.js
samples/nuxt/**, samples/sveltekit/**, samples/next.js/**
Added Nuxt and SvelteKit sample apps (schema, seed, server handlers, plugins, UI, CSS); updated Next.js sample to pass fetch/logging/optimistic flags and use QuerySettingsProvider logging.
ORM & ID generator changes
packages/orm/*, packages/orm/src/client/crud/operations/base.ts, packages/language/res/stdlib.zmodel, packages/language/src/validators/*, tests/e2e/**
Added optional format argument to uuid/cuid/nanoid/ulid; runtime formatting in ORM (formatGeneratedValue); validators check format placeholders and generator args; e2e tests added/updated for generated IDs and format strings.
SvelteKit adapter & handlers
packages/server/src/adapter/sveltekit/*
Introduced route-based SvelteKitRouteHandler and exported named SvelteKitHandler alias; added route handler module for +server.ts usage.
Common-helpers & tests
packages/common-helpers/*
Version bumps, added vitest config, many unit tests added; tiny-invariant guarded against undefined process.
Name-mapping & executor tweak
packages/orm/src/client/executor/name-mapper.ts
Added enum-aware transform for binary operations to map enum values in comparisons and IN lists.
Tests & infra
packages/clients/client-helpers/test/*, packages/common-helpers/test/*, tests/e2e/**, tests/regression/**
Extensive new/updated tests for fetcher, invalidation, mutator, visitors, optimistic updater, query-analysis, common-helpers, and e2e suites; vitest config moved to per-package files.
Widespread package bumps & metadata
packages/*/package.json, samples/*/package.json, tests/*/package.json
Many workspace packages bumped 3.0.0 → 3.1.0; package exports/files/deps/devDeps/peerDeps adjusted across multiple packages.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 I hopped through code with eager feet,

Pulled helpers out and made things neat.
Queries now share a QueryContext tune,
Nuxt and Svelte dance beneath the moon.
IDs wear patterns — I munched a carrot, true!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.14% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'merge dev to main' is vague and generic, providing no meaningful information about the actual changeset contents. Replace with a descriptive title summarizing the main changes, such as 'Release v3.1.0: Client helpers, TanStack Query refactor, and schema updates' or similar to convey the significance of the changeset.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65388a5 and 97ec4af.

📒 Files selected for processing (2)
  • packages/language/src/validators/function-invocation-validator.ts
  • samples/sveltekit/src/routes/api/model/[...path]/+server.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • samples/sveltekit/src/routes/api/model/[...path]/+server.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/language/src/validators/function-invocation-validator.ts (3)
packages/language/src/utils.ts (1)
  • getLiteral (272-286)
packages/sdk/src/ts-schema-generator.ts (1)
  • getLiteral (1084-1097)
packages/language/src/generated/ast.ts (2)
  • InvocationExpr (533-538)
  • InvocationExpr (540-540)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: claude-review
  • GitHub Check: build-test (22.x, postgresql)
  • GitHub Check: build-test (22.x, sqlite)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
packages/language/src/validators/function-invocation-validator.ts (3)

90-101: LGTM: Format validation logic is sound.

The validation correctly:

  • Uses findIndex to locate the format parameter by name, safely handling cases where the parameter doesn't exist
  • Guards with formatArg !== undefined to only validate literal string arguments
  • Uses negative lookbehind to ensure %s is not escaped

Note: The regex /(?<!\\)%s/g requires ES2018+ for negative lookbehind support, which should be fine for a modern TypeScript/Node.js project.


195-221: Previous critical issues have been resolved.

The undefined checks have been correctly added to both _checkUuid (line 201) and _checkCuid (line 215), preventing false positives when version arguments are non-literal expressions. This addresses the critical issues flagged in previous reviews.


223-235: LGTM: Nanoid validation is correct.

The function properly validates that the length argument is a positive number, with correct undefined guards to prevent false positives on non-literal arguments.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR merges the dev branch to main, bumping the version from 3.0.0 to 3.1.0 across all packages. The changes include significant enhancements to the ORM's name mapping capabilities, support for format strings in ID generation functions, a major refactoring of the TanStack Query client packages, new sample applications for SvelteKit and Nuxt, and various testing improvements.

Key Changes:

  • Enhanced name mapping for enum values in query builder operations
  • Added format string support for ID generation functions (uuid, cuid, nanoid, ulid)
  • Refactored TanStack Query packages with extracted common helpers and improved Svelte/Vue support
  • Added comprehensive test coverage for common helper utilities
  • New SvelteKit and Nuxt sample applications

Reviewed changes

Copilot reviewed 138 out of 145 changed files in this pull request and generated no comments.

Show a summary per file
File Description
vitest.config.ts Updated test project configuration to use specific config files
scripts/bump-version.ts Enhanced version bumping script with minor/patch support
packages/orm/src/client/executor/name-mapper.ts Added enum name mapping for binary operations in query builder
packages/orm/src/client/crud/operations/base.ts Implemented format string support for ID generators
packages/language/res/stdlib.zmodel Added format parameter to ID generation functions
packages/language/src/validators/function-invocation-validator.ts Added validation for format strings
packages/server/src/adapter/sveltekit/* Added new SvelteKit route handler
packages/clients/tanstack-query/src/* Major refactoring with extracted common helpers
packages/common-helpers/src/tiny-invariant.ts Fixed invariant check for browser environments
packages/create-zenstack/src/index.ts Changed package installation from @next to @latest
samples/sveltekit/* New SvelteKit sample application
samples/nuxt/* New Nuxt sample application
samples/next.js/* Updated Next.js sample with new features
pnpm-workspace.yaml Updated TanStack Query catalog versions
Multiple test files Added comprehensive test coverage

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/clients/tanstack-query/src/svelte/index.svelte.ts (1)

264-314: Inconsistent rootOptions merging for some hooks.

The useInfiniteFindMany (line 265) and all mutation hooks (lines 268-302) pass options directly without merging with rootOptions. This differs from the React and Vue implementations where all hooks merge root options.

This means configuration passed via useClientQueries(schema, options) won't propagate to infinite queries or mutations in Svelte.

🔎 Proposed fix to apply consistent merging
         useInfiniteFindMany: (args: any, options?: any) => {
-            return useInternalInfiniteQuery(schema, modelName, 'findMany', args, options);
+            return useInternalInfiniteQuery(schema, modelName, 'findMany', args, merge(rootOptions, options));
         },

         useCreate: (options?: any) => {
-            return useInternalMutation(schema, modelName, 'POST', 'create', options);
+            return useInternalMutation(schema, modelName, 'POST', 'create', merge(rootOptions, options));
         },

         useCreateMany: (options?: any) => {
-            return useInternalMutation(schema, modelName, 'POST', 'createMany', options);
+            return useInternalMutation(schema, modelName, 'POST', 'createMany', merge(rootOptions, options));
         },
         // ... apply same pattern to remaining mutation hooks
🧹 Nitpick comments (21)
packages/language/src/validators/function-invocation-validator.ts (1)

90-101: Format string validation logic is correct.

The validation correctly enforces that ID generation functions with a format parameter must include an unescaped "%s". The negative lookbehind regex pattern properly distinguishes escaped from unescaped placeholders.

Optional improvements

1. Remove unnecessary regex flag:
The /g (global) flag is unnecessary when using test() since it only checks for existence:

-                !/(?<!\\)%s/g.test(formatArg) // an unescaped %s must be present
+                !/(?<!\\)%s/.test(formatArg) // an unescaped %s must be present

2. Consider a more descriptive error message:
The current message doesn't explain the purpose of "%s". Consider:

-                accept('error', 'argument must include "%s"', {
+                accept('error', 'format string must include an unescaped "%s" placeholder for the generated ID', {
packages/language/res/stdlib.zmodel (1)

86-86: Format parameters added consistently.

The optional format: String? parameter has been added to all four ID generation functions, maintaining backward compatibility and aligning with the validator's expectations.

Consider adding documentation

The format parameter behavior isn't documented. Consider adding JSDoc comments to explain:

  • The format string must contain an unescaped "%s" placeholder
  • The "%s" will be replaced with the generated ID
  • Example usage: uuid(7, 'user_%s') generates IDs like 'user_abc123...'

Example for line 84-87:

 /**
  * Generates a globally unique identifier based on the UUID specs.
+ * @param version: The UUID version to generate.
+ * @param format: Optional format string that must include "%s" placeholder for the generated ID.
  */
 function uuid(version: Int?, format: String?): String {
 } @@@expressionContext([DefaultValue])

Also applies to: 92-92, 98-98, 104-104

packages/server/src/adapter/sveltekit/route-handler.ts (2)

1-59: Consider extracting shared logic (optional).

While the implementation is solid, there's significant code overlap (~50%) between this route handler and the deprecated hooks handler (client validation, query/body parsing, error handling patterns). Since the old handler is deprecated, this duplication may be acceptable. However, if both handlers will coexist for some time during migration, consider extracting shared logic into helper functions to improve maintainability.


37-40: Add explicit documentation of the [...path] catch-all route parameter requirement.

The JSDoc comment for SvelteKitRouteHandler states it should be used in a +server.ts API route file but does not mention that the route file must use [...path] as a catch-all parameter. Update the JSDoc to explicitly document this requirement so developers understand why the error "route is missing path parameter" occurs, e.g.:

/**
 * SvelteKit server route handler for handling CRUD requests. This handler must be used in a `+server.ts`
 * API route file within a dynamic route using the [...path] catch-all parameter, e.g. `routes/api/model/[...path]/+server.ts`.
 */
tests/e2e/orm/client-api/generated-id-format-strings.test.ts (1)

45-180: Consider adding test coverage for error cases and edge cases.

The current test suite focuses on happy paths but could benefit from additional coverage:

  1. Error handling:

    • Invalid format strings (e.g., no %s placeholder)
    • Multiple %s placeholders (is this supported?)
    • Format string with special characters or SQL injection attempts
  2. Edge cases:

    • Empty format string ""
    • Format string with only %s and no prefix/suffix
    • Very long format strings
  3. Test isolation:

    • Consider whether createTestClient properly cleans up resources between tests
    • If tests are slow, explore using a shared client with transaction rollback
Example error case tests
it('handles format strings without placeholders', async () => {
    const schema = `
        model Test {
            id    Int    @id
            field String @default(uuid(4, "no_placeholder"))
        }
    `;
    const client = await createTestClient(schema);
    
    const record = await client.test.create({ data: { id: 1 } });
    
    // Should this append the UUID or throw an error?
    // Verify expected behavior
    expect(record.field).toBeDefined();
});

it('handles empty format strings', async () => {
    const schema = `
        model Test {
            id    Int    @id
            field String @default(uuid(4, ""))
        }
    `;
    const client = await createTestClient(schema);
    
    const record = await client.test.create({ data: { id: 1 } });
    
    // Should return raw UUID without prefix?
    expect(record.field).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-/);
});
packages/common-helpers/test/sleep.test.ts (1)

52-58: Consider wrapping fake timers in try/finally for cleanup safety.

If the test fails or throws before vi.useRealTimers(), subsequent tests may run with fake timers still enabled, causing flaky behavior.

🔎 Proposed improvement
     it('should work with setTimeout mock', async () => {
         vi.useFakeTimers();
-        const promise = sleep(1000);
-        vi.advanceTimersByTime(1000);
-        await promise;
-        vi.useRealTimers();
+        try {
+            const promise = sleep(1000);
+            vi.advanceTimersByTime(1000);
+            await promise;
+        } finally {
+            vi.useRealTimers();
+        }
     });
samples/next.js/app/providers.tsx (1)

12-12: Consider disabling logging in production builds.

The logging: true flag is enabled in this sample. While helpful for development and demonstration purposes, consider disabling logging in production to avoid performance overhead and potential data leakage.

💡 Suggested approach for environment-based logging
-            <QuerySettingsProvider value={{ endpoint: '/api/model', logging: true }}>{children}</QuerySettingsProvider>
+            <QuerySettingsProvider value={{ endpoint: '/api/model', logging: process.env.NODE_ENV !== 'production' }}>{children}</QuerySettingsProvider>
samples/nuxt/zenstack/seed.ts (1)

6-21: Add error handling and logging to the seed script.

The seed script lacks error handling and feedback, which could make debugging difficult if seeding fails.

🔎 Suggested improvements
 async function main() {
+    console.log('Starting database seed...');
+    
     const db = new ZenStackClient(schema, {
         dialect: new SqliteDialect({
             database: new SQLite('./zenstack/dev.db'),
         }),
     });

+    console.log('Deleting existing users...');
     await db.user.deleteMany();

+    console.log('Creating seed users...');
     await db.user.createMany({
         data: [
             { id: '1', name: 'Alice', email: '[email protected]' },
             { id: '2', name: 'Bob', email: '[email protected]' },
         ],
     });
+    
+    console.log('Seed completed successfully!');
 }

-main();
+main().catch((error) => {
+    console.error('Seed failed:', error);
+    process.exit(1);
+});
samples/nuxt/app/plugins/tanstack-query.ts (1)

14-14: Consider making logging environment-dependent.

Logging is hardcoded to true, which may not be desirable in production environments. Consider using Nuxt's runtime config to control this based on the environment.

🔎 Environment-based logging configuration
 export default defineNuxtPlugin((nuxtApp) => {
     const queryClient = new QueryClient();

     nuxtApp.vueApp.use(VueQueryPlugin, { queryClient });

     nuxtApp.vueApp.mixin({
         setup() {
             provideQuerySettingsContext({
                 endpoint: '/api/model',
-                logging: true
+                logging: process.env.NODE_ENV === 'development'
             });
         }
     });
 });
samples/sveltekit/src/routes/+layout.svelte (1)

16-16: Consider making logging environment-dependent.

Similar to the Nuxt plugin, logging is hardcoded to true. For a sample/demo application this is fine, but consider making it environment-dependent in production applications.

🔎 Environment-based logging configuration
+import { dev } from '$app/environment';
+
-setQuerySettingsContext({ endpoint: '/api/model', logging: true });
+setQuerySettingsContext({ endpoint: '/api/model', logging: dev });
samples/nuxt/nuxt.config.ts (1)

8-11: Consider adding a brief comment explaining the @ts-ignore.

The @ts-ignore suppresses type errors from the Tailwind Vite plugin. A brief comment explaining why it's needed would help maintainability.

🔎 Suggested improvement
     vite: {
-        // @ts-ignore
+        // @ts-ignore - Tailwind Vite plugin types don't align with Nuxt's Vite config types
         plugins: [tailwindcss()],
     },
samples/sveltekit/src/routes/+page.svelte (1)

1-68: Well-structured Svelte component with TanStack Query integration.

The script section correctly implements:

  • Reactive state with Svelte 5 $state rune
  • Custom fetch wrapper (lines 13-17) for testing optimistic updates
  • Query/mutation setup with proper reactive dependencies
  • Random post creation with user assignment

Minor note: The console.log at line 47 could be removed or converted to proper logging for production use.

packages/clients/tanstack-query/src/common/client.ts (1)

17-41: Consider type predicate for better type narrowing.

The implementation is correct, but the filter on line 40 could benefit from a type predicate for better TypeScript inference. Additionally, the optional chaining on lines 27-29 is redundant after the null check on line 23.

🔎 Optional improvement for type safety
 export function getAllQueries(queryClient: QueryClient): readonly QueryInfo[] {
     return queryClient
         .getQueryCache()
         .getAll()
         .map(({ queryKey, state }) => {
             const parsed = parseQueryKey(queryKey);
             if (!parsed) {
                 return undefined;
             }
             return {
-                model: parsed?.model,
-                operation: parsed?.operation,
-                args: parsed?.args,
+                model: parsed.model,
+                operation: parsed.operation,
+                args: parsed.args,
                 data: state.data,
                 optimisticUpdate: !!parsed.flags.optimisticUpdate,
                 updateData: (data: unknown, cancelOnTheFlyQueries: boolean) => {
                     queryClient.setQueryData<unknown>(queryKey, data);
                     if (cancelOnTheFlyQueries) {
                         queryClient.cancelQueries({ queryKey }, { revert: false, silent: true });
                     }
                 },
             };
         })
-        .filter((entry) => !!entry);
+        .filter((entry): entry is QueryInfo => !!entry);
 }
packages/clients/tanstack-query/src/common/query-key.ts (3)

1-4: Minor documentation inconsistency.

The comment says "Prefix for react-query keys" but this module is used across React, Vue, and Svelte integrations. Consider updating to "Prefix for tanstack-query keys" for accuracy.

🔎 Suggested fix
 /**
- * Prefix for react-query keys.
+ * Prefix for tanstack-query keys.
  */
 export const QUERY_KEY_PREFIX = 'zenstack';

34-34: Unnecessary non-null assertion.

The operation! assertion is unnecessary since operation is already a required string parameter that cannot be null or undefined.

🔎 Suggested fix
-    return [QUERY_KEY_PREFIX, model, operation!, args, { infinite, optimisticUpdate }];
+    return [QUERY_KEY_PREFIX, model, operation, args, { infinite, optimisticUpdate }];

48-57: Type guard could be more robust.

The isZenStackQueryKey guard only validates length and prefix. Consider adding type checks for the other elements to prevent runtime errors when consuming the parsed key.

🔎 Suggested enhancement
 export function isZenStackQueryKey(queryKey: readonly unknown[]): queryKey is QueryKey {
     if (queryKey.length < 5) {
         return false;
     }

     if (queryKey[0] !== QUERY_KEY_PREFIX) {
         return false;
     }

+    // Validate model and operation are strings
+    if (typeof queryKey[1] !== 'string' || typeof queryKey[2] !== 'string') {
+        return false;
+    }
+
     return true;
 }
samples/next.js/app/page.tsx (2)

17-21: Demo-only artificial delay.

The 1-second delay is appropriate for demonstrating optimistic updates but ensure this doesn't make it to production code. Consider adding a comment to clarify this is for demonstration purposes only.

🔎 Suggested clarification
     const fetch: FetchFn = async (url, init) => {
-        // simulate a delay for showing optimistic update effect
+        // DEMO ONLY: simulate network latency to showcase optimistic update effect
         await new Promise((resolve) => setTimeout(resolve, 1000));
         return globalThis.fetch(url, init);
     };

144-149: Consider adding disabled state during mutation.

The Delete button doesn't disable during the mutation, which could lead to double-clicks. Consider using deletePost.isPending to disable the button.

🔎 Suggested enhancement
                                         <button
-                                            className="rounded-md px-2 py-1 text-white cursor-pointer underline text-xs"
+                                            className="rounded-md px-2 py-1 text-white cursor-pointer underline text-xs disabled:opacity-50"
                                             onClick={() => onDeletePost(post.id)}
+                                            disabled={deletePost.isPending}
                                         >
                                             Delete
                                         </button>
packages/clients/client-helpers/src/fetch.ts (2)

20-23: Type safety concern with undefined return.

Returning undefined as any bypasses TypeScript's type checking. Consider updating the return type of fetcher to Promise<R | undefined> or documenting this behavior in the JSDoc.

🔎 Suggested fix
 /**
  * A fetcher function that uses fetch API to make HTTP requests and automatically unmarshals
  * the response using superjson.
+ *
+ * @returns The deserialized response data, or `undefined` if the mutation result cannot be read back due to policy restrictions.
  */
-export async function fetcher<R>(url: string, options?: RequestInit, customFetch?: FetchFn): Promise<R> {
+export async function fetcher<R>(url: string, options?: RequestInit, customFetch?: FetchFn): Promise<R | undefined> {
     const _fetch = customFetch ?? fetch;
     const res = await _fetch(url, options);
     if (!res.ok) {
         const errData = unmarshal(await res.text());
         if (errData.error?.rejectedByPolicy && errData.error?.rejectReason === 'cannot-read-back') {
             // policy doesn't allow mutation result to be read back, just return undefined
-            return undefined as any;
+            return undefined;
         }

99-107: Consider adding error handling for malformed JSON.

The unmarshal function calls JSON.parse without try-catch. If the input is malformed JSON, it will throw an unhandled error. Consider adding error handling or documenting that callers should handle this.

🔎 Suggested enhancement
 /**
  * Unmarshal the given string value using superjson, assuming the value is a JSON stringified
  * object containing the serialized data and serialization metadata.
+ *
+ * @throws {SyntaxError} If the input is not valid JSON.
  */
 export function unmarshal(value: string) {
     const parsed = JSON.parse(value);
packages/clients/tanstack-query/src/svelte/index.svelte.ts (1)

449-451: Consider addressing @ts-ignore comments.

Multiple @ts-ignore comments suppress type errors for queryClient interactions. This suggests a type mismatch between the query client type and the invalidation/query retrieval functions.

Consider adding proper type assertions or investigating the underlying type incompatibility to improve type safety.

Also applies to: 469-470, 491-492

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e71bee7 and 6917a8c.

⛔ Files ignored due to path filters (5)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • samples/nuxt/public/favicon.ico is excluded by !**/*.ico
  • samples/nuxt/public/nuxt.svg is excluded by !**/*.svg
  • samples/sveltekit/src/lib/assets/favicon.svg is excluded by !**/*.svg
  • samples/sveltekit/static/svelte.png is excluded by !**/*.png
📒 Files selected for processing (140)
  • .github/workflows/build-test.yml
  • .github/workflows/bump-version.yml
  • .github/workflows/publish-release.yml
  • .github/workflows/update-samples.yml
  • .gitignore
  • CONTRIBUTING.md
  • README.md
  • package.json
  • packages/auth-adapters/better-auth/package.json
  • packages/cli/package.json
  • packages/cli/src/actions/generate.ts
  • packages/cli/src/actions/init.ts
  • packages/clients/client-helpers/eslint.config.js
  • packages/clients/client-helpers/package.json
  • packages/clients/client-helpers/src/constants.ts
  • packages/clients/client-helpers/src/fetch.ts
  • packages/clients/client-helpers/src/index.ts
  • packages/clients/client-helpers/src/invalidation.ts
  • packages/clients/client-helpers/src/logging.ts
  • packages/clients/client-helpers/src/mutator.ts
  • packages/clients/client-helpers/src/nested-read-visitor.ts
  • packages/clients/client-helpers/src/nested-write-visitor.ts
  • packages/clients/client-helpers/src/optimistic.ts
  • packages/clients/client-helpers/src/query-analysis.ts
  • packages/clients/client-helpers/src/types.ts
  • packages/clients/client-helpers/test/fetch.test.ts
  • packages/clients/client-helpers/test/invalidation.test.ts
  • packages/clients/client-helpers/test/mutator.test.ts
  • packages/clients/client-helpers/test/nested-read-visitor.test.ts
  • packages/clients/client-helpers/test/nested-write-visitor.test.ts
  • packages/clients/client-helpers/test/optimistic.test.ts
  • packages/clients/client-helpers/test/query-analysis.test.ts
  • packages/clients/client-helpers/test/test-helpers.ts
  • packages/clients/client-helpers/tsconfig.json
  • packages/clients/client-helpers/tsconfig.test.json
  • packages/clients/client-helpers/tsup.config.ts
  • packages/clients/client-helpers/vitest.config.ts
  • packages/clients/tanstack-query/.gitignore
  • packages/clients/tanstack-query/package.json
  • packages/clients/tanstack-query/src/common/.gitignore
  • packages/clients/tanstack-query/src/common/client.ts
  • packages/clients/tanstack-query/src/common/query-key.ts
  • packages/clients/tanstack-query/src/common/types.ts
  • packages/clients/tanstack-query/src/react.ts
  • packages/clients/tanstack-query/src/svelte/index.svelte.ts
  • packages/clients/tanstack-query/src/utils/common.ts
  • packages/clients/tanstack-query/src/utils/serialization.ts
  • packages/clients/tanstack-query/src/utils/types.ts
  • packages/clients/tanstack-query/src/vue.ts
  • packages/clients/tanstack-query/test/react-query.test.tsx
  • packages/clients/tanstack-query/test/svelte-typing-test.ts
  • packages/clients/tanstack-query/tsconfig.general.json
  • packages/clients/tanstack-query/tsconfig.json
  • packages/clients/tanstack-query/tsconfig.svelte.json
  • packages/common-helpers/package.json
  • packages/common-helpers/src/tiny-invariant.ts
  • packages/common-helpers/test/case-conversion.test.ts
  • packages/common-helpers/test/clone.test.ts
  • packages/common-helpers/test/enumerable.test.ts
  • packages/common-helpers/test/is-plain-object.test.ts
  • packages/common-helpers/test/param-case.test.ts
  • packages/common-helpers/test/safe-json-stringify.test.ts
  • packages/common-helpers/test/sleep.test.ts
  • packages/common-helpers/test/tiny-invariant.test.ts
  • packages/common-helpers/test/zip.test.ts
  • packages/common-helpers/vitest.config.ts
  • packages/config/eslint-config/package.json
  • packages/config/typescript-config/package.json
  • packages/config/vitest-config/base.config.js
  • packages/config/vitest-config/package.json
  • packages/create-zenstack/package.json
  • packages/create-zenstack/src/index.ts
  • packages/language/package.json
  • packages/language/res/stdlib.zmodel
  • packages/language/src/validators/function-invocation-validator.ts
  • packages/language/test/function-invocation.test.ts
  • packages/orm/package.json
  • packages/orm/src/client/crud/operations/base.ts
  • packages/orm/src/client/executor/name-mapper.ts
  • packages/plugins/policy/package.json
  • packages/schema/package.json
  • packages/sdk/package.json
  • packages/server/package.json
  • packages/server/src/adapter/sveltekit/handler.ts
  • packages/server/src/adapter/sveltekit/index.ts
  • packages/server/src/adapter/sveltekit/route-handler.ts
  • packages/testtools/package.json
  • packages/zod/package.json
  • pnpm-workspace.yaml
  • samples/next.js/app/page.tsx
  • samples/next.js/app/providers.tsx
  • samples/next.js/next.config.ts
  • samples/next.js/package.json
  • samples/nuxt/.gitignore
  • samples/nuxt/README.md
  • samples/nuxt/app/app.vue
  • samples/nuxt/app/assets/css/main.css
  • samples/nuxt/app/plugins/tanstack-query.ts
  • samples/nuxt/nuxt.config.ts
  • samples/nuxt/package.json
  • samples/nuxt/public/robots.txt
  • samples/nuxt/server/api/model/[...].ts
  • samples/nuxt/server/utils/db.ts
  • samples/nuxt/tsconfig.json
  • samples/nuxt/zenstack/input.ts
  • samples/nuxt/zenstack/models.ts
  • samples/nuxt/zenstack/schema-lite.ts
  • samples/nuxt/zenstack/schema.ts
  • samples/nuxt/zenstack/schema.zmodel
  • samples/nuxt/zenstack/seed.ts
  • samples/orm/package.json
  • samples/sveltekit/.gitignore
  • samples/sveltekit/.prettierrc
  • samples/sveltekit/README.md
  • samples/sveltekit/package.json
  • samples/sveltekit/src/app.d.ts
  • samples/sveltekit/src/app.html
  • samples/sveltekit/src/lib/db.ts
  • samples/sveltekit/src/lib/index.ts
  • samples/sveltekit/src/routes/+layout.svelte
  • samples/sveltekit/src/routes/+page.svelte
  • samples/sveltekit/src/routes/api/model/[...path]/+server.ts
  • samples/sveltekit/src/routes/layout.css
  • samples/sveltekit/src/zenstack/input.ts
  • samples/sveltekit/src/zenstack/models.ts
  • samples/sveltekit/src/zenstack/schema-lite.ts
  • samples/sveltekit/src/zenstack/schema.ts
  • samples/sveltekit/src/zenstack/schema.zmodel
  • samples/sveltekit/src/zenstack/seed.ts
  • samples/sveltekit/svelte.config.js
  • samples/sveltekit/tsconfig.json
  • samples/sveltekit/vite.config.ts
  • scripts/bump-version.ts
  • tests/e2e/orm/client-api/generated-id-format-strings.test.ts
  • tests/e2e/orm/client-api/name-mapping.test.ts
  • tests/e2e/package.json
  • tests/regression/package.json
  • tests/runtimes/bun/package.json
  • tests/runtimes/edge-runtime/package.json
  • vitest.config.ts
💤 Files with no reviewable changes (4)
  • packages/clients/client-helpers/src/query-analysis.ts
  • packages/clients/tanstack-query/src/utils/types.ts
  • packages/clients/tanstack-query/src/utils/serialization.ts
  • packages/clients/tanstack-query/src/utils/common.ts
🧰 Additional context used
📓 Path-based instructions (2)
tests/e2e/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

E2E tests should validate real-world schema compatibility with established projects

Files:

  • tests/e2e/orm/client-api/name-mapping.test.ts
  • tests/e2e/orm/client-api/generated-id-format-strings.test.ts
**/*.zmodel

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.zmodel: Always run zenstack generate after modifying ZModel schemas
ZModel schema files should define database structure and policies that compile to TypeScript via zenstack generate

Files:

  • samples/nuxt/zenstack/schema.zmodel
  • packages/language/res/stdlib.zmodel
  • samples/sveltekit/src/zenstack/schema.zmodel
🧠 Learnings (14)
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to tests/**/type*.{ts,tsx} : Ensure TypeScript inference and type coverage are validated through type coverage tests

Applied to files:

  • .gitignore
  • packages/common-helpers/test/zip.test.ts
  • packages/common-helpers/test/clone.test.ts
  • packages/config/vitest-config/base.config.js
  • packages/clients/client-helpers/test/nested-read-visitor.test.ts
  • packages/common-helpers/test/param-case.test.ts
  • packages/language/test/function-invocation.test.ts
  • packages/clients/tanstack-query/src/common/types.ts
  • tests/e2e/orm/client-api/generated-id-format-strings.test.ts
  • vitest.config.ts
  • package.json
  • packages/clients/tanstack-query/src/common/.gitignore
  • packages/common-helpers/test/enumerable.test.ts
  • packages/clients/client-helpers/test/fetch.test.ts
  • packages/clients/client-helpers/test/invalidation.test.ts
  • packages/common-helpers/test/safe-json-stringify.test.ts
  • packages/clients/client-helpers/tsconfig.test.json
  • samples/nuxt/tsconfig.json
  • packages/common-helpers/test/is-plain-object.test.ts
  • packages/common-helpers/test/case-conversion.test.ts
  • packages/clients/client-helpers/tsconfig.json
  • packages/clients/client-helpers/test/nested-write-visitor.test.ts
  • packages/clients/client-helpers/test/mutator.test.ts
  • packages/common-helpers/test/sleep.test.ts
  • packages/clients/client-helpers/test/query-analysis.test.ts
  • packages/common-helpers/test/tiny-invariant.test.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.{ts,tsx} : Implement plugin hooks at ORM, Kysely, and entity mutation levels for query interception and customization

Applied to files:

  • samples/orm/package.json
  • packages/cli/src/actions/init.ts
  • samples/sveltekit/src/zenstack/schema.ts
  • samples/nuxt/zenstack/models.ts
  • samples/sveltekit/src/zenstack/models.ts
  • samples/nuxt/README.md
  • samples/nuxt/app/plugins/tanstack-query.ts
  • samples/nuxt/zenstack/schema-lite.ts
  • packages/clients/client-helpers/src/types.ts
  • tests/e2e/orm/client-api/name-mapping.test.ts
  • packages/clients/client-helpers/test/optimistic.test.ts
  • packages/clients/tanstack-query/src/common/query-key.ts
  • samples/sveltekit/src/zenstack/schema-lite.ts
  • packages/clients/tanstack-query/tsconfig.general.json
  • packages/clients/tanstack-query/src/common/types.ts
  • packages/clients/client-helpers/src/invalidation.ts
  • samples/sveltekit/src/zenstack/seed.ts
  • packages/clients/client-helpers/src/optimistic.ts
  • samples/next.js/app/page.tsx
  • packages/orm/package.json
  • samples/nuxt/server/utils/db.ts
  • packages/clients/client-helpers/test/invalidation.test.ts
  • samples/nuxt/zenstack/seed.ts
  • packages/clients/client-helpers/src/mutator.ts
  • packages/clients/tanstack-query/src/vue.ts
  • packages/clients/client-helpers/test/mutator.test.ts
  • packages/clients/tanstack-query/src/react.ts
  • packages/create-zenstack/src/index.ts
  • samples/sveltekit/src/lib/db.ts
  • packages/clients/tanstack-query/test/svelte-typing-test.ts
  • packages/clients/client-helpers/test/query-analysis.test.ts
  • packages/clients/client-helpers/test/test-helpers.ts
  • samples/nuxt/zenstack/schema.ts
  • samples/nuxt/zenstack/input.ts
  • samples/sveltekit/src/zenstack/input.ts
  • packages/clients/tanstack-query/src/svelte/index.svelte.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/cli/**/*.test.{ts,tsx} : CLI package tests should focus on action-specific tests for each command

Applied to files:

  • packages/common-helpers/test/zip.test.ts
  • packages/common-helpers/package.json
  • packages/cli/src/actions/init.ts
  • packages/common-helpers/test/param-case.test.ts
  • vitest.config.ts
  • packages/clients/client-helpers/package.json
  • packages/clients/client-helpers/tsconfig.test.json
  • packages/common-helpers/test/case-conversion.test.ts
  • packages/clients/client-helpers/test/nested-write-visitor.test.ts
  • packages/clients/client-helpers/test/mutator.test.ts
  • packages/create-zenstack/src/index.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to tests/e2e/**/*.{ts,tsx} : E2E tests should validate real-world schema compatibility with established projects

Applied to files:

  • packages/common-helpers/test/zip.test.ts
  • tests/e2e/package.json
  • packages/common-helpers/test/clone.test.ts
  • packages/clients/client-helpers/test/nested-read-visitor.test.ts
  • packages/common-helpers/test/param-case.test.ts
  • packages/language/test/function-invocation.test.ts
  • samples/nuxt/zenstack/schema-lite.ts
  • tests/e2e/orm/client-api/name-mapping.test.ts
  • packages/clients/client-helpers/test/optimistic.test.ts
  • samples/sveltekit/src/zenstack/schema-lite.ts
  • tests/e2e/orm/client-api/generated-id-format-strings.test.ts
  • vitest.config.ts
  • CONTRIBUTING.md
  • packages/clients/tanstack-query/src/common/.gitignore
  • packages/clients/client-helpers/test/fetch.test.ts
  • packages/clients/client-helpers/test/invalidation.test.ts
  • packages/common-helpers/test/safe-json-stringify.test.ts
  • packages/clients/client-helpers/tsconfig.test.json
  • packages/common-helpers/test/is-plain-object.test.ts
  • packages/clients/client-helpers/test/nested-write-visitor.test.ts
  • packages/clients/client-helpers/test/mutator.test.ts
  • packages/common-helpers/test/sleep.test.ts
  • packages/clients/client-helpers/test/query-analysis.test.ts
  • packages/clients/client-helpers/test/test-helpers.ts
  • samples/nuxt/zenstack/schema.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to **/*.zmodel : ZModel schema files should define database structure and policies that compile to TypeScript via `zenstack generate`

Applied to files:

  • samples/nuxt/server/api/model/[...].ts
  • packages/zod/package.json
  • samples/sveltekit/src/zenstack/schema.ts
  • samples/nuxt/zenstack/models.ts
  • samples/sveltekit/src/zenstack/models.ts
  • samples/nuxt/README.md
  • samples/nuxt/zenstack/schema-lite.ts
  • samples/nuxt/zenstack/schema.zmodel
  • samples/sveltekit/src/zenstack/schema-lite.ts
  • packages/clients/tanstack-query/tsconfig.general.json
  • samples/sveltekit/src/zenstack/seed.ts
  • packages/clients/tanstack-query/tsconfig.json
  • packages/clients/tanstack-query/src/common/.gitignore
  • samples/nuxt/server/utils/db.ts
  • packages/clients/client-helpers/tsconfig.test.json
  • samples/nuxt/zenstack/seed.ts
  • packages/clients/client-helpers/tsconfig.json
  • packages/clients/client-helpers/src/mutator.ts
  • packages/create-zenstack/src/index.ts
  • samples/sveltekit/src/lib/db.ts
  • samples/sveltekit/package.json
  • packages/schema/package.json
  • samples/sveltekit/src/zenstack/schema.zmodel
  • packages/language/package.json
  • packages/clients/client-helpers/test/test-helpers.ts
  • samples/nuxt/zenstack/schema.ts
  • samples/nuxt/zenstack/input.ts
  • samples/sveltekit/src/zenstack/input.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.test.{ts,tsx} : ORM package tests should include comprehensive client API tests and policy tests

Applied to files:

  • packages/clients/client-helpers/test/nested-read-visitor.test.ts
  • packages/clients/client-helpers/src/types.ts
  • tests/e2e/orm/client-api/name-mapping.test.ts
  • packages/clients/client-helpers/test/optimistic.test.ts
  • packages/clients/tanstack-query/src/common/types.ts
  • tests/e2e/orm/client-api/generated-id-format-strings.test.ts
  • samples/sveltekit/src/zenstack/seed.ts
  • packages/orm/package.json
  • packages/clients/client-helpers/package.json
  • packages/clients/client-helpers/test/fetch.test.ts
  • packages/clients/client-helpers/test/invalidation.test.ts
  • packages/clients/client-helpers/tsconfig.test.json
  • packages/common-helpers/test/is-plain-object.test.ts
  • samples/nuxt/zenstack/seed.ts
  • packages/clients/client-helpers/src/mutator.ts
  • packages/clients/client-helpers/test/nested-write-visitor.test.ts
  • packages/clients/client-helpers/test/mutator.test.ts
  • packages/create-zenstack/src/index.ts
  • packages/clients/tanstack-query/test/svelte-typing-test.ts
  • packages/clients/client-helpers/test/query-analysis.test.ts
  • packages/clients/client-helpers/test/test-helpers.ts
  • samples/nuxt/zenstack/input.ts
  • samples/sveltekit/src/zenstack/input.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.{ts,tsx} : Use Kysely as the query builder interface for low-level database queries, avoiding raw SQL when possible

Applied to files:

  • packages/cli/src/actions/init.ts
  • samples/sveltekit/src/zenstack/schema.ts
  • samples/nuxt/zenstack/models.ts
  • samples/sveltekit/src/zenstack/models.ts
  • samples/nuxt/README.md
  • samples/nuxt/zenstack/schema-lite.ts
  • packages/clients/client-helpers/src/types.ts
  • tests/e2e/orm/client-api/name-mapping.test.ts
  • packages/clients/tanstack-query/src/common/query-key.ts
  • samples/sveltekit/src/zenstack/schema-lite.ts
  • packages/clients/tanstack-query/tsconfig.general.json
  • packages/clients/tanstack-query/src/common/types.ts
  • packages/clients/tanstack-query/test/react-query.test.tsx
  • samples/sveltekit/src/zenstack/seed.ts
  • packages/clients/client-helpers/src/optimistic.ts
  • packages/orm/package.json
  • samples/nuxt/server/utils/db.ts
  • pnpm-workspace.yaml
  • packages/clients/tanstack-query/tsconfig.svelte.json
  • samples/nuxt/zenstack/seed.ts
  • packages/clients/client-helpers/src/mutator.ts
  • packages/clients/tanstack-query/src/vue.ts
  • packages/clients/tanstack-query/src/react.ts
  • packages/clients/tanstack-query/package.json
  • packages/create-zenstack/src/index.ts
  • samples/sveltekit/src/lib/db.ts
  • packages/clients/tanstack-query/test/svelte-typing-test.ts
  • samples/nuxt/zenstack/schema.ts
  • samples/nuxt/zenstack/input.ts
  • samples/sveltekit/src/zenstack/input.ts
  • packages/clients/tanstack-query/src/svelte/index.svelte.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Use Turbo for build orchestration and run `pnpm build`, `pnpm watch`, `pnpm lint`, and `pnpm test` for development tasks

Applied to files:

  • packages/cli/src/actions/init.ts
  • package.json
📚 Learning: 2025-10-21T16:09:31.218Z
Learnt from: ymc9
Repo: zenstackhq/zenstack-v3 PR: 319
File: packages/runtime/src/client/executor/zenstack-query-executor.ts:63-72
Timestamp: 2025-10-21T16:09:31.218Z
Learning: In ZenStack, TypeDefs can be inherited by models. When a TypeDef contains fields with `map` attributes, those mapped field names need to be processed by the QueryNameMapper since they become part of the inheriting model's schema. Therefore, when checking if a schema has mapped names (e.g., in `schemaHasMappedNames`), both `schema.models` and `schema.typeDefs` must be inspected for `@map` and `map` attributes.

Applied to files:

  • samples/sveltekit/src/zenstack/schema.ts
  • samples/nuxt/zenstack/schema-lite.ts
  • samples/sveltekit/src/zenstack/schema-lite.ts
  • samples/sveltekit/src/zenstack/schema.zmodel
  • packages/clients/client-helpers/test/test-helpers.ts
  • samples/nuxt/zenstack/schema.ts
  • samples/nuxt/zenstack/input.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to **/*.zmodel : Always run `zenstack generate` after modifying ZModel schemas

Applied to files:

  • samples/nuxt/zenstack/models.ts
  • samples/sveltekit/src/zenstack/models.ts
  • samples/sveltekit/src/zenstack/schema.zmodel
  • samples/nuxt/zenstack/schema.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Database migrations should use Prisma CLI under the hood via ZenStack commands

Applied to files:

  • samples/nuxt/zenstack/schema.zmodel
  • samples/sveltekit/src/zenstack/seed.ts
  • samples/nuxt/server/utils/db.ts
  • samples/nuxt/zenstack/seed.ts
  • packages/create-zenstack/src/index.ts
  • samples/sveltekit/src/lib/db.ts
  • samples/nuxt/zenstack/schema.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to ide/vscode/**/{package.json,version.ts} : The VSCode IDE extension package should maintain a different version from other packages to comply with VSCode Marketplace requirements

Applied to files:

  • tests/regression/package.json
  • scripts/bump-version.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Support only SQLite (better-sqlite3) and PostgreSQL (pg) as database adapters

Applied to files:

  • CONTRIBUTING.md
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Use `pnpm` with workspaces for package management, pinned to version `pnpm10.12.1`

Applied to files:

  • package.json
  • pnpm-workspace.yaml
  • packages/cli/package.json
🧬 Code graph analysis (36)
packages/common-helpers/test/zip.test.ts (1)
packages/common-helpers/src/zip.ts (1)
  • zip (4-11)
packages/common-helpers/test/clone.test.ts (1)
packages/common-helpers/src/clone.ts (1)
  • clone (6-24)
samples/sveltekit/src/zenstack/schema.ts (2)
packages/schema/src/schema.ts (1)
  • SchemaDef (11-19)
packages/schema/src/expression-utils.ts (1)
  • ExpressionUtils (19-123)
packages/common-helpers/test/param-case.test.ts (1)
packages/common-helpers/src/param-case.ts (1)
  • paramCase (5-22)
samples/nuxt/app/plugins/tanstack-query.ts (1)
packages/clients/tanstack-query/src/vue.ts (1)
  • provideQuerySettingsContext (76-78)
samples/nuxt/zenstack/schema-lite.ts (3)
packages/schema/src/schema.ts (1)
  • SchemaDef (11-19)
packages/schema/src/expression-utils.ts (1)
  • ExpressionUtils (19-123)
samples/next.js/zenstack/schema-lite.ts (1)
  • SchemaType (9-105)
packages/clients/client-helpers/test/optimistic.test.ts (3)
packages/clients/client-helpers/test/test-helpers.ts (3)
  • createSchema (6-12)
  • createField (17-23)
  • createRelationField (28-37)
packages/clients/client-helpers/src/types.ts (1)
  • QueryInfo (48-82)
packages/clients/client-helpers/src/optimistic.ts (1)
  • createOptimisticUpdater (68-139)
packages/server/src/adapter/sveltekit/route-handler.ts (3)
packages/schema/src/schema.ts (1)
  • SchemaDef (11-19)
packages/server/src/adapter/common.ts (2)
  • CommonAdapterOptions (8-13)
  • logInternalError (15-21)
packages/orm/src/client/contract.ts (1)
  • ClientContract (63-197)
samples/sveltekit/src/zenstack/schema-lite.ts (3)
packages/schema/src/schema.ts (1)
  • SchemaDef (11-19)
packages/schema/src/expression-utils.ts (1)
  • ExpressionUtils (19-123)
samples/next.js/zenstack/schema-lite.ts (1)
  • SchemaType (9-105)
packages/server/src/adapter/sveltekit/handler.ts (2)
packages/schema/src/schema.ts (1)
  • SchemaDef (11-19)
packages/server/src/adapter/sveltekit/index.ts (1)
  • SvelteKitHandlerOptions (1-1)
tests/e2e/orm/client-api/generated-id-format-strings.test.ts (2)
packages/orm/src/client/crud/operations/base.ts (1)
  • schema (98-100)
packages/testtools/src/client.ts (1)
  • createTestClient (101-248)
packages/clients/client-helpers/src/invalidation.ts (3)
packages/schema/src/schema.ts (1)
  • SchemaDef (11-19)
packages/clients/client-helpers/src/types.ts (1)
  • ORMWriteActionType (28-28)
packages/clients/client-helpers/src/query-analysis.ts (2)
  • getMutatedModels (25-73)
  • getReadModels (9-20)
samples/sveltekit/src/zenstack/seed.ts (1)
packages/orm/src/dialects/sqlite.ts (1)
  • SqliteDialect (1-1)
packages/clients/client-helpers/src/optimistic.ts (4)
packages/schema/src/schema.ts (1)
  • SchemaDef (11-19)
packages/clients/tanstack-query/src/common/client.ts (1)
  • getAllQueries (17-41)
packages/clients/client-helpers/src/types.ts (2)
  • QueryInfo (48-82)
  • ORMWriteActionType (28-28)
packages/clients/client-helpers/src/mutator.ts (1)
  • applyMutation (20-40)
packages/clients/client-helpers/src/fetch.ts (2)
packages/clients/client-helpers/src/types.ts (1)
  • QueryError (33-43)
packages/common-helpers/src/lower-case-first.ts (1)
  • lowerCaseFirst (1-3)
packages/orm/src/client/crud/operations/base.ts (1)
packages/schema/src/expression-utils.ts (1)
  • ExpressionUtils (19-123)
samples/sveltekit/src/routes/api/model/[...path]/+server.ts (1)
packages/server/src/adapter/sveltekit/index.ts (1)
  • SvelteKitRouteHandler (2-2)
packages/common-helpers/test/enumerable.test.ts (1)
packages/common-helpers/src/enumerable.ts (1)
  • enumerate (9-17)
packages/clients/client-helpers/test/fetch.test.ts (2)
packages/clients/client-helpers/src/fetch.ts (5)
  • serialize (71-74)
  • deserialize (79-81)
  • marshal (86-93)
  • unmarshal (99-107)
  • fetcher (15-37)
packages/clients/client-helpers/src/types.ts (1)
  • QueryError (33-43)
samples/nuxt/server/utils/db.ts (1)
packages/orm/src/dialects/sqlite.ts (1)
  • SqliteDialect (1-1)
packages/clients/client-helpers/test/invalidation.test.ts (2)
packages/clients/client-helpers/test/test-helpers.ts (3)
  • createSchema (6-12)
  • createField (17-23)
  • createRelationField (28-37)
packages/clients/client-helpers/src/invalidation.ts (1)
  • createInvalidator (26-44)
packages/common-helpers/test/safe-json-stringify.test.ts (1)
packages/common-helpers/src/safe-json-stringify.ts (1)
  • safeJSONStringify (4-12)
packages/common-helpers/test/is-plain-object.test.ts (1)
packages/common-helpers/src/is-plain-object.ts (1)
  • isPlainObject (5-23)
packages/common-helpers/test/case-conversion.test.ts (2)
packages/common-helpers/src/lower-case-first.ts (1)
  • lowerCaseFirst (1-3)
packages/common-helpers/src/upper-case-first.ts (1)
  • upperCaseFirst (1-3)
packages/clients/client-helpers/src/mutator.ts (1)
packages/schema/src/schema.ts (1)
  • SchemaDef (11-19)
packages/clients/client-helpers/test/nested-write-visitor.test.ts (2)
packages/clients/client-helpers/test/test-helpers.ts (3)
  • createSchema (6-12)
  • createField (17-23)
  • createRelationField (28-37)
packages/clients/client-helpers/src/nested-write-visitor.ts (2)
  • NestedWriteVisitor (98-359)
  • NestedWriteVisitorContext (10-25)
packages/clients/client-helpers/test/mutator.test.ts (2)
packages/clients/client-helpers/test/test-helpers.ts (2)
  • createSchema (6-12)
  • createField (17-23)
packages/clients/client-helpers/src/mutator.ts (1)
  • applyMutation (20-40)
samples/next.js/app/providers.tsx (1)
packages/clients/tanstack-query/src/react.ts (1)
  • QuerySettingsProvider (75-75)
samples/sveltekit/src/lib/db.ts (1)
packages/orm/src/dialects/sqlite.ts (1)
  • SqliteDialect (1-1)
packages/common-helpers/test/sleep.test.ts (1)
packages/common-helpers/src/sleep.ts (1)
  • sleep (1-5)
packages/clients/tanstack-query/test/svelte-typing-test.ts (3)
packages/clients/tanstack-query/src/react.ts (1)
  • useClientQueries (262-277)
packages/clients/tanstack-query/src/vue.ts (1)
  • useClientQueries (214-229)
packages/clients/tanstack-query/test/schemas/basic/schema-lite.ts (1)
  • schema (172-172)
packages/clients/client-helpers/test/query-analysis.test.ts (2)
packages/clients/client-helpers/test/test-helpers.ts (3)
  • createSchema (6-12)
  • createField (17-23)
  • createRelationField (28-37)
packages/clients/client-helpers/src/query-analysis.ts (2)
  • getReadModels (9-20)
  • getMutatedModels (25-73)
packages/clients/client-helpers/src/nested-read-visitor.ts (1)
packages/schema/src/schema.ts (1)
  • FieldDef (62-78)
packages/common-helpers/test/tiny-invariant.test.ts (1)
packages/common-helpers/src/tiny-invariant.ts (1)
  • invariant (4-14)
packages/clients/client-helpers/test/test-helpers.ts (1)
packages/schema/src/schema.ts (2)
  • SchemaDef (11-19)
  • FieldDef (62-78)
samples/nuxt/zenstack/schema.ts (2)
packages/schema/src/schema.ts (1)
  • SchemaDef (11-19)
packages/schema/src/expression-utils.ts (1)
  • ExpressionUtils (19-123)
🪛 markdownlint-cli2 (0.18.1)
README.md

10-10: Images should have alternate text (alt text)

(MD045, no-alt-text)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Upload results
  • GitHub Check: build-test (22.x, sqlite)
  • GitHub Check: build-test (22.x, postgresql)
  • GitHub Check: claude-review

* feat(orm): support where enum

* fix(orm): super transform for where

* feat(orm): support nested enum in where and/or

* fix: resolve field's model from scope instead of relying on table qualification

* fix(orm): cuid() generator should respect version argument

---------

Co-authored-by: FTB_lag <[email protected]>
@claude
Copy link

claude bot commented Dec 29, 2025

Claude finished @ymc9's task —— View job


PR Review: dev → main

This is a comprehensive refactoring PR that introduces significant improvements to the client architecture. Here's my detailed review:

Review Checklist

  • Analyze overall PR scope and changes
  • Review new client-helpers package
  • Review tanstack-query refactoring
  • Check version bumps and package.json changes
  • Review new samples (Nuxt)
  • Assess test coverage
  • Check for potential bugs and security issues
  • Evaluate performance considerations
  • Provide final recommendations

Code Quality & Architecture

Excellent refactoring approach:

  • Smart extraction: The new @zenstackhq/client-helpers package properly extracts shared functionality from tanstack-query
  • Clean separation: Clear boundaries between generic helpers and framework-specific implementations
  • Type safety: Strong TypeScript typing throughout, proper generic constraints
  • Modular design: Well-structured exports and logical module organization

Best practices followed:

  • Consistent error handling patterns in packages/clients/client-helpers/src/fetch.ts:15-36
  • Proper use of SuperJSON for serialization with Decimal.js support
  • Clean async/await patterns without callback hell

Test Coverage - Outstanding

Comprehensive testing strategy:

  • 7 new test files with extensive coverage:
    • fetch.test.ts: 423 lines - serialization, HTTP error handling
    • invalidation.test.ts: 602 lines - query cache invalidation logic
    • mutator.test.ts: 1,533 lines - optimistic updates and mutations
    • nested-read-visitor.test.ts: 949 lines - data traversal
    • nested-write-visitor.test.ts: 1,244 lines - nested write operations
    • optimistic.test.ts: 743 lines - optimistic update scenarios
    • query-analysis.test.ts: 1,399 lines - query relationship analysis

Total: ~6,000 lines of test code - exceptional coverage for new functionality.

Security Assessment

No security concerns found:

  • ❌ No use of eval(), Function(), or innerHTML
  • ✅ Proper URL encoding in makeUrl() function: packages/clients/client-helpers/src/fetch.ts:49
  • ✅ Safe error handling without exposing sensitive data
  • ✅ Controlled console logging only for debugging: packages/clients/client-helpers/src/logging.ts:13
  • ✅ No hardcoded secrets or credentials

Performance Considerations

Well-designed performance features:

  • Intelligent invalidation: packages/clients/client-helpers/src/invalidation.ts - smart query cache invalidation based on data relationships
  • Optimistic updates: packages/clients/client-helpers/src/optimistic.ts - reduces perceived latency
  • Efficient serialization: Uses SuperJSON for complex types like Decimal.js
  • Lazy evaluation: Query functions use reactive arguments in framework integrations

Package Management

Clean dependency management:

  • All packages properly bumped to 3.1.0
  • New client-helpers package has minimal, focused dependencies
  • Proper workspace dependencies using workspace:*
  • Catalog dependencies for consistency

New Nuxt Sample

Great addition:

  • samples/nuxt/app/app.vue demonstrates proper Vue 3 + Nuxt integration
  • Shows optimistic updates with visual feedback
  • Good example of the new client architecture in action

⚠️ Minor Issues Identified

  1. Console output in production code: packages/clients/client-helpers/src/fetch.ts:34 - should use logger parameter instead
  2. Missing TypeScript strict checks: Some any types could be more specific in mutation handling
  3. Error message clarity: Generic error message in fetcher() could be more descriptive

Breaking Changes Assessment

Well-managed refactoring:

  • Public APIs remain stable - this is primarily internal restructuring
  • Tanstack-query exports maintain compatibility
  • New client-helpers package adds functionality without removing existing features

Final Recommendation: ✅ APPROVE

This is an excellent refactoring PR that significantly improves the codebase:

Strengths:

  • 🎯 Exceptional test coverage - 6,000+ lines of comprehensive tests
  • 🏗️ Clean architecture - proper separation of concerns
  • 🔒 Security conscious - no security anti-patterns found
  • 🚀 Performance focused - intelligent caching and optimistic updates
  • 📦 Well-structured - clean package boundaries and dependencies

Impact: This refactoring sets up a solid foundation for future client implementations while maintaining backward compatibility.

The only minor issues are cosmetic and don't affect functionality. This PR demonstrates high engineering standards and thorough attention to detail.


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: 2

🧹 Nitpick comments (1)
tests/e2e/orm/client-api/default-values.test.ts (1)

29-52: Solid validation logic with good coverage.

The test effectively validates:

  • All ID generation formats with appropriate validators
  • Version-specific UUID and CUID generation
  • Length-specific nanoid generation
  • Boolean default and override behavior via createMany

The test structure is clear and assertions are precise.

Consider expanding test coverage to include:

  • Verifying defaults don't override explicitly provided values (for non-boolean fields)
  • Testing defaults with other operations like update, upsert, or within transactions
  • Edge cases like concurrent entity creation with defaults

These additions would further align with the guideline that "E2E tests should validate real-world schema compatibility with established projects."

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6917a8c and be330ff.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • packages/language/src/validators/function-invocation-validator.ts
  • packages/language/test/function-invocation.test.ts
  • packages/orm/package.json
  • packages/orm/src/client/crud/operations/base.ts
  • tests/e2e/orm/client-api/default-values.test.ts
  • tests/e2e/package.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/language/test/function-invocation.test.ts
  • tests/e2e/package.json
  • packages/orm/src/client/crud/operations/base.ts
🧰 Additional context used
📓 Path-based instructions (1)
tests/e2e/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

E2E tests should validate real-world schema compatibility with established projects

Files:

  • tests/e2e/orm/client-api/default-values.test.ts
🧠 Learnings (5)
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.test.{ts,tsx} : ORM package tests should include comprehensive client API tests and policy tests

Applied to files:

  • tests/e2e/orm/client-api/default-values.test.ts
  • packages/orm/package.json
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to tests/e2e/**/*.{ts,tsx} : E2E tests should validate real-world schema compatibility with established projects

Applied to files:

  • tests/e2e/orm/client-api/default-values.test.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.{ts,tsx} : Use Kysely as the query builder interface for low-level database queries, avoiding raw SQL when possible

Applied to files:

  • tests/e2e/orm/client-api/default-values.test.ts
  • packages/orm/package.json
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.{ts,tsx} : Implement plugin hooks at ORM, Kysely, and entity mutation levels for query interception and customization

Applied to files:

  • tests/e2e/orm/client-api/default-values.test.ts
  • packages/orm/package.json
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to **/*.zmodel : ZModel schema files should define database structure and policies that compile to TypeScript via `zenstack generate`

Applied to files:

  • tests/e2e/orm/client-api/default-values.test.ts
  • packages/orm/package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-test (22.x, sqlite)
  • GitHub Check: build-test (22.x, postgresql)
  • GitHub Check: claude-review
🔇 Additional comments (7)
packages/language/src/validators/function-invocation-validator.ts (2)

90-101: LGTM!

The format validation logic is correct. The regex /(?<!\\)%s/g properly detects an unescaped %s placeholder using a negative lookbehind. The dynamic lookup of the format parameter index is appropriate.


223-235: LGTM!

The undefined guard length !== undefined && length <= 0 correctly handles non-literal arguments, avoiding false positives. This is the pattern that should be applied to _checkUuid and _checkCuid.

packages/orm/package.json (2)

125-125: No action needed—Zod versions are correctly aligned.

The catalog defines zod: ^4.0.0, and the peerDependency correctly references it via catalog:. The devDependency specifies ^4.1.0 to ensure testing with a compatible version; both constraints are satisfied by the resolved [email protected]. Since Zod is not directly used in orm/src, there are no breaking changes to address, and consumers are properly informed via the peerDependency declaration.


88-88: This concern is incorrect — cuid 3.0.0 exists, and the dual dependency is intentional.

The cuid package does have a version 3.0.0 on npm. The dual dependency of cuid: ^3.0.0 and @paralleldrive/cuid2: ^2.2.2 is intentional, as confirmed by the source code in packages/orm/src/client/crud/operations/base.ts, which imports both and uses conditional logic to select between cuid v1 and cuid v2 formats based on a version parameter:

const generated = version === 2 ? cuid2() : cuid1();

This aligns with the PR's "ID-format support" objective.

Likely an incorrect or invalid review comment.

tests/e2e/orm/client-api/default-values.test.ts (3)

1-6: LGTM! Clean import organization.

The aliased imports for CUID validators prevent naming conflicts, and the new createTestClient helper simplifies test setup.


27-27: LGTM! Proper use of test helper.

The createTestClient helper appropriately encapsulates schema compilation, database setup, and client initialization for E2E testing.


8-23: The schema correctly tests a comprehensive range of ID generation functions with proper format support. All default functions—uuid() (v4), uuid(4), uuid(7), cuid(), cuid(1), cuid(2), nanoid(), nanoid(8), and ulid()—are fully supported and implemented in the ORM's evalGenerator method. The test validates real-world schema compatibility by creating records and verifying generated values are the correct type and format. No changes needed.

* issue: rpc on update, 422 filtering error

the error appears to be caused by the json filtering logic, it is attempting
to utilize the filtering logic when there is an update on a list or json
field even if there are no such values to be filtered during an update
when attempting to set a new value

* fix: rpc update handling

* fix: undo the ORM side change since it's unrelated to the original issue

* fix: get back the `args ?? {}` safety guard

---------

Co-authored-by: ymc9 <[email protected]>
@claude
Copy link

claude bot commented Dec 30, 2025

Claude finished @ymc9's task —— View job


PR Review: dev → main

This is a comprehensive refactoring PR that introduces significant improvements to the client architecture. Here's my detailed review:

Review Checklist

  • Analyze overall PR scope and changes
  • Review new client-helpers package
  • Review tanstack-query refactoring
  • Check ID format generation implementation
  • Review new samples (Nuxt, SvelteKit)
  • Assess test coverage
  • Check for potential bugs and security issues
  • Evaluate performance considerations
  • Provide final recommendations

Code Quality & Architecture

Excellent refactoring approach:

  • Smart extraction: The new @zenstackhq/client-helpers package properly extracts shared functionality from tanstack-query
  • Clean separation: Clear boundaries between generic helpers and framework-specific implementations
  • Type safety: Strong TypeScript typing throughout, proper generic constraints
  • Modular design: Well-structured exports and logical module organization

Best practices followed:

  • Consistent error handling patterns in packages/clients/client-helpers/src/fetch.ts:15-36
  • Proper use of SuperJSON for serialization with Decimal.js support
  • Clean async/await patterns without callback hell

Test Coverage - Outstanding

Comprehensive testing strategy:

  • 7 new test files with extensive coverage:
    • fetch.test.ts: 423 lines - serialization, HTTP error handling
    • invalidation.test.ts: 602 lines - query cache invalidation logic
    • mutator.test.ts: 1,533 lines - optimistic updates and mutations
    • nested-read-visitor.test.ts: 949 lines - data traversal
    • nested-write-visitor.test.ts: 1,244 lines - nested write operations
    • optimistic.test.ts: 743 lines - optimistic update scenarios
    • query-analysis.test.ts: 1,399 lines - query relationship analysis

Total: ~6,000 lines of test code - exceptional coverage for new functionality.

Security Assessment

No security concerns found:

  • ❌ No use of eval(), Function(), or innerHTML
  • ✅ Proper URL encoding in makeUrl() function: packages/clients/client-helpers/src/fetch.ts:49
  • ✅ Safe error handling without exposing sensitive data
  • ✅ Controlled console logging only for debugging: packages/clients/client-helpers/src/logging.ts:13
  • ✅ No hardcoded secrets or credentials

Performance Considerations

Well-designed performance features:

  • Intelligent invalidation: packages/clients/client-helpers/src/invalidation.ts - smart query cache invalidation based on data relationships
  • Optimistic updates: packages/clients/client-helpers/src/optimistic.ts - reduces perceived latency
  • Efficient serialization: Uses SuperJSON for complex types like Decimal.js
  • Lazy evaluation: Query functions use reactive arguments in framework integrations

ID Format Generation Feature

Solid implementation:

  • Proper validation: Format strings must contain unescaped %s placeholder
  • Multiple generators: Support for UUID v4/v7, CUID v1/v2, nanoid, and ULID
  • Escape sequences: Handles \%s escaping correctly
  • Comprehensive tests: E2E tests validate real-world usage patterns

⚠️ Critical Issues Identified

1. String Replacement Security Vulnerability

packages/orm/src/client/crud/operations/base.ts:907

The replace() method can interpret special patterns in generated IDs:

return formatExpr.value.replace(/(?<!\\)%s/g, generated).replace(/\\%s/g, '%s');

If a generated ID contains $&, $', or $1-$99, they'll be treated as replacement patterns instead of literal strings.

Fix: Use a replacer function: .replace(/(?<!\\)%s/g, () => generated)

2. CUID Format Argument Bug

packages/orm/src/client/crud/operations/base.ts:871

CUID uses wrong argument index for format string:

return this.formatGeneratedValue(generated, defaultValue.args?.[1]); // Should be [0]

The cuid() function takes format as first argument, but code uses args[1].

⚠️ Minor Issues

  1. TanStack Svelte Query v6: The upgrade to @tanstack/[email protected] has breaking changes (requires Svelte 5+, new API patterns). Consider documenting migration steps.

  2. Sample Security Warning: Demo code in samples/nuxt/server/api/model/[...].ts:8-10 could be more prominent about security risks of fully open clients.

  3. Error Boundary: Early return guards in Nuxt sample (samples/nuxt/app/app.vue:37-39) don't handle empty arrays properly.

Package Management

Clean dependency management:

  • All packages properly bumped to 3.1.0
  • New client-helpers package has minimal, focused dependencies
  • Proper workspace dependencies using workspace:*
  • Catalog dependencies for consistency

New Samples

Great additions:

  • samples/nuxt/app/app.vue demonstrates proper Vue 3 + Nuxt integration
  • samples/sveltekit/ shows SvelteKit + TanStack Query patterns
  • Both samples demonstrate optimistic updates with visual feedback
  • Good examples of the new client architecture in action

Final Recommendation: ✅ APPROVE with Critical Fixes

This is an excellent refactoring PR that significantly improves the codebase:

Strengths:

  • 🎯 Exceptional test coverage - 6,000+ lines of comprehensive tests
  • 🏗️ Clean architecture - proper separation of concerns
  • 🔒 Security conscious - no security anti-patterns found
  • 🚀 Performance focused - intelligent caching and optimistic updates
  • 📦 Well-structured - clean package boundaries and dependencies

Required Fixes:

  1. Fix string replacement security issue in formatGeneratedValue()
  2. Correct CUID format argument index from [1] to [0]

Impact: This refactoring sets up a solid foundation for future client implementations while maintaining backward compatibility.

The critical issues are fixable and don't affect the overall architectural quality. This PR demonstrates high engineering standards and thorough attention to detail.


@claude
Copy link

claude bot commented Dec 30, 2025

Claude finished @ymc9's task —— View job


PR Review: dev → main

This is a comprehensive refactoring PR that introduces significant improvements to the client architecture. Here's my detailed review:

Review Checklist

  • Analyze overall PR scope and changes
  • Review new client-helpers package
  • Review tanstack-query refactoring
  • Check ID format generation implementation
  • Review new samples (Nuxt, SvelteKit)
  • Assess test coverage
  • Check for potential bugs and security issues
  • Evaluate performance considerations
  • Provide final recommendations

Code Quality & Architecture

Excellent refactoring approach:

  • Smart extraction: The new @zenstackhq/client-helpers package properly extracts shared functionality from tanstack-query
  • Clean separation: Clear boundaries between generic helpers and framework-specific implementations
  • Type safety: Strong TypeScript typing throughout, proper generic constraints
  • Modular design: Well-structured exports and logical module organization

Best practices followed:

  • Consistent error handling patterns in packages/clients/client-helpers/src/fetch.ts:15-36
  • Proper use of SuperJSON for serialization with Decimal.js support
  • Clean async/await patterns without callback hell

Test Coverage - Outstanding

Comprehensive testing strategy:

  • 7 new test files with extensive coverage:
    • fetch.test.ts: 423 lines - serialization, HTTP error handling
    • invalidation.test.ts: 602 lines - query cache invalidation logic
    • mutator.test.ts: 1,533 lines - optimistic updates and mutations
    • nested-read-visitor.test.ts: 949 lines - data traversal
    • nested-write-visitor.test.ts: 1,244 lines - nested write operations
    • optimistic.test.ts: 743 lines - optimistic update scenarios
    • query-analysis.test.ts: 1,399 lines - query relationship analysis

Total: ~6,000 lines of test code - exceptional coverage for new functionality.

Security Assessment

No security concerns found:

  • ❌ No use of eval(), Function(), or innerHTML
  • ✅ Proper URL encoding in makeUrl() function: packages/clients/client-helpers/src/fetch.ts:49
  • ✅ Safe error handling without exposing sensitive data
  • ✅ Controlled console logging only for debugging: packages/clients/client-helpers/src/logging.ts:13
  • ✅ No hardcoded secrets or credentials

Performance Considerations

Well-designed performance features:

  • Intelligent invalidation: packages/clients/client-helpers/src/invalidation.ts - smart query cache invalidation based on data relationships
  • Optimistic updates: packages/clients/client-helpers/src/optimistic.ts - reduces perceived latency
  • Efficient serialization: Uses SuperJSON for complex types like Decimal.js
  • Lazy evaluation: Query functions use reactive arguments in framework integrations

ID Format Generation Feature

Solid implementation:

  • Proper validation: Format strings must contain unescaped %s placeholder
  • Multiple generators: Support for UUID v4/v7, CUID v1/v2, nanoid, and ULID
  • Escape sequences: Handles \%s escaping correctly
  • Comprehensive tests: E2E tests validate real-world usage patterns

⚠️ Critical Issues Identified

1. String Replacement Security Vulnerability

packages/orm/src/client/crud/operations/base.ts:907

The replace() method can interpret special patterns in generated IDs:

return formatExpr.value.replace(/(?<!\\)%s/g, generated).replace(/\\%s/g, '%s');

If a generated ID contains $&, $', or $1-$99, they'll be treated as replacement patterns instead of literal strings.

Fix: Use a replacer function: .replace(/(?<!\\)%s/g, () => generated)

2. CUID Format Argument Bug

packages/orm/src/client/crud/operations/base.ts:871

CUID uses wrong argument index for format string:

return this.formatGeneratedValue(generated, defaultValue.args?.[1]); // Should be [0]

The cuid() function takes format as first argument, but code uses args[1].

3. Delete Mutation Bug

packages/clients/client-helpers/src/mutator.ts:110

The delete callback uses if (r) to check for updates, but null is falsy. When deleting a single object successfully returns null, this check fails and the result is lost. The test at packages/clients/client-helpers/test/mutator.test.ts:786 acknowledges this bug.

Fix: Change to if (r !== undefined)

⚠️ Minor Issues

  1. TanStack Svelte Query v6: The upgrade to @tanstack/[email protected] has breaking changes (requires Svelte 5+, new API patterns). Consider documenting migration steps.

  2. Sample Security Warning: Demo code in samples/nuxt/server/api/model/[...].ts:8-10 could be more prominent about security risks of fully open clients.

  3. Empty Array Handling: Early return guards in Nuxt sample (samples/nuxt/app/app.vue:37-39) don't handle empty arrays properly.

  4. Production Logging: Logging is hardcoded to true in samples - should be environment-dependent.

Package Management

Clean dependency management:

  • All packages properly bumped to 3.1.0
  • New client-helpers package has minimal, focused dependencies
  • Proper workspace dependencies using workspace:*
  • Catalog dependencies for consistency

New Samples

Great additions:

  • samples/nuxt/app/app.vue demonstrates proper Vue 3 + Nuxt integration
  • samples/sveltekit/ shows SvelteKit + TanStack Query patterns
  • Both samples demonstrate optimistic updates with visual feedback
  • Good examples of the new client architecture in action

Final Recommendation: ✅ APPROVE with Critical Fixes

This is an excellent refactoring PR that significantly improves the codebase:

Strengths:

  • 🎯 Exceptional test coverage - 6,000+ lines of comprehensive tests
  • 🏗️ Clean architecture - proper separation of concerns
  • 🔒 Security conscious - no security anti-patterns found
  • 🚀 Performance focused - intelligent caching and optimistic updates
  • 📦 Well-structured - clean package boundaries and dependencies

Required Fixes:

  1. Fix string replacement security issue in formatGeneratedValue()
  2. Correct CUID format argument index from [1] to [0]
  3. Fix delete mutation callback to handle null properly

Impact: This refactoring sets up a solid foundation for future client implementations while maintaining backward compatibility.

The critical issues are fixable and don't affect the overall architectural quality. This PR demonstrates high engineering standards and thorough attention to detail.


@ymc9 ymc9 added this pull request to the merge queue Dec 30, 2025
Merged via the queue into main with commit de795f5 Dec 30, 2025
8 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 31, 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.

5 participants