@aventara/prisma7-adapter 0.1.0-pilot.1 → 0.1.0-pilot.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -20
- package/dist/discovery/artifact.loader.d.ts +6 -2
- package/dist/discovery/artifact.loader.js +12 -57
- package/dist/discovery/capability-surface.interface.d.ts +29 -40
- package/dist/discovery/capability-surface.interface.js +0 -12
- package/dist/discovery/capability.deriver.d.ts +10 -40
- package/dist/discovery/capability.deriver.js +0 -179
- package/dist/discovery/client-shape.verifier.d.ts +13 -0
- package/dist/discovery/client-shape.verifier.js +93 -0
- package/dist/discovery/discovery-artifact.interface.d.ts +55 -30
- package/dist/discovery/enum.mapper.d.ts +6 -14
- package/dist/discovery/enum.mapper.js +1 -2
- package/dist/discovery/field.mapper.d.ts +49 -63
- package/dist/discovery/field.mapper.js +0 -161
- package/dist/discovery/identifier.mapper.d.ts +38 -77
- package/dist/discovery/identifier.mapper.js +0 -64
- package/dist/discovery/model.discoverer.d.ts +18 -35
- package/dist/discovery/model.discoverer.js +66 -141
- package/dist/discovery/operations.deriver.d.ts +10 -38
- package/dist/discovery/operations.deriver.js +0 -40
- package/dist/discovery/scalar.mapper.d.ts +12 -35
- package/dist/discovery/scalar.mapper.js +0 -8
- package/dist/discovery/schema-facts.interface.d.ts +26 -25
- package/dist/discovery/schema-facts.interface.js +0 -10
- package/dist/discovery/schema.reader.d.ts +53 -64
- package/dist/discovery/schema.reader.js +0 -174
- package/dist/discovery/types.reader.d.ts +0 -4
- package/dist/discovery/types.reader.js +2 -177
- package/dist/discovery/vocabulary.dictionary.d.ts +36 -75
- package/dist/discovery/vocabulary.dictionary.js +2 -152
- package/dist/errors/error.normalizer.d.ts +85 -135
- package/dist/errors/error.normalizer.js +1 -70
- package/dist/errors/prisma-error.reader.d.ts +53 -68
- package/dist/errors/prisma-error.reader.js +0 -91
- package/dist/execution/first-variant.emulator.d.ts +11 -22
- package/dist/execution/first-variant.emulator.js +0 -142
- package/dist/execution/operation.executor.d.ts +12 -19
- package/dist/execution/operation.executor.js +0 -92
- package/dist/execution/result.shaper.d.ts +22 -31
- package/dist/execution/result.shaper.js +0 -24
- package/dist/execution/transaction.executor.d.ts +36 -73
- package/dist/generate/artifact.emitter.d.ts +27 -27
- package/dist/generate/artifact.emitter.js +30 -13
- package/dist/generate/declared-support.reader.d.ts +11 -11
- package/dist/generate/declared-support.reader.js +0 -12
- package/dist/generate/discovery.bin.js +0 -10
- package/dist/generate/discovery.cli.js +0 -55
- package/dist/generate/existence.gate.d.ts +26 -32
- package/dist/generate/existence.gate.js +0 -6
- package/dist/generate/index.d.ts +10 -12
- package/dist/generate/index.js +0 -42
- package/dist/generate/node-version.guard.js +0 -12
- package/dist/generate/prisma-cli.resolver.d.ts +9 -11
- package/dist/generate/prisma-cli.resolver.js +0 -13
- package/dist/generate/prisma.generate.d.ts +49 -65
- package/dist/generate/prisma.generate.js +13 -123
- package/dist/generate/reader-crosscheck.d.ts +36 -60
- package/dist/generate/reader-crosscheck.js +5 -41
- package/dist/prisma-client.interface.d.ts +14 -19
- package/dist/prisma-client.interface.js +0 -6
- package/dist/prisma.adapter.d.ts +9 -17
- package/dist/prisma.adapter.js +11 -53
- package/dist/provider/provider-profile.d.ts +6 -23
- package/dist/provider/provider-profile.js +0 -64
- package/dist/translation/identifier.addressing.d.ts +15 -22
- package/dist/translation/identifier.addressing.js +0 -10
- package/dist/translation/mutation.translator.d.ts +19 -43
- package/dist/translation/mutation.translator.js +1 -209
- package/dist/translation/operation.translator.d.ts +36 -63
- package/dist/translation/operation.translator.js +1 -51
- package/dist/translation/order-pagination.translator.d.ts +24 -31
- package/dist/translation/order-pagination.translator.js +0 -43
- package/dist/translation/projection.translator.d.ts +25 -58
- package/dist/translation/projection.translator.js +0 -27
- package/dist/translation/scalar.conversion.d.ts +15 -26
- package/dist/translation/scalar.conversion.js +1 -129
- package/dist/translation/where.translator.d.ts +17 -28
- package/dist/translation/where.translator.js +0 -83
- package/package.json +4 -3
|
@@ -3,7 +3,6 @@ import path from "node:path";
|
|
|
3
3
|
export class PrismaSchemaReaderError extends Error {
|
|
4
4
|
name = "PrismaSchemaReaderError";
|
|
5
5
|
}
|
|
6
|
-
/** Field attributes this reader models. Anything else is recorded, not dropped. */
|
|
7
6
|
const FIELD_ATTRIBUTES = new Set([
|
|
8
7
|
"@id",
|
|
9
8
|
"@unique",
|
|
@@ -12,9 +11,7 @@ const FIELD_ATTRIBUTES = new Set([
|
|
|
12
11
|
"@relation",
|
|
13
12
|
"@ignore",
|
|
14
13
|
]);
|
|
15
|
-
/** Block attributes this reader models. */
|
|
16
14
|
const BLOCK_ATTRIBUTES = new Set(["@@id", "@@unique", "@@ignore"]);
|
|
17
|
-
/** `@relation` arguments this reader models. A positional argument is the name. */
|
|
18
15
|
const RELATION_ARGUMENTS = new Set([
|
|
19
16
|
"name",
|
|
20
17
|
"fields",
|
|
@@ -22,23 +19,7 @@ const RELATION_ARGUMENTS = new Set([
|
|
|
22
19
|
"onDelete",
|
|
23
20
|
"onUpdate",
|
|
24
21
|
]);
|
|
25
|
-
/**
|
|
26
|
-
* Bucket 2 at `@relation`'s argument level. `map:` names the foreign-key
|
|
27
|
-
* CONSTRAINT — measured at 7.10.0, `@relation(…, map: "beta_alpha_fk")` renders
|
|
28
|
-
* `ALTER TABLE "B" ADD CONSTRAINT "beta_alpha_fk" FOREIGN KEY …`, and that name
|
|
29
|
-
* appears nowhere else. It fails the surface test and the reason behind it
|
|
30
|
-
* alike, which is what separates it from `onDelete`/`onUpdate` on the same
|
|
31
|
-
* attribute.
|
|
32
|
-
*/
|
|
33
22
|
const DISCARDED_RELATION_ARGUMENTS = new Set(["map"]);
|
|
34
|
-
/**
|
|
35
|
-
* PSL's referential-action spellings, renamed into Aventara's words. PSL writes
|
|
36
|
-
* the SQL-standard set in PascalCase, so this is a rename, not a translation,
|
|
37
|
-
* and no Prisma spelling travels with the fact.
|
|
38
|
-
*
|
|
39
|
-
* A sixth spelling is bucket 3 rather than a guess: an action this reader cannot
|
|
40
|
-
* name is an action a planner would silently mis-plan around.
|
|
41
|
-
*/
|
|
42
23
|
const REFERENTIAL_ACTIONS = {
|
|
43
24
|
Cascade: "cascade",
|
|
44
25
|
Restrict: "restrict",
|
|
@@ -46,45 +27,10 @@ const REFERENTIAL_ACTIONS = {
|
|
|
46
27
|
SetNull: "set-null",
|
|
47
28
|
SetDefault: "set-default",
|
|
48
29
|
};
|
|
49
|
-
/**
|
|
50
|
-
* **Bucket 2 — recognised and deliberately discarded.** DB-naming and
|
|
51
|
-
* physical-layout concerns with no Aventara vocabulary at all.
|
|
52
|
-
*
|
|
53
|
-
* The list is explicit on purpose: the discard is then a decision a reader can
|
|
54
|
-
* see and audit, not an omission. Bucket 1 is `FIELD_ATTRIBUTES`,
|
|
55
|
-
* `BLOCK_ATTRIBUTES`, `@db.*` and `RELATION_ARGUMENTS`; bucket 3 is everything
|
|
56
|
-
* on neither list, which is recorded in `unhandled` and stops the build.
|
|
57
|
-
*/
|
|
58
30
|
const DISCARDED_ATTRIBUTES = new Set(["@map", "@@map", "@@index"]);
|
|
59
|
-
/**
|
|
60
|
-
* Bucket 2 at argument level: the named arguments of a **handled** block
|
|
61
|
-
* attribute that this reader discards. `map:`'s index name is the only one —
|
|
62
|
-
* measured at 7.10.0, it reaches nothing but the 🛑 `internal/class.ts`
|
|
63
|
-
* inline-schema blob, so no value of it can ever be observed through Aventara.
|
|
64
|
-
*
|
|
65
|
-
* `name:` was on this list and is **not** any more (2026-09-29): a custom name
|
|
66
|
-
* REPLACES the positional compound alias in `WhereUniqueInput` and in the
|
|
67
|
-
* `AtLeast` key union, so it is a public-API fact, it is modelled
|
|
68
|
-
* ({@link PrismaSchemaBlockIdentifier}), and this list is where discarding it
|
|
69
|
-
* silently broke addressing for every named identifier.
|
|
70
|
-
*
|
|
71
|
-
* An argument on neither this list nor `@@id`/`@@unique`'s modelled set is
|
|
72
|
-
* bucket 3.
|
|
73
|
-
*/
|
|
74
31
|
const DISCARDED_BLOCK_ATTRIBUTE_ARGUMENTS = new Set(["map"]);
|
|
75
|
-
/** The named arguments of `@@id`/`@@unique` this reader models, besides the list. */
|
|
76
32
|
const BLOCK_IDENTIFIER_ARGUMENTS = new Set(["name"]);
|
|
77
|
-
/**
|
|
78
|
-
* The argument name under which `@@id`/`@@unique` may carry its member list.
|
|
79
|
-
*
|
|
80
|
-
* PSL accepts the SAME list positionally or named — `@@unique([b, a])` and
|
|
81
|
-
* `@@unique(fields: [b, a])` are one constraint, and a generated client keys both
|
|
82
|
-
* `b_a` (verified with `prisma validate` and a generated client at 7.10.0,
|
|
83
|
-
* 2026-09-29). Which spelling is used is therefore not a fact about the schema,
|
|
84
|
-
* and no argument here is located by POSITION.
|
|
85
|
-
*/
|
|
86
33
|
const BLOCK_IDENTIFIER_LIST_ARGUMENT = "fields";
|
|
87
|
-
/** A member this reader can locate: a field name and nothing else. */
|
|
88
34
|
const PLAIN_NAME = /^[A-Za-z_]\w*$/;
|
|
89
35
|
const BLOCK_KEYWORDS = new Set([
|
|
90
36
|
"model",
|
|
@@ -94,10 +40,6 @@ const BLOCK_KEYWORDS = new Set([
|
|
|
94
40
|
"type",
|
|
95
41
|
"view",
|
|
96
42
|
]);
|
|
97
|
-
/**
|
|
98
|
-
* Reads a schema file, or a schema **folder** (Prisma 7 accepts either).
|
|
99
|
-
* A folder is read as readdir-plus-merge in filename order.
|
|
100
|
-
*/
|
|
101
43
|
export async function readPrismaSchema(location) {
|
|
102
44
|
const stats = await stat(location).catch((cause) => {
|
|
103
45
|
throw new PrismaSchemaReaderError(`the Prisma schema at ${location} could not be read`, { cause });
|
|
@@ -117,10 +59,6 @@ export async function readPrismaSchema(location) {
|
|
|
117
59
|
}
|
|
118
60
|
return readPrismaSchemaSources(sources);
|
|
119
61
|
}
|
|
120
|
-
/**
|
|
121
|
-
* The reader proper, over text already in hand. Merging several sources is
|
|
122
|
-
* concatenation: blocks are file-local and references are by name.
|
|
123
|
-
*/
|
|
124
62
|
export function readPrismaSchemaSources(sources) {
|
|
125
63
|
const enums = [];
|
|
126
64
|
const models = [];
|
|
@@ -133,17 +71,9 @@ export function readPrismaSchemaSources(sources) {
|
|
|
133
71
|
function readOneSource(source, enums, models, unhandled) {
|
|
134
72
|
let open = null;
|
|
135
73
|
const lines = source.text.split("\n");
|
|
136
|
-
// Relations whose action argument this reader could not NAME. They are
|
|
137
|
-
// recorded in `unhandled` and must not then be handed a default: a resolved
|
|
138
|
-
// action is a claim about what a delete does, and the reader has just said it
|
|
139
|
-
// does not know. Kept beside the facts rather than on them, so the public
|
|
140
|
-
// shape carries no "unreadable" state that no consumer can ever observe (a
|
|
141
|
-
// non-empty `unhandled` fails the generate step first).
|
|
142
74
|
const unreadableActions = new Set();
|
|
143
75
|
for (const [index, raw] of lines.entries()) {
|
|
144
76
|
const site = { file: source.file, line: index + 1 };
|
|
145
|
-
// Comments go first, and go away string-aware: `"a, b // not a comment"`
|
|
146
|
-
// carries a `//` that is data, not a comment.
|
|
147
77
|
const line = stripComment(raw).trim();
|
|
148
78
|
if (line.length === 0) {
|
|
149
79
|
continue;
|
|
@@ -168,9 +98,6 @@ function readOneSource(source, enums, models, unhandled) {
|
|
|
168
98
|
else if (open.kind === "model") {
|
|
169
99
|
models.push({
|
|
170
100
|
name: open.name,
|
|
171
|
-
// Defaults resolve HERE and not on the field line: the default
|
|
172
|
-
// turns on the foreign key's optionality, and a `@relation` may
|
|
173
|
-
// name a scalar declared below it.
|
|
174
101
|
fields: resolvePrismaReferentialDefaults(open.fields, unreadableActions),
|
|
175
102
|
blockId: open.blockId,
|
|
176
103
|
blockUniques: open.blockUniques,
|
|
@@ -185,8 +112,6 @@ function readOneSource(source, enums, models, unhandled) {
|
|
|
185
112
|
continue;
|
|
186
113
|
}
|
|
187
114
|
if (open.kind === "enum") {
|
|
188
|
-
// An enum body carries members and block attributes. `@@map` on the enum
|
|
189
|
-
// is bucket 2 like every other `@@map`; anything else `@@` is bucket 3.
|
|
190
115
|
if (line.startsWith("@@")) {
|
|
191
116
|
for (const attribute of splitAttributes(line)) {
|
|
192
117
|
const name = attributeName(attribute);
|
|
@@ -196,9 +121,6 @@ function readOneSource(source, enums, models, unhandled) {
|
|
|
196
121
|
}
|
|
197
122
|
continue;
|
|
198
123
|
}
|
|
199
|
-
// A member is a name, optionally followed by attributes. A member-level
|
|
200
|
-
// `@map` falls into bucket 2 and is no longer a special case, so the
|
|
201
|
-
// attributes are located only far enough to find where the name ends.
|
|
202
124
|
const attributes = splitAttributes(line);
|
|
203
125
|
const name = line.slice(0, firstTopLevelAt(line) ?? line.length).trim();
|
|
204
126
|
if (!/^[A-Za-z_]\w*$/.test(name)) {
|
|
@@ -254,7 +176,6 @@ function openBlock(line, site, record) {
|
|
|
254
176
|
return { kind: "enum", name, site, values: [] };
|
|
255
177
|
}
|
|
256
178
|
if (keyword === "type" || keyword === "view") {
|
|
257
|
-
// Modelling these would be evaluating, not locating (see the header).
|
|
258
179
|
record("block", keyword, line);
|
|
259
180
|
}
|
|
260
181
|
return { kind: "skip", name };
|
|
@@ -262,16 +183,12 @@ function openBlock(line, site, record) {
|
|
|
262
183
|
function readBlockAttributeLine(line, open, record) {
|
|
263
184
|
const attributes = splitAttributes(line);
|
|
264
185
|
if (attributes.length !== 1) {
|
|
265
|
-
// `prisma validate` rejects two block attributes on one line with P1012
|
|
266
|
-
// (case 3 in the header), so this is unreachable through the real tool.
|
|
267
|
-
// It is recorded rather than assumed away.
|
|
268
186
|
record("line", "block-attribute-line", line);
|
|
269
187
|
return;
|
|
270
188
|
}
|
|
271
189
|
const attribute = attributes[0];
|
|
272
190
|
const name = attributeName(attribute);
|
|
273
191
|
if (DISCARDED_ATTRIBUTES.has(name)) {
|
|
274
|
-
// Bucket 2. `@@map` and `@@index` are DB-naming and physical-layout only.
|
|
275
192
|
return;
|
|
276
193
|
}
|
|
277
194
|
if (!BLOCK_ATTRIBUTES.has(name)) {
|
|
@@ -282,21 +199,6 @@ function readBlockAttributeLine(line, open, record) {
|
|
|
282
199
|
open.ignored = true;
|
|
283
200
|
return;
|
|
284
201
|
}
|
|
285
|
-
// Every argument is classified BY NAME, never by position. PSL writes the
|
|
286
|
-
// member list either positionally or as `fields:`, and lets the named
|
|
287
|
-
// arguments come in any order; all eight spellings of `@@unique` and all five
|
|
288
|
-
// of `@@id` are accepted by `prisma validate` 7.10.0 (measured 2026-09-29).
|
|
289
|
-
// Reading the list as "the first argument" is what made the reader take
|
|
290
|
-
// `@@unique(fields: [b, a])` as one member called `fields: [b, a]`, silently,
|
|
291
|
-
// and the generate step then refused a schema Prisma accepts.
|
|
292
|
-
//
|
|
293
|
-
// The other arguments are classified, not skipped: `name:` is modelled (its
|
|
294
|
-
// value is an `AtLeast` key), `map:` is bucket 2 (invisible on the 🟢
|
|
295
|
-
// surface), and an argument spelling this reader has never seen is bucket 3
|
|
296
|
-
// and stops the build.
|
|
297
|
-
//
|
|
298
|
-
// Member order follows THE ATTRIBUTE'S LIST, never the field declaration
|
|
299
|
-
// order: `@@unique([y, x])` is `["y", "x"]` even where `x` is declared first.
|
|
300
202
|
let members = null;
|
|
301
203
|
let unlocatable = false;
|
|
302
204
|
let alias = null;
|
|
@@ -305,8 +207,6 @@ function readBlockAttributeLine(line, open, record) {
|
|
|
305
207
|
const key = keyed?.[1];
|
|
306
208
|
const value = keyed?.[2] ?? argument;
|
|
307
209
|
if (key === undefined || key === BLOCK_IDENTIFIER_LIST_ARGUMENT) {
|
|
308
|
-
// The positional argument IS the member list, and `fields:` is that
|
|
309
|
-
// same list named; PSL rejects a declaration carrying both.
|
|
310
210
|
const located = listMembers(value);
|
|
311
211
|
if (located === null) {
|
|
312
212
|
unlocatable = true;
|
|
@@ -316,8 +216,6 @@ function readBlockAttributeLine(line, open, record) {
|
|
|
316
216
|
continue;
|
|
317
217
|
}
|
|
318
218
|
if (BLOCK_IDENTIFIER_ARGUMENTS.has(key)) {
|
|
319
|
-
// Located and unquoted, never evaluated: PSL writes it as a string
|
|
320
|
-
// literal and Prisma spells the key with exactly those characters.
|
|
321
219
|
alias = unquote(value);
|
|
322
220
|
continue;
|
|
323
221
|
}
|
|
@@ -326,11 +224,6 @@ function readBlockAttributeLine(line, open, record) {
|
|
|
326
224
|
}
|
|
327
225
|
}
|
|
328
226
|
if (members === null || unlocatable) {
|
|
329
|
-
// Bucket 3, and deliberately not a guess: a member spelled `a(sort: Desc)`
|
|
330
|
-
// — valid PSL at 7.10.0 — cannot be reduced to a field name without
|
|
331
|
-
// EVALUATING it, which this reader does not do. Recorded, so the generate
|
|
332
|
-
// step stops loudly rather than emitting an identifier over a column no
|
|
333
|
-
// `AdapterFieldMap` carries.
|
|
334
227
|
record("block-attribute", name, attribute);
|
|
335
228
|
return;
|
|
336
229
|
}
|
|
@@ -349,8 +242,6 @@ function readFieldLine(line, site, record, unreadableActions) {
|
|
|
349
242
|
const name = tokens[0];
|
|
350
243
|
const written = tokens[1];
|
|
351
244
|
if (tokens.length !== 2 || name === undefined || written === undefined) {
|
|
352
|
-
// `prisma validate` rejects a split field declaration with P1012 (case 4 in
|
|
353
|
-
// the header), so this is unreachable through the real tool.
|
|
354
245
|
record("line", "field-declaration", line);
|
|
355
246
|
return null;
|
|
356
247
|
}
|
|
@@ -369,14 +260,10 @@ function readFieldLine(line, site, record, unreadableActions) {
|
|
|
369
260
|
: splitAttributes(line.slice(attributeStart))) {
|
|
370
261
|
const attributeName_ = attributeName(attribute);
|
|
371
262
|
if (attributeName_.startsWith("@db.")) {
|
|
372
|
-
// A hint only, kept exactly as written.
|
|
373
263
|
native.push(attribute);
|
|
374
264
|
continue;
|
|
375
265
|
}
|
|
376
266
|
if (DISCARDED_ATTRIBUTES.has(attributeName_)) {
|
|
377
|
-
// Bucket 2. A field-level `@map` names a column; Aventara has no
|
|
378
|
-
// vocabulary for a column name, and recording it here would fail the
|
|
379
|
-
// generate step on most real Prisma schemas.
|
|
380
267
|
continue;
|
|
381
268
|
}
|
|
382
269
|
if (!FIELD_ATTRIBUTES.has(attributeName_)) {
|
|
@@ -397,7 +284,6 @@ function readFieldLine(line, site, record, unreadableActions) {
|
|
|
397
284
|
ignored = true;
|
|
398
285
|
break;
|
|
399
286
|
case "@default":
|
|
400
|
-
// RAW TEXT. Matched against a table downstream, never parsed here.
|
|
401
287
|
defaultArgument = argumentText(attribute);
|
|
402
288
|
break;
|
|
403
289
|
default:
|
|
@@ -430,15 +316,12 @@ function readRelationAttribute(attribute, record, unreadableActions) {
|
|
|
430
316
|
for (const argument of splitTopLevel(argumentText(attribute) ?? "", ",")) {
|
|
431
317
|
const keyed = /^([A-Za-z_]\w*)\s*:\s*([\s\S]*)$/.exec(argument);
|
|
432
318
|
if (keyed === null) {
|
|
433
|
-
// A positional argument is the relation name — the ONLY evidence
|
|
434
|
-
// pairing a back-reference with its forward reference.
|
|
435
319
|
name = unquote(argument);
|
|
436
320
|
continue;
|
|
437
321
|
}
|
|
438
322
|
const key = keyed[1];
|
|
439
323
|
const value = keyed[2].trim();
|
|
440
324
|
if (DISCARDED_RELATION_ARGUMENTS.has(key)) {
|
|
441
|
-
// Bucket 2: a foreign-key constraint name, and nothing else.
|
|
442
325
|
continue;
|
|
443
326
|
}
|
|
444
327
|
if (!RELATION_ARGUMENTS.has(key)) {
|
|
@@ -450,8 +333,6 @@ function readRelationAttribute(attribute, record, unreadableActions) {
|
|
|
450
333
|
continue;
|
|
451
334
|
}
|
|
452
335
|
if (key === "onDelete" || key === "onUpdate") {
|
|
453
|
-
// Located, then renamed through a closed table. An enum member is
|
|
454
|
-
// written bare in PSL, so this is a lookup and not an evaluation.
|
|
455
336
|
const action = REFERENTIAL_ACTIONS[value];
|
|
456
337
|
if (action === undefined) {
|
|
457
338
|
record("attribute-argument", key, argument);
|
|
@@ -466,9 +347,6 @@ function readRelationAttribute(attribute, record, unreadableActions) {
|
|
|
466
347
|
}
|
|
467
348
|
continue;
|
|
468
349
|
}
|
|
469
|
-
// `fields:`/`references:` are located the same way the block attributes'
|
|
470
|
-
// list is, and by the same rule: a member this reader cannot reduce to a
|
|
471
|
-
// field name without evaluating it is bucket 3, not a guess.
|
|
472
350
|
const located = listMembers(value);
|
|
473
351
|
if (located === null) {
|
|
474
352
|
record("attribute-argument", key, argument);
|
|
@@ -493,40 +371,10 @@ function readRelationAttribute(attribute, record, unreadableActions) {
|
|
|
493
371
|
}
|
|
494
372
|
return relation;
|
|
495
373
|
}
|
|
496
|
-
/**
|
|
497
|
-
* Absent `onDelete`/`onUpdate` replaced by Prisma's **effective** default, on the
|
|
498
|
-
* owning edges of one model.
|
|
499
|
-
*
|
|
500
|
-
* An absent action and the explicitly-written default are one fact about what a
|
|
501
|
-
* delete does, so there is no third state to carry. What the default IS was
|
|
502
|
-
* measured at 7.10.0 with
|
|
503
|
-
* `prisma migrate diff --from-empty --to-schema <file> --script`, identical on
|
|
504
|
-
* `postgresql` and `sqlite` (2026-09-29):
|
|
505
|
-
*
|
|
506
|
-
* ```text
|
|
507
|
-
* alphaId String alpha Alpha → ON DELETE RESTRICT ON UPDATE CASCADE
|
|
508
|
-
* alphaId String? alpha Alpha? → ON DELETE SET NULL ON UPDATE CASCADE
|
|
509
|
-
* aId String, aK String? a A? → ON DELETE RESTRICT ON UPDATE CASCADE
|
|
510
|
-
* ```
|
|
511
|
-
*
|
|
512
|
-
* The third line is why the discriminant is stated as it is. The plain reading —
|
|
513
|
-
* "a required relation restricts, an optional one nulls" — is wrong: the
|
|
514
|
-
* relation field's own `?` does not decide it. `SET NULL` appears exactly when
|
|
515
|
-
* **every** scalar in `fields:` is optional, which is the only case where the
|
|
516
|
-
* database can actually write the null. A compound key with one required member
|
|
517
|
-
* restricts, and so does an optional relation field over a required foreign key.
|
|
518
|
-
*
|
|
519
|
-
* A foreign key naming no sibling is left non-optional here, which resolves to
|
|
520
|
-
* `restrict`; `field.mapper.ts` throws on that schema before any consumer reads
|
|
521
|
-
* the action.
|
|
522
|
-
*/
|
|
523
374
|
function resolvePrismaReferentialDefaults(fields, unreadableActions) {
|
|
524
375
|
const optionalByName = new Map(fields.map((field) => [field.name, field.optional]));
|
|
525
376
|
return fields.map((field) => {
|
|
526
377
|
const relation = field.relation;
|
|
527
|
-
// `fields:` is what makes an edge the owning side, and PSL forbids the
|
|
528
|
-
// other side from carrying an action at all — so a back-reference keeps
|
|
529
|
-
// `null`, which reads as "ask the other edge".
|
|
530
378
|
if (relation === null || relation.fields === null) {
|
|
531
379
|
return field;
|
|
532
380
|
}
|
|
@@ -545,11 +393,6 @@ function resolvePrismaReferentialDefaults(fields, unreadableActions) {
|
|
|
545
393
|
};
|
|
546
394
|
});
|
|
547
395
|
}
|
|
548
|
-
/* ------------------------------------------------------------------ *
|
|
549
|
-
* Locating primitives. Every one of these finds where a token is; not *
|
|
550
|
-
* one of them decides what a token means. *
|
|
551
|
-
* ------------------------------------------------------------------ */
|
|
552
|
-
/** Removes a `//` comment, string-aware: a `//` inside a string literal is data. */
|
|
553
396
|
function stripComment(line) {
|
|
554
397
|
let inString = false;
|
|
555
398
|
for (let i = 0; i < line.length; i += 1) {
|
|
@@ -573,7 +416,6 @@ function stripComment(line) {
|
|
|
573
416
|
}
|
|
574
417
|
return line;
|
|
575
418
|
}
|
|
576
|
-
/** The index of the first attribute `@` outside strings and brackets, or null. */
|
|
577
419
|
function firstTopLevelAt(text) {
|
|
578
420
|
for (const index of topLevelAtIndexes(text)) {
|
|
579
421
|
return index;
|
|
@@ -604,23 +446,19 @@ function* topLevelAtIndexes(text) {
|
|
|
604
446
|
depth -= 1;
|
|
605
447
|
}
|
|
606
448
|
else if (ch === "@" && depth === 0 && text[i - 1] !== "@") {
|
|
607
|
-
// `@@` is one marker, not two attributes.
|
|
608
449
|
yield i;
|
|
609
450
|
}
|
|
610
451
|
}
|
|
611
452
|
}
|
|
612
|
-
/** Splits `@a(1) @@b([x])` into whole attributes, strings and brackets respected. */
|
|
613
453
|
function splitAttributes(text) {
|
|
614
454
|
const starts = [...topLevelAtIndexes(text)];
|
|
615
455
|
return starts
|
|
616
456
|
.map((start, position) => text.slice(start, starts[position + 1] ?? text.length).trim())
|
|
617
457
|
.filter((attribute) => attribute.length > 0);
|
|
618
458
|
}
|
|
619
|
-
/** `@db.Decimal(10, 2)` → `@db.Decimal`; `@@unique([y, x])` → `@@unique`. */
|
|
620
459
|
function attributeName(attribute) {
|
|
621
460
|
return /^@@?[A-Za-z_][\w.]*/.exec(attribute)?.[0] ?? attribute;
|
|
622
461
|
}
|
|
623
|
-
/** The text between an attribute's outermost parentheses, or null when it has none. */
|
|
624
462
|
function argumentText(attribute) {
|
|
625
463
|
const open = attribute.indexOf("(");
|
|
626
464
|
if (open < 0 || !attribute.trimEnd().endsWith(")")) {
|
|
@@ -631,17 +469,6 @@ function argumentText(attribute) {
|
|
|
631
469
|
.slice(open + 1, -1)
|
|
632
470
|
.trim();
|
|
633
471
|
}
|
|
634
|
-
/**
|
|
635
|
-
* A `[…]` argument's members in written order, or **null** when the value is not
|
|
636
|
-
* a list of plain field names.
|
|
637
|
-
*
|
|
638
|
-
* Null is the locator's honest answer, and the caller turns it into an
|
|
639
|
-
* `unhandled` entry. Two shapes reach it, both valid PSL at 7.10.0: a member
|
|
640
|
-
* carrying an argument of its own (`@@unique([a(sort: Desc)])`), and a value that
|
|
641
|
-
* is not a list at all. Either one would have to be EVALUATED to yield a field
|
|
642
|
-
* name, and a reader that guesses here emits an identifier over a column that
|
|
643
|
-
* does not exist — which is exactly how the named-list defect stayed silent.
|
|
644
|
-
*/
|
|
645
472
|
function listMembers(value) {
|
|
646
473
|
const trimmed = value.trim();
|
|
647
474
|
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
|
|
@@ -650,7 +477,6 @@ function listMembers(value) {
|
|
|
650
477
|
const members = splitTopLevel(trimmed.slice(1, -1), ",");
|
|
651
478
|
return members.every((member) => PLAIN_NAME.test(member)) ? members : null;
|
|
652
479
|
}
|
|
653
|
-
/** Splits on a separator at bracket depth 0, outside string literals. */
|
|
654
480
|
function splitTopLevel(text, separator) {
|
|
655
481
|
const out = [];
|
|
656
482
|
let depth = 0;
|
|
@@ -15,10 +15,6 @@ export declare class PrismaTypesReaderError extends Error {
|
|
|
15
15
|
/**
|
|
16
16
|
* The reader's one checker question, exported because it is the distinction the
|
|
17
17
|
* whole reader rests on and therefore has to be testable on its own.
|
|
18
|
-
*
|
|
19
|
-
* Returns each written type argument's type as the checker renders it, so
|
|
20
|
-
* `FieldRef<"M0", 'String'>` answers `['"M0"', '"String"']` — including when
|
|
21
|
-
* `FieldRef` itself does not resolve.
|
|
22
18
|
*/
|
|
23
19
|
export declare function readWrittenTypeArguments(checker: ts.TypeChecker, reference: ts.TypeReferenceNode): readonly string[];
|
|
24
20
|
export declare function readPrismaCapabilitySurface(clientDirectory: string): PrismaTypesReadResult;
|