@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
@@ -3,74 +3,9 @@ import { prismaDelegates, } from "../prisma-client.interface.js";
3
3
  import { PRISMA_VARIANT_DISPATCH, translateOperation, } from "../translation/operation.translator.js";
4
4
  import { runFirstVariantEmulation } from "./first-variant.emulator.js";
5
5
  import { shapeCount, shapeRow, shapeRows, } from "./result.shaper.js";
6
- /**
7
- * The execution half of T-7: a translated {@link PrismaCall} → a provider call,
8
- * and `Adapter.execute` made real.
9
- *
10
- * Translation is pure and lives in `translation/operation.translator.ts`; this
11
- * module owns the three things that need a client and nothing else:
12
- *
13
- * 1. **Reaching the delegate.** See {@link delegateKeyOf} — the one derivation in
14
- * this package, and the one place it is made.
15
- * 2. **Classifying a failure.** The closed P-code table in
16
- * `errors/error.normalizer.ts` answers `absence` → `null` (D-811),
17
- * `outcome` → a deliberate `FrameworkError`, or `unclassified` → propagate, so
18
- * core's `AdapterBoundary` reports `A3001` with core's own text and sends the
19
- * raw error to diagnostics. The adapter never guesses a code it did not measure.
20
- * 3. **Shaping the answer.** `execution/result.shaper.ts` places every `$count`
21
- * envelope, converts every returned value to the runtime form core's output
22
- * validator accepts (C-834's inbound half) and reads the number a count variant
23
- * owes; which of its three entry points runs is the *variant's* `result` row in
24
- * the dispatch table, not a guess from the shape Prisma returned. The shaper is
25
- * handed the model and the Resource as well as the plan, because the scalar a
26
- * returned value belongs to is a fact of the model and not of the value.
27
- *
28
- * # One executor type, two scopes (D-807, C-804)
29
- *
30
- * `scope: "root"` is the adapter's own `execute`. `scope: "transaction"` is what
31
- * `Adapter.transaction`'s callback hands core: the same code over a transaction
32
- * client. The scope decides exactly one thing — whether a first-style emulation
33
- * opens its own interactive transaction or reuses the one already open — and a
34
- * transaction-scoped executor closes over the transaction client only, so the root
35
- * client is unreachable from inside it.
36
- *
37
- * # C-834 is converted at its own seam, not at this one
38
- *
39
- * All three value crossings read ONE table, `translation/scalar.conversion.ts`:
40
- * a write's data through `mutation.translator.ts`, a filter operand through
41
- * `where.translator.ts`, and a returned row through `execution/result.shaper.ts`.
42
- * None of them is converted here. Converting at this seam would have put the
43
- * scalar table in a fourth place and hidden the gap from the module that owns it.
44
- *
45
- * **Core's output validation runs after this module, and strips.** For a
46
- * projection-carrying variant `planReadOperation` sets `materialize: true` and
47
- * `materializeRecord` ends in `Object.fromEntries(plan.requested…)` **[CORE]**, so
48
- * a spare key the adapter returned would be removed before the validator saw it.
49
- * That is why the emulator's own return value is asserted directly in
50
- * `first-variant.emulator.spec.ts` as well as end-to-end here: an end-to-end
51
- * assertion about extra keys cannot fail on that path.
52
- */
53
- /** A call the executor cannot issue against the client it was given. */
54
6
  export class PrismaExecutionError extends Error {
55
7
  name = "PrismaExecutionError";
56
8
  }
57
- /**
58
- * Prisma's delegate property for a model.
59
- *
60
- * **Derived, and that is a recorded residual rather than a comfortable choice.**
61
- * The authoritative token is readable — `generated/<engine>/models/<M>.ts`, a 🟢
62
- * file, writes it as the extension key: `GetSelect<{ … },
63
- * ExtArgs["result"]["author"]>` for model `Author` — but `types.reader.ts` reads
64
- * the 🟢 files for *capability* facts and does not record it, and the client
65
- * property itself is declared only in `internal/class.ts`, which that reader
66
- * refuses by design. So the rule applied here is Prisma's own: lower-case the
67
- * first character and nothing else. `operation.executor.spec.ts` asserts the
68
- * derivation against the real generated client for **every** model in the schema,
69
- * so it is observed for this repository's six models; a model name whose Prisma
70
- * property does not follow the rule would be caught there and not in production.
71
- * The clean owner of the fact is the generate-time translation table, beside
72
- * `identifierKeys`, and that is left as a pointer rather than taken here.
73
- */
74
9
  export function delegateKeyOf(model) {
75
10
  const first = model.slice(0, 1);
76
11
  return `${first.toLowerCase()}${model.slice(1)}`;
@@ -98,9 +33,6 @@ export function createPrismaOperationExecutor(dependencies) {
98
33
  steps: translation.plan.steps,
99
34
  identifiers: identifiersOf(dependencies, operation.resource),
100
35
  inUnit: (work) => withUnit(dependencies, (client) => work((call, step) => {
101
- // The READ goes through the same classification as any other
102
- // call; the WRITE's own `P2025` is the emulator's to interpret,
103
- // by call site.
104
36
  const send = (one) => resolveDelegateCall(client, one)(one.args);
105
37
  return step === "read"
106
38
  ? issueClassified(send, call, operation)
@@ -111,33 +43,15 @@ export function createPrismaOperationExecutor(dependencies) {
111
43
  },
112
44
  };
113
45
  }
114
- /**
115
- * D-807's unit. Standalone the emulation opens its **own** interactive
116
- * transaction and the steps run on **that** client; inside a plan it reuses the
117
- * outer transaction client, so its steps roll back with the plan rather than
118
- * independently (§14.5). No isolation level is escalated (C-830).
119
- */
120
46
  async function withUnit(dependencies, work) {
121
47
  if (dependencies.scope === "transaction") {
122
- // The plan's transaction is already open and `client` IS its client, so a
123
- // nested one would let these steps roll back independently of the plan.
124
48
  return work(dependencies.client);
125
49
  }
126
50
  const options = dependencies.transactionOptions;
127
- // The client the callback is handed is the one the steps run on: that is what
128
- // makes the two statements one unit rather than two statements near one.
129
51
  return options === undefined
130
52
  ? dependencies.client.$transaction((transactional) => work(transactional))
131
53
  : dependencies.client.$transaction((transactional) => work(transactional), options);
132
54
  }
133
- /**
134
- * What the variant owes, read off the dispatch table.
135
- *
136
- * An `"unavailable"` entry is refused here rather than silently shaped: core
137
- * rejects the request with `A2002 UNKNOWN_OPERATION` long before this, because
138
- * `operations.deriver.ts` never advertised it, so reaching this line means the
139
- * Contract and the adapter have diverged and that is a defect, not a request.
140
- */
141
55
  function resultKindOf(dispatch) {
142
56
  if (dispatch.kind === "unavailable") {
143
57
  throw new PrismaExecutionError(`the Prisma adapter advertises no call for this operation: ${dispatch.reason}`);
@@ -171,12 +85,6 @@ function resolveDelegateCall(client, call) {
171
85
  }
172
86
  return (args) => Promise.resolve(method.call(delegate, args));
173
87
  }
174
- /**
175
- * Issues one call and routes its failure through the closed P-code table.
176
- *
177
- * `absence` returns `null`, which core's `OUTCOME_TABLE` turns into `A2003` for a
178
- * strict-unique variant (D-811) — the adapter never names the A-code.
179
- */
180
88
  async function issueClassified(issue, call, operation) {
181
89
  try {
182
90
  return await issue(call);
@@ -1,33 +1,25 @@
1
1
  import type { AdapterModel } from "@aventara/core";
2
2
  import type { PrismaProjectionPlan } from "../translation/projection.translator.js";
3
3
  /**
4
- * Task 8 / T-8 — the shape core's output validator expects back (C-832, §10.3),
5
- * and C-834's inbound value conversion.
6
- *
7
4
  * **The envelope is the adapter's to build, not core's.** `output-validator.ts`
8
5
  * accepts exactly `["count"]` or `["data","count"]` for a relation whose plan
9
- * carries the `$count` reducer, and rejects any key outside the projection —
10
- * which includes Prisma's own `_count` **[CORE, runtime/output-validator.ts]**.
11
- * So Prisma's answer `{ id, posts: [...], _count: { posts: 3 } }` must leave here
12
- * as `{ id, posts: { data: [...], count: 3 } }`, with `_count` gone.
13
- *
14
- * **The three §10.3 shapes, and which one applies:**
15
- *
16
- * | nested select | shape | this module |
17
- * |---|---|---|
18
- * | `["id","title"]` | array | returns Prisma's rows, values converted |
19
- * | `["$count"]` | `{ count }` | reads `_count.<edge>`; no rows were selected |
20
- * | `["id","$count"]` | `{ data, count }` | pairs the rows with `_count.<edge>` |
6
+ * carries the `$count` reducer, and rejects any key outside the projection — which
7
+ * includes Prisma's own `_count` **[CORE, runtime/output-validator.ts]**. So
8
+ * Prisma's answer `{ id, posts: [...], _count: { posts: 3 } }` must leave here as
9
+ * `{ id, posts: { data: [...], count: 3 } }`, with `_count` gone.
21
10
  *
22
- * # C-834's inbound half lives here, and it is keyed by the MODEL
11
+ * | nested select | shape | this module | |---|---|---| | `["id","title"]` | array
12
+ * | returns Prisma's rows, values converted | | `["$count"]` | `{ count }` | reads
13
+ * `_count.<edge>`; no rows were selected | | `["id","$count"]` | `{ data, count }`
14
+ * | pairs the rows with `_count.<edge>` |
23
15
  *
24
- * The same validator that rejects a spare key also checks every projected
25
- * scalar's runtime form, and a decimal must be core's own `Decimal`
26
- * **[CORE, output-validator.ts → `normalizeScalarFieldValue`]**. Prisma returns an
27
- * object whose constructor is `Decimal2`, so before this conversion existed every
28
- * read of a decimal column failed core's output validation and the caller was
29
- * told `A3000` — measured through `createFramework(...).execute(...)` on both
30
- * engines (2026-10-02). The conversion itself belongs to
16
+ * The same validator that rejects a spare key also checks every projected scalar's
17
+ * runtime form, and a decimal must be core's own `Decimal` **[CORE,
18
+ * output-validator.ts → `normalizeScalarFieldValue`]**. Prisma returns an object
19
+ * whose constructor is `Decimal2`, so before this conversion existed every read of
20
+ * a decimal column failed core's output validation and the caller was told `A3000`
21
+ * — measured through `createFramework(...).execute(...)` on both engines
22
+ * (2026-10-02). The conversion itself belongs to
31
23
  * `translation/scalar.conversion.ts`, the one table that knows how each scalar
32
24
  * crosses the boundary in either direction; this module owns only *which field a
33
25
  * returned value belongs to*.
@@ -49,15 +41,15 @@ import type { PrismaProjectionPlan } from "../translation/projection.translator.
49
41
  * cannot place an envelope).
50
42
  *
51
43
  * **Cardinality is never assumed.** `AdapterOperation` carries no plan, and the
52
- * arguments the plan is reconstructed from do not say whether an edge is to-one
53
- * or to-many. So recursion is driven by the value Prisma actually returned — an
54
- * array is shaped elementwise, a record is shaped as one row, `null` passes
55
- * through — which is total without knowing the cardinality at all.
44
+ * arguments the plan is reconstructed from do not say whether an edge is to-one or
45
+ * to-many. So recursion is driven by the value Prisma actually returned — an array
46
+ * is shaped elementwise, a record is shaped as one row, `null` passes through —
47
+ * which is total without knowing the cardinality at all.
56
48
  *
57
49
  * Nothing here invents a number. A missing or non-numeric `_count` entry for a
58
- * planned reducer is a translation/driver disagreement, and it raises rather
59
- * than defaulting to `0`: a wrong count that validates is worse than a loud
60
- * failure, because core would forward it to the caller as fact.
50
+ * planned reducer is a translation/driver disagreement, and it raises rather than
51
+ * defaulting to `0`: a wrong count that validates is worse than a loud failure,
52
+ * because core would forward it to the caller as fact.
61
53
  *
62
54
  * **Nothing Prisma returned is written into.** A row is copied only when some
63
55
  * value in it actually changed, so a result needing no conversion and no envelope
@@ -99,5 +91,4 @@ export declare function shapeRows(rows: readonly unknown[], shaping: PrismaRowSh
99
91
  * **[CORE, runtime/output-validator.ts]**.
100
92
  */
101
93
  export declare function shapeCount(result: unknown): number;
102
- /** T-8's shaper, as one value the operation executor can hold. */
103
94
  export declare const prismaResultShaper: PrismaResultShaper;
@@ -1,14 +1,11 @@
1
1
  import { receiveScalarValue } from "../translation/scalar.conversion.js";
2
- /** A provider result the adapter will not reshape into a claim about the data. */
3
2
  export class PrismaResultShapeError extends Error {
4
3
  constructor(reason) {
5
4
  super(reason);
6
5
  this.name = "PrismaResultShapeError";
7
6
  }
8
7
  }
9
- /** Prisma's spelling of the reducer envelope on the parent row. */
10
8
  const PRISMA_COUNT_KEY = "_count";
11
- /** An edge with no reducer anywhere beneath it. */
12
9
  const NO_REDUCERS = { relations: [] };
13
10
  function isRecord(value) {
14
11
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -21,7 +18,6 @@ function readCount(row, field) {
21
18
  }
22
19
  return count;
23
20
  }
24
- /** The nested shaping for one edge: its target Resource, and its own plan. */
25
21
  function edgeShaping(shaping, field, node) {
26
22
  return {
27
23
  model: shaping.model,
@@ -47,13 +43,6 @@ function shapeReducerRows(value, field, shaping) {
47
43
  }
48
44
  return value.map((entry) => shapeRow(entry, shaping));
49
45
  }
50
- /**
51
- * Converts every scalar in one provider row and places every `$count` envelope.
52
- *
53
- * @returns the row unchanged, by identity, when no value needed converting and
54
- * the plan records no edge that needs reshaping; a copy otherwise. The row Prisma
55
- * returned is never mutated.
56
- */
57
46
  export function shapeRow(row, shaping) {
58
47
  if (!isRecord(row)) {
59
48
  return row;
@@ -68,13 +57,10 @@ export function shapeRow(row, shaping) {
68
57
  const shaped = {};
69
58
  for (const [key, value] of Object.entries(row)) {
70
59
  if (key === PRISMA_COUNT_KEY && envelopes.length > 0) {
71
- // The reducer pass below reads it; core rejects it as a spare key.
72
60
  continue;
73
61
  }
74
62
  const field = resource.fields[key];
75
63
  if (field === undefined) {
76
- // Not a field of this Resource — Prisma's own `_count` with no reducer
77
- // planned, or a divergence. Core's output validation owns that verdict.
78
64
  shaped[key] = value;
79
65
  continue;
80
66
  }
@@ -86,7 +72,6 @@ export function shapeRow(row, shaping) {
86
72
  }
87
73
  const node = nodes.get(key);
88
74
  if (node !== undefined && node.reducers.length > 0) {
89
- // Written by the reducer pass, from the raw row.
90
75
  continue;
91
76
  }
92
77
  const nested = shapeValue(value, edgeShaping(shaping, field, node));
@@ -109,7 +94,6 @@ export function shapeRow(row, shaping) {
109
94
  }
110
95
  return changed ? shaped : row;
111
96
  }
112
- /** Shapes a `many` result: one pass per row. */
113
97
  export function shapeRows(rows, shaping) {
114
98
  let changed = false;
115
99
  const shaped = rows.map((row) => {
@@ -119,13 +103,6 @@ export function shapeRows(rows, shaping) {
119
103
  });
120
104
  return changed ? shaped : rows;
121
105
  }
122
- /**
123
- * Reads the number a `count` variant must return.
124
- *
125
- * Prisma answers a bare number from `count` and `{ count }` from `updateMany` /
126
- * `deleteMany`; core requires a non-negative safe integer either way
127
- * **[CORE, runtime/output-validator.ts]**.
128
- */
129
106
  export function shapeCount(result) {
130
107
  const count = isRecord(result) ? result.count : result;
131
108
  if (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0) {
@@ -133,7 +110,6 @@ export function shapeCount(result) {
133
110
  }
134
111
  return count;
135
112
  }
136
- /** T-8's shaper, as one value the operation executor can hold. */
137
113
  export const prismaResultShaper = {
138
114
  shapeRow,
139
115
  shapeRows,
@@ -2,56 +2,32 @@ import type { AdapterModel, AdapterOperationExecutor } from "@aventara/core";
2
2
  import type { PrismaResourceTranslation } from "../discovery/discovery-artifact.interface.js";
3
3
  import type { PrismaDelegateHost, PrismaTransactionOptions } from "../prisma-client.interface.js";
4
4
  /**
5
- * T-9 — `Adapter.transaction`: one interactive Prisma transaction per framework
6
- * plan, and the transaction-scoped executor core runs the plan through.
7
- *
8
5
  * The body is one `client.$transaction(async (tx) => work(scopedExecutorFor(tx)),
9
- * options)`. Everything interesting about this module is what it deliberately
10
- * does **not** do, so each omission is recorded here rather than left to be
6
+ * options)`. Everything interesting about this module is what it deliberately does
7
+ * **not** do, so each omission is recorded here rather than left to be
11
8
  * rediscovered.
12
9
  *
13
- * # The callback form, never the array form (C-803)
14
- *
15
10
  * Prisma's array form — `$transaction` applied to a list of prepared calls —
16
- * cannot honour a plan at all. An Aventara plan is
17
- * **sequential by contract**: operation *n* may carry a `$ref` resolved from
18
- * operation *n − 1*'s final data, and core resolves it between calls, so the
19
- * second call's arguments do not exist until the first has answered. The array
20
- * form takes every call up front. It is therefore a framework invariant and not
21
- * a preference, and it is protected twice: {@link PrismaDelegateHost} declares
22
- * `$transaction` in its callback form only, so the array overload is not
23
- * *representable* through the seam this adapter holds, and
24
- * `transaction.executor.spec.ts` asserts both that every call this adapter makes
25
- * passes a function and that the array form's own opening token occurs nowhere in
26
- * `src` (that spec assembles the token rather than writing it, for the same
27
- * reason this sentence does not quote it).
28
- *
29
- * # The root client is unreachable from inside (C-804)
30
- *
31
- * `scopedExecutorFor` closes over the callback's `tx` and nothing else, so no
32
- * plan operation can reach the client this module was constructed with — there is
33
- * no path to it from the value core is handed. `scope: "transaction"` additionally
34
- * tells `operation.executor.ts` that an emulated first-style variant must reuse
35
- * this transaction rather than open its own (D-807): inside a plan a nested
36
- * savepoint would let the emulation roll back independently of the plan, which
37
- * §14.5 forbids.
38
- *
39
- * # No `try`, no `catch`, no wrapper (C-805, C-806)
11
+ * cannot honour a plan at all. An Aventara plan is **sequential by contract**:
12
+ * operation *n* may carry a `$ref` resolved from operation *n − 1*'s final data,
13
+ * and core resolves it between calls, so the second call's arguments do not exist
14
+ * until the first has answered. The array form takes every call up front. It is
15
+ * therefore a framework invariant and not a preference, and it is protected twice:
16
+ * {@link PrismaDelegateHost} declares `$transaction` in its callback form only, so
17
+ * the array overload is not *representable* through the seam this adapter holds,
18
+ * and `transaction.executor.spec.ts` asserts both that every call this adapter
19
+ * makes passes a function and that the array form's own opening token occurs
20
+ * nowhere in `src` (that spec assembles the token rather than writing it, for the
21
+ * same reason this sentence does not quote it).
22
+ *
23
+ * `scopedExecutorFor` closes over the callback's `tx` and nothing else, so no plan
24
+ * operation can reach the client this module was constructed with — there is no
25
+ * path to it from the value core is handed.
40
26
  *
41
27
  * Core throws a private frozen abort sentinel out of `work` after it has recorded
42
- * a failing operation, and recognises the rejection **by `===` alone**. Prisma
43
- * 7.10.0 rethrows the caught value by reference after a best-effort rollback
44
- * **[P7R Q3]**, so the contract is met by *adding nothing*: a `catch` that
45
- * re-threw anything — even `error` wrapped in context — would turn every plan
46
- * failure into core's `foreign-abort` integrity failure and lose the operation's
47
- * own code.
28
+ * a failing operation, and recognises the rejection **by `===` alone**.
48
29
  *
49
- * **Prisma swallows its own rollback failures.** The rollback call's rejection is
50
- * caught and handed to an internal debug logger — `await
51
- * this._engine.transaction("rollback", …).catch(E => { kt("rollback attempt
52
- * %d/%d failed: %O", …) })`, with `kt = debug("prisma:client")` **[PKG,
53
- * `runtime/client.js`; P7R Q4]** — so a failed rollback never reaches this
54
- * module. **Induced, not assumed** (2026-10-02, PostgreSQL 18): a write inside a
30
+ * **Induced, not assumed** (2026-10-02, PostgreSQL 18): a write inside a
55
31
  * transaction, the backend terminated from a second connection, a frozen sentinel
56
32
  * thrown from the callback. Prisma's `ROLLBACK` failed — `prisma:client rollback
57
33
  * attempt 1/1 failed: '{}'` appears under `DEBUG=prisma*` — and the value the
@@ -62,22 +38,18 @@ import type { PrismaDelegateHost, PrismaTransactionOptions } from "../prisma-cli
62
38
  * succeeded, because it cannot observe that it did. A commit failure is different:
63
39
  * it is thrown from inside the same `try` and rejects like a callback throw.
64
40
  *
65
- * # `maxWait`, `timeout` and `isolationLevel` are adapter-owned (D-806, F-702)
66
- *
67
41
  * They are provider concepts: core's `Adapter.transaction(work)` carries no
68
- * options and must not grow any, so they are set once at
69
- * `createPrismaAdapter({ transactionOptions })` and are not part of Aventara's
70
- * public configuration. Prisma's own defaults stand when they are absent —
71
- * `maxWait` 2000 ms, `timeout` 5000 ms **[PKG, `runtime/client.d.ts`]** — and the
72
- * supported isolation levels come from the provider profile, which discovered
73
- * them, rather than from a table written here.
42
+ * options and must not grow any, so they are set once at `createPrismaAdapter({
43
+ * transactionOptions })` and are not part of Aventara's public configuration.
44
+ * Prisma's own defaults stand when they are absent — `maxWait` 2000 ms, `timeout`
45
+ * 5000 ms **[PKG, `runtime/client.d.ts`]** — and the supported isolation levels
46
+ * come from the provider profile, which discovered them, rather than from a table
47
+ * written here.
74
48
  *
75
49
  * The options are **not** forwarded to the scoped executor. At `"transaction"`
76
50
  * scope `operation.executor.ts` never opens a transaction, so an option passed
77
51
  * there would be dead configuration that reads as if it did something.
78
52
  *
79
- * # `P2028`, as measured rather than as documented (C-821, D-808)
80
- *
81
53
  * Induced against PostgreSQL 18 on `@prisma/client` 7.10.0, 2026-10-02 (the
82
54
  * assertions live in `conformance/postgres.conformance.spec.ts`):
83
55
  *
@@ -104,27 +76,18 @@ import type { PrismaDelegateHost, PrismaTransactionOptions } from "../prisma-cli
104
76
  *
105
77
  * So of the seven `P2028` classes in 7.10.0 — `TransactionManagerError` plus
106
78
  * `nt`/`un`/`cn`/`fr`/`pn`/`Me`/`dn` **[PKG, `runtime/client.js`]** — the
107
- * separable-by-`meta` set is **three**: the transaction-timeout expiry carries
108
- * `{ operation, timeout, timeTaken }`, the unsupported isolation level carries
109
- * `{ isolationLevel }`, and the acquisition timeout carries nothing and is
110
- * separable *by* that emptiness from the other two. It is **not** separable from
111
- * the four remaining classes: transaction-not-found, already-committed,
112
- * already-rolled-back and internal-consistency all arrive as `P2028` with no
113
- * `meta` at all, and telling those five apart needs the message text, which
114
- * ADR 0006 forbids. C-821's claim that the invalid isolation level is a
115
- * `meta`-carrying `P2028` subclass therefore **holds**, by a narrower route than
116
- * C-821 states: only a level Prisma's own union does not contain is rejected by
117
- * the query compiler first, as `P2023`. An earlier revision of this comment
79
+ * separable-by-`meta` set is **three**: the transaction-timeout expiry carries `{
80
+ * operation, timeout, timeTaken }`, the unsupported isolation level carries `{
81
+ * isolationLevel }`, and the acquisition timeout carries nothing and is separable
82
+ * *by* that emptiness from the other two. An earlier revision of this comment
118
83
  * withdrew the claim outright; it was measured on the wrong input, and
119
84
  * `conformance/postgres.conformance.spec.ts` and
120
85
  * `conformance/sqlite.conformance.spec.ts` both assert the corrected fact.
121
86
  *
122
- * D-808's outcome — `P2028` → `A3002 TRANSACTION_ERROR` — needs no row in the
123
- * adapter's P-code table to hold, and that is the honest reading of **where** it
124
- * holds. A `P2028` that ends the transaction rejects out of `$transaction` itself,
125
- * is not core's sentinel, and core's own `infrastructureFailure` answers `A3002`.
126
- * A `P2028` raised by a *delegate call* inside the callback is a different route:
127
- * it is unclassified by the closed table, so core's `AdapterBoundary` attributes
87
+ * A `P2028` that ends the transaction rejects out of `$transaction` itself, is not
88
+ * core's sentinel, and core's own `infrastructureFailure` answers `A3002`. A
89
+ * `P2028` raised by a *delegate call* inside the callback is a different route: it
90
+ * is unclassified by the closed table, so core's `AdapterBoundary` attributes
128
91
  * `A3001` to that operation. `A3002` is therefore the answer for the failure that
129
92
  * ends the transaction, not a uniform answer for the code — recorded here because
130
93
  * the difference is invisible from the table alone.
@@ -135,13 +98,13 @@ export type PrismaTransactionExecutorDependencies = {
135
98
  readonly model: AdapterModel;
136
99
  /** The generate-time translation table (Prisma addressing per Resource). */
137
100
  readonly translation: Readonly<Record<string, PrismaResourceTranslation>>;
138
- /** Adapter-owned, never Aventara public configuration (D-806). */
101
+ /** Adapter-owned, never Aventara public configuration. */
139
102
  readonly transactionOptions?: PrismaTransactionOptions;
140
103
  };
141
104
  /**
142
105
  * `Adapter<M>["transaction"]` without the optionality: the adapter mounts it
143
- * exactly when the discovered model declares `transactions: "interactive"`
144
- * (D-805, DA-12), so the function itself is total.
106
+ * exactly when the discovered model declares `transactions: "interactive"`, so the
107
+ * function itself is total.
145
108
  */
146
109
  export type PrismaTransactionRunner<M extends AdapterModel> = <T>(work: (executor: AdapterOperationExecutor<M>) => Promise<T>) => Promise<T>;
147
110
  export declare function createPrismaTransactionExecutor<M extends AdapterModel>(dependencies: PrismaTransactionExecutorDependencies): PrismaTransactionRunner<M>;
@@ -1,25 +1,21 @@
1
- import type { PrismaAdapterModel, PrismaReferentialActionTable, PrismaTranslationTable } from "../discovery/discovery-artifact.interface.js";
1
+ import type { PrismaAdapterModel, PrismaClientShape, PrismaCreateRequirementTable, PrismaReferentialActionTable, PrismaTranslationTable } from "../discovery/discovery-artifact.interface.js";
2
2
  import type { PrismaProvider } from "../provider/provider-profile.js";
3
3
  /**
4
- * T-3.2 under **ADR 0009 (Accepted), which overrides the plan**: the discovery
5
- * artifact is **one TypeScript module**, not JSON.
4
+ * It predates the ADR and loses to it, for a reason the ADR states plainly:
5
+ * `resolveJsonModule` widens `"string"` to `string`, so a JSON model carries no
6
+ * literals, and carrying them would take a second emitted `.d.ts` beside the data
7
+ * — two emissions from one source, which can drift. One module written `as const
8
+ * satisfies AdapterModel` makes the value and the type the same expression, so
9
+ * drift is not merely detectable but unrepresentable.
6
10
  *
7
- * The plan's T-3.2 says JSON. It predates the ADR and loses to it, for a reason
8
- * the ADR states plainly: `resolveJsonModule` widens `"string"` to `string`, so
9
- * a JSON model carries no literals, and carrying them would take a second
10
- * emitted `.d.ts` beside the data — two emissions from one source, which can
11
- * drift. One module written `as const satisfies AdapterModel` makes the value
12
- * and the type the same expression, so drift is not merely detectable but
13
- * unrepresentable.
11
+ * The emitted module is still passive, serializable, introspectable, hashable and
12
+ * free of runtime callbacks. It contains three `const` bindings and no executable
13
+ * statement; `discovery.model` and `discovery.translation` *reference* their
14
+ * bindings rather than restating them, so there is exactly one representation of
15
+ * each half in the file.
14
16
  *
15
- * The emitted module is still passive, serializable, introspectable, hashable
16
- * and free of runtime callbacks (C-841). It contains three `const` bindings and
17
- * no executable statement; `discovery.model` and `discovery.translation`
18
- * *reference* their bindings rather than restating them, so there is exactly one
19
- * representation of each half in the file.
20
- *
21
- * **Three halves, and the seam between them is deliberate.** `model` is what
22
- * core consumes: canonicalized, digested into `modelHash`, and compiled into the
17
+ * **Three halves, and the seam between them is deliberate.** `model` is what core
18
+ * consumes: canonicalized, digested into `modelHash`, and compiled into the
23
19
  * Contract. `translation` is Prisma addressing — which key a `WhereUniqueInput`
24
20
  * answers to for each identifier. `referentialActions` is what deleting a parent
25
21
  * row does to its children, resolved. Both of the latter sit BESIDE the model
@@ -27,18 +23,18 @@ import type { PrismaProvider } from "../provider/provider-profile.js";
27
23
  * carries no semantic content, and inside the model it would change the Contract
28
24
  * identity and force every generated client to be regenerated for nothing; a
29
25
  * referential action is not part of the model at all, and inside it the action
30
- * would reach the compiled Contract and the ClientContract, which have no word
31
- * for one.
26
+ * would reach the compiled Contract and the ClientContract, which have no word for
27
+ * one.
32
28
  *
33
- * **Determinism (C-843).** The model text is the canonical JSON core already
34
- * uses for Contract identity, re-indented — not `JSON.stringify` over whatever
35
- * key order the readers happened to build. So two generate runs over one schema
36
- * emit byte-identical modules, and `modelHash` is a digest of exactly the bytes
37
- * the module carries, computed by the same `computeModelHash` the runtime
38
- * loader checks it with. One hash function, one canonicalization, both halves.
29
+ * **Determinism.** The model text is the canonical JSON core already uses for
30
+ * Contract identity, re-indented — not `JSON.stringify` over whatever key order
31
+ * the readers happened to build. So two generate runs over one schema emit
32
+ * byte-identical modules, and `modelHash` is a digest of exactly the bytes the
33
+ * module carries, computed by the same `computeModelHash` the runtime loader
34
+ * checks it with. One hash function, one canonicalization, both halves.
39
35
  */
40
36
  export type PrismaArtifactEmission = {
41
- /** e.g. `"7.10.0"`; only the major is fenced at load (C-846). */
37
+ /** e.g. `"7.10.0"`; only the major is fenced at load. */
42
38
  readonly prismaVersion: string;
43
39
  readonly provider: PrismaProvider;
44
40
  readonly model: PrismaAdapterModel;
@@ -46,5 +42,9 @@ export type PrismaArtifactEmission = {
46
42
  readonly translation: PrismaTranslationTable;
47
43
  /** Resolved referential actions per Resource — also beside `model`, also outside the hash. */
48
44
  readonly referentialActions: PrismaReferentialActionTable;
45
+ /** The generated client's models and field references — beside `model`, outside the hash. */
46
+ readonly client: PrismaClientShape;
47
+ /** What a create of each Resource cannot do without — beside `model`, outside the hash. */
48
+ readonly createRequirements: PrismaCreateRequirementTable;
49
49
  };
50
50
  export declare function emitDiscoveryArtifactModule(emission: PrismaArtifactEmission): Promise<string>;
@@ -1,11 +1,5 @@
1
1
  import { canonicalizeJson } from "@aventara/core";
2
2
  import { computeModelHash } from "../discovery/artifact.loader.js";
3
- /**
4
- * Biome formats and lints checked-in `.ts`, and this file's bytes are the
5
- * contract. The suppressions are emitted rather than configured so the artifact
6
- * stays correct wherever a consumer drops it, including a repository whose
7
- * formatter nobody here chose.
8
- */
9
3
  const BANNER = [
10
4
  "// biome-ignore-all format: generated output; these bytes are the artifact",
11
5
  "// biome-ignore-all lint: generated output",
@@ -51,6 +45,28 @@ const REFERENTIAL_ACTIONS_DOC = [
51
45
  " * referential action is not part of the model.",
52
46
  " */",
53
47
  ];
48
+ const CLIENT_DOC = [
49
+ "/**",
50
+ " * The Prisma client this artifact was generated against: each model it has a",
51
+ " * delegate for, and that model's field references (`prisma.<model>.fields`).",
52
+ " *",
53
+ " * `createPrismaAdapter` reads the same facts off the client it is given and",
54
+ " * refuses to start when they differ — the schema changed and the client was",
55
+ " * regenerated without this artifact. Beside the model and outside `modelHash`.",
56
+ " */",
57
+ ];
58
+ const CREATE_REQUIREMENTS_DOC = [
59
+ "/**",
60
+ " * What a create of each Resource cannot do without: a field with no default,",
61
+ " * or a required relation, which may instead be written through `orFields` —",
62
+ " * the scalar fields holding its reference — and which a nested create",
63
+ " * reached through its `inverse` gets from the parent.",
64
+ " *",
65
+ " * The framework checks every create against it after pipes and computed",
66
+ " * behaviours and answers a missing value as `A2004`/`V1000` at its path,",
67
+ " * before the database is reached. Beside the model and outside `modelHash`.",
68
+ " */",
69
+ ];
54
70
  const MODEL_DOC = [
55
71
  "/**",
56
72
  " * The Base Contract: what the schema and the generated client agreed on,",
@@ -66,13 +82,6 @@ const MODEL_DOC = [
66
82
  const DISCOVERY_DOC = [
67
83
  "/** Pass this to `createPrismaAdapter({ discovery })`. */",
68
84
  ];
69
- /**
70
- * Canonical JSON, re-indented as a TypeScript object literal.
71
- *
72
- * Both halves go through this one function, so C-843 holds over the whole file
73
- * rather than over the model alone: whatever key order the readers happened to
74
- * build, two runs over one schema emit byte-identical bytes.
75
- */
76
85
  function canonicalLiteral(value) {
77
86
  return JSON.stringify(JSON.parse(canonicalizeJson(value)), null, "\t");
78
87
  }
@@ -92,6 +101,12 @@ export async function emitDiscoveryArtifactModule(emission) {
92
101
  ...REFERENTIAL_ACTIONS_DOC,
93
102
  `export const referentialActions = ${canonicalLiteral(emission.referentialActions)} as const;`,
94
103
  "",
104
+ ...CLIENT_DOC,
105
+ `export const client = ${canonicalLiteral(emission.client)} as const;`,
106
+ "",
107
+ ...CREATE_REQUIREMENTS_DOC,
108
+ `export const createRequirements = ${canonicalLiteral(emission.createRequirements)} as const;`,
109
+ "",
95
110
  ...DISCOVERY_DOC,
96
111
  "export const discovery = {",
97
112
  `\tprismaVersion: ${JSON.stringify(emission.prismaVersion)},`,
@@ -100,6 +115,8 @@ export async function emitDiscoveryArtifactModule(emission) {
100
115
  "\tmodel,",
101
116
  "\ttranslation,",
102
117
  "\treferentialActions,",
118
+ "\tclient,",
119
+ "\tcreateRequirements,",
103
120
  "} as const;",
104
121
  ].join("\n")}\n`;
105
122
  }
@@ -1,17 +1,17 @@
1
1
  /**
2
- * What this adapter **declares** it supports, read from its own manifest — the
3
- * one place the fact is stated (R7: support is the adapter's to declare).
2
+ * What this adapter **declares** it supports, read from its own manifest — the one
3
+ * place the fact is stated.
4
4
  *
5
- * The generate step needs two of those facts at run time: the `prisma` range,
6
- * to decide whether a `prisma` CLI it found may run (D11), and the `typescript`
7
- * range, to name in the sentence that refuses a compiler with no classic API
8
- * (D4). Both are `peerDependencies` entries (D8). Reading them here rather than
9
- * restating them as literals is what keeps the refusal sentences, the version
10
- * check and the published manifest from drifting apart.
5
+ * The generate step needs two of those facts at run time: the `prisma` range, to
6
+ * decide whether a `prisma` CLI it found may run, and the `typescript` range, to
7
+ * name in the sentence that refuses a compiler with no classic API. Both are
8
+ * `peerDependencies` entries. Reading them here rather than restating them as
9
+ * literals is what keeps the refusal sentences, the version check and the
10
+ * published manifest from drifting apart.
11
11
  *
12
- * The manifest is two directories up from this module wherever it runs: `src/generate/`
13
- * under the specs, `dist/generate/` in a consumer's install, `.generate/generate/`
14
- * under this package's own `generate` script.
12
+ * The manifest is two directories up from this module wherever it runs:
13
+ * `src/generate/` under the specs, `dist/generate/` in a consumer's install,
14
+ * `.generate/generate/` under this package's own `generate` script.
15
15
  */
16
16
  export type DeclaredSupport = {
17
17
  /** `peerDependencies.prisma`: the Prisma CLI versions the readers were written against. */