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

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 +3 -3
@@ -1,38 +1,38 @@
1
1
  /**
2
- * The total read of one hostile value (T-10's first half).
2
+ * The total read of one hostile value.
3
3
  *
4
- * Everything on the failure path arrives as `unknown`. `@prisma/client`
5
- * 7.10.0 raises `PrismaClientKnownRequestError`, whose `meta` is typed
6
- * `Record<string, unknown>` and is **not** a discriminated union, so a
7
- * reader that trusts the type would be reading a shape nobody promised.
8
- * Worse, the value need not come from Prisma at all: a developer callback,
9
- * a driver, or a proxy can hand the adapter anything.
4
+ * Everything on the failure path arrives as `unknown`. `@prisma/client` 7.10.0
5
+ * raises `PrismaClientKnownRequestError`, whose `meta` is typed `Record<string,
6
+ * unknown>` and is **not** a discriminated union, so a reader that trusts the type
7
+ * would be reading a shape nobody promised. Worse, the value need not come from
8
+ * Prisma at all: a developer callback, a driver, or a proxy can hand the adapter
9
+ * anything.
10
10
  *
11
- * So this module reads structurally and defensively, under two rules it
12
- * never relaxes — the same totality discipline as core's diagnostics
13
- * reporter, which also observes values it cannot vouch for:
11
+ * So this module reads structurally and defensively, under two rules it never
12
+ * relaxes — the same totality discipline as core's diagnostics reporter, which
13
+ * also observes values it cannot vouch for:
14
14
  *
15
15
  * 1. **No accessor is ever invoked.** Every read goes through
16
- * `Object.getOwnPropertyDescriptor` and takes `descriptor.value`. An
17
- * accessor descriptor yields `undefined` and its getter is never called,
18
- * so a booby-trapped property cannot run code, mutate state, or throw
19
- * from inside the adapter's failure path.
20
- * 2. **Nothing escapes.** Every reflective call is guarded, because a Proxy
21
- * may throw from its `getOwnPropertyDescriptor` or `ownKeys` trap, and a
22
- * reader that throws while classifying a failure would replace a legible
23
- * outcome with an illegible one.
24
- *
25
- * Reading only *own* properties is deliberate rather than incidental:
26
- * 7.10.0 carries `code` and `meta` as own data properties (observed own
27
- * keys: `["code","meta","clientVersion","name"]`), so the own read is
28
- * sufficient for every real error, and a `code` that exists only as an
29
- * inherited accessor is precisely the case rule 1 refuses to touch.
16
+ * `Object.getOwnPropertyDescriptor` and takes `descriptor.value`. An accessor
17
+ * descriptor yields `undefined` and its getter is never called, so a
18
+ * booby-trapped property cannot run code, mutate state, or throw from inside
19
+ * the adapter's failure path.
20
+ * 2. **Nothing escapes.** Every reflective call is guarded, because a Proxy may
21
+ * throw from its `getOwnPropertyDescriptor` or `ownKeys` trap, and a reader
22
+ * that throws while classifying a failure would replace a legible outcome with
23
+ * an illegible one.
24
+ *
25
+ * Reading only *own* properties is deliberate rather than incidental: 7.10.0
26
+ * carries `code` and `meta` as own data properties (observed own keys:
27
+ * `["code","meta","clientVersion","name"]`), so the own read is sufficient for
28
+ * every real error, and a `code` that exists only as an inherited accessor is
29
+ * precisely the case rule 1 refuses to touch.
30
30
  *
31
31
  * ## What this module does NOT read
32
32
  *
33
- * `message` is never read. At 7.10.0 a `PrismaClientKnownRequestError`
34
- * message is not a sentence — it is a rendered call site. Measured
35
- * verbatim on this repository's own schema:
33
+ * `message` is never read. At 7.10.0 a `PrismaClientKnownRequestError` message is
34
+ * not a sentence — it is a rendered call site. Measured verbatim on this
35
+ * repository's own schema:
36
36
  *
37
37
  * ```text
38
38
  * Invalid `client.author.create()` invocation in
@@ -43,35 +43,29 @@
43
43
  * Unique constraint failed on the fields: (`email`)
44
44
  * ```
45
45
  *
46
- * That single string carries an absolute file path, a source excerpt and
47
- * the violated constraint. C-819 forbids all three from reaching `cause`,
48
- * and a *deliberate* outcome's top-level `cause.message` is still forwarded
49
- * verbatim (register row F-723, open). The cheapest guarantee is not to
50
- * read the message at all, so the leak has no source; the raw error still
51
- * reaches core's diagnostics sink, which is server-side and may carry it.
52
- *
53
- * ## Version bound (C-846)
46
+ * That single string carries an absolute file path, a source excerpt and the
47
+ * violated constraint. The cheapest guarantee is not to read the message at all,
48
+ * so the leak has no source; the raw error still reaches core's diagnostics sink,
49
+ * which is server-side and may carry it.
54
50
  *
55
51
  * Everything below is scoped to `@prisma/client` **7.x**. Prisma 8 replaced
56
- * P-codes with dotted `NAMESPACE.SUBCODE` codes, which makes a Prisma 8
57
- * adapter a re-map of this module rather than a patch to it.
52
+ * P-codes with dotted `NAMESPACE.SUBCODE` codes, which makes a Prisma 8 adapter a
53
+ * re-map of this module rather than a patch to it.
58
54
  */
59
55
  /**
60
- * The violated constraint as 7.10.0's driver adapters actually report it,
61
- * at `meta.driverAdapterError.cause.constraint` (D-810).
56
+ * The violated constraint as 7.10.0's driver adapters actually report it, at
57
+ * `meta.driverAdapterError.cause.constraint`.
62
58
  *
63
59
  * The arms are **provider-shaped, measured on both engines**, not guessed:
64
60
  *
65
- * | Engine | Unique (P2002) | Foreign key (P2003) |
61
+ * | Engine | Unique (P2002) | Foreign key (P2003) |
66
62
  * |-----------------------------------|-------------------------------|-------------------------|
67
- * | `@prisma/adapter-better-sqlite3` | `{ fields: ["email"] }` | `{ foreignKey: {} }` |
68
- * | `@prisma/adapter-pg` | `{ index: "Author_email_key" }` | `{ index: "Post_authorId_fkey" }` |
63
+ * | `@prisma/adapter-better-sqlite3` | `{ fields: ["email"] }` | `{ foreignKey: {}
64
+ * }` | | `@prisma/adapter-pg` | `{ index: "Author_email_key" }` | `{ index:
65
+ * "Post_authorId_fkey" }` |
69
66
  *
70
- * It is therefore **not a string**, and the two engines do not agree on
71
- * which identifier they can name — SQLite names columns, PostgreSQL names
72
- * the index. Every arm's payload is provider-owned text and never crosses
73
- * the boundary (C-819); the *shape* is kept because it is structurally
74
- * separable, which is what RC-8 needs from Task 12.
67
+ * It is therefore **not a string**, and the two engines do not agree on which
68
+ * identifier they can name — SQLite names columns, PostgreSQL names the index.
75
69
  */
76
70
  export type PrismaConstraintSubject = {
77
71
  readonly kind: "fields";
@@ -93,25 +87,20 @@ export type PrismaConstraintSubject = {
93
87
  */
94
88
  export type PrismaErrorFacts = {
95
89
  /**
96
- * The P-code, when it is an own string property. A missing or non-string
97
- * `code` leaves this `undefined`, which the table answers as
98
- * `unclassified` — never as a guess (D-812).
90
+ * The P-code, when it is an own string property. A missing or non-string `code`
91
+ * leaves this `undefined`, which the table answers as `unclassified` — never as a
92
+ * guess.
99
93
  */
100
94
  readonly code: string | undefined;
101
95
  /**
102
- * The own, enumerable key names of `meta`, in the order 7.10.0 wrote
103
- * them. This is the *structural* signature of a failure, and the only
104
- * discriminant ADR 0006 permits: it forbids inferring an outcome from
105
- * error text, and `meta.operation` is English prose (`"an update"`,
106
- * `"a delete"`, `"a query"`, `"a nested connect"`).
96
+ * The own, enumerable key names of `meta`, in the order 7.10.0 wrote them.
107
97
  */
108
98
  readonly metaKeys: readonly string[];
109
99
  /** `meta.modelName` — the Prisma model the failure occurred on. */
110
100
  readonly modelName: string | undefined;
111
101
  /**
112
- * `meta.relation` — present exactly when the failure is about a
113
- * *relation reference* rather than about the record the call selected.
114
- * This is the measured discriminant behind D-811 (see the normalizer).
102
+ * `meta.relation` — present exactly when the failure is about a *relation
103
+ * reference* rather than about the record the call selected.
115
104
  */
116
105
  readonly relation: string | undefined;
117
106
  /** `meta.model` — the model at the far end of `relation`. */
@@ -119,17 +108,13 @@ export type PrismaErrorFacts = {
119
108
  /** `meta.driverAdapterError.cause.constraint`, read structurally. */
120
109
  readonly constraint: PrismaConstraintSubject | undefined;
121
110
  /**
122
- * `meta.target` — **read so that it is measured, depended on by
123
- * nothing** (C-820, RC-9).
111
+ * `meta.target` — **read so that it is measured, depended on by nothing**.
124
112
  *
125
- * The v7 error-reference page still documents `{ target: ['email'] }`,
126
- * and RC-9 records that whether 7.10.0 ever emits it could not be
127
- * determined from the package. It was induced on both engines here and
128
- * **`target` was absent from every payload** — P2002 (single and
129
- * compound unique), P2003, P2025 (three shapes), P2018 and P2034, on
130
- * SQLite and on PostgreSQL. The docs are stale; the installed package
131
- * governs (spec §18.1). This field exists so the next reader sees that
132
- * measured, and does not "fix" the code to match the documentation.
113
+ * It was induced on both engines here and **`target` was absent from every
114
+ * payload** — P2002 (single and compound unique), P2003, P2025 (three shapes),
115
+ * P2018 and P2034, on SQLite and on PostgreSQL. The docs are stale; the installed
116
+ * package governs. This field exists so the next reader sees that measured, and
117
+ * does not "fix" the code to match the documentation.
133
118
  */
134
119
  readonly legacyTarget: readonly string[] | undefined;
135
120
  };
@@ -1,73 +1,6 @@
1
- /**
2
- * The total read of one hostile value (T-10's first half).
3
- *
4
- * Everything on the failure path arrives as `unknown`. `@prisma/client`
5
- * 7.10.0 raises `PrismaClientKnownRequestError`, whose `meta` is typed
6
- * `Record<string, unknown>` and is **not** a discriminated union, so a
7
- * reader that trusts the type would be reading a shape nobody promised.
8
- * Worse, the value need not come from Prisma at all: a developer callback,
9
- * a driver, or a proxy can hand the adapter anything.
10
- *
11
- * So this module reads structurally and defensively, under two rules it
12
- * never relaxes — the same totality discipline as core's diagnostics
13
- * reporter, which also observes values it cannot vouch for:
14
- *
15
- * 1. **No accessor is ever invoked.** Every read goes through
16
- * `Object.getOwnPropertyDescriptor` and takes `descriptor.value`. An
17
- * accessor descriptor yields `undefined` and its getter is never called,
18
- * so a booby-trapped property cannot run code, mutate state, or throw
19
- * from inside the adapter's failure path.
20
- * 2. **Nothing escapes.** Every reflective call is guarded, because a Proxy
21
- * may throw from its `getOwnPropertyDescriptor` or `ownKeys` trap, and a
22
- * reader that throws while classifying a failure would replace a legible
23
- * outcome with an illegible one.
24
- *
25
- * Reading only *own* properties is deliberate rather than incidental:
26
- * 7.10.0 carries `code` and `meta` as own data properties (observed own
27
- * keys: `["code","meta","clientVersion","name"]`), so the own read is
28
- * sufficient for every real error, and a `code` that exists only as an
29
- * inherited accessor is precisely the case rule 1 refuses to touch.
30
- *
31
- * ## What this module does NOT read
32
- *
33
- * `message` is never read. At 7.10.0 a `PrismaClientKnownRequestError`
34
- * message is not a sentence — it is a rendered call site. Measured
35
- * verbatim on this repository's own schema:
36
- *
37
- * ```text
38
- * Invalid `client.author.create()` invocation in
39
- * /home/.../packages/prisma7-adapter/src/errors/__probe__.spec.ts:50:18
40
- *
41
- * 49 await capture("P2002 duplicate email", () =>
42
- * → 50 client.author.create(
43
- * Unique constraint failed on the fields: (`email`)
44
- * ```
45
- *
46
- * That single string carries an absolute file path, a source excerpt and
47
- * the violated constraint. C-819 forbids all three from reaching `cause`,
48
- * and a *deliberate* outcome's top-level `cause.message` is still forwarded
49
- * verbatim (register row F-723, open). The cheapest guarantee is not to
50
- * read the message at all, so the leak has no source; the raw error still
51
- * reaches core's diagnostics sink, which is server-side and may carry it.
52
- *
53
- * ## Version bound (C-846)
54
- *
55
- * Everything below is scoped to `@prisma/client` **7.x**. Prisma 8 replaced
56
- * P-codes with dotted `NAMESPACE.SUBCODE` codes, which makes a Prisma 8
57
- * adapter a re-map of this module rather than a patch to it.
58
- */
59
- /** Only objects and functions can carry own properties. */
60
1
  function canCarryProperties(value) {
61
2
  return ((typeof value === "object" && value !== null) || typeof value === "function");
62
3
  }
63
- /**
64
- * One own *data* property, or `undefined`.
65
- *
66
- * `"value" in descriptor` is what separates a data descriptor from an
67
- * accessor one: an accessor descriptor has `get`/`set` and no `value`, so
68
- * it falls through and the getter is never invoked. The `try` is for a
69
- * Proxy whose `getOwnPropertyDescriptor` trap throws.
70
- */
71
4
  function ownValue(target, key) {
72
5
  if (!canCarryProperties(target)) {
73
6
  return undefined;
@@ -86,7 +19,6 @@ function ownString(target, key) {
86
19
  const value = ownValue(target, key);
87
20
  return typeof value === "string" ? value : undefined;
88
21
  }
89
- /** Own enumerable key names, or none. Guarded for a hostile `ownKeys` trap. */
90
22
  function ownKeys(target) {
91
23
  if (!canCarryProperties(target)) {
92
24
  return [];
@@ -98,11 +30,6 @@ function ownKeys(target) {
98
30
  return [];
99
31
  }
100
32
  }
101
- /**
102
- * A string list read element by element: a sparse array, a nested array or
103
- * a list of objects yields only the strings it does contain, and anything
104
- * that is not an array at all yields `undefined`.
105
- */
106
33
  function ownStringList(target, key) {
107
34
  const value = ownValue(target, key);
108
35
  if (!Array.isArray(value)) {
@@ -116,16 +43,6 @@ function ownStringList(target, key) {
116
43
  }
117
44
  return strings;
118
45
  }
119
- /**
120
- * `meta.driverAdapterError.cause.constraint`, as the engines shape it.
121
- *
122
- * The chain is walked one accessor-free step at a time, so a `meta` that is
123
- * a string, a `driverAdapterError` that is `null`, or a `cause` with a
124
- * throwing trap all end as `undefined` rather than as a throw. A
125
- * `constraint` that is present but matches no measured arm reports
126
- * `"unreadable"` — a recorded unknown, which is not the same answer as "no
127
- * constraint was reported".
128
- */
129
46
  function readConstraint(meta) {
130
47
  const cause = ownValue(ownValue(meta, "driverAdapterError"), "cause");
131
48
  const constraint = ownValue(cause, "constraint");
@@ -145,14 +62,6 @@ function readConstraint(meta) {
145
62
  }
146
63
  return { kind: "unreadable" };
147
64
  }
148
- /**
149
- * Reads one hostile value into the facts a classification may use.
150
- *
151
- * Total: every input produces a `PrismaErrorFacts`. `readPrismaError(null)`
152
- * and `readPrismaError(Object.create(null))` are as valid as a real
153
- * `PrismaClientKnownRequestError` — they simply report nothing, which the
154
- * table answers as `unclassified`.
155
- */
156
65
  export function readPrismaError(error) {
157
66
  const meta = ownValue(error, "meta");
158
67
  return {
@@ -2,35 +2,25 @@ import { FrameworkError } from "@aventara/core";
2
2
  import type { PrismaIdentifierAddress } from "../discovery/discovery-artifact.interface.js";
3
3
  import type { PrismaCall } from "../translation/operation.translator.js";
4
4
  /**
5
- * The lost-row conflict: the read selected a row and the re-asserted write
6
- * matched none, so **nothing was written** and a retry may succeed.
5
+ * The lost-row conflict: the read selected a row and the re-asserted write matched
6
+ * none, so **nothing was written** and a retry may succeed.
7
7
  *
8
- * `A2014 STALE_SELECTION`, and this is the only place that names it. The cause
9
- * carries framework text and no issues: like `A2008` and `A2013` it pairs with
10
- * **no** V-code, because a conflict is not a Framework Validation Layer failure
11
- * and ADR 0008 decision 1 would give a conflict V-code no legal rollup (C-839).
8
+ * `A2014 STALE_SELECTION`, and this is the only place that names it.
12
9
  */
13
10
  export declare function lostRowConflict(): FrameworkError;
14
11
  /**
15
12
  * Issues one of the emulation's two statements.
16
- *
17
- * `step` is passed because the two are classified **differently** and the
18
- * discrimination is by call site: the read's failures go through the closed P-code
19
- * table like any other call, while the write's `P2025` is this module's lost row
20
- * and must not reach D-811's rule.
21
13
  */
22
14
  export type FirstVariantIssuer = (call: PrismaCall, step: "read" | "write") => Promise<unknown>;
23
15
  /**
24
16
  * How the emulation's two statements are issued, and against which client.
25
17
  *
26
- * **`inUnit` opens the unit AND yields the issuer scoped to it**, in that order,
27
- * which is the only shape that cannot get D-807 wrong. An earlier draft of this
28
- * module took the two separately — a `unit` that opened a transaction and an
29
- * `issue` already bound to a client — and the standalone case then opened a
30
- * transaction whose two statements ran on the **root** client, outside it: the
31
- * emulation would have looked atomic and been nothing of the kind, and no
32
- * assertion about the issued `where` would have noticed. Making the unit the thing
33
- * that hands out the issuer removes the gap rather than documenting it.
18
+ * An earlier draft of this module took the two separately — a `unit` that opened a
19
+ * transaction and an `issue` already bound to a client — and the standalone case
20
+ * then opened a transaction whose two statements ran on the **root** client,
21
+ * outside it: the emulation would have looked atomic and been nothing of the kind,
22
+ * and no assertion about the issued `where` would have noticed. Making the unit
23
+ * the thing that hands out the issuer removes the gap rather than documenting it.
34
24
  *
35
25
  * This module therefore never sees a Prisma client, a provider name or a
36
26
  * transaction; it sees one issuer, already in the right unit.
@@ -44,9 +34,8 @@ export type FirstVariantEmulation = {
44
34
  * Opens the one adapter-internal unit and runs `work` inside it.
45
35
  *
46
36
  * Standalone this opens an interactive transaction and the issuer it yields is
47
- * scoped to it; inside a plan it runs `work` against the outer transaction
48
- * client, so the emulated steps roll back **with** the plan rather than
49
- * independently (§14.5).
37
+ * scoped to it; inside a plan it runs `work` against the outer transaction client,
38
+ * so the emulated steps roll back **with** the plan rather than independently.
50
39
  */
51
40
  readonly inUnit: <T>(work: (issue: FirstVariantIssuer) => Promise<T>) => Promise<T>;
52
41
  };
@@ -1,145 +1,14 @@
1
1
  import { FrameworkError } from "@aventara/core";
2
2
  import { readPrismaError } from "../errors/prisma-error.reader.js";
3
3
  import { addressIdentifier } from "../translation/identifier.addressing.js";
4
- /**
5
- * `update.first` and `delete.first` — the two **framework-manufactured**
6
- * variants, and the one adapter-internal unit that manufactures them (D-807,
7
- * D-828, C-856; closes F-704).
8
- *
9
- * # What is manufactured, and why that word
10
- *
11
- * The shape Aventara advertises — *general filter · ordering decides "first" ·
12
- * effect limited to one row · the affected row is returned, in one call* — is
13
- * essentially absent across ORM design, because SQL splits the ingredients along
14
- * a dialect fault line: `UPDATE … ORDER BY … LIMIT` is a MySQL/MariaDB extension
15
- * while `RETURNING` is Postgres/SQLite/MSSQL, so whichever dialect grants the
16
- * limit denies the returning **[SURVEY]**. Prisma 7.10.0's `Operation` union has
17
- * no `updateFirst` and no `deleteFirst` **[PKG]**. So these are not a Prisma-
18
- * shaped gap an adapter might omit; they are an operation the framework
19
- * manufactures, and the honest framing — the one the plan, C-857 and the package
20
- * README all use — is *framework-manufactured, non-atomic, with a defined
21
- * conflict outcome*, **not** "dangerous".
22
- *
23
- * # Two statements, and the firstness lives in the first one
24
- *
25
- * 1. `findFirst({ where, orderBy, select: <identifier> })`. The `orderBy` the
26
- * caller sent is the **selector for which row**, not a limit: ordering and
27
- * single-row selection are natural on a read. No match → `null`, which core's
28
- * outcome mapper turns into `A1001 NO_MATCH` (200, `data: null`) (C-826).
29
- * 2. `update`/`delete` narrowed to the identifier the read returned **with the
30
- * caller's original predicate re-asserted** — `{ <identifier>, AND: <original
31
- * where> }` — carrying the caller's own projection, so the row the contract
32
- * owes comes back from the write itself (§8.2, §8.4). `WhereUniqueInput`'s
33
- * extended form admits exactly this, and `extendedWhereUnique` is GA **[RUN8]**.
34
- *
35
- * Prisma's `limit` on `updateMany`/`deleteMany` is **not** used. That is not a
36
- * principle — D-827 rules the opposite, an argument the ORM offers is not hidden —
37
- * it is that the `limit` cannot express this operation: neither `updateMany` nor
38
- * `deleteMany` carries an `orderBy` in 7.10.0 **[RUN8]**, so `limit: 1` selects an
39
- * **arbitrary** row and two identical calls can hit different rows. The identifier
40
- * from the read is what makes the write deterministic.
41
- *
42
- * # What re-assertion closes, stated precisely
43
- *
44
- * It closes the **wrong-row race**: without it the write is by identifier alone
45
- * and can mutate a row that no longer matches what the caller asked for; with it
46
- * the write can only ever touch a row the read selected, and a row that moved out
47
- * of the predicate is **detected** rather than mis-mutated.
48
- *
49
- * It does **not** close **stale firstness**. Between the read and the write
50
- * another writer can insert or update a row that sorts *ahead* of the selected one
51
- * and still matches the original predicate. Both steps are blind to it — the read
52
- * never saw it, and the re-asserted write succeeds against the row it was given —
53
- * so `update.first` can mutate a row that is no longer the first ordered match and
54
- * report ordinary success. Nothing detects it and no re-assertion can, because the
55
- * missing fact is about a row that was never read. Closing it needs either
56
- * `Serializable` isolation, which core's `Adapter.transaction(work)` seam cannot
57
- * express (RC-3), so it is **accepted as a documented property**: C-830 has Phase 8
58
- * escalate no isolation, and `first-variant.emulator.spec.ts` carries a case that
59
- * *demonstrates* the residual rather than asserting it away.
60
- *
61
- * # The lost row is discriminated BY CALL SITE, never by `meta`
62
- *
63
- * A write that matches nothing after a successful read is the **lost-row race**: a
64
- * distinct semantic outcome the caller may retry. Under the chosen Route A it
65
- * arrives as a thrown `P2025` — measured at the emulator's own call site, inside
66
- * an interactive transaction, on both engines (2026-10-02):
67
- *
68
- * ```text
69
- * tx.author.update({ where: { id: <read>, AND: [{ tenantId: "gone" }] }, data })
70
- * -> P2025, meta = { modelName: "Author", operation: "an update" }
71
- * ```
72
- *
73
- * That meta is **identical** to the one D-811 forgives into a `null` for
74
- * `update.unique`/`delete.unique`, so the two rules cannot be told apart by the
75
- * error at all. They are told apart by **where the call was made**: a `P2025`
76
- * caught by {@link runFirstVariantEmulation}'s write step is the lost row, and
77
- * nothing else in this adapter forgives a `P2025` into a `null`. This is why the
78
- * emulator does not consult `classifyPrismaError` for its write step and why
79
- * D-811's rule is not shared.
80
- *
81
- * {@link lostRowConflict} is the one seam that names the outcome. It lives here
82
- * rather than in `error.normalizer.ts` (ownership corrected 2026-09-23) because
83
- * the normalizer owns the closed P-code table and this condition has **no provider
84
- * error behind it at all** under Route B, and under Route A the provider error is
85
- * only the messenger. It answers **`A2014 STALE_SELECTION`** (409) — its own
86
- * discriminated code since the architect's assent of 2026-10-02 (RC-8, D-831),
87
- * replacing the coarse `A2008` it shared with the constraint cases and with
88
- * `A2013`'s concurrent write. The code is reached from here and from nowhere
89
- * else: the closed P-code table has no row that can emit it, because the
90
- * discriminant is the call site and not anything in the error.
91
- *
92
- * # One adapter-internal unit — F-704's answer
93
- *
94
- * Inside a plan the emulation reuses the **outer transaction client**; standalone
95
- * it opens **its own interactive transaction** (D-807, C-830). The nested option
96
- * is real — `$transaction` is not in the interactive-client deny list, which is
97
- * exactly `["$connect","$disconnect","$on","$use","$extends"]`, and
98
- * `Transaction.Options.newTxId` exists for nested interactive transactions
99
- * **[PKG]** — and it is rejected on semantics, not on SQL: a nested savepoint
100
- * would let an emulated variant roll back *independently* of the plan, which
101
- * contradicts §14.5 ("if any operation fails, all transaction writes roll back")
102
- * and core's single-sentinel model. The literal `SAVEPOINT` statement is not in
103
- * the JS bundle, so the decision deliberately rests on that semantic argument
104
- * rather than on an unverifiable claim about the SQL issued.
105
- *
106
- * No isolation level is escalated: RC-3's readings are unadopted, safety comes
107
- * from re-assertion, and the spec asserts the transaction **boundary**, not a
108
- * level.
109
- */
110
- /**
111
- * The framework-owned text of the lost-row outcome, which is `A2014`'s published
112
- * meaning verbatim. It holds for **any** adapter that reaches this state,
113
- * including one that reaches it in a single statement with optimistic
114
- * concurrency: it names no provider, no emulation, no savepoint, no retry loop
115
- * and no statement count. Carries no provider string (C-819).
116
- */
117
4
  const LOST_ROW_MESSAGE = "The state this operation had selected changed before its effect could be applied, so nothing was written.";
118
- /**
119
- * The lost-row conflict: the read selected a row and the re-asserted write
120
- * matched none, so **nothing was written** and a retry may succeed.
121
- *
122
- * `A2014 STALE_SELECTION`, and this is the only place that names it. The cause
123
- * carries framework text and no issues: like `A2008` and `A2013` it pairs with
124
- * **no** V-code, because a conflict is not a Framework Validation Layer failure
125
- * and ADR 0008 decision 1 would give a conflict V-code no legal rollup (C-839).
126
- */
127
5
  export function lostRowConflict() {
128
6
  return new FrameworkError("A2014", { message: LOST_ROW_MESSAGE });
129
7
  }
130
- /** Prisma's code for "the records required for this operation were not found". */
131
8
  const MISSING_RECORD_CODE = "P2025";
132
- /** A plan that is not the two-step shape this module manufactures. */
133
9
  export class PrismaEmulationError extends Error {
134
10
  name = "PrismaEmulationError";
135
11
  }
136
- /**
137
- * Runs one first-style variant.
138
- *
139
- * @returns the affected row, shaped to the caller's `select`/`include` by the
140
- * write's own projection, or `null` when the read matched nothing.
141
- * @throws the lost-row conflict when the re-asserted write matches no row.
142
- */
143
12
  export async function runFirstVariantEmulation(emulation) {
144
13
  const [read, write] = emulation.steps;
145
14
  if (read === undefined || write === undefined) {
@@ -148,7 +17,6 @@ export async function runFirstVariantEmulation(emulation) {
148
17
  return emulation.inUnit(async (issue) => {
149
18
  const selected = await issue(read, "read");
150
19
  if (selected === null || selected === undefined) {
151
- // C-826: the adapter returns `null` and core assigns `A1001 NO_MATCH`.
152
20
  return null;
153
21
  }
154
22
  if (!isRecord(selected)) {
@@ -157,9 +25,6 @@ export async function runFirstVariantEmulation(emulation) {
157
25
  const addressed = addressIdentifier({
158
26
  addresses: emulation.identifiers,
159
27
  values: selected,
160
- // `"subset"`: the read selects the identifier today, and a future route
161
- // may select the caller's projection beside it. Either way the criterion
162
- // is complete, which is all the write needs.
163
28
  match: "subset",
164
29
  emit: (_member, value) => value,
165
30
  });
@@ -169,12 +34,6 @@ export async function runFirstVariantEmulation(emulation) {
169
34
  try {
170
35
  return await issue({
171
36
  ...write,
172
- // The invariant, and the whole point of the step: the issued `where`
173
- // carries BOTH the identifier and the caller's original predicate
174
- // (C-856, D-829). The predicate arrives already under `AND` from the
175
- // translator, so merging the identifier in cannot reshape it — and when
176
- // the caller sent no `where` there is no `AND` to carry, because
177
- // inventing an empty one would emit a token the request never did.
178
37
  args: {
179
38
  ...write.args,
180
39
  where: { ...addressed.where, ...writeWhere(write) },
@@ -189,7 +48,6 @@ export async function runFirstVariantEmulation(emulation) {
189
48
  }
190
49
  });
191
50
  }
192
- /** The re-asserted predicate the translator placed on the write template. */
193
51
  function writeWhere(write) {
194
52
  const where = write.args.where;
195
53
  if (where === undefined) {
@@ -2,28 +2,23 @@ import type { AdapterModel, AdapterOperationExecutor } from "@aventara/core";
2
2
  import type { PrismaResourceTranslation } from "../discovery/discovery-artifact.interface.js";
3
3
  import { type PrismaDelegateHost, type PrismaTransactionOptions } from "../prisma-client.interface.js";
4
4
  /**
5
- * The execution half of T-7: a translated {@link PrismaCall} → a provider call,
6
- * and `Adapter.execute` made real.
7
- *
8
5
  * Translation is pure and lives in `translation/operation.translator.ts`; this
9
6
  * module owns the three things that need a client and nothing else:
10
7
  *
11
8
  * 1. **Reaching the delegate.** See {@link delegateKeyOf} — the one derivation in
12
9
  * this package, and the one place it is made.
13
10
  * 2. **Classifying a failure.** The closed P-code table in
14
- * `errors/error.normalizer.ts` answers `absence` → `null` (D-811),
15
- * `outcome` → a deliberate `FrameworkError`, or `unclassified` → propagate, so
16
- * core's `AdapterBoundary` reports `A3001` with core's own text and sends the
17
- * raw error to diagnostics. The adapter never guesses a code it did not measure.
11
+ * `errors/error.normalizer.ts` answers `absence` → `null`, `outcome` → a
12
+ * deliberate `FrameworkError`, or `unclassified` → propagate, so core's
13
+ * `AdapterBoundary` reports `A3001` with core's own text and sends the raw
14
+ * error to diagnostics. The adapter never guesses a code it did not measure.
18
15
  * 3. **Shaping the answer.** `execution/result.shaper.ts` places every `$count`
19
16
  * envelope, converts every returned value to the runtime form core's output
20
- * validator accepts (C-834's inbound half) and reads the number a count variant
21
- * owes; which of its three entry points runs is the *variant's* `result` row in
22
- * the dispatch table, not a guess from the shape Prisma returned. The shaper is
23
- * handed the model and the Resource as well as the plan, because the scalar a
24
- * returned value belongs to is a fact of the model and not of the value.
25
- *
26
- * # One executor type, two scopes (D-807, C-804)
17
+ * validator accepts and reads the number a count variant owes; which of its
18
+ * three entry points runs is the *variant's* `result` row in the dispatch
19
+ * table, not a guess from the shape Prisma returned. The shaper is handed the
20
+ * model and the Resource as well as the plan, because the scalar a returned
21
+ * value belongs to is a fact of the model and not of the value.
27
22
  *
28
23
  * `scope: "root"` is the adapter's own `execute`. `scope: "transaction"` is what
29
24
  * `Adapter.transaction`'s callback hands core: the same code over a transaction
@@ -32,10 +27,8 @@ import { type PrismaDelegateHost, type PrismaTransactionOptions } from "../prism
32
27
  * transaction-scoped executor closes over the transaction client only, so the root
33
28
  * client is unreachable from inside it.
34
29
  *
35
- * # C-834 is converted at its own seam, not at this one
36
- *
37
- * All three value crossings read ONE table, `translation/scalar.conversion.ts`:
38
- * a write's data through `mutation.translator.ts`, a filter operand through
30
+ * All three value crossings read ONE table, `translation/scalar.conversion.ts`: a
31
+ * write's data through `mutation.translator.ts`, a filter operand through
39
32
  * `where.translator.ts`, and a returned row through `execution/result.shaper.ts`.
40
33
  * None of them is converted here. Converting at this seam would have put the
41
34
  * scalar table in a fourth place and hidden the gap from the module that owns it.
@@ -63,7 +56,7 @@ export type PrismaOperationExecutorDependencies = {
63
56
  /** The generate-time translation table (Prisma addressing per Resource). */
64
57
  readonly translation: Readonly<Record<string, PrismaResourceTranslation>>;
65
58
  readonly scope: PrismaExecutionScope;
66
- /** Adapter-owned, never Aventara public configuration (D-806). */
59
+ /** Adapter-owned, never Aventara public configuration. */
67
60
  readonly transactionOptions?: PrismaTransactionOptions;
68
61
  };
69
62
  /**