@aventara/prisma7-adapter 0.1.0-pilot.3 → 0.1.0-pilot.4

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 (48) hide show
  1. package/README.md +22 -22
  2. package/dist/discovery/artifact.loader.d.ts +2 -7
  3. package/dist/discovery/artifact.loader.js +4 -0
  4. package/dist/discovery/capability-surface.interface.d.ts +2 -66
  5. package/dist/discovery/capability.deriver.d.ts +2 -47
  6. package/dist/discovery/discovery-artifact.interface.d.ts +34 -155
  7. package/dist/discovery/discovery-artifact.interface.js +1 -1
  8. package/dist/discovery/enum.mapper.d.ts +0 -24
  9. package/dist/discovery/field.mapper.d.ts +3 -114
  10. package/dist/discovery/field.mapper.js +6 -6
  11. package/dist/discovery/identifier.mapper.d.ts +4 -84
  12. package/dist/discovery/model.discoverer.d.ts +2 -69
  13. package/dist/discovery/operations.deriver.d.ts +0 -36
  14. package/dist/discovery/scalar.mapper.d.ts +1 -32
  15. package/dist/discovery/scalar.mapper.js +2 -2
  16. package/dist/discovery/schema-facts.interface.d.ts +3 -83
  17. package/dist/discovery/schema.reader.d.ts +0 -133
  18. package/dist/discovery/types.reader.d.ts +1 -9
  19. package/dist/discovery/types.reader.js +7 -7
  20. package/dist/discovery/vocabulary.dictionary.d.ts +6 -133
  21. package/dist/discovery/vocabulary.dictionary.js +2 -2
  22. package/dist/errors/error.normalizer.d.ts +5 -168
  23. package/dist/errors/prisma-error.reader.d.ts +1 -93
  24. package/dist/execution/first-variant.emulator.d.ts +5 -28
  25. package/dist/execution/operation.executor.d.ts +1 -62
  26. package/dist/execution/result.shaper.d.ts +0 -56
  27. package/dist/execution/transaction.executor.d.ts +0 -98
  28. package/dist/generate/artifact.emitter.d.ts +1 -35
  29. package/dist/generate/artifact.emitter.js +26 -43
  30. package/dist/generate/declared-support.reader.d.ts +0 -20
  31. package/dist/generate/discovery.cli.d.ts +0 -4
  32. package/dist/generate/existence.gate.d.ts +3 -37
  33. package/dist/generate/index.d.ts +4 -57
  34. package/dist/generate/prisma-cli.resolver.d.ts +0 -22
  35. package/dist/generate/prisma.generate.d.ts +14 -83
  36. package/dist/generate/reader-crosscheck.d.ts +3 -88
  37. package/dist/generate/reader-crosscheck.js +4 -4
  38. package/dist/prisma-client.interface.d.ts +5 -25
  39. package/dist/prisma.adapter.d.ts +21 -22
  40. package/dist/provider/provider-profile.d.ts +6 -14
  41. package/dist/translation/identifier.addressing.d.ts +0 -38
  42. package/dist/translation/mutation.translator.d.ts +5 -51
  43. package/dist/translation/operation.translator.d.ts +1 -102
  44. package/dist/translation/order-pagination.translator.d.ts +1 -47
  45. package/dist/translation/projection.translator.d.ts +1 -46
  46. package/dist/translation/scalar.conversion.d.ts +0 -64
  47. package/dist/translation/where.translator.d.ts +0 -52
  48. package/package.json +2 -2
@@ -2,74 +2,13 @@ import { type TransactionCapability } from "@aventara/core";
2
2
  import type { PrismaComposedArtifact, PrismaModelComposer } from "../generate/prisma.generate.js";
3
3
  import type { PrismaCapabilitySurface } from "./capability-surface.interface.js";
4
4
  import type { PrismaSchemaFacts } from "./schema-facts.interface.js";
5
- /**
6
- * Every rule it applies belongs to somebody else. This module **orders and
7
- * assembles**, and it owns exactly two judgements of its own:
8
- *
9
- * 1. **Which diagnostics are fatal.** A mapper answers with a diagnostic; whether
10
- * the build stops is composition's call, because only composition knows that a
11
- * model with an unmapped column cannot produce a Contract at all.
12
- * 2. **The scalar registry.** `ScalarRegistry` is the set of Aventara scalar
13
- * identities the discovered fields actually use — a fact no single field can
14
- * state and only the whole pass can.
15
- *
16
- * # Three halves, from one pass over one field list
17
- *
18
- * `model` is core's: canonicalized, hashed, compiled into the Contract.
19
- * `translation` is the adapter's Prisma addressing, and `referentialActions` is
20
- * what the resolved `@relation(onDelete:/onUpdate:)` arguments say. All three are
21
- * built from the SAME pass: the translation table from the same
22
- * `mapPrismaIdentifiers` answer the model's `identifiers` come from — `.members`
23
- * into the model, the whole address into the table — and the action table from the
24
- * same `projectPrismaField` answer the model's relation fields come from. Two
25
- * parallel passes would have to be kept in step; one pass cannot drift.
26
- *
27
- * `PrismaTranslationTable<M>` and `PrismaReferentialActionTable<M>` are mapped
28
- * types over the model's resources, so a resource this module adds to the model
29
- * and forgets to address — or forgets to answer the actions for — is a **compile
30
- * error** rather than a runtime `undefined` at the first unique read or the first
31
- * cascade question.
32
- *
33
- * The action table carries **only owning edges**, because only an owning edge has
34
- * an action: PSL forbids the back-reference from declaring one, so the
35
- * back-reference's answer is its partner's. Its direction is the one documented on
36
- * `PrismaReferentialEdge` and on `field.mapper.ts`'s `PrismaRelationEdge` —
37
- * *deleting a `target` does `onDelete` to rows of the enclosing resource* — and it
38
- * is carried out of field projection unchanged rather than re-derived here.
39
- *
40
- * # No provider reaches this module, and that is enforced by the input
41
- *
42
- * The composer is created with a {@link TransactionCapability} and nothing else.
43
- * `provider` is not a member anywhere on the path, so `if (provider === …)` is a
44
- * compile error rather than a convention. The generate step keeps `provider` only
45
- * to stamp the artifact, which is a recorded fact and not a branch.
46
- *
47
- * `transactions` is a **driver** fact and the driver is a runtime choice, so it is
48
- * declared to the composer at generate time. The two cannot silently disagree: an
49
- * artifact that says `"interactive"` loaded by an adapter whose declared driver
50
- * says `"none"` mounts no `transaction`, and core refuses construction with
51
- * `DISCOVERY_TRANSACTION_CAPABILITY_MISMATCH` **[CORE, framework.factory.ts]** — a
52
- * hard, legible failure rather than a silent one.
53
- *
54
- * The capability members are **scope-qualified** now, so the one record holds both
55
- * answers: the create stage is advertised at the `nested` write scope and not at
56
- * the bulk one. Composition asks for no variant, passes no `operation`, and
57
- * advertises nothing it cannot honour. The mutation translator keeps its refusal —
58
- * it is the belt to core's braces, not the only guard — but it is no longer the
59
- * ONLY place the restriction exists.
60
- */
61
- /** Composition refuses: the collected diagnostics, named and reported together. */
5
+ /** Thrown when the schema cannot be composed into a model; lists every problem. */
62
6
  export declare class PrismaModelCompositionError extends Error {
63
7
  readonly name = "PrismaModelCompositionError";
64
8
  readonly diagnostics: readonly string[];
65
9
  constructor(diagnostics: readonly string[]);
66
10
  }
67
11
  export type PrismaModelCompositionOptions = {
68
- /**
69
- * The driver's transaction capability. The ONLY declared fact composition accepts,
70
- * and deliberately not the whole profile: a `PrismaProviderProfile` carries
71
- * `provider`.
72
- */
73
12
  readonly transactions: TransactionCapability;
74
13
  };
75
14
  export type PrismaModelDiscoveryInput = PrismaModelCompositionOptions & {
@@ -80,12 +19,6 @@ export type PrismaModelDiscoveryInput = PrismaModelCompositionOptions & {
80
19
  /** Resource names the existence gate admitted, in PSL declaration order. */
81
20
  readonly admitted: readonly string[];
82
21
  };
83
- /**
84
- * The generate step's `composeModel` seam, filled.
85
- *
86
- * A factory rather than a bare function because the step's seam takes only the
87
- * readers' output — order and refusal over external effects is the step's job, and
88
- * the declared driver fact is the caller's.
89
- */
22
+ /** Creates the composer `runPrismaGenerateStep` needs, for one transaction capability. */
90
23
  export declare function createPrismaModelComposer(options: PrismaModelCompositionOptions): PrismaModelComposer;
91
24
  export declare function discoverPrismaModel(input: PrismaModelDiscoveryInput): PrismaComposedArtifact;
@@ -1,46 +1,10 @@
1
1
  import type { AdapterResource, ResourceOperations, TransactionCapability } from "@aventara/core";
2
2
  import type { PrismaAdapterModel } from "./discovery-artifact.interface.js";
3
- /**
4
- * No mapper, no discoverer, no emitter and no executor may drop a variant, because
5
- * an omission in two places is an omission nobody can audit.
6
- *
7
- * # Three inputs, and `provider` is not one of them
8
- *
9
- * ```text
10
- * findUnique, findUniqueOrThrow, findFirst, findFirstOrThrow, findMany, create,
11
- * createMany, createManyAndReturn, delete, update, deleteMany, updateMany,
12
- * updateManyAndReturn, upsert, count, aggregate, groupBy
13
- * ```
14
- *
15
- * The second input is the model's identifiers, because `find.unique`,
16
- * `update.unique`, `delete.unique` and `upsert.unique` are meaningless without
17
- * one. The third is `transactions` — the **only** declared profile row this gate
18
- * needs. The whole `PrismaProviderProfile` is deliberately NOT accepted: it
19
- * carries `provider`, and a module that cannot be handed a provider cannot branch
20
- * on one (dictionary invariant 6). `isolationLevels` is not a precondition of any
21
- * variant.
22
- *
23
- * `supportsEmulatedFirstVariants` is **derived, exactly `transactions !== "none"`,
24
- * and stored nowhere**. A stored copy would be a second source of truth for a
25
- * one-line derivation.
26
- *
27
- * # What is NOT gated here, and why that is deliberate
28
- *
29
- * So `update.first` reads `emulatedFirst && exposes("update")` and `delete.first`
30
- * reads `emulatedFirst && exposes("delete")`. Under Route B those cells would read
31
- * `updateManyAndReturn` and `deleteMany` instead — which is exactly why the write
32
- * call is NOT folded into `emulatedFirst`: a shared precondition naming one
33
- * route's call would assert a gate the other route does not have, an assertion
34
- * that cannot fail for the right reason.
35
- */
36
- /** Core's own identifier tuples for a discovered Resource. */
37
3
  type DiscoveredIdentifiers = AdapterResource<PrismaAdapterModel>["identifiers"];
38
4
  export type ResourceOperationsInput = {
39
- /** `<M>Delegate`'s own methods, as the types reader read them. */
40
5
  readonly operations: readonly string[];
41
6
  /** The model's usable identifiers; empty means no unique addressing exists. */
42
7
  readonly identifiers: DiscoveredIdentifiers;
43
- /** The one declared profile row this gate reads. */
44
8
  readonly transactions: TransactionCapability;
45
9
  };
46
10
  export declare function deriveResourceOperations(input: ResourceOperationsInput): ResourceOperations;
@@ -1,43 +1,12 @@
1
1
  import type { PrismaResourceSurface } from "./capability-surface.interface.js";
2
2
  import type { PrismaSchemaField } from "./schema-facts.interface.js";
3
3
  import { type PrismaDiscoveryDiagnostic, type PrismaScalarMapping } from "./vocabulary.dictionary.js";
4
- /**
5
- * # Four answers, no fifth, and no guess
6
- *
7
- * The outcome is a closed union because every way this can end is a decision
8
- * somebody has to be able to read back:
9
- *
10
- * - `scalar` — the Prisma type name had a row in the dictionary's total table.
11
- * - `enum` — the written type is an enum `enums.ts` confirms. `enum.mapper.ts`'s
12
- * `projectEnumBackedColumn` is where that becomes a field type ref.
13
- * - `excluded` — PSL declares the column and the 🟢 types do not expose it.
14
- * - `unmapped` — a type name in neither table. A **diagnostic**, never a silent
15
- * drop and never a nearest-neighbour guess.
16
- *
17
- * # Why `format` is emitted here at all — and why so little of it is
18
- *
19
- * What the plan row was *right* about is that most of what looks like a format is
20
- * policy. `format` is **enforced**: `normalizeScalarFieldValue` turns `format:
21
- * "uuid"` into a `V1003` rejection of request input **[CORE,
22
- * `runtime/scalar-runtime.ts`]**. So the bar for discovering one is not "the
23
- * schema suggests it" but "the column itself rejects what Aventara would then
24
- * reject". That intent belongs in `FrameworkScalarFieldConfig.format` **[CORE]**,
25
- * which the Contract compiler already layers over the adapter's value.
26
- *
27
- * That is the correct answer, not a gap.
28
- *
29
- * # Nothing here branches on a provider
30
- *
31
- * `@db.Uuid` is unwritable on SQLite, so the SQLite schema simply never carries it
32
- * and the same table gives the same right answer on both engines without a
33
- * provider arm.
34
- */
35
4
  export type ScalarMappingInput = {
36
5
  /** The PSL model name, carried so a diagnostic can name the column in full. */
37
6
  readonly resource: string;
38
7
  /** PSL — what the column IS. */
39
8
  readonly field: PrismaSchemaField;
40
- /** TYPE — what this client exposes. The existence gate. */
9
+ /** TYPE — what this client exposes. */
41
10
  readonly surface: PrismaResourceSurface;
42
11
  /** TYPE — `enums.ts`, which is what makes an enum name an enum name. */
43
12
  readonly enums: Readonly<Record<string, readonly string[]>>;
@@ -7,7 +7,7 @@ export function mapPrismaScalarField(input) {
7
7
  outcome: "excluded",
8
8
  diagnostic: {
9
9
  code: "not-exposed",
10
- message: `${column} is declared in schema.prisma and absent from the generated 🟢 types, so it is omitted: ${explainAbsence(field, surface)}`,
10
+ message: `${column} is declared in schema.prisma and absent from the generated client's types, so it is omitted: ${explainAbsence(field, surface)}`,
11
11
  },
12
12
  };
13
13
  }
@@ -37,7 +37,7 @@ function explainAbsence(field, surface) {
37
37
  return "it carries `@ignore`";
38
38
  }
39
39
  if (Object.hasOwn(surface.relationFields, field.name)) {
40
- return "the 🟢 types expose it as a relation edge, not a scalar column";
40
+ return "the generated client's types expose it as a relation edge, not a scalar column";
41
41
  }
42
42
  return "no schema attribute explains it — a provider drop, an `Unsupported(…)` column or a preview-feature exclusion";
43
43
  }
@@ -1,15 +1,6 @@
1
- /**
2
- * Nothing here is imported from `@prisma/client`: these are this adapter's own
3
- * facts over schema text, which is what keeps discovery a pure function of plain
4
- * data and unit-testable without a generated client.
5
- *
6
- * Read `schema.reader.ts`'s header for the boundary these facts are drawn on: the
7
- * reader **locates** tokens and never **evaluates** them.
8
- */
9
1
  import type { PrismaReferentialAction } from "./discovery-artifact.interface.js";
10
2
  /** Where a fact — or an unhandled construct — was found. One-based lines, as `prisma validate` reports them. */
11
3
  export type PrismaSchemaSite = {
12
- /** The schema file as the caller named it; a folder read names each member file. */
13
4
  readonly file: string;
14
5
  readonly line: number;
15
6
  };
@@ -17,17 +8,6 @@ export type PrismaSchemaSite = {
17
8
  * A construct the reader **does not recognise at all** — bucket 3 of the reader's
18
9
  * three-way classification: a `type`/`view` block, an unrecognised `@relation`
19
10
  * argument, or any attribute spelling added after 7.10.0.
20
- *
21
- * `@map`, `@@map`, `@@index` and `@@id`/`@@unique`'s `map:` are **not** here: they
22
- * are recognised and deliberately discarded (bucket 2, the reader's explicit
23
- * `DISCARDED_ATTRIBUTES` list), because their values appear nowhere on the 🟢
24
- * generated surface. Recording them would fail the generate step on any schema
25
- * that maps a column name. `name:` is **not** discarded — see {@link
26
- * PrismaSchemaBlockIdentifier}.
27
- *
28
- * Bucket 3 is recorded rather than dropped because silently dropping an
29
- * unrecognised attribute is how a grammar change becomes wrong output instead of a
30
- * build stop. A non-empty `PrismaSchemaFacts.unhandled` FAILS the generate step.
31
11
  */
32
12
  export type PrismaSchemaUnknown = {
33
13
  readonly kind: PrismaSchemaUnknownKind;
@@ -41,11 +21,7 @@ export type PrismaSchemaUnknown = {
41
21
  };
42
22
  export type PrismaSchemaUnknownKind =
43
23
  /** An `@…` on a field declaration. */
44
- "field-attribute"
45
- /** An `@@…` on its own line inside a block. */
46
- | "block-attribute"
47
- /** A named argument of a recognised attribute that the reader neither models nor deliberately discards. */
48
- | "attribute-argument"
24
+ "field-attribute" | "block-attribute" | "attribute-argument"
49
25
  /** A top-level block whose keyword is neither `model`, `enum`, `generator` nor `datasource`. */
50
26
  | "block"
51
27
  /** A line the reader could not classify at all. */
@@ -55,48 +31,15 @@ export type PrismaSchemaFacts = {
55
31
  readonly models: readonly PrismaSchemaModel[];
56
32
  /**
57
33
  * Attributes and blocks the reader does not recognise at all (a `type`/`view`
58
- * block, an unrecognised argument, any spelling added after 7.10.0).
59
- * `@map`/`@@map`/`@@index` are recognised and deliberately discarded instead, so
60
- * they never appear here. Never silently dropped: a non-empty list FAILS the
61
- * generate step.
34
+ * block, an unrecognised argument, any spelling added after 7.10.0).
62
35
  */
63
36
  readonly unhandled: readonly PrismaSchemaUnknown[];
64
37
  };
65
38
  export type PrismaSchemaEnum = {
66
39
  readonly name: string;
67
- /**
68
- * Declaration order is semantic; a member-level `@map` is discarded (bucket 2),
69
- * not recorded.
70
- */
71
40
  readonly values: readonly string[];
72
41
  readonly site: PrismaSchemaSite;
73
42
  };
74
- /**
75
- * A block-level uniqueness criterion — `@@id([…])` or `@@unique([…])` — as PSL
76
- * states it: the members in **the attribute's own order**, and `name:`'s value
77
- * when the attribute carries one.
78
- *
79
- * `name:` is carried rather than discarded because bucket 2's rule is that an
80
- * attribute argument is discardable **iff its value appears nowhere on the 🟢
81
- * surface**, and this one appears there twice. Measured at 7.10.0 (regenerated
82
- * 2026-09-29): `@@unique([b, a], name: "altKey")` emits
83
- * `altKey?: Prisma.NamedUniqueAltKeyCompoundUniqueInput` in `WhereUniqueInput`
84
- * and keys `}, "id" | "altKey">`, and `@@id([a, b], name: "pk")` keys `}, "pk">`
85
- * — the custom name **replaces** the positional `b_a` / `a_b` alias. `map:` does
86
- * stay discarded: `named_idx` appears in the whole generated tree only in the 🛑
87
- * `internal/class.ts` inline-schema blob.
88
- *
89
- * The list itself is read from the positional argument or from `fields:`,
90
- * whichever the schema writes, and in whatever order the other arguments follow:
91
- * `@@unique([b, a])`, `@@unique(fields: [b, a])` and
92
- * `@@unique(name: "k", map: "m", fields: [b, a])` are all valid at 7.10.0 and the
93
- * first two produce the identical generated key `b_a`.
94
- *
95
- * `null` means the attribute wrote no `name:`, not "the positional form": the
96
- * positional key is derivable from `members`, and a name is derivable from
97
- * nothing, so only the undecidable half is recorded. What derives the addressing
98
- * key from the pair is `identifier.mapper.ts`.
99
- */
100
43
  export type PrismaSchemaBlockIdentifier = {
101
44
  /** Member order as written in the attribute's list. Never sorted. */
102
45
  readonly members: readonly string[];
@@ -120,7 +63,6 @@ export type PrismaSchemaField = {
120
63
  readonly type: string;
121
64
  /** `[]` */
122
65
  readonly list: boolean;
123
- /** `?` — the ONLY source of nullability. */
124
66
  readonly optional: boolean;
125
67
  /** `@id` */
126
68
  readonly id: boolean;
@@ -128,15 +70,9 @@ export type PrismaSchemaField = {
128
70
  readonly unique: boolean;
129
71
  /** `@updatedAt` */
130
72
  readonly updatedAt: boolean;
131
- /** `@default(…)`'s argument as RAW TEXT — matched against a table, never parsed. */
132
73
  readonly defaultArgument: string | null;
133
74
  readonly relation: PrismaSchemaRelation | null;
134
- /**
135
- * `@db.*` as written, e.g. `@db.Decimal(10, 2)`. Kept verbatim and never parsed. A
136
- * native type is the **only** proof discovery accepts for `format`; configuration
137
- * may then override it, since `AdapterScalarField.format` and
138
- * `FrameworkScalarFieldConfig.format` are separate members the compiler layers.
139
- */
75
+ /** `@db.*` as written, e.g. `@db.Decimal(10, 2)`. */
140
76
  readonly native: readonly string[];
141
77
  /** `@ignore` */
142
78
  readonly ignored: boolean;
@@ -145,7 +81,6 @@ export type PrismaSchemaField = {
145
81
  export type PrismaSchemaRelation = {
146
82
  /** `@relation("Name")` — the ONLY evidence pairing a back-reference with its forward reference. */
147
83
  readonly name: string | null;
148
- /** The FK scalar(s); read, used, then discarded. */
149
84
  readonly fields: readonly string[] | null;
150
85
  readonly references: readonly string[] | null;
151
86
  /**
@@ -153,21 +88,6 @@ export type PrismaSchemaRelation = {
153
88
  * absent argument carries Prisma's effective default rather than a third
154
89
  * state, because an absent action and the explicitly-written default are one
155
90
  * fact about what a delete does.
156
- *
157
- * `null` means **this edge is not the owning side** — it declares no
158
- * `fields:`, so no foreign key and no action belong to it. PSL enforces that
159
- * itself: `prisma validate` 7.10.0 answers *"The relation field `reqs` on
160
- * Model `Alpha` must not specify the `onDelete` or `onUpdate` argument in the
161
- * @relation attribute. You must only specify it on the opposite field `alpha`
162
- * on model `Req`"*. `null` therefore reads as "ask the other edge", never as
163
- * "no action applies". It is also what an unreadable action spelling leaves
164
- * behind, and that case is recorded in `unhandled` and fails the generate
165
- * step, so no consumer sees it.
166
- *
167
- * The inversion matters to every consumer: the action is declared on the
168
- * CHILD and fires when the PARENT is deleted. Read with
169
- * `PrismaSchemaField.type` — the target model — one owning edge says
170
- * "deleting a `<type>` does `<onDelete>` to a row of the enclosing model".
171
91
  */
172
92
  readonly onDelete: PrismaReferentialAction | null;
173
93
  /** `@relation(onUpdate: …)`, resolved the same way. Read independently of `onDelete`. */
@@ -1,137 +1,4 @@
1
1
  import type { PrismaSchemaFacts } from "./schema-facts.interface.js";
2
- /**
3
- * # The boundary, and why this file is a reader and not a parser
4
- *
5
- * It **locates** tokens and never **evaluates** them.
6
- *
7
- * PSL's model body is line-oriented by the language's own rule, not by this
8
- * reader's assumption. Re-measured against `prisma` 7.10.0 on 2026-09-28, all four
9
- * constructs that would let a declaration legally span lines are **rejected** with
10
- * `Error code: P1012` — so the classic regex-parser failure cannot occur:
11
- *
12
- * 1. a wrapped `@@unique([…])` — the error COUNT is layout-dependent, the
13
- * rejection is not. A two-line wrap (`@@unique([x,` / `y])`) gives 2 errors,
14
- * both `error: Error validating: This line is not a valid field or attribute
15
- * definition.`; a four-line wrap gives 4 — three of that sentence plus one
16
- * `Error validating model "A": This field declaration is invalid. It is either
17
- * missing a name or a type.`
18
- * 2. a wrapped field attribute (`@default(uuid())` on the line below `id String
19
- * @id`) — 1 error, `This line is not a valid field or attribute definition.`
20
- * 3. two block attributes on one line (`@@unique([x]) @@index([y])`) — 1 error,
21
- * `This line is not a valid field or attribute definition.`
22
- * 4. a split field declaration (`id` then `String @id`) — 2 errors, **both**
23
- * `Error validating model "A": This field declaration is invalid. It is either
24
- * missing a name or a type.`
25
- *
26
- * All four are indeed `P1012`, **and the line-oriented conclusion holds
27
- * unchanged** — the design did not move. But case 4 never emits that sentence at
28
- * all (it emits *"This field declaration is invalid. It is either missing a name
29
- * or a type."* twice), and case 1 emits a second, different sentence besides it.
30
- * The evidence supports the rule; the single-sentence attribution does not, and
31
- * the plan now says so.
32
- *
33
- * Re-running it: `prisma` lives at `packages/prisma7-adapter/node_modules/.bin/
34
- * prisma`, not the worktree root. Invoked from the root it silently produces no
35
- * output, which looks exactly like "no errors".
36
- *
37
- * # What crossing the boundary would mean
38
- *
39
- * The day this reader **evaluates** — resolving `env()`, interpreting
40
- * `dbgenerated(…)`, modelling `type`/`view` blocks, following `@@schema`, or
41
- * reconstructing relations without `@relation` names — it has become a parser and
42
- * needs a different decision. Hence:
43
- *
44
- * - `@default`'s argument is kept as **raw text** and matched against a table
45
- * downstream, never parsed.
46
- * - `@db.*` is kept **as written** and never parsed. It is the only proof
47
- * discovery accepts for `format`; configuration overrides it later.
48
- * - a field's `type` stays the written token — `Grade` is not resolved to the
49
- * enum, which is also why a schema **folder** needs no linker (below).
50
- * - an attribute is classified three ways, not two (below), and only the third
51
- * class is recorded in `unhandled`. A non-empty `unhandled` fails the generate
52
- * step: silently dropping an **unrecognised** attribute is how a grammar change
53
- * becomes wrong output instead of a build stop. The version fence is what
54
- * converts a grammar change from silent wrongness into a build stop.
55
- *
56
- * # The three-way classification (corrected 2026-09-28)
57
- *
58
- * 1. **recognised and modelled** — `FIELD_ATTRIBUTES`, `BLOCK_ATTRIBUTES`, `@db.*`
59
- * and `RELATION_ARGUMENTS`.
60
- * 2. **recognised and deliberately discarded**, via the explicit
61
- * `DISCARDED_ATTRIBUTES` / `DISCARDED_BLOCK_ATTRIBUTE_ARGUMENTS` lists —
62
- * `@map`, `@@map`, `@@index`, and `@@id`/`@@unique`'s `map:` argument. The rule
63
- * is one line long: an argument is discardable **iff its value appears nowhere
64
- * on the 🟢 surface**. The list being explicit is the point: the discard is a
65
- * decision a reader can see and audit, not an omission. That rule is also what
66
- * took `name:` back OFF this list (2026-09-29): its value is a
67
- * `WhereUniqueInput` key and an `AtLeast` key, so it is carried on
68
- * `PrismaSchemaBlockIdentifier`.
69
- * 3. **unrecognised** — on neither list, therefore `unhandled`, therefore a build
70
- * stop.
71
- *
72
- * # Surface-invisibility was EVIDENCE, not the meaning (2026-09-29)
73
- *
74
- * Bucket 2's rule reads "discardable iff the value appears nowhere on the 🟢
75
- * surface", and `@relation(onDelete: Cascade)` passes that test: measured at
76
- * 7.10.0, `Cascade` reaches nothing but the 🛑 `internal/class.ts` inline-schema
77
- * blob. It is still **not** discardable, and this is the first construct where the
78
- * test and the rule it stands for come apart.
79
- *
80
- * The rule's *reason* was that such attributes configure the database below the
81
- * ORM entrypoint and therefore carry no framework meaning. A referential action
82
- * fails that reason while passing the test: it changes what a delete **does** —
83
- * rows disappear that the caller never named — which is exactly what a transaction
84
- * planner must reason about before it agrees to run a plan. So invisibility on the
85
- * generated types is *evidence* for "no framework meaning", and evidence can be
86
- * outweighed; it was never the meaning itself. `@map` and `@@index` remain
87
- * discarded because they fail the test AND the reason. `@relation`'s `map:` joins
88
- * them: a foreign-key constraint name is genuinely DB-side, and it was bucket 3
89
- * until now, which made `@relation(map: …)` — valid PSL — a build stop on its own.
90
- *
91
- * Actions are read on both arguments and carried **resolved**: an absent argument
92
- * is not a third state, it is Prisma's effective default, measured rather than
93
- * assumed (see `REFERENTIAL_ACTIONS` and `resolvePrismaReferentialDefaults`).
94
- *
95
- * Bucket 2 previously did not exist, and a field-level `@map` landed in
96
- * `unhandled`: the framework would have refused to build against any schema that
97
- * maps a column name, which is most real Prisma schemas.
98
- *
99
- * # Arguments are located by NAME, never by position (added 2026-09-29)
100
- *
101
- * PSL lets the same argument list arrive positionally or named, and lets the named
102
- * arguments come in any order. Measured against `prisma validate` 7.10.0 and
103
- * against a generated client on 2026-09-29: `@@unique([b, a])`, `@@unique(fields:
104
- * [b, a])`, `@@unique(name: "k", map: "m", fields: [b, a])` and every other
105
- * permutation are accepted, and the client keys the unnamed ones `b_a` and the
106
- * named ones `k` regardless of the order they were written in. The spelling is
107
- * therefore not a fact about the schema, and a reader that took the list to be the
108
- * FIRST argument read `@@unique(fields: [b, a])` as one member called `fields: [b,
109
- * a]` — silently, with `unhandled` empty — so the generate step refused a schema
110
- * Prisma accepts. ARGUMENT order does not matter; MEMBER order inside the list
111
- * still does, because it is part of the identity and of the addressing key.
112
- *
113
- * The same rule applies to `@relation`, which mixes a positional argument (the
114
- * relation name) with named ones; it was already order-independent and its FK read
115
- * was unaffected.
116
- *
117
- * A list whose members are not plain field names — `@@unique([a(sort: Desc)])`,
118
- * valid PSL at 7.10.0 — cannot be located without evaluating, so it is bucket 3
119
- * rather than a guess.
120
- *
121
- * # One deliberate non-recording beyond the buckets
122
- *
123
- * `generator` and `datasource` block bodies are skipped, not recorded: they are
124
- * tooling configuration, not model content, and their contents are exactly what
125
- * this reader must not evaluate (`env()` lives there).
126
- *
127
- * # A schema folder is readdir plus merge
128
- *
129
- * Prisma 7 accepts a folder (`prisma validate --schema <folder>` verified at
130
- * 7.10.0). Blocks are file-local and references are by name, so there is no
131
- * cross-file grammar and **no linker**: reading a folder appends each file's facts
132
- * in filename order and nothing is rewritten. Every fact keeps the file and line
133
- * it was written on.
134
- */
135
2
  export type PrismaSchemaSource = {
136
3
  readonly file: string;
137
4
  readonly text: string;
@@ -2,19 +2,11 @@ import ts from "typescript";
2
2
  import type { PrismaCapabilitySurface } from "./capability-surface.interface.js";
3
3
  export type PrismaTypesReadResult = {
4
4
  readonly surface: PrismaCapabilitySurface;
5
- /**
6
- * Every file under the client directory the reader actually opened. Reported
7
- * rather than assumed: the 🛑 files are absent by refusal, not by luck, and a
8
- * caller — or a spec — can assert the file **set** and not only the output.
9
- */
5
+ /** Every file under the client directory the reader actually opened. */
10
6
  readonly filesRead: readonly string[];
11
7
  };
12
8
  export declare class PrismaTypesReaderError extends Error {
13
9
  readonly name = "PrismaTypesReaderError";
14
10
  }
15
- /**
16
- * The reader's one checker question, exported because it is the distinction the
17
- * whole reader rests on and therefore has to be testable on its own.
18
- */
19
11
  export declare function readWrittenTypeArguments(checker: ts.TypeChecker, reference: ts.TypeReferenceNode): readonly string[];
20
12
  export declare function readPrismaCapabilitySurface(clientDirectory: string): PrismaTypesReadResult;
@@ -21,7 +21,7 @@ export function readPrismaCapabilitySurface(clientDirectory) {
21
21
  const root = path.resolve(clientDirectory);
22
22
  const publicFiles = listPublicFiles(root);
23
23
  if (publicFiles.length === 0) {
24
- throw new PrismaTypesReaderError(`no generated 🟢 file was found under ${root}`);
24
+ throw new PrismaTypesReaderError(`no generated Prisma client file was found under ${root}`);
25
25
  }
26
26
  const filesRead = new Set();
27
27
  const host = createPublicOnlyHost(root, filesRead);
@@ -63,11 +63,11 @@ function createPublicOnlyHost(root, filesRead) {
63
63
  return undefined;
64
64
  }
65
65
  if (isPrivatePath(root, resolved)) {
66
- throw new PrismaTypesReaderError(`the types reader asked for ${path.relative(root, resolved)}, which is a 🛑 file`);
66
+ throw new PrismaTypesReaderError(`the types reader asked for ${path.relative(root, resolved)}, which is an internal file of the generated client that must not be imported`);
67
67
  }
68
68
  const text = readFileSync(resolved, "utf8");
69
69
  if (text.includes(RED_BANNER) || !text.includes(GREEN_BANNER)) {
70
- throw new PrismaTypesReaderError(`${path.relative(root, resolved)} does not carry the 🟢 banner`);
70
+ throw new PrismaTypesReaderError(`${path.relative(root, resolved)} does not carry Prisma's "${GREEN_BANNER}" banner`);
71
71
  }
72
72
  filesRead.add(resolved);
73
73
  return text;
@@ -128,7 +128,7 @@ function projectionMembers(index, name) {
128
128
  ? written.typeArguments?.[0]
129
129
  : written;
130
130
  if (literal === undefined || !ts.isTypeLiteralNode(literal)) {
131
- throw new PrismaTypesReaderError(`${name} is declared by the 🟢 types and is not a written object type, so its projection members cannot be read`);
131
+ throw new PrismaTypesReaderError(`${name} is declared by the generated client's types and is not a written object type, so its projection members cannot be read`);
132
132
  }
133
133
  return new Set(memberNames(literal.members.filter(ts.isPropertySignature)));
134
134
  }
@@ -197,7 +197,7 @@ function keysOfReferenced(index, node, accept) {
197
197
  }
198
198
  const members = membersOf(index, referencedName(reference));
199
199
  if (members === null) {
200
- throw new PrismaTypesReaderError(`${referencedName(reference)} is referenced by the 🟢 types but declared nowhere in them`);
200
+ throw new PrismaTypesReaderError(`${referencedName(reference)} is referenced by the generated client's types but declared nowhere in them`);
201
201
  }
202
202
  return memberNames(members);
203
203
  }
@@ -325,7 +325,7 @@ function readMutationOperations(index, model, stage, field, member) {
325
325
  }
326
326
  const members = membersOf(index, referencedName(reference));
327
327
  if (members === null) {
328
- throw new PrismaTypesReaderError(`${referencedName(reference)} is referenced by ${model}Unchecked${stage}Input.${field} and declared nowhere in the 🟢 types`);
328
+ throw new PrismaTypesReaderError(`${referencedName(reference)} is referenced by ${model}Unchecked${stage}Input.${field} and declared nowhere in the generated client's types`);
329
329
  }
330
330
  return memberNames(members);
331
331
  }
@@ -371,7 +371,7 @@ function readRelationFields(checker, index, payload, where, create, update, incl
371
371
  }
372
372
  const targetMembers = membersOf(index, referencedName(targetPayload));
373
373
  if (targetMembers === null) {
374
- throw new PrismaTypesReaderError(`${referencedName(targetPayload)} is declared nowhere in the 🟢 types`);
374
+ throw new PrismaTypesReaderError(`${referencedName(targetPayload)} is declared nowhere in the generated client's types`);
375
375
  }
376
376
  const edge = findReference(include.get(name)?.type, (n) => n.endsWith("Args"));
377
377
  const edgeArguments = edge === null