@aventara/prisma7-adapter 0.1.0-pilot.0 → 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.
- package/README.md +39 -28
- 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 +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
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
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
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
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
|
-
*
|
|
60
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
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**.
|
|
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
|
-
* **
|
|
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
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
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
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
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
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* is
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
*
|
|
16
|
-
*
|
|
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
|
-
*
|
|
26
|
+
* would reach the compiled Contract and the ClientContract, which have no word for
|
|
27
|
+
* one.
|
|
32
28
|
*
|
|
33
|
-
* **Determinism
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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:
|
|
13
|
-
* under the specs, `dist/generate/` in a consumer's install,
|
|
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. */
|