@aventara/prisma7-adapter 0.1.0-pilot.3 → 1.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +190 -0
  2. package/README.md +44 -29
  3. package/dist/discovery/artifact.loader.d.ts +2 -7
  4. package/dist/discovery/artifact.loader.js +8 -1
  5. package/dist/discovery/capability-surface.interface.d.ts +7 -64
  6. package/dist/discovery/capability.deriver.d.ts +2 -47
  7. package/dist/discovery/capability.deriver.js +3 -0
  8. package/dist/discovery/discovery-artifact.interface.d.ts +50 -159
  9. package/dist/discovery/discovery-artifact.interface.js +1 -1
  10. package/dist/discovery/enum.mapper.d.ts +0 -24
  11. package/dist/discovery/field.mapper.d.ts +4 -115
  12. package/dist/discovery/field.mapper.js +7 -7
  13. package/dist/discovery/identifier.mapper.d.ts +4 -84
  14. package/dist/discovery/model.discoverer.d.ts +3 -70
  15. package/dist/discovery/model.discoverer.js +5 -4
  16. package/dist/discovery/operations.deriver.d.ts +2 -37
  17. package/dist/discovery/scalar.mapper.d.ts +1 -32
  18. package/dist/discovery/scalar.mapper.js +2 -2
  19. package/dist/discovery/schema-facts.interface.d.ts +3 -83
  20. package/dist/discovery/schema.reader.d.ts +0 -133
  21. package/dist/discovery/types.reader.d.ts +1 -9
  22. package/dist/discovery/types.reader.js +18 -7
  23. package/dist/discovery/vocabulary.dictionary.d.ts +8 -134
  24. package/dist/discovery/vocabulary.dictionary.js +2 -2
  25. package/dist/errors/error.normalizer.d.ts +5 -168
  26. package/dist/errors/prisma-error.reader.d.ts +1 -93
  27. package/dist/execution/first-variant.emulator.d.ts +5 -28
  28. package/dist/execution/operation.executor.d.ts +3 -63
  29. package/dist/execution/result.shaper.d.ts +0 -56
  30. package/dist/execution/transaction.executor.d.ts +2 -99
  31. package/dist/generate/artifact.emitter.d.ts +1 -35
  32. package/dist/generate/artifact.emitter.js +31 -47
  33. package/dist/generate/declared-support.reader.d.ts +0 -20
  34. package/dist/generate/discovery.cli.d.ts +0 -4
  35. package/dist/generate/existence.gate.d.ts +3 -37
  36. package/dist/generate/index.d.ts +4 -57
  37. package/dist/generate/prisma-cli.resolver.d.ts +0 -22
  38. package/dist/generate/prisma.generate.d.ts +14 -83
  39. package/dist/generate/reader-crosscheck.d.ts +3 -88
  40. package/dist/generate/reader-crosscheck.js +4 -4
  41. package/dist/prisma-client.interface.d.ts +5 -25
  42. package/dist/prisma.adapter.d.ts +21 -22
  43. package/dist/prisma.adapter.js +1 -0
  44. package/dist/provider/provider-profile.d.ts +6 -14
  45. package/dist/translation/identifier.addressing.d.ts +0 -38
  46. package/dist/translation/mutation.translator.d.ts +6 -52
  47. package/dist/translation/mutation.translator.js +1 -1
  48. package/dist/translation/operation.translator.d.ts +3 -103
  49. package/dist/translation/order-pagination.translator.d.ts +2 -48
  50. package/dist/translation/order-pagination.translator.js +1 -1
  51. package/dist/translation/projection.translator.d.ts +1 -46
  52. package/dist/translation/scalar.conversion.d.ts +2 -65
  53. package/dist/translation/scalar.conversion.js +1 -1
  54. package/dist/translation/where.translator.d.ts +0 -52
  55. package/package.json +5 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,190 @@
1
+ # @aventara/prisma7-adapter
2
+
3
+ ## 1.0.0-rc.0
4
+
5
+ ### Major Changes
6
+
7
+ - First release candidate of Aventara 1.0. The public API is now considered stable; changes before 1.0.0 are possible
8
+ based on feedback.
9
+
10
+ ### Minor Changes
11
+
12
+ - 24ffbab: A nested `$update` or `$delete` through a to-many relation filters plain fields only on Prisma 7, and Aventara now says
13
+ so up front: a relation filter in its `where` — `{ posts: { $update: { where: { category: { name: "x" } }, … } } }`, at
14
+ any depth, under `AND`/`OR`/`NOT` too — is refused with `A2004` / `V1006` before the database is asked, and is a type
15
+ error in `framework.application`, `framework.client`, `appTx`/`clientTx` and the generated client. Before, it reached
16
+ Prisma and fail as `A3001`. A to-one relation's nested filter keeps relation filters. The limit is stated by the adapter
17
+ in the Contract (`nestedWriteFilter: "scalar"`), so the ClientContract hash changes: regenerate the client.
18
+
19
+ ### Patch Changes
20
+
21
+ - 24ffbab: Each package ships its `CHANGELOG.md`, so the release notes are in the installed package beside the README.
22
+ - a973e89: A write that sets an optional relation both ways — its reference field and the relation itself, such as `categoryId`
23
+ and `category: { $connect: … }` in the same record — is now refused before it reaches the database, exactly like a
24
+ required relation: `A2004` with a `V1012` issue at the relation, message `Set "categoryId" or "category", not both.`
25
+ Before, the database refused it and the caller received `A3001`. It applies to `create`, `update` and `upsert`, to
26
+ nested writes at any depth, and to each step of a transaction.
27
+
28
+ The discovery artifact now records, for each relation, the fields that hold its reference (`references` on each edge
29
+ of `referentialActions`), and a create requirement no longer repeats them. Regenerate your artifact with
30
+ `npm run aventara:prepare` (or `aventara-prisma7-generate`); an artifact from an earlier version is refused with a
31
+ message that says so. An adapter's `referentialActions` edges carry the same `references` member.
32
+
33
+ - a973e89: With the Prisma adapter, a record that writes one relation through its reference field and another relation directly —
34
+ such as `authorId` beside `category: { $connect: … }` — is now refused before it reaches the database: `A2004` with a
35
+ `V1012` issue at the relation write, message
36
+ `Use relation fields only, or reference fields only, in one record: "authorId" is a reference field and "category" is a relation.`
37
+ Before, Prisma refused it and the caller received `A3001`. Write the record's relations all as relations
38
+ (`author: { $connect: … }, category: { $connect: … }`) or all through their fields (`authorId`, `categoryId`). A
39
+ reference field beside a relation that holds no reference on that side — `mentorId` beside `posts: { $create: … }` on
40
+ an author — is unchanged and still accepted. It applies to `create`, `update` and `upsert`, to nested writes at any
41
+ depth, and to each step of a transaction.
42
+
43
+ The limit is part of the Contract: a Contract whose data source has it carries `referenceWrites: "uniform"`, so the
44
+ Contract hash of every Prisma-backed application changes. Regenerate your discovery artifact
45
+ (`npm run aventara:prepare`) and your generated client (`avclient generate`). An adapter states the limit with
46
+ `referenceWrites: "uniform"` in its model; without it, nothing changes.
47
+
48
+ - Updated dependencies [24ffbab]
49
+ - Updated dependencies [24ffbab]
50
+ - Updated dependencies [24ffbab]
51
+ - Updated dependencies [24ffbab]
52
+ - Updated dependencies [24ffbab]
53
+ - Updated dependencies [24ffbab]
54
+ - Updated dependencies [24ffbab]
55
+ - Updated dependencies [24ffbab]
56
+ - Updated dependencies [24ffbab]
57
+ - Updated dependencies [24ffbab]
58
+ - Updated dependencies [24ffbab]
59
+ - Updated dependencies [24ffbab]
60
+ - Updated dependencies [a973e89]
61
+ - Updated dependencies [1e65e9a]
62
+ - Updated dependencies
63
+ - Updated dependencies [a973e89]
64
+ - Updated dependencies [a973e89]
65
+ - Updated dependencies [1e65e9a]
66
+ - Updated dependencies [1e65e9a]
67
+ - Updated dependencies [24ffbab]
68
+ - Updated dependencies [2d00ad7]
69
+ - Updated dependencies [384a50c]
70
+ - Updated dependencies [d43d1cc]
71
+ - Updated dependencies [3f98749]
72
+ - Updated dependencies [032f792]
73
+ - Updated dependencies [dbe6961]
74
+ - @aventara/core@1.0.0-rc.0
75
+
76
+ ## 0.1.0-pilot.4
77
+
78
+ ### Patch Changes
79
+
80
+ - f37dcd6: A discovery artifact written by an older version of the adapter now fails the TypeScript build with an error that
81
+ says what to do: `Property 'regenerate_with_aventara_prepare' is missing`. Run `npm run aventara:prepare`
82
+ (or `aventara-prisma7-generate`) to regenerate it. Every newly generated artifact carries the property, and
83
+ `createPrismaAdapter` refuses an out-of-date artifact at startup with the same instruction.
84
+ - f37dcd6: The documentation shipped with the package — the comments an editor shows on hover, and the README — now describes
85
+ what each export does, how to use it, what it returns and what it throws. Notes about how the package was built and
86
+ why are no longer part of what is published.
87
+
88
+ The generate step's error messages name "the generated client's types" in words instead of Prisma's emoji labels.
89
+
90
+ - Updated dependencies [f37dcd6]
91
+ - @aventara/core@0.1.0-pilot.4
92
+
93
+ ## 0.1.0-pilot.3
94
+
95
+ ### Patch Changes
96
+
97
+ - The manifest declares the adapter's CLI alias, `aventara.adapter.alias: "prisma7"`: the value `aventara new --orm` and `aventara init --orm` take.
98
+ - @aventara/core@0.1.0-pilot.3
99
+
100
+ ## 0.1.0-pilot.2
101
+
102
+ ### Patch Changes
103
+
104
+ - e69e88d: The published packages are written for the people who use them. The JavaScript ships without comments; the type
105
+ declarations keep their documentation, and it, the error messages, the protocol fixtures' labels and the comments in a
106
+ generated client no longer carry the project's internal development notes.
107
+ - 06ed5d8: A relation write that would leave a child without the parent its required relation demands — `$set: []` or
108
+ `$disconnect` on a to-many whose children require the parent — now answers `A2008` (the same conflict a violated
109
+ reference constraint answers) instead of `A3001`. Nothing is written.
110
+ - 23937f8: A create that lacks a value the database requires is refused as `A2004` with a `V1000` issue at the value's path,
111
+ instead of reaching the database and answering `A3001`. It covers `create.one`, every row of `create.many`, `upsert`'s
112
+ `create` branch, nested `$create` (and `$connectOrCreate`/`$upsert` creates) at any depth, and transaction steps
113
+ (`["operations", i, "args", …]`). A required relation is satisfied by writing it or every scalar that holds its
114
+ reference; a nested create never needs the relation it is created through. Defaults, `@updatedAt`, autoincrement,
115
+ optional and list columns are never required. The check runs after pipes and computed behaviours, so a value they
116
+ supply counts.
117
+
118
+ An adapter states the requirements through the new optional `Adapter.createRequirements`; the Prisma 7 adapter reads
119
+ them from the generated client's create inputs at generate time and records them in the discovery artifact, so the
120
+ artifact must be regenerated (`aventara:prepare`).
121
+
122
+ - 287fc27: A discovery artifact older than the Prisma client is refused at startup. The artifact now records the client it was
123
+ generated from (each model and its field references), and `createPrismaAdapter` — which runs before the application
124
+ listens — compares that record with the client it is given: a model or field added, removed or renamed since the
125
+ artifact was generated stops the server with one sentence naming the difference and `aventara:prepare`. An artifact
126
+ generated by an earlier version carries no record and is refused with the same instruction; a project set up by
127
+ `aventara` regenerates it on install.
128
+ - Updated dependencies [c2a3b7c]
129
+ - Updated dependencies [e69e88d]
130
+ - Updated dependencies [ab58424]
131
+ - Updated dependencies [23937f8]
132
+ - @aventara/core@0.1.0-pilot.2
133
+
134
+ ## 0.1.0-pilot.1
135
+
136
+ ### Patch Changes
137
+
138
+ - 799346e: The package READMEs (each package's npm page) are rewritten for the people who use the packages. The Prisma 7
139
+ adapter's README now states the actual behaviour of a to-many relation or `$count` projected on
140
+ `update.many`/`create.many`: a validation refusal, `A2004` with `V1008`, before any statement runs (it said `A3001`).
141
+ Every install or run command names the `@pilot` dist-tag.
142
+ - Updated dependencies [a45f8a8]
143
+ - Updated dependencies [799346e]
144
+ - @aventara/core@0.1.0-pilot.1
145
+
146
+ ## 0.1.0-pilot.0
147
+
148
+ ### Minor Changes
149
+
150
+ - First pilot release of `@aventara/prisma7-adapter`, the Prisma 7 adapter. Peers: `@prisma/client`, `prisma` and the
151
+ driver adapters `^7.10.0`; `typescript` `>=5.5.0 <7` for the generate step. `typescript` and `prisma` are optional
152
+ peers used only by the generate step, so neither enters a deployed install.
153
+
154
+ - **`createPrismaAdapter({ client, discovery, provider, driver, transactionOptions? })`** is the entry point. Your
155
+ application owns the Prisma client, built with a driver adapter; the adapter never builds one, reads a connection
156
+ string or reads an environment variable. `provider` and `driver` are stated by you, and the driver decides whether
157
+ transactions are offered. It is `async` because it verifies the discovery artifact.
158
+ - **Discovery runs at generate time.** `aventara-prisma7-generate` (or the `@aventara/prisma7-adapter/generate`
159
+ export) reads `schema.prisma` and the generated client's types and writes one module, the discovery artifact: the
160
+ model, how each Resource is addressed, its referential actions, a `modelHash` and the Prisma version. At startup the
161
+ adapter checks that the artifact matches its `modelHash` and that the installed `@prisma/client` major is the one it
162
+ was generated against, and stops with an instruction to regenerate otherwise. The artifact is rewritten only when its
163
+ bytes change, and replaced atomically, so a reader never sees a half-written file.
164
+ - **The Contract says what Prisma serves.** `createMany`/`updateMany` results project fewer relations than a read
165
+ (the rule is which side holds the foreign key, not cardinality); the Contract advertises that per argument position,
166
+ and an out-of-position projection or nested write is refused with `A2004` (`V1008` / `V1006`) before any query.
167
+ - **Cascading deletes.** The schema's referential actions are stated to the Framework, so a transaction step that
168
+ depends on rows an earlier step's cascading delete removed is refused before the plan runs (`A2004`/`V1019`).
169
+ - **Conflicts are told apart:** `A2008 CONFLICT` for a uniqueness or reference constraint, `A2013
170
+ CONCURRENT_MODIFICATION` when another operation changed the same state, `A2014 STALE_SELECTION` when the selected
171
+ record changed before the write.
172
+ - **Behaviours to know:**
173
+ - `delete.many` is not offered (Prisma 7 has no `deleteManyAndReturn`); use `delete.count`.
174
+ - `update.first` and `delete.first` are a read followed by a write narrowed to the record the read selected, and are
175
+ not atomic: the record written is one the read selected, or nothing is written and you are told (`A2014`). A
176
+ record that comes to sort first between the two statements is not seen. If firstness at the instant of the write
177
+ matters, select the record yourself and use `update.unique`/`delete.unique`.
178
+ - A serialization failure (`P2034`) is never retried automatically: a retry would re-run your pipeline callbacks.
179
+ Retrying is yours to decide.
180
+ - Whether transactions are offered depends on the driver and is part of the ClientContract hash, so generate the
181
+ client against the deployment's own driver.
182
+ - No raw SQL is ever issued.
183
+ - PostgreSQL and SQLite are supported.
184
+ - Every export has a `default` condition, so a CommonJS project can `require()` it. The manifest declares what the
185
+ adapter supports (providers, drivers, generators), which `@aventara/cli` reads.
186
+
187
+ ### Patch Changes
188
+
189
+ - Updated dependencies
190
+ - @aventara/core@0.1.0-pilot.0
package/README.md CHANGED
@@ -4,16 +4,16 @@ The Prisma 7 Adapter for Aventara. It discovers what a Prisma schema and its gen
4
4
  compiles that into an Aventara `AdapterModel`, and executes validated Aventara operations against a
5
5
  Prisma client the application owns.
6
6
 
7
- This document is written for the person wiring it into an application. It states what the adapter
7
+ This document is written for the person setting it up in an application. It states what the adapter
8
8
  does, what it deliberately does **not** offer, and the two places where a correct Aventara operation
9
9
  has a weaker guarantee than its name suggests. Everything a consumer has to know is in the body of
10
10
  this file; nothing load-bearing is in a footnote.
11
11
 
12
- - **Status:** pilot. Two engines are proven — PostgreSQL and SQLite. Every other Prisma provider is
12
+ - **Status:** release candidate. Two engines are proven — PostgreSQL and SQLite. Every other Prisma provider is
13
13
  unmeasured, and this file says so where it matters.
14
14
  - **Peer dependencies:** `@prisma/client` `^7.10.0` (required); `prisma`, `@prisma/adapter-better-sqlite3`
15
15
  and `@prisma/adapter-pg` `^7.10.0` and `typescript` `>=5.5.0 <7` (optional). 7.10.0 is the version the
16
- error map was measured on; the adapter is bound to Prisma **7**'s error surface; see
16
+ error map targets; the adapter is bound to Prisma **7**'s error surface; see
17
17
  [Prisma 7 only](#prisma-7-only).
18
18
 
19
19
  ---
@@ -59,8 +59,8 @@ checks is a `crypto.subtle` promise — so every fence fires once, here, before
59
59
 
60
60
  ### `maxWait`, `timeout` and `isolationLevel` are adapter-owned
61
61
 
62
- They are provider concepts. Core's `Adapter.transaction(work)` seam carries no options and must not
63
- grow any, so these live on `createPrismaAdapter({ transactionOptions })` and are not Aventara public
62
+ They are provider concepts. Core's `Adapter.transaction(work)` interface carries no options and will
63
+ not grow any, so these live on `createPrismaAdapter({ transactionOptions })` and are not Aventara public
64
64
  configuration. Prisma's own defaults stand when they are absent: `maxWait` 2000 ms, `timeout` 5000 ms.
65
65
 
66
66
  Supported isolation levels come from the declared provider: PostgreSQL admits `ReadUncommitted`,
@@ -127,14 +127,15 @@ project's `devDependencies` — if you are generating a Prisma client at all, yo
127
127
  The step reads two sources in the same instant — the `schema.prisma` text and the generated client's
128
128
  own public types — and cross-checks them against each other. What it emits is a TypeScript module
129
129
  exporting one `discovery` value with eight members: the canonical `AdapterModel` (scalars, enums,
130
- Resources with fields, identifiers and available operations, plus the `transactions` capability), the
130
+ Resources with fields, identifiers and available operations, plus the `transactions` capability and the
131
+ `referenceWrites` limit), the
131
132
  Prisma addressing each Resource is reached by, the resolved referential actions per Resource, a record
132
133
  of the generated client it was read from (each model and its field references), the create
133
134
  requirements per Resource, a `modelHash`, and the `prismaVersion` it was generated against.
134
135
 
135
136
  The create requirements are what a create cannot do without — a column with no default, or a required
136
- relation, which may be written instead through the scalar fields that hold its reference — read off the
137
- question marks of the generated create inputs. The framework checks every create against them after
137
+ relation, which may be written instead through the scalar fields that hold its reference (the edge's
138
+ `references`, below) — read off the question marks of the generated create inputs. The framework checks every create against them after
138
139
  your pipes and computed behaviours have run, and answers a missing value as `A2004` with a `V1000`
139
140
  issue at its path (`["arguments", "data", "title"]`, or `["arguments", "data", "author"]` for a
140
141
  required relation) instead of letting the database refuse it. A nested create does not need the
@@ -177,7 +178,7 @@ Operation availability is **derived**, never assumed. Every omission is decided
177
178
  three facts: the generated delegate's own method set, the Resource's identifiers, and the driver's
178
179
  transaction capability.
179
180
 
180
- Across the pilot's representative schema the compiled Contracts advertise **fifteen** distinct
181
+ For a typical schema the compiled Contracts advertise **fifteen** distinct
181
182
  `family.variant` operations, identically on all three Contract scopes. `delete.many` is **not** one of
182
183
  them.
183
184
 
@@ -198,7 +199,7 @@ are derived — so `if (provider === …)` is a compile error rather than a code
198
199
  the rule because a provider table is a second source of truth that drifts, silently, one Prisma
199
200
  release at a time.
200
201
 
201
- Observed consequences on the two pilot engines, all of them read from generated types rather than
202
+ Consequences on PostgreSQL and SQLite, all of them read from generated types rather than
202
203
  tabulated:
203
204
 
204
205
  | Capability | PostgreSQL | SQLite | Read from |
@@ -213,7 +214,7 @@ tabulated:
213
214
  **Beyond PostgreSQL and SQLite nothing here is measured.** MySQL, SQL Server, MongoDB, CockroachDB
214
215
  and D1 were not generated against. Because every capability is read from the client you actually
215
216
  generated, a provider that lacks something advertises less rather than failing at runtime — but the
216
- pilot makes no claim about them.
217
+ this release makes no claim about them.
217
218
 
218
219
  ### `update.many` and `create.many` cannot project a to-many relation
219
220
 
@@ -277,7 +278,7 @@ no re-assertion can, because the missing fact is about a record that was never r
277
278
 
278
279
  This is a **documented, permitted property** of a framework-manufactured variant, published as
279
280
  policy rather than discovered in practice. Serializable isolation is not the remedy: core's
280
- `Adapter.transaction(work)` seam carries no isolation parameter, so an adapter cannot set a level for
281
+ `Adapter.transaction(work)` interface carries no isolation parameter, so an adapter cannot set a level for
281
282
  a manufactured variant or for a plan, and a provider transaction's level is fixed before its first
282
283
  statement, so a per-operation escalation is not expressible at all. SQLite admits `Serializable`
283
284
  only, and the residual persists there regardless.
@@ -293,7 +294,7 @@ closed" without naming one is wrong.
293
294
  ### Why the write is by identifier and not by Prisma's `limit`
294
295
 
295
296
  Prisma **does** offer a top-level `limit` on `updateMany`, `deleteMany` and `updateManyAndReturn` in
296
- 7.10.0 — measured, present on every model of both pilot engines. The adapter does not use it, and the
297
+ 7.10.0 — present on every model on PostgreSQL and SQLite. The adapter does not use it, and the
297
298
  reason is not concealment: Aventara's doctrine is to **expose** what the ORM offers, not to hide it.
298
299
 
299
300
  The reason is that `limit` **cannot express this operation**. None of those three argument types
@@ -321,13 +322,13 @@ prose, may change, and deliberately says less than the provider did.
321
322
 
322
323
  ### The three conflict codes
323
324
 
324
- Three situations used to collapse into one `409 A2008`. They no longer do.
325
+ Three different situations each have their own code.
325
326
 
326
327
  | Code | Means | Retry? |
327
328
  |---|---|---|
328
329
  | `A2008 CONFLICT` | The operation would violate a **uniqueness or reference constraint** the Resource declares. Raised from Prisma's `P2002` and `P2003`. | No — re-running the same request unchanged fails the same way. |
329
330
  | `A2013 CONCURRENT_MODIFICATION` | Another operation **modified the same state concurrently**, so this operation was not applied and nothing was written. Raised from Prisma's `P2034`. | Yes, the same request may succeed. The framework performs **no** automatic retry. |
330
- | `A2014 STALE_SELECTION` | The state this operation had **selected changed** before its effect could be applied, so nothing was written. Raised by the first-variant manufacture's own seam. | Yes, the same request may succeed. No automatic retry. |
331
+ | `A2014 STALE_SELECTION` | The state this operation had **selected changed** before its effect could be applied, so nothing was written. Raised by the adapter's `update.first` and `delete.first`. | Yes, the same request may succeed. No automatic retry. |
331
332
 
332
333
  `A2008` **narrowed** to the constraint case; it was not repurposed. If you previously treated `A2008`
333
334
  as "any conflict", the two situations that left it now have their own codes.
@@ -342,7 +343,7 @@ Two consequences worth stating explicitly:
342
343
  constraint; the envelope does **not** say so, and nothing should be parsed out of it.
343
344
  - **A write conflict and a deadlock share `A2013` deliberately.** Both arrive as `P2034` with
344
345
  identical `meta` keys, differing only in a provider-owned code (`40001` versus `40P01`) that never
345
- crosses the boundary. They are not separable at this seam, so one outcome is the honest answer.
346
+ crosses the boundary. They cannot be told apart here, so one outcome is the honest answer.
346
347
 
347
348
  ### None of the three carries `cause.issues`
348
349
 
@@ -380,13 +381,13 @@ hook.
380
381
  answers `A3002 TRANSACTION_ERROR` for the failure that *ends* the transaction, and never reads the
381
382
  message text to decide anything.
382
383
 
383
- What is separable **structurally** (measured on both pilot engines, never inferred from docs):
384
+ What is separable **structurally** (on PostgreSQL and SQLite, never inferred from docs):
384
385
 
385
386
  | Situation | Code | `meta` |
386
387
  |---|---|---|
387
388
  | Transaction-timeout expiry | `P2028` | `{ operation, timeout, timeTaken }` — the expiry is identifiable |
388
389
  | `maxWait` acquisition timeout | `P2028` | **empty** — identifiable *by* that emptiness, and by nothing finer |
389
- | Valid-but-unsupported isolation level (e.g. `Snapshot` on either pilot engine) | `P2028` | `{ isolationLevel }` |
390
+ | Valid-but-unsupported isolation level (e.g. `Snapshot` on either engine) | `P2028` | `{ isolationLevel }` |
390
391
  | An isolation level outside Prisma's own set (e.g. `"Bogus"`) | **`P2023`** | `{ providedIsolationLevel }` — a different code entirely |
391
392
 
392
393
  The remaining **four** classes — transaction not found, already committed, already rolled back, and
@@ -431,7 +432,7 @@ transaction**. It is one `client.$transaction(async (tx) => …)` per Aventara t
431
432
  - the **root client is unreachable** from inside the callback: the executor core is handed closes over
432
433
  the transaction client and nothing else;
433
434
  - the value the callback threw is rethrown **by identity**. The adapter adds no `try`, no `catch` and
434
- no wrapper, because that is what the seam requires and wrapping would turn every plan failure into
435
+ no wrapper, because that is what core's transaction interface requires and wrapping would turn every plan failure into
435
436
  an integrity failure.
436
437
 
437
438
  A first-style variant inside a plan **reuses the plan's transaction** rather than opening a nested
@@ -477,10 +478,10 @@ discovery.referentialActions;
477
478
  // {
478
479
  // Shop: { owningEdges: [] },
479
480
  // Item: { owningEdges: [{ field: "shop", target: "Shop",
480
- // cardinality: "one",
481
+ // cardinality: "one", references: ["shopId"],
481
482
  // onDelete: "cascade", onUpdate: "cascade" }] },
482
483
  // Clerk: { owningEdges: [{ field: "shop", target: "Shop",
483
- // cardinality: "one",
484
+ // cardinality: "one", references: ["shopId"],
484
485
  // onDelete: "set-null", onUpdate: "cascade" }] },
485
486
  // }
486
487
  ```
@@ -492,14 +493,28 @@ To answer *"what does deleting a `Shop` do?"*, scan every Resource's `owningEdge
492
493
  (set-null). Both ends of that sentence are Resource names and nothing in the shape tells them apart, so
493
494
  the inverted reading produces no error at all, only a plan that reasons backwards.
494
495
 
496
+ `references` are the edge's `@relation(fields: […])` — the scalars that hold the reference. A record
497
+ that writes a relation and one of its reference fields both (`shopId` and `shop: { $connect: … }`,
498
+ required relation or optional) is refused before the database is reached: `A2004` with a `V1012` issue
499
+ at the relation, message `Set "shopId" or "shop", not both.` They are also what a required relation may
500
+ be written through instead of the relation itself.
501
+
502
+ Prisma takes one record's relations either all as relation writes or all through their reference
503
+ fields — its checked and unchecked inputs — so every artifact's model states `referenceWrites: "uniform"`,
504
+ and every Contract compiled from it carries the same member. A record that writes `shopId` beside
505
+ another relation's write (`supplier: { $connect: … }`) is refused before the database is reached:
506
+ `A2004` with a `V1012` issue at the relation write. A reference field beside a back-reference (a
507
+ relation that holds no reference on that side, such as a shop's `items`) mixes nothing and is
508
+ accepted.
509
+
495
510
  Only **owning** edges appear: PSL forbids the non-owning side of a relation from declaring an action,
496
511
  so a back-reference's answer is its partner's and carries no entry of its own. Both actions are
497
512
  *resolved* — an absent PSL argument is not a third state but Prisma's effective default (`set-null`
498
- where every foreign-key scalar is optional, `restrict` otherwise, `cascade` on update), measured on both
499
- pilot engines. An action spelling the reader cannot name stops the generate step rather than reaching
513
+ where every foreign-key scalar is optional, `restrict` otherwise, `cascade` on update), on both
514
+ PostgreSQL and SQLite. An action spelling the reader cannot name stops the generate step rather than reaching
500
515
  the artifact.
501
516
 
502
- Neither the model nor the compiled Contracts carry a referential action — the actions are beside the
517
+ Neither the model nor the compiled Contracts carry a referential action or a reference field — the edges are beside the
503
518
  model and outside `modelHash`, so they move no Contract value and no hash. `createPrismaAdapter` states
504
519
  them to core through the adapter's optional `referentialActions` member, translated one to one into core's
505
520
  five words (mirroring Prisma's and SQL's actions; direction unchanged):
@@ -508,7 +523,7 @@ five words (mirroring Prisma's and SQL's actions; direction unchanged):
508
523
  |---|---|---|
509
524
  | `cascade` | `cascade` | deletes them, and the closure recurses into them |
510
525
  | `restrict` | `restrict` | refuses the delete while they reference it |
511
- | `no-action` | `noAction` | refuses the delete while they reference it — measured identical to `restrict` on both engines, because `NoAction` is not `DEFERRABLE` on either |
526
+ | `no-action` | `noAction` | refuses the delete while they reference it — identical to `restrict` on both engines, because `NoAction` is not `DEFERRABLE` on either |
512
527
  | `set-null` | `setNull` | keeps them, the reference set to null |
513
528
  | `set-default` | `setDefault` | keeps them, the reference set to its column default |
514
529
 
@@ -522,8 +537,8 @@ what the schema says rather than collapsing it into what core currently reads.
522
537
  once, at framework construction, and the transaction planner refuses a step that depends on rows an
523
538
  earlier step's cascade removed — its own Resource, or one its arguments reach through a relation — with
524
539
  `A2004`/`V1019` *"Transaction step depends on a row removed by an earlier step's cascade"*, before any
525
- provider statement is issued. It is measured on both pilot engines against `Metric.author`
526
- (`onDelete: Cascade`), the pilot schemas' one cascade edge: deleting an `Author` and then reading its
540
+ provider statement is issued. For example, with `Metric.author`
541
+ (`onDelete: Cascade`) as the one cascade edge: deleting an `Author` and then reading its
527
542
  `Metric` rows is refused; deleting an `Author` and then reading `Post` (`Restrict`), or a `Category` and
528
543
  then `Post` (`SetNull`), is not. The refusal is per Resource — a plan cannot know which rows a delete
529
544
  will match — and plan-scoped: a single delete that cascades runs.
@@ -544,7 +559,7 @@ Prisma's typed delegate surface.
544
559
 
545
560
  ## Also worth knowing
546
561
 
547
- - **`$count` on a paginated relation reports the pre-pagination count.** Measured on both engines: a
562
+ - **`$count` on a paginated relation reports the pre-pagination count.** On both engines, a
548
563
  relation's `_count` is unaffected by `take`/`skip`/`cursor` applied to the same relation in the same
549
564
  query. A filter on the relation *does* affect it.
550
565
  - **`limit`/`offset`/`cursor` are the public names.** Prisma's `take`/`skip` never surface.
@@ -555,7 +570,7 @@ Prisma's typed delegate surface.
555
570
  `find.first`, `update.first` and `delete.first` answer `A1001 NO_MATCH` with `null` data. The
556
571
  distinction is deliberate and is core's, not the adapter's.
557
572
 
558
- ## Not covered by the pilot
573
+ ## Not covered
559
574
 
560
575
  Stated so nothing here reads as a claim it is not:
561
576
 
@@ -1,4 +1,4 @@
1
- import type { PrismaAdapterModel } from "./discovery-artifact.interface.js";
1
+ import { type PrismaAdapterModel } from "./discovery-artifact.interface.js";
2
2
  /** Every artifact failure is this class, and every one of them says how to fix it. */
3
3
  export declare class PrismaDiscoveryArtifactError extends Error {
4
4
  /**
@@ -21,10 +21,5 @@ export type PrismaDiscoveryArtifactLoad = {
21
21
  * identity and into every generated client.
22
22
  */
23
23
  export declare function computeModelHash(model: PrismaAdapterModel): Promise<string>;
24
- /**
25
- * Validates an artifact and hands back its model **unchanged**.
26
- *
27
- * Three checks, in the order that makes each one's failure legible: shape, then
28
- * the `prismaVersion` major fence, then `modelHash`.
29
- */
24
+ /** Validates an artifact and hands back its model **unchanged**. */
30
25
  export declare function loadDiscoveryArtifact(load: PrismaDiscoveryArtifactLoad): Promise<PrismaAdapterModel>;
@@ -1,4 +1,5 @@
1
- import { canonicalizeJson } from "@aventara/core";
1
+ import { canonicalizeJson } from "@aventara/core/internal";
2
+ import { DISCOVERY_ARTIFACT_FORMAT, } from "./discovery-artifact.interface.js";
2
3
  const HASH_PREFIX = "sha256:";
3
4
  const REGENERATION_INSTRUCTION = "Regenerate the discovery artifact — `npm run aventara:prepare` in a project " +
4
5
  "set up by `aventara`, or the adapter's generate step " +
@@ -32,6 +33,9 @@ function assertArtifactShape(artifact) {
32
33
  if (!isRecord(artifact)) {
33
34
  throw new PrismaDiscoveryArtifactError("The discovery artifact is not a JSON object.");
34
35
  }
36
+ if (artifact.regenerate_with_aventara_prepare !== DISCOVERY_ARTIFACT_FORMAT) {
37
+ throw new PrismaDiscoveryArtifactError("The discovery artifact was written by another version of @aventara/prisma7-adapter.");
38
+ }
35
39
  for (const key of ["prismaVersion", "provider", "modelHash"]) {
36
40
  if (typeof artifact[key] !== "string") {
37
41
  throw new PrismaDiscoveryArtifactError(`The discovery artifact's \`${key}\` is not a string.`);
@@ -59,6 +63,9 @@ function assertArtifactShape(artifact) {
59
63
  artifact.model.transactions !== "interactive") {
60
64
  throw new PrismaDiscoveryArtifactError("The discovery artifact's `model.transactions` is not a transaction capability.");
61
65
  }
66
+ if (artifact.model.referenceWrites !== "uniform") {
67
+ throw new PrismaDiscoveryArtifactError("The discovery artifact's `model.referenceWrites` is not `uniform`, which every Prisma model is.");
68
+ }
62
69
  }
63
70
  export async function loadDiscoveryArtifact(load) {
64
71
  const { artifact, installedPrismaVersion } = load;
@@ -1,14 +1,4 @@
1
- /**
2
- * Nothing here is imported from `@prisma/client`: these are this adapter's own
3
- * facts over public generated types, which is what keeps discovery a pure function
4
- * of plain data and unit-testable without a client.
5
- *
6
- * Read `types.reader.ts`'s header for the two rules that give these facts their
7
- * meaning: only the 🟢 files are opened, and token-shaped facts come from the
8
- * **written** type arguments rather than from the instantiated reference.
9
- */
10
1
  export type PrismaCapabilitySurface = {
11
- /** The EXISTENCE GATE: an entity is discoverable iff it appears here. */
12
2
  readonly resources: Readonly<Record<string, PrismaResourceSurface>>;
13
3
  /** `enums.ts`'s `as const` objects, in property order. */
14
4
  readonly enums: Readonly<Record<string, readonly string[]>>;
@@ -19,7 +9,6 @@ export type PrismaResourceSurface = {
19
9
  readonly relationFields: Readonly<Record<string, PrismaRelationSurface>>;
20
10
  /** `AtLeast<…, K>`'s key union — cross-check only; order unused. */
21
11
  readonly uniqueKeys: readonly string[];
22
- /** `<M>Delegate`'s own methods → `ResourceOperations`. */
23
12
  readonly operations: readonly string[];
24
13
  /** e.g. `updateMany` → `["data", "where", "limit"]`. */
25
14
  readonly manyWriteArguments: Readonly<Record<string, readonly string[]>>;
@@ -29,24 +18,12 @@ export type PrismaScalarSurface = {
29
18
  readonly token: string;
30
19
  /** `<Kind>Filter` keys, in Prisma's spelling — NAMES ONLY. */
31
20
  readonly filters: readonly string[];
32
- /**
33
- * The operand SHAPE per filter key, as written in the generated type — because two
34
- * engines can carry the same key with different operands, and a name set cannot
35
- * express that: `path` is `string[]` on PostgreSQL and `string` on SQLite, and
36
- * `in`/`notIn` admit `List<Kind>FieldRefInput` on PostgreSQL only.
37
- */
38
21
  readonly filterOperands: Readonly<Record<string, string>>;
39
22
  /**
40
23
  * `<M>UncheckedCreateInput`'s entry for this field, read as the keys of the
41
24
  * `…Input` it references: `[]` for a plain value, and **`null` where the client
42
25
  * exposes no entry at all**, which is the only evidence that a column is not
43
26
  * creatable.
44
- *
45
- * The UNCHECKED input is the source because it is what the delegate accepts:
46
- * `create`'s argument is `XOR<<M>CreateInput, <M>UncheckedCreateInput>`, and the
47
- * checked half omits every foreign-key scalar (the relation owns it there). Read
48
- * from the checked half, `Post.authorId` and `Post.categoryId` come back absent
49
- * and Aventara would refuse writes Prisma accepts.
50
27
  */
51
28
  readonly createOperations: readonly string[] | null;
52
29
  /**
@@ -77,11 +54,6 @@ export type PrismaScalarSurface = {
77
54
  * `<Kind>FieldUpdateOperationsInput`, or of `<M>Update<field>Input` for a scalar
78
55
  * list (`set`, `push`), or `[]` for a plain value (`Json`), or `null` where the
79
56
  * client exposes no entry.
80
- *
81
- * Measured at 7.10.0 (2026-10-02): reading `<M>UpdateInput` instead under-reports
82
- * six of the twenty-five scalar columns per engine — `Author.mentorId`,
83
- * `Metric.authorId`, `Post.authorId`, `Post.categoryId`, `Category.id` and
84
- * `Post.id` — every one of which `<M>UncheckedUpdateInput` admits.
85
57
  */
86
58
  readonly updateOperations: readonly string[] | null;
87
59
  readonly orderable: boolean;
@@ -89,19 +61,11 @@ export type PrismaScalarSurface = {
89
61
  readonly nullsOrderable: boolean;
90
62
  };
91
63
  export type PrismaRelationSurface = {
92
- /** Read off `$<Target>Payload`'s own `name` literal, never off the alias's spelling. */
93
64
  readonly target: string;
94
65
  readonly many: boolean;
95
66
  readonly nullable: boolean;
96
67
  /** `ListRelationFilter` vs `[Nullable]ScalarRelationFilter`. */
97
68
  readonly filters: readonly string[];
98
- /**
99
- * `<T>CreateNested{One,Many}Without<M>Input` keys — the **create** stage's own
100
- * nested-write input, read off `<M>CreateInput`'s entry for this edge.
101
- *
102
- * Reusing one list for both stages advertises five directives per to-many edge,
103
- * and two per to-one edge, that Prisma has no key for at the create stage.
104
- */
105
69
  readonly createMutations: readonly string[];
106
70
  /**
107
71
  * Whether `<M>CreateInput` — the CHECKED create input, the only one that carries
@@ -111,20 +75,15 @@ export type PrismaRelationSurface = {
111
75
  * entry for the edge at all.
112
76
  */
113
77
  readonly createRequired: boolean;
114
- /** `<T>Update*Without<M>NestedInput` keys — the **update** stage's own input. */
115
78
  readonly updateMutations: readonly string[];
116
79
  /**
117
- * `<M>$<edge>Args`'s own member names, as written. The edge's whole argument
118
- * surface, because four separate capabilities are read off it — pagination,
119
- * ordering, nested filtering of the returned edge, and projection — and a
120
- * residual cannot answer the other three.
80
+ * Whether the update stage's `deleteMany` takes a `<T>ScalarWhereInput` — a filter of
81
+ * the target's scalar fields only, which `updateMany`'s `where` shares. Read off the
82
+ * nested input's own member type; `false` where the input has no `deleteMany` (a
83
+ * to-one edge, whose `update`/`delete` take a full `<T>WhereInput`).
121
84
  */
85
+ readonly scalarNestedWriteFilter: boolean;
122
86
  readonly edgeArguments: readonly string[];
123
- /**
124
- * {@link edgeArguments} minus what projection and predicate already own — a
125
- * derived projection kept because it is the one a to-one/to-many split reads at
126
- * a glance: a to-one edge leaves nothing.
127
- */
128
87
  readonly pagination: readonly string[];
129
88
  /** The edge is admitted by `<M>OrderByWithRelationInput`. */
130
89
  readonly orderable: boolean;
@@ -133,22 +92,6 @@ export type PrismaRelationSurface = {
133
92
  /**
134
93
  * Whether **every** `<M>Select{Create,Update}ManyAndReturn` the client declares
135
94
  * lists this edge.
136
- *
137
- * # Presence, read. Not foreign-key ownership, computed
138
- *
139
- * The *explanation* for which edges survive into a bulk-returning write's
140
- * projection is foreign-key ownership rather than cardinality — a to-one that does
141
- * not hold its own FK is absent, which is what the `User`/`Profile` fixture exists
142
- * to prove. That explanation is **not** the mechanism. The mechanism is this
143
- * member: the edge is in the type, or it is not.
144
- *
145
- * **Every**, not *any*: one {@link ProjectionScope} covers both bulk-returning
146
- * variants, so a scope the adapter advertises must hold in both. At 7.10.0 the two
147
- * types agree member-for-member on both pilot engines, so the conjunction is
148
- * currently indistinguishable from either half — `types.reader.spec.ts` asserts
149
- * that agreement, so the day it stops holding is the day a reader reports it
150
- * instead of silently picking a side. **Zero declared types answer `false`**,
151
- * never vacuously `true`: a client with no `*AndReturn` type admits nothing there.
152
95
  */
153
96
  readonly bulkSelect: boolean;
154
97
  /** The {@link bulkSelect} question asked of `<M>Include{Create,Update}ManyAndReturn`. */
@@ -164,8 +107,8 @@ export type PrismaRelationSurface = {
164
107
  */
165
108
  readonly bulkReducers: boolean;
166
109
  /**
167
- * Whether `<M>CreateManyInput` — the data argument of the bulk create — exposes an
168
- * entry for this edge.
110
+ * Whether `<M>CreateManyInput` — the data argument of the bulk create — exposes
111
+ * an entry for this edge.
169
112
  */
170
113
  readonly bulkCreatable: boolean;
171
114
  /**