Skip to content

feat(mongo-orm): codec-aware where(), typed field mutations, 1:N tests (FL-04/06/08)#336

Merged
wmadden merged 30 commits intomainfrom
tml-2246-orm-query-and-mutation-ergonomics-fl-04-fl-06-fl-08
Apr 15, 2026
Merged

feat(mongo-orm): codec-aware where(), typed field mutations, 1:N tests (FL-04/06/08)#336
wmadden merged 30 commits intomainfrom
tml-2246-orm-query-and-mutation-ergonomics-fl-04-fl-06-fl-08

Conversation

@wmadden
Copy link
Copy Markdown
Contributor

@wmadden wmadden commented Apr 14, 2026

closes TML-2246

Intent

Three ORM ergonomics gaps in the Mongo family forced the retail store example to drop to raw MongoDB commands for common patterns: typed array/numeric mutations required mongoRaw, ObjectId filter setup required a hand-rolled helper, and 1:N relation includes were incorrectly unwinding arrays. This PR closes all three gaps (FL-04, FL-06, FL-08) and removes all resulting workarounds from the example app.

Key snippet

// Codec-aware object where() — no manual ObjectId filter helper needed
await db.orders.where({ userId: currentUser._id }).all()

// Typed field operations — no mongoRaw needed
await db.carts.where({ _id: cartId }).update(u => [
  u.items.push(newItem),
  u.totalPrice.set(newTotal),
])

// 1:N include — already worked at runtime, now tested
await db.users.include('tasks').all()

Change map

The story

  1. New field-accessor.ts introduces the typed mutation surface (FL-04). A FieldAccessor<TContract, ModelName> proxy exposes per-field FieldExpression<T> objects, each with typed operators: set, unset, push, pull, addToSet, pop, and inc/mul (restricted to T extends number). A callable signature u("homeAddress.city") handles dot-path access into value objects, with the path type-checked at compile time via recursive template literal types (DotPath<TContract, ModelName>). compileFieldOperations groups the resulting FieldOperation[] by MongoDB operator key, passing each value through the codec's wrapValue callback so encoding stays consistent with update(plainObject).

  2. MongoCollection.where() gains an object overload (FL-06). Passing a plain { field: value } object compiles each key into a MongoFieldFilter.eq expression, with values run through #wrapFieldValue for codec encoding. The #isFilterExpr guard is backed by a unique symbol brand on MongoFilterExpression rather than a structural 'kind' in filter check, so user data objects with a kind field are never misrouted.

  3. update(), updateAll(), updateCount(), and upsert() accept callbacks. All four mutation methods now accept either a plain data object (existing path) or a (u: FieldAccessor) => FieldOperation[] callback. The upsert() path additionally guards against dot-path operations in callbacks — MongoDB rejects overlapping paths between $set and $setOnInsert, which would silently produce incomplete documents on insert.

  4. FL-08 required no implementation changes — 1:N reference relation $lookup without $unwind was already correct. The gap was test coverage; the ORM fixture contract is enriched with a tasks 1:N relation and the integration suite proves it.

  5. Retail store cleanup. object-id-filter.ts is deleted. All call sites across carts.ts, orders.ts, products.ts, users.ts, and invoices.ts use object where() or update(u => [...]) callbacks. No mongoRaw remains in any data access file.

Behavior changes & evidence

Compatibility / migration / risk

None. All new method forms are additive overloads; existing plain-object update() calls are unchanged.

Follow-ups / open questions

  • TML-2259: FieldExpression<T> gates inc()/mul() on T extends number. ADR 170 specifies the fuller mechanism via CodecTypes[Id]['traits']. Current shortcut is correct for all existing Mongo codecs.
  • Dot-path prohibition in upsert() callbacks is runtime-only; a type-level restriction is possible as a follow-up.

Non-goals / intentionally out of scope

  • No changes to the SQL ORM or cross-family shared interface.
  • No new codec or contract authoring changes.
  • Trait-based operator gating (ADR 170) is deferred to TML-2259.

Summary by CodeRabbit

  • New Features

    • Accept plain-object predicates in where() and callback-style field operations for update()/upsert() with typed helpers (set, push, pull, inc, mul, unset, pop, addToSet).
    • include() returns 1:N related records as arrays.
  • Improvements

    • Codec-aware filtering: values (including ID strings) are encoded automatically for correct matching.
    • Ergonomics & safety: concise mutation APIs with guards against dot-path and _id targets.
  • Tests

    • Expanded unit and integration coverage for ergonomics, field-accessors, and codec behavior.

wmadden added 18 commits April 14, 2026 08:56
…artifacts

Extract consolidated framework limitations from code reviews into a
standalone document at projects/mongo-example-apps/framework-limitations.md.
Covers type ergonomics (FL-01–FL-03), query capabilities (FL-04–FL-08),
schema/migration gaps (FL-09–FL-12), and missing capabilities (FL-13–FL-15).
Tracks 6 items addressed by this branch (FL-A1–FL-A6).

Also commits the PR #327 review artifacts (system-design-review, code-review,
walkthrough), the round 2 spec, and the round 2 plan.
Review artifacts under projects/**/reviews/ are useful during
development but should not ship in PRs. Added a gitignore rule
to prevent re-addition.
The where() codec gap applies to any field whose codec has a non-identity
encode, not just ObjectId. Updated framework-limitations.md and
next-steps.md to reflect the corrected root cause and scope.
Captures design decisions from discussion: codec-generic where() encoding,
full ADR 180 field accessor with dot-path traversal, 1:N back-relation
test coverage, and retail store cleanup scope.
4 milestones: 1:N back-relation tests, codec-aware where(), ADR 180
field accessor with dot-path traversal, retail store cleanup. All
acceptance criteria mapped to unit or type tests.
Add User.tasks 1:N reference relation to the ORM test fixture and
verify that include() produces $lookup without $unwind. Type tests
confirm the return type for 1:N included relations is an array.
where() now accepts a plain object { fieldName: value } in addition to
MongoFilterExpr. Values are encoded through codecs automatically via
the field codecId from the contract. Multiple fields produce an AND
of equality filters. Chainable with MongoFilterExpr where() calls.

Also fixes MongoWhereFilter type to correctly navigate the contract
field type structure (field.type.codecId instead of field.codecId).
Add value objects (HomeAddress), numeric field (loginCount), and
array field (tags) to the ORM test fixture for testing mutation
operators and dot-path access.

Implement createFieldAccessor() with Proxy-based field expressions
supporting $set, $unset, $inc, $mul, $push, $pull, $addToSet, $pop.
Add compile-time DotPath and ResolveDotPathType types for type-safe
dot-path access into value objects (ADR 180).
update(), updateAll(), updateCount(), and upsert() now accept a
callback (u => [...ops]) that uses the Proxy-based field accessor
to produce typed mutation operators ($set, $inc, $push, etc.).

Field operations are compiled into a grouped update document and
codec-encoded using the contract field definitions. Dot-path access
resolves codecs through value object field chains.
Export FieldAccessor, FieldExpression, FieldOperation, DotPath,
ResolveDotPathType, UpdateOperator types and createFieldAccessor
from the ORM package public API.
…mics

Replace all objectIdEq() calls with object-based where({ field: value })
in carts, orders, products, users, and invoices data files.

Replace db.raw addToCart/removeFromCart/updateOrderStatus with ORM
callback-based update() and upsert() using field accessor operators
($push, $pull). Delete object-id-filter.ts.

Fix upsert $setOnInsert to exclude fields referenced by any update
operator (not just $set), preventing MongoDB path conflicts.
…rn type, add updateAll callback test

Address code review findings F02, F03, F05:
- F02: skip codec wrapping for $unset sentinel values
- F03: add missing updateAll() callback form test
- F05: return Record<string, Record<string, MongoValue>> from
  compileFieldOperations, eliminating unsound cast
Addresses code review finding F06: all three features now have
integration tests against a real MongoDB instance (MongoMemoryReplSet)
proving codec encoding, BSON round-trip, and pipeline correctness.
… with a kind field

#isFilterExpr used a structural check ("kind" in filter) which would
misroute user data objects that legitimately have a "kind" field.
Replace with a symbol brand on the MongoFilterExpression base class
that plain objects can never carry.
FieldExpression<T> now intersects with NumericOps only when T extends
number, so calling inc()/mul() on a string field is a type error.
The runtime implementation still carries all methods via
RuntimeFieldExpression; gating is purely at the type level.
Dot-path operations like u("homeAddress.city").set("LA") conflict with
$setOnInsert on the insert path: MongoDB rejects overlapping paths, and
excluding the parent field from $setOnInsert creates an incomplete
document. Add a runtime guard that throws with a clear error message
when dot-path operations are detected in upsert callbacks.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 14, 2026

📝 Walkthrough

Walkthrough

Adds codec-aware object-shaped where() predicates, a typed callback-style field-operation API for updates/upserts with codec-aware value wrapping and dot-path/_id guards, brands Mongo filter-expression nodes, removes example ObjectId helpers, extends the test contract User schema, and expands unit/integration/type tests.

Changes

Cohort / File(s) Summary
Docs
docs/planning/mongo-target/next-steps.md
Reworded FL-06: where() should not assume codec identity and must encode filter values via field codecs; expanded scope in work plan.
Examples — Retail Store (data)
examples/retail-store/src/data/carts.ts, examples/retail-store/src/data/invoices.ts, examples/retail-store/src/data/orders.ts, examples/retail-store/src/data/products.ts, examples/retail-store/src/data/users.ts
Removed raw ObjectId helpers/executeRaw; replaced objectIdEq/rawObjectIdFilter with where({ ... }); migrated raw findOneAndUpdate/$push/$pull to ORM upsert/update patterns; tightened some parameter types and removed async from functions.
Examples — Removed helper
examples/retail-store/src/data/object-id-filter.ts
Deleted module that provided objectIdEq, rawObjectIdFilter, and ObjectId validation.
Examples — Tests / Seed
examples/retail-store/test/*.ts, examples/retail-store/src/seed.ts
Minor test/input updates: cast statusHistory readonly; added exitMethod: null in seeds/tests; other test payload typings tightened.
Query AST — Branding
packages/2-mongo-family/4-query/query-ast/src/filter-expressions.ts, packages/2-mongo-family/4-query/query-ast/src/exports/execution.ts, packages/2-mongo-family/4-query/query-ast/test/filter-expressions.test.ts
Added runtime brand and exported isMongoFilterExpr type-guard; marked prototype brand non-enumerable; tests assert brand behavior and re-exported guard.
Contract Fixtures
packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.ts, packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.json
Extended User model: added loginCount, tags, nullable homeAddress value object; added tasks 1:N relation; added top-level valueObjects.HomeAddress.
Field Accessor — New Module & Exports
packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts, packages/2-mongo-family/5-query-builders/orm/src/exports/index.ts
New typed FieldAccessor/FieldExpression/FieldOperation types and runtime createFieldAccessor proxy; compileFieldOperations implemented; new exports added.
Collection API & Update Flow
packages/2-mongo-family/5-query-builders/orm/src/collection.ts
Added where() overload accepting plain object predicates (compiles to field-eq exprs with codec wrapping); added callback-based update/updateAll/updateCount/upsert overloads producing FieldOperation[]; refactored update doc generation to operator-grouped shape; added value-wrapping helpers and runtime guards (dot-path, _id).
Types
packages/2-mongo-family/5-query-builders/orm/src/types.ts
MongoWhereFilter now reads codec metadata from fields[K].type.codecId when type.kind === 'scalar'.
ORM Tests — Unit & Integration & Types
packages/2-mongo-family/5-query-builders/orm/test/..., packages/2-mongo-family/5-query-builders/orm/test/integration/..., packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts
Extensive tests added/updated: object-shaped where() codec behavior, createFieldAccessor and compileFieldOperations unit tests, callback-based update/upsert semantics and operator grouping, dot-path/_id rejection tests, integration ORM ergonomics, and type-level assertions for FieldAccessor/DotPath.
Runtime Type Tests
packages/2-mongo-family/7-runtime/test/orm-contract-types.test-d.ts
Updated InferModelRow<User> expectations to include loginCount, tags, and homeAddress.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Collection as ORM.Collection
  participant Accessor as FieldAccessor
  participant Compiler as compileFieldOperations
  participant Codec as Codec/Wrap
  participant Driver as MongoDriver
  participant DB as MongoDB

  Client->>Collection: update((u) => { ... })
  Collection->>Accessor: createFieldAccessor() and invoke callback
  Accessor-->>Collection: returns FieldOperation[]
  Collection->>Compiler: compileFieldOperations(ops, wrapValue)
  Compiler->>Codec: wrap values (attach codecId / MongoParamRef)
  Codec-->>Driver: send operator-grouped update document
  Driver->>DB: execute update command
  DB-->>Driver: ack/result
  Driver-->>Collection: result
  Collection-->>Client: return result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 I used to nest IDs in a hole,

now codecs tuck them safe and whole.
Field ops hop on typed paths so neat,
updates bundled, dot-dreams kept discrete.
Hooray — the ORM and rabbit dance fleet!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: codec-aware where() filters, typed field mutations via FieldAccessor, and 1:N relation tests, all tied to specific feature-level objectives (FL-04/06/08).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tml-2246-orm-query-and-mutation-ergonomics-fl-04-fl-06-fl-08

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

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 14, 2026

Open in StackBlitz

@prisma-next/mongo-runtime

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-runtime@336

@prisma-next/family-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/family-mongo@336

@prisma-next/sql-runtime

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-runtime@336

@prisma-next/family-sql

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/family-sql@336

@prisma-next/extension-paradedb

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/extension-paradedb@336

@prisma-next/extension-pgvector

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/extension-pgvector@336

@prisma-next/postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/postgres@336

@prisma-next/sql-orm-client

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-orm-client@336

@prisma-next/sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sqlite@336

@prisma-next/target-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/target-mongo@336

@prisma-next/adapter-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/adapter-mongo@336

@prisma-next/driver-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/driver-mongo@336

@prisma-next/contract

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/contract@336

@prisma-next/utils

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/utils@336

@prisma-next/config

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/config@336

@prisma-next/errors

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/errors@336

@prisma-next/framework-components

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/framework-components@336

@prisma-next/operations

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/operations@336

@prisma-next/contract-authoring

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/contract-authoring@336

@prisma-next/ids

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/ids@336

@prisma-next/psl-parser

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/psl-parser@336

@prisma-next/psl-printer

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/psl-printer@336

@prisma-next/cli

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/cli@336

@prisma-next/emitter

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/emitter@336

@prisma-next/migration-tools

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/migration-tools@336

@prisma-next/vite-plugin-contract-emit

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/vite-plugin-contract-emit@336

@prisma-next/runtime-executor

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/runtime-executor@336

@prisma-next/mongo-codec

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-codec@336

@prisma-next/mongo-contract

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-contract@336

@prisma-next/mongo-value

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-value@336

@prisma-next/mongo-contract-psl

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-contract-psl@336

@prisma-next/mongo-contract-ts

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-contract-ts@336

@prisma-next/mongo-emitter

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-emitter@336

@prisma-next/mongo-schema-ir

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-schema-ir@336

@prisma-next/mongo-query-ast

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-query-ast@336

@prisma-next/mongo-orm

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-orm@336

@prisma-next/mongo-pipeline-builder

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-pipeline-builder@336

@prisma-next/mongo-lowering

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-lowering@336

@prisma-next/mongo-wire

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-wire@336

@prisma-next/sql-contract

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract@336

@prisma-next/sql-errors

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-errors@336

@prisma-next/sql-operations

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-operations@336

@prisma-next/sql-schema-ir

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-schema-ir@336

@prisma-next/sql-contract-psl

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract-psl@336

@prisma-next/sql-contract-ts

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract-ts@336

@prisma-next/sql-contract-emitter

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract-emitter@336

@prisma-next/sql-lane-query-builder

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-lane-query-builder@336

@prisma-next/sql-relational-core

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-relational-core@336

@prisma-next/sql-builder

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-builder@336

@prisma-next/target-postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/target-postgres@336

@prisma-next/target-sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/target-sqlite@336

@prisma-next/adapter-postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/adapter-postgres@336

@prisma-next/adapter-sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/adapter-sqlite@336

@prisma-next/driver-postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/driver-postgres@336

@prisma-next/driver-sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/driver-sqlite@336

commit: 8190ed2

Copy link
Copy Markdown

@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

🧹 Nitpick comments (1)
packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts (1)

1-1080: Consider splitting this test file.

This test file is now ~1080 lines, exceeding the 500-line guideline. Consider splitting by functionality into separate files such as:

  • collection.chaining.test.ts
  • collection.where.test.ts
  • collection.mutations.test.ts
  • collection.field-accessor.test.ts
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts` around
lines 1 - 1080, The file is too large; split the big test suite into multiple
focused test files by moving the describe blocks into new files (e.g., move the
"MongoCollection chaining" block to collection.chaining.test.ts, the
"MongoCollection object-based where()" plus related where chaining tests to
collection.where.test.ts, the "createFieldAccessor()" and
"compileFieldOperations()" blocks to collection.field-accessor.test.ts, and the
large "MongoCollection write methods" block (including
create/createAll/update/updateAll/upsert/delete/*mutation-related* tests) to
collection.mutations.test.ts); preserve imports used across blocks
(MongoFieldFilter, MongoParamRef, createMongoCollection, createFieldAccessor,
compileFieldOperations, createMockExecutor helper and the
contract/defaultUserData), and ensure each new file exports/uses the same helper
functions (move createMockExecutor and shared constants to a small test-utils
module or duplicate them minimally) so tests compile and run unchanged aside
from file splits.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/2-mongo-family/5-query-builders/orm/src/collection.ts`:
- Around line 351-364: Callback-based updates currently allow modifying _id
because compileFieldOperations invoked from callback paths doesn't reject _id
like object-based updates do via `#toSetFields`; update the `#resolveUpdateDoc`
method to inspect the compiled FieldOperation[] (from FieldAccessor callbacks)
and throw the same error string ('Mutation payloads cannot modify `_id`') if any
operation targets _id, thereby matching the protection provided for object
updates in `#toSetFields` and covering the update, updateAll, and updateCount
callback flows.

---

Nitpick comments:
In `@packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts`:
- Around line 1-1080: The file is too large; split the big test suite into
multiple focused test files by moving the describe blocks into new files (e.g.,
move the "MongoCollection chaining" block to collection.chaining.test.ts, the
"MongoCollection object-based where()" plus related where chaining tests to
collection.where.test.ts, the "createFieldAccessor()" and
"compileFieldOperations()" blocks to collection.field-accessor.test.ts, and the
large "MongoCollection write methods" block (including
create/createAll/update/updateAll/upsert/delete/*mutation-related* tests) to
collection.mutations.test.ts); preserve imports used across blocks
(MongoFieldFilter, MongoParamRef, createMongoCollection, createFieldAccessor,
compileFieldOperations, createMockExecutor helper and the
contract/defaultUserData), and ensure each new file exports/uses the same helper
functions (move createMockExecutor and shared constants to a small test-utils
module or duplicate them minimally) so tests compile and run unchanged aside
from file splits.
🪄 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.yml

Review profile: CHILL

Plan: Pro

Run ID: 8f93eca9-2c82-45d1-8dea-1a803205cce2

📥 Commits

Reviewing files that changed from the base of the PR and between 17fd2dc and 7385b54.

⛔ Files ignored due to path filters (5)
  • projects/mongo-example-apps/framework-limitations.md is excluded by !projects/**
  • projects/mongo-example-apps/plans/orm-query-mutation-ergonomics-plan.md is excluded by !projects/**
  • projects/mongo-example-apps/plans/retail-store-round-2-plan.md is excluded by !projects/**
  • projects/mongo-example-apps/specs/orm-query-mutation-ergonomics.spec.md is excluded by !projects/**
  • projects/mongo-example-apps/specs/retail-store-round-2.spec.md is excluded by !projects/**
📒 Files selected for processing (22)
  • docs/planning/mongo-target/next-steps.md
  • examples/retail-store/src/data/carts.ts
  • examples/retail-store/src/data/invoices.ts
  • examples/retail-store/src/data/object-id-filter.ts
  • examples/retail-store/src/data/orders.ts
  • examples/retail-store/src/data/products.ts
  • examples/retail-store/src/data/users.ts
  • examples/retail-store/test/order-lifecycle.test.ts
  • packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.ts
  • packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.json
  • packages/2-mongo-family/4-query/query-ast/src/exports/execution.ts
  • packages/2-mongo-family/4-query/query-ast/src/filter-expressions.ts
  • packages/2-mongo-family/4-query/query-ast/test/filter-expressions.test.ts
  • packages/2-mongo-family/5-query-builders/orm/src/collection.ts
  • packages/2-mongo-family/5-query-builders/orm/src/exports/index.ts
  • packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts
  • packages/2-mongo-family/5-query-builders/orm/src/types.ts
  • packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts
  • packages/2-mongo-family/5-query-builders/orm/test/integration/orm-ergonomics.test.ts
  • packages/2-mongo-family/5-query-builders/orm/test/integration/polymorphism.test.ts
  • packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts
  • packages/2-mongo-family/7-runtime/test/orm-contract-types.test-d.ts
💤 Files with no reviewable changes (1)
  • examples/retail-store/src/data/object-id-filter.ts

Comment thread packages/2-mongo-family/5-query-builders/orm/src/collection.ts
wmadden added 2 commits April 14, 2026 14:31
…essor

Replace non-null assertions with nullish-coalescing fallbacks and
explicit local variables to satisfy biome strict mode.
Object-based updates reject _id via #toSetFields, but callback-based
updates (update, updateAll, updateCount, upsert) bypassed that check.
Add _id validation in #resolveUpdateDoc and the upsert callback path
so u._id.set() throws the same error as object mutations.
@wmadden
Copy link
Copy Markdown
Contributor Author

wmadden commented Apr 14, 2026

Addressed all review findings:

  • _id validation for callback-based updates — Fixed. #resolveUpdateDoc and the upsert() callback path now reject _id operations with the same error as object-based updates. Unit tests added for update(), updateAll(), and upsert() callback paths.
  • Test file splitting (nitpick) — Acknowledged. Will track separately; the current file is functional and the split is a refactoring concern outside this PR scope.

Copy link
Copy Markdown

@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 (1)
packages/2-mongo-family/5-query-builders/orm/src/collection.ts (1)

460-473: ⚠️ Potential issue | 🟠 Major

Reject _id in callback mutations too.

#toSetFields() blocks _id for plain objects, but these callback branches compile arbitrary FieldOperation[] directly. A callback can still target _id here, so the new overloads no longer enforce the same invariant as the existing object path. Add the same top-level _id check before compileFieldOperations().

Also applies to: 641-652

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/2-mongo-family/5-query-builders/orm/src/collection.ts` around lines
460 - 473, The callback-based update branch in upsert() (the path using
createFieldAccessor, ops, and compileFieldOperations with
this.#wrapFieldOpValue) currently allows operations targeting the top-level _id
field; add the same top-level _id rejection that `#toSetFields` enforces before
calling compileFieldOperations so `_id` updates are rejected in callback
mutations, and apply the identical fix to the second callback branch (the
similar compileFieldOperations usage around the other upsert/update handling) to
ensure consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/2-mongo-family/5-query-builders/orm/src/collection.ts`:
- Around line 646-652: When the callback path (dataOrCallback function) produces
no operations, normalize that result to match the object overload by handling
empty ops returned from compileFieldOperations; inside the branch that calls
createFieldAccessor<TContract, ModelName>() and compileFieldOperations(...
this.#wrapFieldOpValue ...), check if the generated ops is empty (e.g., length
=== 0 or deep-empty) and then return the same no-op shape used by the object
overload (for example { $set: {} }) or throw a clear error; update the behavior
in the dataOrCallback branch so update(), updateAll(), and updateCount() see
identical no-op semantics as the object overload.
- Around line 656-692: The current `#wrapFieldOpValue` and `#wrapDotPathValue` only
re-wrap scalar leaves, so when an operation value is a value-object (e.g.,
callback payloads for $set/$push/$pull) its nested scalar fields skip codec
wrapping; update both `#wrapFieldOpValue` and `#wrapDotPathValue` to detect when the
target field type is a valueObject and the provided value is an object (not a
MongoParamRef) and then pass that object through the same value-object wrapping
logic used elsewhere (call the existing `#wrapValueObject` or the same wrapping
routine used by `#wrapFieldValue`) so nested scalar fields get wrapped with their
codecIds; ensure you handle dot-path resolution in `#wrapDotPathValue` to obtain
the correct ContractValueObject (voDef) and invoke the wrapper before returning.

---

Duplicate comments:
In `@packages/2-mongo-family/5-query-builders/orm/src/collection.ts`:
- Around line 460-473: The callback-based update branch in upsert() (the path
using createFieldAccessor, ops, and compileFieldOperations with
this.#wrapFieldOpValue) currently allows operations targeting the top-level _id
field; add the same top-level _id rejection that `#toSetFields` enforces before
calling compileFieldOperations so `_id` updates are rejected in callback
mutations, and apply the identical fix to the second callback branch (the
similar compileFieldOperations usage around the other upsert/update handling) to
ensure consistency.
🪄 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.yml

Review profile: CHILL

Plan: Pro

Run ID: 928fccf1-aed1-4e90-bed4-d2220b9aed71

📥 Commits

Reviewing files that changed from the base of the PR and between 7385b54 and e1f26b3.

📒 Files selected for processing (2)
  • packages/2-mongo-family/5-query-builders/orm/src/collection.ts
  • packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts

Comment thread packages/2-mongo-family/5-query-builders/orm/src/collection.ts
Comment thread packages/2-mongo-family/5-query-builders/orm/src/collection.ts
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts (1)

1-1112: Consider splitting this test file in a follow-up.

This file is 1112 lines, exceeding the 500-line guideline. Natural split boundaries exist:

  • collection.chaining.test.ts — chaining and terminal methods
  • collection.field-accessor.test.tscreateFieldAccessor() and compileFieldOperations() tests
  • collection.write.test.ts — create/update/delete methods

As per coding guidelines, "Keep test files under 500 lines to maintain readability and navigability."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts` around
lines 1 - 1112, Test file is too large (>1100 lines); split into smaller files
to follow the 500-line guideline. Create three new test files and move the
described blocks: 1) collection.chaining.test.ts — move the top-level
"MongoCollection chaining" and "MongoCollection terminal methods" describe
blocks (and related tests that assert plan/stage compilation like all(),
first()); 2) collection.field-accessor.test.ts — move the
"createFieldAccessor()" and "compileFieldOperations()" describe blocks and their
tests (functions: createFieldAccessor, compileFieldOperations, FieldAccessor,
FieldOperation); 3) collection.write.test.ts — move all write-related describe
blocks ("MongoCollection write methods", including create/createAll/createCount,
update/updateAll/updateCount/upsert, include rejection on write terminals, _id
rejection on update paths, undefined normalization, immutability) and their
tests (refer to methods create, createAll, update, updateAll, updateCount,
delete, deleteCount, upsert). Ensure each new file imports the same helpers used
here (createMongoCollection, createMockExecutor, MongoFieldFilter,
MongoParamRef, contract fixture, etc.), update import paths as needed, and run
tests to verify no inter-file dependencies remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts`:
- Around line 1-1112: Test file is too large (>1100 lines); split into smaller
files to follow the 500-line guideline. Create three new test files and move the
described blocks: 1) collection.chaining.test.ts — move the top-level
"MongoCollection chaining" and "MongoCollection terminal methods" describe
blocks (and related tests that assert plan/stage compilation like all(),
first()); 2) collection.field-accessor.test.ts — move the
"createFieldAccessor()" and "compileFieldOperations()" describe blocks and their
tests (functions: createFieldAccessor, compileFieldOperations, FieldAccessor,
FieldOperation); 3) collection.write.test.ts — move all write-related describe
blocks ("MongoCollection write methods", including create/createAll/createCount,
update/updateAll/updateCount/upsert, include rejection on write terminals, _id
rejection on update paths, undefined normalization, immutability) and their
tests (refer to methods create, createAll, update, updateAll, updateCount,
delete, deleteCount, upsert). Ensure each new file imports the same helpers used
here (createMongoCollection, createMockExecutor, MongoFieldFilter,
MongoParamRef, contract fixture, etc.), update import paths as needed, and run
tests to verify no inter-file dependencies remain.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 052a950b-f64f-4d9b-a095-166e75bacc32

📥 Commits

Reviewing files that changed from the base of the PR and between e1f26b3 and 5c72d3b.

📒 Files selected for processing (2)
  • packages/2-mongo-family/5-query-builders/orm/src/collection.ts
  • packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/2-mongo-family/5-query-builders/orm/src/collection.ts

wmadden added 2 commits April 15, 2026 06:45
…eld operations

Empty callback updates now produce { $set: {} } matching object-based update
behavior. Value-object payloads in callback mutations (set/push/pull) are now
recursively wrapped through their codecs, matching the wrapping applied to
object-based updates and create paths.
Copy link
Copy Markdown

@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

🧹 Nitpick comments (2)
packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts (2)

29-35: Use a fixture factory instead of a shared mutable object.

defaultUserData is reused across many tests and contains a mutable array (tags). A factory avoids hidden coupling and mutation bleed between tests.

Refactor sketch
-const defaultUserData = {
-  name: 'Alice',
-  email: 'a@b.c',
-  loginCount: 0,
-  tags: [] as string[],
-  homeAddress: null,
-};
+const createDefaultUserData = () => ({
+  name: 'Alice',
+  email: 'a@b.c',
+  loginCount: 0,
+  tags: [] as string[],
+  homeAddress: null,
+});
-const result = await col.create(defaultUserData);
+const result = await col.create(createDefaultUserData());

Also applies to: 529-530, 578-580, 760-761, 1064-1065

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts` around
lines 29 - 35, defaultUserData is a shared mutable fixture (contains tags:
string[]) causing tests to leak state; replace it with a factory function (e.g.,
makeDefaultUserData or defaultUserDataFactory) that returns a fresh object each
call and update all tests that reference defaultUserData to call the factory
instead (also replace other occurrences of the same shared object). Ensure the
factory clones/creates new primitives and a new array for tags so tests no
longer share mutable state.

162-1112: Please split this test file by concern.

This file is now very large and mixes multiple feature areas (chaining/read paths, includes, object-where, field-accessor unit tests, callback mutation guards). Splitting will improve reviewability and failure triage.

As per coding guidelines, *.test.ts files should stay under 500 lines and be split by logical boundaries using descriptive names.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts` around
lines 162 - 1112, The single large test file should be split into smaller files
by concern: create separate test files for (1) "object-based where" and related
MongoCollection read/terminal tests (describe 'MongoCollection object-based
where()', 'MongoCollection variant()', 'MongoCollection include()',
'MongoCollection terminal methods'), (2) "field accessor &
compileFieldOperations" (describe 'createFieldAccessor()',
'compileFieldOperations()'), (3) "write methods and mutation guards" (describe
blocks under 'MongoCollection write methods', including
create/createAll/update/updateAll/upsert/delete and related
rejection/immutability tests), and (4) any "include rejection on write
terminals" and "undefined/_id normalization" tests if not already in write
group; for each new file move the matching describe(...) blocks (e.g.,
'createFieldAccessor()', 'compileFieldOperations()', 'MongoCollection
variant()', etc.) into their respective files, ensure shared helpers/fixtures
like createMockExecutor, contract, defaultUserData, MongoFieldFilter,
MongoParamRef are imported from the original test utils module, update any
relative imports, and keep each resulting *.test.ts under ~500 lines so CI
passes; do not change test logic—only relocate blocks and adjust
imports/exports.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts`:
- Around line 722-753: The test names claim they validate the "correct update
doc" but only assert row/modified counts; update the tests that use
createMockExecutor, createMongoCollection, MongoFieldFilter.eq,
col.updateAll(...) and col.updateCount(...) to also assert the actual generated
update document (available as executor.lastPlan) matches the expected Mongo
update shape (e.g., $inc/$set structure and field paths), or else change the
test names to reflect they only check counts; ensure assertions reference
executor.lastPlan and verify operator grouping/encoding for u.loginCount.inc(1)
and u.name.set('X').

---

Nitpick comments:
In `@packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts`:
- Around line 29-35: defaultUserData is a shared mutable fixture (contains tags:
string[]) causing tests to leak state; replace it with a factory function (e.g.,
makeDefaultUserData or defaultUserDataFactory) that returns a fresh object each
call and update all tests that reference defaultUserData to call the factory
instead (also replace other occurrences of the same shared object). Ensure the
factory clones/creates new primitives and a new array for tags so tests no
longer share mutable state.
- Around line 162-1112: The single large test file should be split into smaller
files by concern: create separate test files for (1) "object-based where" and
related MongoCollection read/terminal tests (describe 'MongoCollection
object-based where()', 'MongoCollection variant()', 'MongoCollection include()',
'MongoCollection terminal methods'), (2) "field accessor &
compileFieldOperations" (describe 'createFieldAccessor()',
'compileFieldOperations()'), (3) "write methods and mutation guards" (describe
blocks under 'MongoCollection write methods', including
create/createAll/update/updateAll/upsert/delete and related
rejection/immutability tests), and (4) any "include rejection on write
terminals" and "undefined/_id normalization" tests if not already in write
group; for each new file move the matching describe(...) blocks (e.g.,
'createFieldAccessor()', 'compileFieldOperations()', 'MongoCollection
variant()', etc.) into their respective files, ensure shared helpers/fixtures
like createMockExecutor, contract, defaultUserData, MongoFieldFilter,
MongoParamRef are imported from the original test utils module, update any
relative imports, and keep each resulting *.test.ts under ~500 lines so CI
passes; do not change test logic—only relocate blocks and adjust
imports/exports.
🪄 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.yml

Review profile: CHILL

Plan: Pro

Run ID: c19b9c15-3ca4-49c6-8a57-ae6a9a0af31b

📥 Commits

Reviewing files that changed from the base of the PR and between 5c72d3b and 7f09682.

📒 Files selected for processing (1)
  • packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
packages/2-mongo-family/5-query-builders/orm/src/collection.ts (1)

698-730: Dot-path traversal does not handle array indices.

The current implementation treats all dot-path segments as field names. If a user specifies a path like "items.0.name" (array index access), it will fail to resolve the field definition and return the value unwrapped.

This is likely acceptable for now since array index paths are uncommon in typical MongoDB updates, but worth noting for future consideration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/2-mongo-family/5-query-builders/orm/src/collection.ts` around lines
698 - 730, The traversal in `#wrapDotPathValue` treats every dot segment as an
object field and fails on numeric array indices (e.g. "items.0.name"); update
the for-loop in `#wrapDotPathValue` to detect numeric segments (e.g. /^\d+$/) and
skip treating them as object keys (continue to next segment) so array indices
don't break resolution, and additionally handle arrays of value objects by, when
currentField?.type.kind === 'array' and the array item type is a valueObject,
drilling into the array item type (set currentField to the array item
definition) before attempting the voDef lookup so value-object wrapping still
works for paths like "items.0.name". Ensure you reference currentField,
parts[i], voDef, and use the existing `#wrapValueObject` for final wrapping.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/2-mongo-family/5-query-builders/orm/src/collection.ts`:
- Around line 698-730: The traversal in `#wrapDotPathValue` treats every dot
segment as an object field and fails on numeric array indices (e.g.
"items.0.name"); update the for-loop in `#wrapDotPathValue` to detect numeric
segments (e.g. /^\d+$/) and skip treating them as object keys (continue to next
segment) so array indices don't break resolution, and additionally handle arrays
of value objects by, when currentField?.type.kind === 'array' and the array item
type is a valueObject, drilling into the array item type (set currentField to
the array item definition) before attempting the voDef lookup so value-object
wrapping still works for paths like "items.0.name". Ensure you reference
currentField, parts[i], voDef, and use the existing `#wrapValueObject` for final
wrapping.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: a6f03164-0b0f-48b9-b6a9-a61898cfdf92

📥 Commits

Reviewing files that changed from the base of the PR and between 7f09682 and 00fdc93.

📒 Files selected for processing (2)
  • packages/2-mongo-family/5-query-builders/orm/src/collection.ts
  • packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts

wmadden added 4 commits April 15, 2026 07:37
Symbol-based brands break under dual-package installs because each copy
of the module creates its own unique symbol. A string-keyed, non-enumerable
prototype property survives this scenario since string in-checks compare
by value, not reference identity.
… type maps

ResolveFieldType only handled scalar fields, causing value-object
fields (e.g. homeAddress) to fall through to unknown. This silently
accepted any value in set()/push()/pull() for those fields.

Now uses ResolvedModelRow (ExtractMongoFieldOutputTypes with
InferModelRow fallback), matching the same pattern as
ResolvedOutputRow in types.ts. Value-object fields resolve to their
concrete emitted types, restoring type safety.
…act types

Use CartItemInput, OrderLineItemInput, StatusEntryInput,
InvoiceLineItemInput, FieldInputTypes from contract.d.ts instead of
inline object type literals. This ensures the data layer stays in sync
with schema changes automatically and demonstrates the value of
contract-emitted types.
MongoFieldFilter,
MongoNotExpr,
MongoOrExpr,
mongoFilterBrand,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This should not be exported

…d definition

Replace exported mongoFilterBrand constant with isMongoFilterExpr()
predicate defined alongside the brand. The brand is now an
implementation detail; consumers use the predicate instead of
performing raw in-checks against an exported constant.
const ops = input.update(accessor);
const idOp = ops.find((op) => op.field === '_id');
if (idOp) {
throw new Error('Mutation payloads cannot modify `_id`');
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Do we have a better runtime error type/envelope defined? We need one, we can't throw raw errors on a meaningful user execution path

wmadden added 3 commits April 15, 2026 08:11
…oint

The filter brand predicate is an implementation detail, not a public
API. Move it from the execution barrel to a dedicated ./internal
entry point so only internal consumers (mongo-orm) can access it.
The predicate belongs alongside the types it discriminates. Remove
the bogus ./internal entry point — it was not a real access control
boundary and had no precedent in this repo.
currentField = partKey ? voDef.fields[partKey] : undefined;
}

if (currentField?.type.kind === 'scalar' && value instanceof MongoParamRef) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

DOn't use instanceof

Comment on lines +721 to +724
const voDef = (this.#contract as { valueObjects?: Record<string, ContractValueObject> })
.valueObjects?.[voName];
if (voDef) {
return this.#wrapValueObject(raw as Record<string, unknown>, voDef);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Why the casts?

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts (1)

1-1139: Test file exceeds 500-line limit.

At ~1139 lines, this file exceeds the 500-line guideline for test files. The author acknowledged this will be tracked separately as a refactor. Consider splitting by functionality:

  • collection.chaining.test.ts - chaining/immutability tests
  • collection.where.test.ts - object-based and filter-based where tests
  • collection.mutations.test.ts - create/update/delete tests
  • collection.field-accessor.test.ts - field accessor and compilation tests
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts` around
lines 1 - 1139, The test file is too large (~1139 lines); split it into focused
test files to meet the 500-line guideline by extracting related suites: move
chaining/immutability tests (describe 'MongoCollection chaining' and the
'immutability' suite) into collection.chaining.test.ts; move object-based where
tests (describe 'MongoCollection object-based where()' and related
include/windowing rejection cases) into collection.where.test.ts; move all
mutation and write-path tests (describe 'MongoCollection write methods'
including
create/createAll/createCount/update/updateAll/updateCount/upsert/delete/deleteCount
and the include rejection/undefined/_id rejection/windowing rejection suites)
into collection.mutations.test.ts; and move
field-accessor/compileFieldOperations suites (describe 'createFieldAccessor()'
and 'compileFieldOperations()') into collection.field-accessor.test.ts; ensure
each new file retains imports for createMongoCollection, MongoFieldFilter,
createFieldAccessor, compileFieldOperations, MongoParamRef, AsyncIterableResult
and the contract fixture, and preserve individual it() blocks and any helper
createMockExecutor function and defaultUserData so tests still reference
createMongoCollection, MongoFieldFilter, createFieldAccessor,
compileFieldOperations, and FieldAccessor correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts`:
- Around line 1-1139: The test file is too large (~1139 lines); split it into
focused test files to meet the 500-line guideline by extracting related suites:
move chaining/immutability tests (describe 'MongoCollection chaining' and the
'immutability' suite) into collection.chaining.test.ts; move object-based where
tests (describe 'MongoCollection object-based where()' and related
include/windowing rejection cases) into collection.where.test.ts; move all
mutation and write-path tests (describe 'MongoCollection write methods'
including
create/createAll/createCount/update/updateAll/updateCount/upsert/delete/deleteCount
and the include rejection/undefined/_id rejection/windowing rejection suites)
into collection.mutations.test.ts; and move
field-accessor/compileFieldOperations suites (describe 'createFieldAccessor()'
and 'compileFieldOperations()') into collection.field-accessor.test.ts; ensure
each new file retains imports for createMongoCollection, MongoFieldFilter,
createFieldAccessor, compileFieldOperations, MongoParamRef, AsyncIterableResult
and the contract fixture, and preserve individual it() blocks and any helper
createMockExecutor function and defaultUserData so tests still reference
createMongoCollection, MongoFieldFilter, createFieldAccessor,
compileFieldOperations, and FieldAccessor correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 95c6aafb-0214-45d0-9620-18bb8d45f95a

📥 Commits

Reviewing files that changed from the base of the PR and between 7f09682 and 8190ed2.

📒 Files selected for processing (15)
  • examples/retail-store/src/data/carts.ts
  • examples/retail-store/src/data/events.ts
  • examples/retail-store/src/data/invoices.ts
  • examples/retail-store/src/data/orders.ts
  • examples/retail-store/src/data/users.ts
  • examples/retail-store/src/seed.ts
  • examples/retail-store/test/aggregation.test.ts
  • examples/retail-store/test/crud-lifecycle.test.ts
  • packages/2-mongo-family/4-query/query-ast/src/exports/execution.ts
  • packages/2-mongo-family/4-query/query-ast/src/filter-expressions.ts
  • packages/2-mongo-family/4-query/query-ast/test/filter-expressions.test.ts
  • packages/2-mongo-family/5-query-builders/orm/src/collection.ts
  • packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts
  • packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts
  • packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts
✅ Files skipped from review due to trivial changes (1)
  • examples/retail-store/test/aggregation.test.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/2-mongo-family/4-query/query-ast/src/exports/execution.ts
  • examples/retail-store/src/data/users.ts
  • examples/retail-store/src/data/orders.ts
  • examples/retail-store/src/data/carts.ts
  • packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts
  • packages/2-mongo-family/5-query-builders/orm/src/collection.ts
  • examples/retail-store/src/data/invoices.ts

@wmadden wmadden merged commit 6737f1f into main Apr 15, 2026
16 checks passed
@wmadden wmadden deleted the tml-2246-orm-query-and-mutation-ergonomics-fl-04-fl-06-fl-08 branch April 15, 2026 06:04
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.

1 participant