@aventara/prisma7-adapter 0.1.0-pilot.1 → 0.1.0-pilot.3

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 (79) hide show
  1. package/README.md +33 -20
  2. package/dist/discovery/artifact.loader.d.ts +6 -2
  3. package/dist/discovery/artifact.loader.js +12 -57
  4. package/dist/discovery/capability-surface.interface.d.ts +29 -40
  5. package/dist/discovery/capability-surface.interface.js +0 -12
  6. package/dist/discovery/capability.deriver.d.ts +10 -40
  7. package/dist/discovery/capability.deriver.js +0 -179
  8. package/dist/discovery/client-shape.verifier.d.ts +13 -0
  9. package/dist/discovery/client-shape.verifier.js +93 -0
  10. package/dist/discovery/discovery-artifact.interface.d.ts +55 -30
  11. package/dist/discovery/enum.mapper.d.ts +6 -14
  12. package/dist/discovery/enum.mapper.js +1 -2
  13. package/dist/discovery/field.mapper.d.ts +49 -63
  14. package/dist/discovery/field.mapper.js +0 -161
  15. package/dist/discovery/identifier.mapper.d.ts +38 -77
  16. package/dist/discovery/identifier.mapper.js +0 -64
  17. package/dist/discovery/model.discoverer.d.ts +18 -35
  18. package/dist/discovery/model.discoverer.js +66 -141
  19. package/dist/discovery/operations.deriver.d.ts +10 -38
  20. package/dist/discovery/operations.deriver.js +0 -40
  21. package/dist/discovery/scalar.mapper.d.ts +12 -35
  22. package/dist/discovery/scalar.mapper.js +0 -8
  23. package/dist/discovery/schema-facts.interface.d.ts +26 -25
  24. package/dist/discovery/schema-facts.interface.js +0 -10
  25. package/dist/discovery/schema.reader.d.ts +53 -64
  26. package/dist/discovery/schema.reader.js +0 -174
  27. package/dist/discovery/types.reader.d.ts +0 -4
  28. package/dist/discovery/types.reader.js +2 -177
  29. package/dist/discovery/vocabulary.dictionary.d.ts +36 -75
  30. package/dist/discovery/vocabulary.dictionary.js +2 -152
  31. package/dist/errors/error.normalizer.d.ts +85 -135
  32. package/dist/errors/error.normalizer.js +1 -70
  33. package/dist/errors/prisma-error.reader.d.ts +53 -68
  34. package/dist/errors/prisma-error.reader.js +0 -91
  35. package/dist/execution/first-variant.emulator.d.ts +11 -22
  36. package/dist/execution/first-variant.emulator.js +0 -142
  37. package/dist/execution/operation.executor.d.ts +12 -19
  38. package/dist/execution/operation.executor.js +0 -92
  39. package/dist/execution/result.shaper.d.ts +22 -31
  40. package/dist/execution/result.shaper.js +0 -24
  41. package/dist/execution/transaction.executor.d.ts +36 -73
  42. package/dist/generate/artifact.emitter.d.ts +27 -27
  43. package/dist/generate/artifact.emitter.js +30 -13
  44. package/dist/generate/declared-support.reader.d.ts +11 -11
  45. package/dist/generate/declared-support.reader.js +0 -12
  46. package/dist/generate/discovery.bin.js +0 -10
  47. package/dist/generate/discovery.cli.js +0 -55
  48. package/dist/generate/existence.gate.d.ts +26 -32
  49. package/dist/generate/existence.gate.js +0 -6
  50. package/dist/generate/index.d.ts +10 -12
  51. package/dist/generate/index.js +0 -42
  52. package/dist/generate/node-version.guard.js +0 -12
  53. package/dist/generate/prisma-cli.resolver.d.ts +9 -11
  54. package/dist/generate/prisma-cli.resolver.js +0 -13
  55. package/dist/generate/prisma.generate.d.ts +49 -65
  56. package/dist/generate/prisma.generate.js +13 -123
  57. package/dist/generate/reader-crosscheck.d.ts +36 -60
  58. package/dist/generate/reader-crosscheck.js +5 -41
  59. package/dist/prisma-client.interface.d.ts +14 -19
  60. package/dist/prisma-client.interface.js +0 -6
  61. package/dist/prisma.adapter.d.ts +9 -17
  62. package/dist/prisma.adapter.js +11 -53
  63. package/dist/provider/provider-profile.d.ts +6 -23
  64. package/dist/provider/provider-profile.js +0 -64
  65. package/dist/translation/identifier.addressing.d.ts +15 -22
  66. package/dist/translation/identifier.addressing.js +0 -10
  67. package/dist/translation/mutation.translator.d.ts +19 -43
  68. package/dist/translation/mutation.translator.js +1 -209
  69. package/dist/translation/operation.translator.d.ts +36 -63
  70. package/dist/translation/operation.translator.js +1 -51
  71. package/dist/translation/order-pagination.translator.d.ts +24 -31
  72. package/dist/translation/order-pagination.translator.js +0 -43
  73. package/dist/translation/projection.translator.d.ts +25 -58
  74. package/dist/translation/projection.translator.js +0 -27
  75. package/dist/translation/scalar.conversion.d.ts +15 -26
  76. package/dist/translation/scalar.conversion.js +1 -129
  77. package/dist/translation/where.translator.d.ts +17 -28
  78. package/dist/translation/where.translator.js +0 -83
  79. package/package.json +4 -3
@@ -1,17 +1,3 @@
1
- /**
2
- * T-3.1's table, row for row, so a reader can audit one against the other.
3
- *
4
- * **`format?` diverges from T-3.1's table deliberately, and the plan row is the
5
- * one that is wrong.** T-3.1 says `format?` is `CFG` — "PSL's `@db.*` is a hint
6
- * only … policy, not discovery". Spec §6.1 says the opposite in as many words:
7
- * UUID "is a string scalar plus passive format metadata **when the adapter can
8
- * prove the format**", §18.3's discovery mapping reads "scalar type → stable
9
- * framework scalar + optional format", C-809 restates both, T-5 restates them
10
- * again, and `AdapterScalarField.format?` exists **[CORE]** — a member with no
11
- * producer if discovery never emits one. So the source is `PSL` **and** `CFG`:
12
- * discovery emits what the provider's own column type proves, configuration owns
13
- * everything else. `scalar.mapper.ts`'s header carries the full argument.
14
- */
15
1
  export const VOCABULARY = [
16
2
  {
17
3
  fact: 'kind: "scalar" / "relation"',
@@ -61,7 +47,7 @@ export const VOCABULARY = [
61
47
  {
62
48
  fact: "enum values",
63
49
  sources: ["PSL"],
64
- rule: "Declaration order is semantic (§19.2) and is never sorted; cross-checked against `enums.ts`.",
50
+ rule: "Declaration order is semantic and is never sorted; cross-checked against `enums.ts`.",
65
51
  },
66
52
  {
67
53
  fact: "field & operation capabilities",
@@ -76,7 +62,7 @@ export const VOCABULARY = [
76
62
  {
77
63
  fact: "transactions",
78
64
  sources: ["DRIVER"],
79
- rule: "F-801 — the configured driver adapter, not the provider name.",
65
+ rule: "The configured driver adapter, not the provider name.",
80
66
  },
81
67
  {
82
68
  fact: "COMPUTED_* / VIRTUAL",
@@ -84,15 +70,6 @@ export const VOCABULARY = [
84
70
  rule: "Outside `AdapterLifecycleTrait` by construction [CORE]; the adapter cannot emit them.",
85
71
  },
86
72
  ];
87
- /**
88
- * Total and 1:1 over `BUILT_IN_SCALARS` **[CORE]**. There is no default arm: a
89
- * Prisma type name absent from this table is a diagnostic, not a guess (C-809).
90
- *
91
- * **No row carries a `format`**, and that is the point: a format is a property of
92
- * a *column*, not of a Prisma *type name*. `String` alone proves nothing, so the
93
- * one thing that can prove something — the native column type — is a separate
94
- * lookup (`PRISMA_NATIVE_FORMATS`).
95
- */
96
73
  export const PRISMA_SCALAR_TABLE = {
97
74
  String: { scalar: "string" },
98
75
  Boolean: { scalar: "boolean" },
@@ -104,48 +81,9 @@ export const PRISMA_SCALAR_TABLE = {
104
81
  Bytes: { scalar: "bytes" },
105
82
  Json: { scalar: "json" },
106
83
  };
107
- /**
108
- * The only `@db.*` spellings that **prove** a format, matched by exact written
109
- * spelling and never parsed.
110
- *
111
- * The bar for a row here is not "the name suggests a format". It is: **the column
112
- * type itself rejects the values `format` would later reject.** `format` is
113
- * enforced, not decorative — `normalizeScalarFieldValue` turns `format: "uuid"`
114
- * into a `V1003` rejection of request input **[CORE,
115
- * `runtime/scalar-runtime.ts`]** — so a row that is merely probable would have
116
- * Aventara refuse writes the database accepts.
117
- *
118
- * `@db.Uuid` clears that bar: PostgreSQL's `uuid` column rejects
119
- * `'not-a-uuid'` (`ERROR: invalid input syntax for type uuid`) where a `text`
120
- * column takes it **[RUN8 — 2026-09-28, `aventara-postgres`, postgres:18]**.
121
- *
122
- * `@default(uuid())` does **not** clear it, which is where T-5's wording is
123
- * narrowed: on a `String` column with no `@db.Uuid` the column is `text`, only
124
- * *generated* values are UUIDs, and a client may legally supply any string. That
125
- * intent belongs in `FrameworkScalarFieldConfig.format` **[CORE]**, which the
126
- * Contract compiler already applies over the adapter's value.
127
- *
128
- * `@db.Decimal(10, 2)` and `@db.VarChar(255)` are absent because they prove no
129
- * *format*; they carry precision and width, which Aventara has no word for.
130
- */
131
84
  export const PRISMA_NATIVE_FORMATS = {
132
85
  "@db.Uuid": "uuid",
133
86
  };
134
- /**
135
- * T-3.1's `@default` table.
136
- *
137
- * **`traits` is the whole trait set, where T-3.1's table lists only the trait the
138
- * *argument* adds.** The two are consistent, not contradictory: `DEFAULTED` comes
139
- * from `@default`'s **presence** (C-808: "`@default` → `DEFAULTED` and, when
140
- * generator-owned, `GENERATED_ON_CREATE`"), and C-811's acceptance criteria spell
141
- * the full sets out — `@default(autoincrement())` → `["DEFAULTED",
142
- * "GENERATED_ON_CREATE"]`, `@default("x")` → `["DEFAULTED"]`. This table states
143
- * the answer rather than half of it, so no consumer has to remember to add the
144
- * other half.
145
- *
146
- * The lookup that walks this table lives with the lifecycle projection (Task 4),
147
- * where it can be driven red; this pass owns the data only.
148
- */
149
87
  export const PRISMA_DEFAULT_TABLE = [
150
88
  {
151
89
  match: { literal: "uuid()" },
@@ -193,51 +131,12 @@ export const PRISMA_DEFAULT_TABLE = [
193
131
  evidence: "RUN8",
194
132
  },
195
133
  {
196
- // Mongo; unreachable for the SQL providers Phase 8 supports.
197
134
  match: { pattern: /^auto\(\)$/ },
198
135
  traits: ["DEFAULTED", "GENERATED_ON_CREATE"],
199
136
  evidence: "UNVERIFIED",
200
137
  },
201
138
  ];
202
- /**
203
- * Any other text — `"BRONZE"`, `"x"`, `sequence()`, `nanoid()`, a spelling added
204
- * after 7.10.0. The value is still `DEFAULTED`, because `@default` is present;
205
- * the *unknown* is what fails the generate step, so an unrecognised generator
206
- * function is a build stop rather than a column Aventara quietly thinks the
207
- * client must supply.
208
- */
209
139
  export const PRISMA_UNMATCHED_DEFAULT_TRAITS = ["DEFAULTED"];
210
- /* ------------------------------------------------------------------ *
211
- * Prisma → Aventara renames (invariant 2) *
212
- * ------------------------------------------------------------------ */
213
- /**
214
- * The dictionary's other half: the point at which Prisma's spelling stops and
215
- * Aventara's begins.
216
- *
217
- * **One untyped `PRISMA_RENAMES` became six typed tables (2026-10-02, T-6).** The
218
- * single `Record<string, string>` could not make a mistake a compile error: a
219
- * rename to a word core does not have, or a Prisma key nobody decided about,
220
- * both typechecked. Each table below is keyed on the Prisma spelling and valued in
221
- * the ONE Aventara vocabulary that spelling can land in, so `"arrayContains"`
222
- * misspelt fails to compile. The table had no consumer, so nothing was broken by
223
- * splitting it.
224
- *
225
- * `null` is a value, not an omission: it means **recognised, and Aventara has no
226
- * word for it** — the nested bulk writes, `distinct`, Prisma's `set` assignment.
227
- * A key absent from a table is a different thing entirely, and the deriver that
228
- * reads these refuses rather than dropping it silently, because a silently dropped
229
- * capability key is how C-855's failure mode begins.
230
- *
231
- * C-837's relation *translator* table — which Prisma argument each directive emits
232
- * — is still Task 9's. This is the vocabulary half only.
233
- */
234
- /**
235
- * `<Kind>Filter` / `<Kind>NullableFilter` / `Json[Nullable]Filter` keys.
236
- *
237
- * Total and 1:1 over `ScalarFilterOperator` **[CORE]** at 7.10.0, measured over
238
- * both generated clients: 23 Prisma keys, 23 Aventara operators, no survivor on
239
- * either side.
240
- */
241
140
  export const PRISMA_FILTER_OPERATORS = {
242
141
  equals: "equals",
243
142
  not: "not",
@@ -263,15 +162,6 @@ export const PRISMA_FILTER_OPERATORS = {
263
162
  array_starts_with: "arrayStartsWith",
264
163
  array_ends_with: "arrayEndsWith",
265
164
  };
266
- /**
267
- * `[Nullable]ScalarRelationFilter` and `ListRelationFilter` keys.
268
- *
269
- * `is` and `isNot` both answer `match`: §18.5 has one to-one matching capability
270
- * and no `is`/`isNot` wrapper, so the two Prisma keys collapse into it. `nested`
271
- * and `null` are **not** keys of any filter input — they are read off the edge's
272
- * own `where` argument and off the edge's nullability — so they are absent here by
273
- * construction rather than by omission.
274
- */
275
165
  export const PRISMA_RELATION_FILTERS = {
276
166
  is: "match",
277
167
  isNot: "match",
@@ -279,15 +169,6 @@ export const PRISMA_RELATION_FILTERS = {
279
169
  every: "every",
280
170
  none: "none",
281
171
  };
282
- /**
283
- * `<Kind>FieldUpdateOperationsInput` and `<M><Stage><field>Input` keys.
284
- *
285
- * `set` is `null` because plain assignment is not a directive in Aventara: the
286
- * value is written directly, and `ScalarMutationDirective` has no `$set`
287
- * **[CORE]**. `unset` is carried although neither Phase 8 engine emits it — it is
288
- * Mongo's, and a key the dictionary recognises and never advertises is better than
289
- * one that makes the deriver throw the day a third provider is added.
290
- */
291
172
  export const PRISMA_SCALAR_DIRECTIVES = {
292
173
  set: null,
293
174
  increment: "$increment",
@@ -297,14 +178,6 @@ export const PRISMA_SCALAR_DIRECTIVES = {
297
178
  push: "$push",
298
179
  unset: "$unset",
299
180
  };
300
- /**
301
- * `<M>Update<edge>Without<M>NestedInput` / `<M>Create<edge>…NestedInput` keys.
302
- *
303
- * `createMany`, `updateMany` and `deleteMany` are `null`: they are nested BULK
304
- * writes, and `RelationMutationDirective` models per-row intent only **[CORE]**.
305
- * Aventara expresses the same effect by addressing the target Resource, so
306
- * advertising them would promise an argument no directive could carry.
307
- */
308
181
  export const PRISMA_RELATION_DIRECTIVES = {
309
182
  create: "$create",
310
183
  connect: "$connect",
@@ -318,15 +191,6 @@ export const PRISMA_RELATION_DIRECTIVES = {
318
191
  updateMany: null,
319
192
  deleteMany: null,
320
193
  };
321
- /**
322
- * `<M>$<edge>Args` keys, valued in `PaginationCapability`.
323
- *
324
- * `null` means the argument is read for something else, or for nothing:
325
- * `orderBy` answers `order`, `where` answers the `nested` filter capability,
326
- * `select`/`omit`/`include` answer projection, and `distinct` is a Prisma concept
327
- * Aventara has no word for. The table is total over the keys both clients write,
328
- * so a new edge argument at 7.11 is an unrecognised key rather than a silent drop.
329
- */
330
194
  export const PRISMA_EDGE_ARGUMENTS = {
331
195
  take: "limit",
332
196
  skip: "offset",
@@ -338,7 +202,6 @@ export const PRISMA_EDGE_ARGUMENTS = {
338
202
  include: null,
339
203
  where: null,
340
204
  };
341
- /** `_count` on `<M>Include` / `<M>CountOutputType`. */
342
205
  export const PRISMA_REDUCERS = {
343
206
  _count: "$count",
344
207
  };
@@ -346,19 +209,6 @@ const BOTH_ENGINES = [
346
209
  "@prisma/adapter-better-sqlite3",
347
210
  "@prisma/adapter-pg",
348
211
  ];
349
- /**
350
- * Task 1's observations, inlined with the engines they were made on
351
- * **[RUN8, `src/environment.spec.ts`]**. Identical through both driver adapters,
352
- * so the representation is not driver-dependent *between these two engines* —
353
- * which is a narrower claim than "not driver-dependent", and the narrower one is
354
- * what was measured.
355
- *
356
- * A row is `RUN8` only where a read-back value was actually asserted. `String`,
357
- * `Boolean`, `Int` and `Float` were written into a row and never read back under
358
- * assertion, so they stay `[UNVERIFIED]` (§12): a plausible value shape is not an
359
- * observed one, and this table exists precisely so that nobody has to guess which
360
- * is which.
361
- */
362
212
  export const OBSERVED_VALUE_SHAPES = [
363
213
  {
364
214
  prismaType: "Decimal",
@@ -1,25 +1,19 @@
1
1
  import { FrameworkError, type OperationErrorCode, type OperationIdentity } from "@aventara/core";
2
2
  import { type PrismaConstraintSubject } from "./prisma-error.reader.js";
3
3
  /**
4
- * T-10 — error normalization: one closed table from `@prisma/client` 7.x
5
- * P-codes to Aventara outcomes, and nothing else.
6
- *
7
- * ## Version bound (C-846)
8
- *
9
4
  * The table's keys are P-codes, which are **7.x's** error surface. Prisma 8
10
- * replaced them with dotted `NAMESPACE.SUBCODE` codes, so a Prisma 8
11
- * adapter re-maps this module rather than patching it.
12
- * `error.normalizer.spec.ts` reads `@prisma/client`'s own `package.json`
13
- * and fails if the installed major is not `7`, so the bound is enforced
14
- * rather than described.
5
+ * replaced them with dotted `NAMESPACE.SUBCODE` codes, so a Prisma 8 adapter
6
+ * re-maps this module rather than patching it. `error.normalizer.spec.ts` reads
7
+ * `@prisma/client`'s own `package.json` and fails if the installed major is not
8
+ * `7`, so the bound is enforced rather than described.
15
9
  *
16
10
  * ## Every code in the table was induced, not inherited
17
11
  *
18
- * Nothing here rests on documentation. Each rule was produced by throwing a
19
- * real statement at a real engine on this repository's own schema, on
20
- * `@prisma/adapter-better-sqlite3` and `@prisma/adapter-pg`, both 7.10.0.
21
- * The measured payloads, inlined because the source measurement lives
22
- * outside the repository:
12
+ * Nothing here rests on documentation. Each rule was produced by throwing a real
13
+ * statement at a real engine on this repository's own schema, on
14
+ * `@prisma/adapter-better-sqlite3` and `@prisma/adapter-pg`, both 7.10.0. The
15
+ * measured payloads, inlined because the source measurement lives outside the
16
+ * repository:
23
17
  *
24
18
  * ```text
25
19
  * P2002 author.create duplicate @unique email
@@ -58,6 +52,11 @@ import { type PrismaConstraintSubject } from "./prisma-error.reader.js";
58
52
  * operation: "a nested connect",
59
53
  * neededFor: "inline the relation on 'Post' record(s)" }
60
54
  *
55
+ * P2014 author.update with posts.set [] (or posts.disconnect) while a post
56
+ * still requires that author
57
+ * both meta = { modelName: "Author", relation: "AuthorToPost",
58
+ * modelA: "Post", modelB: "Author" }
59
+ *
61
60
  * P2018 author.update with posts.connect to a missing post
62
61
  * sqlite meta = { modelName: "Author", expectedRows: 1,
63
62
  * relation: "AuthorToPost", relationType: "one-to-many" }
@@ -71,83 +70,62 @@ import { type PrismaConstraintSubject } from "./prisma-error.reader.js";
71
70
  *
72
71
  * ## Two inherited claims that the measurement refutes
73
72
  *
74
- * 1. **`meta.errorIdentifier` does not exist.** D-811 and the error-code map
75
- * both state that P2025's cases are told apart by
76
- * `meta.errorIdentifier` (`MISSING_RECORD` / `MISSING_RELATED_RECORD` /
77
- * `INCOMPLETE_CONNECT_INPUT`), and both marked it unverified at the meta
78
- * level. It is **absent from every payload above**; the identifiers are
79
- * internal rule names, not a `meta` key. What 7.10.0 actually emits is
80
- * two different *key sets*, and the discriminant is the presence of
81
- * `meta.relation` (with `meta.model`): present → the failure is about a
82
- * relation reference; absent → the record the call selected was not
83
- * there. That is structural, which is what ADR 0006 requires — the
84
- * alternative, `meta.operation`, is English prose.
85
- * 2. **`meta.target` is never emitted.** RC-9's open question is answered:
86
- * the v7 docs are stale (see `PrismaErrorFacts.legacyTarget`).
87
- *
88
- * Also unobserved: the `{}` meta that `expectNonEmpty` was expected to
89
- * carry. `findUniqueOrThrow` and `findFirstOrThrow` both report
90
- * `{ modelName, operation: "a query" }`, so no empty-meta P2025 was
91
- * produced by any statement tried here. Either arm classifies identically
92
- * under the rule above (no `relation` → root record), so the rule is safe
93
- * either way, but the shape itself stays unverified.
94
- *
95
- * ## RC-8 is decided, and this measurement is what decided it
96
- *
97
- * Three distinct situations used to answer the one `A2008 CONFLICT`. Coarse
98
- * but correct, so nothing provisional ever reached the wire and the
99
- * discriminant was purely additive (RC-8, D-831). The architect assented
100
- * (2026-10-02) to the discrimination this measurement showed to be available:
101
- * `A2008` now means the **constraint** case alone, a write conflict or
102
- * deadlock answers **`A2013 CONCURRENT_MODIFICATION`**, and the lost-row race
103
- * answers **`A2014 STALE_SELECTION`** from the emulator's own seam. The
104
- * observed surface each decision rests on is this:
73
+ * 1. It is **absent from every payload above**; the identifiers are internal rule
74
+ * names, not a `meta` key. What 7.10.0 actually emits is two different *key
75
+ * sets*, and the discriminant is the presence of `meta.relation` (with
76
+ * `meta.model`): present → the failure is about a relation reference; absent →
77
+ * the record the call selected was not there.
78
+ *
79
+ * Also unobserved: the `{}` meta that `expectNonEmpty` was expected to carry.
80
+ * `findUniqueOrThrow` and `findFirstOrThrow` both report `{ modelName, operation:
81
+ * "a query" }`, so no empty-meta P2025 was produced by any statement tried here.
82
+ * Either arm classifies identically under the rule above (no `relation` → root
83
+ * record), so the rule is safe either way, but the shape itself stays unverified.
84
+ *
85
+ * Three distinct situations used to answer the one `A2008 CONFLICT`. Coarse but
86
+ * correct, so nothing provisional ever reached the wire and the discriminant was
87
+ * purely additive. The observed surface each decision rests on is this:
105
88
  *
106
89
  * | Situation | Code | `meta` keys observed | Structurally separable? |
107
- * |---|---|---|---|
108
- * | Uniqueness violation | `P2002` | `modelName`, `driverAdapterError` | **Yes** — distinct P-code; `cause.kind` is `"UniqueConstraintViolation"`; `cause.constraint` is `{fields}` (SQLite) or `{index}` (PostgreSQL) |
109
- * | Write conflict / deadlock | `P2034` | `modelName`, `driverAdapterError` | **Yes** from a constraint violation — distinct P-code; `cause.kind` is `"TransactionWriteConflict"`; **no** `constraint` at all. **No** from each other: `cause.originalCode` is `40001` for a write conflict and `40P01` for a deadlock, and that field is provider-owned and never crosses the boundary |
110
- * | Lost-row race | *none* | *none* | **Yes, trivially** — there is no provider error behind it: it is a zero-row count in a sequence the framework manufactured, and it is raised in `execution/first-variant.emulator.ts`, never here |
111
- *
112
- * So all three are separable **without reading any message text**, which is
113
- * what ADR 0006 requires and what C-821 warns `P2028` cannot offer, and that
114
- * is why the discrimination is implemented by P-code and by call site alone.
90
+ * |---|---|---|---| | Uniqueness violation | `P2002` | `modelName`,
91
+ * `driverAdapterError` | **Yes** — distinct P-code; `cause.kind` is
92
+ * `"UniqueConstraintViolation"`; `cause.constraint` is `{fields}` (SQLite) or
93
+ * `{index}` (PostgreSQL) | | Write conflict / deadlock | `P2034` | `modelName`,
94
+ * `driverAdapterError` | **Yes** from a constraint violation — distinct P-code;
95
+ * `cause.kind` is `"TransactionWriteConflict"`; **no** `constraint` at all. **No**
96
+ * from each other: `cause.originalCode` is `40001` for a write conflict and
97
+ * `40P01` for a deadlock, and that field is provider-owned and never crosses the
98
+ * boundary | | Lost-row race | *none* | *none* | **Yes, trivially** — there is no
99
+ * provider error behind it: it is a zero-row count in a sequence the framework
100
+ * manufactured, and it is raised in `execution/first-variant.emulator.ts`, never
101
+ * here |
102
+ *
115
103
  * The two provider-backed ones differ by P-code; `driverAdapterError.cause.kind`
116
104
  * is a second, independent witness.
117
105
  *
118
- * **A write conflict and a deadlock share `A2013` deliberately.** Both arrive
119
- * as `P2034` with identical `meta` keys, differing only in a provider-owned
120
- * `originalCode` (`40001` versus `40P01`) that never crosses the boundary.
121
- * They are not separable at this seam, so one outcome is the only honest
122
- * answer; splitting them later would need a discriminant that does not exist. `describePrismaConflict` is the recorded
123
- * surface, and `PrismaConstraintSubject` records that the two engines do not
124
- * agree on *which* identifier they can name — a uniqueness discriminant that
125
- * promised the caller the constraint's name would be unsatisfiable on SQLite.
126
- *
127
- * ## F-608 / D-816 — what was observed about non-`Error` throws
106
+ * **A write conflict and a deadlock share `A2013` deliberately.** Both arrive as
107
+ * `P2034` with identical `meta` keys, differing only in a provider-owned
108
+ * `originalCode` (`40001` versus `40P01`) that never crosses the boundary. They
109
+ * are not separable at this seam, so one outcome is the only honest answer;
110
+ * splitting them later would need a discriminant that does not exist.
111
+ * `describePrismaConflict` is the recorded surface, and `PrismaConstraintSubject`
112
+ * records that the two engines do not agree on *which* identifier they can name —
113
+ * a uniqueness discriminant that promised the caller the constraint's name would
114
+ * be unsatisfiable on SQLite.
128
115
  *
129
116
  * **No Prisma path observed here threw a non-`Error`.** Every induced failure
130
- * arrived as a `PrismaClientKnownRequestError`, which extends `Error`
131
- * (`error instanceof Error` was asserted true for all of them), and the one
132
- * non-Prisma failure the engines produced — `better-sqlite3` refusing a
133
- * missing directory — was a `TypeError`. So the primitive-throw ambiguity
134
- * F-608 describes did not arise for the only real adapter that exists, and
135
- * the general fix (boxing primitive Adapter throws at core's seam) has no
136
- * demonstrated urgency. That observation, not a guess, is what Task 14 (§14a)
137
- * judges the row on. The adapter half is discharged here:
138
- * `prismaOutcomeFailure` returns a `FrameworkError`, so every failure this
139
- * module originates is an `Error` subclass, enforced by a spec.
140
- *
141
- * ## F-723 — why nothing leaks today
142
- *
143
- * A deliberate `FrameworkError`'s top-level `cause.message` is forwarded
144
- * verbatim; that channel is still open and is Phase 13's. What keeps it from
145
- * leaking is this module's own rule (C-819): the cause is framework text, and
146
- * the reader never reads `message` at all, so there is no provider string in
147
- * scope to forward. If that rule is ever relaxed, the leak is immediate —
148
- * which is the input Phase 13 should receive.
117
+ * arrived as a `PrismaClientKnownRequestError`, which extends `Error` (`error
118
+ * instanceof Error` was asserted true for all of them), and the one non-Prisma
119
+ * failure the engines produced — `better-sqlite3` refusing a missing directory —
120
+ * was a `TypeError`. The adapter half is discharged here: `prismaOutcomeFailure`
121
+ * returns a `FrameworkError`, so every failure this module originates is an
122
+ * `Error` subclass, enforced by a spec.
123
+ *
124
+ * What keeps it from leaking is this module's own rule: the cause is framework
125
+ * text, and the reader never reads `message` at all, so there is no provider
126
+ * string in scope to forward.
149
127
  */
150
- /** The three answers a Prisma failure can get. T-10, verbatim. */
128
+ /** The three answers a Prisma failure can get. */
151
129
  export type PrismaErrorClassification = {
152
130
  readonly kind: "absence";
153
131
  } | {
@@ -157,19 +135,14 @@ export type PrismaErrorClassification = {
157
135
  readonly kind: "unclassified";
158
136
  };
159
137
  /**
160
- * When a code is a `null` result rather than a failure (D-811).
161
- *
162
- * `variants` is read literally from D-811: Prisma's `update`/`delete` throw
163
- * P2025 when the selector matches nothing, and core's outcome mapper turns a
164
- * returned `null` into `A2003 NOT_FOUND` for exactly those two variants, so
165
- * the adapter hands back `null` instead of inventing a failure.
166
- *
167
- * `subject` is the measured `meta` shape that may be forgiven. The same
168
- * P2025 reaches `update.unique` for a second reason — a nested `connect`
169
- * whose target is missing — and that one is a caller error about the
170
- * *arguments*, not an absent record. `"root-record"` means `meta` carries no
171
- * relation reference; the single-member union says that is the one shape
172
- * measured, and leaves room for a second if one is ever observed.
138
+ * When a code is a `null` result rather than a failure.
139
+ *
140
+ * `subject` is the measured `meta` shape that may be forgiven. The same P2025
141
+ * reaches `update.unique` for a second reason — a nested `connect` whose target is
142
+ * missing — and that one is a caller error about the *arguments*, not an absent
143
+ * record. `"root-record"` means `meta` carries no relation reference; the
144
+ * single-member union says that is the one shape measured, and leaves room for a
145
+ * second if one is ever observed.
173
146
  */
174
147
  type PrismaAbsenceRule = {
175
148
  readonly variants: readonly `${string}.${string}`[];
@@ -177,14 +150,14 @@ type PrismaAbsenceRule = {
177
150
  };
178
151
  type PrismaErrorRule = {
179
152
  /**
180
- * The outcome this code answers. Typed as core's own
181
- * `OperationErrorCode`, so the adapter cannot widen the code space —
182
- * a code the table does not contain has no rule at all (C-838).
153
+ * The outcome this code answers. Typed as core's own `OperationErrorCode`, so the
154
+ * adapter cannot widen the code space — a code the table does not contain has no
155
+ * rule at all.
183
156
  */
184
157
  readonly outcome: OperationErrorCode;
185
158
  readonly absence?: PrismaAbsenceRule;
186
159
  };
187
- /** One closed table, scoped to `@prisma/client` 7.x (C-846). */
160
+ /** One closed table, scoped to `@prisma/client` 7.x. */
188
161
  export type PrismaErrorTable = Readonly<Record<string, PrismaErrorRule>>;
189
162
  /**
190
163
  * The installed-major this table is written against. The spec asserts the
@@ -192,53 +165,30 @@ export type PrismaErrorTable = Readonly<Record<string, PrismaErrorRule>>;
192
165
  */
193
166
  export declare const PRISMA_ERROR_TABLE_MAJOR = "7";
194
167
  /**
195
- * Every code here was induced above. Codes that 7.10.0 *can* raise but that
196
- * no statement here produced — `P2000`, `P2011`, `P2014`, `P2017`, `P2020`,
197
- * `P2023`, `P2010`, `P2028`, `P2029`, `P2030`, `P2036`, `P2037`, `P2039`,
198
- * `P1008`, `P1017`, `P1018` — are **deliberately absent**: an unmeasured
199
- * rule is a guess, and D-812's answer for anything unrecognised is to leave
200
- * it to core's `AdapterBoundary`, which reports `A3001 ADAPTER_ERROR` with
201
- * core's own text and sends the raw error to diagnostics. Adding a row
202
- * later is additive and costs nothing; shipping a wrong one is not
203
- * recoverable.
168
+ * Every code here was induced above. Adding a row later is additive and costs
169
+ * nothing; shipping a wrong one is not recoverable.
204
170
  */
205
171
  export declare const PRISMA_ERROR_TABLE: PrismaErrorTable;
206
172
  /**
207
173
  * Classifies one failed Prisma call.
208
174
  *
209
- * `error` is `unknown` and stays `unknown`: the reader is the only thing
210
- * that touches it, and the rules see nothing but measured facts. The
211
- * operation is needed because D-811's answer depends on the variant the
212
- * caller asked for, which is framework knowledge and not in the error.
175
+ * `error` is `unknown` and stays `unknown`: the reader is the only thing that
176
+ * touches it, and the rules see nothing but measured facts.
213
177
  */
214
178
  export declare function classifyPrismaError(error: unknown, operation: Pick<OperationIdentity, "family" | "variant">): PrismaErrorClassification;
215
179
  /**
216
180
  * The deliberate failure an `outcome` classification stands for.
217
181
  *
218
- * It is a `FrameworkError`, so every failure this adapter originates is an
219
- * `Error` subclass — D-816's adapter half, which needs no core change.
220
- *
221
- * The cause carries framework text and nothing else: no issues, and that is
222
- * now the **rule** rather than an absence (C-839, ADR 0008 decision 4). A
223
- * conflict is not a Framework Validation Layer failure, ADR 0008 decision 1
224
- * makes a V-code *determine* the A-code, and `validationCode` rolls up to only
225
- * `A2001`/`A2002`/`A2004`/`A2007`/`A2009` — so a conflict V-code would have no
226
- * legal rollup, which is why `A2008`, `A2013` and `A2014` pair with none and
227
- * carry `cause.issues` absent. And no provider text, because a deliberate
228
- * outcome's top-level `cause.message` is forwarded verbatim (C-819, F-723).
229
- * Nothing read off the Prisma error reaches this value.
182
+ * The cause carries framework text and nothing else: no issues, and that is now
183
+ * the **rule** rather than an absence. And no provider text, because a deliberate
184
+ * outcome's top-level `cause.message` is forwarded verbatim. Nothing read off the
185
+ * Prisma error reaches this value.
230
186
  */
231
187
  export declare function prismaOutcomeFailure(code: OperationErrorCode): FrameworkError;
232
188
  /**
233
- * What RC-8 will discriminate on, recorded rather than re-measured.
234
- *
235
- * **Server-side only.** Every field is provider-owned, so this value never
236
- * reaches a `cause` — which is why it is a separate function and not a field
237
- * on `PrismaErrorClassification`: nothing can carry it onto the wire by
238
- * accident. It is the measurement that answered §14c — whether the three
239
- * situations once collapsed into `A2008` are *structurally* separable, which
240
- * they are — and it remains the record of what a diagnostics sink could
241
- * usefully be told.
189
+ * **Server-side only.** Every field is provider-owned, so this value never reaches
190
+ * a `cause` — which is why it is a separate function and not a field on
191
+ * `PrismaErrorClassification`: nothing can carry it onto the wire by accident.
242
192
  */
243
193
  export type PrismaConflictSubject = {
244
194
  readonly code: string;
@@ -1,44 +1,12 @@
1
1
  import { FrameworkError, } from "@aventara/core";
2
2
  import { readPrismaError, } from "./prisma-error.reader.js";
3
- /**
4
- * The installed-major this table is written against. The spec asserts the
5
- * installed `@prisma/client` agrees.
6
- */
7
3
  export const PRISMA_ERROR_TABLE_MAJOR = "7";
8
- /**
9
- * Every code here was induced above. Codes that 7.10.0 *can* raise but that
10
- * no statement here produced — `P2000`, `P2011`, `P2014`, `P2017`, `P2020`,
11
- * `P2023`, `P2010`, `P2028`, `P2029`, `P2030`, `P2036`, `P2037`, `P2039`,
12
- * `P1008`, `P1017`, `P1018` — are **deliberately absent**: an unmeasured
13
- * rule is a guess, and D-812's answer for anything unrecognised is to leave
14
- * it to core's `AdapterBoundary`, which reports `A3001 ADAPTER_ERROR` with
15
- * core's own text and sends the raw error to diagnostics. Adding a row
16
- * later is additive and costs nothing; shipping a wrong one is not
17
- * recoverable.
18
- */
19
4
  export const PRISMA_ERROR_TABLE = {
20
- // Uniqueness conflict — caller-actionable state conflict, HTTP 409 (D-810, C-820).
21
5
  P2002: { outcome: "A2008" },
22
- // Foreign-key violation — an invalid reference is the same class of
23
- // conflict, and D-810 maps it to the same code. These two are what
24
- // `A2008` now means, and the whole of it: re-running either request
25
- // unchanged fails the same way, which is what separates them from
26
- // `A2013`/`A2014`.
27
6
  P2003: { outcome: "A2008" },
28
- // Write conflict or deadlock: another operation reached the same state
29
- // first, so nothing this one asked for was applied. Both arrive as P2034
30
- // and are not separable at this seam, so they share one outcome. Prisma
31
- // performs no automatic retry and this adapter adds none (C-822, D-809,
32
- // F-705): a retried plan would re-run developer callbacks whose side
33
- // effects are not rollback-safe. A retry is the caller's to make.
7
+ P2014: { outcome: "A2008" },
34
8
  P2034: { outcome: "A2013" },
35
- // The required connected records were not found: measured only with a
36
- // relation reference in `meta`, and always about the arguments.
37
9
  P2018: { outcome: "A2004" },
38
- // Depends on records that were required but not found. A caller error
39
- // about the arguments by default; a `null` result on the two strict
40
- // unique variants, and only when the missing record is the one the call
41
- // selected rather than a relation target (D-811).
42
10
  P2025: {
43
11
  outcome: "A2004",
44
12
  absence: {
@@ -47,38 +15,17 @@ export const PRISMA_ERROR_TABLE = {
47
15
  },
48
16
  },
49
17
  };
50
- /** Framework-owned text, one line per code it may emit. */
51
18
  const OUTCOME_MESSAGES = {
52
- // `A2008` now means the constraint case alone, and its text is
53
- // UNCHANGED on purpose. §13.2's narrowed meaning — "would violate a
54
- // uniqueness or reference constraint the Resource declares" — is
55
- // documentation, NOT a legal wire message: C-819's measured sweeps ban
56
- // `constraint`, `Unique` and `UNIQUE` from a provider-originated
57
- // envelope, because those are the provider's own words for this very
58
- // failure. Narrowing the code's meaning does not license naming the
59
- // constraint to the caller, and this line is what keeps the two apart.
60
19
  A2008: "The operation conflicts with the current state of the resource.",
61
20
  A2013: "Another operation modified the same state concurrently, so this operation was not applied and nothing was written.",
62
21
  A2004: "The operation's arguments referenced a record that does not exist.",
63
22
  };
64
- /**
65
- * `"root-record"`: the failure is about the record the call selected, not
66
- * about a relation target. Measured discriminant, not message text.
67
- */
68
23
  function matchesAbsenceSubject(subject, facts) {
69
24
  switch (subject) {
70
25
  case "root-record":
71
26
  return facts.relation === undefined;
72
27
  }
73
28
  }
74
- /**
75
- * Classifies one failed Prisma call.
76
- *
77
- * `error` is `unknown` and stays `unknown`: the reader is the only thing
78
- * that touches it, and the rules see nothing but measured facts. The
79
- * operation is needed because D-811's answer depends on the variant the
80
- * caller asked for, which is framework knowledge and not in the error.
81
- */
82
29
  export function classifyPrismaError(error, operation) {
83
30
  const facts = readPrismaError(error);
84
31
  if (facts.code === undefined) {
@@ -100,22 +47,6 @@ export function classifyPrismaError(error, operation) {
100
47
  ? { kind: "absence" }
101
48
  : { kind: "outcome", code: rule.outcome };
102
49
  }
103
- /**
104
- * The deliberate failure an `outcome` classification stands for.
105
- *
106
- * It is a `FrameworkError`, so every failure this adapter originates is an
107
- * `Error` subclass — D-816's adapter half, which needs no core change.
108
- *
109
- * The cause carries framework text and nothing else: no issues, and that is
110
- * now the **rule** rather than an absence (C-839, ADR 0008 decision 4). A
111
- * conflict is not a Framework Validation Layer failure, ADR 0008 decision 1
112
- * makes a V-code *determine* the A-code, and `validationCode` rolls up to only
113
- * `A2001`/`A2002`/`A2004`/`A2007`/`A2009` — so a conflict V-code would have no
114
- * legal rollup, which is why `A2008`, `A2013` and `A2014` pair with none and
115
- * carry `cause.issues` absent. And no provider text, because a deliberate
116
- * outcome's top-level `cause.message` is forwarded verbatim (C-819, F-723).
117
- * Nothing read off the Prisma error reaches this value.
118
- */
119
50
  export function prismaOutcomeFailure(code) {
120
51
  return new FrameworkError(code, {
121
52
  message: OUTCOME_MESSAGES[code] ?? "The operation could not be completed.",