Skip to content

tml-2994: BetterAuth extension — managed contract space, contract-typed adapter, example#968

Open
SevInf wants to merge 33 commits into
mainfrom
tml-2994-better-auth-extension
Open

tml-2994: BetterAuth extension — managed contract space, contract-typed adapter, example#968
SevInf wants to merge 33 commits into
mainfrom
tml-2994-better-auth-extension

Conversation

@SevInf

@SevInf SevInf commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Adds @prisma-next/extension-better-auth: a BetterAuth database adapter backed by Prisma Next's contract machinery. The auth schema ships as a managed contract space (the first extension space that ships table DDL — the framework owns db init/db update/verification for it), and BetterAuth's stringly-typed persistence interface is translated onto contract-typed ORM collections, so model/field mismatches are compile-time errors inside the package rather than runtime surprises in apps.

Closes TML-2994, TML-2995, TML-2996 (single-PR delivery of the BetterAuth Extension project; spec + review ledger under projects/extension-better-auth/).

Changes

  • Contract space (/pack, /contract)spaceId: 'better-auth' defines User/Session/Account/Verification (BetterAuth's canonical core schema: default table names in public, text ids, onDelete: Cascade FKs, User.email/Session.token uniques, User → Session[]/Account[] backrelations) from PSL source, with a baseline migration and refs/head.json. managed control — unlike the supabase space's external posture, prisma-next db init creates these tables and db update walks the space's migration graph. /contract ships extensionModel-branded handles so app contracts can declare cross-space FKs onto User (demonstrated by the example's Profile).
  • Adapter (/adapter)prismaNextAdapter(db) built on better-auth v1.5+ createAdapterFactory. The BetterAuth-model→collection map is as const satisfies Record<SpaceModelName, …> against the emitted contract.d.ts, so contract/adapter divergence fails pnpm typecheck in both directions; field/operator validation reads the shipped contract; unknown surfaces throw typed errors (UNKNOWN_MODEL/UNKNOWN_FIELD/UNSUPPORTED_OPERATOR/…). Native consumeOne rides Collection.delete()'s atomic find-first + narrowed DELETE … RETURNING (single-winner proven by an 8-way parallel race test); transaction rebinds a nested adapter onto the runtime's transaction scope (rollback proven); the join parameter translates onto Collection.include() with typed failure for unmapped targets (native path proven by collection spies — the fallback separate-queries path is never taken).
  • Runtime descriptor (/runtime) — descriptor-only SqlRuntimeExtensionDescriptor (no facade), required for constructing a client over an aggregate contract that includes the space; discovered as a product gap when building the example (plain postgres({ contractJson: aggregate }) rejects pack-bearing contracts without it).
  • Integration tests (test/integration) — BetterAuth's official conformance suite (@better-auth/test-utils/adapter) over PGlite: 156 passed / 0 failed, with 69 skips in four documented categories (project non-goals; 8 decode-dependent join tests pending TML-3015 — see below; one upstream test-utils bug that voids provided transaction implementations; one upstream test that leaks generateId state). Plus a managed-space lifecycle journey (emit → plan → init → update-no-op → verify, catalog-asserted) and a real betterAuth() sign-up → session e2e.
  • Example (examples/better-auth) — full consumer story: three-step schema flow, sign-up → authenticated request over a minimal server, cross-space Profile → user FK (cascade observed live), and a self-testing suite that re-emits byte-identical artifacts, integrity-checks both spaces, and runs db init through the real CLI. Documents the two-views architecture (aggregate client + space view over one shared pool) that adapter consumers need because the emitted aggregate does not fold pack domain models.
  • Docs — package README (two-views architecture first, plus the adapter's typed-seam contract); ADR 212 amendment legitimizing the src/contract/ PSL-authored layout and the managed table-DDL space variant; extension-precedent references updated (docs/architecture docs/subsystems/6., .agents/rules/contract-space-package-layout.mdc).

Why

  • Managed space over app-owned tables: BetterAuth's ecosystem expects the ORM to own the auth schema's lifecycle. Shipping it as a managed contract space keeps contract emit → migration plan → db init as the only setup path (no manual SQL anywhere, including tests and the example) and proves extension spaces can carry table DDL through the existing computeExtensionSpaceApplyPath machinery — no framework changes were needed.
  • Typed seam over passthrough: the adapter narrows BetterAuth's strings at exactly one contract-validated boundary and drives only the real collection API, so values always cross contract codecs (Date↔timestamptz, boolean↔bool round-trips are behaviourally tested) and the compile-time exhaustive map makes schema drift unshippable.
  • Conformance suite as the bar: BetterAuth's own testAdapter suites (including join coverage with native joins enabled) are the acceptance test, not hand-rolled approximations. The run surfaced real product gaps now fixed here (cascade semantics, /runtime descriptor, User backrelations) — and one genuine ORM bug, split out as TML-3015 / PR tml-3015: decode to-many include() payloads through the codec boundary #966 (to-many include() payloads bypass the codec decode boundary); the 8 conformance tests blocked on it are disabled with per-test reasons and re-enable when it merges.
  • Follow-up tickets filed rather than scope-crept: TML-3016 (detached-receiver codec factory bug), TML-3017 (@prisma/dev close-hang under churn), TML-3021 (migration serializer pretty/minified mismatch), TML-3020 (duplicate ADR numbers).

The projects/extension-better-auth/ folder (spec, plan, dispatch briefs, review ledger, QA script + run report) is transient per projects/README.md and is deleted at project close-out.

Summary by CodeRabbit

  • New Features
    • Added a Better Auth extension with managed authentication tables, migrations, typed contract handles, runtime support, and a Prisma Next adapter.
    • Expanded adapter capabilities with contract-typed CRUD, filtering/sorting, joins, transactions, shared connection pool support, and structured validation errors.
    • Added an end-to-end Better Auth example with email/password auth, profiles, cross-space relations, and authenticated API access.
    • Allowed contract spaces to use grouped layouts (for PSL-authored variants) and to ship framework-managed table DDL.
  • Documentation
    • Added detailed architecture and extension/example walkthrough documentation for the Better Auth extension.
  • Tests
    • Added comprehensive end-to-end, integration, lifecycle, contract, migration, and type-level coverage.

@SevInf
SevInf requested a review from a team as a code owner July 13, 2026 11:13
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a managed BetterAuth contract-space extension with Postgres migrations, a contract-typed adapter, shared-pool integration, branded cross-space handles, an end-to-end example, and adapter, lifecycle, and authentication tests.

Changes

BetterAuth extension and contract space

Layer / File(s) Summary
Contract-space architecture and repository wiring
.agents/rules/..., architecture.config.json, biome.jsonc, docs/architecture docs/...
Documents grouped PSL contract layouts, managed table DDL, extension package architecture, and migration reference-file handling.
BetterAuth contract space and pack artifacts
packages/3-extensions/better-auth/...
Adds the BetterAuth schema, generated contract artifacts, branded handles, baseline migration, pack/runtime descriptors, exports, and package tooling.
Contract-typed BetterAuth adapter
packages/3-extensions/better-auth/src/adapter/...
Adds model and relation validation, typed errors, where/order translation, CRUD operations, joins, transactions, atomic consumption, and shared-pool construction.
Example application and cross-space database views
examples/better-auth/...
Adds the Profile contract, BetterAuth integration, shared pool/database wiring, HTTP routes, migrations, documentation, and generated contract artifacts.
Adapter and lifecycle validation
packages/3-extensions/better-auth/test/..., test/integration/...
Adds adapter behavior, type, descriptor, runtime, conformance, managed-space lifecycle, and authentication flow coverage.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • prisma/prisma-next#25 — Adds SQL-lane/Postgres lowering operator support used by the BetterAuth adapter’s comparison translation.

Suggested reviewers: jkomyno

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.96% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main additions: a BetterAuth extension with managed contract space, typed adapter, and example.
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 tml-2994-better-auth-extension

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

Copy link
Copy Markdown

Open in StackBlitz

@prisma-next/extension-author-tools

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

@prisma-next/mongo-runtime

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

@prisma-next/family-mongo

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

@prisma-next/sql-runtime

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

@prisma-next/family-sql

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

@prisma-next/extension-arktype-json

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

@prisma-next/extension-better-auth

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/extension-better-auth@968

@prisma-next/middleware-cache

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

@prisma-next/mongo

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

@prisma-next/extension-paradedb

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

@prisma-next/extension-pgvector

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

@prisma-next/extension-postgis

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

@prisma-next/postgres

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

@prisma-next/sql-orm-client

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

@prisma-next/sqlite

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

@prisma-next/extension-supabase

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

@prisma-next/target-mongo

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

@prisma-next/adapter-mongo

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

@prisma-next/driver-mongo

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

@prisma-next/contract

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

@prisma-next/utils

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

@prisma-next/config

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

@prisma-next/errors

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

@prisma-next/framework-components

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

@prisma-next/operations

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

@prisma-next/ts-render

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

@prisma-next/contract-authoring

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

@prisma-next/ids

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

@prisma-next/psl-parser

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

@prisma-next/psl-printer

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

@prisma-next/cli

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

@prisma-next/cli-telemetry

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

@prisma-next/config-loader

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

@prisma-next/emitter

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

@prisma-next/language-server

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

@prisma-next/migration-tools

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

prisma-next

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

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

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

@prisma-next/mongo-codec

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

@prisma-next/mongo-contract

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

@prisma-next/mongo-value

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

@prisma-next/mongo-contract-psl

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

@prisma-next/mongo-contract-ts

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

@prisma-next/mongo-emitter

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

@prisma-next/mongo-schema-ir

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

@prisma-next/mongo-query-ast

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

@prisma-next/mongo-orm

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

@prisma-next/mongo-query-builder

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

@prisma-next/mongo-lowering

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

@prisma-next/mongo-wire

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

@prisma-next/sql-contract

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

@prisma-next/sql-errors

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

@prisma-next/sql-operations

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

@prisma-next/sql-schema-ir

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

@prisma-next/sql-contract-psl

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

@prisma-next/sql-contract-ts

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

@prisma-next/sql-contract-emitter

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

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

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

@prisma-next/sql-relational-core

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

@prisma-next/sql-builder

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

@prisma-next/target-postgres

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

@prisma-next/target-sqlite

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

@prisma-next/adapter-postgres

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

@prisma-next/adapter-sqlite

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

@prisma-next/driver-postgres

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

@prisma-next/driver-sqlite

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

commit: 78de265

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

size-limit report 📦

Path Size
postgres / no-emit 156.35 KB (0%)
postgres / emit 131.37 KB (0%)
mongo / no-emit 98.71 KB (0%)
mongo / emit 89.43 KB (0%)
cf-worker / no-emit 182.94 KB (0%)
cf-worker / emit 155.41 KB (0%)

@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: 8

🧹 Nitpick comments (4)
packages/3-extensions/better-auth/test/contract-handles.test.ts (2)

70-83: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Brand assertions via expectTypeOf may pass even if branding is lost.

toEqualTypeOf<TargetFieldRef<...>>() on branded refs can produce false positives because expectTypeOf can erase brand information. Consider an identity-check (Equal<A,B>) pattern to preserve the brand in the comparison.

Based on learnings, for branded types prefer a type-level identity check over expectTypeOf since the latter can erase brand info and produce false positives.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/3-extensions/better-auth/test/contract-handles.test.ts` around lines
70 - 83, Replace the branded-type assertions in the User.refs.id,
Session.refs.id, Account.refs.id, and Verification.refs.id tests with an
identity-check pattern such as Equal<A, B>, ensuring the comparison preserves
the better-auth brand. Keep the existing runtime spaceId, namespaceId, and
tableName assertions unchanged.

Source: Learnings


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

Cross-package import reaches into another package's test source via a relative path.

../../../2-sql/1-core/contract/test/test-support reaches into the 2-sql/1-core/contract package's internals from this extension package. Prefer importing createTestSqlNamespace through that package's published export surface rather than a relative path into its test/ tree.

Based on learnings, cross-package imports should use the other package's published package name/export entrypoints rather than relative paths like ../../../<other-package>/....

#!/bin/bash
# Find the source of createTestSqlNamespace and whether it is exported via a package entrypoint.
rg -nP 'createTestSqlNamespace' -g '!**/contract-handles.test.ts' -C2
fd -t f 'package.json' packages/2-sql/1-core/contract --exec sh -c 'echo "== {} =="; jq -r ".name, (.exports|keys[]?)" {}'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/3-extensions/better-auth/test/contract-handles.test.ts` at line 28,
Replace the relative import of createTestSqlNamespace in
contract-handles.test.ts with an import from the 2-sql/1-core/contract package’s
published name or export entrypoint. Use the package’s existing export surface
for this symbol and do not reach into its contract/test directory.
packages/3-extensions/better-auth/src/adapter/model-map.ts (1)

102-116: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Relation resolution only reads the first local/target field, silently dropping composite keys.

localField/targetField take only [0] of localFields/targetFields with an empty-string fallback. For this space's single-column FKs (userIdid) this is fine, but a future composite-key relation would silently resolve against a partial (or empty-string) key rather than failing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/3-extensions/better-auth/src/adapter/model-map.ts` around lines 102
- 116, The relationsBySpaceModel mapping currently reduces localFields and
targetFields to their first entries, losing composite-key relations. Update the
relation construction to validate that each relation has exactly one local and
target field, preserving those values for single-column foreign keys and
explicitly rejecting or otherwise handling composite or empty field arrays
instead of using partial or empty-string keys.
examples/better-auth/migrations/app/20260713T0837_init/migration.ts (1)

24-35: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Redundant index on userId.

Adding the UNIQUE constraint on userId (Lines 24-29) already creates an implicit unique b-tree index on that column in Postgres. The subsequent createIndex on the same single column (Lines 30-35) is redundant — it adds storage and write-maintenance cost on every insert/update without any additional query-planning benefit, since the unique index already covers equality/range lookups on userId.

♻️ Proposed fix: drop the redundant index
       this.addUnique({
         schema: 'public',
         table: 'profile',
         constraint: 'profile_userId_key',
         columns: ['userId'],
       }),
-      this.createIndex({
-        schema: 'public',
-        table: 'profile',
-        index: 'profile_userId_idx',
-        columns: ['userId'],
-      }),
       this.addForeignKey({

If this scaffold intentionally pairs FK columns with an explicit index regardless of an existing covering unique constraint (e.g. so the index survives if the unique constraint is later dropped), that convention would be worth confirming against other migrations in the repo.

🤖 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/better-auth/migrations/app/20260713T0837_init/migration.ts` around
lines 24 - 35, Remove the redundant createIndex operation for profile.userId
from the migration, while preserving the addUnique constraint
profile_userId_key. Do not add a replacement index unless the repository’s
migration convention explicitly requires one.
🤖 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 `@architecture.config.json`:
- Around line 519-542: Add architecture entries for the missing better-auth
adapter and runtime surfaces: register src/adapter/**, src/exports/adapter.ts,
src/runtime/**, and src/exports/runtime.ts with the same extensions, adapters,
and shared classifications used by the existing pack and contract entries.

In `@packages/3-extensions/better-auth/package.json`:
- Around line 58-72: Update the "./pack" entry in the package exports map to use
explicit "types" and "import" conditions, pointing to the appropriate
declaration and runtime files, matching the structure of "./contract",
"./adapter", and "./runtime".

In `@packages/3-extensions/better-auth/src/adapter/index.ts`:
- Around line 212-216: Update updateMany to pass its update payload through the
same asRecord guard used by update before calling assertKnownFields, preserving
the typed PrismaNextAdapterError with INVALID_OPERATOR_VALUE for non-object
payloads. Keep the existing model resolution, field validation, and scoped
updateCount flow unchanged.
- Around line 112-131: Update the singular operation paths for update, delete,
and consumeOne to return early when where is undefined or empty, preventing
execution against the unscoped collection. Keep scopeToWhere’s existing
raw-collection behavior and the bulk method behavior unchanged, and anchor the
changes to the singular operation implementations near scopeToWhere.

In `@packages/3-extensions/better-auth/src/adapter/model-map.ts`:
- Around line 1-4: Validate the imported contractJson at runtime before
fieldsBySpaceModel and relationsBySpaceModel consume it. Import the established
validateContract helper and pass contractJson through
validateContract<Contract>(...) using the fully typed Contract, then use the
validated result for both model/field and relation resolution while preserving
the existing SpaceModelName typing.

In `@packages/3-extensions/better-auth/src/contract/handles.ts`:
- Around line 21-97: Update the field definitions in User, Session, and Account
to mark every contract-nullable column as optional using the existing
nullability marker, including User.image, Session.ipAddress and userAgent, and
the nullable Account fields. Leave required identifiers and timestamps
unchanged, and ensure the handles match the nullability represented in
contract.json.

In `@packages/3-extensions/better-auth/test/adapter-crud.test.ts`:
- Around line 1-2: Update the file-header descriptions in
packages/3-extensions/better-auth/test/adapter-crud.test.ts lines 1-2 and
packages/3-extensions/better-auth/test/adapter-advanced.test.ts lines 1-2 to
identify the tests as using the PostgreSQL dev server rather than PGlite; no
other test behavior changes are needed.

In `@test/integration/test/extension-better-auth.harness.helpers.ts`:
- Around line 39-112: Update setupBetterAuthTestApp to guard all setup work
after database and tempRoot creation with cleanup on failure, ensuring
database.close() is attempted and tempRoot is removed before rethrowing the
original error. In the returned teardown method, make the bounded database-close
wait non-rejecting so rmSync(tempRoot, ...) always executes even when
database.close() rejects, while preserving the existing timeout behavior.

---

Nitpick comments:
In `@examples/better-auth/migrations/app/20260713T0837_init/migration.ts`:
- Around line 24-35: Remove the redundant createIndex operation for
profile.userId from the migration, while preserving the addUnique constraint
profile_userId_key. Do not add a replacement index unless the repository’s
migration convention explicitly requires one.

In `@packages/3-extensions/better-auth/src/adapter/model-map.ts`:
- Around line 102-116: The relationsBySpaceModel mapping currently reduces
localFields and targetFields to their first entries, losing composite-key
relations. Update the relation construction to validate that each relation has
exactly one local and target field, preserving those values for single-column
foreign keys and explicitly rejecting or otherwise handling composite or empty
field arrays instead of using partial or empty-string keys.

In `@packages/3-extensions/better-auth/test/contract-handles.test.ts`:
- Around line 70-83: Replace the branded-type assertions in the User.refs.id,
Session.refs.id, Account.refs.id, and Verification.refs.id tests with an
identity-check pattern such as Equal<A, B>, ensuring the comparison preserves
the better-auth brand. Keep the existing runtime spaceId, namespaceId, and
tableName assertions unchanged.
- Line 28: Replace the relative import of createTestSqlNamespace in
contract-handles.test.ts with an import from the 2-sql/1-core/contract package’s
published name or export entrypoint. Use the package’s existing export surface
for this symbol and do not reach into its contract/test directory.
🪄 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: 9ac67805-bbc2-48e4-bdc5-3a2267614c1d

📥 Commits

Reviewing files that changed from the base of the PR and between 7f47601 and e802411.

⛔ Files ignored due to path filters (24)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • projects/extension-better-auth/plan.md is excluded by !projects/**
  • projects/extension-better-auth/qa/manual-qa.md is excluded by !projects/**
  • projects/extension-better-auth/qa/qa-run-2026-07-13.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/01-scaffold-and-contract-space.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/02-managed-space-lifecycle-test.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/03-branded-contract-handles-r2.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/03-branded-contract-handles.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/04-adapter-core-crud-r2.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/04-adapter-core-crud.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/05-adapter-consume-transaction-join.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/06-conformance-and-e2e-integration-r2.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/06-conformance-and-e2e-integration-r3.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/06-conformance-and-e2e-integration.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/07-example-app-r2.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/07-example-app-r3.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/07-example-app.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/08-docs-and-close-out-prep-r2.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/08-docs-and-close-out-prep-r3.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/08-docs-and-close-out-prep.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/plan.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/spec.md is excluded by !projects/**
  • projects/extension-better-auth/spec.md is excluded by !projects/**
  • projects/extension-better-auth/trace.jsonl is excluded by !projects/**
📒 Files selected for processing (76)
  • .agents/rules/contract-space-package-layout.mdc
  • architecture.config.json
  • biome.jsonc
  • docs/architecture docs/adrs/ADR 212 - Contract spaces.md
  • docs/architecture docs/adrs/ADR 231 - Stringly-typed third-party interfaces adapt over contract-typed collections.md
  • docs/architecture docs/subsystems/6. Ecosystem Extensions & Packs.md
  • examples/better-auth/.gitignore
  • examples/better-auth/README.md
  • examples/better-auth/biome.jsonc
  • examples/better-auth/migrations/app/20260713T0837_init/end-contract.d.ts
  • examples/better-auth/migrations/app/20260713T0837_init/end-contract.json
  • examples/better-auth/migrations/app/20260713T0837_init/migration.json
  • examples/better-auth/migrations/app/20260713T0837_init/migration.ts
  • examples/better-auth/migrations/app/20260713T0837_init/ops.json
  • examples/better-auth/migrations/better-auth/20260713T0717_create_auth_tables/migration.json
  • examples/better-auth/migrations/better-auth/20260713T0717_create_auth_tables/ops.json
  • examples/better-auth/migrations/better-auth/contract.d.ts
  • examples/better-auth/migrations/better-auth/contract.json
  • examples/better-auth/migrations/better-auth/refs/head.json
  • examples/better-auth/package.json
  • examples/better-auth/prisma-next.config.ts
  • examples/better-auth/src/auth.ts
  • examples/better-auth/src/main.ts
  • examples/better-auth/src/prisma/contract.d.ts
  • examples/better-auth/src/prisma/contract.json
  • examples/better-auth/src/prisma/contract.ts
  • examples/better-auth/src/prisma/db.ts
  • examples/better-auth/src/server.ts
  • examples/better-auth/test/example.integration.test.ts
  • examples/better-auth/tsconfig.json
  • examples/better-auth/vitest.config.ts
  • packages/3-extensions/better-auth/README.md
  • packages/3-extensions/better-auth/biome.jsonc
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/end-contract.d.ts
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/end-contract.json
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/migration.json
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/migration.ts
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/ops.json
  • packages/3-extensions/better-auth/migrations/refs/head.json
  • packages/3-extensions/better-auth/package.json
  • packages/3-extensions/better-auth/prisma-next.config.ts
  • packages/3-extensions/better-auth/src/adapter/db-surface.ts
  • packages/3-extensions/better-auth/src/adapter/errors.ts
  • packages/3-extensions/better-auth/src/adapter/index.ts
  • packages/3-extensions/better-auth/src/adapter/join.ts
  • packages/3-extensions/better-auth/src/adapter/model-map.ts
  • packages/3-extensions/better-auth/src/adapter/where.ts
  • packages/3-extensions/better-auth/src/contract/contract.d.ts
  • packages/3-extensions/better-auth/src/contract/contract.json
  • packages/3-extensions/better-auth/src/contract/contract.prisma
  • packages/3-extensions/better-auth/src/contract/handles.ts
  • packages/3-extensions/better-auth/src/exports/adapter.ts
  • packages/3-extensions/better-auth/src/exports/contract.ts
  • packages/3-extensions/better-auth/src/exports/pack.ts
  • packages/3-extensions/better-auth/src/exports/runtime.ts
  • packages/3-extensions/better-auth/src/pack/index.ts
  • packages/3-extensions/better-auth/src/runtime/descriptor.ts
  • packages/3-extensions/better-auth/test/adapter-advanced.test.ts
  • packages/3-extensions/better-auth/test/adapter-crud.test.ts
  • packages/3-extensions/better-auth/test/adapter-errors.test.ts
  • packages/3-extensions/better-auth/test/adapter-types.test-d.ts
  • packages/3-extensions/better-auth/test/contract-handles.test.ts
  • packages/3-extensions/better-auth/test/contract-types.test-d.ts
  • packages/3-extensions/better-auth/test/descriptor.test.ts
  • packages/3-extensions/better-auth/test/runtime-descriptor.test.ts
  • packages/3-extensions/better-auth/tsconfig.json
  • packages/3-extensions/better-auth/tsconfig.prod.json
  • packages/3-extensions/better-auth/tsdown.config.ts
  • packages/3-extensions/better-auth/vitest.config.ts
  • test/integration/package.json
  • test/integration/test/extension-better-auth.betterauth-e2e.test.ts
  • test/integration/test/extension-better-auth.conformance.e2e.test.ts
  • test/integration/test/extension-better-auth.harness.helpers.ts
  • test/integration/test/extension-better-auth.lifecycle.e2e.test.ts
  • test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/better-auth-lifecycle/contract.ts
  • test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/better-auth-lifecycle/prisma-next.config.with-db.ts

Comment thread architecture.config.json
Comment thread packages/3-extensions/better-auth/package.json
Comment thread packages/3-extensions/better-auth/src/adapter/index.ts
Comment thread packages/3-extensions/better-auth/src/adapter/index.ts
Comment thread packages/3-extensions/better-auth/src/adapter/model-map.ts
Comment thread packages/3-extensions/better-auth/src/contract/handles.ts
Comment on lines +1 to +2
/**
* Behavioural coverage for `prismaNextAdapter` over a real PGlite database:

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm createDevDatabase provisions a real Postgres dev server (not PGlite).
rg -nP 'export (async )?function createDevDatabase|pglite|prisma/dev|`@electric-sql/pglite`' test/utils -C2

Repository: prisma/prisma-next

Length of output: 2646


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n-- adapter-crud.test.ts --\n'
sed -n '1,12p' packages/3-extensions/better-auth/test/adapter-crud.test.ts

printf '\n-- adapter-advanced.test.ts --\n'
sed -n '1,12p' packages/3-extensions/better-auth/test/adapter-advanced.test.ts

printf '\n-- createDevDatabase implementation --\n'
sed -n '1,120p' test/utils/src/exports/index.ts

Repository: prisma/prisma-next

Length of output: 4766


File headers should say PostgreSQL dev server, not PGlite. Both suites use createDevDatabase() from @prisma-next/test-utils, which starts @prisma/dev’s Postgres dev server.

  • packages/3-extensions/better-auth/test/adapter-crud.test.ts#L1-L2
  • packages/3-extensions/better-auth/test/adapter-advanced.test.ts#L1-L2
📍 Affects 2 files
  • packages/3-extensions/better-auth/test/adapter-crud.test.ts#L1-L2 (this comment)
  • packages/3-extensions/better-auth/test/adapter-advanced.test.ts#L1-L2
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/3-extensions/better-auth/test/adapter-crud.test.ts` around lines 1 -
2, Update the file-header descriptions in
packages/3-extensions/better-auth/test/adapter-crud.test.ts lines 1-2 and
packages/3-extensions/better-auth/test/adapter-advanced.test.ts lines 1-2 to
identify the tests as using the PostgreSQL dev server rather than PGlite; no
other test behavior changes are needed.

Source: Learnings

Comment thread test/integration/test/extension-better-auth.harness.helpers.ts
* lowers to a real cross-space FK onto `"public"."user"(id)` — created by
* `db init` alongside the pack's own tables.
*/
const Profile = model('Profile', {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Use PSL

Comment thread examples/better-auth/src/prisma/db.ts Outdated
* Contract-space view for the BetterAuth adapter (`User`, `Session`,
* `Account`, `Verification` collections).
*/
readonly authDb: AuthDb;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Shouldn't AuthDb already be a subset of Db? Why do we need a separate one? We shouldn't need a separate insteance of DB for that

SevInf added a commit that referenced this pull request Jul 13, 2026
…he shared pool

Operator direction on PR #968, both design comments:

PSL: the app contract moves from TS builders to contract.prisma using
the cross-space reference grammar (user better-auth:public.User
@relation(..., onDelete: Cascade), supabase example precedent). The
lowering is identical — same storage hash, byte-identical emitted
contract.json — so the committed migrations and mirrors stay valid.

One client: db.ts constructs a single postgres() client over the
aggregate on an app-owned pool; auth.ts hands the same pool to
prismaNextAdapter({ pg }); the space view is no longer user-visible
ceremony. /api/me returns the session and its user through BetterAuth
plus the Profile through the ORM; the cascade proof observes the
cross-space FK at the database level through the shared pool. READMEs
retell the one-client story with the aggregate-no-folding explanation
moved to background. Harness setup now releases the dev database and
temp dir on mid-setup failure, and teardown never rejects (bounded
close, rmSync in finally).

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
SevInf added 22 commits July 15, 2026 13:28
…ntract space

PSL-authored contract space defining the four BetterAuth core models —
User, Session, Account, Verification — as singular tables in the public
namespace: text ids, timestamptz timestamps, unique(user.email),
unique(session.token), and navigable N:1 relations Session.user /
Account.user. Managed control policy (contract default): the framework
owns the tables DDL via the planner-emitted baseline migration (Path A,
ADR 212), head pinned in migrations/refs/head.json.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…xports

The pack JSON-imports the emitted artefacts via contractSpaceFromJson
(pgvector precedent) and publishes the managed space under spaceId
better-auth. Descriptor tests pin the four core tables, uniques, FKs,
navigable N:1 relations, baseline migration reachability, and
descriptor self-consistency; type-level tests pin the contract.d.ts
model surface. /pack stays tree-shaken: its only runtime import is
contractSpaceFromJson.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Same extensions/adapters slots as the supabase extension: pack +
contract sources and their exports files, all on the shared plane.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…Glite

In-process CLI journey for an app that lists the better-auth pack in
extensionPacks: contract emit → migration plan (seeds the descriptor-
shipped baseline into migrations/better-auth/) → db init creates
user/session/account/verification with the unique constraints
(user.email, session.token) and FKs (session.userId → user.id,
account.userId → user.id) asserted via catalog introspection, with the
space marker at the published head → db update at head plans zero
operations and reports no-op → db verify clean, including --strict.
No manual SQL participates in the lifecycle; raw queries are read-only
catalog introspection.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
User, Session, Account, Verification handles branded spaceId
better-auth with public-namespace singular-table coordinates (supabase
handles.ts precedent). Tests pin the brand/coordinates (incl. the
type-level TargetFieldRef brand), prove cross-space FK lowering through
the public authoring surface (defineContract + rel.belongsTo +
constraints.foreignKey onto User.refs.id → storage FK with
spaceId/namespace/table/column resolved and the cross-space domain
relation), and lock handle↔contract.json consistency so handle drift
against the emitted space fails the suite.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
The consistency loop now compares whole per-column codecId maps between
each handle and the shipped contract.json domain, not just column-name
sets — a handle column whose codec disagrees with the emitted space
(e.g. emailVerified declared pg/text@1 instead of pg/bool@1) fails the
suite.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…ollections

/adapter exposes prismaNextAdapter(db) on createAdapterFactory
(better-auth ^1.5 peer, 1.6.23 dev): create/findOne/findMany/update/
updateMany/delete/deleteMany/count execute exclusively through the
better-auth space ORM collections, so values cross the seam through
contract codecs (Date↔timestamptz, boolean↔bool proven against PGlite).
BetterAuth where operators (eq/ne/lt/lte/gt/gte/in/not_in/contains/
starts_with/ends_with with escaped LIKE patterns) and AND/OR connector
folding translate onto typed accessor comparators; sortBy/limit/offset
map to orderBy/take/skip.

The model map is satisfies-bound to Record<SpaceModelName, string>, so
a contract model without a mapping (or vice versa) fails pnpm
typecheck. BetterAuthDb is the minimal structural collection surface —
an ordinary PostgresClient over an aggregate including the space is
assignable (pinned by type-level test). Factory-admitted surfaces the
space does not define (plugin tables, additionalFields, relation names
as fields, trait-unsupported operators, insensitive mode) fail fast
with PrismaNextAdapterError naming the surface. Config is honest:
supportsNumericIds false, transaction false until the native
consumeOne/transaction wiring lands.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
src/adapter/index.ts no longer re-exports sibling modules (golden rule:
no reexports outside exports/ folders); src/exports/adapter.ts now
aggregates errors, db-surface, model-map, and the factory directly.
Tests consume the public exports surface. No behaviour change.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…cked joins

consumeOne rides Collection.delete() — the atomic find-first +
identity-narrowed DELETE…RETURNING — so concurrent consumers of one
verification row get exactly one winner (8-way race over PGlite,
repeated rounds). The transaction config opens db.transaction() and
rebinds the adapter to the transaction scope collections via a nested
factory instance (reference-adapter pattern), so a flow that throws
after writes rolls back atomically; config no longer declares
transaction: false. join on findOne/findMany resolves each JoinConfig
entry against the contract space navigable relations (joined model +
from/to columns must all match) and chains Collection.include(), so
with experimental joins the factory receives natively-joined rows —
collection spies prove the joined model collection is never queried
separately — and a join the contract cannot express throws a typed
UNKNOWN_JOIN_RELATION error.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
CARRY-OVER 2 (pre-authorized): BetterAuth conformance join coverage
demands reverse one-to-many joins (user → session/account). The space
PSL gains list backrelations named after BetterAuth model names so the
native join path lands joined rows under the keys the factory expects;
storage hash is unchanged (domain-only change — no migration churn).
The adapter honours JoinConfig.limit on to-many joins via the include
refinement take() (BetterAuth default cap 100); to-one joins ignore
limit per the factory contract. The unmapped-join typed error is now
pinned at the join-resolution seam (user → verification), since the
factory-admitted core joins are all contract-expressible after this
change.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…ntegration

The real consumer path is green and always-on: a betterAuth() instance
over prismaNextAdapter and a PGlite db migrated via the framework CLI
path signs up with email/password, persists user/account/session rows
readable through the contract-typed collections, retrieves the session
from the sign-up cookie, and signs back in.

The official conformance harness (normal/joins/auth-flow/transactions
suites, non-goal tests disabled via the harness disableTests mechanism
with per-test reasons) is wired but gated behind
BETTER_AUTH_CONFORMANCE=1: three surfaced findings block a green run —
missing ON DELETE CASCADE FK semantics in the space (harness cleanup
relies on BetterAuth canonical DDL), undecoded codec values in the
ORM to-many include payloads (framework decode boundary), and an
upstream @better-auth/test-utils wrapper bug that drops provided
transaction implementations. Findings documented in the file header;
136→9 of 225 harness tests were driven green during triage before the
remaining blockers were isolated as out-of-dispatch-scope.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
BetterAuth canonical schema declares ON DELETE CASCADE on both user
references (session.userId, account.userId); the space shipped plain
FKs, so deleting a user with live sessions/accounts violated the
constraints instead of cascading. Add onDelete: Cascade to the PSL,
re-emit the space contract (storage hash changes — the authorized
space exception), and regenerate the baseline migration + head ref.
The lifecycle test now asserts the referential action in its
whole-shape pg_constraint introspection; the descriptor test pins
onDelete: cascade on both FK entries.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…sable categories

Remove the BETTER_AUTH_CONFORMANCE=1 env gate: the suite now runs in
every pnpm test:integration sweep. Disabled tests fall into exactly
four documented categories — non-goal surfaces (plugin tables,
additionalFields, renamed models/fields), the harness generateId leak,
decode-dependent native joins (TML-3015; the same-named normal-suite
tests pass through the fallback join path), and the upstream
transaction-wrapper bug (rollback coverage lives in the extension
package tests). 156 tests pass, 69 skip, none fail.

Harness stability for the long run: keep dev-server idle timeout at
600s so the 1s default stops reaping pooled connections between tests
(9 async "Connection terminated unexpectedly" errors), and bound
database.close() in teardown — after the full suite volume the dev
server close never resolves (small-run teardown returns promptly;
tracked as a TML-2995 finding), and the per-file worker exit reclaims
the in-memory PGlite either way.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…unMigrations is a no-op

The teardown comment cited TML-2995 for the @prisma/dev close-hang;
the dedicated ticket is TML-3017. The top docblock claimed
runMigrations re-runs db init — it is an intentional no-op because
db init already walked the space to head at module scope; say what
the code does.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…n construct

An app whose aggregate contract lists the better-auth pack could not
construct a postgres() client: the runtime requirement check rejects
the contract ("Contract requires extension pack better-auth") and the
control-plane pack descriptor is not assignable to the extensions
option (no codecs slot). Every sibling schema-contributing pack
(supabase, pgvector, postgis, paradedb) ships a /runtime export for
exactly this; add the better-auth equivalent — descriptor only, no
client facade (apps use plain postgres({ extensions })). The package
test pins the fails-iff surface: construction over an aggregate
requiring the pack throws without the descriptor and succeeds with it.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
A minimal real app: better-auth pack in the config, an app Profile
model with a cross-space cascading FK onto the branded User handle,
betterAuth() over prismaNextAdapter, and a node:http server exposing
the auth handler plus one authenticated endpoint reading the session
and traversing Profile → user.

Client construction resolves the D7 flag through the public surface:
two typed views over one shared pool — the aggregate client passes the
pack /runtime descriptor via extensions (without it postgres() rejects
the contract), and the adapter rides the contract-space view with
marker verification left to the aggregate client. The README documents
why: the aggregate records pack models as cross-space references, not
navigable domain models, so db.orm.public.User does not exist on the
aggregate and Profile.user is not include()-able across spaces today.

The integration test is the CI surface for the README: emit determinism
against the committed aggregate, offline integrity of both committed
migration spaces, real CLI db init on a fresh dev database, the FK
whole-shape in pg_constraint, sign-up → authenticated /api/me through
the real server, and the FK cascade observed through the ORM.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
The docblock and test name said "ORM join through the aggregate
client", contradicting server.ts and the README: cross-space relations
are not include()-able today; the server follows the FK explicitly
across the two typed views. Strings only, no behaviour change.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…, precedent references

Package README leads with the two-views consumer architecture (the
aggregate does not fold pack domain models; cross-space relations are
typed never; hence aggregate db + space-view authDb with verifyMarker
false), then the four subpath surfaces, the space schema, the
three-step flow, and the adapter posture — every claim written from
the shipped source.

ADR 212 gains an amendment (house inline-amendment convention):
the src/contract/ grouped layout is an accepted variant for
PSL-authored spaces (supabase/better-auth shape; the regen script
resolves both layouts as the documented rule), and managed spaces may
ship application-visible table DDL — better-auth is the worked
example. The layout rule and the ecosystem subsystem doc name
better-auth as the managed-space precedent alongside pgvector/paradedb.

New ADR 231 records the adapter pattern: stringly-typed third-party
interfaces adapt over contract-typed collections, with five
obligations — exhaustive typed model map, codec-boundary crossing,
fail-fast typed errors, native capability mapping (atomic consume,
transaction rebinding, include-backed joins), and two-views
consumption.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…ample

The example test runs db init in-place against an ephemeral dev
database; the CLI writes migrations/app/refs/db.* describing that
database, so every test run rewrote the tracked copies and dirtied the
tree. Only head pins and migration packages are meaningful to commit
(the demo example tracks no db refs either); gitignore the db.* refs.
Verified: the example test passes from a fresh-clone state without
them.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
The snippet invented a decodeInput() helper that does not exist and
contradicted the ADR's own obligation 2 (codecs cross inside the
collection). Replace with the shipped shape from the adapter source:
resolveSpaceModel → assertKnownFields → collection.create(data).

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…equirement

Raw defineContract calls now require an explicit createNamespace;
follow the supabase handles-test precedent (createTestSqlNamespace via
relative test-support import, package rootDir widened to the workspace
root like supabase and sql-runtime).

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
SevInf added 11 commits July 15, 2026 13:29
The emitter now stamps scalarList: true into sql capabilities; the
committed example aggregate is emit output and must stay byte-stable
under fixtures:check. Hashes unchanged (capabilities do not feed the
storage hash), so the committed migrations and head refs stay valid.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…emit for scalarList

prismaContract() now requires createNamespace (supabase precedent:
postgresCreateNamespace), and fixtures:emit runs this package's
build:contract-space, so the shipped space contract must re-emit
byte-stable. The re-emit adds only the scalarList capability; storage
hash unchanged, so the shipped baseline migration and head ref stay
valid (descriptor self-consistency test green).

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…error handler, README copy fidelity

F-1: migration plan at head rewrites the pinned better-auth space
mirrors even when it plans nothing; the committed mirrors were stale
(missing the scalarList capability stamp; head.json in pretty-printed
form where the rewrite path emits minified), so README step 2 dirtied a
fresh clone. Commit the mirrors as the rewrite path emits them, exempt
consuming-app mirror head refs (migrations/*/refs/head.json) from the
biome formatter, sync the pack baseline end-contract from the shipped
space contract, and extend the example test to assert plan-at-head
leaves the mirrors byte-identical (assertion proven to fire against the
stale bytes).

F-3: the db.ts template installed no pg pool error handler, so an
idle-client disconnect (pgbouncer restart, serverless reaping, network
blip) crashed the whole process via unhandled error event. Add the
logged handler (react-router-demo precedent).

F-4 (+F-7): package README copy fidelity — extensions vs extensionPacks
for the postgres defineConfig, pnpm exec prefixes on the schema-flow
commands, and the requirement-check rejection quoted verbatim from the
runtime (also in the example README, db.ts, and the runtime descriptor
docblock); example README now states profile is null until the app
creates a row, before promising the joined shape.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…ger, QA script + run

Transient per projects/README.md; deleted at project close-out.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
The spec reserved the durability judgment for the operator; it was never put to them. No shipped file referenced the ADR. The adapter-contract content remains in the package README; the operator-decreed ADR 212 amendment stays.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
BetterAuth >=1.6.17 standardizes update/consumeOne as null-returning
no-ops and delete as a plain no-op when called with an empty or absent
where; scopeToWhere returned the unscoped collection instead, so a
forwarded empty where became a whole-table write. The installed 1.6.23
factory guards its own update path but forwards delete/consumeOne
unguarded (verified against @better-auth/core factory.mjs). Tests were
red for exactly those two before the guards landed.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…ullability, brand identity checks

- updateMany routes its payload through asRecord like the singular path
  (object-shape guard before field validation).
- model-map deserializes contract.json through PostgresContractSerializer
  at module load (Type Parameter Pattern) instead of trusting the JSON
  import type, and rejects relations that do not declare exactly one
  local + target field instead of silently taking the first.
- Handles mark every contract-nullable column .optional() (10 columns
  across User/Session/Account); the handle-contract consistency loop now
  compares per-column nullability whole-map style alongside codec ids
  (red for the three models before the handles fix).
- Brand assertions use an Equal<A,B> mutual-assignability check that
  preserves phantom brands; ./pack export gains types/import conditions;
  architecture.config.json covers the adapter and runtime globs; test
  headers name the @prisma/dev Postgres dev server instead of claiming
  bare PGlite.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…ew internally

The two-client ceremony was user-visible plumbing: apps had to
construct a second postgres() client over the pack space contract just
to hand the adapter typed collections. The adapter now accepts the
app's shared pg.Pool and constructs that view itself (verifyMarker
false, with the rationale moved into adapter source: the marker names
the aggregate; this is a partial view of the same database). Pool only
by design — no url form — so the adapter never owns an uncloseable
pool. The structural BetterAuthDb form stays for tests and advanced
injection. Covered by shared-pool CRUD, rollback-through-the-pool, and
pool-visibility tests.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…he shared pool

Operator direction on PR #968, both design comments:

PSL: the app contract moves from TS builders to contract.prisma using
the cross-space reference grammar (user better-auth:public.User
@relation(..., onDelete: Cascade), supabase example precedent). The
lowering is identical — same storage hash, byte-identical emitted
contract.json — so the committed migrations and mirrors stay valid.

One client: db.ts constructs a single postgres() client over the
aggregate on an app-owned pool; auth.ts hands the same pool to
prismaNextAdapter({ pg }); the space view is no longer user-visible
ceremony. /api/me returns the session and its user through BetterAuth
plus the Profile through the ORM; the cascade proof observes the
cross-space FK at the database level through the shared pool. READMEs
retell the one-client story with the aggregate-no-folding explanation
moved to background. Harness setup now releases the dev database and
temp dir on mid-setup failure, and teardown never rejects (bounded
close, rmSync in finally).

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
The include-decode codec boundary is fixed on main (TML-2990): include
cells cross codec decodeJson with the timestamptz offset rendering
accepted, which is exactly what these eight joins-suite tests needed.
All eight pass un-gated; the disable categories drop from four to
three (non-goals, generateId leak, upstream transaction wrapper).
Conformance: 164 passed / 61 skipped.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Our importer entries carried pre-rebase peer resolutions (pg 8.21,
vitest 4.1.8, mongodb 7.2) and the removed sql-contract-ts devDep;
plain pnpm install realigns them with main catalog pins.

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
@SevInf
SevInf force-pushed the tml-2994-better-auth-extension branch from e6c506d to 78de265 Compare July 15, 2026 13:48

@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: 2

🤖 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/better-auth/test/example.integration.test.ts`:
- Around line 118-128: Update the FK query in the integration test to verify the
documented public.profile(userId) → public.user(id) relationship, not merely a
cascade to a relation named user. Join pg_namespace and pg_attribute for both
tables, filter the public schema and the profile/user column names, and retain
the on_delete assertion.
- Around line 186-194: Make the test named “cascades profile deletion when the
user is deleted” self-contained by creating the Ada user and profile records
within the test or an existing shared setup helper before querying Profile.
Preserve the current database-level DELETE and cascade assertions, and ensure
the setup does not depend on the preceding test.
🪄 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: cc0a0f92-0034-48e2-9e3f-d01cf092b228

📥 Commits

Reviewing files that changed from the base of the PR and between e6c506d and 78de265.

⛔ Files ignored due to path filters (24)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • projects/extension-better-auth/plan.md is excluded by !projects/**
  • projects/extension-better-auth/qa/manual-qa.md is excluded by !projects/**
  • projects/extension-better-auth/qa/qa-run-2026-07-13.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/01-scaffold-and-contract-space.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/02-managed-space-lifecycle-test.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/03-branded-contract-handles-r2.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/03-branded-contract-handles.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/04-adapter-core-crud-r2.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/04-adapter-core-crud.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/05-adapter-consume-transaction-join.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/06-conformance-and-e2e-integration-r2.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/06-conformance-and-e2e-integration-r3.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/06-conformance-and-e2e-integration.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/07-example-app-r2.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/07-example-app-r3.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/07-example-app.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/08-docs-and-close-out-prep-r2.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/08-docs-and-close-out-prep-r3.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/dispatches/08-docs-and-close-out-prep.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/plan.md is excluded by !projects/**
  • projects/extension-better-auth/slices/better-auth-extension/spec.md is excluded by !projects/**
  • projects/extension-better-auth/spec.md is excluded by !projects/**
  • projects/extension-better-auth/trace.jsonl is excluded by !projects/**
📒 Files selected for processing (69)
  • .agents/rules/contract-space-package-layout.mdc
  • architecture.config.json
  • biome.jsonc
  • docs/architecture docs/adrs/ADR 212 - Contract spaces.md
  • docs/architecture docs/subsystems/6. Ecosystem Extensions & Packs.md
  • examples/better-auth/.gitignore
  • examples/better-auth/README.md
  • examples/better-auth/biome.jsonc
  • examples/better-auth/migrations/app/20260713T0837_init/end-contract.d.ts
  • examples/better-auth/migrations/app/20260713T0837_init/end-contract.json
  • examples/better-auth/migrations/app/20260713T0837_init/migration.json
  • examples/better-auth/migrations/app/20260713T0837_init/migration.ts
  • examples/better-auth/migrations/app/20260713T0837_init/ops.json
  • examples/better-auth/migrations/better-auth/20260713T0717_create_auth_tables/migration.json
  • examples/better-auth/migrations/better-auth/20260713T0717_create_auth_tables/ops.json
  • examples/better-auth/migrations/better-auth/contract.d.ts
  • examples/better-auth/migrations/better-auth/contract.json
  • examples/better-auth/migrations/better-auth/refs/head.json
  • examples/better-auth/package.json
  • examples/better-auth/prisma-next.config.ts
  • examples/better-auth/src/auth.ts
  • examples/better-auth/src/main.ts
  • examples/better-auth/src/prisma/contract.d.ts
  • examples/better-auth/src/prisma/contract.json
  • examples/better-auth/src/prisma/contract.prisma
  • examples/better-auth/src/prisma/db.ts
  • examples/better-auth/src/server.ts
  • examples/better-auth/test/example.integration.test.ts
  • examples/better-auth/tsconfig.json
  • examples/better-auth/vitest.config.ts
  • packages/3-extensions/better-auth/README.md
  • packages/3-extensions/better-auth/biome.jsonc
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/end-contract.d.ts
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/end-contract.json
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/migration.json
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/migration.ts
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/ops.json
  • packages/3-extensions/better-auth/migrations/refs/head.json
  • packages/3-extensions/better-auth/package.json
  • packages/3-extensions/better-auth/prisma-next.config.ts
  • packages/3-extensions/better-auth/src/adapter/db-surface.ts
  • packages/3-extensions/better-auth/src/adapter/errors.ts
  • packages/3-extensions/better-auth/src/adapter/index.ts
  • packages/3-extensions/better-auth/src/adapter/join.ts
  • packages/3-extensions/better-auth/src/adapter/model-map.ts
  • packages/3-extensions/better-auth/src/adapter/where.ts
  • packages/3-extensions/better-auth/src/contract/contract.d.ts
  • packages/3-extensions/better-auth/src/contract/contract.json
  • packages/3-extensions/better-auth/src/contract/contract.prisma
  • packages/3-extensions/better-auth/src/contract/handles.ts
  • packages/3-extensions/better-auth/src/exports/adapter.ts
  • packages/3-extensions/better-auth/src/exports/contract.ts
  • packages/3-extensions/better-auth/src/exports/pack.ts
  • packages/3-extensions/better-auth/src/exports/runtime.ts
  • packages/3-extensions/better-auth/src/pack/index.ts
  • packages/3-extensions/better-auth/src/runtime/descriptor.ts
  • packages/3-extensions/better-auth/test/adapter-advanced.test.ts
  • packages/3-extensions/better-auth/test/adapter-crud.test.ts
  • packages/3-extensions/better-auth/test/adapter-errors.test.ts
  • packages/3-extensions/better-auth/test/adapter-pool-form.test.ts
  • packages/3-extensions/better-auth/test/adapter-types.test-d.ts
  • packages/3-extensions/better-auth/test/contract-handles.test.ts
  • packages/3-extensions/better-auth/test/contract-types.test-d.ts
  • packages/3-extensions/better-auth/test/descriptor.test.ts
  • packages/3-extensions/better-auth/test/runtime-descriptor.test.ts
  • packages/3-extensions/better-auth/tsconfig.json
  • packages/3-extensions/better-auth/tsconfig.prod.json
  • packages/3-extensions/better-auth/tsdown.config.ts
  • packages/3-extensions/better-auth/vitest.config.ts
🚧 Files skipped from review as they are similar to previous changes (63)
  • packages/3-extensions/better-auth/biome.jsonc
  • packages/3-extensions/better-auth/migrations/refs/head.json
  • examples/better-auth/biome.jsonc
  • examples/better-auth/migrations/better-auth/contract.d.ts
  • examples/better-auth/.gitignore
  • examples/better-auth/prisma-next.config.ts
  • packages/3-extensions/better-auth/src/exports/contract.ts
  • packages/3-extensions/better-auth/tsdown.config.ts
  • examples/better-auth/src/prisma/contract.prisma
  • packages/3-extensions/better-auth/tsconfig.prod.json
  • examples/better-auth/migrations/better-auth/20260713T0717_create_auth_tables/migration.json
  • examples/better-auth/migrations/app/20260713T0837_init/end-contract.json
  • packages/3-extensions/better-auth/tsconfig.json
  • examples/better-auth/migrations/better-auth/contract.json
  • examples/better-auth/vitest.config.ts
  • packages/3-extensions/better-auth/src/runtime/descriptor.ts
  • biome.jsonc
  • examples/better-auth/tsconfig.json
  • packages/3-extensions/better-auth/src/exports/pack.ts
  • packages/3-extensions/better-auth/vitest.config.ts
  • examples/better-auth/migrations/better-auth/20260713T0717_create_auth_tables/ops.json
  • packages/3-extensions/better-auth/README.md
  • .agents/rules/contract-space-package-layout.mdc
  • packages/3-extensions/better-auth/src/exports/runtime.ts
  • examples/better-auth/src/auth.ts
  • examples/better-auth/package.json
  • packages/3-extensions/better-auth/src/contract/contract.prisma
  • packages/3-extensions/better-auth/src/exports/adapter.ts
  • examples/better-auth/migrations/app/20260713T0837_init/migration.json
  • packages/3-extensions/better-auth/package.json
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/ops.json
  • examples/better-auth/src/server.ts
  • architecture.config.json
  • packages/3-extensions/better-auth/src/pack/index.ts
  • packages/3-extensions/better-auth/test/contract-handles.test.ts
  • packages/3-extensions/better-auth/test/adapter-errors.test.ts
  • packages/3-extensions/better-auth/src/adapter/errors.ts
  • examples/better-auth/migrations/app/20260713T0837_init/migration.ts
  • packages/3-extensions/better-auth/test/contract-types.test-d.ts
  • packages/3-extensions/better-auth/src/contract/handles.ts
  • examples/better-auth/migrations/app/20260713T0837_init/ops.json
  • packages/3-extensions/better-auth/src/contract/contract.json
  • examples/better-auth/src/main.ts
  • docs/architecture docs/adrs/ADR 212 - Contract spaces.md
  • packages/3-extensions/better-auth/src/adapter/where.ts
  • examples/better-auth/migrations/app/20260713T0837_init/end-contract.d.ts
  • packages/3-extensions/better-auth/test/runtime-descriptor.test.ts
  • packages/3-extensions/better-auth/src/adapter/db-surface.ts
  • packages/3-extensions/better-auth/prisma-next.config.ts
  • examples/better-auth/src/prisma/db.ts
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/migration.ts
  • packages/3-extensions/better-auth/src/adapter/join.ts
  • packages/3-extensions/better-auth/test/descriptor.test.ts
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/migration.json
  • packages/3-extensions/better-auth/src/contract/contract.d.ts
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/end-contract.json
  • packages/3-extensions/better-auth/src/adapter/index.ts
  • packages/3-extensions/better-auth/test/adapter-types.test-d.ts
  • packages/3-extensions/better-auth/src/adapter/model-map.ts
  • examples/better-auth/src/prisma/contract.d.ts
  • packages/3-extensions/better-auth/migrations/20260713T0717_create_auth_tables/end-contract.d.ts
  • docs/architecture docs/subsystems/6. Ecosystem Extensions & Packs.md
  • examples/better-auth/README.md

Comment on lines +118 to +128
const rows = await withClient(database.connectionString, async (client) => {
const result = await client.query(
`SELECT confrel.relname AS references_table, con.confdeltype::text AS on_delete
FROM pg_constraint con
JOIN pg_class rel ON rel.oid = con.conrelid
JOIN pg_class confrel ON confrel.oid = con.confrelid
WHERE rel.relname = 'profile' AND con.contype = 'f'`,
);
return result.rows;
});
expect(rows).toEqual([{ references_table: 'user', on_delete: 'c' }]);

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

Verify the schema and referenced column in the FK assertion.

This query can pass for any cascading profile FK targeting a relation named user; it does not prove the documented "public"."profile"(userId) → "public"."user"(id) contract. Include namespace and attribute joins in the query.

🤖 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/better-auth/test/example.integration.test.ts` around lines 118 -
128, Update the FK query in the integration test to verify the documented
public.profile(userId) → public.user(id) relationship, not merely a cascade to a
relation named user. Join pg_namespace and pg_attribute for both tables, filter
the public schema and the profile/user column names, and retain the on_delete
assertion.

Comment on lines +186 to +194
it('cascades profile deletion when the user is deleted', {
timeout: timeouts.databaseOperation,
}, async () => {
const before = await appDb.db.orm.public.Profile.where({ id: 'profile-ada' }).first();
expect(before).not.toBeNull();

// Delete the user at the database level: the cross-space FK's
// ON DELETE CASCADE removes the dependent profile row.
await appDb.pool.query('DELETE FROM "public"."user" WHERE email = $1', ['ada@example.com']);

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

Make the cascade test self-contained.

This test relies on the preceding test to create ada@example.com and profile-ada. Running it alone—or after the preceding test fails—does not exercise the cascade. Seed its user and profile within this test or a shared setup helper.

🤖 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/better-auth/test/example.integration.test.ts` around lines 186 -
194, Make the test named “cascades profile deletion when the user is deleted”
self-contained by creating the Ada user and profile records within the test or
an existing shared setup helper before querying Profile. Preserve the current
database-level DELETE and cascade assertions, and ensure the setup does not
depend on the preceding test.

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