fix(orm): variance-annotate ToManyRelationUpdateInput to avoid excessively deep instantiation - #2855
Conversation
…ively deep instantiation
Passing a generated `XxxUpdateArgs`/`XxxUpsertArgs` type directly to the
corresponding client method (e.g. `db.tag.upsert(args)`) on a schema with a
many-to-many relation made TypeScript report TS2589 ("Type instantiation is
excessively deep and possibly infinite") or run out of memory. The generated
type uses the default `QueryOptions` while the client uses the projected
`QueryRelevantOptions`, so TypeScript had to compare two instantiations of
`ToManyRelationUpdateInput` with different `Options`. Without variance
annotations it measured the variance structurally through the recursive
nested update/upsert types, which is what blew up. `ToOneRelationUpdateInput`
was already annotated for this reason; this applies the same to the to-many
variant.
Fixes #2778
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: zenstackhq/zenstack/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (4)
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughThe ORM update input type now marks four parameters invariant. A regression test uses generated upsert and update argument types with a SQLite schema containing a many-to-many relation. ChangesMany-to-many type recursion
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix · Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to The regression test checks the intended typed update and upsert calls. No actionable issue remains before merge. Security Architecture ReviewSecurity architecture risk: ⚪ Minimal · up to The change affects how TypeScript compares many-to-many update types. It leaves the available relation operations and their create restrictions unchanged, and no new runtime security risk was identified. Retained concerns Security review detailsSecurity Blast Radius
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Some tools did not complete. Review the errors below. 🔧 ESLint
packages/orm/src/client/crud-types.tsESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox. 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. Comment |
Fixes #2778
Problem
Passing a generated
XxxUpdateArgs/XxxUpsertArgstype (frominput.ts, orUpdateArgs<Schema, Model>directly) to the matching client method fails on a schema with a many-to-many relation:On TS 5/6
tscruns out of memory instead; the language service becomes unusable.Root cause
The generated args type uses the default
QueryOptions<Schema>, while the client's methods use the projectedQueryRelevantOptions<Schema, Options>. Checking the argument therefore compares two instantiations ofToManyRelationUpdateInputwith differentOptions. That alias had no variance annotations, so TypeScript measured its variance structurally through the recursive nested update/upsert types (NestedUpdateInput,NestedUpsertInput,XOR, ...). A trace shows that singlegetVariancesWorkercall taking ~63s and ending "unmeasurable", after which the structural fallback explodes.ToOneRelationUpdateInputwas alreadyin out-annotated for exactly this reason; the to-many variant was missed.Fix
Add the same
in outannotations toToManyRelationUpdateInput.Verification
tests/regression/test/issue-2778coversUpsertArgsandUpdateArgspassed straight intoupsert/updateon a many-to-many schema (type-check + runtime).tscfortests/regressionandtests/e2epasses;tests/e2e/orm/client-apipasses on SQLite (the only failures are two MySQL timezone tests that need a local MySQL and fail identically ondev).🤖 Generated with Claude Code
Summary by CodeRabbit