Skip to content

tml-2913: native scalar-list query & update operations#910

Open
SevInf wants to merge 5 commits into
mainfrom
scalar-lists-slice3
Open

tml-2913: native scalar-list query & update operations#910
SevInf wants to merge 5 commits into
mainfrom
scalar-lists-slice3

Conversation

@SevInf

@SevInf SevInf commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

What & why

Slice 3 of native-scalar-arrays (TML-2913). Slices 1–2 made SQL scalar lists first-class in storage/authoring/migration/read (native text[]/int4[] columns with element-wise codecs). This slice makes them queryable and mutable — the operation surface.

Branches off main (has slices 1 & 2, merged). Independent of the check-constraint-unification PR.

What's here

Read predicates & array filters (registry ops on the sql-builder fns surface, lowering via OperationExpr templates — no new AST):

  • has (x = ANY(tags)) — element membership
  • arrayContains (@>), containedBy (<@), overlaps (&&) — array filters
  • length (cardinality), index (tags[i], element-typed, nullable)

Update mutators (element-preserving many: true return, on the update set callback):

  • arrayAppend (array_append), arrayRemove (array_remove), set-whole-array (raw-values path)

Comparison builtins widened to lists (FR16)eq/ne/gt/lt/gte/lte accept whole-list operands. Type-level only (they already lower to BinaryExpr(op), which Postgres applies to arrays). Added as disjoint overloads keyed on the many discriminant so scalar-call inference is untouched (NFR3); gated by the element codec's equality (eq/ne) / order (ordering) trait.

Decisions

  • arrayContains for @>: the natural name contains is already registered by the postgis extension (geometry), and the op registry is global-by-name — so the list "contains-all" filter is arrayContains.
  • Two incidental infra completions the operation layer needed: the framework op-registry learned the { many: true; elementTraits } self-arm, and the SQL emitter now projects many onto the storage-column .d.ts literal (so f.tags types as a list expression). The emitter change is guarded on many === true — non-list contracts are byte-identical (NFR2; only the one list-column fixture regenerated).

Acceptance criteria

  • AC3where((f,fns)=>fns.has(f.tags,x)) executes x = ANY(tags); wrong-typed element and scalar receiver are compile errors. ✅
  • AC7 — an update appends to and removes from tags String[] (array_append/array_remove) and the read returns the mutated list decoded element-wise. ✅
  • AC11fns.eq(f.tags,['a','b']) matches by array equality, fns.gt(f.tags,['a']) by lexicographic order; wrong-typed element and an ordering op on an unorderable-element list are compile errors. ✅

Verification

build 68/68 · typecheck 143/143 · lint 82/82 · lint:deps clean · fixtures:check byte-clean · cast ratchet delta 0 · adapter-postgres + sql-builder green · scalar-lists integration green (real Postgres). Runtime ACs proven on embedded Postgres; compile-error ACs via test-d.ts with load-bearing @ts-expect-error.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for native list/array operations, including membership, containment, overlap, length, indexing, and array updates.
    • Comparison operations now work with whole-list values where appropriate, improving support for array-based fields.
    • PostgreSQL queries now generate the expected array operators and functions for these capabilities.
  • Bug Fixes

    • Improved type and runtime handling for list fields, including nullability, ordering, and element access.

SevInf and others added 5 commits July 3, 2026 09:30
…ice 3 A]

Promote the prototyped `has` list operation into a live Postgres registry op.
`fns.has(f.tags, x)` now type-checks over a list column and executes as
`x = ANY(tags)` on real Postgres; a wrong-typed element and a scalar receiver
are compile errors.

Necessary unblocks discovered at DoR (both minimal and list-scoped):
- framework operation registry: accept the `{ many: true; elementTraits? }`
  self-spec arm (type + register validation), so list-self ops can register.
- SQL emitter: project `many: true` into the storage-table column `.d.ts`
  literal, so `Db<Contract>` types a `many` column as a list receiver
  (regenerated the scalar-lists fixture .d.ts; contract.json unchanged).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
… 3 B]

Promote the read-side v1 list ops into live Postgres registry ops, mirroring
the `has` pattern (runtime impl in `descriptor-meta.ts`, type mirror in
`operation-types.ts`), each proven to render the right SQL and execute on real
Postgres, with compile-error cases for wrong-typed operands and scalar
receivers:

- `containedBy` (`{{self}} <@ {{arg0}}`) — whole-array operand, bool.
- `overlaps`    (`{{self}} && {{arg0}}`) — whole-array operand, bool.
- `length`      (`cardinality({{self}})`) — non-null pg/int4.
- `index`       (`{{self}}[{{arg0}}]`)   — 1-based access, nullable element codec.

Whole-array operands accept a `readonly T[]` literal (lowered to an `ARRAY[...]`
literal) or another `ScalarListExpression<CodecId>`; the element type is tied to
the receiver's `CodecId`, so a wrong-typed element is a compile error. `index`
returns the receiver's element codec (nullable, since out-of-range access is
NULL).

Dropped from this slice: the `contains` (`@>`) op named in the plan collides
with the postgis extension's existing geometry `contains` op. The base postgres
adapter is always a stack contributor, so registering `contains` there
double-registers in any postgres+postgis stack (`Operation "contains" is
already registered`) and breaks `pnpm build`. Reported rather than renamed
against spec or cast around.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Mirror the existing `containedBy` (`<@`) op as `arrayContains` (`@>`, "list
contains every element of the argument array") — same whole-array operand shape,
same element-type linkage to the receiver `CodecId`, same non-null bool return —
differing only in the infix template and the name.

Named `arrayContains` rather than `contains`: the op registry is global-by-name
and the postgis extension already registers `contains` for geometry, so a base
`contains` op would double-register in any postgres+postgis stack.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…et) [slice 3 C]

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…) [slice 3 D]

Add disjoint list-operand overloads to the six comparison builtins
(eq/ne/gt/gte/lt/lte) keyed on the `many` discriminant. Each builtin now
intersects its untouched scalar signature with a `ListComparison` call
signature that accepts a whole-list receiver plus a matching list expression or
literal array. Element-trait gating (`equality` for eq/ne, `order` for the
ordering ops) is enforced via `CodecIdsWithTrait`, so an ordering op over a list
whose element codec lacks `order` is a compile error. Type-level widening only;
the runtime already lowers array operands correctly via the shared param path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
@SevInf SevInf requested a review from a team as a code owner July 3, 2026 11:21
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a many: true self-shaping mode to operation registration with validation, extends SQL builder comparison operators to accept list operands via a new ListComparison type, implements Postgres native list operations (has, arrayContains, containedBy, overlaps, length, index, arrayAppend, arrayRemove), updates the emitter for many columns, and adds corresponding type-level and runtime integration tests.

Changes

Scalar-list operations support

Layer / File(s) Summary
Operation registry many self-spec
packages/1-framework/1-core/operations/src/index.ts, packages/1-framework/1-core/operations/test/operations-registry.test.ts
SelfSpec gains a mutually exclusive many: true mode with optional elementTraits; registry validation enforces exactly one of codecId/traits/many, with new passing and error tests.
Emitter and codec type exports for many columns
packages/2-sql/3-tooling/emitter/src/index.ts, packages/2-sql/3-tooling/emitter/test/emitter-hook.storage-column-types.test.ts, packages/2-sql/4-lanes/relational-core/src/expression.ts
Emitted table literal types include readonly many: true for many columns; CodecIdsWithTrait is now exported.
SQL builder list comparison typings
packages/2-sql/4-lanes/sql-builder/src/expression.ts, packages/2-sql/4-lanes/sql-builder/test/types/scalar-list-ops.types.test-d.ts
New ListComparison type and updated BuiltinFunctions comparison signatures (eq, ne, gt, gte, lt, lte) accept list-expression operands when element codecs carry required traits, validated by expanded type tests.
Postgres list operation implementations
packages/3-targets/6-adapters/postgres/src/core/descriptor-meta.ts, packages/3-targets/6-adapters/postgres/src/types/operation-types.ts
Adds has, arrayContains, containedBy, overlaps, length, index, arrayAppend, arrayRemove Postgres operations with SQL lowering templates, helper functions, and corresponding query operation types.
Runtime and type integration tests
test/integration/test/scalar-lists/*
New integration tests verify generated SQL and runtime behavior for list operators/mutations against Postgres, and new type tests assert compile-time correctness of list operator signatures.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • prisma/prisma-next#25: Extends the same comparison operator set (gt/gte/lt/lte) that this PR builds list-receiver overloads on top of.
  • prisma/prisma-next#594: Modifies the same BuiltinFunctions type in sql-builder/src/expression.ts edited by this PR.
  • prisma/prisma-next#846: Introduces the scalar-array (many)/ScalarListExpression machinery that this PR's list operations and typings build upon.

Suggested reviewers: aqrln, jkomyno, wmadden

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR’s main change: native scalar-list query and update operations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 scalar-lists-slice3

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.

❤️ Share

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

@prisma-next/extension-author-tools

npm i https://pkg.pr.new/@prisma-next/extension-author-tools@910

@prisma-next/mongo-runtime

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

@prisma-next/family-mongo

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

@prisma-next/sql-runtime

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

@prisma-next/family-sql

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

@prisma-next/extension-arktype-json

npm i https://pkg.pr.new/@prisma-next/extension-arktype-json@910

@prisma-next/middleware-cache

npm i https://pkg.pr.new/@prisma-next/middleware-cache@910

@prisma-next/mongo

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

@prisma-next/extension-paradedb

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

@prisma-next/extension-pgvector

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

@prisma-next/extension-postgis

npm i https://pkg.pr.new/@prisma-next/extension-postgis@910

@prisma-next/postgres

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

@prisma-next/sql-orm-client

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

@prisma-next/sqlite

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

@prisma-next/extension-supabase

npm i https://pkg.pr.new/@prisma-next/extension-supabase@910

@prisma-next/target-mongo

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

@prisma-next/adapter-mongo

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

@prisma-next/driver-mongo

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

@prisma-next/contract

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

@prisma-next/utils

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

@prisma-next/config

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

@prisma-next/errors

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

@prisma-next/framework-components

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

@prisma-next/operations

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

@prisma-next/ts-render

npm i https://pkg.pr.new/@prisma-next/ts-render@910

@prisma-next/contract-authoring

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

@prisma-next/ids

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

@prisma-next/psl-parser

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

@prisma-next/psl-printer

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

@prisma-next/cli

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

@prisma-next/cli-telemetry

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

@prisma-next/config-loader

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

@prisma-next/emitter

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

@prisma-next/language-server

npm i https://pkg.pr.new/@prisma-next/language-server@910

@prisma-next/migration-tools

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

prisma-next

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

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

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

@prisma-next/mongo-codec

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

@prisma-next/mongo-contract

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

@prisma-next/mongo-value

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

@prisma-next/mongo-contract-psl

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

@prisma-next/mongo-contract-ts

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

@prisma-next/mongo-emitter

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

@prisma-next/mongo-schema-ir

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

@prisma-next/mongo-query-ast

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

@prisma-next/mongo-orm

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

@prisma-next/mongo-query-builder

npm i https://pkg.pr.new/@prisma-next/mongo-query-builder@910

@prisma-next/mongo-lowering

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

@prisma-next/mongo-wire

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

@prisma-next/sql-contract

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

@prisma-next/sql-errors

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

@prisma-next/sql-operations

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

@prisma-next/sql-schema-ir

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

@prisma-next/sql-contract-psl

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

@prisma-next/sql-contract-ts

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

@prisma-next/sql-contract-emitter

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

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

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

@prisma-next/sql-relational-core

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

@prisma-next/sql-builder

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

@prisma-next/target-postgres

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

@prisma-next/target-sqlite

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

@prisma-next/adapter-postgres

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

@prisma-next/adapter-sqlite

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

@prisma-next/driver-postgres

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

@prisma-next/driver-sqlite

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

commit: 3abca2d

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

size-limit report 📦

Path Size
postgres / no-emit 161.14 KB (+0.25% 🔺)
postgres / emit 148.24 KB (+0.26% 🔺)
mongo / no-emit 98.2 KB (0%)
mongo / emit 89.39 KB (0%)
cf-worker / no-emit 189.3 KB (+0.23% 🔺)
cf-worker / emit 174.63 KB (+0.27% 🔺)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/1-framework/1-core/operations/src/index.ts (1)

44-61: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Error messages don't reflect the new many mode.

targetCount === 0 still throws "has neither codecId nor traits", and targetCount > 1 still throws "has both codecId and traits" — neither mentions many, even though a self: {} (no codecId/traits/many) or a conflicting { many: true, traits: [...] } self would now hit these same messages. This misleads whoever debugs a failing registration involving many. Note the test at operations-registry.test.ts (Line 122) already hardcodes the stale "both codecId and traits" text, so both need to move together.

✏️ Proposed fix for generic messaging
         if (targetCount === 0) {
-          throw new Error(`Operation "${name}" self has neither codecId nor traits`);
+          throw new Error(`Operation "${name}" self has none of codecId, traits, or many`);
         }
         if (targetCount > 1) {
-          throw new Error(`Operation "${name}" self has both codecId and traits`);
+          throw new Error(`Operation "${name}" self must have exactly one of codecId, traits, or many`);
         }
🤖 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/1-framework/1-core/operations/src/index.ts` around lines 44 - 61,
Update the error handling in register so the messages account for
descriptor.self.many as a valid target alongside codecId and traits. In the
register name/descriptor flow, change the zero-target and multi-target Error
text to mention codecId, traits, and many consistently, and make sure any
assertions in operations-registry.test.ts that hardcode the old wording are
updated to match the new messages.
🧹 Nitpick comments (5)
packages/3-targets/6-adapters/postgres/src/core/descriptor-meta.ts (1)

183-189: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the elementCodecOf helper instead of re-inlining its body.

has (Lines 184-189) and index (Lines 268-273) duplicate the exact codec-resolution block that elementCodecOf already encapsulates and that arrayAppend/arrayRemove reuse. Consolidate to avoid divergence.

♻️ Reuse helper in `has`
-        const listCodec = codecOf(self);
-        const elementCodec =
-          listCodec === undefined
-            ? undefined
-            : listCodec.typeParams === undefined
-              ? { codecId: listCodec.codecId }
-              : { codecId: listCodec.codecId, typeParams: listCodec.typeParams };
+        const elementCodec = elementCodecOf(self);

Apply the same substitution in index (Lines 267-273).

🤖 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/descriptor-meta.ts` around
lines 183 - 189, The codec-resolution logic in has and index is duplicated and
should reuse the existing elementCodecOf helper instead of re-inlining its body.
Update both locations to call elementCodecOf with the same self argument used in
codecOf(self), matching the pattern already used by arrayAppend and arrayRemove,
so the codec selection stays centralized in descriptor-meta.ts.
packages/2-sql/4-lanes/sql-builder/test/types/scalar-list-ops.types.test-d.ts (1)

91-127: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Missing negative test for eq/ne with a non-equality-trait element.

All fixtures in CT carry equality, so there's no @ts-expect-error case proving eq/ne reject a list whose element codec lacks equality (only the ordering-trait-missing case is tested via flags). Not blocking, but would round out the trait-gating coverage symmetrically with the ordering tests.

🤖 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/sql-builder/test/types/scalar-list-ops.types.test-d.ts`
around lines 91 - 127, The scalar-list overload tests in
scalar-list-ops.types.test-d.ts are missing a negative case for equality-based
comparison on a list whose element codec does not have the equality trait.
Extend the existing `fns.eq`/`fns.ne` coverage in the `scalar comparison` /
`equality builtins` tests by adding an `@ts-expect-error` assertion using a list
fixture whose element type lacks `equality`, mirroring the existing
`fns.gt`/`fns.lt` rejection checks for `flags` and keeping the test names and
`fns`/`f` symbols aligned with the current suite.
packages/2-sql/4-lanes/sql-builder/src/expression.ts (1)

4-35: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Verbose doc comment on ListComparison.

The design (trait-gated list overload kept disjoint from scalar overload via the many discriminant) is non-trivial and the comment does explain real rationale, but per repo guidelines comments should be avoided when code can express intent directly. Consider trimming to the essential "why" (disjointness via many discriminant) and dropping the mechanical description of RequiredTraits, which is already conveyed by the type parameter name and usage sites.

As per coding guidelines, **/*.{ts,tsx,js,jsx,mts,mjs}: "Don't add comments if avoidable, prefer code that expresses its intent."

🤖 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/sql-builder/src/expression.ts` around lines 4 - 35,
The doc comment above ListComparison is too verbose for a type that already
expresses most of its intent. Trim it down to the minimal rationale needed to
understand the overload separation, keeping only the point about the many
discriminant making the list and scalar call signatures disjoint, and remove the
detailed explanation of RequiredTraits and the mechanical lowering behavior.

Source: Coding guidelines

test/integration/test/scalar-lists/scalar-list-mutations.integration.test.ts (2)

168-219: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Whole-array replacement test doesn't assert on generated SQL.

Unlike the arrayAppend/arrayRemove test above (which asserts capturedSql contains array_append(/array_remove(), this test only checks the round-tripped row values. Since this test exists specifically to validate "whole-array replacement via the raw-values path" (per PR objectives), asserting the emitted SQL uses the expected raw-array lowering (rather than only checking end state) would give stronger confidence the intended code path is exercised.

🤖 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 `@test/integration/test/scalar-lists/scalar-list-mutations.integration.test.ts`
around lines 168 - 219, The whole-array replacement integration test currently
only verifies the final row value, so it does not confirm that the raw-array
lowering path was used. Update the test named “replaces the whole list column
with a raw array literal” to capture the SQL emitted by the `runtime.execute`
call that uses `builder.public.item.update({ tags: ['x', 'y'] })`, and assert
the generated SQL reflects a raw array replacement rather than `array_append` or
`array_remove`. Keep the existing round-trip assertion, but add SQL-level
expectations so this test exercises the intended `sqlBuilder`/`rawCodecInferer`
path.

105-219: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Same duplicated setup pattern as the sibling read-ops test file.

Both it() blocks re-implement the schema reset / migrate / runtime+context construction sequence found in orm-list-read.integration.test.ts. Consider factoring a shared helper (in a common test-utils module for the scalar-lists suite) used by both files to avoid the same boilerplate being maintained in two places.

🤖 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 `@test/integration/test/scalar-lists/scalar-list-mutations.integration.test.ts`
around lines 105 - 219, The two scalar list mutation tests duplicate the same
schema reset, migration, and runtime/context setup already used in the sibling
read-ops test file. Extract that repeated setup into a shared helper for the
scalar-lists suite, then have both `it()` blocks in
`scalar-list-mutations.integration.test.ts` call it instead of re-creating the
`withClient`, `migrateContract`, `createTestRuntimeFromClient`,
`createExecutionContext`, and `sqlBuilder` wiring inline. Keep the helper in a
common test-utils module so the setup stays in one place and both mutation tests
remain focused on their assertions.
🤖 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/3-targets/6-adapters/postgres/src/core/descriptor-meta.ts`:
- Around line 148-150: The arrayOperandToExpr path currently renders raw JS
arrays as untyped ARRAY literals, which breaks empty-array operands in
PostgreSQL array operators. Update arrayOperandToExpr and the related renderer
path in descriptor-meta.ts to preserve the receiver’s element codec or emit an
explicit ::type[] cast when creating LiteralExpr.of(operand), so @>, <@, and &&
always get a typed array literal even for empty arrays.

In `@test/integration/test/scalar-lists/orm-list-read.integration.test.ts`:
- Around line 148-411: The four integration tests repeat the same schema reset,
migration, runtime setup, and SQL-capture boilerplate, and the same pattern
appears in the related scalar-list mutation tests. Extract that shared setup
into a helper such as a reset/migrate step plus a setup function that builds the
`captureMiddleware`, `runtime`, `context`, and `capturedSql` for
`createTestRuntimeFromClient`/`createExecutionContext`, then update each `it()`
block to use the shared helper and keep only the test-specific assertions.

In `@test/integration/test/scalar-lists/scalar-list-ops.types.test-d.ts`:
- Around line 155-160: Move the `@ts-expect-error` marker from the comment above
the update callback to the specific id: assignment line in the
db.public.item.update test so it applies to the actual type error on
fns.arrayAppend(f.tags, 'x') and avoids TS2578; use the db.public.item.update,
fns.arrayAppend, and id property locations to place it precisely.

---

Outside diff comments:
In `@packages/1-framework/1-core/operations/src/index.ts`:
- Around line 44-61: Update the error handling in register so the messages
account for descriptor.self.many as a valid target alongside codecId and traits.
In the register name/descriptor flow, change the zero-target and multi-target
Error text to mention codecId, traits, and many consistently, and make sure any
assertions in operations-registry.test.ts that hardcode the old wording are
updated to match the new messages.

---

Nitpick comments:
In `@packages/2-sql/4-lanes/sql-builder/src/expression.ts`:
- Around line 4-35: The doc comment above ListComparison is too verbose for a
type that already expresses most of its intent. Trim it down to the minimal
rationale needed to understand the overload separation, keeping only the point
about the many discriminant making the list and scalar call signatures disjoint,
and remove the detailed explanation of RequiredTraits and the mechanical
lowering behavior.

In
`@packages/2-sql/4-lanes/sql-builder/test/types/scalar-list-ops.types.test-d.ts`:
- Around line 91-127: The scalar-list overload tests in
scalar-list-ops.types.test-d.ts are missing a negative case for equality-based
comparison on a list whose element codec does not have the equality trait.
Extend the existing `fns.eq`/`fns.ne` coverage in the `scalar comparison` /
`equality builtins` tests by adding an `@ts-expect-error` assertion using a list
fixture whose element type lacks `equality`, mirroring the existing
`fns.gt`/`fns.lt` rejection checks for `flags` and keeping the test names and
`fns`/`f` symbols aligned with the current suite.

In `@packages/3-targets/6-adapters/postgres/src/core/descriptor-meta.ts`:
- Around line 183-189: The codec-resolution logic in has and index is duplicated
and should reuse the existing elementCodecOf helper instead of re-inlining its
body. Update both locations to call elementCodecOf with the same self argument
used in codecOf(self), matching the pattern already used by arrayAppend and
arrayRemove, so the codec selection stays centralized in descriptor-meta.ts.

In
`@test/integration/test/scalar-lists/scalar-list-mutations.integration.test.ts`:
- Around line 168-219: The whole-array replacement integration test currently
only verifies the final row value, so it does not confirm that the raw-array
lowering path was used. Update the test named “replaces the whole list column
with a raw array literal” to capture the SQL emitted by the `runtime.execute`
call that uses `builder.public.item.update({ tags: ['x', 'y'] })`, and assert
the generated SQL reflects a raw array replacement rather than `array_append` or
`array_remove`. Keep the existing round-trip assertion, but add SQL-level
expectations so this test exercises the intended `sqlBuilder`/`rawCodecInferer`
path.
- Around line 105-219: The two scalar list mutation tests duplicate the same
schema reset, migration, and runtime/context setup already used in the sibling
read-ops test file. Extract that repeated setup into a shared helper for the
scalar-lists suite, then have both `it()` blocks in
`scalar-list-mutations.integration.test.ts` call it instead of re-creating the
`withClient`, `migrateContract`, `createTestRuntimeFromClient`,
`createExecutionContext`, and `sqlBuilder` wiring inline. Keep the helper in a
common test-utils module so the setup stays in one place and both mutation tests
remain focused on their assertions.
🪄 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: aeb15c2f-8477-403f-8fe6-8cfead5ff0f5

📥 Commits

Reviewing files that changed from the base of the PR and between 284a838 and 3abca2d.

⛔ Files ignored due to path filters (1)
  • test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts is excluded by !**/generated/**
📒 Files selected for processing (12)
  • packages/1-framework/1-core/operations/src/index.ts
  • packages/1-framework/1-core/operations/test/operations-registry.test.ts
  • packages/2-sql/3-tooling/emitter/src/index.ts
  • packages/2-sql/3-tooling/emitter/test/emitter-hook.storage-column-types.test.ts
  • packages/2-sql/4-lanes/relational-core/src/expression.ts
  • packages/2-sql/4-lanes/sql-builder/src/expression.ts
  • packages/2-sql/4-lanes/sql-builder/test/types/scalar-list-ops.types.test-d.ts
  • packages/3-targets/6-adapters/postgres/src/core/descriptor-meta.ts
  • packages/3-targets/6-adapters/postgres/src/types/operation-types.ts
  • test/integration/test/scalar-lists/orm-list-read.integration.test.ts
  • test/integration/test/scalar-lists/scalar-list-mutations.integration.test.ts
  • test/integration/test/scalar-lists/scalar-list-ops.types.test-d.ts

Comment on lines +148 to +150
function arrayOperandToExpr(operand: unknown): AnyExpression {
return Array.isArray(operand) ? LiteralExpr.of(operand) : toExpr(operand);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect LiteralExpr.of signature to see if it accepts/derives element codec typing
fd -t f 'ast' packages/2-sql/4-lanes/relational-core/src -x true
rg -nP -C4 '\bLiteralExpr\b' packages/2-sql/4-lanes/relational-core/src
rg -nP -C4 'class LiteralExpr|static of' packages/2-sql/4-lanes/relational-core/src

Repository: prisma/prisma-next

Length of output: 23827


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the adapter file and inspect the relevant range.
ast-grep outline packages/3-targets/6-adapters/postgres/src/core/descriptor-meta.ts --view expanded
wc -l packages/3-targets/6-adapters/postgres/src/core/descriptor-meta.ts
sed -n '120,190p' packages/3-targets/6-adapters/postgres/src/core/descriptor-meta.ts

# Find the lowering/rendering path for LiteralExpr and array operations.
rg -n -C 4 'arrayContains|containedBy|overlaps|arrayOperandToExpr|LiteralExpr\.of|LiteralExpr|@>' packages/3-targets/6-adapters/postgres/src packages/2-sql/4-lanes/relational-core/src

Repository: prisma/prisma-next

Length of output: 30804


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect array rendering and any special-casing for empty arrays / typed array operands.
sed -n '740,790p' packages/3-targets/6-adapters/postgres/src/core/sql-renderer.ts
rg -n -C 3 'ARRAY\[\]|empty array|array.*cast|cast.*array|ListExpression|fromValues|arrayContains|containedBy|overlaps' packages/3-targets/6-adapters/postgres/src packages/2-sql/4-lanes/relational-core/src

# Check whether array-operation tests exercise empty arrays or typed list operands.
fd -t f '.*(test|spec)\.(ts|tsx|js|jsx)$' packages/3-targets/6-adapters/postgres/src packages/2-sql/4-lanes/relational-core/src
rg -n -C 3 'arrayContains|containedBy|overlaps|has\(' packages/3-targets/6-adapters/postgres/src packages/2-sql/4-lanes/relational-core/src

Repository: prisma/prisma-next

Length of output: 39026


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect how list expressions are rendered compared with array literals.
sed -n '560,640p' packages/3-targets/6-adapters/postgres/src/core/sql-renderer.ts

# Inspect operation rendering to see whether it injects casts or type hints.
sed -n '640,740p' packages/3-targets/6-adapters/postgres/src/core/sql-renderer.ts

Repository: prisma/prisma-next

Length of output: 5526


Cast raw array operands before rendering
packages/3-targets/6-adapters/postgres/src/core/descriptor-meta.ts:148-150
arrayOperandToExpr lowers raw JS arrays to LiteralExpr.of(operand), and the renderer emits them as bare ARRAY[...] literals. That leaves empty arrays as ARRAY[], which PostgreSQL can’t type for @>, <@, or &&. Thread the receiver’s element codec through this path or add an explicit ::type[] cast.

🤖 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/descriptor-meta.ts` around
lines 148 - 150, The arrayOperandToExpr path currently renders raw JS arrays as
untyped ARRAY literals, which breaks empty-array operands in PostgreSQL array
operators. Update arrayOperandToExpr and the related renderer path in
descriptor-meta.ts to preserve the receiver’s element codec or emit an explicit
::type[] cast when creating LiteralExpr.of(operand), so @>, <@, and && always
get a typed array literal even for empty arrays.

Comment on lines +148 to +411

it(
'filters rows through the native `has` membership op, lowering to `= ANY(...)`',
async () => {
if (!database) throw new Error('database not initialised');

await withClient(database.connectionString, async (client) => {
await client.query('DROP SCHEMA IF EXISTS public CASCADE');
await client.query('CREATE SCHEMA public');
await client.query('DROP SCHEMA IF EXISTS prisma_contract CASCADE');
});
await migrateContract(database.connectionString);

await withClient(database.connectionString, async (client) => {
const capturedSql: string[] = [];
const captureMiddleware: SqlMiddleware = {
name: 'capture-sql',
beforeExecute(plan) {
capturedSql.push(plan.sql);
},
};
const runtime = await createTestRuntimeFromClient(
contract as FrameworkContract<SqlStorage>,
client,
{ verifyMarker: false, middleware: [captureMiddleware] },
);

const context = createExecutionContext<Contract>({
contract,
stack: createSqlExecutionStack({
target: postgresRuntimeTarget,
adapter: postgresRuntimeAdapter,
extensionPacks: [],
}),
});

const db = orm({ runtime, context });
await db.public.Item.create({ id: 1, tags: ['react', 'vue'], scores: [1] });
await db.public.Item.create({ id: 2, tags: ['vue', 'svelte'], scores: [2] });
await db.public.Item.create({ id: 3, tags: ['svelte'], scores: [3] });

const builder = sqlBuilder({ context, rawCodecInferer: postgresRawCodecInferer });
const rows = await runtime.execute(
builder.public.item
.select('id')
.where((f, fns) => fns.has(f.tags, 'vue'))
.orderBy((f) => f.id)
.build(),
);

expect(capturedSql.some((s) => /= ANY\(/.test(s))).toBe(true);
expect(rows).toEqual([{ id: 1 }, { id: 2 }]);
});
},
timeouts.spinUpPpgDev,
);

it(
'filters rows through native array ops (arrayContains @>, containedBy <@, overlaps &&)',
async () => {
if (!database) throw new Error('database not initialised');

await withClient(database.connectionString, async (client) => {
await client.query('DROP SCHEMA IF EXISTS public CASCADE');
await client.query('CREATE SCHEMA public');
await client.query('DROP SCHEMA IF EXISTS prisma_contract CASCADE');
});
await migrateContract(database.connectionString);

await withClient(database.connectionString, async (client) => {
const capturedSql: string[] = [];
const captureMiddleware: SqlMiddleware = {
name: 'capture-sql',
beforeExecute(plan) {
capturedSql.push(plan.sql);
},
};
const runtime = await createTestRuntimeFromClient(
contract as FrameworkContract<SqlStorage>,
client,
{ verifyMarker: false, middleware: [captureMiddleware] },
);

const context = createExecutionContext<Contract>({
contract,
stack: createSqlExecutionStack({
target: postgresRuntimeTarget,
adapter: postgresRuntimeAdapter,
extensionPacks: [],
}),
});

const db = orm({ runtime, context });
await db.public.Item.create({ id: 1, tags: ['react', 'vue'], scores: [1] });
await db.public.Item.create({ id: 2, tags: ['vue', 'svelte'], scores: [2] });
await db.public.Item.create({ id: 3, tags: ['svelte'], scores: [3] });

const builder = sqlBuilder({ context, rawCodecInferer: postgresRawCodecInferer });

const within = await runtime.execute(
builder.public.item
.select('id')
.where((f, fns) => fns.containedBy(f.tags, ['vue', 'svelte']))
.orderBy((f) => f.id)
.build(),
);
expect(capturedSql.some((s) => s.includes('<@'))).toBe(true);
expect(capturedSql.some((s) => s.includes('ARRAY['))).toBe(true);
expect(within).toEqual([{ id: 2 }, { id: 3 }]);

const overlapping = await runtime.execute(
builder.public.item
.select('id')
.where((f, fns) => fns.overlaps(f.tags, ['react']))
.orderBy((f) => f.id)
.build(),
);
expect(capturedSql.some((s) => s.includes('&&'))).toBe(true);
expect(overlapping).toEqual([{ id: 1 }]);

const superset = await runtime.execute(
builder.public.item
.select('id')
.where((f, fns) => fns.arrayContains(f.tags, ['svelte']))
.orderBy((f) => f.id)
.build(),
);
expect(capturedSql.some((s) => s.includes('@>'))).toBe(true);
expect(capturedSql.some((s) => s.includes('ARRAY['))).toBe(true);
expect(superset).toEqual([{ id: 2 }, { id: 3 }]);
});
},
timeouts.spinUpPpgDev,
);

it(
'compares whole lists through the eq/gt comparison builtins (FR16)',
async () => {
if (!database) throw new Error('database not initialised');

await withClient(database.connectionString, async (client) => {
await client.query('DROP SCHEMA IF EXISTS public CASCADE');
await client.query('CREATE SCHEMA public');
await client.query('DROP SCHEMA IF EXISTS prisma_contract CASCADE');
});
await migrateContract(database.connectionString);

await withClient(database.connectionString, async (client) => {
const capturedSql: string[] = [];
const captureMiddleware: SqlMiddleware = {
name: 'capture-sql',
beforeExecute(plan) {
capturedSql.push(plan.sql);
},
};
const runtime = await createTestRuntimeFromClient(
contract as FrameworkContract<SqlStorage>,
client,
{ verifyMarker: false, middleware: [captureMiddleware] },
);

const context = createExecutionContext<Contract>({
contract,
stack: createSqlExecutionStack({
target: postgresRuntimeTarget,
adapter: postgresRuntimeAdapter,
extensionPacks: [],
}),
});

const db = orm({ runtime, context });
await db.public.Item.create({ id: 1, tags: ['react', 'vue'], scores: [1] });
await db.public.Item.create({ id: 2, tags: ['svelte'], scores: [2] });
await db.public.Item.create({ id: 3, tags: ['react', 'vue'], scores: [3] });

const builder = sqlBuilder({ context, rawCodecInferer: postgresRawCodecInferer });

const equal = await runtime.execute(
builder.public.item
.select('id')
.where((f, fns) => fns.eq(f.tags, ['react', 'vue']))
.orderBy((f) => f.id)
.build(),
);
expect(capturedSql.some((s) => /"tags" = \$\d+/.test(s))).toBe(true);
expect(equal).toEqual([{ id: 1 }, { id: 3 }]);

const greater = await runtime.execute(
builder.public.item
.select('id')
.where((f, fns) => fns.gt(f.tags, ['react']))
.orderBy((f) => f.id)
.build(),
);
expect(capturedSql.some((s) => /"tags" > \$\d+/.test(s))).toBe(true);
// ['react','vue'] and ['svelte'] both sort lexicographically after ['react']
expect(greater).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]);
});
},
timeouts.spinUpPpgDev,
);

it(
'computes list length (cardinality) and 1-based nullable element access',
async () => {
if (!database) throw new Error('database not initialised');

await withClient(database.connectionString, async (client) => {
await client.query('DROP SCHEMA IF EXISTS public CASCADE');
await client.query('CREATE SCHEMA public');
await client.query('DROP SCHEMA IF EXISTS prisma_contract CASCADE');
});
await migrateContract(database.connectionString);

await withClient(database.connectionString, async (client) => {
const capturedSql: string[] = [];
const captureMiddleware: SqlMiddleware = {
name: 'capture-sql',
beforeExecute(plan) {
capturedSql.push(plan.sql);
},
};
const runtime = await createTestRuntimeFromClient(
contract as FrameworkContract<SqlStorage>,
client,
{ verifyMarker: false, middleware: [captureMiddleware] },
);

const context = createExecutionContext<Contract>({
contract,
stack: createSqlExecutionStack({
target: postgresRuntimeTarget,
adapter: postgresRuntimeAdapter,
extensionPacks: [],
}),
});

const db = orm({ runtime, context });
await db.public.Item.create({ id: 1, tags: ['react', 'vue'], scores: [10, 20] });
await db.public.Item.create({ id: 2, tags: ['svelte'], scores: [30] });

const builder = sqlBuilder({ context, rawCodecInferer: postgresRawCodecInferer });
const rows = await runtime.execute(
builder.public.item
.select((f, fns) => ({
id: f.id,
len: fns.length(f.tags),
firstTag: fns.index(f.tags, 1),
secondScore: fns.index(f.scores, 2),
}))
.orderBy((f) => f.id)
.build(),
);

expect(capturedSql.some((s) => s.includes('cardinality('))).toBe(true);
expect(capturedSql.some((s) => /\[\$\d+/.test(s))).toBe(true);
expect(rows).toEqual([
{ id: 1, len: 2, firstTag: 'react', secondScore: 20 },
{ id: 2, len: 1, firstTag: 'svelte', secondScore: null },
]);
});
},
timeouts.spinUpPpgDev,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract shared setup to remove ~4x duplicated boilerplate.

Each it() block repeats an identical sequence: drop/recreate public schema, drop prisma_contract schema, migrateContract, build capturedSql/captureMiddleware, create runtime, and create context. This is copy-pasted four times (and again in scalar-list-mutations.integration.test.ts). Extracting a shared helper (e.g. a beforeEach that resets the schema/migrates, plus a setupCapturingRuntime(client) helper returning { runtime, context, capturedSql, builder }) would materially cut duplication and make each test focus on its actual assertions.

♻️ Sketch of a shared helper
+async function resetSchema(connectionString: string): Promise<void> {
+  await withClient(connectionString, async (client) => {
+    await client.query('DROP SCHEMA IF EXISTS public CASCADE');
+    await client.query('CREATE SCHEMA public');
+    await client.query('DROP SCHEMA IF EXISTS prisma_contract CASCADE');
+  });
+}
+
+function setupCapturingRuntime(client: Client) {
+  const capturedSql: string[] = [];
+  const captureMiddleware: SqlMiddleware = {
+    name: 'capture-sql',
+    beforeExecute(plan) {
+      capturedSql.push(plan.sql);
+    },
+  };
+  return { capturedSql, captureMiddleware };
+}

Then each it() becomes: await resetSchema(...); await migrateContract(...); await withClient(..., async (client) => { const { capturedSql, captureMiddleware } = setupCapturingRuntime(client); ... }).

🤖 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 `@test/integration/test/scalar-lists/orm-list-read.integration.test.ts` around
lines 148 - 411, The four integration tests repeat the same schema reset,
migration, runtime setup, and SQL-capture boilerplate, and the same pattern
appears in the related scalar-list mutation tests. Extract that shared setup
into a helper such as a reset/migrate step plus a setup function that builds the
`captureMiddleware`, `runtime`, `context`, and `capturedSql` for
`createTestRuntimeFromClient`/`createExecutionContext`, then update each `it()`
block to use the shared helper and keep only the test-specific assertions.

Comment on lines +155 to +160
test('a list mutator is rejected for a scalar column', () => {
// @ts-expect-error -- id is a scalar int column; a text list mutator is not assignable
db.public.item.update((f, fns) => ({
id: fns.arrayAppend(f.tags, 'x'),
}));
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file snippet =="
sed -n '130,175p' test/integration/test/scalar-lists/scalar-list-ops.types.test-d.ts

echo
echo "== search for update/arrayAppend definitions =="
rg -n "arrayAppend|update\\(" test/integration src packages . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' | head -n 200

Repository: prisma/prisma-next

Length of output: 36280


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file size =="
wc -l test/integration/test/scalar-lists/scalar-list-ops.types.test-d.ts

echo
echo "== snippet around target =="
nl -ba test/integration/test/scalar-lists/scalar-list-ops.types.test-d.ts | sed -n '145,165p'

echo
echo "== locate relevant symbols =="
rg -n "arrayAppend|arrayPrepend|update\\s*\\(" test/integration test src packages -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' | head -n 200

Repository: prisma/prisma-next

Length of output: 312


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Try to locate the file that defines the database item update shape and array mutators.
for f in $(git ls-files | rg '(\.ts|\.d\.ts|\.tsx)$'); do
  if rg -q "arrayAppend|arrayPrepend|update\\s*\\(" "$f"; then
    echo "### $f"
    rg -n "arrayAppend|arrayPrepend|update\\s*\\(" "$f"
  fi
done | head -n 200

Repository: prisma/prisma-next

Length of output: 13573


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read only the target test and inspect nearby declarations/types that affect the error location.
echo "== target test snippet =="
nl -ba test/integration/test/scalar-lists/scalar-list-ops.types.test-d.ts | sed -n '145,165p'

echo
echo "== nearby update/mutator declarations =="
rg -n -C 2 "arrayAppend|update\\s*\\(" test/integration/test/scalar-lists/scalar-list-ops.types.test-d.ts test/integration/test -g '!**/node_modules/**' | head -n 200

Repository: prisma/prisma-next

Length of output: 222


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat >/tmp/ts-expect-error-placement.ts <<'TS'
declare function fn(arg: { id: number }): void;
declare const bad: string;

// case 1: directive above the call line
// `@ts-expect-error`
fn({
  id: bad,
});

// case 2: directive above the offending property line
fn({
  // `@ts-expect-error`
  id: bad,
});
TS

if command -v tsc >/dev/null 2>&1; then
  tsc --noEmit --pretty false /tmp/ts-expect-error-placement.ts
else
  echo "tsc not available"
fi

Repository: prisma/prisma-next

Length of output: 367


Move @ts-expect-error to the id: line
@ts-expect-error only applies to the next line, so the current placement leaves the assignment error on id: fns.arrayAppend(f.tags, 'x') unsuppressed and can also trigger TS2578.

🤖 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 `@test/integration/test/scalar-lists/scalar-list-ops.types.test-d.ts` around
lines 155 - 160, Move the `@ts-expect-error` marker from the comment above the
update callback to the specific id: assignment line in the db.public.item.update
test so it applies to the actual type error on fns.arrayAppend(f.tags, 'x') and
avoids TS2578; use the db.public.item.update, fns.arrayAppend, and id property
locations to place it precisely.

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