@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.
- package/README.md +33 -20
- package/dist/discovery/artifact.loader.d.ts +6 -2
- package/dist/discovery/artifact.loader.js +12 -57
- package/dist/discovery/capability-surface.interface.d.ts +29 -40
- package/dist/discovery/capability-surface.interface.js +0 -12
- package/dist/discovery/capability.deriver.d.ts +10 -40
- package/dist/discovery/capability.deriver.js +0 -179
- package/dist/discovery/client-shape.verifier.d.ts +13 -0
- package/dist/discovery/client-shape.verifier.js +93 -0
- package/dist/discovery/discovery-artifact.interface.d.ts +55 -30
- package/dist/discovery/enum.mapper.d.ts +6 -14
- package/dist/discovery/enum.mapper.js +1 -2
- package/dist/discovery/field.mapper.d.ts +49 -63
- package/dist/discovery/field.mapper.js +0 -161
- package/dist/discovery/identifier.mapper.d.ts +38 -77
- package/dist/discovery/identifier.mapper.js +0 -64
- package/dist/discovery/model.discoverer.d.ts +18 -35
- package/dist/discovery/model.discoverer.js +66 -141
- package/dist/discovery/operations.deriver.d.ts +10 -38
- package/dist/discovery/operations.deriver.js +0 -40
- package/dist/discovery/scalar.mapper.d.ts +12 -35
- package/dist/discovery/scalar.mapper.js +0 -8
- package/dist/discovery/schema-facts.interface.d.ts +26 -25
- package/dist/discovery/schema-facts.interface.js +0 -10
- package/dist/discovery/schema.reader.d.ts +53 -64
- package/dist/discovery/schema.reader.js +0 -174
- package/dist/discovery/types.reader.d.ts +0 -4
- package/dist/discovery/types.reader.js +2 -177
- package/dist/discovery/vocabulary.dictionary.d.ts +36 -75
- package/dist/discovery/vocabulary.dictionary.js +2 -152
- package/dist/errors/error.normalizer.d.ts +85 -135
- package/dist/errors/error.normalizer.js +1 -70
- package/dist/errors/prisma-error.reader.d.ts +53 -68
- package/dist/errors/prisma-error.reader.js +0 -91
- package/dist/execution/first-variant.emulator.d.ts +11 -22
- package/dist/execution/first-variant.emulator.js +0 -142
- package/dist/execution/operation.executor.d.ts +12 -19
- package/dist/execution/operation.executor.js +0 -92
- package/dist/execution/result.shaper.d.ts +22 -31
- package/dist/execution/result.shaper.js +0 -24
- package/dist/execution/transaction.executor.d.ts +36 -73
- package/dist/generate/artifact.emitter.d.ts +27 -27
- package/dist/generate/artifact.emitter.js +30 -13
- package/dist/generate/declared-support.reader.d.ts +11 -11
- package/dist/generate/declared-support.reader.js +0 -12
- package/dist/generate/discovery.bin.js +0 -10
- package/dist/generate/discovery.cli.js +0 -55
- package/dist/generate/existence.gate.d.ts +26 -32
- package/dist/generate/existence.gate.js +0 -6
- package/dist/generate/index.d.ts +10 -12
- package/dist/generate/index.js +0 -42
- package/dist/generate/node-version.guard.js +0 -12
- package/dist/generate/prisma-cli.resolver.d.ts +9 -11
- package/dist/generate/prisma-cli.resolver.js +0 -13
- package/dist/generate/prisma.generate.d.ts +49 -65
- package/dist/generate/prisma.generate.js +13 -123
- package/dist/generate/reader-crosscheck.d.ts +36 -60
- package/dist/generate/reader-crosscheck.js +5 -41
- package/dist/prisma-client.interface.d.ts +14 -19
- package/dist/prisma-client.interface.js +0 -6
- package/dist/prisma.adapter.d.ts +9 -17
- package/dist/prisma.adapter.js +11 -53
- package/dist/provider/provider-profile.d.ts +6 -23
- package/dist/provider/provider-profile.js +0 -64
- package/dist/translation/identifier.addressing.d.ts +15 -22
- package/dist/translation/identifier.addressing.js +0 -10
- package/dist/translation/mutation.translator.d.ts +19 -43
- package/dist/translation/mutation.translator.js +1 -209
- package/dist/translation/operation.translator.d.ts +36 -63
- package/dist/translation/operation.translator.js +1 -51
- package/dist/translation/order-pagination.translator.d.ts +24 -31
- package/dist/translation/order-pagination.translator.js +0 -43
- package/dist/translation/projection.translator.d.ts +25 -58
- package/dist/translation/projection.translator.js +0 -27
- package/dist/translation/scalar.conversion.d.ts +15 -26
- package/dist/translation/scalar.conversion.js +1 -129
- package/dist/translation/where.translator.d.ts +17 -28
- package/dist/translation/where.translator.js +0 -83
- 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
|
|
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: "
|
|
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
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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
|
-
*
|
|
20
|
-
* `@prisma/adapter-better-sqlite3` and `@prisma/adapter-pg`, both 7.10.0.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
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.
|
|
75
|
-
*
|
|
76
|
-
* `meta.
|
|
77
|
-
* `
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
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
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
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
|
-
*
|
|
120
|
-
* `originalCode` (`40001` versus `40P01`) that never crosses the boundary.
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* agree on *which* identifier they can name —
|
|
125
|
-
* promised the caller the constraint's name would
|
|
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
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* `
|
|
139
|
-
*
|
|
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.
|
|
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
|
|
161
|
-
*
|
|
162
|
-
* `
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
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
|
-
*
|
|
182
|
-
*
|
|
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
|
|
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.
|
|
196
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
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
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
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
|
-
|
|
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.",
|