@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
|
@@ -1,88 +1,9 @@
|
|
|
1
1
|
import { advertisedDirectives, } from "@aventara/core";
|
|
2
2
|
import { addressIdentifier } from "./identifier.addressing.js";
|
|
3
3
|
import { emitScalarValue } from "./scalar.conversion.js";
|
|
4
|
-
/**
|
|
5
|
-
* T-7's mutation half — one validated Aventara `data` in, one Prisma write
|
|
6
|
-
* argument out.
|
|
7
|
-
*
|
|
8
|
-
* # Pure, and the tables are the module
|
|
9
|
-
*
|
|
10
|
-
* No client, no database, no provider name. Three tables carry everything this
|
|
11
|
-
* module knows, each one TOTAL over a core union by `satisfies`, so a member
|
|
12
|
-
* added to core with no row here is a compile error rather than a silently
|
|
13
|
-
* dropped argument (C-855's emission half, C-837's translator table — the one
|
|
14
|
-
* `vocabulary.dictionary.ts` explicitly left to this task):
|
|
15
|
-
*
|
|
16
|
-
* - {@link SCALAR_DIRECTIVE_TOKENS} over `ScalarMutationDirective`;
|
|
17
|
-
* - {@link RELATION_DIRECTIVE_EMISSIONS} over `RelationMutationDirective`;
|
|
18
|
-
* - {@link MUTATION_DATA_SITES} over every standard operation key, which is
|
|
19
|
-
* C-818's enforcement point.
|
|
20
|
-
*
|
|
21
|
-
* C-834's own table is NOT one of them any more: the scalar value conversion was
|
|
22
|
-
* born here, and `where.translator.ts` and `execution/result.shaper.ts` need the
|
|
23
|
-
* same answers, so it moved to `scalar.conversion.ts` and all three read it
|
|
24
|
-
* (Task 10's `identifier.addressing.ts` is the precedent). This module's
|
|
25
|
-
* emission site is {@link emitValue}, which is now a call rather than a copy.
|
|
26
|
-
*
|
|
27
|
-
* Phase 7's `RELATION_MUTATION_RECORD_SITES` is the pattern
|
|
28
|
-
* (`packages/core/src/runtime/mutation-planner.ts`): `as const satisfies
|
|
29
|
-
* Readonly<Record<Union, …>>` keeps every cell a literal while making the union
|
|
30
|
-
* coverage a compile-time obligation. Two of core's passes are already keyed
|
|
31
|
-
* that way; this is the third, and the only one that names Prisma.
|
|
32
|
-
*
|
|
33
|
-
* # Refusing is part of honouring (the `where.translator.ts` stance)
|
|
34
|
-
*
|
|
35
|
-
* The adapter assumes validated input (C-845). It still refuses to EMIT a
|
|
36
|
-
* directive the discovered model does not advertise for that field and stage,
|
|
37
|
-
* and refuses one for which Prisma's nested input at that stage and cardinality
|
|
38
|
-
* has no key at all. Both refusals are C-855 seen from the emission side: an
|
|
39
|
-
* argument is honoured or it is not sent. A directive quietly dropped here would
|
|
40
|
-
* surface as a write that silently did less than it was asked to.
|
|
41
|
-
*
|
|
42
|
-
* # C-818 cannot reach the Contract, so it is enforced here
|
|
43
|
-
*
|
|
44
|
-
* `RelationFieldCapabilities` is one record per field with a single `create`
|
|
45
|
-
* member **[CORE, adapters/adapter-field.ts]**, so "relation create directives
|
|
46
|
-
* for `create.one` and none for `create.many`" is unrepresentable in the model;
|
|
47
|
-
* `model.discoverer.ts` composes the permissive `create.one` answer and says so.
|
|
48
|
-
* The restriction therefore lives in {@link MUTATION_DATA_SITES}, which is keyed
|
|
49
|
-
* by the operation the data belongs to. Measured at 7.10.0 on both engines
|
|
50
|
-
* (2026-10-02), a nested relation key in a bulk write is a hard failure, not a
|
|
51
|
-
* no-op:
|
|
52
|
-
*
|
|
53
|
-
* - `post.createMany({ data: [{ title, author: { connect: … } }] })` →
|
|
54
|
-
* *"Argument `authorId` is missing."*
|
|
55
|
-
* - `post.updateMany({ where, data: { category: { connect: … } } })` →
|
|
56
|
-
* *"Unknown argument `category`. Available options are marked with ?."*
|
|
57
|
-
*
|
|
58
|
-
* # Nothing in the caller's arguments is touched
|
|
59
|
-
*
|
|
60
|
-
* Every object and array returned is new. The input is read, never written: a
|
|
61
|
-
* translator that mutated its argument would corrupt the second attempt of a
|
|
62
|
-
* retried operation, which is the one place the bug would not show up in a
|
|
63
|
-
* single-shot test.
|
|
64
|
-
*/
|
|
65
|
-
/** A mutation the model, the stage or the active operation does not license. */
|
|
66
4
|
export class PrismaMutationTranslationError extends Error {
|
|
67
5
|
name = "PrismaMutationTranslationError";
|
|
68
6
|
}
|
|
69
|
-
/* ------------------------------------------------------------------ *
|
|
70
|
-
* C-837 — the scalar directive table *
|
|
71
|
-
* ------------------------------------------------------------------ */
|
|
72
|
-
/**
|
|
73
|
-
* The Prisma operand key for each of core's scalar mutation directives.
|
|
74
|
-
*
|
|
75
|
-
* The inverse of discovery's `PRISMA_SCALAR_DIRECTIVES` rename table, written
|
|
76
|
-
* here rather than imported from it for the reason `where.translator.ts` gives:
|
|
77
|
-
* that table is generate-time and `vocabulary.dictionary.ts` is excluded from
|
|
78
|
-
* the published build, while this module is runtime. `mutation.translator.spec.ts`
|
|
79
|
-
* round-trips every emitted token back through the dictionary, so the two cannot
|
|
80
|
-
* drift.
|
|
81
|
-
*
|
|
82
|
-
* Prisma's `set` has no row because it is not a directive: plain assignment is
|
|
83
|
-
* how core spells it, and the bare value is what this module emits — wrapping it
|
|
84
|
-
* in `{ set: … }` would add a token the request never carried.
|
|
85
|
-
*/
|
|
86
7
|
const SCALAR_DIRECTIVE_TOKENS = {
|
|
87
8
|
$increment: "increment",
|
|
88
9
|
$decrement: "decrement",
|
|
@@ -91,53 +12,6 @@ const SCALAR_DIRECTIVE_TOKENS = {
|
|
|
91
12
|
$push: "push",
|
|
92
13
|
$unset: "unset",
|
|
93
14
|
};
|
|
94
|
-
/**
|
|
95
|
-
* §18.5's normalization table, forward: one Aventara directive → one Prisma
|
|
96
|
-
* nested-write key and operand, per stage and cardinality.
|
|
97
|
-
*
|
|
98
|
-
* Total over `RelationMutationDirective`, so a ninth directive fails to compile
|
|
99
|
-
* — the same obligation core's two tables carry. Every cell below was read off
|
|
100
|
-
* the generated types and then RUN on both engines (2026-10-02); the three that
|
|
101
|
-
* matter are not key-for-key renames and would each have been wrong the obvious
|
|
102
|
-
* way.
|
|
103
|
-
*
|
|
104
|
-
* **To-many `$update` emits `updateMany`, not `update`.** Core validates a
|
|
105
|
-
* to-many `$update` entry's `where` with `validateWhere` — a GENERAL filter, not
|
|
106
|
-
* a unique selector **[CORE, operations/operation-validator-core.ts]** — and
|
|
107
|
-
* §18.5 says so in as many words: *"Prisma nested update/updateMany normalize to
|
|
108
|
-
* $update (general where → update every relation match)"*. Prisma's nested
|
|
109
|
-
* `update` takes `<T>UpdateWithWhereUnique…Input`, which refuses a general
|
|
110
|
-
* filter: *"Argument `where` of type PostWhereUniqueInput needs at least one of
|
|
111
|
-
* `id` arguments."* `updateMany` takes `{ where: <T>ScalarWhereInput, data }`,
|
|
112
|
-
* which is the shape core actually hands over.
|
|
113
|
-
*
|
|
114
|
-
* **To-many `$delete` emits `deleteMany`, for the same reason**, and its operand
|
|
115
|
-
* is the where UNWRAPPED: `deleteMany?: PostScalarWhereInput`. The singular
|
|
116
|
-
* `delete` is a `<T>WhereUniqueInput[]` and refuses the entry: *"Argument
|
|
117
|
-
* `delete`: Invalid value provided. Expected PostWhereUniqueInput[], provided
|
|
118
|
-
* Object."*
|
|
119
|
-
*
|
|
120
|
-
* **A `<T>ScalarWhereInput` carries scalars only**, so the two bulk forms refuse
|
|
121
|
-
* a relation predicate rather than emit one Prisma will reject: *"Unknown
|
|
122
|
-
* argument `category`. Did you mean `categoryId`?"* — and `updateMany`'s `data`
|
|
123
|
-
* is an `UpdateMany` input, so it admits no nested relation write either:
|
|
124
|
-
* *"Unknown argument `category`."*
|
|
125
|
-
*
|
|
126
|
-
* **The create stage admits four directives, not eight.**
|
|
127
|
-
* `<T>Create…NestedInput` carries `create`, `connectOrCreate`, `createMany` and
|
|
128
|
-
* `connect` and nothing else, on both cardinalities — *"Unknown argument
|
|
129
|
-
* `disconnect`. Did you mean `connect`?"*, *"Unknown argument `set`."* A to-one
|
|
130
|
-
* edge has no `set` at EITHER stage: *"Unknown argument `set`. Did you mean
|
|
131
|
-
* `upsert`?"* The `create: null` cells are therefore measured facts.
|
|
132
|
-
*
|
|
133
|
-
* They were also where discovery over-advertised: `PrismaRelationSurface` carried
|
|
134
|
-
* ONE mutation list, read off the UPDATE-stage nested input and reused for both
|
|
135
|
-
* stages, so the model offered `$set`/`$disconnect`/`$update`/`$delete`/`$upsert`
|
|
136
|
-
* at the create stage, which Prisma has no key for. This table refused them
|
|
137
|
-
* loudly — which is C-855's whole point — and the deriver now reads each stage
|
|
138
|
-
* from its own nested input (`createMutations` / `updateMutations`), so the
|
|
139
|
-
* Contract no longer advertises what these cells refuse.
|
|
140
|
-
*/
|
|
141
15
|
const RELATION_DIRECTIVE_EMISSIONS = {
|
|
142
16
|
$create: {
|
|
143
17
|
create: {
|
|
@@ -202,22 +76,6 @@ const RELATION_DIRECTIVE_EMISSIONS = {
|
|
|
202
76
|
},
|
|
203
77
|
},
|
|
204
78
|
};
|
|
205
|
-
/**
|
|
206
|
-
* Every standard operation, and the mutation data it carries — `null` for the
|
|
207
|
-
* ten that carry none.
|
|
208
|
-
*
|
|
209
|
-
* Total over the whole operation set rather than over the mutating subset, so a
|
|
210
|
-
* variant added to `ResourceOperations` fails to compile here instead of
|
|
211
|
-
* reaching the `forbidden`/`nested` question by default. The read and delete
|
|
212
|
-
* families are `null` for the same reason the dictionary spells a missing
|
|
213
|
-
* Aventara word `null`: *"no data"* is an answer, and an absent row is not.
|
|
214
|
-
*
|
|
215
|
-
* `create.many`, `create.count`, `update.many` and `update.count` are the
|
|
216
|
-
* `forbidden` four: they become `createMany`/`updateMany`, whose inputs carry
|
|
217
|
-
* scalars and foreign keys only. `update.first` is `nested` because the variant
|
|
218
|
-
* is emulated as a read followed by a singular `update` (T-7.1), and that write
|
|
219
|
-
* step is an `<M>UpdateInput`.
|
|
220
|
-
*/
|
|
221
79
|
const MUTATION_DATA_SITES = {
|
|
222
80
|
"find.first": null,
|
|
223
81
|
"find.unique": null,
|
|
@@ -236,14 +94,6 @@ const MUTATION_DATA_SITES = {
|
|
|
236
94
|
"delete.count": null,
|
|
237
95
|
"upsert.unique": { stages: ["create", "update"], relations: "nested" },
|
|
238
96
|
};
|
|
239
|
-
/**
|
|
240
|
-
* Translates one validated Aventara mutation `data` into the Prisma write
|
|
241
|
-
* argument for the named operation and stage.
|
|
242
|
-
*
|
|
243
|
-
* @throws {PrismaMutationTranslationError} rather than emitting a directive the
|
|
244
|
-
* model does not advertise, one Prisma has no key for at this stage and
|
|
245
|
-
* cardinality, or a nested relation write inside a bulk argument (C-818).
|
|
246
|
-
*/
|
|
247
97
|
export function translateMutationData(input, dependencies) {
|
|
248
98
|
const key = `${input.operation.family}.${input.operation.variant}`;
|
|
249
99
|
if (!isOperationKey(key)) {
|
|
@@ -281,9 +131,7 @@ function translateRecord(pass, resourceKey, data, stage, path) {
|
|
|
281
131
|
continue;
|
|
282
132
|
}
|
|
283
133
|
if (pass.relations === "forbidden") {
|
|
284
|
-
|
|
285
|
-
// which variant this data belongs to.
|
|
286
|
-
throw new PrismaMutationTranslationError(`${path}.${key}: a bulk write carries no nested relation directive; Prisma's createMany/updateMany inputs admit scalars and foreign keys only (C-818)`);
|
|
134
|
+
throw new PrismaMutationTranslationError(`${path}.${key}: a bulk write carries no nested relation directive; Prisma's createMany/updateMany inputs admit scalars and foreign keys only`);
|
|
287
135
|
}
|
|
288
136
|
emitted[key] = translateRelationWrite(pass, field, value, stage, `${path}.${key}`);
|
|
289
137
|
}
|
|
@@ -296,9 +144,6 @@ function translateScalarWrite(pass, field, value, stage, path) {
|
|
|
296
144
|
}
|
|
297
145
|
const directives = directiveKeysOf(value, path);
|
|
298
146
|
if (directives === undefined) {
|
|
299
|
-
// Core's own rule: a value that is not a directive object IS plain
|
|
300
|
-
// assignment, and Prisma spells that the same way. Wrapping it in
|
|
301
|
-
// `{ set: … }` would emit a token the request never carried.
|
|
302
147
|
return emitValue(pass, field, value, path);
|
|
303
148
|
}
|
|
304
149
|
const operand = {};
|
|
@@ -313,16 +158,6 @@ function translateScalarWrite(pass, field, value, stage, path) {
|
|
|
313
158
|
}
|
|
314
159
|
return operand;
|
|
315
160
|
}
|
|
316
|
-
/**
|
|
317
|
-
* The directive keys of a scalar write, or `undefined` when the value is a
|
|
318
|
-
* value.
|
|
319
|
-
*
|
|
320
|
-
* The discriminant is the `$` prefix, which is core's own
|
|
321
|
-
* (`validateScalarMutation` rejects a mixed object with `V1001` *"Scalar
|
|
322
|
-
* mutation action objects may contain only $-prefixed directive keys"*). It is
|
|
323
|
-
* mirrored rather than improved on: a `json` column's value is a plain object
|
|
324
|
-
* too, and any second discriminant would disagree with core's on some input.
|
|
325
|
-
*/
|
|
326
161
|
function directiveKeysOf(value, path) {
|
|
327
162
|
if (!isRecord(value)) {
|
|
328
163
|
return undefined;
|
|
@@ -337,13 +172,6 @@ function directiveKeysOf(value, path) {
|
|
|
337
172
|
}
|
|
338
173
|
return directives;
|
|
339
174
|
}
|
|
340
|
-
/**
|
|
341
|
-
* C-834's emission site: one Aventara runtime value, one Prisma operand.
|
|
342
|
-
*
|
|
343
|
-
* The conversion itself belongs to `scalar.conversion.ts`, which owns both
|
|
344
|
-
* directions for every scalar; this module owns only *where* a value sits in a
|
|
345
|
-
* write argument.
|
|
346
|
-
*/
|
|
347
175
|
function emitValue(pass, field, value, path) {
|
|
348
176
|
return emitScalarValue(pass.model, field, value, path);
|
|
349
177
|
}
|
|
@@ -374,11 +202,6 @@ function translateRelationWrite(pass, field, value, stage, path) {
|
|
|
374
202
|
}
|
|
375
203
|
return emitted;
|
|
376
204
|
}
|
|
377
|
-
/**
|
|
378
|
-
* One directive's operand, with its ARITY preserved: core admits a single entry
|
|
379
|
-
* or an array on a to-many edge, Prisma admits both, and collapsing or
|
|
380
|
-
* expanding one would change the shape the request carried.
|
|
381
|
-
*/
|
|
382
205
|
function emitRelationOperand(pass, target, cardinality, form, operand, path) {
|
|
383
206
|
if (cardinality === "many" && Array.isArray(operand)) {
|
|
384
207
|
return operand.map((entry, index) => emitRelationEntry(pass, target, form, entry, `${path}[${index}]`));
|
|
@@ -418,8 +241,6 @@ function emitRelationEntry(pass, target, form, entry, path) {
|
|
|
418
241
|
throw new PrismaMutationTranslationError(`${path}: a to-one $delete is true or an optional where object`);
|
|
419
242
|
}
|
|
420
243
|
if (!Object.hasOwn(entry, "where")) {
|
|
421
|
-
// `{}` is "delete the related row", which Prisma spells `true`. The
|
|
422
|
-
// empty object is not a Prisma where: `delete: {}` would match nothing.
|
|
423
244
|
return true;
|
|
424
245
|
}
|
|
425
246
|
return pass.dependencies.translateWhere(target, entry.where, `${path}.where`);
|
|
@@ -436,14 +257,11 @@ function emitRelationEntry(pass, target, form, entry, path) {
|
|
|
436
257
|
const { where, data } = requireMembers(entry, ["where", "data"], path);
|
|
437
258
|
return {
|
|
438
259
|
where: scalarWhere(pass, target, where, `${path}.where`),
|
|
439
|
-
// `<T>UpdateManyWithWhere…Input.data` is an UpdateMany input: scalars
|
|
440
|
-
// and foreign keys only, exactly as a top-level `updateMany`.
|
|
441
260
|
data: translateRecord(bulk(pass), target, data, "update", `${path}.data`),
|
|
442
261
|
};
|
|
443
262
|
}
|
|
444
263
|
case "to-many-delete-many": {
|
|
445
264
|
const { where } = requireMembers(entry, ["where"], path);
|
|
446
|
-
// `deleteMany` takes the scalar where itself, not `{ where }`.
|
|
447
265
|
return scalarWhere(pass, target, where, `${path}.where`);
|
|
448
266
|
}
|
|
449
267
|
case "to-many-upsert": {
|
|
@@ -464,27 +282,14 @@ function bulk(pass) {
|
|
|
464
282
|
? pass
|
|
465
283
|
: { ...pass, relations: "forbidden" };
|
|
466
284
|
}
|
|
467
|
-
/** A to-one `$update`/`$upsert` `where` is optional, and absent is not `undefined`. */
|
|
468
285
|
function optionalWhere(pass, target, entry, path) {
|
|
469
286
|
if (!isRecord(entry) || !Object.hasOwn(entry, "where")) {
|
|
470
287
|
return {};
|
|
471
288
|
}
|
|
472
|
-
// Prisma's to-one `update`/`upsert` `where` is a general `<T>WhereInput`, so
|
|
473
|
-
// core's flat unique selector needs no addressing here — it is already a valid
|
|
474
|
-
// filter. Measured on both engines (2026-10-02).
|
|
475
289
|
return {
|
|
476
290
|
where: pass.dependencies.translateWhere(target, entry.where, `${path}.where`),
|
|
477
291
|
};
|
|
478
292
|
}
|
|
479
|
-
/**
|
|
480
|
-
* A `<T>ScalarWhereInput`: the nested bulk forms' filter, which carries scalar
|
|
481
|
-
* columns only.
|
|
482
|
-
*
|
|
483
|
-
* Core's `validateWhere` admits a relation predicate here, so this is a real
|
|
484
|
-
* narrowing and it refuses rather than emits: Prisma answers *"Unknown argument
|
|
485
|
-
* `category`. Did you mean `categoryId`?"* on both engines. Reported as a
|
|
486
|
-
* C-855 gap in the advertised surface rather than silently dropped.
|
|
487
|
-
*/
|
|
488
293
|
function scalarWhere(pass, target, where, path) {
|
|
489
294
|
requireScalarPredicate(pass, target, where, path);
|
|
490
295
|
return pass.dependencies.translateWhere(target, where, path);
|
|
@@ -513,15 +318,6 @@ function requireScalarPredicate(pass, target, where, path) {
|
|
|
513
318
|
}
|
|
514
319
|
}
|
|
515
320
|
}
|
|
516
|
-
/**
|
|
517
|
-
* Core's FLAT unique selector → Prisma's `<T>WhereUniqueInput`, for a NESTED
|
|
518
|
-
* `connect`/`disconnect`/`set`/`update`/`upsert` target.
|
|
519
|
-
*
|
|
520
|
-
* The addressing rule itself lives in `identifier.addressing.ts`, which Task 10
|
|
521
|
-
* extracted when the top-level where-unique made it the third copy. What stays
|
|
522
|
-
* here is this site's two decisions: the match is **exact**, and each member's
|
|
523
|
-
* value goes out through C-834's scalar emission (`scalar.conversion.ts`).
|
|
524
|
-
*/
|
|
525
321
|
function addressUnique(pass, target, selector, path) {
|
|
526
322
|
if (!isRecord(selector)) {
|
|
527
323
|
throw new PrismaMutationTranslationError(`${path} must be a unique identifier object`);
|
|
@@ -534,10 +330,6 @@ function addressUnique(pass, target, selector, path) {
|
|
|
534
330
|
if (addresses === undefined) {
|
|
535
331
|
throw new PrismaMutationTranslationError(`${path}: Resource "${target}" has no Prisma addressing in the translation table`);
|
|
536
332
|
}
|
|
537
|
-
// `"exact"`: core's unique selector carries one identifier and nothing else,
|
|
538
|
-
// so a spare key means it is not that identifier. Each member goes out through
|
|
539
|
-
// C-834's scalar emission, which is why the conversion is passed in rather
|
|
540
|
-
// than the table imported by the addressing module.
|
|
541
333
|
const addressed = addressIdentifier({
|
|
542
334
|
addresses,
|
|
543
335
|
values: selector,
|
|
@@ -2,14 +2,10 @@ import type { AdapterModel, AdapterOperation } from "@aventara/core";
|
|
|
2
2
|
import type { PrismaResourceTranslation } from "../discovery/discovery-artifact.interface.js";
|
|
3
3
|
import { type PrismaProjectionPlan } from "./projection.translator.js";
|
|
4
4
|
/**
|
|
5
|
-
* T-7 — **one validated Aventara operation → one provider call description**,
|
|
6
|
-
* and the module that makes the whole sixteen-variant grammar assertable as pure
|
|
7
|
-
* data with no database in sight.
|
|
8
|
-
*
|
|
9
5
|
* Splitting translation from execution is the plan's single largest testability
|
|
10
|
-
* decision
|
|
11
|
-
*
|
|
12
|
-
*
|
|
6
|
+
* decision: everything below is a pure function of the discovered model, the
|
|
7
|
+
* generate-time translation table and the arguments core validated. The database
|
|
8
|
+
* is needed only for the *other* half, `execution/operation.executor.ts`.
|
|
13
9
|
*
|
|
14
10
|
* # The dispatch table is the module
|
|
15
11
|
*
|
|
@@ -21,43 +17,32 @@ import { type PrismaProjectionPlan } from "./projection.translator.js";
|
|
|
21
17
|
*
|
|
22
18
|
* # The negative half of the table is the load-bearing half
|
|
23
19
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* permanently. Aventara is not an ORM and reaches the database only through the
|
|
30
|
-
* adapter's ORM; a raw statement would forfeit the ORM's own injection
|
|
31
|
-
* protection and oblige Aventara to build its own. Because the exclusion lives in
|
|
32
|
-
* the *type*, a raw call is not representable in a {@link PrismaExecutionPlan} at
|
|
33
|
-
* all: the rule is enforced by the compiler, not by a reviewer's memory.
|
|
20
|
+
* Aventara is not an ORM and reaches the database only through the adapter's ORM;
|
|
21
|
+
* a raw statement would forfeit the ORM's own injection protection and oblige
|
|
22
|
+
* Aventara to build its own. Because the exclusion lives in the *type*, a raw call
|
|
23
|
+
* is not representable in a {@link PrismaExecutionPlan} at all: the rule is
|
|
24
|
+
* enforced by the compiler, not by a reviewer's memory.
|
|
34
25
|
* `operation.translator.spec.ts` asserts the same thing from the value side, as
|
|
35
26
|
* the belt to that type's braces.
|
|
36
27
|
*
|
|
37
28
|
* # `delete.many` is a hole in the table, and the hole is the decision
|
|
38
29
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* omission is a fact a reader can find rather than an absence they have to infer.
|
|
30
|
+
* `operations.deriver.ts` therefore never advertises it, so core rejects the
|
|
31
|
+
* request with `A2002 UNKNOWN_OPERATION` before any adapter call. The table still
|
|
32
|
+
* carries the key — it is total over core's grammar — with an `"unavailable"`
|
|
33
|
+
* entry, so the omission is a fact a reader can find rather than an absence they
|
|
34
|
+
* have to infer.
|
|
45
35
|
*
|
|
46
36
|
* # First-style variants, and the write route
|
|
47
37
|
*
|
|
48
|
-
* `update.first` and `delete.first` are **framework-manufactured
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
38
|
+
* `update.first` and `delete.first` are **framework-manufactured**: a cross-ORM
|
|
39
|
+
* survey found the four-part shape — general filter, ordering decides "first",
|
|
40
|
+
* effect limited to one row, the affected row is returned, all in one call —
|
|
41
|
+
* essentially absent across ORM design, and Prisma 7.10.0 has no
|
|
52
42
|
* `updateFirst`/`deleteFirst` **[PKG]**. So the plan is an ordinary read then
|
|
53
43
|
* write, and its two steps are {@link PrismaExecutionPlan}'s `"emulated"` arm.
|
|
54
44
|
*
|
|
55
|
-
*
|
|
56
|
-
* measured it rather than inheriting it.** T-7.1.1 recorded two routes and §14e
|
|
57
|
-
* reserved the choice; §1.0 forbids an implementation task from blocking on an
|
|
58
|
-
* owner decision, so Task 10 ran both against the real 7.10.0 client on **both**
|
|
59
|
-
* engines. Every row came out for Route A, and two of them came out *against*
|
|
60
|
-
* Route B:
|
|
45
|
+
* Every row came out for Route A, and two of them came out *against* Route B:
|
|
61
46
|
*
|
|
62
47
|
* ```text
|
|
63
48
|
* measured 2026-10-02, @prisma/client 7.10.0, SQLite + PostgreSQL 18, identical
|
|
@@ -76,13 +61,8 @@ import { type PrismaProjectionPlan } from "./projection.translator.js";
|
|
|
76
61
|
* B deleteMany({ where: { id, AND: […] } }) -> { count: 1 } — carries no row at all
|
|
77
62
|
* ```
|
|
78
63
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* pre-write projection read and no identifier strip. Route B cannot serve a
|
|
82
|
-
* caller projection that reaches a to-many relation or `$count` at all, and its
|
|
83
|
-
* `delete.first` must read before it writes. The route is **one entry** in the
|
|
84
|
-
* table below — {@link FIRST_VARIANT_WRITE_ROUTE} — which is the seam D-801/§9
|
|
85
|
-
* put here precisely so §14e's ratification (or reversal) is one edit.
|
|
64
|
+
* Route B cannot serve a caller projection that reaches a to-many relation or
|
|
65
|
+
* `$count` at all, and its `delete.first` must read before it writes.
|
|
86
66
|
*
|
|
87
67
|
* # The write step of an emulated plan is a template, and says so
|
|
88
68
|
*
|
|
@@ -90,17 +70,15 @@ import { type PrismaProjectionPlan } from "./projection.translator.js";
|
|
|
90
70
|
* step is **not**, by construction: its `where` carries the caller's re-asserted
|
|
91
71
|
* predicate under `AND` and the identifier is added by
|
|
92
72
|
* `execution/first-variant.emulator.ts` from the row the read returned. That is
|
|
93
|
-
* what "emulated" means — the second statement is not knowable until the first
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* # Nothing is emitted that the framework did not send (C-855, D-830)
|
|
73
|
+
* what "emulated" means — the second statement is not knowable until the first has
|
|
74
|
+
* run — and it is why the emulator, not this module, issues it.
|
|
97
75
|
*
|
|
98
76
|
* Every key below is added only when the corresponding Aventara argument is
|
|
99
77
|
* **present**. An absent argument and an `undefined` one are different arguments
|
|
100
78
|
* to a provider, so `"take" in args` is never made true by accident. An argument
|
|
101
79
|
* this module does not recognise raises rather than being dropped: an argument
|
|
102
|
-
* that typechecks and is silently ignored is worse than an absent one, because
|
|
103
|
-
*
|
|
80
|
+
* that typechecks and is silently ignored is worse than an absent one, because the
|
|
81
|
+
* absent one fails loudly at the caller.
|
|
104
82
|
*/
|
|
105
83
|
/** The `ModelAction` subset this adapter uses. See the header for the exclusions. */
|
|
106
84
|
export type PrismaAction = "findUnique" | "findFirst" | "findMany" | "count" | "create" | "createMany" | "createManyAndReturn" | "update" | "updateMany" | "updateManyAndReturn" | "delete" | "deleteMany" | "upsert";
|
|
@@ -115,7 +93,7 @@ export type PrismaCall = {
|
|
|
115
93
|
readonly action: PrismaAction;
|
|
116
94
|
readonly args: Readonly<Record<string, unknown>>;
|
|
117
95
|
};
|
|
118
|
-
/** A variant is either one call, or an emulated sequence
|
|
96
|
+
/** A variant is either one call, or an emulated sequence. */
|
|
119
97
|
export type PrismaExecutionPlan = {
|
|
120
98
|
readonly kind: "single";
|
|
121
99
|
readonly call: PrismaCall;
|
|
@@ -125,11 +103,11 @@ export type PrismaExecutionPlan = {
|
|
|
125
103
|
readonly atomicity: "outer-tx" | "own-tx";
|
|
126
104
|
};
|
|
127
105
|
/**
|
|
128
|
-
* What the variant owes its caller, and therefore which shaper runs
|
|
106
|
+
* What the variant owes its caller, and therefore which shaper runs.
|
|
129
107
|
*
|
|
130
|
-
* It is a property of the *variant*, not of the provider answer, so it belongs
|
|
131
|
-
*
|
|
132
|
-
*
|
|
108
|
+
* It is a property of the *variant*, not of the provider answer, so it belongs in
|
|
109
|
+
* the dispatch table beside the action rather than in a second table the executor
|
|
110
|
+
* would have to keep in step.
|
|
133
111
|
*/
|
|
134
112
|
export type PrismaResultKind = "row" | "rows" | "count";
|
|
135
113
|
export type PrismaVariantDispatch = {
|
|
@@ -145,7 +123,7 @@ export type PrismaVariantDispatch = {
|
|
|
145
123
|
readonly kind: "unavailable";
|
|
146
124
|
readonly reason: string;
|
|
147
125
|
};
|
|
148
|
-
/** The sixteen-variant dispatch table
|
|
126
|
+
/** The sixteen-variant dispatch table, total over core's grammar. */
|
|
149
127
|
export declare const PRISMA_VARIANT_DISPATCH: {
|
|
150
128
|
readonly "find.first": {
|
|
151
129
|
readonly kind: "single";
|
|
@@ -216,7 +194,7 @@ export declare const PRISMA_VARIANT_DISPATCH: {
|
|
|
216
194
|
};
|
|
217
195
|
readonly "delete.many": {
|
|
218
196
|
readonly kind: "unavailable";
|
|
219
|
-
readonly reason: "@prisma/client 7.10.0 has no deleteManyAndReturn, so delete.many is omitted rather than emulated
|
|
197
|
+
readonly reason: "@prisma/client 7.10.0 has no deleteManyAndReturn, so delete.many is omitted rather than emulated; delete.count deletes a filtered set";
|
|
220
198
|
};
|
|
221
199
|
readonly "delete.count": {
|
|
222
200
|
readonly kind: "single";
|
|
@@ -237,13 +215,8 @@ export declare class PrismaOperationTranslationError extends Error {
|
|
|
237
215
|
* The translation of one operation: the plan, plus the projection plan the result
|
|
238
216
|
* shaper needs.
|
|
239
217
|
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
* {@link PrismaProjectionPlan} — the reconstruction of where every `$count`
|
|
243
|
-
* envelope sits — is produced *here*, while translating the projection, and
|
|
244
|
-
* cannot be recovered from the emitted Prisma arguments afterwards (Prisma's
|
|
245
|
-
* `_count` is a sibling key, not a position). Returning it beside the plan is
|
|
246
|
-
* what keeps `result.shaper.ts` free of a second, drifting reconstruction.
|
|
218
|
+
* Returning it beside the plan is what keeps `result.shaper.ts` free of a second,
|
|
219
|
+
* drifting reconstruction.
|
|
247
220
|
*/
|
|
248
221
|
export type PrismaOperationTranslation = {
|
|
249
222
|
readonly plan: PrismaExecutionPlan;
|
|
@@ -251,9 +224,9 @@ export type PrismaOperationTranslation = {
|
|
|
251
224
|
/**
|
|
252
225
|
* The caller's `where`, translated, or `undefined` when they sent none.
|
|
253
226
|
*
|
|
254
|
-
* Recorded for the emulator, which re-asserts it on the write
|
|
255
|
-
*
|
|
256
|
-
*
|
|
227
|
+
* Recorded for the emulator, which re-asserts it on the write. It is the same
|
|
228
|
+
* value the read step carries, by identity, so the two halves of the manufacture
|
|
229
|
+
* cannot assert different predicates.
|
|
257
230
|
*/
|
|
258
231
|
readonly predicate: Readonly<Record<string, unknown>> | undefined;
|
|
259
232
|
};
|
|
@@ -3,32 +3,16 @@ import { translateMutationData } from "./mutation.translator.js";
|
|
|
3
3
|
import { translateOrderBy, translatePagination, } from "./order-pagination.translator.js";
|
|
4
4
|
import { translateProjection, } from "./projection.translator.js";
|
|
5
5
|
import { translateWhere } from "./where.translator.js";
|
|
6
|
-
/**
|
|
7
|
-
* The chosen first-variant write route, in one place (§14e, T-7.1.1).
|
|
8
|
-
*
|
|
9
|
-
* Route A: the read selects the identifier, and the write is Prisma's own
|
|
10
|
-
* `update`/`delete` narrowed to that identifier with the caller's predicate
|
|
11
|
-
* re-asserted. Both are projection-carrying — their result maps through
|
|
12
|
-
* `GetFindResult`, the same helper `findFirst` uses **[PKG,
|
|
13
|
-
* `runtime/client.d.ts:1352`, `:1356`, `:1144`]** — and Task 10 measured both on
|
|
14
|
-
* both engines (see the header). Switching to Route B is an edit to these two
|
|
15
|
-
* cells and to `first-variant.emulator.ts`'s lost-row detection, and nothing else.
|
|
16
|
-
*/
|
|
17
6
|
const FIRST_VARIANT_WRITE_ROUTE = {
|
|
18
7
|
update: "update",
|
|
19
8
|
delete: "delete",
|
|
20
9
|
};
|
|
21
|
-
/** The sixteen-variant dispatch table (T-7, §18.4), total over core's grammar. */
|
|
22
10
|
export const PRISMA_VARIANT_DISPATCH = {
|
|
23
11
|
"find.first": { kind: "single", action: "findFirst", result: "row" },
|
|
24
|
-
// RC-2: `findUnique` and a returned `null`, never `findUniqueOrThrow` — core's
|
|
25
|
-
// `OUTCOME_TABLE` owns the absence decision and maps the `null` to `A2003`.
|
|
26
12
|
"find.unique": { kind: "single", action: "findUnique", result: "row" },
|
|
27
13
|
"find.many": { kind: "single", action: "findMany", result: "rows" },
|
|
28
14
|
"find.count": { kind: "single", action: "count", result: "count" },
|
|
29
15
|
"create.one": { kind: "single", action: "create", result: "row" },
|
|
30
|
-
// C-817: the row-returning bulk write is what `create.many` owes its caller;
|
|
31
|
-
// `createMany` answers `{ count }` and is `create.count`'s call instead.
|
|
32
16
|
"create.many": {
|
|
33
17
|
kind: "single",
|
|
34
18
|
action: "createManyAndReturn",
|
|
@@ -57,22 +41,14 @@ export const PRISMA_VARIANT_DISPATCH = {
|
|
|
57
41
|
"delete.unique": { kind: "single", action: "delete", result: "row" },
|
|
58
42
|
"delete.many": {
|
|
59
43
|
kind: "unavailable",
|
|
60
|
-
reason: "@prisma/client 7.10.0 has no deleteManyAndReturn, so delete.many is omitted rather than emulated
|
|
44
|
+
reason: "@prisma/client 7.10.0 has no deleteManyAndReturn, so delete.many is omitted rather than emulated; delete.count deletes a filtered set",
|
|
61
45
|
},
|
|
62
46
|
"delete.count": { kind: "single", action: "deleteMany", result: "count" },
|
|
63
47
|
"upsert.unique": { kind: "single", action: "upsert", result: "row" },
|
|
64
48
|
};
|
|
65
|
-
/** An operation the adapter will not translate. */
|
|
66
49
|
export class PrismaOperationTranslationError extends Error {
|
|
67
50
|
name = "PrismaOperationTranslationError";
|
|
68
51
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Translates one validated Aventara operation.
|
|
71
|
-
*
|
|
72
|
-
* @throws {PrismaOperationTranslationError} for a variant the adapter advertises
|
|
73
|
-
* no call for, an argument it does not recognise, or a unique selector that
|
|
74
|
-
* addresses no row — never a silently narrowed or widened call.
|
|
75
|
-
*/
|
|
76
52
|
export function translateOperation(input) {
|
|
77
53
|
const key = `${input.operation.family}.${input.operation.variant}`;
|
|
78
54
|
const dispatch = Object.hasOwn(PRISMA_VARIANT_DISPATCH, key)
|
|
@@ -125,9 +101,6 @@ export function translateOperation(input) {
|
|
|
125
101
|
args: {
|
|
126
102
|
...(predicate === undefined ? {} : { where: predicate }),
|
|
127
103
|
...orderByArgument(context),
|
|
128
|
-
// The read selects the identifier and nothing else: under Route A
|
|
129
|
-
// the WRITE carries the caller's projection, so reading it twice
|
|
130
|
-
// would be a statement the contract does not need.
|
|
131
104
|
select: identifierSelect(context),
|
|
132
105
|
},
|
|
133
106
|
},
|
|
@@ -135,8 +108,6 @@ export function translateOperation(input) {
|
|
|
135
108
|
model: context.resource,
|
|
136
109
|
action: dispatch.write,
|
|
137
110
|
args: {
|
|
138
|
-
// A template: the identifier is added by the emulator from the row
|
|
139
|
-
// the read returned. `AND` carries the re-assertion (C-856, D-829).
|
|
140
111
|
...(predicate === undefined ? {} : { where: { AND: predicate } }),
|
|
141
112
|
...(context.operation.family === "update"
|
|
142
113
|
? { data: mutationData(context, "update", "data") }
|
|
@@ -151,12 +122,6 @@ export function translateOperation(input) {
|
|
|
151
122
|
predicate,
|
|
152
123
|
};
|
|
153
124
|
}
|
|
154
|
-
/**
|
|
155
|
-
* The four variants whose `where` is core's FLAT unique selector rather than a
|
|
156
|
-
* general predicate, read from core's own rule: `hasGeneralRootWhere` is false
|
|
157
|
-
* for exactly `find.unique`, `update.unique`, `delete.unique` and `upsert.unique`
|
|
158
|
-
* **[CORE, operations/operation-validator.ts]**.
|
|
159
|
-
*/
|
|
160
125
|
function isUniqueSelectorVariant(key) {
|
|
161
126
|
return (key === "find.unique" ||
|
|
162
127
|
key === "update.unique" ||
|
|
@@ -224,7 +189,6 @@ function singleCallArguments(context, key, predicate, projectionArgs) {
|
|
|
224
189
|
throw new PrismaOperationTranslationError(`"${key}" has a dispatch entry and no argument shape, which is a translator defect`);
|
|
225
190
|
}
|
|
226
191
|
}
|
|
227
|
-
/** Core's flat unique selector → `<M>WhereUniqueInput`, at the top level. */
|
|
228
192
|
function uniqueWhere(context) {
|
|
229
193
|
const selector = context.args.where;
|
|
230
194
|
if (!isRecord(selector)) {
|
|
@@ -234,10 +198,6 @@ function uniqueWhere(context) {
|
|
|
234
198
|
addresses: identifiersOf(context, context.resource),
|
|
235
199
|
values: selector,
|
|
236
200
|
match: "exact",
|
|
237
|
-
// A unique selector's values are scalars core has already normalized. The
|
|
238
|
-
// one conversion C-834 names — a core `Decimal`, which Prisma refuses — is
|
|
239
|
-
// NOT applied here, and that is a recorded gap rather than an omission: see
|
|
240
|
-
// `operation.executor.ts`'s header.
|
|
241
201
|
emit: (_member, value) => value,
|
|
242
202
|
});
|
|
243
203
|
if (addressed === undefined) {
|
|
@@ -245,7 +205,6 @@ function uniqueWhere(context) {
|
|
|
245
205
|
}
|
|
246
206
|
return addressed.where;
|
|
247
207
|
}
|
|
248
|
-
/** The identifier the first-style read selects: the first recorded criterion. */
|
|
249
208
|
function identifierSelect(context) {
|
|
250
209
|
const [address] = identifiersOf(context, context.resource);
|
|
251
210
|
if (address === undefined) {
|
|
@@ -293,7 +252,6 @@ function mutationData(context, stage, argument) {
|
|
|
293
252
|
data: context.args[argument],
|
|
294
253
|
}, mutationDependencies(context));
|
|
295
254
|
}
|
|
296
|
-
/** `create.many`/`create.count` carry an ARRAY of records **[CORE]**. */
|
|
297
255
|
function bulkMutationData(context, key, stage) {
|
|
298
256
|
const data = context.args.data;
|
|
299
257
|
if (!Array.isArray(data)) {
|
|
@@ -317,14 +275,6 @@ function mutationDependencies(context) {
|
|
|
317
275
|
])),
|
|
318
276
|
};
|
|
319
277
|
}
|
|
320
|
-
/**
|
|
321
|
-
* Task 7's vocabulary, as the projection translator's one dependency.
|
|
322
|
-
*
|
|
323
|
-
* The nested arguments are translated against the **target** Resource, which the
|
|
324
|
-
* projection path names: `projection.translator.ts` owns where a relation sits
|
|
325
|
-
* and this closure owns what its arguments mean, which is the seam that keeps the
|
|
326
|
-
* `_count` filter the *same* translated `where` the relation read uses.
|
|
327
|
-
*/
|
|
328
278
|
function relationArgumentTranslator(context) {
|
|
329
279
|
return (args, { path }) => {
|
|
330
280
|
const target = resolveRelationTarget(context, path);
|