feat: release prisma next extension#444
Conversation
🦋 Changeset detectedLatest commit: 4c2d6d4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR adds ChangesPrisma Next integration and example
Sequence Diagram(s)sequenceDiagram
participant App
participant PrismaRuntime as Prisma Next Runtime
participant Cipherstash as `@cipherstash/prisma-next`
participant Stack as `@cipherstash/stack`
participant Postgres
App->>Cipherstash: cipherstashFromStack({ contractJson })
Cipherstash->>Stack: create EncryptionClient and SDK adapter
App->>PrismaRuntime: execute query with Encrypted* values
PrismaRuntime->>Cipherstash: bulkEncryptMiddleware beforeExecute
Cipherstash->>Stack: bulkEncrypt / bulkDecrypt
PrismaRuntime->>Postgres: run SQL with eql_v2_encrypted values
Postgres-->>PrismaRuntime: rows
PrismaRuntime-->>App: encrypted results ready for decryptAll / field decrypt
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
examples/prisma/package.json (1)
1-37:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd explicit Node and pnpm version constraints for the example.
This manifest is missing runtime/tooling pins required for example apps.
Suggested patch
{ "name": "@cipherstash/prisma-next-example", "private": true, "version": "0.0.0", + "engines": { + "node": ">=22" + }, + "packageManager": "pnpm@9", "description": "End-to-end example of `@cipherstash/prisma-next`: searchable application-layer encryption for Postgres with Prisma Next, using `@cipherstash/stack` as the SDK.", "type": "module",As per coding guidelines,
examples/**/package.json: "Use Node.js >= 22 and pnpm 9.x for example apps".🤖 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 `@examples/prisma/package.json` around lines 1 - 37, The package.json for the Prisma example lacks Node and pnpm version pins; add an "engines" field specifying "node": ">=22" and add a "packageManager" field set to "pnpm@9" (or "pnpm@9.x") at the top level of package.json so the example enforces Node >=22 and pnpm 9.x. Update the top-level JSON object (near "name", "version", etc.) to include these fields and ensure the file remains valid JSON.packages/cli/src/commands/init/steps/install-deps.ts (1)
100-117:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRe-check
@cipherstash/prisma-nextafter install before declaring success.When Prisma Next is targeted, the post-install verification only checks
@cipherstash/stackandstash. A failed@cipherstash/prisma-nextinstall can still report success and skip the missing-package warning.Suggested fix
- const stackInstalled = isPackageInstalled(STACK_PACKAGE) - const cliInstalled = isPackageInstalled(CLI_PACKAGE) + const stackInstalled = isPackageInstalled(STACK_PACKAGE) + const cliInstalled = isPackageInstalled(CLI_PACKAGE) + const prismaNextInstalled = wantPrismaNext + ? isPackageInstalled(PRISMA_NEXT_PACKAGE) + : true - if (stackInstalled && cliInstalled) { + if (stackInstalled && cliInstalled && prismaNextInstalled) { p.log.success('Stack dependencies installed.') } else { const stillMissing = [ ...(stackInstalled ? [] : [`${STACK_PACKAGE} (prod)`]), + ...(prismaNextInstalled ? [] : [`${PRISMA_NEXT_PACKAGE} (prod)`]), ...(cliInstalled ? [] : [`${CLI_PACKAGE} (dev)`]), ] p.log.warn(`Still missing: ${stillMissing.join(', ')}.`) p.note( `You can retry manually:\n ${(failed.length ? failed : commands).join('\n ')}`, 'Manual Installation', ) }🤖 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/cli/src/commands/init/steps/install-deps.ts` around lines 100 - 117, The post-install verification only re-checks STACK_PACKAGE and CLI_PACKAGE; add a re-check for the Prisma Next package by calling isPackageInstalled for the prisma-next constant (e.g. PRISMA_NEXT_PACKAGE or `@cipherstash/prisma-next`) after installation, include its boolean (prismaNextInstalled) in the stillMissing array logic and warnings (so missing prisma-next shows up), and return it in the final state alongside stackInstalled and cliInstalled; update any use of failed/commands messaging to include prisma-next where appropriate.
🧹 Nitpick comments (6)
packages/prisma-next/src/stack/derive-schemas.ts (1)
140-142: ⚡ Quick winUse
hasOwnPropertyfor more precise flag validation.The
inoperator accepts inherited properties from the prototype chain. WhileObject.entries()inapplyTypeParamsprevents prototype keys from reaching this function in practice, usinghasOwnPropertyis more semantically correct and defensive.Suggested fix
function isCipherstashFlag(value: string): value is CipherstashFlag { - return value in FLAG_DISPATCH + return Object.prototype.hasOwnProperty.call(FLAG_DISPATCH, value) }🤖 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/prisma-next/src/stack/derive-schemas.ts` around lines 140 - 142, The isCipherstashFlag function uses the `in` operator which can match inherited prototype properties; update it to perform a direct own-property check against FLAG_DISPATCH (e.g. use Object.prototype.hasOwnProperty.call(FLAG_DISPATCH, value)) so only FLAG_DISPATCH's own keys are accepted. Locate the isCipherstashFlag function and replace the `return value in FLAG_DISPATCH` line with a hasOwnProperty-based check to make flag validation precise and defensive.packages/prisma-next/test/codec-runtime.test.ts (1)
213-217: ⚡ Quick winAdd explicit
undefinedcoverage in the null/undefined decode test.The test name promises both cases, but only asserts
null. Add a directundefinedassertion to keep this contract pinned.Suggested patch
- it('decode passes through null/undefined unchanged', async () => { + it('decode handles null and undefined ciphertext payloads', async () => { const codec = createCipherstashStringCodec(emptySdk()); const decoded = await codec.decode(null as unknown as string, ctxWithColumn('user', 'email')); expect(decoded.expose().ciphertext).toBeNull(); + + const decodedUndefined = await codec.decode( + undefined as unknown as string, + ctxWithColumn('user', 'email'), + ); + expect(decodedUndefined.expose().ciphertext).toBeUndefined(); });🤖 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/prisma-next/test/codec-runtime.test.ts` around lines 213 - 217, The test "decode passes through null/undefined unchanged" only asserts the null case; add an explicit undefined assertion: call createCipherstashStringCodec(emptySdk()) then await codec.decode(undefined as unknown as string, ctxWithColumn('user','email')) and assert decoded.expose().ciphertext is undefined (or toBeUndefined()) alongside the existing null check so both null and undefined are covered in the codec.decode test.packages/prisma-next/test/envelope-json.test.ts (1)
45-65: ⚡ Quick winAlign this read-side test with its stated signal/call-contract intent.
The test title says
decrypt({signal})behavior, but it never passes a signal or asserts SDK call args (ciphertext/table/column/signal). Please either rename the test or add the same call-shape assertion pattern used in sibling envelope 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/prisma-next/test/envelope-json.test.ts` around lines 45 - 65, The test title promises to verify decrypt({signal}) but never passes or asserts the signal or the SDK call shape; update the test that constructs EncryptedJson via EncryptedJson.fromInternal and then call envelope.decrypt({ signal }) with a created AbortSignal (e.g., from new AbortController().signal) and assert decryptMock was called once with an argument object containing the original ciphertext, table: 'audit', column: 'payload' and the same signal (and keep the existing expect(result).toBe(decoded)). This mirrors the sibling envelope tests' call-shape assertions and ensures decryptMock receives ciphertext/table/column/signal.packages/prisma-next/test/sdk-adapter.test.ts (2)
10-12: ⚡ Quick winInconsistent semicolon usage in imports.
Lines 10-12 are missing semicolons, while the rest of the file consistently uses them (e.g., lines 14, 233, 235). This inconsistency can lead to maintainability issues and potential ASI (Automatic Semicolon Insertion) pitfalls.
✨ Proposed fix
-import { encryptedColumn, encryptedTable } from '@cipherstash/stack/schema' -import type { EncryptionClient } from '@cipherstash/stack/client' -import { describe, expect, it, vi } from 'vitest' +import { encryptedColumn, encryptedTable } from '@cipherstash/stack/schema'; +import type { EncryptionClient } from '@cipherstash/stack/client'; +import { describe, expect, it, vi } from 'vitest';🤖 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/prisma-next/test/sdk-adapter.test.ts` around lines 10 - 12, The three import statements importing encryptedColumn, encryptedTable, EncryptionClient and Vitest symbols (describe, expect, it, vi) are missing trailing semicolons; update the import lines (the lines that import encryptedColumn/encryptedTable, the line importing type EncryptionClient, and the line importing from 'vitest') to include semicolons at the end to match the file's existing style and avoid ASI issues.
67-71: 💤 Low valueConsider extracting the valid envelope to a shared test fixture.
The
validEnvelopeconstant is used across multiple test cases (lines 204, 216, 217, 220). If the envelope structure needs to evolve, having it defined at the top level makes updates easier and clarifies the canonical shape for EQL v2 envelopes in 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/prisma-next/test/sdk-adapter.test.ts` around lines 67 - 71, Extract the inline validEnvelope constant into a shared test fixture so multiple tests can import and reuse the canonical EQL v2 envelope shape; create a new exported fixture (e.g., VALID_ENVELOPE) and replace the local const validEnvelope in sdk-adapter.test.ts and any other test files (where referenced at lines ~204, 216, 217, 220) with imports from that fixture, keeping the same object shape ({ v: 2, i: { t: 'users', c: 'email' }, c: 'ct-blob' }) so updates are centralized and tests continue to reference the same symbol.packages/prisma-next/tsconfig.json (1)
17-18: ⚡ Quick winConsider enabling unused code checks.
noUnusedLocalsandnoUnusedParametersare disabled, which can allow dead code to accumulate. Enabling these checks helps catch unused variables and parameters early, improving maintainability.♻️ Proposed enhancement
- "noUnusedLocals": false, - "noUnusedParameters": false, + "noUnusedLocals": true, + "noUnusedParameters": true,🤖 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/prisma-next/tsconfig.json` around lines 17 - 18, Enable TypeScript unused-code checks by setting "noUnusedLocals" and "noUnusedParameters" to true in the tsconfig (currently false); after enabling them, run the type checker and fix reported issues by removing or using unused variables/parameters, renaming intentionally unused parameters with a leading underscore, or adding explicit /* eslint-disable *//ts-ignore comments only when necessary; target the settings "noUnusedLocals" and "noUnusedParameters" in packages/prisma-next/tsconfig.json and resolve any reported unused symbol warnings across the codebase (functions, methods, and parameter lists) accordingly.
🤖 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 `@examples/prisma/.cipherstash/context.json`:
- Line 4: The generated context JSON contains a stale encryptionClientPath value
("./src/encryption/index.ts") that doesn't match this example's entrypoint;
update the "encryptionClientPath" field in .cipherstash/context.json to point to
the actual module used in this PR (e.g., "src/db.ts" or "./src/db.ts") so
tooling can locate the integration entrypoint (look for the encryptionClientPath
key in .cipherstash/context.json and replace the old path with the correct
src/db.ts path).
In `@examples/prisma/README.md`:
- Around line 46-50: Update the README to include a short "Native module
externalization" note stating that the native package "@cipherstash/protect-ffi"
must be externalized from bundlers and loaded at runtime via require (e.g.,
require('@cipherstash/protect-ffi')) and any ENV or platform notes needed; also
add an explicit "Running tests" section with the exact commands to run the
example's tests (install, emit/build, and the test runner commands such as pnpm
install && pnpm emit && pnpm test or the equivalent used in CI), and ensure
these sections appear alongside the existing setup/usage instructions referenced
near the pnpm install && pnpm emit && pnpm typecheck snippet.
In `@examples/prisma/src/index.ts`:
- Line 147: Remove logging of decrypted sensitive plaintext by eliminating
direct calls to decrypt() inside console.log; for occurrences like
row.email.decrypt(), row.salary.decrypt(), or any other .decrypt() usages in
console.log (e.g., the line with console.log(` ${row.id}: ${await
row.email.decrypt()}`) and the similar calls at lines referenced), replace the
output with non-sensitive indicators such as logging only row.id or a
masked/placeholder value (e.g., "[REDACTED_EMAIL]" or "salary: [REDACTED]") or a
boolean/summary (e.g., "hasEmail: true"), ensuring no decrypted plaintext is
ever written to logs.
In `@packages/prisma-next/package.json`:
- Around line 26-63: The exports map in package.json currently provides only
"import" (ESM) entries for subpaths like "./codec-types", "./column-types",
"./control", "./middleware", "./migration", "./operation-types", "./pack",
"./runtime", and "./stack" but tsup is building only ESM so no .cjs artifacts
exist; fix by updating tsup.config.ts to include 'cjs' in the format array (e.g.
format: ['esm','cjs']) so .cjs outputs are emitted, then add a "require":
"./dist/<name>.cjs" condition to each corresponding subpath in the package.json
"exports" map (for every "./codec-types", "./column-types", "./control",
"./middleware", "./migration", "./operation-types", "./pack", "./runtime",
"./stack") so CommonJS consumers can resolve the CJS bundles.
In `@packages/prisma-next/src/execution/cell-codec-factory.ts`:
- Around line 182-185: The runtime guidance string referencing the wrong package
should be updated: in the error message built in cell-codec-factory (the
template that includes `this.descriptor.codecId` and `this.#typeName`) replace
the incorrect import path `@prisma-next/extension-cipherstash/runtime` with the
package’s actual published runtime package name; keep the surrounding guidance
that mentions `createParameterizedCodecDescriptors(sdk)` and the example
`create${this.#typeName}Codec(sdk)` factory call intact so users are directed to
the correct module to import from.
- Around line 144-174: When encode encounters a pre-encrypt envelope
(handle.ciphertext === undefined) ensure we fail fast if no SDK is attached: add
a guard that if this.sdk === undefined you throw a
runtimeError('RUNTIME.ENCODE_FAILED', ...) describing that the codec was used
without an attached SDK and include this.descriptor.codecId, a reason like
'cipherstash-sdk-not-attached', and envelopeRouting with handle.table and
handle.column; keep the existing middleware-check branch (which uses
isBulkEncryptMiddlewareRegistered(this.sdk) and sets
this.#middlewareCheckPassed) but only run it when this.sdk is present so the
error for missing SDK is deterministic at the codec boundary.
In `@packages/prisma-next/src/exports/control.ts`:
- Around line 38-69: The import of baselineOps (import baselineOps ... ops.json)
referenced by CIPHERSTASH_BASELINE_MIGRATION_NAME is missing and will break
descriptor loading; either add the generated
migrations/20260601T0000_install_eql_bundle/ops.json artifact to the PR so
baselineOps is resolvable, or modify the contract-space wiring where
cipherstashContractSpace is built (the migrations array entry that uses
metadata: baselineMetadata and ops: baselineOps) to point ops to an existing
artifact/source (e.g., replace baselineOps with the correct JSON import or a
fallback loader) so module resolution succeeds.
In `@packages/prisma-next/src/stack/sdk-adapter.ts`:
- Around line 175-179: The validation currently checks typeof (value as { i: {
t?: unknown; c?: unknown } }).i === 'object' then uses 't' in ... and 'c' in ...
which will throw if i is null; update the conditional that builds valid (used in
sdk-adapter.ts) to explicitly ensure value.i is non-null (e.g. value.i !== null)
before doing the 't' in and 'c' in membership checks so a ciphertext with i:
null yields a controlled validation failure instead of a TypeError; keep the
surrounding isEncryptedPayload(value) check and the same property names (i, t,
c).
In `@packages/prisma-next/test/bundling-isolation.test.ts`:
- Around line 34-36: Update the incorrect package name in the top-of-file
comment: replace the reference to `@prisma-next/extension-cipherstash#test` with
the actual package name `@cipherstash/prisma-next#test` (matching the test
message used later, e.g. the string around line 187) so the turbo.json
dependency comment is accurate and not confusing to maintainers.
In `@packages/prisma-next/test/decrypt-all.test.ts`:
- Line 60: Remove the payload interpolation from the mock SDK error strings in
the test so plaintext-like values aren't logged: replace the dynamic message
that references args.ciphertext (the line throwing new Error(`mock SDK: cannot
decrypt: ${JSON.stringify(args.ciphertext)}`)) with a fixed diagnostic string
such as "mock SDK: cannot decrypt" (and make the same change for the similar
occurrence around the other failing throw at lines ~80), leaving the throw
behavior unchanged but eliminating embedding of sensitive payload data.
---
Outside diff comments:
In `@examples/prisma/package.json`:
- Around line 1-37: The package.json for the Prisma example lacks Node and pnpm
version pins; add an "engines" field specifying "node": ">=22" and add a
"packageManager" field set to "pnpm@9" (or "pnpm@9.x") at the top level of
package.json so the example enforces Node >=22 and pnpm 9.x. Update the
top-level JSON object (near "name", "version", etc.) to include these fields and
ensure the file remains valid JSON.
In `@packages/cli/src/commands/init/steps/install-deps.ts`:
- Around line 100-117: The post-install verification only re-checks
STACK_PACKAGE and CLI_PACKAGE; add a re-check for the Prisma Next package by
calling isPackageInstalled for the prisma-next constant (e.g.
PRISMA_NEXT_PACKAGE or `@cipherstash/prisma-next`) after installation, include its
boolean (prismaNextInstalled) in the stillMissing array logic and warnings (so
missing prisma-next shows up), and return it in the final state alongside
stackInstalled and cliInstalled; update any use of failed/commands messaging to
include prisma-next where appropriate.
---
Nitpick comments:
In `@packages/prisma-next/src/stack/derive-schemas.ts`:
- Around line 140-142: The isCipherstashFlag function uses the `in` operator
which can match inherited prototype properties; update it to perform a direct
own-property check against FLAG_DISPATCH (e.g. use
Object.prototype.hasOwnProperty.call(FLAG_DISPATCH, value)) so only
FLAG_DISPATCH's own keys are accepted. Locate the isCipherstashFlag function and
replace the `return value in FLAG_DISPATCH` line with a hasOwnProperty-based
check to make flag validation precise and defensive.
In `@packages/prisma-next/test/codec-runtime.test.ts`:
- Around line 213-217: The test "decode passes through null/undefined unchanged"
only asserts the null case; add an explicit undefined assertion: call
createCipherstashStringCodec(emptySdk()) then await codec.decode(undefined as
unknown as string, ctxWithColumn('user','email')) and assert
decoded.expose().ciphertext is undefined (or toBeUndefined()) alongside the
existing null check so both null and undefined are covered in the codec.decode
test.
In `@packages/prisma-next/test/envelope-json.test.ts`:
- Around line 45-65: The test title promises to verify decrypt({signal}) but
never passes or asserts the signal or the SDK call shape; update the test that
constructs EncryptedJson via EncryptedJson.fromInternal and then call
envelope.decrypt({ signal }) with a created AbortSignal (e.g., from new
AbortController().signal) and assert decryptMock was called once with an
argument object containing the original ciphertext, table: 'audit', column:
'payload' and the same signal (and keep the existing
expect(result).toBe(decoded)). This mirrors the sibling envelope tests'
call-shape assertions and ensures decryptMock receives
ciphertext/table/column/signal.
In `@packages/prisma-next/test/sdk-adapter.test.ts`:
- Around line 10-12: The three import statements importing encryptedColumn,
encryptedTable, EncryptionClient and Vitest symbols (describe, expect, it, vi)
are missing trailing semicolons; update the import lines (the lines that import
encryptedColumn/encryptedTable, the line importing type EncryptionClient, and
the line importing from 'vitest') to include semicolons at the end to match the
file's existing style and avoid ASI issues.
- Around line 67-71: Extract the inline validEnvelope constant into a shared
test fixture so multiple tests can import and reuse the canonical EQL v2
envelope shape; create a new exported fixture (e.g., VALID_ENVELOPE) and replace
the local const validEnvelope in sdk-adapter.test.ts and any other test files
(where referenced at lines ~204, 216, 217, 220) with imports from that fixture,
keeping the same object shape ({ v: 2, i: { t: 'users', c: 'email' }, c:
'ct-blob' }) so updates are centralized and tests continue to reference the same
symbol.
In `@packages/prisma-next/tsconfig.json`:
- Around line 17-18: Enable TypeScript unused-code checks by setting
"noUnusedLocals" and "noUnusedParameters" to true in the tsconfig (currently
false); after enabling them, run the type checker and fix reported issues by
removing or using unused variables/parameters, renaming intentionally unused
parameters with a leading underscore, or adding explicit /* eslint-disable
*//ts-ignore comments only when necessary; target the settings "noUnusedLocals"
and "noUnusedParameters" in packages/prisma-next/tsconfig.json and resolve any
reported unused symbol warnings across the codebase (functions, methods, and
parameter lists) accordingly.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 22f2d5d4-fc3f-4fb3-b4c2-f235afb8c4bb
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (132)
.changeset/add-prisma-next-integration.mdexamples/prisma/.cipherstash/context.jsonexamples/prisma/.env.exampleexamples/prisma/README.mdexamples/prisma/docker-compose.ymlexamples/prisma/migrations/app/20260513T1735_initial/end-contract.d.tsexamples/prisma/migrations/app/20260513T1735_initial/end-contract.jsonexamples/prisma/migrations/app/20260513T1735_initial/migration.jsonexamples/prisma/migrations/app/20260513T1735_initial/migration.tsexamples/prisma/migrations/app/20260513T1735_initial/ops.jsonexamples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/contract.jsonexamples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/migration.jsonexamples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/ops.jsonexamples/prisma/migrations/cipherstash/contract.d.tsexamples/prisma/migrations/cipherstash/contract.jsonexamples/prisma/migrations/cipherstash/refs/head.jsonexamples/prisma/package.jsonexamples/prisma/prisma-next.config.tsexamples/prisma/prisma/schema.prismaexamples/prisma/src/db.tsexamples/prisma/src/index.tsexamples/prisma/src/prisma/contract.d.tsexamples/prisma/src/prisma/contract.jsonexamples/prisma/tsconfig.jsonpackage.jsonpackages/cli/src/bin/stash.tspackages/cli/src/commands/db/detect.tspackages/cli/src/commands/init/index.tspackages/cli/src/commands/init/providers/__tests__/prisma-next.test.tspackages/cli/src/commands/init/providers/prisma-next.tspackages/cli/src/commands/init/steps/build-schema.tspackages/cli/src/commands/init/steps/install-deps.tspackages/cli/src/commands/init/steps/install-eql.tspackages/cli/src/commands/init/types.tspackages/prisma-next/DEVELOPING.mdpackages/prisma-next/README.mdpackages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.tspackages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.jsonpackages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.jsonpackages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.tspackages/prisma-next/migrations/20260601T0000_install_eql_bundle/ops.jsonpackages/prisma-next/migrations/refs/head.jsonpackages/prisma-next/package.jsonpackages/prisma-next/prisma-next.config.tspackages/prisma-next/src/contract-authoring.tspackages/prisma-next/src/contract.d.tspackages/prisma-next/src/contract.jsonpackages/prisma-next/src/contract.prismapackages/prisma-next/src/execution/abort.tspackages/prisma-next/src/execution/cell-codec-factory.tspackages/prisma-next/src/execution/codec-runtime.tspackages/prisma-next/src/execution/decrypt-all.tspackages/prisma-next/src/execution/envelope-base.tspackages/prisma-next/src/execution/envelope-bigint.tspackages/prisma-next/src/execution/envelope-boolean.tspackages/prisma-next/src/execution/envelope-date.tspackages/prisma-next/src/execution/envelope-double.tspackages/prisma-next/src/execution/envelope-json.tspackages/prisma-next/src/execution/envelope-string.tspackages/prisma-next/src/execution/helpers.tspackages/prisma-next/src/execution/middleware-registry.tspackages/prisma-next/src/execution/operators.tspackages/prisma-next/src/execution/parameterized.tspackages/prisma-next/src/execution/routing.tspackages/prisma-next/src/execution/sdk.tspackages/prisma-next/src/exports/codec-types.tspackages/prisma-next/src/exports/column-types.tspackages/prisma-next/src/exports/contract-space-typing.tspackages/prisma-next/src/exports/control.tspackages/prisma-next/src/exports/middleware.tspackages/prisma-next/src/exports/migration.tspackages/prisma-next/src/exports/operation-types.tspackages/prisma-next/src/exports/pack.tspackages/prisma-next/src/exports/runtime.tspackages/prisma-next/src/exports/stack.tspackages/prisma-next/src/extension-metadata/codec-metadata.tspackages/prisma-next/src/extension-metadata/constants.tspackages/prisma-next/src/extension-metadata/descriptor-meta.tspackages/prisma-next/src/middleware/bulk-encrypt.tspackages/prisma-next/src/migration/call-classes.tspackages/prisma-next/src/migration/cipherstash-codec.tspackages/prisma-next/src/migration/codec-hooks-factory.tspackages/prisma-next/src/migration/eql-bundle.tspackages/prisma-next/src/migration/eql-install.generated.tspackages/prisma-next/src/stack/derive-schemas.tspackages/prisma-next/src/stack/from-stack.tspackages/prisma-next/src/stack/sdk-adapter.tspackages/prisma-next/src/types/codec-types.tspackages/prisma-next/src/types/operation-types.tspackages/prisma-next/test/abort.test.tspackages/prisma-next/test/authoring.test.tspackages/prisma-next/test/bulk-encrypt-middleware.test.tspackages/prisma-next/test/bundling-isolation.test.tspackages/prisma-next/test/call-classes.test.tspackages/prisma-next/test/call-classes.types.test-d.tspackages/prisma-next/test/cipherstash-codec-numeric.test.tspackages/prisma-next/test/cipherstash-codec-other-codecs.test.tspackages/prisma-next/test/cipherstash-codec-string.test.tspackages/prisma-next/test/cipherstash-codec.test.tspackages/prisma-next/test/codec-runtime.test.tspackages/prisma-next/test/column-types.test.tspackages/prisma-next/test/decrypt-all.test.tspackages/prisma-next/test/derive-schemas.test.tspackages/prisma-next/test/descriptor.test.tspackages/prisma-next/test/envelope-bigint.test.tspackages/prisma-next/test/envelope-boolean.test.tspackages/prisma-next/test/envelope-date.test.tspackages/prisma-next/test/envelope-double.test.tspackages/prisma-next/test/envelope-json.test.tspackages/prisma-next/test/envelope-string.test.tspackages/prisma-next/test/envelope.types.test-d.tspackages/prisma-next/test/equality-trait-removal.test.tspackages/prisma-next/test/from-stack-divergence.test.tspackages/prisma-next/test/helpers.test.tspackages/prisma-next/test/helpers.types.test-d.tspackages/prisma-next/test/operation-types.types.test-d.tspackages/prisma-next/test/operator-lowering-equality.test.tspackages/prisma-next/test/operator-lowering-order-range.test.tspackages/prisma-next/test/operator-lowering-text-search.test.tspackages/prisma-next/test/operator-lowering.helpers.tspackages/prisma-next/test/operator-lowering.test.tspackages/prisma-next/test/psl-interpretation-numeric.test.tspackages/prisma-next/test/psl-interpretation-other-types.test.tspackages/prisma-next/test/psl-interpretation.test.tspackages/prisma-next/test/routing.test.tspackages/prisma-next/test/runtime-descriptor.test.tspackages/prisma-next/test/sdk-adapter.test.tspackages/prisma-next/test/sdk.types.test-d.tspackages/prisma-next/tsconfig.jsonpackages/prisma-next/tsup.config.tspackages/prisma-next/vitest.config.tspnpm-workspace.yaml
| { | ||
| "cliVersion": "0.14.0", | ||
| "integration": "prisma-next", | ||
| "encryptionClientPath": "./src/encryption/index.ts", |
There was a problem hiding this comment.
Fix stale encryptionClientPath in generated context.
"./src/encryption/index.ts" does not match the example layout in this PR (src/db.ts). Keeping this stale path can break tooling that relies on context metadata to locate integration entrypoints.
Suggested fix
- "encryptionClientPath": "./src/encryption/index.ts",
+ "encryptionClientPath": "./src/db.ts",📝 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.
| "encryptionClientPath": "./src/encryption/index.ts", | |
| "encryptionClientPath": "./src/db.ts", |
🤖 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 `@examples/prisma/.cipherstash/context.json` at line 4, The generated context
JSON contains a stale encryptionClientPath value ("./src/encryption/index.ts")
that doesn't match this example's entrypoint; update the "encryptionClientPath"
field in .cipherstash/context.json to point to the actual module used in this PR
(e.g., "src/db.ts" or "./src/db.ts") so tooling can locate the integration
entrypoint (look for the encryptionClientPath key in .cipherstash/context.json
and replace the old path with the correct src/db.ts path).
| Or, to just verify the example typechecks and emits a valid contract (no database, no workspace): | ||
|
|
||
| ```bash | ||
| pnpm install && pnpm emit && pnpm typecheck | ||
| ``` |
There was a problem hiding this comment.
Add required README sections for native-module externalization and test execution.
This README still misses two required items: a note that @cipherstash/protect-ffi must be externalized and loaded via runtime require, plus explicit “how to run tests” instructions.
As per coding guidelines, "Each example app must include a README covering: setup (env vars, install, run commands), notes on native module externalization, and how to run tests".
Also applies to: 89-93
🤖 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 `@examples/prisma/README.md` around lines 46 - 50, Update the README to include
a short "Native module externalization" note stating that the native package
"@cipherstash/protect-ffi" must be externalized from bundlers and loaded at
runtime via require (e.g., require('@cipherstash/protect-ffi')) and any ENV or
platform notes needed; also add an explicit "Running tests" section with the
exact commands to run the example's tests (install, emit/build, and the test
runner commands such as pnpm install && pnpm emit && pnpm test or the equivalent
used in CI), and ensure these sections appear alongside the existing setup/usage
instructions referenced near the pnpm install && pnpm emit && pnpm typecheck
snippet.
| "exports": { | ||
| "./codec-types": { | ||
| "types": "./dist/codec-types.d.ts", | ||
| "import": "./dist/codec-types.js" | ||
| }, | ||
| "./column-types": { | ||
| "types": "./dist/column-types.d.ts", | ||
| "import": "./dist/column-types.js" | ||
| }, | ||
| "./control": { | ||
| "types": "./dist/control.d.ts", | ||
| "import": "./dist/control.js" | ||
| }, | ||
| "./middleware": { | ||
| "types": "./dist/middleware.d.ts", | ||
| "import": "./dist/middleware.js" | ||
| }, | ||
| "./migration": { | ||
| "types": "./dist/migration.d.ts", | ||
| "import": "./dist/migration.js" | ||
| }, | ||
| "./operation-types": { | ||
| "types": "./dist/operation-types.d.ts", | ||
| "import": "./dist/operation-types.js" | ||
| }, | ||
| "./pack": { | ||
| "types": "./dist/pack.d.ts", | ||
| "import": "./dist/pack.js" | ||
| }, | ||
| "./runtime": { | ||
| "types": "./dist/runtime.d.ts", | ||
| "import": "./dist/runtime.js" | ||
| }, | ||
| "./stack": { | ||
| "types": "./dist/stack.d.ts", | ||
| "import": "./dist/stack.js" | ||
| }, | ||
| "./package.json": "./package.json" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify which package manifests have subpath exports with import but no require.
python - <<'PY'
import json, glob
for path in sorted(glob.glob("packages/*/package.json")):
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
exports = data.get("exports")
if not isinstance(exports, dict):
continue
missing = []
for key, value in exports.items():
if isinstance(value, dict) and "import" in value and "require" not in value:
missing.append(key)
if missing:
print(f"{path}: missing require for {', '.join(missing)}")
PYRepository: cipherstash/stack
Length of output: 231
🏁 Script executed:
# Check build configuration and .cjs file existence in packages/prisma-next
cd packages/prisma-next
echo "=== Contents of packages/prisma-next/ ==="
ls -la
echo -e "\n=== Build script from package.json ==="
cat package.json | grep -A 2 '"scripts"'
echo -e "\n=== Check dist directory for .cjs files ==="
if [ -d "dist" ]; then
ls -la dist/ | head -20
else
echo "dist directory does not exist"
fi
echo -e "\n=== Look for build configuration files ==="
ls -la | grep -E "(tsconfig|esbuild|rollup|webpack|vite)"
echo -e "\n=== Check for build config in root ==="
ls -la ../../ | grep -E "(tsconfig|esbuild|rollup|webpack|vite|turbo)"Repository: cipherstash/stack
Length of output: 1373
🏁 Script executed:
# Check the tsup.config.ts to see what output formats are configured
cat packages/prisma-next/tsup.config.tsRepository: cipherstash/stack
Length of output: 537
The proposed fix is incomplete—CJS output is not being generated.
The exports map violates the coding guideline (ESM-only breaks CJS compatibility). However, the proposed "require": "./dist/[name].cjs" conditions won't work because the build configuration (tsup.config.ts) is set to format: ['esm'] and does not generate .cjs files.
To restore CJS compatibility, you must:
- Update
tsup.config.tsto include'cjs'in theformatarray. - Add
"require"conditions to each subpath export inpackage.json.
🧰 Tools
🪛 GitHub Actions: Test JS / 0_Run Tests.txt
[error] Test step failed. Command 'pnpm run test' exited with code 1 in /home/runner/_work/stack/stack/packages/prisma-next.
🪛 GitHub Actions: Test JS / Run Tests
[error] Test command failed: pnpm run test (vitest run) exited with code 1 due to 6 failing 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/prisma-next/package.json` around lines 26 - 63, The exports map in
package.json currently provides only "import" (ESM) entries for subpaths like
"./codec-types", "./column-types", "./control", "./middleware", "./migration",
"./operation-types", "./pack", "./runtime", and "./stack" but tsup is building
only ESM so no .cjs artifacts exist; fix by updating tsup.config.ts to include
'cjs' in the format array (e.g. format: ['esm','cjs']) so .cjs outputs are
emitted, then add a "require": "./dist/<name>.cjs" condition to each
corresponding subpath in the package.json "exports" map (for every
"./codec-types", "./column-types", "./control", "./middleware", "./migration",
"./operation-types", "./pack", "./runtime", "./stack") so CommonJS consumers can
resolve the CJS bundles.
| if (handle.ciphertext === undefined) { | ||
| // Misconfig diagnostic: when an SDK-bound codec sees a pre-encrypt | ||
| // envelope but no `bulkEncryptMiddleware(sdk)` has been | ||
| // constructed against that same SDK, the two-pass flow can never | ||
| // complete. Throw at the codec boundary with a copy-pasteable | ||
| // wiring snippet rather than letting the envelope reach the pg | ||
| // driver and produce an opaque serialise error. | ||
| if (!this.#middlewareCheckPassed && this.sdk !== undefined) { | ||
| if (!isBulkEncryptMiddlewareRegistered(this.sdk)) { | ||
| throw runtimeError( | ||
| 'RUNTIME.ENCODE_FAILED', | ||
| `cipherstash ${this.descriptor.codecId}: encrypted column value has not been encrypted, ` + | ||
| 'and no `bulkEncryptMiddleware(sdk)` has been registered with this SDK. ' + | ||
| 'Wire it up alongside the extension descriptor:\n\n' + | ||
| ' postgres<Contract>({\n' + | ||
| ' contractJson,\n' + | ||
| ' extensions: [createCipherstashRuntimeDescriptor({ sdk })],\n' + | ||
| ' middleware: [bulkEncryptMiddleware(sdk)],\n' + | ||
| ' });\n\n' + | ||
| 'Both must close over the SAME `sdk` reference. See the @cipherstash/prisma-next README for the full wiring example.', | ||
| { | ||
| codecId: this.descriptor.codecId, | ||
| reason: 'cipherstash-bulk-encrypt-middleware-not-registered', | ||
| envelopeRouting: { table: handle.table, column: handle.column }, | ||
| }, | ||
| ); | ||
| } | ||
| this.#middlewareCheckPassed = true; | ||
| } | ||
| return value; | ||
| } |
There was a problem hiding this comment.
Fail fast when encode runs without an attached SDK.
decode already guards metadata-only codec instances, but encode currently allows the pre-encrypt envelope sentinel to flow through when this.sdk is undefined. That yields a late/opaque failure path instead of a deterministic runtime error at the codec boundary.
Suggested fix
const handle = value.expose();
if (handle.ciphertext === undefined) {
+ if (this.sdk === undefined) {
+ throw runtimeError(
+ 'RUNTIME.ENCODE_FAILED',
+ `cipherstash ${this.descriptor.codecId}: encode invoked on a metadata-only codec instance that has no SDK attached.`,
+ {
+ codecId: this.descriptor.codecId,
+ reason: 'cipherstash-sdk-required',
+ },
+ );
+ }
- if (!this.#middlewareCheckPassed && this.sdk !== undefined) {
+ if (!this.#middlewareCheckPassed) {
if (!isBulkEncryptMiddlewareRegistered(this.sdk)) {
throw runtimeError(📝 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.
| if (handle.ciphertext === undefined) { | |
| // Misconfig diagnostic: when an SDK-bound codec sees a pre-encrypt | |
| // envelope but no `bulkEncryptMiddleware(sdk)` has been | |
| // constructed against that same SDK, the two-pass flow can never | |
| // complete. Throw at the codec boundary with a copy-pasteable | |
| // wiring snippet rather than letting the envelope reach the pg | |
| // driver and produce an opaque serialise error. | |
| if (!this.#middlewareCheckPassed && this.sdk !== undefined) { | |
| if (!isBulkEncryptMiddlewareRegistered(this.sdk)) { | |
| throw runtimeError( | |
| 'RUNTIME.ENCODE_FAILED', | |
| `cipherstash ${this.descriptor.codecId}: encrypted column value has not been encrypted, ` + | |
| 'and no `bulkEncryptMiddleware(sdk)` has been registered with this SDK. ' + | |
| 'Wire it up alongside the extension descriptor:\n\n' + | |
| ' postgres<Contract>({\n' + | |
| ' contractJson,\n' + | |
| ' extensions: [createCipherstashRuntimeDescriptor({ sdk })],\n' + | |
| ' middleware: [bulkEncryptMiddleware(sdk)],\n' + | |
| ' });\n\n' + | |
| 'Both must close over the SAME `sdk` reference. See the @cipherstash/prisma-next README for the full wiring example.', | |
| { | |
| codecId: this.descriptor.codecId, | |
| reason: 'cipherstash-bulk-encrypt-middleware-not-registered', | |
| envelopeRouting: { table: handle.table, column: handle.column }, | |
| }, | |
| ); | |
| } | |
| this.#middlewareCheckPassed = true; | |
| } | |
| return value; | |
| } | |
| if (handle.ciphertext === undefined) { | |
| // Misconfig diagnostic: when an SDK-bound codec sees a pre-encrypt | |
| // envelope but no `bulkEncryptMiddleware(sdk)` has been | |
| // constructed against that same SDK, the two-pass flow can never | |
| // complete. Throw at the codec boundary with a copy-pasteable | |
| // wiring snippet rather than letting the envelope reach the pg | |
| // driver and produce an opaque serialise error. | |
| if (this.sdk === undefined) { | |
| throw runtimeError( | |
| 'RUNTIME.ENCODE_FAILED', | |
| `cipherstash ${this.descriptor.codecId}: encode invoked on a metadata-only codec instance that has no SDK attached.`, | |
| { | |
| codecId: this.descriptor.codecId, | |
| reason: 'cipherstash-sdk-required', | |
| }, | |
| ); | |
| } | |
| if (!this.#middlewareCheckPassed) { | |
| if (!isBulkEncryptMiddlewareRegistered(this.sdk)) { | |
| throw runtimeError( | |
| 'RUNTIME.ENCODE_FAILED', | |
| `cipherstash ${this.descriptor.codecId}: encrypted column value has not been encrypted, ` + | |
| 'and no `bulkEncryptMiddleware(sdk)` has been registered with this SDK. ' + | |
| 'Wire it up alongside the extension descriptor:\n\n' + | |
| ' postgres<Contract>({\n' + | |
| ' contractJson,\n' + | |
| ' extensions: [createCipherstashRuntimeDescriptor({ sdk })],\n' + | |
| ' middleware: [bulkEncryptMiddleware(sdk)],\n' + | |
| ' });\n\n' + | |
| 'Both must close over the SAME `sdk` reference. See the `@cipherstash/prisma-next` README for the full wiring example.', | |
| { | |
| codecId: this.descriptor.codecId, | |
| reason: 'cipherstash-bulk-encrypt-middleware-not-registered', | |
| envelopeRouting: { table: handle.table, column: handle.column }, | |
| }, | |
| ); | |
| } | |
| this.#middlewareCheckPassed = true; | |
| } | |
| return value; | |
| } |
🧰 Tools
🪛 GitHub Actions: Test JS / Run Tests
[error] Test command failed: pnpm run test (vitest run) exited with code 1 due to 6 failing 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/prisma-next/src/execution/cell-codec-factory.ts` around lines 144 -
174, When encode encounters a pre-encrypt envelope (handle.ciphertext ===
undefined) ensure we fail fast if no SDK is attached: add a guard that if
this.sdk === undefined you throw a runtimeError('RUNTIME.ENCODE_FAILED', ...)
describing that the codec was used without an attached SDK and include
this.descriptor.codecId, a reason like 'cipherstash-sdk-not-attached', and
envelopeRouting with handle.table and handle.column; keep the existing
middleware-check branch (which uses isBulkEncryptMiddlewareRegistered(this.sdk)
and sets this.#middlewareCheckPassed) but only run it when this.sdk is present
so the error for missing SDK is deterministic at the codec boundary.
| import baselineOps from '../../migrations/20260601T0000_install_eql_bundle/ops.json' with { | ||
| type: 'json', | ||
| }; | ||
| import headRef from '../../migrations/refs/head.json' with { type: 'json' }; | ||
| import contractJson from '../contract.json' with { type: 'json' }; | ||
| import { | ||
| CIPHERSTASH_BASELINE_MIGRATION_NAME, | ||
| CIPHERSTASH_BIGINT_CODEC_ID, | ||
| CIPHERSTASH_BOOLEAN_CODEC_ID, | ||
| CIPHERSTASH_DATE_CODEC_ID, | ||
| CIPHERSTASH_DOUBLE_CODEC_ID, | ||
| CIPHERSTASH_JSON_CODEC_ID, | ||
| CIPHERSTASH_STRING_CODEC_ID, | ||
| } from '../extension-metadata/constants'; | ||
| import { cipherstashPackMeta } from '../extension-metadata/descriptor-meta'; | ||
| import { | ||
| cipherstashBigIntCodecHooks, | ||
| cipherstashBooleanCodecHooks, | ||
| cipherstashDateCodecHooks, | ||
| cipherstashDoubleCodecHooks, | ||
| cipherstashJsonCodecHooks, | ||
| cipherstashStringCodecHooks, | ||
| } from '../migration/cipherstash-codec'; | ||
|
|
||
| const cipherstashContractSpace = contractSpaceFromJson<Contract<SqlStorage>>({ | ||
| contractJson, | ||
| migrations: [ | ||
| { | ||
| dirName: CIPHERSTASH_BASELINE_MIGRATION_NAME, | ||
| metadata: baselineMetadata, | ||
| ops: baselineOps, | ||
| }, |
There was a problem hiding this comment.
Missing migration artifact import will break descriptor loading.
Line 38 imports ops.json, but this PR’s migration artifact set for packages/prisma-next/migrations/20260601T0000_install_eql_bundle/ does not include that file. This will fail module resolution when loading the control descriptor.
Please either add the generated ops.json artifact for that migration directory or update the contract-space wiring to use an available source of operations.
🧰 Tools
🪛 GitHub Actions: Test JS / Run Tests
[error] Test command failed: pnpm run test (vitest run) exited with code 1 due to 6 failing 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/prisma-next/src/exports/control.ts` around lines 38 - 69, The import
of baselineOps (import baselineOps ... ops.json) referenced by
CIPHERSTASH_BASELINE_MIGRATION_NAME is missing and will break descriptor
loading; either add the generated
migrations/20260601T0000_install_eql_bundle/ops.json artifact to the PR so
baselineOps is resolvable, or modify the contract-space wiring where
cipherstashContractSpace is built (the migrations array entry that uses
metadata: baselineMetadata and ops: baselineOps) to point ops to an existing
artifact/source (e.g., replace baselineOps with the correct JSON import or a
fallback loader) so module resolution succeeds.
| isEncryptedPayload(value) && | ||
| typeof (value as { i: { t?: unknown; c?: unknown } }).i === 'object' && | ||
| 't' in (value as { i: object }).i && | ||
| 'c' in (value as { i: object }).i | ||
| if (!valid) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n packages/prisma-next/src/stack/sdk-adapter.ts | sed -n '170,185p'Repository: cipherstash/stack
Length of output: 909
🌐 Web query:
javascript typeof null object in operator property check
💡 Result:
In JavaScript, typeof null returns "object", a longstanding bug from the language's original implementation where null was represented as 0x00 (all zero bits), sharing the type tag 000 with objects.[1][2][3] Despite this, null is a primitive Null type value, not an object.[4][5] The in operator checks if a property exists in an object or its prototype chain, but throws a TypeError if the right-hand side is not an Object (i.e., a primitive like null).[6][7][8] Example: "prop" in null; // TypeError: cannot use 'in' operator to search for 'prop' in null This occurs because the spec requires: if Type(rval) is not Object, throw TypeError.[8] typeof null === "object" does not make null an object; in verifies the actual type internally.[6][7] To safely check properties, first guard against null/undefined: if (value != null && "prop" in value) { // safe } Or use Object.hasOwn(value, "prop") in modern JS, which returns false for null without error.[6]
Citations:
- 1: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
- 2: https://github.com/mdn/content/blob/main/files/en-us/web/javascript/reference/operators/typeof/index.md
- 3: https://stackoverflow.com/questions/18808226/why-is-typeof-null-object
- 4: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/null
- 5: https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html
- 6: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in
- 7: http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/in_operator_no_object
- 8: https://stackoverflow.com/questions/36878397/how-can-i-detect-whether-it-is-safe-to-use-in-operator-in-javascript
Add null-check for i before property-membership checks.
A ciphertext with i: null will throw a TypeError during the 't' in ... and 'c' in ... operations (since typeof null === "object" but the in operator requires an actual Object) instead of returning your controlled validation error.
Suggested fix
function ensureEncryptedEnvelope(
value: unknown,
kind: 'decrypt' | 'bulkDecrypt',
index?: number,
): Encrypted {
+ const info = (value as { i?: unknown }).i
const valid =
isEncryptedPayload(value) &&
- typeof (value as { i: { t?: unknown; c?: unknown } }).i === 'object' &&
- 't' in (value as { i: object }).i &&
- 'c' in (value as { i: object }).i
+ info !== null &&
+ typeof info === 'object' &&
+ 't' in info &&
+ 'c' in info📝 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.
| isEncryptedPayload(value) && | |
| typeof (value as { i: { t?: unknown; c?: unknown } }).i === 'object' && | |
| 't' in (value as { i: object }).i && | |
| 'c' in (value as { i: object }).i | |
| if (!valid) { | |
| function ensureEncryptedEnvelope( | |
| value: unknown, | |
| kind: 'decrypt' | 'bulkDecrypt', | |
| index?: number, | |
| ): Encrypted { | |
| const info = (value as { i?: unknown }).i | |
| const valid = | |
| isEncryptedPayload(value) && | |
| info !== null && | |
| typeof info === 'object' && | |
| 't' in info && | |
| 'c' in info | |
| if (!valid) { |
🧰 Tools
🪛 GitHub Actions: Test JS / Run Tests
[error] Test command failed: pnpm run test (vitest run) exited with code 1 due to 6 failing 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/prisma-next/src/stack/sdk-adapter.ts` around lines 175 - 179, The
validation currently checks typeof (value as { i: { t?: unknown; c?: unknown }
}).i === 'object' then uses 't' in ... and 'c' in ... which will throw if i is
null; update the conditional that builds valid (used in sdk-adapter.ts) to
explicitly ensure value.i is non-null (e.g. value.i !== null) before doing the
't' in and 'c' in membership checks so a ciphertext with i: null yields a
controlled validation failure instead of a TypeError; keep the surrounding
isEncryptedPayload(value) check and the same property names (i, t, c).
| singleDecryptCalls.push(args); | ||
| const ct = args.ciphertext as { c?: string } | null; | ||
| if (!ct || typeof ct.c !== 'string' || !ct.c.startsWith('ct:')) { | ||
| throw new Error(`mock SDK: cannot decrypt: ${JSON.stringify(args.ciphertext)}`); |
There was a problem hiding this comment.
Remove payload interpolation from mock SDK error strings.
These messages currently include stringified payloads, which can leak plaintext-like test values into logs/output. Prefer fixed diagnostics without embedding payload contents.
Suggested patch
- throw new Error(`mock SDK: cannot decrypt: ${JSON.stringify(args.ciphertext)}`);
+ throw new Error('mock SDK: cannot decrypt ciphertext payload');
@@
- throw new Error(`mock SDK: cannot bulk-decrypt: ${JSON.stringify(ciphertext)}`);
+ throw new Error('mock SDK: cannot bulk-decrypt ciphertext payload');Also applies to: 80-80
🧰 Tools
🪛 GitHub Actions: Test JS / Run Tests
[error] Test command failed: pnpm run test (vitest run) exited with code 1 due to 6 failing 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/prisma-next/test/decrypt-all.test.ts` at line 60, Remove the payload
interpolation from the mock SDK error strings in the test so plaintext-like
values aren't logged: replace the dynamic message that references
args.ciphertext (the line throwing new Error(`mock SDK: cannot decrypt:
${JSON.stringify(args.ciphertext)}`)) with a fixed diagnostic string such as
"mock SDK: cannot decrypt" (and make the same change for the similar occurrence
around the other failing throw at lines ~80), leaving the throw behavior
unchanged but eliminating embedding of sensitive payload data.
|
Reviewed end-to-end. Architecture and batching design are solid — cipher-side batching by 1. Microtask coalescer inside
|
| @@ -0,0 +1,17 @@ | |||
| { | |||
There was a problem hiding this comment.
Did you intend to commit this file?
| // The CipherStash EQL bundle hits a SQL-injection-style bug in | ||
| // `eql_v2.add_encrypted_constraint` when the table or column name | ||
| // needs quoting (reserved words, mixed case, etc.). `user` is | ||
| // reserved in Postgres, so we map the table to `users`; the | ||
| // mixed-case columns are mapped to lowercase column names so the | ||
| // bundle's `%I` interpolation renders them unquoted. Drop these | ||
| // overrides once the upstream bundle bug is fixed. | ||
| @@map("users") |
There was a problem hiding this comment.
What is this referring to? Is there a an issue filed in EQL @calvinbrewer?
| * | ||
| * `cipherstashFromStack({ contractJson })` derives the encryption | ||
| * schemas from the contract, constructs the `@cipherstash/stack` | ||
| * `EncryptionClient` against your `CS_*` env vars, builds the SDK |
There was a problem hiding this comment.
"EncryptionClientagainst yourCS_*` env vars, builds the SDK"
Change to:
"EncryptionClientagainst yourCS_*` env vars or local profile, builds the SDK"
| import type { Contract } from './prisma/contract.d' | ||
| import contractJson from './prisma/contract.json' with { type: 'json' } | ||
|
|
||
| const cipherstash = await cipherstashFromStack({ contractJson }) |
There was a problem hiding this comment.
I find this function name confusing: cipherstashFromStack implies that its loading/building something from the cipherstash stack but it seems to be some kind of adapter for the Prisma contract.
CipherStash is the stack, not something you get from the stack.
This is also quite different to the example if the draft blog post - how does it relate?
| await decryptAll(rows) | ||
| for (const row of rows) { | ||
| console.log(` ${row.id}: ${await row.email.decrypt()}`) | ||
| } |
There was a problem hiding this comment.
This code isn't right. It calls decryptAll, ignores the result and then decrypts each item using a single value call (many ZeroKMS requests). See my general comments about a dataloader type pattern which would avoid this footgun, too.
| console.log(`Found ${rows.length} user(s) with salary > 100,000.`) | ||
| await decryptAll(rows) | ||
| for (const row of rows) { | ||
| console.log(` ${row.id}: salary=${await row.salary.decrypt()}`) | ||
| } |
|
|
||
| # CipherStash workspace credentials — **deployment only**. | ||
| # | ||
| # For local development, run `stash auth login` once. The PKCE flow |
| * comparison functions (`eql_v2.eq`, `eql_v2.ilike`) on | ||
| * `cipherstash/string@1`-typed columns. The lowering shape mirrors the | ||
| * canonical templates in the reference Prisma integration at | ||
| * `reference/cipherstash/stack/packages/stack/src/prisma/core/ |
There was a problem hiding this comment.
This seems to refer to a directory outside of this repo
| function eqlOperator(publicMethod: string, eqlFunction: 'eq' | 'ilike'): SqlOperationDescriptor { | ||
| return { | ||
| self: { codecId: CIPHERSTASH_STRING_CODEC_ID }, | ||
| impl: (self: Expression<ScopeField>, value: unknown): Expression<PgBoolReturn> => { | ||
| const selfCodec = requireSelfCodec(self, publicMethod); | ||
| const selfAst = toExpr(self, selfCodec); | ||
| return buildOperation({ | ||
| method: publicMethod, | ||
| args: [selfAst, asEncryptedParam(selfAst, selfCodec, value)], | ||
| returns: { codecId: PG_BOOL_CODEC_ID, nullable: false }, | ||
| lowering: { | ||
| targetFamily: 'sql', | ||
| strategy: 'function', | ||
| template: `eql_v2.${eqlFunction}({{self}}, {{arg0}})`, | ||
| }, | ||
| }); | ||
| }, | ||
| }; | ||
| } |
There was a problem hiding this comment.
Why is this needed? The eql functions don't need to be called directly (and arguably shouldn't).
eql_v2.like and eql_v2.ilike are the only exceptions (like is missing), = should use a standard operator (same as >, <, >= etc).
| * The framework`s built-in `email.eq(...)` is **not reachable** on | ||
| * cipherstash columns: the cipherstash codec declares no `equality` | ||
| * trait (see `codec-runtime.ts` / `codec-metadata.ts` / `parameterized.ts`), | ||
| * and the model-accessor synthesis in `sql-orm-client` gates | ||
| * `COMPARISON_METHODS_META.eq` on the `equality` trait being present in | ||
| * the column codec`s trait set. Calling `email.eq(...)` on a cipherstash | ||
| * column is therefore `undefined` — the wrong-SQL footgun (where the | ||
| * built-in `eq` would lower to standard SQL `=` against an | ||
| * `eql_v2_encrypted` value, silently returning zero rows because EQL | ||
| * ciphers contain randomized nonces) is closed at the codec layer, not | ||
| * the operator layer. The trait declaration is regression-pinned by | ||
| * `test/equality-trait-removal.test.ts`. |
There was a problem hiding this comment.
This isn't correct. Calling = on an eql_v2_encrypted type is valid and preferred (so long as the argument is correctly encrypted).
I don't have a good understanding of how the codecs work in Prisma Next so I don't know if the operator overrides (cipherstashEq instead of eq) need to stay in place anyway, but technically we don't need to modify the operator itself, just encrypt the argument.
At the very least, the descriptive comment here should be corrected.
There was a problem hiding this comment.
Also, we should use this as an opportunity to start using match instead of like/ilike. EQL 2.3 will move to this as well.
The reason is that the behaviour is different to like as it doesn't correctly support wildcards. Its closer in behaviour to tsearch in postgres.
|
One more — stale doc references that won't resolve for public consumers. Several comments link to Prisma Next internal docs that aren't reachable from this repo.
Options: strip the Also worth reconciling ADR 211 vs ADR 212 between |
| * Public middleware surface for the cipherstash extension. | ||
| * | ||
| * Consumers register the bulk-encrypt middleware in their runtime so | ||
| * `EncryptedString` envelopes embedded in `INSERT` / `UPDATE` plans get | ||
| * encrypted in batches before encode runs: |
There was a problem hiding this comment.
Worth explaining that encryption is performed automatically by the middleware but that decryption is explicit (using decrypt, decryptAll).
| * @see ADR 195 — Planner IR with two renderers. | ||
| * @see ADR 213 — Codec lifecycle hooks. |
There was a problem hiding this comment.
Refers to Prisma design rules not available in this repo.
| * registration without pulling in any runtime code (envelope, SDK, | ||
| * codec runtime, middleware). | ||
| * | ||
| * Mirrors `packages/3-extensions/pgvector/src/exports/pack.ts`. |
There was a problem hiding this comment.
Refers to files in the prisma repo.
|
|
||
| export const EQL_INSTALL_VERSION = 'eql-2.2.1' as const; | ||
|
|
||
| export const EQL_INSTALL_SQL: string = `--! @file schema.sql |
There was a problem hiding this comment.
This looks like the raw function definitions, not the final "built" version of EQL. Released versions strip docs and perform additional transformations that should be included.
See https://github.com/cipherstash/encrypt-query-language/releases
| @@ -0,0 +1,397 @@ | |||
| /** | |||
There was a problem hiding this comment.
As a follow-up (this PR is already huge), but we should make sure these tests run in GH workflow.
|
|
||
| ### `EncryptedEnvelopeBase<T>` — shared envelope superclass | ||
|
|
||
| `packages/3-extensions/cipherstash/src/execution/envelope-base.ts` exports an abstract `EncryptedEnvelopeBase<T>` class that holds the `#`-prefixed `EncryptedHandle<T>` slot and ships the five redaction overrides (`toJSON`, `toString`, `valueOf`, `Symbol.toPrimitive`, `Symbol.for('nodejs.util.inspect.custom')`), `expose()`, `decrypt({ signal? })`, and the post-decrypt plaintext cache. |
There was a problem hiding this comment.
Retains some references to files in the prisma repo.
| | Linear ticket | Surface | | ||
| | --- | --- | | ||
| | [TML-2388](https://linear.app/prisma-company/issue/TML-2388) | Codec-SDK binding refactor — pull the per-tenant SDK binding out of the codec factory closure into the descriptor seam so multi-tenant deployments don't re-author the codec per tenant. | | ||
| | Polymorphic `CipherstashSdk.decrypt` return type | One-line interface widening from `Promise<string>` to `Promise<unknown>` to mirror the bulk shape; removes a narrowing cast in `EncryptedEnvelopeBase.decrypt`. | | ||
| | [TML-2504 — Cipherstash JSONB path-exists predicate: STE-VEC selector hashing](https://linear.app/prisma-company/issue/TML-2504) | `cipherstashJsonbPathExists` against the live EQL bundle expects a hashed STE-VEC selector computed via the CipherStash SDK's `selector(...)` API; the framework currently binds the JSONpath as a plain `pg/text@1` `ParamRef`. Round-trip and the two SELECT-expression helpers (`cipherstashJsonbPathQueryFirst`, `cipherstashJsonbGet`) work; the predicate clause returns zero rows. Resolution requires either a client-side path-hashing middleware or an EQL-side plaintext-path overload. | |
There was a problem hiding this comment.
Some references to Prisma private Linear should be removed/updated.
| - [pgvector extension](../pgvector/README.md) — the structural precedent for codec, parameterized descriptor, and pack-meta layout. | ||
| - [ADR 202 — Codec trait system](../../../docs/architecture%20docs/adrs/ADR%20202%20-%20Codec%20trait%20system.md). | ||
| - [ADR 207 — Codec call context per-query AbortSignal and column metadata](../../../docs/architecture%20docs/adrs/ADR%20207%20-%20Codec%20call%20context%20per-query%20AbortSignal%20and%20column%20metadata.md). | ||
| - [ADR 208 — Higher-order codecs for parameterized types](../../../docs/architecture%20docs/adrs/ADR%20208%20-%20Higher-order%20codecs%20for%20parameterized%20types.md). | ||
| - [ADR 212 — Contract spaces](../../../docs/architecture%20docs/adrs/ADR%20212%20-%20Contract%20spaces.md). | ||
| - [ADR 213 — Codec lifecycle hooks](../../../docs/architecture%20docs/adrs/ADR%20213%20-%20Codec%20lifecycle%20hooks.md). | ||
| - [ADR 214 — Extension operator surface: namespaced replacement operators and the predicate/helper split](../../../docs/architecture%20docs/adrs/ADR%20214%20-%20Extension%20operator%20surface%20namespaced%20replacement%20operators.md). | ||
| - [ADR 215 — Runtime middleware lifecycle: `beforeExecute` fires before `encodeParams`](../../../docs/architecture%20docs/adrs/ADR%20215%20-%20Runtime%20middleware%20lifecycle%20beforeExecute%20before%20encodeParams.md). |
There was a problem hiding this comment.
Most/all of these should be updated or removed.
| "name": "@cipherstash/prisma-next", | ||
| "version": "0.0.0", | ||
| "license": "MIT", | ||
| "author": "CipherStash <hello@cipherstash.com>", |
There was a problem hiding this comment.
This email address doesn't exist FYI!
| @@ -0,0 +1,126 @@ | |||
| # @cipherstash/prisma-next | |||
|
|
|||
| **Searchable field-level encryption for Postgres with [Prisma Next](https://www.npmjs.com/package/@prisma-next/cli)** — via the [EQL bundle](https://cipherstash.com/docs/stack/platform/eql). | |||
There was a problem hiding this comment.
I think this should point to https://www.npmjs.com/package/prisma-next
Probably should link to @cipherstash/stack before EQL, too.
|
|
||
| ## Features | ||
|
|
||
| - 🔒 Six encrypted column types — string, double, bigint, date, boolean, JSON |
There was a problem hiding this comment.
Suggest adding backticks around the types. e.g. string.
| ## Features | ||
|
|
||
| - 🔒 Six encrypted column types — string, double, bigint, date, boolean, JSON | ||
| - 🔍 Searchable encryption — equality, free-text search (ILIKE), range, order, JSON path |
There was a problem hiding this comment.
Suggestion:
Searchable encryption — equality, free-text search, range, order, JSON path and containment
| await decryptAll(rows) | ||
| console.log(await rows[0]?.email.decrypt()) |
There was a problem hiding this comment.
This example isn't correct (same issue as the example code earlier).
| ``` | ||
|
|
||
| ```bash | ||
| stash auth login # one-time, per developer |
| `stash auth login` runs a PKCE flow and caches credentials in your OS keychain — each developer ends up with their own identity for every encrypt / decrypt against the workspace. No `CS_*` env vars in local development. | ||
|
|
||
| The four `CS_*` env vars (`CS_WORKSPACE_CRN`, `CS_CLIENT_ID`, `CS_CLIENT_KEY`, `CS_CLIENT_ACCESS_KEY`) are reserved for production deployments and CI runners. See the [authentication docs](https://cipherstash.com/docs/stack/cipherstash/encryption/prisma-next#authentication) for the full identity story. | ||
|
|
There was a problem hiding this comment.
Suggested copy:
There are 2 main ways to authenticate to CipherStash:
### Local profile (Dev)
`npx stash auth login` lets you login via the browser and saves credentials in the CipherStash profile (`~/.cipherstash`). A key is automatically generated and granted access to the default keyset.
### Env vars (Production)
The four `CS_*` env vars (`CS_WORKSPACE_CRN`, `CS_CLIENT_ID`, `CS_CLIENT_KEY`, `CS_CLIENT_ACCESS_KEY`) are reserved for production deployments and CI runners. See the [authentication docs](https://cipherstash.com/docs/stack/encryption/prisma-next#authentication) for more information.
The auth stuff should really link to /docs/stack/auth but that section hasn't been created yet.
| - 📖 [**Full docs**](https://cipherstash.com/docs/stack/cipherstash/encryption/prisma-next) — column types, operator reference, security model, known limitations. | ||
| - [CipherStash EQL reference](https://cipherstash.com/docs/stack/platform/eql) — encrypted operator semantics and search-config index types. | ||
| - [`@cipherstash/stack`](../stack/README.md) — encryption SDK and schema DSL. | ||
| - [Prisma Next CLI](https://www.npmjs.com/package/@prisma-next/cli) — the framework this extension plugs into. |
There was a problem hiding this comment.
coderdan
left a comment
There was a problem hiding this comment.
Wowza. Epic! Very exciting (but very large!) PR. I manually reviewed as much as I could and relied on Claude and Coderabbit for the rest.
I've added a number of things that we should address before release but they don't block this PR.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
examples/prisma/test/e2e/global-setup.ts (1)
71-77: ⚡ Quick winValidate all required CipherStash env vars upfront.
Line 71 currently gates only on
CS_WORKSPACE_CRN, but the thrown message says companion credentials are also required. Validate the full required set here so auth/config failures are immediate and deterministic.Suggested patch
- if (!process.env['CS_WORKSPACE_CRN']) { + const requiredEnv = ['CS_WORKSPACE_CRN', 'CS_CLIENT_ID', 'CS_CLIENT_KEY'] as const; + const missing = requiredEnv.filter((k) => !process.env[k]); + if (missing.length > 0) { throw new Error( - 'cipherstash e2e harness: `CS_WORKSPACE_CRN` is not set. Populate `.env` ' + - '(see `.env.example`) with a ZeroKMS workspace and the three companion ' + + `cipherstash e2e harness: missing required env var(s): ${missing.join(', ')}. Populate \`.env\` ` + + '(see `.env.example`) with a ZeroKMS workspace and the three companion ' + 'credentials before running `pnpm test:e2e`.', ); }🤖 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 `@examples/prisma/test/e2e/global-setup.ts` around lines 71 - 77, Replace the single-variable guard that only checks process.env['CS_WORKSPACE_CRN'] with a check of the full required set (e.g. ['CS_WORKSPACE_CRN','CS_KEY_ID','CS_CLIENT_ID','CS_CLIENT_SECRET']) by creating a REQUIRED_ENV array, computing missing = REQUIRED_ENV.filter(k => !process.env[k]), and throwing the same Error if missing.length > 0 that includes the list of missing variable names and the existing guidance; update the existing throw site in global-setup.ts (the block that currently tests process.env['CS_WORKSPACE_CRN']) to use this new check so failures are immediate and deterministic.
🤖 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 @.github/workflows/prisma-next-e2e.yml:
- Around line 98-104: The readiness loop using "docker exec
cipherstash-e2e-postgres pg_isready -U cipherstash -d cipherstash_e2e" can
silently time out; modify the loop so after the for i in {1..60} attempts you
check whether the container became ready and if not print a clear error (e.g.,
"Postgres did not become ready within timeout") and exit non-zero (exit 1) to
fail the workflow fast; ensure the final check references the same readiness
command/container name and returns a non-zero exit status when readiness was not
achieved.
In `@examples/prisma/test/e2e/harness.ts`:
- Around line 57-78: The truncateUsers() function's spawnSync('docker', ...)
call should be hardened: pass a reasonable timeout (same pattern used elsewhere,
e.g., pg_isready calls) and include full spawn failure handling by checking
result.error and result.signal in addition to result.status; when any of those
indicate failure, throw an Error that includes the timeout/errno/signal plus
stdout/stderr content to aid debugging. Update the spawnSync invocation in
truncateUsers() to include a timeout option and adjust the subsequent failure
branch to detect and report result.error (process spawn errors), result.signal
(killed by signal), and non-zero result.status with a single detailed error
message containing the error, signal, status, stderr, and stdout.
---
Nitpick comments:
In `@examples/prisma/test/e2e/global-setup.ts`:
- Around line 71-77: Replace the single-variable guard that only checks
process.env['CS_WORKSPACE_CRN'] with a check of the full required set (e.g.
['CS_WORKSPACE_CRN','CS_KEY_ID','CS_CLIENT_ID','CS_CLIENT_SECRET']) by creating
a REQUIRED_ENV array, computing missing = REQUIRED_ENV.filter(k =>
!process.env[k]), and throwing the same Error if missing.length > 0 that
includes the list of missing variable names and the existing guidance; update
the existing throw site in global-setup.ts (the block that currently tests
process.env['CS_WORKSPACE_CRN']) to use this new check so failures are immediate
and deterministic.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: d9ef9669-95c0-420c-ace6-257b456f9233
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (26)
.github/workflows/prisma-next-e2e.yml.gitignoreexamples/prisma/.env.exampleexamples/prisma/package.jsonexamples/prisma/src/db.tsexamples/prisma/test/e2e/README.mdexamples/prisma/test/e2e/bigint.e2e.test.tsexamples/prisma/test/e2e/bool.e2e.test.tsexamples/prisma/test/e2e/date.e2e.test.tsexamples/prisma/test/e2e/docker-compose.ymlexamples/prisma/test/e2e/global-setup.tsexamples/prisma/test/e2e/harness.tsexamples/prisma/test/e2e/json.e2e.test.tsexamples/prisma/test/e2e/mixed.e2e.test.tsexamples/prisma/test/e2e/num.e2e.test.tsexamples/prisma/test/e2e/str-range.e2e.test.tsexamples/prisma/test/e2e/vitest.config.tspackages/prisma-next/DEVELOPING.mdpackages/prisma-next/README.mdpackages/prisma-next/package.jsonpackages/prisma-next/src/execution/cell-codec-factory.tspackages/prisma-next/src/execution/operators.tspackages/prisma-next/src/exports/migration.tspackages/prisma-next/src/exports/pack.tspackages/prisma-next/test/bundling-isolation.test.tspackages/prisma-next/turbo.json
✅ Files skipped from review due to trivial changes (6)
- examples/prisma/test/e2e/README.md
- examples/prisma/.env.example
- .gitignore
- packages/prisma-next/src/exports/migration.ts
- packages/prisma-next/DEVELOPING.md
- packages/prisma-next/README.md
🚧 Files skipped from review as they are similar to previous changes (7)
- examples/prisma/package.json
- packages/prisma-next/package.json
- packages/prisma-next/test/bundling-isolation.test.ts
- examples/prisma/src/db.ts
- packages/prisma-next/src/exports/pack.ts
- packages/prisma-next/src/execution/cell-codec-factory.ts
- packages/prisma-next/src/execution/operators.ts
| for i in {1..60}; do | ||
| if docker exec cipherstash-e2e-postgres pg_isready -U cipherstash -d cipherstash_e2e >/dev/null 2>&1; then | ||
| echo "Postgres ready" | ||
| break | ||
| fi | ||
| sleep 1 | ||
| done |
There was a problem hiding this comment.
Fail fast when Postgres never becomes ready.
The readiness loop can timeout silently and continue to the test step, which delays and obscures the root failure. Add an explicit timeout check and exit non-zero here.
Proposed fix
- name: Start E2E Postgres container
working-directory: examples/prisma
run: |
docker compose -f test/e2e/docker-compose.yml up -d
# Wait for pg_isready before handing off to the suite — the
# global-setup hook expects the container to already be up.
+ ready=0
for i in {1..60}; do
if docker exec cipherstash-e2e-postgres pg_isready -U cipherstash -d cipherstash_e2e >/dev/null 2>&1; then
echo "Postgres ready"
+ ready=1
break
fi
sleep 1
done
+ if [ "$ready" -ne 1 ]; then
+ echo "Postgres did not become ready within 60s"
+ docker logs cipherstash-e2e-postgres || true
+ exit 1
+ fi📝 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.
| for i in {1..60}; do | |
| if docker exec cipherstash-e2e-postgres pg_isready -U cipherstash -d cipherstash_e2e >/dev/null 2>&1; then | |
| echo "Postgres ready" | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| ready=0 | |
| for i in {1..60}; do | |
| if docker exec cipherstash-e2e-postgres pg_isready -U cipherstash -d cipherstash_e2e >/dev/null 2>&1; then | |
| echo "Postgres ready" | |
| ready=1 | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| if [ "$ready" -ne 1 ]; then | |
| echo "Postgres did not become ready within 60s" | |
| docker logs cipherstash-e2e-postgres || true | |
| exit 1 | |
| fi |
🤖 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 @.github/workflows/prisma-next-e2e.yml around lines 98 - 104, The readiness
loop using "docker exec cipherstash-e2e-postgres pg_isready -U cipherstash -d
cipherstash_e2e" can silently time out; modify the loop so after the for i in
{1..60} attempts you check whether the container became ready and if not print a
clear error (e.g., "Postgres did not become ready within timeout") and exit
non-zero (exit 1) to fail the workflow fast; ensure the final check references
the same readiness command/container name and returns a non-zero exit status
when readiness was not achieved.
| export function truncateUsers(): void { | ||
| const result = spawnSync( | ||
| 'docker', | ||
| [ | ||
| 'exec', | ||
| 'cipherstash-e2e-postgres', | ||
| 'psql', | ||
| '-U', | ||
| 'cipherstash', | ||
| '-d', | ||
| 'cipherstash_e2e', | ||
| '-c', | ||
| 'TRUNCATE TABLE users', | ||
| ], | ||
| { stdio: 'pipe' }, | ||
| ); | ||
| if (result.status !== 0) { | ||
| throw new Error( | ||
| `cipherstash e2e harness: TRUNCATE failed (exit ${result.status}):\n` + | ||
| `${result.stderr?.toString() ?? ''}\n${result.stdout?.toString() ?? ''}`, | ||
| ); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
fd -i -e ts . examples/prisma/test/e2e | xargs rg -n -C3 'spawnSync\('
echo "---- harness truncateUsers() failure checks ----"
rg -n -C6 'export function truncateUsers|spawnSync\(|timeout|result\.error|result\.signal|result\.status' examples/prisma/test/e2e/harness.tsRepository: cipherstash/stack
Length of output: 3586
Add timeout + comprehensive spawn failure handling in truncateUsers().
Line 58's spawnSync call lacks a timeout (line 71 shows { stdio: 'pipe' } only), creating a risk of indefinite hang if docker exec stalls. Additionally, line 73 checks only result.status !== 0, ignoring result.error and result.signal cases that communicate spawn failures and signals.
Other spawnSync calls in the suite (e.g., pg_isready at global-setup.ts:79) already include timeouts, establishing the pattern to follow.
Suggested patch
export function truncateUsers(): void {
+ const TRUNCATE_TIMEOUT_MS = 10_000;
const result = spawnSync(
'docker',
@@
- { stdio: 'pipe' },
+ { stdio: 'pipe', timeout: TRUNCATE_TIMEOUT_MS },
);
- if (result.status !== 0) {
+ if (result.error || result.signal || result.status !== 0) {
throw new Error(
- `cipherstash e2e harness: TRUNCATE failed (exit ${result.status}):\n` +
- `${result.stderr?.toString() ?? ''}\n${result.stdout?.toString() ?? ''}`,
+ `cipherstash e2e harness: TRUNCATE failed` +
+ `${result.error ? ` (spawn error: ${result.error.message})` : ''}` +
+ `${result.signal ? ` (signal: ${result.signal})` : ''}` +
+ `${typeof result.status === 'number' ? ` (exit ${result.status})` : ''}:\n` +
+ `${result.stderr?.toString() ?? ''}\n${result.stdout?.toString() ?? ''}`,
);
}
}🤖 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 `@examples/prisma/test/e2e/harness.ts` around lines 57 - 78, The
truncateUsers() function's spawnSync('docker', ...) call should be hardened:
pass a reasonable timeout (same pattern used elsewhere, e.g., pg_isready calls)
and include full spawn failure handling by checking result.error and
result.signal in addition to result.status; when any of those indicate failure,
throw an Error that includes the timeout/errno/signal plus stdout/stderr content
to aid debugging. Update the spawnSync invocation in truncateUsers() to include
a timeout option and adjust the subsequent failure branch to detect and report
result.error (process spawn errors), result.signal (killed by signal), and
non-zero result.status with a single detailed error message containing the
error, signal, status, stderr, and stdout.
Summary by CodeRabbit
New Features
@cipherstash/prisma-nextpackage for encrypted Prisma Next column support with encrypted search operatorsstash init --prisma-nextCLI command for streamlined Prisma Next project setupEncryptedString,EncryptedDouble,EncryptedBigInt,EncryptedDate,EncryptedBoolean,EncryptedJsonDocumentation