Skip to content

Conversation

@ymc9
Copy link
Member

@ymc9 ymc9 commented Nov 3, 2025

Summary by CodeRabbit

  • New Features

    • Full TanStack Query client integration with typed React hooks, queries, mutations, optimistic updates and cache invalidation.
    • CLI options --lite and --lite-only to emit simplified schema outputs.
    • New Next.js + TanStack Query sample app with data seeding and API route.
  • Documentation

    • Quick Start updated to link ORM and Next.js + TanStack Query samples.
  • Tests

    • Added comprehensive React Query test suite for the TanStack client.
  • Chores

    • Bumped packages to 3.0.0-beta.16 and updated dependency catalog entries.

ymc9 and others added 4 commits November 2, 2025 10:26
* chore: fix sample update workflow

* update
#356)

* WIP

* feat: tanstack-query/react CRUD hooks implementation

* fix build

* addressing PR comments

* update

* update

* migrate tests

* update

* fix test environment

* use happy-dom instead of jsdom
* feat(schema): add option for generating lite schema

* fix --lite-only option and add CLI test
Co-authored-by: ymc9 <104139426+ymc9@users.noreply.github.com>
Copilot AI review requested due to automatic review settings November 3, 2025 17:37
@claude
Copy link

claude bot commented Nov 3, 2025

Claude finished @ymc9's task —— View job


PR Review: merge dev to main (v3.0.0-beta.16)

I'll provide a comprehensive review of this large PR. Here's my plan:

Review Checklist

  • Analyze the new tanstack-query package implementation
  • Review CLI generate functionality changes
  • Examine schema generation updates (lite schema feature)
  • Review the new Next.js sample project
  • Check package management and dependency changes
  • Assess security considerations
  • Evaluate performance implications
  • Review test coverage
  • Provide overall feedback and recommendations

Code Quality & Best Practices ✅

New TanStack Query Package (packages/clients/tanstack-query/)

Strengths:

  • Well-structured TypeScript with comprehensive type definitions
  • Good separation of concerns with utilities split into logical modules
  • React hooks follow established patterns with proper TypeScript generics
  • Comprehensive test coverage with real-world scenarios (1787 lines of tests)
  • Proper error handling and query key management

Areas for consideration:

  • The use of any type in some hook implementations (packages/clients/tanstack-query/src/react.ts:250-341) could be more strictly typed
  • Consider adding JSDoc comments for complex hook types for better developer experience

Schema Generation Updates

Strengths:

  • New --lite and --lite-only CLI options provide flexibility for different use cases
  • Clean separation between full and lite schema generation in packages/sdk/src/ts-schema-generator.ts:79-86
  • Proper plugin architecture maintained with backward compatibility

Potential issue:

  • Missing validation for mutually exclusive options (--lite and --lite-only can both be true, which might be confusing)

Security Considerations ⚠️

Critical Security Note in Sample Project

The Next.js sample (samples/next.js/app/api/model/[...path]/route.ts:6-12) contains this concerning comment:

// fully open ZenStackClient is used here for demo purposes only, in a real application,
// you should use one with access policies enabled
getClient: () => db,

Recommendations:

  1. Good: The comment clearly warns about security implications
  2. ⚠️ Consider: Add a more prominent warning in the README about this being for demo only
  3. ⚠️ Consider: Include an example of properly secured client configuration

Performance Considerations ✅

TanStack Query Implementation

  • Proper query key generation prevents unnecessary re-renders
  • Infinite query support for pagination performance
  • Optimistic updates implemented correctly with rollback capability
  • Query invalidation is selective rather than blanket invalidation

Bundle Size Impact

  • New tanstack-query package adds dependencies but they're peer dependencies
  • Lite schema generation can reduce bundle size for client-side usage

Test Coverage 📊

Strengths:

  • Comprehensive test suite for tanstack-query package (1787 lines)
  • Tests cover query hooks, mutations, optimistic updates, and error scenarios
  • E2E test infrastructure updated with new schema generation options
  • CLI tests updated for new --lite functionality

Gaps identified:

  • No specific tests for the mutual exclusion of --lite and --lite-only options
  • Missing tests for error scenarios in schema generation with lite options

Architecture & Design ✅

Package Reorganization

  • Moving from packages/tanstack-query/ to packages/clients/tanstack-query/ improves organization
  • Workspace structure properly maintained
  • Clean dependency management with proper peer dependencies

API Design

  • Consistent with existing ZenStack patterns
  • Type-safe client generation maintains compile-time safety
  • Hook naming follows React Query conventions

Dependencies & Package Management ✅

  • Version bump to 3.0.0-beta.16 is appropriate for this level of changes
  • Peer dependencies properly configured for React and TanStack Query
  • Workspace dependencies correctly referenced
  • No security vulnerabilities identified in new dependencies

Potential Bugs & Issues 🐛

  1. Type Safety: Some any usage in hook implementations could hide potential runtime errors
  2. CLI Options: No validation prevents --lite and --lite-only being used together
  3. Error Handling: Sample project lacks error boundaries which could crash in production

Performance Benchmarks Missing

  • No performance comparison between full and lite schema generation
  • Memory usage impact of the new tanstack-query implementation not measured

Recommendations 📋

  1. High Priority:

    • Add validation for mutually exclusive CLI options
    • Consider stricter typing in hook implementations
    • Add more prominent security warnings in sample documentation
  2. Medium Priority:

    • Add JSDoc comments for complex TypeScript types
    • Include performance benchmarks for schema generation modes
    • Add error boundary example to Next.js sample
  3. Low Priority:

    • Consider adding configuration validation for lite schema options
    • Add examples of properly secured client configuration

Overall Assessment ⭐

This is a high-quality PR that significantly advances ZenStack v3's capabilities. The new TanStack Query integration is well-implemented, the lite schema feature addresses real performance needs, and the test coverage is comprehensive. The architectural decisions are sound and maintain consistency with existing patterns.

Approval Recommendation: ✅ Approve with minor suggestions

The code is production-ready with the understanding that the security warnings in the sample project are appropriately documented.


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 implements a "lite schema" generation feature for ZenStack v3, which allows generating TypeScript schemas without attribute metadata. The main changes include:

  • Added --lite and --lite-only CLI options for generating lite schemas
  • Modified the TypeScript schema generator to support lite mode (omitting attributes)
  • Updated API signature from positional outputDir parameter to an options object with { outDir, lite, liteOnly }
  • Added a new @zenstackhq/tanstack-query package for TanStack Query integration
  • Created sample applications demonstrating the new features
  • Version bumped to 3.0.0-beta.16

Reviewed Changes

Copilot reviewed 72 out of 89 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/sdk/src/ts-schema-generator.ts Core implementation of lite schema generation, changed API signature to accept options object
packages/cli/src/index.ts Added --lite and --lite-only CLI flags
packages/cli/src/actions/generate.ts Passes lite options to the TypeScript plugin
packages/cli/src/plugins/typescript.ts Reads lite options and passes to generator
packages/testtools/src/schema.ts Updated to use new generator API with options object
tests/*/generate.ts Updated generator calls to use new API signature
packages/clients/tanstack-query/* New package for TanStack Query integration
samples/orm/* New ORM sample application
samples/next.js/* New Next.js + TanStack Query sample
pnpm-workspace.yaml Added catalog entries for new dependencies
*/package.json Version bumps and dependency updates

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

@coderabbitai
Copy link

coderabbitai bot commented Nov 3, 2025

Walkthrough

Adds lite schema generation flags and generator options; introduces a new TanStack Query client package with React-Query hooks and rich utils; adds a Next.js sample app and generated schemas; updates many package versions to 3.0.0-beta.16; replaces some devDeps with catalog entries and changes the sample-generation workflow command.

Changes

Cohort / File(s) Summary
CLI: generate command
packages/cli/src/index.ts, packages/cli/src/actions/generate.ts, packages/cli/src/plugins/typescript.ts, packages/cli/test/generate.test.ts, packages/cli/test/ts-schema-gen.test.ts
Add --lite and --lite-only flags, thread lite/liteOnly through pluginOptions/defaultPlugins, adjust TypeScript plugin invocation, and add tests for lite behavior.
SDK: TypeScript schema generator
packages/sdk/src/ts-schema-generator.ts, packages/sdk/src/model-utils.ts
Change TsSchemaGenerator.generate to accept { outDir, lite?, liteOnly? }; propagate lite/liteOnly through generation internals and emit schema and optional schema-lite outputs.
Generator call sites
packages/testtools/src/schema.ts, tests/e2e/orm/scripts/generate.ts, tests/regression/generate.ts
Update callers to pass options object ({ outDir, lite }), add withLiteSchema flag in testtools and support loading schema-lite artifacts.
New TanStack Query client package
packages/clients/tanstack-query/package.json, packages/clients/tanstack-query/src/react.ts, packages/clients/tanstack-query/src/utils/*.ts, packages/clients/tanstack-query/test/*, packages/clients/tanstack-query/tsconfig*.json, packages/clients/tanstack-query/vitest.config.ts, packages/clients/tanstack-query/scripts/generate.ts
Add new package implementing React-Query integration: providers/context, useClientQueries/useModelQueries, internal query/mutation helpers, serialization, query-key/URL builders, invalidation and optimistic-update plumbing, nested read/write visitors, mutator, query analysis utilities, tests, configs, and generation script.
Removed legacy tanstack-query
packages/tanstack-query/package.json, packages/tanstack-query/src/react.ts, packages/tanstack-query/tsconfig.json
Delete previous standalone package manifest, adapter module, and tsconfig (superseded by packages/clients/tanstack-query).
New Next.js sample app
samples/next.js/* (e.g. package.json, app/**, zenstack/**, lib/db.ts, configs, seed.ts)
Add a full Next.js sample with API route, providers, generated schema/schema-lite, types, seed script, Tailwind/PostCSS, TS and ESLint configs, and package manifest.
Generated schemas & types for tests/samples
packages/clients/tanstack-query/test/schemas/basic/*, samples/next.js/zenstack/*, samples/next.js/zenstack/schema.zmodel
Add generated schema, schema-lite, model and input TypeScript files and zmodel sources for tests and the sample.
Mutator / Visitors / Query analysis utilities
packages/clients/tanstack-query/src/utils/mutator.ts, .../nested-read-visitor.ts, .../nested-write-visitor.ts, .../query-analysis.ts, .../serialization.ts, .../common.ts, .../types.ts
Add modules implementing optimistic mutation application, nested read/write visitors, query analysis, SuperJSON-based serialization, query-key & URL builders, invalidation and optimistic-update setup, and related types.
Version bumps to 3.0.0-beta.16
package.json, packages/*/package.json, samples/orm/package.json, tests/*/package.json, etc.
Bump many packages from 3.0.0-beta.153.0.0-beta.16.
Dependency / pnpm / workflow / README updates
.github/workflows/update-samples.yml, pnpm-workspace.yaml, various package.json devDeps (replaced with catalog:), README.md
Replace some devDeps with catalog: placeholders and add catalog entries (react, next, etc.), change workflow step to run npm run generate (was npx zen generate), and update README quick-start sample links.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~150 minutes

  • Files/areas requiring extra attention:
    • packages/clients/tanstack-query/src/react.ts and packages/clients/tanstack-query/src/utils/* (complex public API, async/mutation/invalidation flows).
    • packages/clients/tanstack-query/src/utils/mutator.ts and nested visitors (deep recursive logic, schema-driven mutation application).
    • packages/sdk/src/ts-schema-generator.ts (API change, multi-output generation, lite propagation).
    • Cross-package consistency after removal of legacy packages/tanstack-query and new packages/clients/tanstack-query.
    • Generated artifacts and Next.js sample wiring (ensure schema/schema-lite parity and API route/provider correctness).

Possibly related PRs

Poem

🐇 I nibbled code in moonlit night,

two schemata now gleam — full and light.
Hooks hop forth with queries keen,
Next app blooms in fields of green.
Beta sixteen — carrots bright! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly summarizes the main objective: merging the dev branch into main with a specific version bump to v3.0.0-beta.16, which aligns with the extensive changeset that includes version bumps across numerous package.json files and new features like the TanStack Query client integration.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

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

@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: 10

Caution

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

⚠️ Outside diff range comments (1)
packages/sdk/src/ts-schema-generator.ts (1)

1148-1152: Keep full-schema imports when both variants are emitted

When the CLI runs with --lite (without --lite-only) we still generate schema.ts alongside schema-lite.ts. Routing models.ts/input.ts to schema-lite in that case drops model- and field-level metadata (attributes, computed-field stubs, etc.), which the runtime types rely on. The result is that existing consumers of models.ts suddenly see partial schema definitions.

Please keep the lite import exclusively for the liteOnly scenario. A concise fix is to pass options.liteOnly into generateSchemaImport in both call sites (models and inputs), leaving the default path unchanged whenever the full schema exists.

[suggested_diff]

-        statements.push(this.generateSchemaImport(model, true, true, !!(options.lite || options.liteOnly)));
+        statements.push(this.generateSchemaImport(model, true, true, !!options.liteOnly));
...
-        statements.push(this.generateSchemaImport(model, false, true, !!(options.lite || options.liteOnly)));
+        statements.push(this.generateSchemaImport(model, false, true, !!options.liteOnly));

Also applies to: 1321-1325

🧹 Nitpick comments (5)
samples/next.js/zenstack/input.ts (1)

31-50: LGTM! Consistent Post model exports.

The Post model type exports mirror the User model structure, providing the same comprehensive coverage of operations. The consistency across models is excellent for developer experience.

Optional: Consider adding a blank line between model sections (after line 30) in the code generator to improve readability.

samples/next.js/README.md (1)

1-7: Consider expanding documentation.

The README is minimal but functional. For improved developer experience, consider adding sections like: project structure overview, key files/directories, environment setup (database config, env vars), and links to relevant ZenStack/TanStack Query documentation.

packages/cli/test/ts-schema-gen.test.ts (1)

424-444: Test logic is correct, but consider improving the API.

The test correctly validates lite schema generation. However, the function call with multiple undefined parameters followed by true is not very readable:

generateTsSchema(..., undefined, undefined, undefined, true)

Consider refactoring generateTsSchema to accept an options object instead of positional parameters for better maintainability.

samples/next.js/lib/db.ts (1)

6-10: Consider using an absolute path or environment variable for the database location.

The relative path './zenstack/dev.db' may resolve inconsistently depending on the working directory at runtime. In Next.js applications, especially when deployed to serverless environments, the working directory can vary.

Apply this diff to use an absolute path:

+import path from 'node:path';
+
 export const db = new ZenStackClient(schema, {
     dialect: new SqliteDialect({
-        database: new SQLite('./zenstack/dev.db'),
+        database: new SQLite(path.join(process.cwd(), 'zenstack/dev.db')),
     }),
 });

Alternatively, use an environment variable for better configurability:

import path from 'node:path';

const dbPath = process.env.DATABASE_PATH || path.join(process.cwd(), 'zenstack/dev.db');

export const db = new ZenStackClient(schema, {
    dialect: new SqliteDialect({
        database: new SQLite(dbPath),
    }),
});
packages/cli/src/index.ts (1)

62-63: Prevent conflicting lite flags. Allowing --lite and --lite-only simultaneously produces an ambiguous combination (both booleans true) that downstream code must reconcile. Mark them as mutually exclusive to fail fast at the CLI layer.

-        .addOption(new Option('--lite', 'also generate a lite version of schema without attributes').default(false))
-        .addOption(new Option('--lite-only', 'only generate lite version of schema without attributes').default(false))
+        .addOption(
+            new Option('--lite', 'also generate a lite version of schema without attributes')
+                .conflicts('liteOnly')
+                .default(false),
+        )
+        .addOption(
+            new Option('--lite-only', 'only generate lite version of schema without attributes')
+                .conflicts('lite')
+                .default(false),
+        )
📜 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 ead10f7 and 7839d5f.

⛔ Files ignored due to path filters (7)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • samples/next.js/app/favicon.ico is excluded by !**/*.ico
  • samples/next.js/public/file.svg is excluded by !**/*.svg
  • samples/next.js/public/globe.svg is excluded by !**/*.svg
  • samples/next.js/public/next.svg is excluded by !**/*.svg
  • samples/next.js/public/vercel.svg is excluded by !**/*.svg
  • samples/next.js/public/window.svg is excluded by !**/*.svg
📒 Files selected for processing (72)
  • .github/workflows/update-samples.yml (1 hunks)
  • README.md (1 hunks)
  • package.json (2 hunks)
  • packages/cli/package.json (2 hunks)
  • packages/cli/src/actions/generate.ts (2 hunks)
  • packages/cli/src/index.ts (1 hunks)
  • packages/cli/src/plugins/typescript.ts (1 hunks)
  • packages/cli/test/generate.test.ts (1 hunks)
  • packages/cli/test/ts-schema-gen.test.ts (1 hunks)
  • packages/clients/tanstack-query/package.json (1 hunks)
  • packages/clients/tanstack-query/scripts/generate.ts (1 hunks)
  • packages/clients/tanstack-query/src/react.ts (1 hunks)
  • packages/clients/tanstack-query/src/utils/common.ts (1 hunks)
  • packages/clients/tanstack-query/src/utils/mutator.ts (1 hunks)
  • packages/clients/tanstack-query/src/utils/nested-read-visitor.ts (1 hunks)
  • packages/clients/tanstack-query/src/utils/nested-write-visitor.ts (1 hunks)
  • packages/clients/tanstack-query/src/utils/query-analysis.ts (1 hunks)
  • packages/clients/tanstack-query/src/utils/serialization.ts (1 hunks)
  • packages/clients/tanstack-query/src/utils/types.ts (1 hunks)
  • packages/clients/tanstack-query/test/react-query.test.tsx (1 hunks)
  • packages/clients/tanstack-query/test/schemas/basic/input.ts (1 hunks)
  • packages/clients/tanstack-query/test/schemas/basic/models.ts (1 hunks)
  • packages/clients/tanstack-query/test/schemas/basic/schema-lite.ts (1 hunks)
  • packages/clients/tanstack-query/test/schemas/basic/schema.zmodel (1 hunks)
  • packages/clients/tanstack-query/tsconfig.json (1 hunks)
  • packages/clients/tanstack-query/tsconfig.test.json (1 hunks)
  • packages/clients/tanstack-query/vitest.config.ts (1 hunks)
  • packages/common-helpers/package.json (1 hunks)
  • packages/config/eslint-config/package.json (1 hunks)
  • packages/config/typescript-config/package.json (1 hunks)
  • packages/config/vitest-config/package.json (1 hunks)
  • packages/create-zenstack/package.json (1 hunks)
  • packages/dialects/sql.js/package.json (1 hunks)
  • packages/language/package.json (1 hunks)
  • packages/orm/package.json (2 hunks)
  • packages/plugins/policy/package.json (2 hunks)
  • packages/schema/package.json (1 hunks)
  • packages/sdk/package.json (1 hunks)
  • packages/sdk/src/model-utils.ts (2 hunks)
  • packages/sdk/src/ts-schema-generator.ts (13 hunks)
  • packages/server/package.json (3 hunks)
  • packages/tanstack-query/package.json (0 hunks)
  • packages/tanstack-query/src/react.ts (0 hunks)
  • packages/tanstack-query/tsconfig.json (0 hunks)
  • packages/testtools/package.json (2 hunks)
  • packages/testtools/src/schema.ts (5 hunks)
  • packages/zod/package.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • samples/next.js/.gitignore (1 hunks)
  • samples/next.js/README.md (1 hunks)
  • samples/next.js/app/api/model/[...path]/route.ts (1 hunks)
  • samples/next.js/app/globals.css (1 hunks)
  • samples/next.js/app/layout.tsx (1 hunks)
  • samples/next.js/app/page.tsx (1 hunks)
  • samples/next.js/app/providers.tsx (1 hunks)
  • samples/next.js/eslint.config.mjs (1 hunks)
  • samples/next.js/lib/db.ts (1 hunks)
  • samples/next.js/next.config.ts (1 hunks)
  • samples/next.js/package.json (1 hunks)
  • samples/next.js/postcss.config.mjs (1 hunks)
  • samples/next.js/tsconfig.json (1 hunks)
  • samples/next.js/zenstack/input.ts (1 hunks)
  • samples/next.js/zenstack/models.ts (1 hunks)
  • samples/next.js/zenstack/schema-lite.ts (1 hunks)
  • samples/next.js/zenstack/schema.ts (1 hunks)
  • samples/next.js/zenstack/schema.zmodel (1 hunks)
  • samples/next.js/zenstack/seed.ts (1 hunks)
  • samples/orm/package.json (2 hunks)
  • tests/e2e/orm/scripts/generate.ts (2 hunks)
  • tests/e2e/package.json (1 hunks)
  • tests/regression/generate.ts (1 hunks)
  • tests/regression/package.json (1 hunks)
💤 Files with no reviewable changes (3)
  • packages/tanstack-query/src/react.ts
  • packages/tanstack-query/tsconfig.json
  • packages/tanstack-query/package.json
🧰 Additional context used
🧠 Learnings (1)
📚 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/next.js/zenstack/schema-lite.ts
  • packages/sdk/src/model-utils.ts
  • packages/clients/tanstack-query/src/utils/query-analysis.ts
  • samples/next.js/zenstack/schema.ts
  • packages/clients/tanstack-query/test/schemas/basic/schema-lite.ts
  • samples/next.js/zenstack/input.ts
  • samples/next.js/zenstack/models.ts
⏰ 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). (2)
  • GitHub Check: build-test (20.x, sqlite)
  • GitHub Check: build-test (20.x, postgresql)
🔇 Additional comments (59)
packages/sdk/src/model-utils.ts (1)

30-30: Comment improvements enhance clarity.

The comment updates make the code's intent clearer:

  • Line 30 correctly emphasizes that inherited fields may have different AST node identities, removing the "abstract" qualifier that was unnecessarily restrictive.
  • Line 40 better specifies that only explicit @id or @@id fields gate the unique-field fallback logic, and "don't further check" is more precise than "don't check."

Also applies to: 40-40

samples/next.js/zenstack/input.ts (3)

1-7: LGTM! Well-structured auto-generated file header.

The warning comments and ESLint disable directive are appropriate for auto-generated code, clearly signaling to developers that manual modifications should be avoided.


11-30: LGTM! Comprehensive and type-safe User model exports.

The type exports provide complete coverage of all CRUD operations and query builders for the User model. The consistent naming convention and proper use of TypeScript generics ensure type safety throughout.


8-10: Verify the schema-lite file is generated.

The imports are well-structured and align with the lite schema generation feature introduced in this PR. Ensure that the ./schema-lite file is properly generated alongside this file.

Run the following script to verify the schema-lite file exists:

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

3-3: Version bump applied correctly.

Straightforward version increment aligns with the PR release cycle.

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

4-4: Version bump applied correctly.

Consistent version increment for this private config package.

packages/server/package.json (2)

161-161: Verify Next.js 16 compatibility.

The peerDependency range has been expanded from ^15.0.0 to ^15.0.0 || ^16.0.0, introducing support for a major version bump. This requires confirmation that the server implementations are tested and compatible with Next.js 16.

Check that:

  • All Next.js adapter code (packages/server/src/next.ts and related) functions correctly with Next.js 16.
  • No breaking changes from Next.js 15→16 affect the server integration layer.
  • This change is intentional and has been validated through testing.

149-149: Verify catalog reference is defined.

The devDependency for "next" uses the catalog: reference, which requires a corresponding entry in the root pnpm workspace catalog configuration.

packages/zod/package.json (1)

3-3: Version bump applied correctly.

Straightforward version increment for private schema package.

tests/e2e/package.json (1)

3-3: Version bump applied correctly.

Consistent version increment for test package.

packages/plugins/policy/package.json (2)

3-3: Version bump applied correctly.

Consistent version increment for policy plugin package.


48-48: Verify catalog reference for @types/better-sqlite3.

The devDependency has been migrated to use the catalog: reference. Ensure this is defined in the root pnpm catalog configuration alongside other catalog: references.

packages/sdk/package.json (1)

3-3: Version bump applied correctly.

Consistent version increment; catalog: references are already in use for this package.

samples/next.js/README.md (1)

1-7: Verify sample scripts are defined.

The README references three commands (pnpm install, pnpm db:init, pnpm dev). Ensure that pnpm db:init and pnpm dev scripts are properly defined in the sample's package.json.

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

3-3: Version bump is consistent.

Beta release version bump aligns with the coordinated monorepo release to 3.0.0-beta.16.

packages/language/package.json (1)

4-4: Version bump is consistent.

Beta release version bump aligns with the coordinated monorepo release to 3.0.0-beta.16.

tests/regression/package.json (1)

3-3: Version bump is consistent.

Beta release version bump aligns with the coordinated monorepo release to 3.0.0-beta.16.

packages/testtools/package.json (1)

3-3: Version bump and catalog migration are consistent.

The 3.0.0-beta.15 → 3.0.0-beta.16 version bump matches the release objective. The migration of @types/better-sqlite3 from a pinned version to catalog: is consistent with the same change applied to packages/orm/package.json, indicating a coordinated dependency catalog standardization.

Verify that catalog: references are resolvable in the pnpm dependency catalog configuration.

Also applies to: 51-51

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

3-3: Version bump is consistent.

Beta release version bump aligns with the coordinated monorepo release to 3.0.0-beta.16.

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

3-3: Version bump is consistent.

Beta release version bump aligns with the coordinated monorepo release to 3.0.0-beta.16.

packages/orm/package.json (1)

3-3: Version bump and catalog migration look correct.

Consistent 3.0.0-beta.15 → 3.0.0-beta.16 bump aligns with PR objectives. The migration of @types/better-sqlite3 from a pinned version to catalog: follows the same pattern applied across the monorepo.

Please verify that the catalog: references for @types/better-sqlite3 are properly defined in the pnpm dependency catalog (likely pnpm-workspace.yaml or .npmrc):

Also applies to: 92-92

packages/clients/tanstack-query/tsconfig.json (1)

1-7: New TypeScript configuration looks appropriate.

The configuration properly extends the shared base configuration and targets ESNext for a modern client package. The include pattern aligns with typical project structure.

Verify that the base TypeScript config path resolves correctly:

packages/dialects/sql.js/package.json (1)

3-3: LGTM! Version bump aligns with the release.

The version increment to 3.0.0-beta.16 is consistent with the PR objectives and matches the broader monorepo version update.

packages/schema/package.json (1)

3-3: LGTM! Version bump aligns with the release.

The version increment to 3.0.0-beta.16 is consistent with the PR objectives and matches the broader monorepo version update.

samples/next.js/.gitignore (1)

1-41: LGTM! Comprehensive Next.js .gitignore configuration.

The ignore patterns are standard and appropriate for a Next.js sample project, covering dependencies, build outputs, environment files, and platform-specific artifacts.

README.md (1)

54-55: LGTM! Improved documentation with explicit sample links.

The updated Quick Start section provides clear paths to both the ORM sample and the new Next.js + TanStack Query sample, making it easier for users to get started with different use cases.

package.json (2)

3-3: LGTM! Version bump aligns with the release.

The version increment to 3.0.0-beta.16 is consistent with the PR objectives and matches the broader monorepo version update.


35-35: LGTM! Prisma devDependency addition is appropriate.

Adding Prisma as a devDependency makes sense given that ZenStack v3 relies on Prisma for database migrations, as documented in the README. The use of catalog: ensures version consistency across the monorepo.

tests/regression/generate.ts (2)

19-19: LGTM! Variable rename improves consistency.

Renaming outputDir to outDir aligns with the new API's option property name and improves code clarity.


24-24: LGTM! API refactoring to options object pattern.

The change from passing a string path to an options object ({ outDir }) aligns with the new TsSchemaGeneratorOptions interface mentioned in the PR summary. This provides better extensibility for additional options like lite and liteOnly.

pnpm-workspace.yaml (1)

18-26: LGTM! Catalog additions support new Next.js and TanStack Query features.

The new catalog entries provide centralized version management for the React ecosystem dependencies used in the Next.js sample and TanStack Query client. The versions are recent and compatible:

  • React 19.2.0 (stable)
  • Next.js 16.0.1 (latest)
  • TanStack Query 5.90.6 (v5 stable)
.github/workflows/update-samples.yml (1)

98-99: Verify that sample repositories have the generate script defined.

The change from npx zen generate to npm run generate delegates the generation command to each sample's package.json. Ensure all v3-sample repositories have a generate script defined, or this workflow will fail.

Run the following script to check if sample repos in this monorepo have the generate script:

samples/next.js/app/api/model/[...path]/route.ts (1)

6-12: Good security documentation for the demo.

The handler setup is correct, and the inline comment appropriately warns that this uses a fully open client suitable only for demos. This is helpful guidance for developers adapting the sample.

packages/clients/tanstack-query/package.json (1)

26-70: Verify optional peer dependencies align with package usage.

The package exports only a ./react entry point but marks both @tanstack/react-query and react as optional peer dependencies. This configuration may be intentional for build/type-generation scenarios, but ensure it aligns with the package's actual runtime requirements.

Run the following script to check if the package has non-React usage patterns:

samples/orm/package.json (1)

3-3: LGTM!

The version bump, private flag, and migration to catalog references are all appropriate for this sample package.

Also applies to: 6-6, 20-20, 24-24

packages/clients/tanstack-query/src/utils/serialization.ts (4)

17-24: LGTM!

The Buffer serializer correctly uses base64 encoding for cross-environment compatibility.


26-32: LGTM!

The serialize function correctly wraps SuperJSON and provides a clean API.


34-39: LGTM!

The deserialize function correctly reconstructs the SuperJSON format.


7-10: Verify the toStringTag check for Decimal interop.

The check v?.toStringTag === '[object Decimal]' accesses a toStringTag property directly on the object. However, Decimal.js instances may not have this property. If you intended to check the object's @@toStringTag symbol, use:

Object.prototype.toString.call(v) === '[object Decimal]'

Or if Decimal.js defines a custom property for interop, verify that it's named toStringTag.

samples/next.js/zenstack/schema.ts (1)

1-115: Generated file - no review needed.

This is an auto-generated schema file with a clear warning header. The structure follows expected patterns for ZenStack schema definitions.

packages/clients/tanstack-query/src/utils/nested-read-visitor.ts (5)

3-19: LGTM!

The callback type and constructor are well-defined and follow good TypeScript practices.


21-31: LGTM!

The callback invocation and validation logic correctly handle traversal control and type safety.


33-41: LGTM!

The select/include detection correctly prioritizes select over include, consistent with Prisma semantics.


43-56: LGTM!

The iteration and recursion logic correctly handles nested selections, _count, and relation traversal.


58-60: LGTM!

The visit method provides a clean entry point for traversal.

packages/cli/package.json (1)

6-6: LGTM!

The version bump and catalog reference migration are appropriate for this beta release.

Also applies to: 48-48

packages/cli/test/generate.test.ts (1)

48-60: LGTM!

The new tests correctly validate the --lite and --lite-only CLI options, following the established test patterns.

packages/clients/tanstack-query/vitest.config.ts (1)

1-11: LGTM!

The Vitest configuration correctly merges the base config and includes both TypeScript and TSX test files, which is appropriate for a package with React components.

packages/clients/tanstack-query/tsconfig.test.json (1)

1-8: LGTM!

The test TypeScript configuration is well-structured, appropriately extends the base config, and correctly configures JSX support for React testing.

packages/clients/tanstack-query/scripts/generate.ts (1)

1-27: LGTM!

The schema generation script is well-implemented with proper error handling and clean async/await patterns. The use of liteOnly: true is appropriate for test schema generation.

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

93-102: LGTM!

The restructuring of defaultPlugins to include options objects properly supports the new lite and liteOnly flags. The implementation correctly threads these options through to plugin generation.

packages/cli/src/plugins/typescript.ts (1)

19-25: LGTM!

The plugin correctly extracts and passes the lite and liteOnly options to the generator. The explicit === true checks ensure type safety when reading from the loosely-typed pluginOptions object.

tests/e2e/orm/scripts/generate.ts (1)

20-32: LGTM!

The refactor correctly adapts to the new TsSchemaGenerator.generate() API, which now accepts an options object instead of positional parameters.

packages/clients/tanstack-query/src/utils/types.ts (1)

1-19: LGTM!

The utility types are well-defined and will be useful for the TanStack Query client. The ORMWriteActions comprehensively covers all write operations, and MaybePromise provides flexibility for async handling.

Note: MaybePromise<T> includes both Promise<T> and PromiseLike<T>. Since Promise<T> implements PromiseLike<T>, you could simplify to T | PromiseLike<T>. However, the current explicit approach may be clearer for API consumers.

samples/next.js/app/layout.tsx (1)

1-27: Verify that the Providers component is marked as a Client Component.

The layout correctly wraps children with the Providers component. However, in Next.js 13+ with the app directory, components are Server Components by default. If Providers uses client-side hooks (such as React Query's QueryClientProvider), it must include the 'use client' directive at the top of the file.

Run the following script to verify the Providers component implementation:

packages/clients/tanstack-query/test/schemas/basic/input.ts (4)

1-10: LGTM! Well-structured header and imports.

The auto-generated file warning is clear, and the imports are properly organized with type-only imports and $ prefixes for clarity.


11-30: LGTM! Comprehensive User model type definitions.

The type exports provide a complete set of strongly-typed ORM operations and helpers for the User model, following a consistent naming convention.


31-50: LGTM! Post model types follow the same consistent pattern.

The type definitions maintain consistency with the User model, ensuring uniform API across all models.


51-70: LGTM! Category model completes the consistent pattern.

All three models maintain identical type structures, which ensures a predictable and type-safe API for the lite schema generation feature being tested.

@claude
Copy link

claude bot commented Nov 3, 2025

Claude finished @ymc9's task —— View job


PR Review: merge dev to main (v3.0.0-beta.16)

I'll provide a comprehensive review of this large PR. Here's my plan:

Review Checklist

  • Analyze the new tanstack-query package implementation
  • Review CLI generate functionality changes
  • Examine schema generation updates (lite schema feature)
  • Review the new Next.js sample project
  • Check package management and dependency changes
  • Assess security considerations
  • Evaluate performance implications
  • Review test coverage
  • Provide overall feedback and recommendations

Code Quality & Best Practices ✅

New TanStack Query Package (packages/clients/tanstack-query/)

Strengths:

  • Well-structured TypeScript with comprehensive type definitions
  • Good separation of concerns with utilities split into logical modules
  • React hooks follow established patterns with proper TypeScript generics
  • Comprehensive test coverage with real-world scenarios (1787 lines of tests)
  • Proper error handling and query key management

Areas for consideration:

  • The use of any type in some hook implementations (packages/clients/tanstack-query/src/react.ts:250-341) could be more strictly typed
  • Consider adding JSDoc comments for complex hook types for better developer experience

Schema Generation Updates

Strengths:

  • New --lite and --lite-only CLI options provide flexibility for different use cases
  • Clean separation between full and lite schema generation in packages/sdk/src/ts-schema-generator.ts:79-86
  • Proper plugin architecture maintained with backward compatibility

Potential issue:

  • Missing validation for mutually exclusive options (--lite and --lite-only can both be true, which might be confusing)

Security Considerations ⚠️

Critical Security Note in Sample Project

The Next.js sample (samples/next.js/app/api/model/[...path]/route.ts:6-12) contains this concerning comment:

// fully open ZenStackClient is used here for demo purposes only, in a real application,
// you should use one with access policies enabled
getClient: () => db,

Recommendations:

  1. Good: The comment clearly warns about security implications
  2. ⚠️ Consider: Add a more prominent warning in the README about this being for demo only
  3. ⚠️ Consider: Include an example of properly secured client configuration

Performance Considerations ✅

TanStack Query Implementation

  • Proper query key generation prevents unnecessary re-renders
  • Infinite query support for pagination performance
  • Optimistic updates implemented correctly with rollback capability
  • Query invalidation is selective rather than blanket invalidation

Bundle Size Impact

  • New tanstack-query package adds dependencies but they're peer dependencies
  • Lite schema generation can reduce bundle size for client-side usage

Test Coverage 📊

Strengths:

  • Comprehensive test suite for tanstack-query package (1787 lines)
  • Tests cover query hooks, mutations, optimistic updates, and error scenarios
  • E2E test infrastructure updated with new schema generation options
  • CLI tests updated for new --lite functionality

Gaps identified:

  • No specific tests for the mutual exclusion of --lite and --lite-only options
  • Missing tests for error scenarios in schema generation with lite options

Architecture & Design ✅

Package Reorganization

  • Moving from packages/tanstack-query/ to packages/clients/tanstack-query/ improves organization
  • Workspace structure properly maintained
  • Clean dependency management with proper peer dependencies

API Design

  • Consistent with existing ZenStack patterns
  • Type-safe client generation maintains compile-time safety
  • Hook naming follows React Query conventions

Dependencies & Package Management ✅

  • Version bump to 3.0.0-beta.16 is appropriate for this level of changes
  • Peer dependencies properly configured for React and TanStack Query
  • Workspace dependencies correctly referenced
  • No security vulnerabilities identified in new dependencies

Potential Bugs & Issues 🐛

  1. Type Safety: Some any usage in hook implementations could hide potential runtime errors
  2. CLI Options: No validation prevents --lite and --lite-only being used together
  3. Error Handling: Sample project lacks error boundaries which could crash in production

Performance Benchmarks Missing

  • No performance comparison between full and lite schema generation
  • Memory usage impact of the new tanstack-query implementation not measured

Recommendations 📋

  1. High Priority:

    • Add validation for mutually exclusive CLI options
    • Consider stricter typing in hook implementations
    • Add more prominent security warnings in sample documentation
  2. Medium Priority:

    • Add JSDoc comments for complex TypeScript types
    • Include performance benchmarks for schema generation modes
    • Add error boundary example to Next.js sample
  3. Low Priority:

    • Consider adding configuration validation for lite schema options
    • Add examples of properly secured client configuration

Overall Assessment ⭐

This is a high-quality PR that significantly advances ZenStack v3's capabilities. The new TanStack Query integration is well-implemented, the lite schema feature addresses real performance needs, and the test coverage is comprehensive. The architectural decisions are sound and maintain consistency with existing patterns.

Approval Recommendation: ✅ Approve with minor suggestions

The code is production-ready with the understanding that the security warnings in the sample project are appropriately documented.


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

♻️ Duplicate comments (3)
packages/clients/tanstack-query/src/react.ts (1)

180-182: Past review issue has been addressed.

The types for useUpdateMany and useDeleteMany now correctly use single argument types (UpdateManyArgs<Schema, Model> and DeleteManyArgs<Schema, Model>) rather than arrays, matching Prisma's contracts. The issue raised in the previous review has been fixed.

Also applies to: 196-198

packages/clients/tanstack-query/test/react-query.test.tsx (2)

1536-1538: Past review issue has been addressed.

This mock correctly uses .put(/.*/) for the update endpoint, matching the HTTP verb that useUpdate issues. The issue raised in the previous review has been fixed.


34-40: Fix makeUrl to match client URL generation.

The local makeUrl helper preserves the model name casing (e.g., /api/model/User/...), but the client's makeUrl lowercases it (e.g., /api/model/user/...), causing mock mismatches. Additionally, the local helper doesn't handle serialization metadata.

Import and use the shared makeUrl helper:

-import { getQueryKey } from '../src/utils/common';
+import { DEFAULT_QUERY_ENDPOINT, getQueryKey, makeUrl as makeClientUrl } from '../src/utils/common';

-function makeUrl(model: string, operation: string, args?: unknown) {
-    let r = `${BASE_URL}/api/model/${model}/${operation}`;
-    if (args) {
-        r += `?q=${encodeURIComponent(JSON.stringify(args))}`;
-    }
-    return r;
-}
+function makeUrl(model: string, operation: string, args?: unknown) {
+    return makeClientUrl(`${BASE_URL}${DEFAULT_QUERY_ENDPOINT}`, model, operation, args);
+}
📜 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 7839d5f and bf2ab4b.

📒 Files selected for processing (2)
  • packages/clients/tanstack-query/src/react.ts (1 hunks)
  • packages/clients/tanstack-query/test/react-query.test.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/clients/tanstack-query/test/react-query.test.tsx (3)
packages/clients/tanstack-query/src/react.ts (3)
  • Provider (82-82)
  • QuerySettingsProvider (75-75)
  • useClientQueries (228-233)
packages/clients/tanstack-query/src/utils/common.ts (2)
  • makeUrl (211-223)
  • getQueryKey (180-190)
packages/clients/tanstack-query/test/schemas/basic/schema-lite.ts (1)
  • schema (9-123)
packages/clients/tanstack-query/src/react.ts (3)
packages/clients/tanstack-query/src/utils/common.ts (9)
  • APIContext (111-126)
  • ExtraQueryOptions (101-106)
  • ExtraMutationOptions (81-96)
  • makeUrl (211-223)
  • getQueryKey (180-190)
  • fetcher (128-159)
  • marshal (192-199)
  • setupInvalidation (234-255)
  • setupOptimisticUpdate (313-357)
packages/schema/src/schema.ts (2)
  • SchemaDef (10-18)
  • GetModels (109-109)
packages/common-helpers/src/lower-case-first.ts (1)
  • lowerCaseFirst (1-3)
🪛 Biome (2.1.2)
packages/clients/tanstack-query/src/react.ts

[error] 85-85: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 230-230: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 251-251: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 255-255: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 259-259: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 263-263: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 267-267: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 271-271: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 275-275: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 279-279: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 283-283: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 287-287: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 291-291: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 295-295: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 299-299: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 303-303: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 307-307: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 311-311: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 315-315: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 319-319: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 323-323: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 327-327: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 331-331: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 335-335: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 339-339: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 351-351: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 359-359: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 374-374: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 382-382: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 400-400: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 404-404: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 430-430: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 434-434: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 474-474: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 475-475: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 494-494: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 528-528: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

⏰ 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: build-test (20.x, postgresql)
  • GitHub Check: build-test (20.x, sqlite)
  • GitHub Check: claude-review
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
packages/clients/tanstack-query/src/react.ts (3)

84-87: Static analysis false positive: useHooksContext is a valid custom hook.

The Biome linter is flagging this and other hook calls as violations of the "hooks at top level" rule. However, this is a false positive. The pattern used throughout this file (factory functions that return hooks) is a valid and common library design pattern. useHooksContext is itself a custom hook that correctly calls useContext at its top level, and it's used within other hooks like useInternalQuery, useInternalMutation, etc.

The linter cannot distinguish between calling hooks in nested callbacks (which would be wrong) and calling hooks within custom hook factories (which is correct).


282-316: Verify checkReadBack parameter consistency.

The checkReadBack parameter is passed differently for various mutations:

  • true for: create (283), createManyAndReturn (291), update (295), updateManyAndReturn (303), upsert (307), delete (311)
  • false for: createMany (287), updateMany (299), deleteMany (315)

This pattern makes sense since batch operations (createMany, updateMany, deleteMany) return BatchResult (just counts), while other operations return model entities that need read-back checks. Please verify this aligns with the expected behavior when policy restrictions prevent reading back mutation results.


476-488: LGTM! Correct HTTP method handling for mutations.

The mutation function correctly handles different HTTP methods:

  • DELETE requests encode args in the URL query string
  • POST/PUT requests send args in the request body as JSON

This follows standard REST API conventions.

packages/clients/tanstack-query/test/react-query.test.tsx (1)

1-1787: Comprehensive test coverage.

This test suite provides excellent coverage of the TanStack Query client, including:

  • Query variants (standard, suspense, infinite)
  • Mutation operations (create, update, delete, upsert)
  • Optimistic updates with nested relations
  • Invalidation strategies
  • Custom optimistic data providers
  • Edge cases (optional relations, nested mutations)

Once the makeUrl helper and HTTP verb issues are fixed, this suite should provide robust validation of the client functionality.

@claude
Copy link

claude bot commented Nov 4, 2025

Claude finished @ymc9's task —— View job


PR Review: merge dev to main (v3.0.0-beta.16)

I'll provide a comprehensive review of this large PR. Here's my plan:

Review Checklist

  • Analyze the new tanstack-query package implementation
  • Review CLI generate functionality changes
  • Examine schema generation updates (lite schema feature)
  • Review the new Next.js sample project
  • Check package management and dependency changes
  • Assess security considerations
  • Evaluate performance implications
  • Review test coverage
  • Provide overall feedback and recommendations

Code Quality & Best Practices ✅

New TanStack Query Package (packages/clients/tanstack-query/)

Strengths:

  • Well-structured TypeScript with comprehensive type definitions
  • Good separation of concerns with utilities split into logical modules
  • React hooks follow established patterns with proper TypeScript generics
  • Comprehensive test coverage with real-world scenarios (1787 lines of tests)
  • Proper error handling and query key management

Areas for consideration:

  • The use of any type in some hook implementations (packages/clients/tanstack-query/src/react.ts:250-341) could be more strictly typed
  • Consider adding JSDoc comments for complex hook types for better developer experience

Schema Generation Updates

Strengths:

  • New --lite and --lite-only CLI options provide flexibility for different use cases
  • Clean separation between full and lite schema generation in packages/sdk/src/ts-schema-generator.ts:79-86
  • Proper plugin architecture maintained with backward compatibility

Potential issue:

  • Missing validation for mutually exclusive options (--lite and --lite-only can both be true, which might be confusing)

Security Considerations ⚠️

Critical Security Note in Sample Project

The Next.js sample (samples/next.js/app/api/model/[...path]/route.ts:6-12) contains this concerning comment:

// fully open ZenStackClient is used here for demo purposes only, in a real application,
// you should use one with access policies enabled
getClient: () => db,

Recommendations:

  1. Good: The comment clearly warns about security implications
  2. ⚠️ Consider: Add a more prominent warning in the README about this being for demo only
  3. ⚠️ Consider: Include an example of properly secured client configuration

Performance Considerations ✅

TanStack Query Implementation

  • Proper query key generation prevents unnecessary re-renders
  • Infinite query support for pagination performance
  • Optimistic updates implemented correctly with rollback capability
  • Query invalidation is selective rather than blanket invalidation

Bundle Size Impact

  • New tanstack-query package adds dependencies but they're peer dependencies
  • Lite schema generation can reduce bundle size for client-side usage

Test Coverage 📊

Strengths:

  • Comprehensive test suite for tanstack-query package (1787 lines)
  • Tests cover query hooks, mutations, optimistic updates, and error scenarios
  • E2E test infrastructure updated with new schema generation options
  • CLI tests updated for new --lite functionality

Gaps identified:

  • No specific tests for the mutual exclusion of --lite and --lite-only options
  • Missing tests for error scenarios in schema generation with lite options

Architecture & Design ✅

Package Reorganization

  • Moving from packages/tanstack-query/ to packages/clients/tanstack-query/ improves organization
  • Workspace structure properly maintained
  • Clean dependency management with proper peer dependencies

API Design

  • Consistent with existing ZenStack patterns
  • Type-safe client generation maintains compile-time safety
  • Hook naming follows React Query conventions

Dependencies & Package Management ✅

  • Version bump to 3.0.0-beta.16 is appropriate for this level of changes
  • Peer dependencies properly configured for React and TanStack Query
  • Workspace dependencies correctly referenced
  • No security vulnerabilities identified in new dependencies

Potential Bugs & Issues 🐛

  1. Type Safety: Some any usage in hook implementations could hide potential runtime errors
  2. CLI Options: No validation prevents --lite and --lite-only being used together
  3. Error Handling: Sample project lacks error boundaries which could crash in production

Performance Benchmarks Missing

  • No performance comparison between full and lite schema generation
  • Memory usage impact of the new tanstack-query implementation not measured

Recommendations 📋

  1. High Priority:

    • Add validation for mutually exclusive CLI options
    • Consider stricter typing in hook implementations
    • Add more prominent security warnings in sample documentation
  2. Medium Priority:

    • Add JSDoc comments for complex TypeScript types
    • Include performance benchmarks for schema generation modes
    • Add error boundary example to Next.js sample
  3. Low Priority:

    • Consider adding configuration validation for lite schema options
    • Add examples of properly secured client configuration

Overall Assessment ⭐

This is a high-quality PR that significantly advances ZenStack v3's capabilities. The new TanStack Query integration is well-implemented, the lite schema feature addresses real performance needs, and the test coverage is comprehensive. The architectural decisions are sound and maintain consistency with existing patterns.

Approval Recommendation: ✅ Approve with minor suggestions

The code is production-ready with the understanding that the security warnings in the sample project are appropriately documented.


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

♻️ Duplicate comments (1)
packages/clients/tanstack-query/src/react.ts (1)

182-184: UpdateMany/DeleteMany types are correctly defined.

The generic parameters for useUpdateMany and useDeleteMany correctly use single object types (UpdateManyArgs<Schema, Model> and DeleteManyArgs<Schema, Model>), not arrays. This aligns with Prisma's API contract. The concern raised in the previous review appears to have been addressed or was not present in this version.

Also applies to: 198-200

🧹 Nitpick comments (1)
packages/clients/tanstack-query/src/react.ts (1)

252-342: Type safety is lost with any types in hook implementations.

All hook implementations use any for args and options parameters. While this provides flexibility, it bypasses the type safety that TypeScript and the generic constraints are designed to provide.

If the generic types are correctly flowing through, consider replacing any with properly typed parameters to maintain type safety throughout the call chain.

📜 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 bf2ab4b and 5dc0087.

📒 Files selected for processing (1)
  • packages/clients/tanstack-query/src/react.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/clients/tanstack-query/src/react.ts (3)
packages/clients/tanstack-query/src/utils/common.ts (9)
  • APIContext (111-126)
  • ExtraQueryOptions (101-106)
  • ExtraMutationOptions (81-96)
  • makeUrl (211-223)
  • getQueryKey (180-190)
  • fetcher (128-159)
  • marshal (192-199)
  • setupInvalidation (234-255)
  • setupOptimisticUpdate (313-357)
packages/schema/src/schema.ts (2)
  • SchemaDef (10-18)
  • GetModels (109-109)
packages/orm/src/client/crud-types.ts (20)
  • FindUniqueArgs (669-671)
  • SelectSubset (509-515)
  • ModelResult (135-179)
  • FindArgs (648-664)
  • CreateArgs (677-682)
  • CreateManyArgs (684-684)
  • BatchResult (201-201)
  • CreateManyAndReturnArgs (686-692)
  • UpdateArgs (815-821)
  • UpdateManyArgs (823-826)
  • UpdateManyAndReturnArgs (828-834)
  • UpsertArgs (842-849)
  • DeleteArgs (961-966)
  • DeleteManyArgs (968-971)
  • CountArgs (977-982)
  • CountResult (988-998)
  • AggregateArgs (1004-1018)
  • AggregateResult (1044-1072)
  • GroupByArgs (1091-1108)
  • GroupByResult (1110-1144)
🪛 Biome (2.1.2)
packages/clients/tanstack-query/src/react.ts

[error] 87-87: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 232-232: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 253-253: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 257-257: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 261-261: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 265-265: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 269-269: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 273-273: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 277-277: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 281-281: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 285-285: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 289-289: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 293-293: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 297-297: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 301-301: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 305-305: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 309-309: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 313-313: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 317-317: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 321-321: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 325-325: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 329-329: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 333-333: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 337-337: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 341-341: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 353-353: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 361-361: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 376-376: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 384-384: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 402-402: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 406-406: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 432-432: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 436-436: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 476-476: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 477-477: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 496-496: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 530-530: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

⏰ 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 (20.x, postgresql)
  • GitHub Check: build-test (20.x, sqlite)
  • GitHub Check: claude-review
🔇 Additional comments (4)
packages/clients/tanstack-query/src/react.ts (4)

1-89: Well-structured context and provider setup.

The deprecation of Provider in favor of QuerySettingsProvider is clearly documented, and the useHooksContext helper appropriately provides a default endpoint fallback.


230-344: Biome linter warnings are false positives for this factory pattern.

The Biome linter flags hook calls within nested functions (lines 232, 253, 257, etc.), but this is a false positive. The pattern here is:

  1. useModelQueries is a factory that returns an object of custom hooks
  2. The returned functions (e.g., useFindUnique) are closures that call internal hooks
  3. When users invoke these at the component top level (e.g., hooks.useFindUnique(args)), it's a valid hook call

Verify that your ESLint configuration with eslint-plugin-react-hooks correctly handles this pattern, or add inline suppressions with explanatory comments if needed. You may need to configure the linter to recognize that the returned functions are custom hooks.

Alternatively, consider adding a comment at the top of the file explaining the pattern:

/* eslint-disable react-hooks/rules-of-hooks */
// This file uses a factory pattern where hooks are returned from other hooks.
// The returned functions are custom hooks that should be called at component top level.

346-450: Query implementations are well-structured.

The internal query hooks properly handle:

  • Context resolution via useHooksContext()
  • Query key generation with appropriate flags
  • Abort signal propagation for cancellation
  • Infinite queries correctly disable optimistic updates

452-531: Mutation implementation correctly handles different HTTP methods and cache management.

The mutation hook properly:

  • Routes DELETE operations to query parameters while POST/PUT use request body
  • Integrates invalidation and optimistic update mechanisms
  • Respects the checkReadBack flag for policy-restricted mutations
  • Uses logging context from settings

@ymc9 ymc9 added this pull request to the merge queue Nov 4, 2025
Merged via the queue into main with commit cebd9d4 Nov 4, 2025
10 checks passed
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.

2 participants