refactor(tanstack-query, orm): thread plugin generics through transactions#2645
refactor(tanstack-query, orm): thread plugin generics through transactions#2645
Conversation
…tion hooks and CRUD signatures ORM - Add `CrudArgsMap` / `CrudArgsType` and `CrudReturnMap` / `CrudReturnType` covering all 18 CRUD operations (incl. `*OrThrow` find variants), parameterized by `Schema, Model, Options, ExtQueryArgs, ExtResult` so plugin-extended args and result shapes flow uniformly. - Replace every per-method `*Args<…>` constraint and explicit return formula on `ClientContract` with `CrudArgsType<…, 'op'>` / `CrudReturnType<…, 'op'>`. - Drop the now-redundant `*Args` / `*Result` type imports from contract.ts. - Add type-only `[ExtQueryArgsMarker]?` / `[ExtResultMarker]?` symbol-keyed brand fields on `ClientContract` so consumers can recover the slots via shallow `infer` (replaces visible `$extQueryArgs?` / `$extResult?` fields). Client-helpers - Add `InferExtQueryArgs<T>` (extracts via `ExtQueryArgsMarker`). - Update `InferExtResult<T>` to use `ExtResultMarker` (same shallow-property pattern as `InferSchema` / `InferOptions`, fixing the previously broken deep `T extends ClientContract<…, infer E, …>` pattern). Tanstack-query (react / vue / svelte) - Thread `ExtQueryArgs` through `ModelQueryHooks`, `useModelQueries`, `ClientHooks`, and `useClientQueries`. The latter now infers slot 3 of `ClientContract` via `InferExtQueryArgs`, so `useClientQueries<DbType>(schema)` carries plugin args/result extensions into every hook. - Make `TransactionMutationOptions<Schema, Options, ExtQueryArgs, ExtResult>` and `TransactionMutationResult<Schema, Options, ExtQueryArgs, ExtResult>` generic, override `mutateAsync<const T extends readonly TransactionOperation<…>[]>` to return `Promise<TransactionResults<Schema, T, Options, ExtResult>>` — the per-position result tuple is narrowed to each op's `model+op+args`. - `TransactionOperation` and `TransactionResults` now thread the same generics via `CrudArgsMap` and `CrudReturnMap`. Tests - Extend `react-typing.test-d.ts` with a `useClientQueries<DbType>` case that verifies plugin `$read`/`$create` extension args and computed-`displayName` result fields propagate through every per-model hook and the transaction. - Add `Sequential transaction › generic parameter influence` block in `sequential-transaction.test.tsx` covering ExtQueryArgs ($read / $create / $update / $delete) and ExtResult flow into transaction op args and per-op return types, plus negative cases asserted via concrete `TransactionOperation` type annotations (which trigger excess-property checks that the inline `mutateAsync` tuple capture doesn't). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds internal type-branding markers and generic CRUD arg/return maps, updates ORM client method typings to use them, threads extension query-arg/result generics through TanStack Query React/Vue/Svelte hook types and transaction typings, and adds large type-only tsc torture tests and related tsconfig/schema. ChangesExtension Type System & Hook Generics
Type-Check Torture Performance Suite
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
|
Claude finished @ymc9's task —— View job Code Review Complete ✅Review Checklist:
🎉 Overall Assessment: ExcellentThis is a sophisticated and well-executed TypeScript refactoring that significantly improves type safety across the ZenStack ecosystem. The implementation demonstrates deep understanding of advanced TypeScript patterns and maintains excellent code quality. ✅ StrengthsType System Design
Implementation Quality
Test Coverage
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/clients/tanstack-query/package.json (1)
56-60:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMove
@zenstackhq/ormtopeerDependenciesanddevDependencies.Line 60 adds it as a regular dependency, but this package's exported types rely on unique symbol markers (
ExtQueryArgsMarker,ExtResultMarker) from@zenstackhq/ormto narrowInferExtQueryArgsandInferExtResult. If consumers end up with separate instances of@zenstackhq/ormin their dependency tree, the symbol identities diverge and type narrowing silently fails, returning{}instead.Suggested manifest change
"dependencies": { "@zenstackhq/client-helpers": "workspace:*", "@zenstackhq/common-helpers": "workspace:*", "@zenstackhq/schema": "workspace:*", - "@zenstackhq/orm": "workspace:*", "decimal.js": "catalog:" }, "devDependencies": { + "@zenstackhq/orm": "workspace:*", "@sveltejs/package": "^2.5.7", ... }, "peerDependencies": { + "@zenstackhq/orm": "workspace:*", "@tanstack/react-query": "^5.0.0", "@tanstack/svelte-query": "^6.0.0", "@tanstack/vue-query": "^5.0.0" },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clients/tanstack-query/package.json` around lines 56 - 60, The package currently lists `@zenstackhq/orm` under "dependencies" but its exported types rely on shared symbol identities (ExtQueryArgsMarker, ExtResultMarker) used by InferExtQueryArgs and InferExtResult; move `@zenstackhq/orm` out of "dependencies" and add it to both "peerDependencies" and "devDependencies" (keeping the same version spec, e.g., workspace:*) so consumers and dev-time builds use the same single instance and the symbol-based type narrowing remains correct.packages/clients/tanstack-query/src/react.ts (1)
85-85:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMissing
InferExtQueryArgsre-export — breaks parity with Svelte/Vue and PR contract.The React adapter imports
InferExtQueryArgs(line 26) and uses it inuseClientQueries's return type (line 460), but the public re-export here does not include it. The Svelte adapter (this PR,svelte/index.svelte.ts:82) does re-export it, and the PR description explicitly states React, Vue, and Svelte adapters all re-exportInferExtQueryArgs. Consumers importing from@zenstackhq/tanstack-query/reactwon't see this type.🔧 Proposed fix
-export type { InferExtResult, InferOptions, InferSchema } from '@zenstackhq/client-helpers'; +export type { InferExtQueryArgs, InferExtResult, InferOptions, InferSchema } from '@zenstackhq/client-helpers';🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clients/tanstack-query/src/react.ts` at line 85, The file is missing a public re-export of InferExtQueryArgs which breaks parity and the return type of useClientQueries; add InferExtQueryArgs to the export list from '@zenstackhq/client-helpers' alongside InferExtResult/InferOptions/InferSchema so consumers of `@zenstackhq/tanstack-query/react` can import that type (update the export statement that currently exports InferExtResult/InferOptions/InferSchema to also export InferExtQueryArgs).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/clients/tanstack-query/src/common/types.ts`:
- Around line 112-139: AllowedTransactionOps currently ignores the Options
parameter causing transaction tuples to allow ops that were sliced away; update
the AllowedTransactionOps type to accept the same Options generic (Options
extends QueryOptions<Schema> = QueryOptions<Schema>) and compute allowed ops
using the sliced operation set (e.g. use GetSlicedOperations<Schema, Model,
Options> or the existing GetSlicedOperations helper) instead of the prior
unconditional keys, then adjust TransactionOperation to pass Options into
AllowedTransactionOps (so the constraint on Op and the resulting CrudArgsMap
lookup both respect Options) and keep the existing handling for
OperationsRequiringCreate/ModelAllowsCreate semantics when deriving the final
union.
---
Outside diff comments:
In `@packages/clients/tanstack-query/package.json`:
- Around line 56-60: The package currently lists `@zenstackhq/orm` under
"dependencies" but its exported types rely on shared symbol identities
(ExtQueryArgsMarker, ExtResultMarker) used by InferExtQueryArgs and
InferExtResult; move `@zenstackhq/orm` out of "dependencies" and add it to both
"peerDependencies" and "devDependencies" (keeping the same version spec, e.g.,
workspace:*) so consumers and dev-time builds use the same single instance and
the symbol-based type narrowing remains correct.
In `@packages/clients/tanstack-query/src/react.ts`:
- Line 85: The file is missing a public re-export of InferExtQueryArgs which
breaks parity and the return type of useClientQueries; add InferExtQueryArgs to
the export list from '@zenstackhq/client-helpers' alongside
InferExtResult/InferOptions/InferSchema so consumers of
`@zenstackhq/tanstack-query/react` can import that type (update the export
statement that currently exports InferExtResult/InferOptions/InferSchema to also
export InferExtQueryArgs).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7b3b3b4a-6220-48af-8fb3-b5bb9c778c59
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (10)
packages/clients/client-helpers/src/types.tspackages/clients/tanstack-query/package.jsonpackages/clients/tanstack-query/src/common/types.tspackages/clients/tanstack-query/src/react.tspackages/clients/tanstack-query/src/svelte/index.svelte.tspackages/clients/tanstack-query/src/vue.tspackages/clients/tanstack-query/test/react/react-typing.test-d.tspackages/clients/tanstack-query/test/react/sequential-transaction.test.tsxpackages/orm/src/client/contract.tspackages/orm/src/client/crud-types.ts
…toring A bare `.ts` (not `.test.ts`) workload that exercises ~30 deeply-related models — including delegate hierarchies (Notification subtypes, Comment subtypes), nested 4–5 level `include` reads, deep nested creates, and `groupBy` / `aggregate` — so `tsc --noEmit` has to evaluate the full breadth of inferred ORM types. Surfaces type-checker performance regressions when ORM type machinery changes. Layout under `tests/e2e/performance/tsc-torture/`: - `zenstack/schema.zmodel` (~30 models, copied from a reference torture schema) - `zenstack/schema.ts` — generated by `pnpm test:generate` - `main.ts` — workload, never executed at runtime (no `.test.` suffix means vitest skips it; tsc still type-checks it via the e2e tsconfig's `**/*.ts` include) - `tsconfig.test.json` — scoped config for running an isolated `tsc -p tests/e2e/performance/tsc-torture --project tsconfig.test.json` to time just this workload Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Claude finished @ymc9's task —— View job Claude finished @ymc9's task —— View job Code Review Complete ✅Review Checklist:
🎉 Overall Assessment: ExcellentThis is a sophisticated and well-executed TypeScript refactoring that significantly improves type safety across the ZenStack ecosystem. The implementation demonstrates deep understanding of advanced TypeScript patterns and maintains excellent code quality. ✅ StrengthsType System Design
Implementation Quality
Test Coverage
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
tests/e2e/performance/tsc-torture/main.ts (1)
614-623: 💤 Low valueHardcoded
taskId: 1in the fan-out is fragile if this file is ever flipped to runtime.
statusChangeNotification.create({ data: { ..., taskId: 1, ... } })works for type-checking, but the file's lifecycle has a real risk of being repurposed (e.g., wired into a perf benchmark that actually executes). ThreadingbootstrapTask.id/testTask.idthroughrunMutations(or accepting ataskIdparameter) would keep the type-check coverage identical while making the workload safe to execute later.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/e2e/performance/tsc-torture/main.ts` around lines 614 - 623, The fan-out loop uses a hardcoded taskId (statusChangeNotification.create called with taskId: 1) which is fragile; update runMutations (or the function that contains the loop) to accept a taskId parameter (or thread bootstrapTask.id/testTask.id into it) and replace the literal 1 with that parameter so db.statusChangeNotification.create connects to a real task id; keep the same shape of the create call and types so type-checking is preserved but the workload is safe to execute at runtime.tests/e2e/performance/tsc-torture/zenstack/schema.zmodel (2)
501-529: 💤 Low valueNotification subtype models reference foreign IDs as plain
Intcolumns without@relation.
MentionNotification.taskId,AssignmentNotification.taskId/assignedByUserId,StatusChangeNotification.taskId,CommentNotification.commentId,ReviewRequestNotification.reviewId/requestedByUserId, andApprovalNotification.targetIdare stored as bare integers with no@relation. That means deleting aTask/Comment/Review/Userwill leave dangling notification rows, and you also lose the typed nested-include surface for these refs (which is exactly the kind of include depth this torture workload is supposed to stress). If the intent is to keep them as opaque references, consider a brief comment in the schema; otherwise add proper relations so the workload exercises a more realistic include shape.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/e2e/performance/tsc-torture/zenstack/schema.zmodel` around lines 501 - 529, Notification subtype models use bare Int IDs which create dangling refs; either mark them as intentionally opaque with a schema comment or add proper relations: update MentionNotification.taskId, AssignmentNotification.taskId and .assignedByUserId, StatusChangeNotification.taskId, CommentNotification.commentId, ReviewRequestNotification.reviewId and .requestedByUserId to include relation fields (e.g., add task Task `@relation`(fields: [taskId], references: [id]) and similarly user User `@relation`(fields: [assignedByUserId], references: [id]), comment Comment `@relation`(...), review Review `@relation`(...)), and for ApprovalNotification decide whether to keep polymorphic targetType/targetId (add a comment) or model concrete relations for each target and wire targetId with `@relation` accordingly so deletes and nested includes work correctly.
1-1: ⚡ Quick winRun
zenstack generateso./zenstack/schemaexists for the typecheck workload.
tests/e2e/performance/tsc-torture/main.tsimports./zenstack/schema, which is produced byzenstack generateagainst this.zmodel. Make sure the typecheck workflow (or a pre-step in the dedicatedtsconfig.test.jsonpipeline) regenerates it whenever this schema changes; otherwise the torture file will fall out of sync with the schema and either fail to type-check or, worse, type-check against a stale schema and miss the regressions this workload is meant to surface.As per coding guidelines: "Always run
zenstack generateafter modifying ZModel schemas".#!/bin/bash # Confirm there's a generate step wired up for the torture workload fd -t f 'package.json' tests/e2e/performance/tsc-torture | xargs -I{} sh -c 'echo "=== {} ==="; cat {}' fd -t f 'tsconfig.test.json' tests/e2e/performance/tsc-torture | xargs -I{} sh -c 'echo "=== {} ==="; cat {}' # Look for a generate command referencing this schema in repo scripts rg -nP -C2 'tsc-torture|zenstack\s+generate' --type=json --type=yaml --type=md🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/e2e/performance/tsc-torture/zenstack/schema.zmodel` at line 1, The typecheck workload relies on generated output from this ZModel but the CI/workflow doesn’t run generation: add a step that runs "zenstack generate" after changes to tests/e2e/performance/tsc-torture/schema.zmodel so ./zenstack/schema is present before TypeScript typechecking; update the torture pipeline (or the tsconfig.test.json pre-step) that exercises tests/e2e/performance/tsc-torture/main.ts to invoke the generator, ensuring regeneration whenever schema.zmodel changes to avoid stale types.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/performance/tsc-torture/main.ts`:
- Around line 34-38: The cleanup ordering is wrong: move leaf/junction deletes
(e.g., commentReaction.deleteMany()) to run before their parent deletes (e.g.,
comment.deleteMany()) so FK-safe "leaf-to-root" is honored; update the
cleanupAll sequence in main.ts so commentReaction (and any other
reaction/reviewComment-like deletes) are executed prior to comment, keeping
attachment/taskLabel vs task ordering unchanged, and ensure the comment and any
related "Leaf / junction tables" block reflects the corrected order.
---
Nitpick comments:
In `@tests/e2e/performance/tsc-torture/main.ts`:
- Around line 614-623: The fan-out loop uses a hardcoded taskId
(statusChangeNotification.create called with taskId: 1) which is fragile; update
runMutations (or the function that contains the loop) to accept a taskId
parameter (or thread bootstrapTask.id/testTask.id into it) and replace the
literal 1 with that parameter so db.statusChangeNotification.create connects to
a real task id; keep the same shape of the create call and types so
type-checking is preserved but the workload is safe to execute at runtime.
In `@tests/e2e/performance/tsc-torture/zenstack/schema.zmodel`:
- Around line 501-529: Notification subtype models use bare Int IDs which create
dangling refs; either mark them as intentionally opaque with a schema comment or
add proper relations: update MentionNotification.taskId,
AssignmentNotification.taskId and .assignedByUserId,
StatusChangeNotification.taskId, CommentNotification.commentId,
ReviewRequestNotification.reviewId and .requestedByUserId to include relation
fields (e.g., add task Task `@relation`(fields: [taskId], references: [id]) and
similarly user User `@relation`(fields: [assignedByUserId], references: [id]),
comment Comment `@relation`(...), review Review `@relation`(...)), and for
ApprovalNotification decide whether to keep polymorphic targetType/targetId (add
a comment) or model concrete relations for each target and wire targetId with
`@relation` accordingly so deletes and nested includes work correctly.
- Line 1: The typecheck workload relies on generated output from this ZModel but
the CI/workflow doesn’t run generation: add a step that runs "zenstack generate"
after changes to tests/e2e/performance/tsc-torture/schema.zmodel so
./zenstack/schema is present before TypeScript typechecking; update the torture
pipeline (or the tsconfig.test.json pre-step) that exercises
tests/e2e/performance/tsc-torture/main.ts to invoke the generator, ensuring
regeneration whenever schema.zmodel changes to avoid stale types.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 008663d1-4312-4f62-a641-bc6677929852
📒 Files selected for processing (4)
tests/e2e/performance/tsc-torture/main.tstests/e2e/performance/tsc-torture/tsconfig.test.jsontests/e2e/performance/tsc-torture/zenstack/schema.tstests/e2e/performance/tsc-torture/zenstack/schema.zmodel
✅ Files skipped from review due to trivial changes (1)
- tests/e2e/performance/tsc-torture/tsconfig.test.json
…on op union `AllowedTransactionOps` now takes `Options` and uses `GetSlicedOperations` in place of the unconditional `keyof CrudArgsMap`, so transaction tuples honor `slicing.includedOperations` / `excludedOperations` the same way the per-model hooks already do. `TransactionOperation` threads `Options` through to both `AllowedTransactionOps` lookups; `ModelAllowsCreate` / `OperationsRequiringCreate` and the `& CoreCrudOperations` (drops `*OrThrow` variants the server rejects) are preserved. Adds a regression test in `react-sliced-client.test-d.ts` that builds a typed client with `includedOperations: ['findUnique','findMany','count']` on `User` and asserts `tx.mutateAsync` rejects sliced-out `create` / `delete` ops via `@ts-expect-error`. Without Options threading those ops would still be allowed in the transaction tuple even though the per-model hooks already filtered them. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Claude finished @ymc9's task —— View job Code Review Complete ✅Review Checklist:
🎉 Overall Assessment: ExcellentThis is a sophisticated and well-executed TypeScript refactoring that significantly improves type safety across the ZenStack ecosystem. The implementation demonstrates deep understanding of advanced TypeScript patterns and maintains excellent code quality. ✅ StrengthsType System Design
Implementation Quality
Test Coverage
|
Summary
End-to-end propagation of plugin-extension generics (
ExtQueryArgs/ExtResult/Options) through the ORM CRUD contract and the TanStack Query hooks, with a focus on making$transaction.useSequential()precisely typed.ORM (
packages/orm)CrudArgsMap<S, M, Options, ExtQueryArgs, ExtResult>/CrudArgsType<…, Op>andCrudReturnMap<S, M, Args, Options, ExtResult>/CrudReturnType<…, Op>covering all 18 CRUD operations (incl.*OrThrowfind variants).ClientContract(find/create/update/upsert/delete + their *Many / *AndReturn / OrThrow variants + count/aggregate/groupBy/exists) now usesCrudArgsType<…, 'op'>andCrudReturnType<…, 'op'>. Removes ~20 redundant*Args/*Resultimports.[ExtQueryArgsMarker]?,[ExtResultMarker]?) onClientContractso the InferXxx helpers can recover the slots via shallowinfer. Symbol keys keep the brands hidden fromdb.autocomplete (replacing the prior visible$extQueryArgs/$extResultfields).Client-helpers (
packages/clients/client-helpers)InferExtQueryArgs<T>(slot 3) and updatedInferExtResult<T>(slot 5) using the shallow property pattern via the new symbol brands. Mirrors the existingInferSchema/InferOptionsapproach.TanStack Query (
packages/clients/tanstack-query)ModelQueryHooks,useModelQueries,ClientHooks, anduseClientQueriesnow threadExtQueryArgs(betweenOptionsandExtResult).useClientQueries<DbType>(schema)infers slot 3 ofClientContractviaInferExtQueryArgs, so plugin-addedwhere-style filters and computed-result fields propagate into every hook.TransactionMutationOptions<S, Options, EQ, ER>andTransactionMutationResult<S, Options, EQ, ER>are now generic.TransactionMutationResultoverridesmutateAsync<const T extends readonly TransactionOperation<…>[]>to returnPromise<TransactionResults<…, T, …>>, narrowing each tuple position to the op's precise return type.TransactionOperationandTransactionResultsthread the new generics viaCrudArgsMap/CrudReturnMap.InferExtQueryArgsis re-exported from each module.Tests
react-typing.test-d.tsconstructing aClientContract<…, MyExtQueryArgs, {}, MyExtResult>and asserting plugin args/result narrowing flows through every per-model hook and the transaction.sequential-transaction.test.tsxcovering\$read/\$create/\$update/\$deleteextension flow into op args andExtResultflow into per-op return types. Negative cases use concreteTransactionOperation<…>annotations (which trigger excess-property checks the inline tuple capture doesn't).Caller-visible behavior
useClientQueries(schema)— unchanged. Defaults keep all generics at{}.useClientQueries<typeof db>(schema)— picks up plugin extensions automatically. Per-model hooks accept extension args; results carry computed fields.db.\$transaction.useSequential().mutateAsync([…] as const)— return is now a precise tuple of per-op results, mirroringdb[model][op](args)typing on the server.Test plan
pnpm --filter @zenstackhq/orm exec tsc --noEmitcleanpnpm --filter @zenstackhq/tanstack-query test— all 63 tests pass (8 files)pnpm --filter e2e test:typecheck) clean🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests