feat: add explicit JSON projection AST foundations (TML-3062) - #1023
Conversation
📝 WalkthroughWalkthroughThe PR adds typed JSON value projection nodes, new function-call/cast/CASE AST expressions, function-source ordinality and column aliases, ORM binding and query-plan updates, and PostgreSQL/SQLite rendering and validation for these constructs. ChangesAST and JSON projection contracts
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant QueryPlanSelect
participant RelationalAST
participant SQLRenderer
QueryPlanSelect->>RelationalAST: build JSON object and array projections
RelationalAST->>SQLRenderer: render JSON value projections and scalar expressions
SQLRenderer-->>QueryPlanSelect: emit PostgreSQL or SQLite SQL
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/2-sql/4-lanes/relational-core/src/ast/types.ts (1)
110-117: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winDeep-freeze
typeParamshere too.structuredClone(codec.typeParams)still leaves nested JSON mutable, so mirrorjson-value-projection.tsand recurse before freezing the wrapper.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/2-sql/4-lanes/relational-core/src/ast/types.ts` around lines 110 - 117, Update frozenCodecRef to deep-freeze the cloned codec.typeParams recursively before constructing the frozen wrapper, following the established approach in json-value-projection.ts. Preserve the existing handling of undefined typeParams and the many flag while ensuring nested JSON values cannot be mutated.
🧹 Nitpick comments (2)
packages/3-extensions/sql-orm-client/test/where-binding.test.ts (1)
405-409: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSplit the combined
toMatchObjectassertion into separateexpect()calls.Bundling
kind,value, andcodecchecks into onetoMatchObjectobscures which field fails on a regression.Based on a retrieved learning: "prefer separate expect() assertions for each field instead of combining checks with toMatchObject() when validating multiple fields... clearer, more actionable failure messages."
♻️ Proposed refactor
- expect(innerWhere.right).toMatchObject({ - kind: 'param-ref', - value: 100, - codec: { codecId: 'pg/int4@1' }, - }); + expect(innerWhere.right.kind).toBe('param-ref'); + expect((innerWhere.right as ParamRef).value).toBe(100); + expect((innerWhere.right as ParamRef).codec).toEqual({ codecId: 'pg/int4@1' });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/3-extensions/sql-orm-client/test/where-binding.test.ts` around lines 405 - 409, Split the toMatchObject assertion for innerWhere.right into separate expect() calls, validating kind, value, and codec independently. Preserve the existing expected values and codecId while making each field failure independently identifiable.Source: Learnings
packages/3-targets/6-adapters/postgres/src/core/sql-renderer.ts (1)
129-140: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate "unreachable kind" introspection logic across the Postgres and SQLite adapters. Both files independently implement the same "read
kindoff an unknown/unreachable AST node" helper for building unsupported-node error messages; the shared root cause is the lack of a common utility in the relational-core AST package.
packages/3-targets/6-adapters/postgres/src/core/sql-renderer.ts#L129-L140: replace the inlineunreachableKindbody with an import of a shareddescribeUnreachableKind/unreachableKindhelper exported from@prisma-next/sql-relational-core/ast(or a small shared adapter-utils module).packages/3-targets/6-adapters/sqlite/src/core/adapter.ts#L48-L62: drop the localnodeKind/unreachableKindpair and import the same shared helper, keeping both adapters' unsupported-kind error formatting in sync as more SQL targets are added.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/3-targets/6-adapters/postgres/src/core/sql-renderer.ts` around lines 129 - 140, Extract the shared unreachable-AST-node kind inspection into a relational-core AST utility and import it in both adapters. In packages/3-targets/6-adapters/postgres/src/core/sql-renderer.ts:129-140, replace unreachableKind’s inline logic with the shared helper; in packages/3-targets/6-adapters/sqlite/src/core/adapter.ts:48-62, remove the local nodeKind/unreachableKind pair and use the same helper while preserving unsupported-kind error formatting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/2-sql/4-lanes/relational-core/src/ast/json-value-projection.ts`:
- Around line 33-44: Extract the deep-freezing, explicit-many-preserving
frozenCodecRef implementation from
packages/2-sql/4-lanes/relational-core/src/ast/json-value-projection.ts lines
33-44 into a shared helper alongside CodecRef in
packages/2-sql/4-lanes/relational-core/src/ast/codec-types.ts, then import and
use it from both locations. Replace the duplicate implementation in
packages/2-sql/4-lanes/relational-core/src/ast/types.ts lines 110-117 with the
shared helper, preserving deep-frozen typeParams and many: false.
---
Outside diff comments:
In `@packages/2-sql/4-lanes/relational-core/src/ast/types.ts`:
- Around line 110-117: Update frozenCodecRef to deep-freeze the cloned
codec.typeParams recursively before constructing the frozen wrapper, following
the established approach in json-value-projection.ts. Preserve the existing
handling of undefined typeParams and the many flag while ensuring nested JSON
values cannot be mutated.
---
Nitpick comments:
In `@packages/3-extensions/sql-orm-client/test/where-binding.test.ts`:
- Around line 405-409: Split the toMatchObject assertion for innerWhere.right
into separate expect() calls, validating kind, value, and codec independently.
Preserve the existing expected values and codecId while making each field
failure independently identifiable.
In `@packages/3-targets/6-adapters/postgres/src/core/sql-renderer.ts`:
- Around line 129-140: Extract the shared unreachable-AST-node kind inspection
into a relational-core AST utility and import it in both adapters. In
packages/3-targets/6-adapters/postgres/src/core/sql-renderer.ts:129-140, replace
unreachableKind’s inline logic with the shared helper; in
packages/3-targets/6-adapters/sqlite/src/core/adapter.ts:48-62, remove the local
nodeKind/unreachableKind pair and use the same helper while preserving
unsupported-kind error formatting.
🪄 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: 456633a7-efe6-4d2f-8aa5-0e2ccd526d97
⛔ Files ignored due to path filters (10)
projects/codec-json-projections/slices/01-sql-json-projection-ast-foundations/dispatches/01-projection-algebra.mdis excluded by!projects/**projects/codec-json-projections/slices/01-sql-json-projection-ast-foundations/dispatches/02-explicit-json-container-adoption.mdis excluded by!projects/**projects/codec-json-projections/slices/01-sql-json-projection-ast-foundations/dispatches/03-scalar-projection-expressions.mdis excluded by!projects/**projects/codec-json-projections/slices/01-sql-json-projection-ast-foundations/dispatches/04-function-source-ordinality-round-2.mdis excluded by!projects/**projects/codec-json-projections/slices/01-sql-json-projection-ast-foundations/dispatches/04-function-source-ordinality.mdis excluded by!projects/**projects/codec-json-projections/slices/01-sql-json-projection-ast-foundations/dispatches/05-projected-codec-preservation-and-slice-gate.mdis excluded by!projects/**projects/codec-json-projections/slices/01-sql-json-projection-ast-foundations/dispatches/06-post-rebase-lint-compatibility.mdis excluded by!projects/**projects/codec-json-projections/slices/01-sql-json-projection-ast-foundations/plan.mdis excluded by!projects/**projects/codec-json-projections/slices/01-sql-json-projection-ast-foundations/spec.mdis excluded by!projects/**projects/codec-json-projections/trace.jsonlis excluded by!projects/**
📒 Files selected for processing (30)
packages/2-sql/4-lanes/relational-core/src/ast/json-value-projection.tspackages/2-sql/4-lanes/relational-core/src/ast/types.tspackages/2-sql/4-lanes/relational-core/src/exports/ast.tspackages/2-sql/4-lanes/relational-core/test/ast/common.test.tspackages/2-sql/4-lanes/relational-core/test/ast/json-container-projection.test-d.tspackages/2-sql/4-lanes/relational-core/test/ast/json-container-projection.test.tspackages/2-sql/4-lanes/relational-core/test/ast/json-value-projection.test.tspackages/2-sql/4-lanes/relational-core/test/ast/kind-discriminants.test.tspackages/2-sql/4-lanes/relational-core/test/ast/raw-expr.test.tspackages/2-sql/4-lanes/relational-core/test/ast/rich-ast.test.tspackages/2-sql/4-lanes/relational-core/test/ast/scalar-projection-expressions.test.tspackages/2-sql/4-lanes/relational-core/test/ast/select.test.tspackages/2-sql/4-lanes/relational-core/test/ast/visitors.test.tspackages/2-sql/4-lanes/relational-core/test/contract-free/expr-select.test.tspackages/3-extensions/pgvector/test/rich-adapter.test.tspackages/3-extensions/sql-orm-client/src/query-plan-aggregate.tspackages/3-extensions/sql-orm-client/src/query-plan-select.tspackages/3-extensions/sql-orm-client/src/where-binding.tspackages/3-extensions/sql-orm-client/test/grouped-collection.test.tspackages/3-extensions/sql-orm-client/test/query-plan-aggregate.test.tspackages/3-extensions/sql-orm-client/test/query-plan-select.test.tspackages/3-extensions/sql-orm-client/test/variant-include.query-plan-fixtures.tspackages/3-extensions/sql-orm-client/test/variant-include.query-plan-nested.test.tspackages/3-extensions/sql-orm-client/test/variant-include.query-plan.test.tspackages/3-extensions/sql-orm-client/test/where-binding.test.tspackages/3-targets/6-adapters/postgres/src/core/sql-renderer.tspackages/3-targets/6-adapters/postgres/test/adapter.test.tspackages/3-targets/6-adapters/sqlite/src/core/adapter.tspackages/3-targets/6-adapters/sqlite/test/adapter.test.tstest/integration/test/sql-orm-client/include.test.ts
@prisma-next/extension-author-tools
@prisma-next/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/extension-arktype-json
@prisma-next/middleware-cache
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@prisma-next/extension-postgis
@prisma-next/postgres
@prisma-next/sql-orm-client
@prisma-next/sqlite
@prisma-next/extension-supabase
@prisma-next/target-mongo
@prisma-next/adapter-mongo
@prisma-next/driver-mongo
@prisma-next/contract
@prisma-next/utils
@prisma-next/config
@prisma-next/errors
@prisma-next/framework-components
@prisma-next/operations
@prisma-next/ts-render
@prisma-next/contract-authoring
@prisma-next/ids
@prisma-next/psl-parser
@prisma-next/psl-printer
@prisma-next/cli
@prisma-next/cli-telemetry
@prisma-next/config-loader
@prisma-next/emitter
@prisma-next/language-server
@prisma-next/migration-tools
prisma-next
@prisma-next/vite-plugin-contract-emit
@prisma-next/mongo-codec
@prisma-next/mongo-contract
@prisma-next/mongo-value
@prisma-next/mongo-contract-psl
@prisma-next/mongo-contract-ts
@prisma-next/mongo-emitter
@prisma-next/mongo-schema-ir
@prisma-next/mongo-query-ast
@prisma-next/mongo-orm
@prisma-next/mongo-query-builder
@prisma-next/mongo-lowering
@prisma-next/mongo-wire
@prisma-next/sql-contract
@prisma-next/sql-errors
@prisma-next/sql-operations
@prisma-next/sql-schema-ir
@prisma-next/sql-contract-psl
@prisma-next/sql-contract-ts
@prisma-next/sql-contract-emitter
@prisma-next/sql-lane-query-builder
@prisma-next/sql-relational-core
@prisma-next/sql-builder
@prisma-next/target-postgres
@prisma-next/target-sqlite
@prisma-next/adapter-postgres
@prisma-next/adapter-sqlite
@prisma-next/driver-postgres
@prisma-next/driver-sqlite
commit: |
size-limit report 📦
|
|
Triage note — this compound review body was decomposed into A10a–A10d so each finding and the reproducible lint blocker can be handled independently. I’ll post separate outcomes for each sub-action; this parent item requires no code change. |
|
On it 👍 — this deep-freeze finding overlaps A01; I’m verifying and recording the shared codec-ref fix as the outcome for A10a. |
|
Done — A10a is satisfied by the shared codec-ref helper: nested typeParams are defensively cloned and deeply frozen on both construction paths. Focused immutability tests, relational-core typecheck, and lint pass. Commit: 268a09f. |
|
Won’t address A10b — the existing toMatchObject assertion intentionally verifies one cohesive AST node shape, so splitting each field into separate expect calls would make the test noisier without improving coverage. A09 removed the redundant manual narrowing around it while preserving this assertion. |
|
Out of scope for A10c — extracting a shared cross-adapter unreachable-kind helper would expand this feature PR into relational-core API and adapter ownership work. The existing local helpers are small and do not block the JSON projection change, so no shared utility is being introduced here. |
|
On it 👍 — I’m reproducing the post-rebase lint:throws delta, then I’ll replace each PR-introduced bare throw with structuredError for user-facing failures or InternalError/assertNever for invariants and run the affected checks. |
|
Done — 1b066d0 replaces exactly the five PR-introduced user-facing bare throws with structured errors and adds shape assertions for empty FunctionSource column aliases, empty CaseExpr branches, grouped HAVING function-call/cast/case rejection, SQLite WITH ORDINALITY, and SQLite returned-column aliases. Focused tests pass; relational-core and SQL ORM typechecks pass; affected package lints pass; SQLite production source typecheck passes before unrelated existing migration-test type errors; pnpm lint:throws is current=698, merge-base=698, delta=0. |
19730c4 to
1b066d0
Compare
|
On it — I’ll inspect the full extension-substrate migration, record the concrete 0.16-to-0.17 extension-author upgrade actions, and validate them by execution in an isolated worktree. 👍 |
|
Done — added the |
Pin the explicit projection algebra, behavior-preserving AST vocabulary, five-dispatch implementation sequence, validation gates, and persistent review contract for the first project slice. Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
82ab686 to
4557df2
Compare
## Linked issue Refs [TML-3061](https://linear.app/prisma-company/issue/TML-3061/target-codec-descriptor-foundations) Base: `main`. Predecessor [#1023](prisma/prisma-next#1023) is merged, so this PR has no stacked-base prerequisite. ## At a glance ```ts nativeTypeFor(ref: CodecRef): string { return this.nativeType(this.validateParams(ref)); } projectJson(expression: ProjectionExpr, ref: CodecRef): ProjectionExpr { const params = this.validateParams(ref); return ref.many === true ? this.jsonArrayProjection(expression, params) : this.jsonProjection(expression, params); } ``` A PostgreSQL descriptor now accepts the generic `CodecRef` that survives framework composition, validates its erased parameters, and enters target-owned typed behavior only after validation. ## Decision This PR ships the public PostgreSQL and SQLite codec descriptor protocols that the lossless JSON projection work will execute through: explicit generic-to-target adapters, narrow authoring helpers, structurally validated immutable registries, and coherent runtime/control adapter composition. It migrates every affected built-in and the pgvector, PostGIS, and arktype-json extensions to those protocols, moves PostgreSQL native-type lookup behind the validated descriptor boundary, and adds the `PublicCodecTypes` declaration-portability boundary plus extension-author migration guidance. ## Reviewer notes - The JSON projection hooks are public protocol capabilities, but production PostgreSQL and SQLite renderers deliberately remain pass-through. TML-3063 will activate descriptor dispatch and own the observable lossless JSON hard cut. - Codec JSON methods, emitted contracts, generated declarations, fixtures, codec IDs, and current SQL/JSON representations are unchanged. Drift in any of those surfaces was treated as a stop condition. - `PublicCodecTypes` intentionally exposes only each codec's `input`, `output`, and `traits` through composed contract declarations. This keeps downstream semantic typing exact without making exported declarations name concrete target descriptor implementation members. - PostgreSQL extension authors must adopt the target protocol and add the target package as a runtime dependency; direct adapter injection also moves to target-typed `codecDescriptors`. - Deterministic target, adapter, extension, declaration, workspace, documentation, skill, manifest, dependency, cast, throw, upgrade, fixture, and audit gates passed. The broad package, integration, and e2e suites were also run, but Postgres/resource-related flakes remained after varying and focused reruns, so their final broad confirmation is CI-deferred and is not claimed green here. ## How it fits together 1. [`PostgresCodecDescriptor`](packages/3-targets/3-targets/postgres/src/core/codec-descriptor.ts) and [`SqliteCodecDescriptor`](packages/3-targets/3-targets/sqlite/src/core/codec-descriptor.ts) add stable structural discriminants and public template methods. Each template method validates erased `CodecRef.typeParams` through the descriptor's Standard Schema before calling a strongly typed target hook. 2. `postgresCodec(...)`, `sqliteCodec(...)`, `definePostgresCodecs(...)`, and `defineSqliteCodecs(...)` make target ownership explicit while preserving generic SQL descriptor IDs, traits, parameter schemas, factories, renderers, metadata, and literal types. PostgreSQL supplies native-type plus scalar/array projection capabilities; SQLite supplies scalar projection and reports unsupported stored scalar-array refs. 3. Target, adapter, and ordered extension contributions are collected once per construction plane. The [PostgreSQL](packages/3-targets/6-adapters/postgres/src/core/codec-lookup.ts) and [SQLite](packages/3-targets/6-adapters/sqlite/src/core/codec-lookup.ts) builders structurally validate that set, reject malformed, wrong-target, raw, or duplicate descriptors, and derive ordinary materialization plus target lookup from the same immutable registry. 4. PostgreSQL parameter rendering now resolves trusted native type names through `nativeTypeFor(ref)` on the validated descriptor. Runtime, control, custom, enum, extension, and array cast spellings remain pinned to their existing SQL. 5. [`PublicCodecTypes`](packages/2-sql/2-authoring/contract-ts/src/contract-types.ts) projects extension codec maps to their public semantic fields before they enter contract types. Consumer declarations retain exact application types and traits while staying portable across package boundaries. 6. The [codec authoring guide](docs/reference/codec-authoring-guide.md) and the upgrade entry under [`skills/extension-author/prisma-next-extension-upgrade/upgrades/0.16-to-0.17/`](skills/extension-author/prisma-next-extension-upgrade/upgrades/0.16-to-0.17/) document target-owned authoring, coherent contribution sets, parameter-ref audits, and the dormant-hook transition. ## Behavior changes & evidence - **Extension authors can define and contribute target-owned PostgreSQL and SQLite codecs through public APIs.** The protocols validate erased refs before typed behavior, preserve concrete tuple/factory typing, and use structural validation across separately loaded packages. Implementation: [PostgreSQL descriptor protocol](packages/3-targets/3-targets/postgres/src/core/codec-descriptor.ts), [SQLite descriptor protocol](packages/3-targets/3-targets/sqlite/src/core/codec-descriptor.ts). Evidence: [PostgreSQL runtime tests](packages/3-targets/3-targets/postgres/test/postgres-codec-descriptor.test.ts), [SQLite runtime tests](packages/3-targets/3-targets/sqlite/test/sqlite-codec-descriptor.test.ts). - **PostgreSQL arrays have a target-owned default projection shape, while SQLite rejects undefined stored scalar-array semantics.** PostgreSQL's default lift binds the source once and preserves null arrays, empty arrays, null elements, and ordinality; SQLite raises a structured error for `CodecRef.many`. Implementation: [PostgreSQL descriptor protocol](packages/3-targets/3-targets/postgres/src/core/codec-descriptor.ts), [SQLite descriptor protocol](packages/3-targets/3-targets/sqlite/src/core/codec-descriptor.ts). Evidence: [PostgreSQL descriptor tests](packages/3-targets/3-targets/postgres/test/postgres-codec-descriptor.test.ts), [SQLite descriptor tests](packages/3-targets/3-targets/sqlite/test/sqlite-codec-descriptor.test.ts). - **Runtime, control, and direct adapter paths now see one coherent descriptor set and fail invalid composition early.** Bare adapters remain built-ins-only; stack composition includes ordered target, adapter, and extension contributions. Implementation: [PostgreSQL registry assembly](packages/3-targets/6-adapters/postgres/src/core/codec-lookup.ts), [SQLite registry assembly](packages/3-targets/6-adapters/sqlite/src/core/codec-lookup.ts). Evidence: [PostgreSQL composition tests](packages/3-targets/6-adapters/postgres/test/postgres-codec-registry-composition.test.ts), [SQLite composition tests](packages/3-targets/6-adapters/sqlite/test/sqlite-codec-registry-composition.test.ts). - **First-party PostgreSQL extensions use one canonical target-typed contribution set.** pgvector, PostGIS, and arktype-json expose the same descriptor arrays through runtime and control metadata while retaining current factories, application types, vector text, HEXEWKB, and structured JSON representations. Implementation: [pgvector codecs](packages/3-extensions/pgvector/src/core/codecs.ts), [PostGIS codecs](packages/3-extensions/postgis/src/core/codecs.ts), [arktype-json codec](packages/3-extensions/arktype-json/src/core/arktype-json-codec.ts). Evidence: [pgvector adoption tests](packages/3-extensions/pgvector/test/postgres-codec-descriptor-adoption.test.ts), [PostGIS adoption tests](packages/3-extensions/postgis/test/postgres-codec-descriptor-adoption.test.ts). - **Consumer-exported contracts remain declaration-portable.** Extension codec maps retain exact public input/output/trait semantics without exposing target descriptor implementation structure. Implementation: [`PublicCodecTypes`](packages/2-sql/2-authoring/contract-ts/src/contract-types.ts). Evidence: [declaration fixture](packages/3-extensions/pgvector/test/contract-declaration-portability.fixture.ts), [declaration type test](packages/3-extensions/pgvector/test/contract-declaration-portability.test-d.ts). - **Current rendering remains unchanged while the new hooks stay dormant.** PostgreSQL and SQLite still render codec, native, and document projections as their wrapped expressions; composition tests assert exact existing JSON SQL and zero descriptor projection calls. Implementation: [PostgreSQL JSON visitor](packages/3-targets/6-adapters/postgres/src/core/sql-renderer.ts), [SQLite JSON visitor](packages/3-targets/6-adapters/sqlite/src/core/adapter.ts). Evidence: [PostgreSQL composition tests](packages/3-targets/6-adapters/postgres/test/postgres-codec-registry-composition.test.ts), [SQLite composition tests](packages/3-targets/6-adapters/sqlite/test/sqlite-codec-registry-composition.test.ts). ## Compatibility / migration / risk - This is a pre-1.0 public SPI change for PostgreSQL-bound extension codecs. Extension descriptors must extend `PostgresCodecDescriptor` or use `postgresCodec(...)`, define target behavior, contribute a `definePostgresCodecs(...)` set through runtime and control paths, and declare `@prisma-next/target-postgres` as a production dependency. - PostgreSQL direct adapter options now accept target-typed `codecDescriptors` in place of an independently assembled generic `codecLookup`; SQLite gains the equivalent target-typed option. - Invalid dynamic composition now fails during adapter construction. Raw generic, wrong-target, structurally malformed, and duplicate descriptors no longer survive until lookup or query lowering. - `PublicCodecTypes` narrows incidental descriptor-derived members out of exported contract types while preserving `input`, `output`, and `traits`. Consumers depending on incidental implementation members through contract types must move to the descriptor API. - There is no data, emitted-contract, codec-ID, codec JSON, or fixture migration in this PR. Production JSON projection remains pass-through until TML-3063. ## Testing performed Passed on the final branch state: - Deterministic build, runtime test, typecheck, and lint gates for PostgreSQL/SQLite targets, PostgreSQL/SQLite adapters, pgvector, PostGIS, and arktype-json. - Root `pnpm build` and `pnpm typecheck`. - Runtime/type coverage for descriptor validation, target tuple preservation, adapter composition, native-type/cast parity, dormant JSON hooks, extension adoption, and `PublicCodecTypes` declaration portability. - Documentation, skill, and package-manifest validation; `pnpm lint:deps`; `pnpm lint:casts`; `pnpm lint:throws`; `pnpm check:upgrade-coverage --mode pr`; `pnpm fixtures:check`; and the bounded closing audits. Run, with final broad confirmation deferred to CI: - `pnpm test:packages` - `pnpm test:integration` - `pnpm test:e2e` Postgres/resource-related flakes remained after reruns at varying and focused scopes. These three broad suites are recorded as executed, not as passing. ## Skill update Updated the codec authoring documentation and the extension-author upgrade instructions in [`skills/extension-author/prisma-next-extension-upgrade/upgrades/0.16-to-0.17/`](skills/extension-author/prisma-next-extension-upgrade/upgrades/0.16-to-0.17/). The upgrade entry detects generic PostgreSQL extension descriptors and walks authors through runtime dependency placement, target subclassing or adaptation, canonical runtime/control contribution sets, required `CodecRef.typeParams`, and behavior-preservation checks. ## Follow-ups - TML-3063 will activate descriptor JSON projection dispatch, land the lossless PostgreSQL/SQLite and extension representations, and remove transitional generic metadata after production consumers have moved. ## Alternatives considered - **Keep target behavior in generic `CodecMeta` or add a framework-owned target map.** Rejected because target SQL/JSON behavior is open-world and belongs to each target package; generic framework composition remains target-neutral. - **Validate descriptors with `instanceof`.** Rejected because extensions may load a separate copy of a target package. Stable structural discriminants and required methods preserve interoperability across module identities. - **Maintain separate ordinary and target registries.** Rejected because the two views could disagree about membership, factories, or behavior. Both views are now derived from one validated descriptor set. - **Make identity JSON projection an implicit default.** Rejected because omission would silently assert that the database representation is already lossless. Every descriptor states the transitional behavior explicitly. - **Activate JSON projection hooks in this PR.** Rejected so registry/authoring migration and representation changes remain independently reviewable. TML-3063 owns the observable hard cut. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated for the public protocols, composition boundary, extensions, declaration portability, and unchanged rendering behavior. - [x] The PR title is in `TML-NNNN: <sentence-case title>` form and names the concrete deliverable. - [x] The **Skill update** section is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added PostgreSQL- and SQLite-specific codec descriptor authoring APIs, including descriptor/codec registries and validation. * Adapters now use provided target-specific codec descriptor sets to keep runtime and control-plane behavior consistent. * Improved native type handling and JSON projection behavior for parameterized and scalar-array scenarios. * **Bug Fixes** * PostGIS geometry codecs now support optional SRID, with updated typing, validation, and output rendering. * **Documentation** * Expanded codec authoring guide, target READMEs, and 0.16 → 0.17 upgrade instructions for adopting the new descriptor model. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Linked issue
Refs TML-3062
Prerequisite #1013 merged the project plan; this PR targets
main, not the deleted planning branch.At a glance
Previously JSON containers accepted bare expressions and could not preserve whether a value represented codec-projected data, a native scalar, or an existing JSON document.
Decision
This PR ships the target-neutral SQL JSON projection AST foundations described in the slice spec:
ProjectionItem.codecforwarding through SQL ORM projection wrappers, including row-number dedup.The PostgreSQL and SQLite renderers consume the new vocabulary now, while executable codec/document projection semantics remain in later slices.
Reviewer notes
NativeJsonValueProjection; there is no compatibility overload.pnpm --filter @prisma-next/adapter-postgres testandpnpm test:packagesdid not pass locally: repeated attempts rotated among connection resets, timeouts, and connection-termination failures. A freshly installed and built pristineorigin/mainworktree reproduced the same failure family. The operator accepted this baseline validation deferral, and CI is authoritative for those full runs.How it fits together
Behavior changes & evidence
ProjectionItem.codecnow describes any known projected result, and row-number dedup forwarding carries the complete codec reference. Evidence: parameterized andmanycodec preservation.Compatibility / migration / risk
FunctionSource.of(fn, args, alias?)callers must replace a string alias with the grouped{ alias, columnAliases? }option; calls that omit the alias remain unchanged. PostgreSQL supports the new ordinality and returned-column-alias options; SQLite rejects those unsupported options with explicit errors.skills/extension-author/prisma-next-extension-upgrade/upgrades/0.16-to-0.17/.Testing performed
Passed on the final rebased branch:
pnpm --filter @prisma-next/sql-relational-core buildpnpm --filter @prisma-next/sql-relational-core testpnpm --filter @prisma-next/sql-relational-core typecheckpnpm --filter @prisma-next/sql-relational-core lintpnpm --filter @prisma-next/adapter-postgres typecheckpnpm --filter @prisma-next/adapter-postgres lintpnpm --filter @prisma-next/adapter-sqlite testpnpm --filter @prisma-next/adapter-sqlite typecheckpnpm --filter @prisma-next/adapter-sqlite lintpnpm --filter @prisma-next/sql-orm-client testpnpm --filter @prisma-next/sql-orm-client typecheckpnpm --filter @prisma-next/sql-orm-client lintpnpm lint:castspnpm lint:depspnpm typecheckwhere-bindingregressiongit diff --checkscansAccepted baseline validation deferral; these full runs are not claimed as passing:
pnpm --filter @prisma-next/adapter-postgres testpnpm test:packagesBoth full-run paths repeatedly rotated among connection resets, timeouts, and connection-termination failures; a freshly installed and built pristine
origin/mainreproduced the same failure family. The operator accepted the baseline deferral, and CI is authoritative.Skill update
Adds the
adopt-sql-json-projection-ast-foundationsextension-author upgrade entry underskills/extension-author/prisma-next-extension-upgrade/upgrades/0.16-to-0.17/. It covers explicit JSON value-projection wrappers, the new function/cast/case visitor variants, groupedFunctionSourcealias arguments, and codec-preserving forwarded projections; the migration is prose-only because each handler and projection policy requires extension-specific reasoning.Follow-ups
Alternatives considered
Checklist
git commit -s) per the DCO.TML-NNNN: <sentence-case title>form.Summary by CodeRabbit
New Features
WITH ORDINALITY.Bug Fixes