Skip to content

feat(emitter): generate FieldInputTypes and wire ORM consumption#335

Open
wmadden wants to merge 20 commits intomainfrom
tml-2245-mongo-type-ergonomics-fix-codec-output-types-fl-010203-tml
Open

feat(emitter): generate FieldInputTypes and wire ORM consumption#335
wmadden wants to merge 20 commits intomainfrom
tml-2245-mongo-type-ergonomics-fix-codec-output-types-fl-010203-tml

Conversation

@wmadden
Copy link
Copy Markdown
Contributor

@wmadden wmadden commented Apr 13, 2026

closes TML-2245

Key snippet

New — single-pass field type resolution

export function resolveFieldType(
  field: ContractField,
  codecLookup?: CodecLookup,
): ResolvedFieldType {
  const { type } = field;
  switch (type.kind) {
    case 'scalar': {
      let outputResolved: string | undefined;
      // renderOutputType only fires for the output side
      if (codecLookup && type.typeParams && Object.keys(type.typeParams).length > 0) {
        const codec = codecLookup.get(type.codecId);
        if (codec?.renderOutputType) {
          const rendered = codec.renderOutputType(type.typeParams);
          if (rendered && isSafeTypeExpression(rendered)) outputResolved = rendered;
        }
      }
      const codecAccessor = `CodecTypes[${serializeValue(type.codecId)}]`;
      return {
        output: applyModifiers(outputResolved ?? `${codecAccessor}['output']`, field),
        input: applyModifiers(`${codecAccessor}['input']`, field),
      };
    }
    // ...
  }
}

The resolution function returns both input and output type expressions in one call. renderOutputType is explicitly output-only — the input side always falls through to the generic codec accessor.

New — ORM fallback heuristic

type ResolvedOutputRow<
  TContract extends MongoContractWithTypeMaps<MongoContract, MongoTypeMaps>,
  ModelName extends string & keyof TContract['models'],
> = string extends keyof ExtractMongoFieldOutputTypes<TContract>
  ? InferModelRow<TContract, ModelName>
  : ModelName extends keyof ExtractMongoFieldOutputTypes<TContract>
    ? {
        -readonly [K in keyof ExtractMongoFieldOutputTypes<TContract>[ModelName]]: ExtractMongoFieldOutputTypes<TContract>[ModelName][K];
      }
    : InferModelRow<TContract, ModelName>;

When FieldOutputTypes is a concrete map (not the defaulted Record<string, Record<string, unknown>>), the ORM indexes directly into it for the model's row type — bypassing the deep InferModelRow conditional chain that TypeScript can't fully evaluate.

Intent

Wire the pre-resolved FieldOutputTypes (and a new FieldInputTypes) from emitted contracts into both the Mongo ORM's and SQL query builder's type resolution, so query results and mutation inputs resolve to concrete primitives (string, number, Date) instead of opaque conditional types. This eliminates ~100 explicit type casts in the retail store example.

Change map

The story

  1. Add single-pass field type resolution: Introduce resolveFieldType returning { input, output } in one call, and generateBothFieldTypesMaps which iterates over models/fields once to produce both maps. renderOutputType is explicitly output-only — the input side always uses the generic codec accessor. Value object aliases are split from {Name} into {Name}Input / {Name}Output.

  2. Extend MongoTypeMaps to carry FieldInputTypes: Add a 4th type parameter (defaulted) alongside the existing FieldOutputTypes parameter. Add a parallel extractor ExtractMongoFieldInputTypes. The defaults ensure backward compatibility for contracts emitted before this change.

  3. ORM prefers pre-resolved types over InferModelRow: Introduce ResolvedOutputRow and ResolvedInputRow type helpers that check whether the contract carries concrete field type maps. If yes, index directly into them (stripping readonly). If no, fall back to InferModelRow. Wire these into DefaultModelRow, InferFullRow, EmbedRelationRowType, CreateInput, and VariantCreateInput.

  4. Re-emit contracts and remove casts: Re-emit both the retail store and mongo-demo contract.d.ts with the new 4-parameter MongoTypeMaps and split value object aliases. Remove all ~100 codec-related casts from the retail store.

  5. Extend SQL TypeMaps and wire query builders: Add TFieldInputTypes as a 5th type parameter to the SQL family's TypeMaps (with a default for backward compat). Add FieldInputTypesOf extractor and ExtractFieldInputTypes convenience alias. Update the SQL emitter's getTypeMapsExpression() to pass FieldInputTypes.

  6. SQL query builders consume pre-resolved field type maps: Introduce FindModelForTable, FindFieldForColumn, and ResolvedColumnTypes type helpers in table-proxy.ts that map table columns back to model fields via the contract's storage mapping — enabling the SQL builder to consume FieldOutputTypes/FieldInputTypes without any new imports from relational-core. ResolveRow gains an optional PreResolved parameter; when pre-resolved types are available for a column, it uses them directly (applying nullability), falling back to the existing codec-based resolution otherwise. ContractToQC is parameterized by table name and computes resolvedColumnOutputTypes per-table. insert()/update() call sites use ResolvedInsertValues/ResolvedUpdateValues for the input side.

Behavior changes & evidence

Compatibility / migration / risk

  • Breaking change to emitted value object aliases: Un-suffixed aliases (e.g., Price, Address) are replaced with PriceOutput / PriceInput. Any user code importing the un-suffixed alias must update.
  • MongoTypeMaps backward-compatible: All new type parameters have defaults. Older contracts continue to compile and the ORM falls back to InferModelRow.
  • No runtime changes: All changes are type-system only.

Follow-ups / open questions

  • TML-2229 (no-emit parameterized types): When parameterized codecs with different input/output types land, renderInputType will need to be added to codecs and resolveFieldType updated to use it for the input side.
  • VariantCreateInput asymmetry: Variant fields use the output type path via VariantModelRow, not the input type path. Documented with a TODO; harmless today but will need fixing when input/output types diverge.

Non-goals / intentionally out of scope

  • SQL builder join-table resolution (pre-resolved types only cover the primary table; joined table columns fall back to codec resolution)
  • renderInputType on codecs (deferred to TML-2229)
  • No-emit path parameterized types (TML-2229)

Summary by CodeRabbit

  • Bug Fixes

    • Fixed identifier and numeric coercion across APIs, checkout/cart/orders, seed data, and tests.
    • Improved ownership and authorization checks to use strict identity comparisons.
  • Data Integrity

    • Enforced strict collection-level JSON schema validation to prevent invalid storage shapes.
  • Documentation

    • Split and clarified input vs. output type contracts for safer data typing and clearer developer intent.
  • Chores

    • Updated CI test environment configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: a8184d01-c3ec-4ee3-8f01-8501c23f3c9b

📥 Commits

Reviewing files that changed from the base of the PR and between f0a760c and e8563a9.

📒 Files selected for processing (1)
  • examples/retail-store/test/api-flows.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/retail-store/test/api-flows.test.ts

📝 Walkthrough

Walkthrough

This PR splits value-object types into input/output across the emitter and generated contracts, introduces per-field FieldInputTypes alongside FieldOutputTypes, updates query-builder types to consume both maps, updates generated contracts/examples/tests to the new types, removes many runtime coercions, and adds MONGOMS_VERSION to CI jobs.

Changes

Cohort / File(s) Summary
CI Configuration
\.github/workflows/ci.yml
Added MONGOMS_VERSION: 7.0.11 env to test, test-e2e, test-integration, and coverage jobs.
Emitter — Domain type generation
packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts, packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts, packages/1-framework/3-tooling/emitter/test/domain-type-generation.test.ts
Introduce ResolvedFieldType, resolveFieldType, generate both input/output field maps, emit paired *Output/*Input aliases, and adjust generator/tests to produce FieldInputTypes alongside outputs.
Mongo contract types & emitter
packages/2-mongo-family/1-foundation/mongo-contract/src/contract-types.ts, .../exports/index.ts, packages/2-mongo-family/3-tooling/emitter/src/index.ts, packages/2-mongo-family/.../test/*
Add fieldInputTypes generic and fieldInputTypes property to MongoTypeMaps, add ExtractMongoFieldInputTypes, update emission to include FieldInputTypes, and update tests/expectations for split aliases.
Mongo generated contract & example
examples/mongo-demo/src/contract.d.ts, examples/mongo-demo/src/contract.json
Generated contract updated: Address split into AddressOutput/AddressInput, TypeMaps now includes FieldInputTypes; collection validators (strict jsonSchema) added/updated; storageHash changed.
Mongo ORM / query-builders
packages/2-mongo-family/5-query-builders/orm/src/types.ts, packages/2-mongo-family/.../test/value-object-inputs.test-d.ts
Add ResolvedOutputRow/ResolvedInputRow helpers using field output/input extraction; update DefaultModelRow, CreateInput, and tests to conditionally resolve fields via FieldOutputTypes/FieldInputTypes.
SQL contract types & emitter
packages/2-sql/1-core/contract/src/types.ts, .../exports/types.ts, packages/2-sql/3-tooling/emitter/src/index.ts, packages/2-sql/.../test/*
Add fieldInputTypes generic and extraction utilities (FieldInputTypesOf, ExtractFieldInputTypes), update emitter to include FieldInputTypes, and update tests / generated alias expectations to split output/input aliases.
SQL query-builder core plumbing
packages/2-sql/4-lanes/sql-builder/src/resolve.ts, .../scope.ts, .../runtime/*, .../types/*
Add PreResolved/resolvedColumnOutputTypes plumbing to ResolveRow and QueryContext; propagate QC['resolvedColumnOutputTypes'] into build() return types across query/mutation implementations and interfaces.
SQL TableProxy & column resolution types
packages/2-sql/4-lanes/sql-builder/src/types/table-proxy.ts
Add conditional types to resolve per-table/column input/output shapes (ResolvedInsertValues, ResolvedUpdateValues), change TableProxy generics and insert/update signatures to use resolved input types driven by ExtractFieldInputTypes.
SQL builder tests & playgrounds
packages/2-sql/4-lanes/sql-builder/test/*, packages/2-sql/4-lanes/sql-builder/test/playground/*
Add type-level tests asserting resolved select/insert/update/delete row types and adjust test type contexts to include resolvedColumnOutputTypes.
Retail-store example (app, contracts, seed, pages)
examples/retail-store/app/..., examples/retail-store/src/contract.d.ts, examples/retail-store/src/seed.ts
Split many value-object types into *Input/*Output, update generated TypeMaps to accept FieldInputTypes, remove numerous String()/Number()/as string coercions across handlers/pages/seed.
Retail-store tests
examples/retail-store/test/*.test.ts
Remove as string and other coercions in tests; assertions updated to compare native ID/price types where appropriate.
Prisma-next demo & client
examples/prisma-next-demo/src/prisma/contract.d.ts, examples/prisma-next-demo/src/orm-client/create-user-with-address.ts
Split Address into AddressOutput/AddressInput, add FieldInputTypes, and update client input type to use AddressInput.
Integration test scripts & small test fixes
test/integration/package.json, test/integration/test/contract-builder.types.test-d.ts
Make second cli.js contract emit invocation non-fatal in npm script and tweak a type-level assertion in a jsonb schema test.
Various emitter/generation tests
packages/2-mongo-family/.../test/*, packages/2-sql/.../test/*, packages/1-framework/.../test/*
Update expected generated d.ts strings and test fixtures to account for additional generic parameter FieldInputTypes and split *Output/*Input aliases.

Sequence Diagram

sequenceDiagram
    participant Framework as Framework (emitter)
    participant ContractDef as Generated Contract (.d.ts)
    participant QueryBuilder as Query Builder / ORM
    participant App as Application Code / Pages

    rect rgba(120, 160, 255, 0.5)
    note over Framework,App: Old Flow (single-direction types)
    Framework->>ContractDef: Emit single value-object types (e.g., Address)
    ContractDef->>QueryBuilder: Provide FieldOutputTypes
    QueryBuilder->>App: Resolve model rows from FieldOutputTypes
    App->>App: Use String()/Number() coercions as needed
    end

    rect rgba(120, 255, 160, 0.5)
    note over Framework,App: New Flow (input/output split)
    Framework->>ContractDef: Emit AddressOutput & AddressInput
    ContractDef->>QueryBuilder: Provide FieldOutputTypes & FieldInputTypes
    QueryBuilder->>App: Resolve ResolvedOutputRow/ResolvedInputRow for runtime and insert/update shapes
    App->>App: Pass native IDs/numbers and input objects without coercion
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I nibbled types beneath the moon,
Split outputs, inputs — a tidy tune,
No more casts in morning light,
IDs and numbers now take flight,
A rabbit's hop makes types just right! 🥕

🚥 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 'feat(emitter): generate FieldInputTypes and wire ORM consumption' clearly summarizes the main change—introducing FieldInputTypes generation in the emitter and integrating it with ORM and query builder consumption.

✏️ 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-2245-mongo-type-ergonomics-fix-codec-output-types-fl-010203-tml

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 13, 2026

Open in StackBlitz

@prisma-next/mongo-runtime

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

@prisma-next/family-mongo

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

@prisma-next/sql-runtime

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

@prisma-next/family-sql

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

@prisma-next/extension-paradedb

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

@prisma-next/extension-pgvector

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

@prisma-next/postgres

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

@prisma-next/sql-orm-client

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

@prisma-next/sqlite

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

@prisma-next/target-mongo

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

@prisma-next/adapter-mongo

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

@prisma-next/driver-mongo

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

@prisma-next/contract

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

@prisma-next/utils

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

@prisma-next/config

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

@prisma-next/errors

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

@prisma-next/framework-components

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

@prisma-next/operations

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

@prisma-next/contract-authoring

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

@prisma-next/ids

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

@prisma-next/psl-parser

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

@prisma-next/psl-printer

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

@prisma-next/cli

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

@prisma-next/emitter

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

@prisma-next/migration-tools

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

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

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

@prisma-next/runtime-executor

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

@prisma-next/mongo-codec

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

@prisma-next/mongo-contract

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

@prisma-next/mongo-value

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

@prisma-next/mongo-contract-psl

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

@prisma-next/mongo-contract-ts

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

@prisma-next/mongo-emitter

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

@prisma-next/mongo-schema-ir

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

@prisma-next/mongo-query-ast

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

@prisma-next/mongo-orm

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

@prisma-next/mongo-pipeline-builder

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

@prisma-next/mongo-lowering

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

@prisma-next/mongo-wire

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

@prisma-next/sql-contract

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

@prisma-next/sql-errors

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

@prisma-next/sql-operations

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

@prisma-next/sql-schema-ir

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

@prisma-next/sql-contract-psl

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

@prisma-next/sql-contract-ts

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

@prisma-next/sql-contract-emitter

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

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

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

@prisma-next/sql-relational-core

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

@prisma-next/sql-builder

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

@prisma-next/target-postgres

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

@prisma-next/target-sqlite

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

@prisma-next/adapter-postgres

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

@prisma-next/adapter-sqlite

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

@prisma-next/driver-postgres

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

@prisma-next/driver-sqlite

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

commit: e8563a9

@wmadden wmadden force-pushed the tml-2233-m4-online-cli-commands-live-introspection branch 5 times, most recently from b6e29a8 to 30a5699 Compare April 14, 2026 03:09
Base automatically changed from tml-2233-m4-online-cli-commands-live-introspection to main April 14, 2026 03:27
wmadden added 15 commits April 14, 2026 06:28
MongoDB 7.0.12+ bundles mozjs with AVX2 instructions that crash on
CI runners without AVX2 support. Pin to 7.0.11 (last pre-AVX2 release)
across test, e2e, integration, and coverage jobs.
Design spec and execution plan for wiring FieldOutputTypes and
FieldInputTypes from emitted contracts into the Mongo ORM row
type resolution, eliminating ~60 type casts in the retail store.
Parameterize generateFieldResolvedType for input/output side so the
emitter can produce both FieldOutputTypes and FieldInputTypes maps.
Value object type aliases are now emitted as {Name}Output / {Name}Input
pairs (e.g. PriceOutput, PriceInput) instead of a single un-suffixed
alias. The Mongo emitter TypeMaps expression now includes
FieldInputTypes as a 4th type parameter.
Add 4th type parameter TFieldInputTypes to MongoTypeMaps with a default
of Record<string, Record<string, unknown>> for backward compatibility.
Add ExtractMongoFieldInputTypes extractor and export it. Existing
contracts with 2- or 3-param MongoTypeMaps continue to compile.
Add ResolvedOutputRow and ResolvedInputRow helper types that prefer
pre-resolved field types from the contract TypeMaps when available,
falling back to InferModelRow for contracts without them. Wire
DefaultModelRow, InferFullRow, EmbedRelationRowType, CreateInput,
and VariantCreateInput to use the resolved helpers.
Both retail-store and mongo-demo contracts now include
FieldInputTypes maps alongside FieldOutputTypes, and value object
type aliases are emitted as {Name}Output / {Name}Input pairs.
With FieldOutputTypes and FieldInputTypes wired into the ORM, query
results and mutation inputs are now properly typed. Remove all
as-string, String(), and Number() coercions on ORM-produced values
from app pages, API routes, seed data, and test files.
The emitter now generates {Name}Output / {Name}Input instead of
un-suffixed value object aliases — update the SQL emitter tests
to match.
Replace the side flag with resolveFieldType returning { input, output }
so callers pick the side they need from the result. This eliminates the
double iteration over models/fields, makes the renderOutputType
asymmetry explicit (output uses renderOutputType, input always falls
through to CodecTypes[...]['input']), and simplifies callers.
generateBothFieldTypesMaps produces both maps in a single pass.
Add NavItemInput self-reference assertion (F04), and type-level tests
for InferFullRow with embedded relations, IncludedRow with references,
and VariantCreateInput when FieldOutputTypes/FieldInputTypes are present
in the contract (F03).
Add 5th type parameter TFieldInputTypes to SQL TypeMaps (mirroring the
Mongo side), add FieldInputTypesOf extractor and ExtractFieldInputTypes
convenience alias, and update the SQL emitter getTypeMapsExpression to
include FieldInputTypes. All parameters have defaults for backward
compatibility.
…ectural changes

The SQL builder operates at the table/column (storage) level while
FieldOutputTypes/FieldInputTypes are keyed by model/field (domain).
Wiring them requires widening TableProxyContract, exporting internal
relational-core helpers, and propagating model context through the scope
system. The SQL builder also uses simple single-level indexed accesses
(not the deep InferModelRow chain that caused Mongo opaque types), so
this is not causing issues today.
SQL builder now consumes FieldOutputTypes and FieldInputTypes from
the contract to resolve column types directly, bypassing opaque
CodecTypes conditionals. This prevents the same opaque-type problem
that motivated the Mongo ORM fix — parameterized codecs like
Vector<1536> or Char<36> now resolve to concrete types.

ResolveRow gains an optional PreResolved parameter, QueryContext
carries resolvedColumnOutputTypes, and table-proxy computes
per-table column resolution via table→model→column→field mapping.
InsertValues/UpdateValues similarly prefer FieldInputTypes when
available. Backward compatible: contracts without field type maps
fall back to codec-based resolution.
@wmadden wmadden force-pushed the tml-2245-mongo-type-ergonomics-fix-codec-output-types-fl-010203-tml branch from 4b87a7e to e2a4ab5 Compare April 14, 2026 04:41
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: 4

🧹 Nitpick comments (1)
packages/1-framework/3-tooling/emitter/test/domain-type-generation.test.ts (1)

897-997: Split this test file before adding more coverage.

This file is already far past the repo’s 500-line test limit, and these new field-type suites add another separable concern. Please move the new FieldInputTypes / FieldOutputTypes / resolveFieldType coverage into a dedicated test file so this one stays navigable.

As per coding guidelines, "Keep test files under 500 lines to maintain readability and navigability. If a test file exceeds this limit, it should be split into multiple files."

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

In `@packages/1-framework/3-tooling/emitter/test/domain-type-generation.test.ts`
around lines 897 - 997, Move the field-type related suites out of the large
domain-type-generation.test.ts into a new test file (e.g.,
domain-field-types.test.ts): cut the describe blocks for
generateFieldInputTypesMap, generateBothFieldTypesMaps and resolveFieldType
(including their it tests) and paste them into the new file, keep the same
imports/fixtures (stubCodecLookup, stubCodec, ContractModel, ContractField,
generateFieldInputTypesMap, generateBothFieldTypesMaps, resolveFieldType) so
tests compile unchanged, remove the moved suites from the original file, and
ensure the test runner picks up the new file by following the same test
naming/convention as other files in the suite.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/mongo-demo/src/contract.d.ts`:
- Line 5: The generated contract.d.ts file includes an unused import "Vector"
from '@prisma-next/adapter-mongo/codec-types'; remove that import from the
emitter output (delete the import line referencing Vector) and update the
emitter code that produces imports so it only emits imports for types actually
referenced in the contract (ensure the emission logic checks referenced symbols
before emitting entries for "Vector" or similar types).

In `@packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts`:
- Around line 351-368: resolveValueObjectType is calling resolveFieldType
without passing the new codecLookup argument, causing nested value-object
members to use generic CodecTypes[...] instead of codec-specific rendered types;
update resolveValueObjectType to accept and forward a codecLookup parameter into
resolveFieldType for each field, then propagate that parameter through
generateValueObjectType and generateValueObjectTypeAliases and update the
generate-contract-dts.ts call site to pass the codecLookup through so all nested
value-object alias generation uses the codec-aware rendering.

In `@packages/2-sql/4-lanes/sql-builder/src/types/table-proxy.ts`:
- Around line 87-101: The code is stripping null from pre-resolved field input
types by wrapping FieldInputs[...] in NonNullable; remove the NonNullable
wrapper so you use FieldInputs[ModelName][FieldName] verbatim (and still union
with null when Table['columns'][K]['nullable'] is true if desired). Update the
same pattern in the other occurrence used by ResolvedUpdateValues (the block
around lines 106-112) that references FindFieldForColumn, FieldInputs, ModelName
and CT so the codec/input types retain any intrinsic nullability instead of
being forcefully non-nullified.

In
`@packages/2-sql/4-lanes/sql-builder/test/playground/resolved-field-types.test-d.ts`:
- Around line 18-21: The test currently uses expectTypeOf<ExtractRow<typeof
result>>().toEqualTypeOf<...>() which does a structural comparison and can lose
brand information like Char<36>; replace this assertion with a strict identity
check using the Equal<> + Expect<> pattern to ensure branded types are preserved
— i.e. assert Expect<Equal<ExtractRow<typeof result>, { id: Char<36>; title:
string }>> so the brand on Char<36> is verified exactly; update the test in
resolved-field-types.test-d.ts and keep references to ExtractRow, result, Char,
Equal and Expect when making the change.

---

Nitpick comments:
In `@packages/1-framework/3-tooling/emitter/test/domain-type-generation.test.ts`:
- Around line 897-997: Move the field-type related suites out of the large
domain-type-generation.test.ts into a new test file (e.g.,
domain-field-types.test.ts): cut the describe blocks for
generateFieldInputTypesMap, generateBothFieldTypesMaps and resolveFieldType
(including their it tests) and paste them into the new file, keep the same
imports/fixtures (stubCodecLookup, stubCodec, ContractModel, ContractField,
generateFieldInputTypesMap, generateBothFieldTypesMaps, resolveFieldType) so
tests compile unchanged, remove the moved suites from the original file, and
ensure the test runner picks up the new file by following the same test
naming/convention as other files in the suite.
🪄 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: 06903213-770b-4ac2-9068-5d174d515e55

📥 Commits

Reviewing files that changed from the base of the PR and between ff06703 and e2a4ab5.

⛔ Files ignored due to path filters (4)
  • packages/2-sql/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts is excluded by !**/generated/**
  • projects/orm-consolidation/plans/codec-output-types-plan.md is excluded by !projects/**
  • projects/orm-consolidation/specs/codec-output-types.spec.md is excluded by !projects/**
  • projects/orm-consolidation/specs/reviews/code-review.md is excluded by !projects/**
📒 Files selected for processing (45)
  • .github/workflows/ci.yml
  • examples/mongo-demo/src/contract.d.ts
  • examples/mongo-demo/src/contract.json
  • examples/retail-store/app/api/auth/signup/route.ts
  • examples/retail-store/app/api/orders/[id]/route.ts
  • examples/retail-store/app/cart/page.tsx
  • examples/retail-store/app/checkout/page.tsx
  • examples/retail-store/app/orders/[id]/page.tsx
  • examples/retail-store/app/orders/page.tsx
  • examples/retail-store/app/page.tsx
  • examples/retail-store/app/products/[id]/page.tsx
  • examples/retail-store/src/contract.d.ts
  • examples/retail-store/src/seed.ts
  • examples/retail-store/test/api-flows.test.ts
  • examples/retail-store/test/cart-lifecycle.test.ts
  • examples/retail-store/test/crud-lifecycle.test.ts
  • examples/retail-store/test/order-lifecycle.test.ts
  • examples/retail-store/test/relations.test.ts
  • examples/retail-store/test/update-operators.test.ts
  • packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts
  • packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts
  • packages/1-framework/3-tooling/emitter/test/domain-type-generation.test.ts
  • packages/2-mongo-family/1-foundation/mongo-contract/src/contract-types.ts
  • packages/2-mongo-family/1-foundation/mongo-contract/src/exports/index.ts
  • packages/2-mongo-family/1-foundation/mongo-contract/test/contract-types.test-d.ts
  • packages/2-mongo-family/3-tooling/emitter/src/index.ts
  • packages/2-mongo-family/3-tooling/emitter/test/emitter-hook.e2e.test.ts
  • packages/2-mongo-family/3-tooling/emitter/test/emitter-hook.generation.test.ts
  • packages/2-mongo-family/5-query-builders/orm/src/types.ts
  • packages/2-mongo-family/5-query-builders/orm/test/value-object-inputs.test-d.ts
  • packages/2-sql/1-core/contract/src/exports/types.ts
  • packages/2-sql/1-core/contract/src/types.ts
  • packages/2-sql/1-core/contract/test/contract-typemaps-shape.test.ts
  • packages/2-sql/3-tooling/emitter/src/index.ts
  • packages/2-sql/3-tooling/emitter/test/emitter-hook.generation.basic.test.ts
  • packages/2-sql/4-lanes/sql-builder/src/resolve.ts
  • packages/2-sql/4-lanes/sql-builder/src/runtime/mutation-impl.ts
  • packages/2-sql/4-lanes/sql-builder/src/runtime/query-impl.ts
  • packages/2-sql/4-lanes/sql-builder/src/scope.ts
  • packages/2-sql/4-lanes/sql-builder/src/types/mutation-query.ts
  • packages/2-sql/4-lanes/sql-builder/src/types/shared.ts
  • packages/2-sql/4-lanes/sql-builder/src/types/table-proxy.ts
  • packages/2-sql/4-lanes/sql-builder/test/integration/mutation-defaults.test.ts
  • packages/2-sql/4-lanes/sql-builder/test/playground/resolved-field-types.test-d.ts
  • packages/2-sql/4-lanes/sql-builder/test/runtime/functions.test.ts

// This file is automatically generated by 'prisma-next contract emit'.
// To regenerate, run: prisma-next contract emit
import type { CodecTypes as MongoCodecTypes } from '@prisma-next/adapter-mongo/codec-types';
import type { Vector } from '@prisma-next/adapter-mongo/codec-types';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if Vector type is used in the mongo-demo contract
rg -n 'Vector' examples/mongo-demo/src/contract.d.ts

Repository: prisma/prisma-next

Length of output: 133


🏁 Script executed:

wc -l examples/mongo-demo/src/contract.d.ts

Repository: prisma/prisma-next

Length of output: 103


🏁 Script executed:

cat examples/mongo-demo/src/contract.d.ts

Repository: prisma/prisma-next

Length of output: 11650


🏁 Script executed:

rg 'Vector' examples/mongo-demo/ -i

Repository: prisma/prisma-next

Length of output: 169


Remove unused Vector import from emitter output.

The Vector type imported from @prisma-next/adapter-mongo/codec-types on line 5 is not used anywhere in the contract definition. Since this is a generated file, the emitter should be fixed to avoid generating stale imports.

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

In `@examples/mongo-demo/src/contract.d.ts` at line 5, The generated contract.d.ts
file includes an unused import "Vector" from
'@prisma-next/adapter-mongo/codec-types'; remove that import from the emitter
output (delete the import line referencing Vector) and update the emitter code
that produces imports so it only emits imports for types actually referenced in
the contract (ensure the emission logic checks referenced symbols before
emitting entries for "Vector" or similar types).

Comment on lines +351 to +368
export function resolveValueObjectType(
_voName: string,
vo: ContractValueObject,
_valueObjects: Record<string, ContractValueObject>,
): ResolvedFieldType {
const outputEntries: string[] = [];
const inputEntries: string[] = [];
for (const [fieldName, field] of Object.entries(vo.fields)) {
const tsType = generateFieldResolvedType(field);
fieldEntries.push(`readonly ${serializeObjectKey(fieldName)}: ${tsType}`);
const resolved = resolveFieldType(field);
const key = `readonly ${serializeObjectKey(fieldName)}`;
outputEntries.push(`${key}: ${resolved.output}`);
inputEntries.push(`${key}: ${resolved.input}`);
}
return fieldEntries.length > 0 ? `{ ${fieldEntries.join('; ')} }` : 'Record<string, never>';
const empty = 'Record<string, never>';
return {
output: outputEntries.length > 0 ? `{ ${outputEntries.join('; ')} }` : empty,
input: inputEntries.length > 0 ? `{ ${inputEntries.join('; ')} }` : empty,
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Thread codecLookup through value-object alias generation too.

resolveFieldType() now supports codec-specific output rendering, but resolveValueObjectType() still calls it without codecLookup. That means FooOutput aliases fall back to CodecTypes[...] for parameterized scalar members, while top-level field maps get the rendered concrete type. Nested value objects will lose the precision this PR is trying to add.

Suggested direction
 export function resolveValueObjectType(
   _voName: string,
   vo: ContractValueObject,
   _valueObjects: Record<string, ContractValueObject>,
+  codecLookup?: CodecLookup,
 ): ResolvedFieldType {
   const outputEntries: string[] = [];
   const inputEntries: string[] = [];
   for (const [fieldName, field] of Object.entries(vo.fields)) {
-    const resolved = resolveFieldType(field);
+    const resolved = resolveFieldType(field, codecLookup);
     const key = `readonly ${serializeObjectKey(fieldName)}`;
     outputEntries.push(`${key}: ${resolved.output}`);
     inputEntries.push(`${key}: ${resolved.input}`);
   }
   const empty = 'Record<string, never>';
   return {
     output: outputEntries.length > 0 ? `{ ${outputEntries.join('; ')} }` : empty,
     input: inputEntries.length > 0 ? `{ ${inputEntries.join('; ')} }` : empty,
   };
 }

You'll also want to pass that lookup through generateValueObjectType() / generateValueObjectTypeAliases() and their generate-contract-dts.ts call site.

Also applies to: 421-423

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

In `@packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts` around
lines 351 - 368, resolveValueObjectType is calling resolveFieldType without
passing the new codecLookup argument, causing nested value-object members to use
generic CodecTypes[...] instead of codec-specific rendered types; update
resolveValueObjectType to accept and forward a codecLookup parameter into
resolveFieldType for each field, then propagate that parameter through
generateValueObjectType and generateValueObjectTypeAliases and update the
generate-contract-dts.ts call site to pass the codecLookup through so all nested
value-object alias generation uses the codec-aware rendering.

Comment on lines +87 to +101
[K in keyof Table['columns']]?: FindFieldForColumn<
C,
ModelName,
K & string
> extends infer FieldName extends string
? FieldName extends keyof FieldInputs[ModelName]
? Table['columns'][K]['nullable'] extends true
? NonNullable<FieldInputs[ModelName][FieldName]> | null
: NonNullable<FieldInputs[ModelName][FieldName]>
: Table['columns'][K]['codecId'] extends keyof CT
? CT[Table['columns'][K]['codecId']]['input']
: unknown
: Table['columns'][K]['codecId'] extends keyof CT
? CT[Table['columns'][K]['codecId']]['input']
: unknown;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't strip null out of pre-resolved field input types.

FieldInputTypes already encode the field’s real input shape. Re-wrapping them as NonNullable<...> (and then re-adding | null from the storage column) will incorrectly reject codecs whose own input type includes null on a non-nullable column. ResolvedUpdateValues inherits the same bug via Line 112.

Suggested fix
 type ResolvedInsertValues<
   C,
   Table extends StorageTable,
   TableName extends string,
   CT extends Record<string, { readonly input: unknown }>,
   FieldInputs extends Record<string, Record<string, unknown>>,
 > = string extends keyof FieldInputs
   ? InsertValues<Table, CT>
   : FindModelForTable<C, TableName> extends infer ModelName extends string
     ? ModelName extends keyof FieldInputs
       ? {
           [K in keyof Table['columns']]?: FindFieldForColumn<
             C,
             ModelName,
             K & string
           > extends infer FieldName extends string
             ? FieldName extends keyof FieldInputs[ModelName]
-              ? Table['columns'][K]['nullable'] extends true
-                ? NonNullable<FieldInputs[ModelName][FieldName]> | null
-                : NonNullable<FieldInputs[ModelName][FieldName]>
+              ? FieldInputs[ModelName][FieldName]
               : Table['columns'][K]['codecId'] extends keyof CT
                 ? CT[Table['columns'][K]['codecId']]['input']
                 : unknown
             : Table['columns'][K]['codecId'] extends keyof CT
               ? CT[Table['columns'][K]['codecId']]['input']
               : unknown;
         }
       : InsertValues<Table, CT>
     : InsertValues<Table, CT>;

Also applies to: 106-112

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

In `@packages/2-sql/4-lanes/sql-builder/src/types/table-proxy.ts` around lines 87
- 101, The code is stripping null from pre-resolved field input types by
wrapping FieldInputs[...] in NonNullable; remove the NonNullable wrapper so you
use FieldInputs[ModelName][FieldName] verbatim (and still union with null when
Table['columns'][K]['nullable'] is true if desired). Update the same pattern in
the other occurrence used by ResolvedUpdateValues (the block around lines
106-112) that references FindFieldForColumn, FieldInputs, ModelName and CT so
the codec/input types retain any intrinsic nullability instead of being
forcefully non-nullified.

Comment on lines +18 to +21
expectTypeOf<ExtractRow<typeof result>>().toEqualTypeOf<{
id: Char<36>;
title: string;
}>();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify current branded-type assertion style in this test file.
rg -n "expectTypeOf|type Equal|type Expect" packages/2-sql/4-lanes/sql-builder/test/playground/resolved-field-types.test-d.ts -C2

Repository: prisma/prisma-next

Length of output: 583


Use strict identity checks for branded type assertions in .test-d.ts.

For branded types like Char<36>, expectTypeOf().toEqualTypeOf() performs structural comparison which can erase brand information and produce false positives. Use the Equal<A, B> identity pattern with Expect<T extends true> instead, which leverages TypeScript's distributive conditional types to check strict type identity including brands.

♻️ Suggested refactor
+type Equal<A, B> =
+  (<T>() => T extends A ? 1 : 2) extends
+  (<T>() => T extends B ? 1 : 2) ? true : false;
+type Expect<T extends true> = T;
+
 test('SELECT resolves parameterized Char column to concrete type via FieldOutputTypes', () => {
   const result = db.articles.select('id', 'title').build();
-  expectTypeOf<ExtractRow<typeof result>>().toEqualTypeOf<{
-    id: Char<36>;
-    title: string;
-  }>();
+  type _row = ExtractRow<typeof result>;
+  type _expected = { id: Char<36>; title: string };
+  type _assert = Expect<Equal<_row, _expected>>;
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/2-sql/4-lanes/sql-builder/test/playground/resolved-field-types.test-d.ts`
around lines 18 - 21, The test currently uses expectTypeOf<ExtractRow<typeof
result>>().toEqualTypeOf<...>() which does a structural comparison and can lose
brand information like Char<36>; replace this assertion with a strict identity
check using the Equal<> + Expect<> pattern to ensure branded types are preserved
— i.e. assert Expect<Equal<ExtractRow<typeof result>, { id: Char<36>; title:
string }>> so the brand on Char<36> is verified exactly; update the test in
resolved-field-types.test-d.ts and keep references to ExtractRow, result, Char,
Equal and Expect when making the change.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/integration/test/contract-builder.types.test-d.ts`:
- Around line 413-414: Replace the value-argument form of the Vitest type
assertion with the explicit generic never form: change the uses of
expectTypeOf(Row['payload']).toEqualTypeOf(undefined as never) and
expectTypeOf(Row['meta']).toEqualTypeOf(undefined as never) to use the generic
API expectTypeOf<Row['payload']>().toEqualTypeOf<never>() and
expectTypeOf<Row['meta']>().toEqualTypeOf<never>(); this makes the intent
explicit and follows Vitest guidance while keeping the same exact-type assertion
semantics.
🪄 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: df00974c-2ee8-4007-a929-cc1c7d8555b9

📥 Commits

Reviewing files that changed from the base of the PR and between 711ae68 and 17c7a2d.

📒 Files selected for processing (1)
  • test/integration/test/contract-builder.types.test-d.ts

Comment on lines +413 to +414
expectTypeOf<Row['payload']>().toEqualTypeOf(undefined as never);
expectTypeOf<Row['meta']>().toEqualTypeOf(undefined as never);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

In Vitest expectTypeOf, for exact type assertions, is expectTypeOf().toEqualTypeOf()preferred overexpectTypeOf().toEqualTypeOf(undefined as never)? Does the value-argument form introduce weaker or inference-sensitive checks?

💡 Result:

Yes, expectTypeOf.toEqualTypeOf is preferred over expectTypeOf.toEqualTypeOf(undefined as never) for exact type assertions in Vitest's expectTypeOf. The official Vitest documentation provides examples using the generic type parameter form for toEqualTypeOf, such as expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: number }>, explicitly recommending this syntax for precise type equality checks. The expect-type library (underlying Vitest's implementation) documentation emphasizes using generic type arguments over concrete values for better error messages and reliability. Concrete values require TypeScript inference of the type argument T in toEqualTypeOf(value), which produces less informative errors on failure due to reliance on a generic Mismatch type. For never specifically, expectTypeOf.toBeNever is shown in docs, but for general exact matches via toEqualTypeOf, the generic directly specifies the bottom type without inference. The value form undefined as never forces never via type assertion but still relies on inference for the matcher parameter, potentially introducing sensitivity to context or less strict structural checks compared to the direct generic, which uses TypeScript's internal identicalness operator (StrictEqualUsingTSInternalIdenticalToOperator) for exact structural equality. No sources indicate the value form introduces "weaker" checks explicitly, but inference can lead to unexpected passes or poorer diagnostics, making the generic form more robust and preferred for exact assertions.

Citations:


🏁 Script executed:

sed -n '410,420p' test/integration/test/contract-builder.types.test-d.ts

Repository: prisma/prisma-next

Length of output: 591


Use explicit generic never form for exact type assertions.

The value-argument form toEqualTypeOf(undefined as never) is less explicit and relies on inference. Use the generic form toEqualTypeOf<never>() for clearer, more robust type-equality checks as recommended by Vitest's official documentation.

Suggested patch
-  expectTypeOf<Row['payload']>().toEqualTypeOf(undefined as never);
-  expectTypeOf<Row['meta']>().toEqualTypeOf(undefined as never);
+  expectTypeOf<Row['payload']>().toEqualTypeOf<never>();
+  expectTypeOf<Row['meta']>().toEqualTypeOf<never>();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expectTypeOf<Row['payload']>().toEqualTypeOf(undefined as never);
expectTypeOf<Row['meta']>().toEqualTypeOf(undefined as never);
expectTypeOf<Row['payload']>().toEqualTypeOf<never>();
expectTypeOf<Row['meta']>().toEqualTypeOf<never>();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/integration/test/contract-builder.types.test-d.ts` around lines 413 -
414, Replace the value-argument form of the Vitest type assertion with the
explicit generic never form: change the uses of
expectTypeOf(Row['payload']).toEqualTypeOf(undefined as never) and
expectTypeOf(Row['meta']).toEqualTypeOf(undefined as never) to use the generic
API expectTypeOf<Row['payload']>().toEqualTypeOf<never>() and
expectTypeOf<Row['meta']>().toEqualTypeOf<never>(); this makes the intent
explicit and follows Vitest guidance while keeping the same exact-type assertion
semantics.

@wmadden wmadden force-pushed the tml-2245-mongo-type-ergonomics-fix-codec-output-types-fl-010203-tml branch from 17c7a2d to 11af80f Compare April 14, 2026 05:04
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/prisma-next-demo/src/orm-client/create-user-with-address.ts`:
- Line 3: Remove the ".d" extension from the TypeScript import in
create-user-with-address.ts: update the import that currently references
'../prisma/contract.d' so it imports AddressInput and Contract from
'../prisma/contract' (extensionless) to follow the repo guideline "Never add
file extensions to imports in TypeScript".
🪄 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: c9dc41a2-ff2e-41e2-9e9d-2d669ae536bf

📥 Commits

Reviewing files that changed from the base of the PR and between 17c7a2d and f0a760c.

⛔ Files ignored due to path filters (1)
  • test/e2e/framework/test/fixtures/generated/contract.d.ts is excluded by !**/generated/**
📒 Files selected for processing (3)
  • examples/prisma-next-demo/src/orm-client/create-user-with-address.ts
  • test/integration/package.json
  • test/integration/test/contract-builder.types.test-d.ts
✅ Files skipped from review due to trivial changes (1)
  • test/integration/test/contract-builder.types.test-d.ts

import type { DefaultModelRow } from '@prisma-next/sql-orm-client';
import type { Runtime } from '@prisma-next/sql-runtime';
import type { Address, Contract } from '../prisma/contract.d';
import type { AddressInput, Contract } from '../prisma/contract.d';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read-only check: find TS/TSX imports that include explicit file extensions.
rg -nP --type=ts --type=tsx "from ['\"][^'\"]+\\.(d|ts|tsx|js|jsx)['\"]"

Repository: prisma/prisma-next

Length of output: 89


🏁 Script executed:

#!/bin/bash
# Read the target file to confirm the import
cat -n examples/prisma-next-demo/src/orm-client/create-user-with-address.ts | head -10

Repository: prisma/prisma-next

Length of output: 524


🏁 Script executed:

#!/bin/bash
# Check if contract.d.ts exists in the expected location
find examples/prisma-next-demo -name "contract.d.ts" -o -name "contract.ts"

Repository: prisma/prisma-next

Length of output: 157


🏁 Script executed:

#!/bin/bash
# Corrected check: find TS imports with explicit file extensions (no tsx type)
rg -nP --type=ts "from ['\"][^'\"]+\\.(d|ts|js|jsx)['\"]" examples/

Repository: prisma/prisma-next

Length of output: 1991


Drop the .d extension from the TypeScript import path.

Line 3 should use an extensionless module specifier per the repo guideline "Never add file extensions to imports in TypeScript".

Suggested patch
-import type { AddressInput, Contract } from '../prisma/contract.d';
+import type { AddressInput, Contract } from '../prisma/contract';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import type { AddressInput, Contract } from '../prisma/contract.d';
import type { AddressInput, Contract } from '../prisma/contract';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/prisma-next-demo/src/orm-client/create-user-with-address.ts` at line
3, Remove the ".d" extension from the TypeScript import in
create-user-with-address.ts: update the import that currently references
'../prisma/contract.d' so it imports AddressInput and Contract from
'../prisma/contract' (extensionless) to follow the repo guideline "Never add
file extensions to imports in TypeScript".

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