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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +33 -20
  2. package/dist/discovery/artifact.loader.d.ts +6 -2
  3. package/dist/discovery/artifact.loader.js +12 -57
  4. package/dist/discovery/capability-surface.interface.d.ts +29 -40
  5. package/dist/discovery/capability-surface.interface.js +0 -12
  6. package/dist/discovery/capability.deriver.d.ts +10 -40
  7. package/dist/discovery/capability.deriver.js +0 -179
  8. package/dist/discovery/client-shape.verifier.d.ts +13 -0
  9. package/dist/discovery/client-shape.verifier.js +93 -0
  10. package/dist/discovery/discovery-artifact.interface.d.ts +55 -30
  11. package/dist/discovery/enum.mapper.d.ts +6 -14
  12. package/dist/discovery/enum.mapper.js +1 -2
  13. package/dist/discovery/field.mapper.d.ts +49 -63
  14. package/dist/discovery/field.mapper.js +0 -161
  15. package/dist/discovery/identifier.mapper.d.ts +38 -77
  16. package/dist/discovery/identifier.mapper.js +0 -64
  17. package/dist/discovery/model.discoverer.d.ts +18 -35
  18. package/dist/discovery/model.discoverer.js +66 -141
  19. package/dist/discovery/operations.deriver.d.ts +10 -38
  20. package/dist/discovery/operations.deriver.js +0 -40
  21. package/dist/discovery/scalar.mapper.d.ts +12 -35
  22. package/dist/discovery/scalar.mapper.js +0 -8
  23. package/dist/discovery/schema-facts.interface.d.ts +26 -25
  24. package/dist/discovery/schema-facts.interface.js +0 -10
  25. package/dist/discovery/schema.reader.d.ts +53 -64
  26. package/dist/discovery/schema.reader.js +0 -174
  27. package/dist/discovery/types.reader.d.ts +0 -4
  28. package/dist/discovery/types.reader.js +2 -177
  29. package/dist/discovery/vocabulary.dictionary.d.ts +36 -75
  30. package/dist/discovery/vocabulary.dictionary.js +2 -152
  31. package/dist/errors/error.normalizer.d.ts +85 -135
  32. package/dist/errors/error.normalizer.js +1 -70
  33. package/dist/errors/prisma-error.reader.d.ts +53 -68
  34. package/dist/errors/prisma-error.reader.js +0 -91
  35. package/dist/execution/first-variant.emulator.d.ts +11 -22
  36. package/dist/execution/first-variant.emulator.js +0 -142
  37. package/dist/execution/operation.executor.d.ts +12 -19
  38. package/dist/execution/operation.executor.js +0 -92
  39. package/dist/execution/result.shaper.d.ts +22 -31
  40. package/dist/execution/result.shaper.js +0 -24
  41. package/dist/execution/transaction.executor.d.ts +36 -73
  42. package/dist/generate/artifact.emitter.d.ts +27 -27
  43. package/dist/generate/artifact.emitter.js +30 -13
  44. package/dist/generate/declared-support.reader.d.ts +11 -11
  45. package/dist/generate/declared-support.reader.js +0 -12
  46. package/dist/generate/discovery.bin.js +0 -10
  47. package/dist/generate/discovery.cli.js +0 -55
  48. package/dist/generate/existence.gate.d.ts +26 -32
  49. package/dist/generate/existence.gate.js +0 -6
  50. package/dist/generate/index.d.ts +10 -12
  51. package/dist/generate/index.js +0 -42
  52. package/dist/generate/node-version.guard.js +0 -12
  53. package/dist/generate/prisma-cli.resolver.d.ts +9 -11
  54. package/dist/generate/prisma-cli.resolver.js +0 -13
  55. package/dist/generate/prisma.generate.d.ts +49 -65
  56. package/dist/generate/prisma.generate.js +13 -123
  57. package/dist/generate/reader-crosscheck.d.ts +36 -60
  58. package/dist/generate/reader-crosscheck.js +5 -41
  59. package/dist/prisma-client.interface.d.ts +14 -19
  60. package/dist/prisma-client.interface.js +0 -6
  61. package/dist/prisma.adapter.d.ts +9 -17
  62. package/dist/prisma.adapter.js +11 -53
  63. package/dist/provider/provider-profile.d.ts +6 -23
  64. package/dist/provider/provider-profile.js +0 -64
  65. package/dist/translation/identifier.addressing.d.ts +15 -22
  66. package/dist/translation/identifier.addressing.js +0 -10
  67. package/dist/translation/mutation.translator.d.ts +19 -43
  68. package/dist/translation/mutation.translator.js +1 -209
  69. package/dist/translation/operation.translator.d.ts +36 -63
  70. package/dist/translation/operation.translator.js +1 -51
  71. package/dist/translation/order-pagination.translator.d.ts +24 -31
  72. package/dist/translation/order-pagination.translator.js +0 -43
  73. package/dist/translation/projection.translator.d.ts +25 -58
  74. package/dist/translation/projection.translator.js +0 -27
  75. package/dist/translation/scalar.conversion.d.ts +15 -26
  76. package/dist/translation/scalar.conversion.js +1 -129
  77. package/dist/translation/where.translator.d.ts +17 -28
  78. package/dist/translation/where.translator.js +0 -83
  79. package/package.json +3 -3
@@ -4,70 +4,59 @@ import type { PrismaAdapterModel, PrismaReferentialAction } from "./discovery-ar
4
4
  import type { PrismaSchemaField, PrismaSchemaUnknown } from "./schema-facts.interface.js";
5
5
  import { type PrismaDiscoveryDiagnostic, type PrismaEvidence } from "./vocabulary.dictionary.js";
6
6
  /**
7
- * Task 4 — one PSL field, answered as an `AdapterScalarField` or an
8
- * `AdapterRelationField` (C-808, C-811, C-813).
9
- *
10
7
  * # Nullability comes from PSL, and the required `Json` column is why
11
8
  *
12
- * `nullable` is `PrismaSchemaField.optional` — PSL's `?` — and **nothing else**
13
- * (T-3.1 invariant 4). The type surface cannot answer the question, and the
14
- * damage is not hypothetical: `$MetricPayload.scalars.sJson` resolves to
15
- * `null | string | number | false | true | JsonObject | JsonArray` while PSL says
16
- * `sJson Json`, required, because JSON null is a *value*. The reader's own facts
17
- * point the same wrong way — `JsonFilterBase.array_contains` admits `| null` and
18
- * `equals` admits `JsonNullValueFilter` on that same required column — so a
19
- * plausible "the operands mention null, therefore the column is nullable" rule
20
- * is wrong for **every** `Json` field while being right everywhere else. That is
21
- * the whole reason this rule is stated as a rule rather than left to taste.
9
+ * `nullable` is `PrismaSchemaField.optional` — PSL's `?` — and **nothing else**.
10
+ * The type surface cannot answer the question, and the damage is not hypothetical:
11
+ * `$MetricPayload.scalars.sJson` resolves to `null | string | number | false |
12
+ * true | JsonObject | JsonArray` while PSL says `sJson Json`, required, because
13
+ * JSON null is a *value*. The reader's own facts point the same wrong way —
14
+ * `JsonFilterBase.array_contains` admits `| null` and `equals` admits
15
+ * `JsonNullValueFilter` on that same required column — so a plausible "the
16
+ * operands mention null, therefore the column is nullable" rule is wrong for
17
+ * **every** `Json` field while being right everywhere else. That is the whole
18
+ * reason this rule is stated as a rule rather than left to taste.
22
19
  *
23
20
  * # The create-optional marker is a cross-check, never a source
24
21
  *
25
22
  * {@link crossCheckCreateOptional} is a separate function that *consumes* this
26
- * projection, and the data flows one way only: the marker never reaches
27
- * {@link projectPrismaField}. `<M>UncheckedCreateInput`'s `?` is **one bit over
28
- * several causes** — `@default(uuid())`, `@default(BRONZE)`, `@default(now())`,
29
- * `@updatedAt`, plain nullability, and a scalar list, which needs no value
30
- * because the empty list is one — and nothing on the 🟢 surface tells them
31
- * apart. Deriving a trait *from* the marker would invert the check into the bug
32
- * it exists to catch: it would make the two readers agree by construction and
33
- * detect nothing.
23
+ * projection, and the data flows one way only: the marker never reaches {@link
24
+ * projectPrismaField}. `<M>UncheckedCreateInput`'s `?` is **one bit over several
25
+ * causes** — `@default(uuid())`, `@default(BRONZE)`, `@default(now())`,
26
+ * `@updatedAt`, plain nullability, and a scalar list, which needs no value because
27
+ * the empty list is one — and nothing on the 🟢 surface tells them apart. Deriving
28
+ * a trait *from* the marker would invert the check into the bug it exists to
29
+ * catch: it would make the two readers agree by construction and detect nothing.
34
30
  *
35
31
  * # `@default` is matched, never parsed
36
32
  *
37
33
  * {@link matchPrismaDefault} walks `PRISMA_DEFAULT_TABLE` over the raw argument
38
- * text the PSL reader carried across unchanged. B1 deferred this matcher to this
39
- * task deliberately: the dictionary owns the data, and the lookup belongs where
40
- * a red test can drive it.
41
- *
42
- * The matcher adds **one** discriminant the dictionary's table does not state,
43
- * and it is a shape test rather than a parse: an argument that begins with an
44
- * identifier followed by `(` is a *generator form*, and anything else is a
45
- * *static value*. Both are `DEFAULTED`; only an unrecognised generator form
46
- * records an unknown. Without that split, `tier Tier @default(BRONZE)` — a field
47
- * of the Phase 8 schemas — would record an unknown and fail the generate step,
48
- * while C-811's own acceptance criteria say `@default("x")` is `["DEFAULTED"]`
49
- * with nothing recorded. The test never reads *inside* the argument, so
34
+ * text the PSL reader carried across unchanged.
35
+ *
36
+ * The matcher adds **one** discriminant the dictionary's table does not state, and
37
+ * it is a shape test rather than a parse: an argument that begins with an
38
+ * identifier followed by `(` is a *generator form*, and anything else is a *static
39
+ * value*. Both are `DEFAULTED`; only an unrecognised generator form records an
40
+ * unknown. The test never reads *inside* the argument, so
50
41
  * `dbgenerated("gen_random_uuid()")` remains a key and `"a, b // not a comment"`
51
42
  * remains opaque text.
52
43
  *
53
44
  * # A relation carries four members, and the FK is not one of them
54
45
  *
55
- * `target`, `cardinality`, `nullable`, and (from Task 6) `capabilities`. The
56
- * `@relation(fields: […], references: […])` scalars are read, used to identify
57
- * the FK column and check it against the edge's own optionality, and then
58
- * **discarded** (C-813). `@relation`'s *name* is not an Aventara concept either,
59
- * and its referential actions are not core's yet, so both travel beside the
60
- * projection as {@link PrismaRelationEdge} rather than inside it: pairing is
61
- * discovery-internal, and it is **PSL-only** — the name
62
- * appears nowhere on the 🟢 surface, so from the types alone `Author` has two
63
- * `Author` edges with nothing to pair them.
46
+ * `target`, `cardinality`, `nullable`, and `capabilities`. The `@relation(fields:
47
+ * […], references: […])` scalars are read, used to identify the FK column and
48
+ * check it against the edge's own optionality, and then **discarded**.
49
+ * `@relation`'s *name* is not an Aventara concept either, and its referential
50
+ * actions are not core's yet, so both travel beside the projection as {@link
51
+ * PrismaRelationEdge} rather than inside it: pairing is discovery-internal, and it
52
+ * is **PSL-only** — the name appears nowhere on the 🟢 surface, so from the types
53
+ * alone `Author` has two `Author` edges with nothing to pair them.
64
54
  *
65
55
  * # Two failure vocabularies, kept apart
66
56
  *
67
- * A **diagnostic** is about the schema: PSL declares a column the 🟢 types do
68
- * not expose (C-808's existence gate, whose fatality the generate step decides),
69
- * or a Prisma type name in no table. A **throw** is about the readers: the two
70
- * sources disagree about a fact they both state, which no schema can cause and
57
+ * A **diagnostic** is about the schema: PSL declares a column the 🟢 types do not
58
+ * expose, or a Prisma type name in no table. A **throw** is about the readers: the
59
+ * two sources disagree about a fact they both state, which no schema can cause and
71
60
  * no diagnostic vocabulary describes. The types reader draws the same line.
72
61
  */
73
62
  /** A reader-versus-reader disagreement: not a schema problem, and not recoverable. */
@@ -75,9 +64,8 @@ export declare class PrismaFieldProjectionError extends Error {
75
64
  readonly name = "PrismaFieldProjectionError";
76
65
  }
77
66
  /**
78
- * The projection minus `capabilities`, which is Task 6's and is derived from the
79
- * capability surface rather than from PSL. `Omit` rather than a restatement, so
80
- * the composed field is the core type by construction.
67
+ * `Omit` rather than a restatement, so the composed field is the core type by
68
+ * construction.
81
69
  */
82
70
  export type ProjectedScalarField = Omit<AdapterScalarField<PrismaAdapterModel>, "capabilities">;
83
71
  export type ProjectedRelationField = Omit<AdapterRelationField<PrismaAdapterModel>, "capabilities">;
@@ -87,20 +75,18 @@ export type ProjectedRelationField = Omit<AdapterRelationField<PrismaAdapterMode
87
75
  *
88
76
  * # Why the actions ride here and not on `AdapterRelationField`
89
77
  *
90
- * `AdapterRelationField` is core's type and its members are C-813's (`target`,
91
- * `cardinality`, `nullable`, `capabilities`). Wiring a referential action into
92
- * the Contract is a core change and belongs to the task that owns core's
93
- * transaction planner, not to discovery. Until then the fact lives where
94
- * discovery can state it truthfully.
78
+ * Wiring a referential action into the Contract is a core change and belongs to
79
+ * the task that owns core's transaction planner, not to discovery. Until then the
80
+ * fact lives where discovery can state it truthfully.
95
81
  *
96
82
  * # The lookup is inverted, and the edge is shaped so a consumer need not invert
97
83
  *
98
84
  * `onDelete` is declared on the CHILD — `Kid.parent @relation(…, onDelete:
99
- * Cascade)` — and fires when the PARENT is deleted. So an edge reads:
100
- * **deleting a `target` does `onDelete` to rows of `resource`.** A consumer
101
- * asking "what does deleting `X` remove?" filters `target === X && onDelete ===
102
- * "cascade"` and reads off `resource`; it does not have to re-derive which side
103
- * of a pair declared what.
85
+ * Cascade)` — and fires when the PARENT is deleted. So an edge reads: **deleting a
86
+ * `target` does `onDelete` to rows of `resource`.** A consumer asking "what does
87
+ * deleting `X` remove?" filters `target === X && onDelete === "cascade"` and reads
88
+ * off `resource`; it does not have to re-derive which side of a pair declared
89
+ * what.
104
90
  *
105
91
  * `null` on both members means this edge is not the owning side (it declares no
106
92
  * `fields:`), so the actions are on its partner — never that no action applies.
@@ -129,7 +115,7 @@ export type FieldProjectionInput = {
129
115
  readonly field: PrismaSchemaField;
130
116
  /** The model's other fields: the only way to identify an FK scalar by name. */
131
117
  readonly siblings: readonly PrismaSchemaField[];
132
- /** TYPE — what this client exposes. The existence gate (C-808). */
118
+ /** TYPE — what this client exposes. The existence gate. */
133
119
  readonly surface: PrismaResourceSurface;
134
120
  /** TYPE — `enums.ts`, which is what makes an enum name an enum name. */
135
121
  readonly enums: Readonly<Record<string, readonly string[]>>;
@@ -140,7 +126,7 @@ export type FieldProjection = {
140
126
  readonly outcome: "scalar";
141
127
  readonly name: string;
142
128
  readonly field: ProjectedScalarField;
143
- /** Recorded unknowns; a non-empty list FAILS the generate step (T-3.1 invariant 5). */
129
+ /** Recorded unknowns; a non-empty list FAILS the generate step. */
144
130
  readonly unknowns: readonly PrismaSchemaUnknown[];
145
131
  } | {
146
132
  readonly outcome: "relation";
@@ -173,8 +159,8 @@ export type PrismaDefaultOutcome =
173
159
  readonly traits: readonly AdapterLifecycleTrait[];
174
160
  };
175
161
  /**
176
- * T-3.1's `@default` table, looked up — never evaluated. The argument arrives as
177
- * opaque text precisely so that this function can be a lookup.
162
+ * The argument arrives as opaque text precisely so that this function can be a
163
+ * lookup.
178
164
  */
179
165
  export declare function matchPrismaDefault(argument: string): PrismaDefaultOutcome;
180
166
  /** Why a field is legitimately optional in `<M>UncheckedCreateInput`. */
@@ -2,74 +2,6 @@ import { LIFECYCLE_TRAITS, } from "@aventara/core";
2
2
  import { projectEnumBackedColumn } from "./enum.mapper.js";
3
3
  import { mapPrismaScalarField } from "./scalar.mapper.js";
4
4
  import { PRISMA_DEFAULT_TABLE, PRISMA_UNMATCHED_DEFAULT_TRAITS, } from "./vocabulary.dictionary.js";
5
- /**
6
- * Task 4 — one PSL field, answered as an `AdapterScalarField` or an
7
- * `AdapterRelationField` (C-808, C-811, C-813).
8
- *
9
- * # Nullability comes from PSL, and the required `Json` column is why
10
- *
11
- * `nullable` is `PrismaSchemaField.optional` — PSL's `?` — and **nothing else**
12
- * (T-3.1 invariant 4). The type surface cannot answer the question, and the
13
- * damage is not hypothetical: `$MetricPayload.scalars.sJson` resolves to
14
- * `null | string | number | false | true | JsonObject | JsonArray` while PSL says
15
- * `sJson Json`, required, because JSON null is a *value*. The reader's own facts
16
- * point the same wrong way — `JsonFilterBase.array_contains` admits `| null` and
17
- * `equals` admits `JsonNullValueFilter` on that same required column — so a
18
- * plausible "the operands mention null, therefore the column is nullable" rule
19
- * is wrong for **every** `Json` field while being right everywhere else. That is
20
- * the whole reason this rule is stated as a rule rather than left to taste.
21
- *
22
- * # The create-optional marker is a cross-check, never a source
23
- *
24
- * {@link crossCheckCreateOptional} is a separate function that *consumes* this
25
- * projection, and the data flows one way only: the marker never reaches
26
- * {@link projectPrismaField}. `<M>UncheckedCreateInput`'s `?` is **one bit over
27
- * several causes** — `@default(uuid())`, `@default(BRONZE)`, `@default(now())`,
28
- * `@updatedAt`, plain nullability, and a scalar list, which needs no value
29
- * because the empty list is one — and nothing on the 🟢 surface tells them
30
- * apart. Deriving a trait *from* the marker would invert the check into the bug
31
- * it exists to catch: it would make the two readers agree by construction and
32
- * detect nothing.
33
- *
34
- * # `@default` is matched, never parsed
35
- *
36
- * {@link matchPrismaDefault} walks `PRISMA_DEFAULT_TABLE` over the raw argument
37
- * text the PSL reader carried across unchanged. B1 deferred this matcher to this
38
- * task deliberately: the dictionary owns the data, and the lookup belongs where
39
- * a red test can drive it.
40
- *
41
- * The matcher adds **one** discriminant the dictionary's table does not state,
42
- * and it is a shape test rather than a parse: an argument that begins with an
43
- * identifier followed by `(` is a *generator form*, and anything else is a
44
- * *static value*. Both are `DEFAULTED`; only an unrecognised generator form
45
- * records an unknown. Without that split, `tier Tier @default(BRONZE)` — a field
46
- * of the Phase 8 schemas — would record an unknown and fail the generate step,
47
- * while C-811's own acceptance criteria say `@default("x")` is `["DEFAULTED"]`
48
- * with nothing recorded. The test never reads *inside* the argument, so
49
- * `dbgenerated("gen_random_uuid()")` remains a key and `"a, b // not a comment"`
50
- * remains opaque text.
51
- *
52
- * # A relation carries four members, and the FK is not one of them
53
- *
54
- * `target`, `cardinality`, `nullable`, and (from Task 6) `capabilities`. The
55
- * `@relation(fields: […], references: […])` scalars are read, used to identify
56
- * the FK column and check it against the edge's own optionality, and then
57
- * **discarded** (C-813). `@relation`'s *name* is not an Aventara concept either,
58
- * and its referential actions are not core's yet, so both travel beside the
59
- * projection as {@link PrismaRelationEdge} rather than inside it: pairing is
60
- * discovery-internal, and it is **PSL-only** — the name
61
- * appears nowhere on the 🟢 surface, so from the types alone `Author` has two
62
- * `Author` edges with nothing to pair them.
63
- *
64
- * # Two failure vocabularies, kept apart
65
- *
66
- * A **diagnostic** is about the schema: PSL declares a column the 🟢 types do
67
- * not expose (C-808's existence gate, whose fatality the generate step decides),
68
- * or a Prisma type name in no table. A **throw** is about the readers: the two
69
- * sources disagree about a fact they both state, which no schema can cause and
70
- * no diagnostic vocabulary describes. The types reader draws the same line.
71
- */
72
- /** A reader-versus-reader disagreement: not a schema problem, and not recoverable. */
73
5
  export class PrismaFieldProjectionError extends Error {
74
6
  name = "PrismaFieldProjectionError";
75
7
  }
@@ -78,16 +10,10 @@ export function projectPrismaField(input) {
78
10
  ? projectRelationField(input)
79
11
  : projectScalarField(input);
80
12
  }
81
- /* ------------------------------------------------------------------ *
82
- * Scalars *
83
- * ------------------------------------------------------------------ */
84
13
  function projectScalarField(input) {
85
14
  const { resource, field, surface, enums } = input;
86
15
  const mapped = mapPrismaScalarField({ resource, field, surface, enums });
87
16
  if (mapped.outcome === "excluded" || mapped.outcome === "unmapped") {
88
- // The existence gate and the unmapped-type diagnostic are the scalar
89
- // mapper's own answers, forwarded whole: restating them here would be a
90
- // second wording of one decision.
91
17
  return mapped;
92
18
  }
93
19
  const exposed = surface.scalarFields[field.name];
@@ -107,7 +33,6 @@ function projectScalarField(input) {
107
33
  kind: "scalar",
108
34
  type,
109
35
  ...(format === undefined ? {} : { format }),
110
- // PSL's `?`, and nothing else (T-3.1 invariant 4).
111
36
  nullable: field.optional,
112
37
  list: field.list,
113
38
  lifecycle: lifecycle.traits,
@@ -115,15 +40,6 @@ function projectScalarField(input) {
115
40
  unknowns: lifecycle.unknowns,
116
41
  };
117
42
  }
118
- /**
119
- * The `FieldRef` token against the written PSL type.
120
- *
121
- * **A token carries its list suffix** — `Post.tags` reads as `String[]`, not
122
- * `String` — so the suffix is split off before the names are compared and is
123
- * then the TYPE half of `list`. Comparing the raw token to the PSL type name
124
- * would make every list field look like a type mismatch, and a provider-drop
125
- * check built on that would fire on a field that is present.
126
- */
127
43
  function assertTokenAgrees(resource, field, token) {
128
44
  const listed = token.endsWith("[]");
129
45
  const base = listed ? token.slice(0, -2) : token;
@@ -134,23 +50,10 @@ function assertTokenAgrees(resource, field, token) {
134
50
  throw new PrismaFieldProjectionError(`${resource}.${field.name} is ${field.list ? "a list" : "not a list"} in schema.prisma and the 🟢 types carry the FieldRef token \`${token}\`, which says the opposite.`);
135
51
  }
136
52
  }
137
- /* ------------------------------------------------------------------ *
138
- * Lifecycle (C-811) *
139
- * ------------------------------------------------------------------ */
140
- /**
141
- * The three adapter traits in core's own declaration order, so a trait list has
142
- * one canonical order and it is not this module's invention. Order is semantic:
143
- * it enters the Contract hash.
144
- */
145
53
  const ADAPTER_TRAIT_ORDER = LIFECYCLE_TRAITS.filter((trait) => trait === "DEFAULTED" ||
146
54
  trait === "GENERATED_ON_CREATE" ||
147
55
  trait === "GENERATED_ON_UPDATE");
148
- /** An argument that begins with an identifier and an open paren: a generator form. */
149
56
  const GENERATOR_FORM = /^[A-Za-z_][A-Za-z0-9_]*\(/;
150
- /**
151
- * T-3.1's `@default` table, looked up — never evaluated. The argument arrives as
152
- * opaque text precisely so that this function can be a lookup.
153
- */
154
57
  export function matchPrismaDefault(argument) {
155
58
  for (const row of PRISMA_DEFAULT_TABLE) {
156
59
  const matched = "literal" in row.match
@@ -176,10 +79,6 @@ function projectLifecycle(resource, field) {
176
79
  present.add(trait);
177
80
  }
178
81
  if (outcome.form === "unrecognised-generator") {
179
- // Recorded as the same `PrismaSchemaUnknown` the PSL reader records, so
180
- // it fails the generate step by construction rather than through a second
181
- // rule written for it. An unrecognised generator function is a build stop,
182
- // not a column Aventara quietly decides the client must supply.
183
82
  unknowns.push({
184
83
  kind: "attribute-argument",
185
84
  spelling: "@default",
@@ -197,16 +96,6 @@ function projectLifecycle(resource, field) {
197
96
  unknowns,
198
97
  };
199
98
  }
200
- /**
201
- * TYPE's only contribution to lifecycle: a check, run in one direction.
202
- *
203
- * Every field the 🟢 `<M>UncheckedCreateInput` marks optional must carry a PSL
204
- * cause. A field the types mark optional with no cause means **one of the two
205
- * readers is wrong** — that is the entire signal, and it is why the marker is
206
- * never read as a trait. Names the types mark that this model does not project
207
- * as a scalar (relation edges) are skipped rather than reported: their
208
- * optionality is structural.
209
- */
210
99
  export function crossCheckCreateOptional(input) {
211
100
  const byName = new Map(input.fields.map((field) => [field.name, field]));
212
101
  const explained = [];
@@ -228,11 +117,7 @@ export function crossCheckCreateOptional(input) {
228
117
  return { explained, unexplained, messages };
229
118
  }
230
119
  function causeOf(projected) {
231
- // Lifecycle first, in core's order, so a field with both a default and a
232
- // nullable column reports the stronger cause.
233
120
  const trait = ADAPTER_TRAIT_ORDER.filter((candidate) => projected.lifecycle.includes(candidate));
234
- // `GENERATED_ON_CREATE` outranks the `DEFAULTED` that always accompanies it:
235
- // it is the more specific reason the client may omit the value.
236
121
  const strongest = trait.includes("GENERATED_ON_CREATE")
237
122
  ? "GENERATED_ON_CREATE"
238
123
  : trait[0];
@@ -244,9 +129,6 @@ function causeOf(projected) {
244
129
  }
245
130
  return projected.list ? "list" : null;
246
131
  }
247
- /* ------------------------------------------------------------------ *
248
- * Relations (C-813) *
249
- * ------------------------------------------------------------------ */
250
132
  function projectRelationField(input) {
251
133
  const { resource, field, siblings, surface } = input;
252
134
  const column = `${resource}.${field.name}`;
@@ -261,11 +143,7 @@ function projectRelationField(input) {
261
143
  };
262
144
  }
263
145
  const cardinality = field.list ? "many" : "one";
264
- // A to-many edge is a collection, and an absent collection is an empty one:
265
- // never nullable. A to-one edge is nullable exactly where PSL wrote `?`.
266
146
  const nullable = cardinality === "many" ? false : field.optional;
267
- // TYPE is the cross-check, from `$<M>Payload.objects`: `mentor:
268
- // $AuthorPayload | null`, `mentees: $AuthorPayload[]`.
269
147
  if (exposed.many !== field.list) {
270
148
  throw new PrismaFieldProjectionError(`${column} is ${field.list ? "a list" : "a single edge"} in schema.prisma and the 🟢 payload says the opposite.`);
271
149
  }
@@ -279,8 +157,6 @@ function projectRelationField(input) {
279
157
  return {
280
158
  outcome: "relation",
281
159
  name: field.name,
282
- // Four members. No target field schema, no FK, no join-table name, no
283
- // owning-side metadata (C-813).
284
160
  field: {
285
161
  kind: "relation",
286
162
  target: field.type,
@@ -293,31 +169,11 @@ function projectRelationField(input) {
293
169
  target: field.type,
294
170
  cardinality,
295
171
  relationName: field.relation?.name ?? null,
296
- // Carried, not projected: the action changes what a delete DOES, so it
297
- // is evidence a transaction planner needs, and it is not a member of
298
- // `AdapterRelationField` (see the edge's own doc).
299
172
  onDelete: field.relation?.onDelete ?? null,
300
173
  onUpdate: field.relation?.onUpdate ?? null,
301
174
  },
302
175
  };
303
176
  }
304
- /**
305
- * The FK, read and then discarded.
306
- *
307
- * `fields: […]` is used for exactly two things, both of them checks: each named
308
- * scalar must be a column this model declares, and an **optional FK forces an
309
- * optional edge**. PSL enforces the second itself — `prisma validate` 7.10.0
310
- * answers *"The relation field `author` uses the scalar fields authorId. At
311
- * least one of those fields is optional. Hence the relation field must be
312
- * optional as well."* — so a violation can only come from a reader defect.
313
- *
314
- * The converse does **not** hold, and assuming it would be wrong: `mentor
315
- * Author? @relation(fields: [mentorId])` with `mentorId String` **required** is
316
- * valid PSL at 7.10.0 and its client still types `mentor: $AuthorPayload |
317
- * null`. So the FK is not the source of nullability — the edge's own `?` is.
318
- *
319
- * Nothing about the FK enters the projection.
320
- */
321
177
  function assertForeignKeyAgrees(column, field, siblings, nullable) {
322
178
  for (const name of field.relation?.fields ?? []) {
323
179
  const scalar = siblings.find((sibling) => sibling.name === name);
@@ -329,21 +185,6 @@ function assertForeignKeyAgrees(column, field, siblings, nullable) {
329
185
  }
330
186
  }
331
187
  }
332
- /**
333
- * Pairs each edge with its back-reference, which **only PSL can do**.
334
- *
335
- * An explicit `@relation("Name")` is the pairing evidence, and for the two
336
- * `Author` → `Author` edges it is the *only* evidence: the name appears nowhere
337
- * on the 🟢 surface, so from the types alone there are two self-edges and
338
- * nothing to say they are one relation.
339
- *
340
- * Unnamed edges are paired by the unordered model pair, which is unambiguous
341
- * because PSL requires a name as soon as two relations connect the same two
342
- * models. Where an unordered pair still has more than two edges, they are left
343
- * unpaired rather than guessed: the implicit name Prisma would compute lives in
344
- * the 🛑 runtime data model, and re-deriving it here would be this module
345
- * reimplementing the generator.
346
- */
347
188
  export function pairPrismaRelationEdges(edges) {
348
189
  const groups = new Map();
349
190
  for (const edge of edges) {
@@ -375,8 +216,6 @@ export function pairPrismaRelationEdges(edges) {
375
216
  }
376
217
  pairs.push(sortKey(first) <= sortKey(second) ? [first, second] : [second, first]);
377
218
  }
378
- // Sorted by the first edge, so the answer's order is a property of the model
379
- // and not of how the groups happened to be keyed.
380
219
  return {
381
220
  pairs: [...pairs].sort((left, right) => sortKey(left[0]).localeCompare(sortKey(right[0]))),
382
221
  unpaired,
@@ -1,8 +1,6 @@
1
1
  import type { PrismaIdentifierAddress } from "./discovery-artifact.interface.js";
2
2
  import type { PrismaSchemaBlockIdentifier, PrismaSchemaModel } from "./schema-facts.interface.js";
3
3
  /**
4
- * PSL uniqueness → `AdapterIdentifier` tuples **[CORE]**: C-814 and C-815.
5
- *
6
4
  * # Why identity comes from PSL
7
5
  *
8
6
  * Because the generated types cannot state it. Measured on 2026-09-28 against
@@ -13,100 +11,70 @@ import type { PrismaSchemaBlockIdentifier, PrismaSchemaModel } from "./schema-fa
13
11
  * model Epsilon { code String @id name String }
14
12
  * ```
15
13
  *
16
- * `models/Delta.ts` and `models/Epsilon.ts` are **byte-identical** — equal
17
- * md5 after a case-insensitive `Delta|delta → MODEL|model` substitution and
18
- * after removing one 4-line `StringFieldUpdateOperationsInput` block, which is
19
- * the generator's first-file-wins dedup of a shared input type and lands in
20
- * whichever model file sorts first. Both declare their `AtLeast` key set as
21
- * `}, "code">` on the same line. A grep for `primaryKey|isId|isUnique` over
22
- * the 🟢 files of that client and of **both** Phase 8 clients is empty — not
23
- * only over the 🟢 files: over the whole generated tree, `internal/`
24
- * included.
14
+ * `models/Delta.ts` and `models/Epsilon.ts` are **byte-identical** — equal md5
15
+ * after a case-insensitive `Delta|delta → MODEL|model` substitution and after
16
+ * removing one 4-line `StringFieldUpdateOperationsInput` block, which is the
17
+ * generator's first-file-wins dedup of a shared input type and lands in whichever
18
+ * model file sorts first. Both declare their `AtLeast` key set as `}, "code">` on
19
+ * the same line.
25
20
  *
26
- * So `@id` and `@unique` are the same fact to TYPE, and a compound `@@id` is
27
- * the same fact as a compound `@@unique`. PSL is the only source that
28
- * distinguishes them.
21
+ * So `@id` and `@unique` are the same fact to TYPE, and a compound `@@id` is the
22
+ * same fact as a compound `@@unique`. PSL is the only source that distinguishes
23
+ * them.
29
24
  *
30
25
  * # Why that negative costs the model nothing
31
26
  *
32
27
  * `AdapterResource.identifiers` is `readonly AdapterIdentifier<TFields>[]`
33
28
  * **[CORE]** — an **unmarked set** of non-empty scalar field tuples with no
34
29
  * primary/secondary distinction. Aventara never asks which identifier is the
35
- * primary key, so a source that cannot tell `@id` from `@unique` withholds
36
- * nothing this mapper needs.
30
+ * primary key, so a source that cannot tell `@id` from `@unique` withholds nothing
31
+ * this mapper needs.
37
32
  *
38
33
  * # The types are a cross-check, and a one-sided one
39
34
  *
40
- * `PrismaResourceSurface.uniqueKeys` — `AtLeast<…, K>`'s key union — is
41
- * checked two-directionally and never read as a source (C-814): every
42
- * identifier's **stored** key must appear among the keys, and every key with no
43
- * identifier behind it must be explained by C-815 and by nothing else. Stored,
44
- * not re-derived: matching a key computed as `members.join("_")` is what used to
45
- * refuse a schema carrying `@@unique(…, name: "X")`, whose surface key is `X`.
46
- * Equality is unsatisfiable by construction, because C-815 omits a criterion
47
- * the generator still emits a key for. Measured: `Author` has three keys
48
- * (`"id" | "email" | "tenantId_handle"`, identical on both engines) and two
49
- * identifiers.
50
- *
51
- * What the two directions do **not** catch is this mapper keeping a criterion
52
- * C-815 rejects: an extra identifier only removes a key from the explained
53
- * set, so both directions still pass. Measured on 2026-09-28 — a build of this
54
- * mapper without the C-815 branch passes the cross-check on all eight
55
- * model/engine pairs. The cross-check guards the two readers against each
56
- * other; C-815 is pinned by its own cases.
35
+ * Stored, not re-derived: matching a key computed as `members.join("_")` is what
36
+ * used to refuse a schema carrying `@@unique(…, name: "X")`, whose surface key is
37
+ * `X`. Measured: `Author` has three keys (`"id" | "email" | "tenantId_handle"`,
38
+ * identical on both engines) and two identifiers.
57
39
  *
58
40
  * Its key **ORDERING is not used at all**. It appears to list the primary key
59
- * first, but that is an undocumented generator artifact, it is ordering inside
60
- * a union — which TypeScript does not preserve semantically — and it
61
- * degenerates completely when a model has exactly one unique criterion, the
62
- * common case. Nothing here may start depending on it.
41
+ * first, but that is an undocumented generator artifact, it is ordering inside a
42
+ * union — which TypeScript does not preserve semantically — and it degenerates
43
+ * completely when a model has exactly one unique criterion, the common case.
44
+ * Nothing here may start depending on it.
63
45
  *
64
46
  * # Nothing here sorts
65
47
  *
66
48
  * Member order is the attribute's list as written, never the field declaration
67
- * order (`@@unique([y, x])` is `["y", "x"]` even where `x` is declared first),
68
- * and the identifiers themselves come out in PSL's own order: field-level
69
- * criteria in field declaration order, then `@@id`, then `@@unique` as
70
- * written. Order enters the Contract hash (§19.2), so a deterministic order
71
- * derived from PSL is the point, not a convenience.
49
+ * order (`@@unique([y, x])` is `["y", "x"]` even where `x` is declared first), and
50
+ * the identifiers themselves come out in PSL's own order: field-level criteria in
51
+ * field declaration order, then `@@id`, then `@@unique` as written. Order enters
52
+ * the Contract hash, so a deterministic order derived from PSL is the point, not a
53
+ * convenience.
72
54
  */
73
55
  /** Which PSL spelling a criterion came from. Reported, and never used to rank one. */
74
56
  export type PrismaIdentifierSource = "@id" | "@unique" | "@@id" | "@@unique";
75
- /** One uniqueness criterion as PSL states it, before C-815 is applied. */
76
57
  export type PrismaUniquenessCriterion = {
77
58
  readonly source: PrismaIdentifierSource;
78
59
  /** Member order as written in the attribute's list. Never sorted. */
79
60
  readonly members: readonly string[];
80
61
  /**
81
- * The Prisma addressing key for this criterion — {@link prismaUniqueInputKey}
82
- * over the PSL facts, computed once here.
83
- *
84
- * Carried by the OMISSIONS too, and not only by the usable identifiers,
85
- * because the TYPE cross-check's second direction matches an `AtLeast` key
86
- * against the omission that explains it: a named criterion C-815 rejects is
87
- * keyed by its name on the generated surface, so an omission without its key
88
- * would leave that key unexplained and fail a schema Prisma accepts.
62
+ * The Prisma addressing key for this criterion — {@link prismaUniqueInputKey} over
63
+ * the PSL facts, computed once here.
89
64
  */
90
65
  readonly key: string;
91
66
  };
92
67
  /**
93
68
  * A criterion that is NOT a usable identifier, recorded with its reason.
94
69
  *
95
- * Recorded rather than dropped for two independent reasons: C-814 requires the
96
- * omission to be reported, and the TYPE cross-check needs it — the generator
97
- * emits an `AtLeast` key for a criterion C-815 rejects, and an unrecorded
98
- * omission would leave that key unexplained and indistinguishable from a
99
- * reader bug.
100
- *
101
- * A discriminated union, because the arms carry different evidence: which
102
- * members are nullable is not the same fact as which members are not scalar
103
- * columns at all.
70
+ * A discriminated union, because the arms carry different evidence: which members
71
+ * are nullable is not the same fact as which members are not scalar columns at
72
+ * all.
104
73
  *
105
- * These are not `PrismaDiscoveryDiagnostic`s. A diagnostic carries prose for
106
- * an engineer; the cross-check needs the member names as data.
74
+ * These are not `PrismaDiscoveryDiagnostic`s. A diagnostic carries prose for an
75
+ * engineer; the cross-check needs the member names as data.
107
76
  */
108
77
  export type PrismaIdentifierOmission = PrismaUniquenessCriterion & ({
109
- /** C-815: SQL uniqueness does not constrain NULLs. */
110
78
  readonly reason: "nullable-participant";
111
79
  readonly nullableMembers: readonly string[];
112
80
  } | {
@@ -151,20 +119,13 @@ export type PrismaIdentifierMapping = {
151
119
  * `WhereUniqueInput` accepts, and the key the generated `AtLeast<…, K>` union
152
120
  * lists.
153
121
  *
154
- * Computed **once**, here, at generate time — the architect's decision on
155
- * 2026-09-29: *"store it, less processing and less room for drifting."* Runtime
156
- * matches the stored key and never re-derives it.
157
- *
158
122
  * The rule, measured at 7.10.0 (regenerated 2026-09-29 over five models):
159
123
  *
160
- * | criterion | key | wraps |
161
- * | ---------------------------------- | --------- | ----- |
162
- * | `id String @id` / `@unique` | `id` | no |
163
- * | `@@unique([b, a])` | `b_a` | yes |
164
- * | `@@id([a, b])` | `a_b` | yes |
165
- * | `@@unique([b, a], name: "altKey")` | `altKey` | yes |
166
- * | `@@id([a, b], name: "pk")` | `pk` | yes |
167
- * | any of the above, `map: "…"` | unchanged | — |
124
+ * | criterion | key | wraps | | ---------------------------------- | --------- |
125
+ * ----- | | `id String @id` / `@unique` | `id` | no | | `@@unique([b, a])` | `b_a`
126
+ * | yes | | `@@id([a, b])` | `a_b` | yes | | `@@unique([b, a], name: "altKey")` |
127
+ * `altKey` | yes | | `@@id([a, b], name: "pk")` | `pk` | yes | | any of the above,
128
+ * `map: "…"` | unchanged | — |
168
129
  *
169
130
  * Two of those rows are why this is a function of the PSL facts and not of the
170
131
  * members alone. A custom `name:` **replaces** the positional alias rather than
@@ -174,8 +135,8 @@ export type PrismaIdentifierMapping = {
174
135
  * 2026-09-29; `map:` stays invisible and stays discarded).
175
136
  *
176
137
  * What this key is **not** is part of the identifier. `AdapterIdentifier` is a
177
- * tuple of field names with nowhere to put a name (C-814), and this key lives in
178
- * the artifact's translation table beside the model, never in it.
138
+ * tuple of field names with nowhere to put a name, and this key lives in the
139
+ * artifact's translation table beside the model, never in it.
179
140
  */
180
141
  export declare function prismaUniqueInputKey(uniqueness: PrismaSchemaBlockIdentifier): string;
181
142
  /**