@aventara/prisma7-adapter 0.1.0-pilot.0

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 (86) hide show
  1. package/LICENSE +91 -0
  2. package/LICENSE-ADDITIONAL-PERMISSION.md +9 -0
  3. package/README.md +564 -0
  4. package/dist/discovery/artifact.loader.d.ts +26 -0
  5. package/dist/discovery/artifact.loader.js +124 -0
  6. package/dist/discovery/capability-surface.interface.d.ts +191 -0
  7. package/dist/discovery/capability-surface.interface.js +13 -0
  8. package/dist/discovery/capability.deriver.d.ts +92 -0
  9. package/dist/discovery/capability.deriver.js +350 -0
  10. package/dist/discovery/discovery-artifact.interface.d.ts +210 -0
  11. package/dist/discovery/discovery-artifact.interface.js +1 -0
  12. package/dist/discovery/enum.mapper.d.ts +59 -0
  13. package/dist/discovery/enum.mapper.js +32 -0
  14. package/dist/discovery/field.mapper.d.ts +244 -0
  15. package/dist/discovery/field.mapper.js +387 -0
  16. package/dist/discovery/identifier.mapper.d.ts +188 -0
  17. package/dist/discovery/identifier.mapper.js +132 -0
  18. package/dist/discovery/model.discoverer.d.ts +108 -0
  19. package/dist/discovery/model.discoverer.js +341 -0
  20. package/dist/discovery/operations.deriver.d.ts +75 -0
  21. package/dist/discovery/operations.deriver.js +92 -0
  22. package/dist/discovery/scalar.mapper.d.ts +81 -0
  23. package/dist/discovery/scalar.mapper.js +51 -0
  24. package/dist/discovery/schema-facts.interface.d.ts +174 -0
  25. package/dist/discovery/schema-facts.interface.js +11 -0
  26. package/dist/discovery/schema.reader.d.ts +162 -0
  27. package/dist/discovery/schema.reader.js +692 -0
  28. package/dist/discovery/types.reader.d.ts +24 -0
  29. package/dist/discovery/types.reader.js +617 -0
  30. package/dist/discovery/vocabulary.dictionary.d.ts +258 -0
  31. package/dist/discovery/vocabulary.dictionary.js +429 -0
  32. package/dist/errors/error.normalizer.d.ts +250 -0
  33. package/dist/errors/error.normalizer.js +135 -0
  34. package/dist/errors/prisma-error.reader.d.ts +144 -0
  35. package/dist/errors/prisma-error.reader.js +167 -0
  36. package/dist/execution/first-variant.emulator.d.ts +64 -0
  37. package/dist/execution/first-variant.emulator.js +205 -0
  38. package/dist/execution/operation.executor.d.ts +87 -0
  39. package/dist/execution/operation.executor.js +210 -0
  40. package/dist/execution/result.shaper.d.ts +103 -0
  41. package/dist/execution/result.shaper.js +141 -0
  42. package/dist/execution/transaction.executor.d.ts +147 -0
  43. package/dist/execution/transaction.executor.js +13 -0
  44. package/dist/generate/artifact.emitter.d.ts +50 -0
  45. package/dist/generate/artifact.emitter.js +105 -0
  46. package/dist/generate/declared-support.reader.d.ts +33 -0
  47. package/dist/generate/declared-support.reader.js +74 -0
  48. package/dist/generate/discovery.bin.d.ts +2 -0
  49. package/dist/generate/discovery.bin.js +15 -0
  50. package/dist/generate/discovery.cli.d.ts +24 -0
  51. package/dist/generate/discovery.cli.js +163 -0
  52. package/dist/generate/existence.gate.d.ts +73 -0
  53. package/dist/generate/existence.gate.js +91 -0
  54. package/dist/generate/index.d.ts +81 -0
  55. package/dist/generate/index.js +65 -0
  56. package/dist/generate/node-version.guard.d.ts +8 -0
  57. package/dist/generate/node-version.guard.js +59 -0
  58. package/dist/generate/prisma-cli.resolver.d.ts +48 -0
  59. package/dist/generate/prisma-cli.resolver.js +95 -0
  60. package/dist/generate/prisma.generate.d.ts +164 -0
  61. package/dist/generate/prisma.generate.js +278 -0
  62. package/dist/generate/reader-crosscheck.d.ts +156 -0
  63. package/dist/generate/reader-crosscheck.js +202 -0
  64. package/dist/index.d.ts +5 -0
  65. package/dist/index.js +1 -0
  66. package/dist/prisma-client.interface.d.ts +48 -0
  67. package/dist/prisma-client.interface.js +9 -0
  68. package/dist/prisma.adapter.d.ts +55 -0
  69. package/dist/prisma.adapter.js +105 -0
  70. package/dist/provider/provider-profile.d.ts +53 -0
  71. package/dist/provider/provider-profile.js +91 -0
  72. package/dist/translation/identifier.addressing.d.ts +80 -0
  73. package/dist/translation/identifier.addressing.js +30 -0
  74. package/dist/translation/mutation.translator.d.ts +115 -0
  75. package/dist/translation/mutation.translator.js +580 -0
  76. package/dist/translation/operation.translator.d.ts +273 -0
  77. package/dist/translation/operation.translator.js +377 -0
  78. package/dist/translation/order-pagination.translator.d.ts +86 -0
  79. package/dist/translation/order-pagination.translator.js +159 -0
  80. package/dist/translation/projection.translator.d.ts +115 -0
  81. package/dist/translation/projection.translator.js +151 -0
  82. package/dist/translation/scalar.conversion.d.ts +101 -0
  83. package/dist/translation/scalar.conversion.js +221 -0
  84. package/dist/translation/where.translator.d.ts +79 -0
  85. package/dist/translation/where.translator.js +254 -0
  86. package/package.json +102 -0
@@ -0,0 +1,188 @@
1
+ import type { PrismaIdentifierAddress } from "./discovery-artifact.interface.js";
2
+ import type { PrismaSchemaBlockIdentifier, PrismaSchemaModel } from "./schema-facts.interface.js";
3
+ /**
4
+ * PSL uniqueness → `AdapterIdentifier` tuples **[CORE]**: C-814 and C-815.
5
+ *
6
+ * # Why identity comes from PSL
7
+ *
8
+ * Because the generated types cannot state it. Measured on 2026-09-28 against
9
+ * prisma 7.10.0, on a two-model scratch client generated for this purpose:
10
+ *
11
+ * ```prisma
12
+ * model Delta { code String @unique name String }
13
+ * model Epsilon { code String @id name String }
14
+ * ```
15
+ *
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.
25
+ *
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.
29
+ *
30
+ * # Why that negative costs the model nothing
31
+ *
32
+ * `AdapterResource.identifiers` is `readonly AdapterIdentifier<TFields>[]`
33
+ * **[CORE]** — an **unmarked set** of non-empty scalar field tuples with no
34
+ * 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.
37
+ *
38
+ * # The types are a cross-check, and a one-sided one
39
+ *
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.
57
+ *
58
+ * 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.
63
+ *
64
+ * # Nothing here sorts
65
+ *
66
+ * 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.
72
+ */
73
+ /** Which PSL spelling a criterion came from. Reported, and never used to rank one. */
74
+ export type PrismaIdentifierSource = "@id" | "@unique" | "@@id" | "@@unique";
75
+ /** One uniqueness criterion as PSL states it, before C-815 is applied. */
76
+ export type PrismaUniquenessCriterion = {
77
+ readonly source: PrismaIdentifierSource;
78
+ /** Member order as written in the attribute's list. Never sorted. */
79
+ readonly members: readonly string[];
80
+ /**
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.
89
+ */
90
+ readonly key: string;
91
+ };
92
+ /**
93
+ * A criterion that is NOT a usable identifier, recorded with its reason.
94
+ *
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.
104
+ *
105
+ * These are not `PrismaDiscoveryDiagnostic`s. A diagnostic carries prose for
106
+ * an engineer; the cross-check needs the member names as data.
107
+ */
108
+ export type PrismaIdentifierOmission = PrismaUniquenessCriterion & ({
109
+ /** C-815: SQL uniqueness does not constrain NULLs. */
110
+ readonly reason: "nullable-participant";
111
+ readonly nullableMembers: readonly string[];
112
+ } | {
113
+ /**
114
+ * A member that is not a scalar column this adapter can name: absent
115
+ * from the model, a relation field, a list, or `@ignore`d out of the
116
+ * generated client. Valid PSL cannot produce the first three; `@ignore`
117
+ * can, and keeping it would emit an identifier over a key no
118
+ * `AdapterFieldMap` carries.
119
+ */
120
+ readonly reason: "unresolved-participant";
121
+ readonly unresolvedMembers: readonly string[];
122
+ } | {
123
+ /** An empty member list: `AdapterIdentifier` is non-empty by type. */
124
+ readonly reason: "no-participants";
125
+ });
126
+ /**
127
+ * What one model's identity reads as: the usable criteria, each as the tuple
128
+ * core consumes **paired with** the Prisma key that addresses it, and why the
129
+ * rest are not usable.
130
+ *
131
+ * One list, two projections, which is the reason the pair is not split here:
132
+ * the composer takes `.members` for `AdapterResource.identifiers` (model
133
+ * content, hashed, Contract-bound) and the whole address for the artifact's
134
+ * translation table (adapter-private, unhashed). Two parallel lists would have
135
+ * to be kept in step; these cannot drift because they are one.
136
+ *
137
+ * `PrismaIdentifierAddress.members` is derived from `AdapterResource`, so a
138
+ * change in core's identifier type reaches this mapper as a compile error.
139
+ * Discovery is where the field map is being **built**, so the abstract form is
140
+ * what applies — `AdapterScalarFieldKey` widens to `string` through
141
+ * `AdapterFieldMap<M>`, making an identifier here a non-empty tuple of field
142
+ * names. The concrete keys are pinned later, in the emitted artifact
143
+ * (`PrismaDiscoveryArtifact<M>`).
144
+ */
145
+ export type PrismaIdentifierMapping = {
146
+ readonly identifiers: readonly PrismaIdentifierAddress[];
147
+ readonly omitted: readonly PrismaIdentifierOmission[];
148
+ };
149
+ /**
150
+ * The Prisma addressing key for a uniqueness criterion: the key a
151
+ * `WhereUniqueInput` accepts, and the key the generated `AtLeast<…, K>` union
152
+ * lists.
153
+ *
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
+ * The rule, measured at 7.10.0 (regenerated 2026-09-29 over five models):
159
+ *
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 | — |
168
+ *
169
+ * Two of those rows are why this is a function of the PSL facts and not of the
170
+ * members alone. A custom `name:` **replaces** the positional alias rather than
171
+ * adding to it, so `members.join("_")` is simply wrong for a named criterion —
172
+ * which is precisely what made the cross-check refuse a schema Prisma accepts,
173
+ * reporting a correctly-discovered identifier as an invented constraint (fixed
174
+ * 2026-09-29; `map:` stays invisible and stays discarded).
175
+ *
176
+ * 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.
179
+ */
180
+ export declare function prismaUniqueInputKey(uniqueness: PrismaSchemaBlockIdentifier): string;
181
+ /**
182
+ * One model's identity, read from PSL alone.
183
+ *
184
+ * A pure function of plain data: no generated client, no `@prisma/client`
185
+ * import, and no TYPE input — which is what lets the cross-check be an
186
+ * independent second opinion rather than a tautology.
187
+ */
188
+ export declare function mapPrismaIdentifiers(model: PrismaSchemaModel): PrismaIdentifierMapping;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * The Prisma addressing key for a uniqueness criterion: the key a
3
+ * `WhereUniqueInput` accepts, and the key the generated `AtLeast<…, K>` union
4
+ * lists.
5
+ *
6
+ * Computed **once**, here, at generate time — the architect's decision on
7
+ * 2026-09-29: *"store it, less processing and less room for drifting."* Runtime
8
+ * matches the stored key and never re-derives it.
9
+ *
10
+ * The rule, measured at 7.10.0 (regenerated 2026-09-29 over five models):
11
+ *
12
+ * | criterion | key | wraps |
13
+ * | ---------------------------------- | --------- | ----- |
14
+ * | `id String @id` / `@unique` | `id` | no |
15
+ * | `@@unique([b, a])` | `b_a` | yes |
16
+ * | `@@id([a, b])` | `a_b` | yes |
17
+ * | `@@unique([b, a], name: "altKey")` | `altKey` | yes |
18
+ * | `@@id([a, b], name: "pk")` | `pk` | yes |
19
+ * | any of the above, `map: "…"` | unchanged | — |
20
+ *
21
+ * Two of those rows are why this is a function of the PSL facts and not of the
22
+ * members alone. A custom `name:` **replaces** the positional alias rather than
23
+ * adding to it, so `members.join("_")` is simply wrong for a named criterion —
24
+ * which is precisely what made the cross-check refuse a schema Prisma accepts,
25
+ * reporting a correctly-discovered identifier as an invented constraint (fixed
26
+ * 2026-09-29; `map:` stays invisible and stays discarded).
27
+ *
28
+ * What this key is **not** is part of the identifier. `AdapterIdentifier` is a
29
+ * tuple of field names with nowhere to put a name (C-814), and this key lives in
30
+ * the artifact's translation table beside the model, never in it.
31
+ */
32
+ export function prismaUniqueInputKey(uniqueness) {
33
+ return uniqueness.name ?? uniqueness.members.join("_");
34
+ }
35
+ /**
36
+ * Every uniqueness criterion PSL states, in PSL's own order. A field spelled
37
+ * `@id @unique` yields two criteria with the same key; the caller keeps the
38
+ * first, so `@id` wins and one column does not become two identifiers.
39
+ */
40
+ function* uniquenessCriteria(model) {
41
+ const criterion = (source, uniqueness) => ({
42
+ source,
43
+ members: uniqueness.members,
44
+ key: prismaUniqueInputKey(uniqueness),
45
+ });
46
+ for (const field of model.fields) {
47
+ // A field-level criterion has no `name:` to carry: PSL gives `@id` and
48
+ // `@unique` no such argument, and Prisma keys them by the column.
49
+ if (field.id) {
50
+ yield criterion("@id", { members: [field.name], name: null });
51
+ }
52
+ if (field.unique) {
53
+ yield criterion("@unique", { members: [field.name], name: null });
54
+ }
55
+ }
56
+ if (model.blockId !== null) {
57
+ yield criterion("@@id", model.blockId);
58
+ }
59
+ for (const blockUnique of model.blockUniques) {
60
+ yield criterion("@@unique", blockUnique);
61
+ }
62
+ }
63
+ /**
64
+ * One model's identity, read from PSL alone.
65
+ *
66
+ * A pure function of plain data: no generated client, no `@prisma/client`
67
+ * import, and no TYPE input — which is what lets the cross-check be an
68
+ * independent second opinion rather than a tautology.
69
+ */
70
+ export function mapPrismaIdentifiers(model) {
71
+ // The scalar columns this adapter can actually name: a relation field, a list
72
+ // and an `@ignore`d column are all keys no `AdapterFieldMap` will carry.
73
+ const columns = new Map();
74
+ for (const field of model.fields) {
75
+ if (field.relation !== null || field.list || field.ignored) {
76
+ continue;
77
+ }
78
+ columns.set(field.name, field);
79
+ }
80
+ const identifiers = [];
81
+ const omitted = [];
82
+ // Deduplication is on the MEMBER TUPLE, not on the addressing key: the tuple
83
+ // is what `@id @unique` duplicates, and it identifies a criterion injectively,
84
+ // while a key does not — a custom `name:` can collide with a field name, and
85
+ // keying the seen-set would then silently drop a different criterion.
86
+ const seen = new Set();
87
+ for (const criterion of uniquenessCriteria(model)) {
88
+ const tuple = JSON.stringify(criterion.members);
89
+ if (seen.has(tuple)) {
90
+ continue;
91
+ }
92
+ seen.add(tuple);
93
+ const [head, ...tail] = criterion.members;
94
+ if (head === undefined) {
95
+ omitted.push({ ...criterion, reason: "no-participants" });
96
+ continue;
97
+ }
98
+ const unresolvedMembers = criterion.members.filter((member) => !columns.has(member));
99
+ if (unresolvedMembers.length > 0) {
100
+ omitted.push({
101
+ ...criterion,
102
+ reason: "unresolved-participant",
103
+ unresolvedMembers,
104
+ });
105
+ continue;
106
+ }
107
+ // C-815. SQL uniqueness does not constrain NULLs, so a criterion with any
108
+ // nullable participant does not guarantee AT MOST ONE row and is not an
109
+ // `Identifier`. Recorded, never dropped: the generator still emits an
110
+ // `AtLeast` key for it, and the TYPE cross-check needs the omission to
111
+ // explain that key.
112
+ //
113
+ // C-815 is typed a Derived Decision because no source states it. Prisma
114
+ // states the same rule from its own side, which is the nearest thing to a
115
+ // source there is: a model whose ONLY criterion has a nullable participant
116
+ // is rejected at validate time with P1012, "Each model must have at least
117
+ // one unique criteria that has only required fields … The following unique
118
+ // criterias were not considered as they contain fields that are not
119
+ // required: - a, b" (7.10.0, measured 2026-09-28).
120
+ const nullableMembers = criterion.members.filter((member) => columns.get(member)?.optional === true);
121
+ if (nullableMembers.length > 0) {
122
+ omitted.push({
123
+ ...criterion,
124
+ reason: "nullable-participant",
125
+ nullableMembers,
126
+ });
127
+ continue;
128
+ }
129
+ identifiers.push({ members: [head, ...tail], key: criterion.key });
130
+ }
131
+ return { identifiers, omitted };
132
+ }
@@ -0,0 +1,108 @@
1
+ import { type TransactionCapability } from "@aventara/core";
2
+ import type { PrismaComposedArtifact, PrismaModelComposer } from "../generate/prisma.generate.js";
3
+ import type { PrismaCapabilitySurface } from "./capability-surface.interface.js";
4
+ import type { PrismaSchemaFacts } from "./schema-facts.interface.js";
5
+ /**
6
+ * Task 6 — **composition**: the one place the two readers' facts become the two
7
+ * halves of the discovery artifact.
8
+ *
9
+ * Every rule it applies belongs to somebody else. That is the design, not a
10
+ * shortcut: the scalar table is `scalar.mapper.ts`'s, the field projection and the
11
+ * lifecycle traits are `field.mapper.ts`'s, identity is `identifier.mapper.ts`'s,
12
+ * capabilities are `capability.deriver.ts`'s, the §8.3 variant gate is
13
+ * `operations.deriver.ts`'s and nowhere else, and the enum comparison is
14
+ * `enum.mapper.ts`'s. This module **orders and assembles**, and it owns exactly
15
+ * two judgements of its own:
16
+ *
17
+ * 1. **Which diagnostics are fatal.** A mapper answers with a diagnostic; whether
18
+ * the build stops is composition's call, because only composition knows that a
19
+ * model with an unmapped column cannot produce a Contract at all.
20
+ * 2. **The scalar registry.** `ScalarRegistry` is the set of Aventara scalar
21
+ * identities the discovered fields actually use — a fact no single field can
22
+ * state and only the whole pass can.
23
+ *
24
+ * # Three halves, from one pass over one field list
25
+ *
26
+ * `model` is core's: canonicalized, hashed, compiled into the Contract.
27
+ * `translation` is the adapter's Prisma addressing, and `referentialActions` is
28
+ * what the resolved `@relation(onDelete:/onUpdate:)` arguments say. All three are
29
+ * built from the SAME pass: the translation table from the same
30
+ * `mapPrismaIdentifiers` answer the model's `identifiers` come from — `.members`
31
+ * into the model, the whole address into the table — and the action table from the
32
+ * same `projectPrismaField` answer the model's relation fields come from. Two
33
+ * parallel passes would have to be kept in step; one pass cannot drift.
34
+ *
35
+ * `PrismaTranslationTable<M>` and `PrismaReferentialActionTable<M>` are mapped
36
+ * types over the model's resources, so a resource this module adds to the model
37
+ * and forgets to address — or forgets to answer the actions for — is a **compile
38
+ * error** rather than a runtime `undefined` at the first unique read or the first
39
+ * cascade question.
40
+ *
41
+ * The action table carries **only owning edges**, because only an owning edge has
42
+ * an action: PSL forbids the back-reference from declaring one, so the
43
+ * back-reference's answer is its partner's. Its direction is the one documented on
44
+ * `PrismaReferentialEdge` and on `field.mapper.ts`'s `PrismaRelationEdge` —
45
+ * *deleting a `target` does `onDelete` to rows of the enclosing resource* — and it
46
+ * is carried out of field projection unchanged rather than re-derived here.
47
+ *
48
+ * # No provider reaches this module, and that is enforced by the input
49
+ *
50
+ * The composer is created with a {@link TransactionCapability} and nothing else.
51
+ * `provider` is not a member anywhere on the path, so `if (provider === …)` is a
52
+ * compile error rather than a convention (dictionary invariant 6, D-802). The
53
+ * generate step keeps `provider` only to stamp the artifact, which is a recorded
54
+ * fact and not a branch.
55
+ *
56
+ * `transactions` is a **driver** fact (T-2) and the driver is a runtime choice, so
57
+ * it is declared to the composer at generate time. The two cannot silently
58
+ * disagree: an artifact that says `"interactive"` loaded by an adapter whose
59
+ * declared driver says `"none"` mounts no `transaction`, and core refuses
60
+ * construction with `DISCOVERY_TRANSACTION_CAPABILITY_MISMATCH` **[CORE,
61
+ * framework.factory.ts]** — a hard, legible failure rather than a silent one.
62
+ *
63
+ * # The C-818 conflict this header used to record is closed
64
+ *
65
+ * It read: a relation carries create directives for `create.one` and none for
66
+ * `create.many` **[DOC7]**, `AdapterRelationField.capabilities` is one record per
67
+ * field with a single `create` member, so composition had to ask
68
+ * `deriveRelationCapabilities` for the `"create.one"` answer — the permissive one —
69
+ * and C-818's restriction was unrepresentable in the Contract.
70
+ *
71
+ * The capability members are **scope-qualified** now, so the one record holds both
72
+ * answers: the create stage is advertised at the `nested` write scope and not at the
73
+ * bulk one. Composition asks for no variant, passes no `operation`, and advertises
74
+ * nothing it cannot honour. The mutation translator keeps its refusal — it is the
75
+ * belt to core's braces, not the only guard (C-855) — but it is no longer the ONLY
76
+ * place the restriction exists.
77
+ */
78
+ /** Composition refuses: the collected diagnostics, named and reported together. */
79
+ export declare class PrismaModelCompositionError extends Error {
80
+ readonly name = "PrismaModelCompositionError";
81
+ readonly diagnostics: readonly string[];
82
+ constructor(diagnostics: readonly string[]);
83
+ }
84
+ export type PrismaModelCompositionOptions = {
85
+ /**
86
+ * The driver's transaction capability (T-2). The ONLY declared fact
87
+ * composition accepts, and deliberately not the whole profile: a
88
+ * `PrismaProviderProfile` carries `provider`.
89
+ */
90
+ readonly transactions: TransactionCapability;
91
+ };
92
+ export type PrismaModelDiscoveryInput = PrismaModelCompositionOptions & {
93
+ /** PSL — what the schema says. */
94
+ readonly facts: PrismaSchemaFacts;
95
+ /** TYPE — what this client can do. */
96
+ readonly surface: PrismaCapabilitySurface;
97
+ /** Resource names the existence gate admitted, in PSL declaration order. */
98
+ readonly admitted: readonly string[];
99
+ };
100
+ /**
101
+ * The generate step's `composeModel` seam, filled.
102
+ *
103
+ * A factory rather than a bare function because the step's seam takes only the
104
+ * readers' output — order and refusal over external effects is the step's job, and
105
+ * the declared driver fact is the caller's.
106
+ */
107
+ export declare function createPrismaModelComposer(options: PrismaModelCompositionOptions): PrismaModelComposer;
108
+ export declare function discoverPrismaModel(input: PrismaModelDiscoveryInput): PrismaComposedArtifact;