Skip to content

Conversation

@ymc9
Copy link
Member

@ymc9 ymc9 commented Dec 16, 2025

fixes #503

Summary by CodeRabbit

  • Bug Fixes

    • Fixed import path generation in code generation for better relative path resolution
    • Enhanced type inference for nested relation selections and field filtering operations
  • Tests

    • Added regression test coverage for nested relation type handling scenarios

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

Copilot AI review requested due to automatic review settings December 16, 2025 12:44
@coderabbitai
Copy link

coderabbitai bot commented Dec 16, 2025

Walkthrough

The PR fixes a type inference issue where nested relations weren't properly included in query results. It refactors ModelResult and ModelSelectResult types to use uniform object-based recursion for relation handling, tightens generic constraints, and adds regression tests validating the fix across three related models.

Changes

Cohort / File(s) Summary
CLI schema import path
packages/cli/src/actions/generate.ts
Changes emitted schema import path from direct outputPath string to path.relative('.', outputPath) for relative path resolution.
ORM type system refactoring
packages/orm/src/client/crud-types.ts
Restructures ModelSelectResult key-filtering logic and relation recursion to use uniform object-based approach instead of conditional Pick shapes; updates ModelResult generic constraints requiring select and omit to be objects and augments with Record<string, unknown>; changes include branch recursion from conditional shapes to direct I[Key] access.
E2E test query update
tests/e2e/orm/schemas/typing/typecheck.ts
Adds take: 1 limit to posts relation filter in findFirstOrThrow test.
Regression test configuration
tests/regression/package.json
Updates build script to run test:generate, renames generate to test:generate, and updates test command to orchestrate generate and type-check steps.
Regression test schema definition
tests/regression/test/issue-503/schema.zmodel, tests/regression/test/issue-503/schema.ts
Introduces SQLite schema with three related models (InternalChat, Message, Media) and corresponding TypeScript schema definition with provider, model, and plugin configuration.
Regression test generated types
tests/regression/test/issue-503/models.ts, tests/regression/test/issue-503/input.ts
Generates type aliases mapping model results and CRUD operation argument types across InternalChat, Message, and Media models; exports FindMany, FindUnique, FindFirst, Create, Update, Upsert, Delete, Count, Aggregate, GroupBy, Where, Select, Include, Omit, and GetPayload types per model.
Regression test case
tests/regression/test/issue-503/regression.test.ts
Adds test exercising nested relation creation with include clause, verifying media type is properly inferred in nested messages.create.media.create scenario.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

  • packages/orm/src/client/crud-types.ts: The core type system changes require careful review of the ModelResult and ModelSelectResult recursion logic. The shift from conditional branching to uniform object-based recursion affects type inference paths and may have subtle interactions with existing code.
  • Type constraint changes in ModelResult: Verify that tightening select and omit to require object type and adding Record<string, unknown> augmentation preserves backward compatibility and correctly broadens acceptable extra properties.
  • Regression test integration: Ensure all generated files (input.ts, models.ts, schema.ts) properly reflect the fixed type behavior and test case correctly validates the nested include scenario from issue #503.

Possibly related PRs

Poem

🐰 A nested tale of types now true,
Where includes bloom in relations new—
No more lost fields in query deep,
The rabbit's fix makes promises keep! 🌿✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(orm):typing issue with deeply nested included relations' directly describes the core change—fixing a typing issue with nested included relations in the ORM.
Linked Issues check ✅ Passed The PR addresses issue #503 by fixing the ModelSelectResult and ModelResult types to properly handle deeply nested included relations, ensuring media field is correctly typed in nested include clauses.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the nested include typing issue: type refactoring, test updates, and regression test additions for issue #503 are all in scope.
✨ 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 fix/issue-503

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.

@claude
Copy link

claude bot commented Dec 16, 2025

Claude finished @ymc9's task —— View job


Code Review Complete ✅

I've thoroughly analyzed the PR that fixes typing issues with deeply nested included relations (#503). Here's my comprehensive feedback:

Summary of Changes

This PR addresses TypeScript typing issues when using deeply nested include relations by simplifying and improving the type inference logic in the ORM's CRUD types system.

Core Changes:

  • packages/orm/src/client/crud-types.ts: Major refactor of ModelSelectResult and ModelResult types (-63/+36 lines)
  • New regression test suite: Added comprehensive test case for issue Query result doesn't contain relation in nested "include" clause #503
  • Minor CLI improvement: Script name standardization in generate.ts
  • Test infrastructure: Updated regression test package.json scripts

Code Quality Assessment

✅ Excellent Improvements:

  1. Simplified Type Logic: The refactored ModelSelectResult type is much cleaner:

    • Removed complex conditional type chains that checked FindArgs compatibility
    • Simplified the recursive relation handling by directly calling ModelResult
    • Better ordering of condition checks (_count → omit → relations)
  2. Enhanced Type Safety:

    • Added extends object constraints to select/include/omit parameters
    • Added Record<string, unknown> intersection for better type inference
    • More precise type narrowing in the conditional logic
  3. Improved Maintainability:

    • Removed duplicate logic for handling select vs include in relations
    • Clear comments explaining each branch of the conditional types
    • Consistent recursive pattern for all relation field handling

✅ Comprehensive Testing:

  1. Excellent Test Coverage: The regression test in tests/regression/test/issue-503/ perfectly demonstrates the fix:

    • Tests deeply nested relations: InternalChatmessagesmedia
    • Validates both creation and querying with nested includes
    • Includes proper schema setup with realistic data model
  2. Integration Testing: Added to existing typing test suite to ensure continued compatibility

Technical Analysis

Performance Considerations:

  • TypeScript compilation performance should improve due to simplified recursive type resolution
  • Reduced type complexity eliminates unnecessary conditional branches during type inference
  • More direct recursion pattern avoids intermediate type computations

Backward Compatibility:

  • Maintains API compatibility - no breaking changes to public interfaces
  • Type inference behavior preserved for existing code
  • Runtime behavior unchanged - purely a typing improvement

Security & Best Practices

✅ Security:

  • No security concerns - purely TypeScript type improvements
  • Generated code follows existing security patterns
  • Test data is minimal and safe

✅ Best Practices:

  • Follows established TypeScript utility type patterns
  • Proper separation of concerns between scalar/relation field handling
  • Good use of distributive conditional types for union handling

Minor Observations

Package.json Script Rename:
The change from "generate" to "test:generate" in tests/regression/package.json improves clarity and follows npm script naming conventions.

CLI Generate.ts:
The single-line change (line 35) appears to be a minor output formatting improvement - good attention to detail.

Verdict

Excellent implementation! This PR successfully:

Solves the core issue: Fixes TypeScript compilation errors with deeply nested includes
Improves code quality: Significantly cleaner and more maintainable type definitions
Maintains backward compatibility: No breaking changes
Provides comprehensive testing: Well-designed regression test suite
Follows best practices: Proper TypeScript patterns and project conventions

No concerns or issues identified. This is a well-executed fix that improves both functionality and code quality.


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

🧹 Nitpick comments (1)
tests/regression/test/issue-503/regression.test.ts (1)

5-30: Good regression test coverage for issue #503.

The test correctly reproduces the exact scenario from the issue: creating a deeply nested relation and then selecting with a nested include. The assertion r.messages[0]?.media implicitly validates that the type system now correctly exposes the media field on the nested messages result.

Consider adding an explicit type assertion to make the compile-time verification more visible:

+        // Type assertion: media should be present in the typed result
+        const media: { id: number; type: string } | null | undefined = r.messages[0]?.media;
         expect(r.messages[0]?.media).toMatchObject({ type: 'Image' });
📜 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 a147840 and e71d8a3.

📒 Files selected for processing (9)
  • packages/cli/src/actions/generate.ts (1 hunks)
  • packages/orm/src/client/crud-types.ts (2 hunks)
  • tests/e2e/orm/schemas/typing/typecheck.ts (1 hunks)
  • tests/regression/package.json (1 hunks)
  • tests/regression/test/issue-503/input.ts (1 hunks)
  • tests/regression/test/issue-503/models.ts (1 hunks)
  • tests/regression/test/issue-503/regression.test.ts (1 hunks)
  • tests/regression/test/issue-503/schema.ts (1 hunks)
  • tests/regression/test/issue-503/schema.zmodel (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.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:

  • tests/regression/test/issue-503/schema.zmodel
tests/e2e/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • tests/e2e/orm/schemas/typing/typecheck.ts
tests/**/type*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Ensure TypeScript inference and type coverage are validated through type coverage tests

Files:

  • tests/e2e/orm/schemas/typing/typecheck.ts
🧠 Learnings (12)
📓 Common learnings
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
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
📚 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:

  • packages/cli/src/actions/generate.ts
  • tests/regression/test/issue-503/models.ts
  • tests/regression/test/issue-503/schema.zmodel
  • tests/regression/test/issue-503/schema.ts
  • tests/regression/test/issue-503/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/generate.ts
  • tests/regression/test/issue-503/models.ts
  • packages/orm/src/client/crud-types.ts
  • tests/regression/test/issue-503/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: Database migrations should use Prisma CLI under the hood via ZenStack commands

Applied to files:

  • packages/cli/src/actions/generate.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/cli/src/actions/generate.ts
  • tests/regression/test/issue-503/regression.test.ts
  • tests/e2e/orm/schemas/typing/typecheck.ts
  • packages/orm/src/client/crud-types.ts
  • tests/regression/test/issue-503/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} : Implement plugin hooks at ORM, Kysely, and entity mutation levels for query interception and customization

Applied to files:

  • packages/cli/src/actions/generate.ts
  • tests/regression/test/issue-503/models.ts
  • packages/orm/src/client/crud-types.ts
  • tests/regression/test/issue-503/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 tests/e2e/**/*.{ts,tsx} : E2E tests should validate real-world schema compatibility with established projects

Applied to files:

  • tests/regression/test/issue-503/models.ts
  • tests/regression/test/issue-503/regression.test.ts
  • tests/regression/package.json
  • tests/e2e/orm/schemas/typing/typecheck.ts
  • tests/regression/test/issue-503/schema.ts
  • tests/regression/test/issue-503/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/cli/**/*.test.{ts,tsx} : CLI package tests should focus on action-specific tests for each command

Applied to files:

  • tests/regression/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: Use Turbo for build orchestration and run `pnpm build`, `pnpm watch`, `pnpm lint`, and `pnpm test` for development tasks

Applied to files:

  • tests/regression/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/**/type*.{ts,tsx} : Ensure TypeScript inference and type coverage are validated through type coverage tests

Applied to files:

  • tests/regression/package.json
  • tests/e2e/orm/schemas/typing/typecheck.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 `pnpm` with workspaces for package management, pinned to version `pnpm10.12.1`

Applied to files:

  • tests/regression/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:

  • tests/regression/test/issue-503/schema.ts
🧬 Code graph analysis (2)
packages/orm/src/client/crud-types.ts (1)
packages/schema/src/schema.ts (5)
  • NonRelationFields (215-219)
  • RelationFields (221-225)
  • RelationFieldType (233-240)
  • ModelFieldIsOptional (242-246)
  • FieldIsArray (266-270)
tests/regression/test/issue-503/schema.ts (2)
packages/schema/src/schema.ts (1)
  • SchemaDef (11-19)
packages/schema/src/expression-utils.ts (1)
  • ExpressionUtils (19-123)
⏰ 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). (5)
  • GitHub Check: Agent
  • GitHub Check: CodeQL analysis (javascript-typescript)
  • GitHub Check: claude-review
  • GitHub Check: build-test (20.x, postgresql)
  • GitHub Check: build-test (20.x, sqlite)
🔇 Additional comments (9)
tests/regression/package.json (1)

7-9: Script path is valid and test workflow is sound.

The refactoring from "generate" to "test:generate" improves clarity. The test flow (generate TypeScript schemas from zmodel files → compile check → run vitest) properly validates the typing fix with type coverage validation through the tsc compilation step.

tests/e2e/orm/schemas/typing/typecheck.ts (1)

165-181: LGTM!

The addition of take: 1 to the nested posts selection is a valid query constraint. This test exercises deeply nested relation selection (posts.author.id) which aligns with the PR's goal of verifying nested relation typing.

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

39-39: LGTM!

Using path.relative('.', outputPath) produces a cleaner relative import path in the example snippet, improving developer experience when copying the generated code.

tests/regression/test/issue-503/schema.zmodel (1)

1-23: LGTM!

The schema correctly reproduces the issue #503 scenario with the three-model relationship chain (InternalChatMessageMedia). The relation definitions properly specify foreign key fields and references.

packages/orm/src/client/crud-types.ts (2)

190-218: Solid fix for deeply nested relation typing.

The refactored ModelResult type correctly handles nested includes by passing I[Key] directly to the recursive call. The key insight is that when processing an include like { messages: { include: { media: true } } }, the inner object { include: { media: true } } is now passed recursively as Args, allowing the type system to properly resolve the nested media relation.

The & Record<string, unknown> constraint appropriately accommodates additional query options (take, where, etc.) that may accompany select/include/omit.


160-170: Consistent recursion pattern for relation fields in select.

The change to pass Select[Key] directly to ModelResult (instead of extracting specific properties) ensures consistent handling of nested relation selections. This allows queries like { select: { posts: { select: { author: { include: { profile: true } } } } } } to properly resolve all nested relations.

tests/regression/test/issue-503/schema.ts (1)

1-109: LGTM!

This is a correctly generated schema file that accurately reflects the ZModel definitions. The relation structures (including the optional media relation with its foreign key) are properly defined to support the regression test scenario.

tests/regression/test/issue-503/models.ts (1)

1-12: LGTM! Auto-generated type aliases are correct.

The generated type exports correctly alias the three models using ModelResult, which should properly reflect nested included relations if the underlying ORM type fix is working as intended.

tests/regression/test/issue-503/input.ts (1)

1-70: LGTM! Generated CRUD types follow consistent patterns.

The comprehensive CRUD type exports for all three models are correctly structured. The GetPayload types (lines 30, 50, 70) use SimplifiedPlainResult, which is the key type for validating that deeply nested included relations are properly reflected in query result typings.

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 fixes a TypeScript typing issue (#503) where deeply nested relations with select, include, and additional query parameters (like take) caused type inference to fail. The core problem was overly complex conditional type checking that prevented TypeScript from properly narrowing types in nested scenarios.

Key Changes:

  • Simplified type inference logic in ModelResult and ModelSelectResult by removing intermediate FindArgs type checks
  • Added & Record<string, unknown> constraints to improve TypeScript's type narrowing in union conditionals
  • Enhanced test coverage with regression test demonstrating the fix

Reviewed changes

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

Show a summary per file
File Description
packages/orm/src/client/crud-types.ts Simplified type recursion logic by removing FindArgs checks and always delegating to ModelResult for relation fields
tests/regression/test/issue-503/schema.zmodel Added test schema with 3-level relation chain (InternalChat → Message → Media)
tests/regression/test/issue-503/schema.ts Generated schema file from zmodel
tests/regression/test/issue-503/regression.test.ts Regression test verifying nested select with include and take parameter works correctly
tests/regression/test/issue-503/models.ts Generated model types
tests/regression/test/issue-503/input.ts Generated input types
tests/e2e/orm/schemas/typing/typecheck.ts Added take parameter to existing typing test to ensure type safety
tests/regression/package.json Renamed generate script to test:generate for consistency
packages/cli/src/actions/generate.ts Improved import path in CLI output to use relative paths

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

@ymc9 ymc9 merged commit fe109c8 into dev Dec 16, 2025
12 checks passed
@ymc9 ymc9 deleted the fix/issue-503 branch December 16, 2025 12:59
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