@aventara/prisma7-adapter 0.1.0-pilot.3 → 1.0.0-rc.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.
- package/CHANGELOG.md +190 -0
- package/README.md +44 -29
- package/dist/discovery/artifact.loader.d.ts +2 -7
- package/dist/discovery/artifact.loader.js +8 -1
- package/dist/discovery/capability-surface.interface.d.ts +7 -64
- package/dist/discovery/capability.deriver.d.ts +2 -47
- package/dist/discovery/capability.deriver.js +3 -0
- package/dist/discovery/discovery-artifact.interface.d.ts +50 -159
- package/dist/discovery/discovery-artifact.interface.js +1 -1
- package/dist/discovery/enum.mapper.d.ts +0 -24
- package/dist/discovery/field.mapper.d.ts +4 -115
- package/dist/discovery/field.mapper.js +7 -7
- package/dist/discovery/identifier.mapper.d.ts +4 -84
- package/dist/discovery/model.discoverer.d.ts +3 -70
- package/dist/discovery/model.discoverer.js +5 -4
- package/dist/discovery/operations.deriver.d.ts +2 -37
- package/dist/discovery/scalar.mapper.d.ts +1 -32
- package/dist/discovery/scalar.mapper.js +2 -2
- package/dist/discovery/schema-facts.interface.d.ts +3 -83
- package/dist/discovery/schema.reader.d.ts +0 -133
- package/dist/discovery/types.reader.d.ts +1 -9
- package/dist/discovery/types.reader.js +18 -7
- package/dist/discovery/vocabulary.dictionary.d.ts +8 -134
- package/dist/discovery/vocabulary.dictionary.js +2 -2
- package/dist/errors/error.normalizer.d.ts +5 -168
- package/dist/errors/prisma-error.reader.d.ts +1 -93
- package/dist/execution/first-variant.emulator.d.ts +5 -28
- package/dist/execution/operation.executor.d.ts +3 -63
- package/dist/execution/result.shaper.d.ts +0 -56
- package/dist/execution/transaction.executor.d.ts +2 -99
- package/dist/generate/artifact.emitter.d.ts +1 -35
- package/dist/generate/artifact.emitter.js +31 -47
- package/dist/generate/declared-support.reader.d.ts +0 -20
- package/dist/generate/discovery.cli.d.ts +0 -4
- package/dist/generate/existence.gate.d.ts +3 -37
- package/dist/generate/index.d.ts +4 -57
- package/dist/generate/prisma-cli.resolver.d.ts +0 -22
- package/dist/generate/prisma.generate.d.ts +14 -83
- package/dist/generate/reader-crosscheck.d.ts +3 -88
- package/dist/generate/reader-crosscheck.js +4 -4
- package/dist/prisma-client.interface.d.ts +5 -25
- package/dist/prisma.adapter.d.ts +21 -22
- package/dist/prisma.adapter.js +1 -0
- package/dist/provider/provider-profile.d.ts +6 -14
- package/dist/translation/identifier.addressing.d.ts +0 -38
- package/dist/translation/mutation.translator.d.ts +6 -52
- package/dist/translation/mutation.translator.js +1 -1
- package/dist/translation/operation.translator.d.ts +3 -103
- package/dist/translation/order-pagination.translator.d.ts +2 -48
- package/dist/translation/order-pagination.translator.js +1 -1
- package/dist/translation/projection.translator.d.ts +1 -46
- package/dist/translation/scalar.conversion.d.ts +2 -65
- package/dist/translation/scalar.conversion.js +1 -1
- package/dist/translation/where.translator.d.ts +0 -52
- package/package.json +5 -4
|
@@ -1,38 +1,5 @@
|
|
|
1
1
|
import type { RelationFieldCapabilities, ScalarFieldCapabilities, ScalarFilterOperator } from "@aventara/core";
|
|
2
2
|
import type { PrismaRelationSurface, PrismaScalarSurface } from "./capability-surface.interface.js";
|
|
3
|
-
/**
|
|
4
|
-
* This module accepts `surface` alone, and that is deliberate: dictionary
|
|
5
|
-
* invariant 6 says a capability is read from the client's own types and never
|
|
6
|
-
* tabulated from a provider row, and the strongest available mechanism for that is
|
|
7
|
-
* to make the provider **unrepresentable** rather than merely unread. A
|
|
8
|
-
* `PrismaProviderProfile` in this input is a `provider` member one keystroke from
|
|
9
|
-
* a branch; with no such member, `if (provider === …)` is a compile error, and the
|
|
10
|
-
* three per-engine differences this pass exists to prove — `mode`, `JsonFilter`'s
|
|
11
|
-
* ordering operators, and the `path` / `in` / `notIn` operand shapes — have to
|
|
12
|
-
* come from the surface because nothing else is in scope.
|
|
13
|
-
*
|
|
14
|
-
* `mapping` went with them because nothing here reads an Aventara scalar identity
|
|
15
|
-
* to answer a capability question.
|
|
16
|
-
*
|
|
17
|
-
* The answer is read off the recorded operand shape, which is already on the
|
|
18
|
-
* surface; the Aventara scalar identity would have been a second, weaker source
|
|
19
|
-
* for the same fact. `mapping` stays out.
|
|
20
|
-
*
|
|
21
|
-
* # Order is canonical, not incidental
|
|
22
|
-
*
|
|
23
|
-
* Every list is emitted in **core's own union declaration order**, held below as a
|
|
24
|
-
* closed ordered array.
|
|
25
|
-
*
|
|
26
|
-
* # Absent and empty are different answers
|
|
27
|
-
*
|
|
28
|
-
* `create: []` / `update: []` mean **writable with no directive** — plain
|
|
29
|
-
* assignment, which is what `set` alone is. An absent `create` / `update` means
|
|
30
|
-
* **not writable**, and core enforces exactly that: `validateScalarMutation`
|
|
31
|
-
* answers `V1006` *"is not writable during <stage>"* when the member is
|
|
32
|
-
* `undefined` **[CORE, operations/operation-validator-core.ts]**. So the two must
|
|
33
|
-
* not be conflated, and the surface's `null` (the client exposes no entry for this
|
|
34
|
-
* column at this stage) is the only thing that produces the absent form.
|
|
35
|
-
*/
|
|
36
3
|
/** A Prisma spelling that no dictionary table recognises: a build stop, never a drop. */
|
|
37
4
|
export declare class PrismaCapabilityDerivationError extends Error {
|
|
38
5
|
readonly name = "PrismaCapabilityDerivationError";
|
|
@@ -40,23 +7,11 @@ export declare class PrismaCapabilityDerivationError extends Error {
|
|
|
40
7
|
export type ScalarCapabilityInput = {
|
|
41
8
|
readonly surface: PrismaScalarSurface;
|
|
42
9
|
};
|
|
43
|
-
/**
|
|
44
|
-
* A relation's capabilities are answered for the field, not for an operation.
|
|
45
|
-
*
|
|
46
|
-
* The `operation: "create.one" | "create.many"` parameter this input used to carry
|
|
47
|
-
* is **gone**. The scope-qualified members hold both answers at once, so there is
|
|
48
|
-
* nothing left to pick and no caller left to pick it.
|
|
49
|
-
*/
|
|
10
|
+
/** A relation's capabilities are answered for the field, not for an operation. */
|
|
50
11
|
export type RelationCapabilityInput = {
|
|
51
12
|
readonly surface: PrismaRelationSurface;
|
|
52
13
|
};
|
|
53
14
|
export declare function deriveScalarCapabilities(input: ScalarCapabilityInput): ScalarFieldCapabilities;
|
|
54
|
-
/**
|
|
55
|
-
* The Prisma operand for each ADVERTISED operator, keyed by the Aventara name.
|
|
56
|
-
*
|
|
57
|
-
* A deriver that answered operator NAMES alone would report the two engines
|
|
58
|
-
* identical where they are not, so the shape is carried beside the capability
|
|
59
|
-
* rather than discarded.
|
|
60
|
-
*/
|
|
15
|
+
/** The Prisma operand for each ADVERTISED operator, keyed by the Aventara name. */
|
|
61
16
|
export declare function deriveScalarFilterOperands(input: ScalarCapabilityInput): Readonly<Partial<Record<ScalarFilterOperator, string>>>;
|
|
62
17
|
export declare function deriveRelationCapabilities(input: RelationCapabilityInput): RelationFieldCapabilities;
|
|
@@ -157,6 +157,9 @@ export function deriveRelationCapabilities(input) {
|
|
|
157
157
|
: {}),
|
|
158
158
|
create: writableIn(directives(surface.createMutations), surface.bulkCreatable),
|
|
159
159
|
update: writableIn(directives(surface.updateMutations), surface.bulkUpdatable),
|
|
160
|
+
...(surface.scalarNestedWriteFilter
|
|
161
|
+
? { nestedWriteFilter: "scalar" }
|
|
162
|
+
: {}),
|
|
160
163
|
};
|
|
161
164
|
}
|
|
162
165
|
function look(table, key, what) {
|
|
@@ -1,39 +1,23 @@
|
|
|
1
|
-
import type { Adapter,
|
|
1
|
+
import type { Adapter, AdapterModel, EnumDefinition, ScalarRegistry, TransactionCapability } from "@aventara/core";
|
|
2
|
+
import type { AdapterResource } from "@aventara/core/internal";
|
|
2
3
|
import type { PrismaProvider } from "../provider/provider-profile.js";
|
|
3
|
-
/**
|
|
4
|
-
* A plain `type`, not `interface … extends`: `AdapterModel.resources` is
|
|
5
|
-
* self-typed `AdapterResource<this>`, and a nominal `extends` hits TS2430.
|
|
6
|
-
*/
|
|
4
|
+
/** The adapter model a discovery artifact carries, in Aventara's vocabulary. */
|
|
7
5
|
export type PrismaAdapterModel = {
|
|
6
|
+
/** The scalar types the schema uses. */
|
|
8
7
|
readonly scalars: ScalarRegistry;
|
|
8
|
+
/** The schema's enums, by name. */
|
|
9
9
|
readonly enums: Readonly<Record<string, EnumDefinition>>;
|
|
10
|
+
/** The schema's models, as Resources. */
|
|
10
11
|
readonly resources: Readonly<Record<string, AdapterResource<PrismaAdapterModel>>>;
|
|
12
|
+
/** Whether the driver supports interactive transactions. */
|
|
11
13
|
readonly transactions: TransactionCapability;
|
|
14
|
+
/**
|
|
15
|
+
* Prisma takes one record's references one way at a time (its checked and
|
|
16
|
+
* unchecked write inputs), so every artifact states `"uniform"`.
|
|
17
|
+
*/
|
|
18
|
+
readonly referenceWrites: NonNullable<AdapterModel["referenceWrites"]>;
|
|
12
19
|
};
|
|
13
|
-
/**
|
|
14
|
-
* One identifier, and the **Prisma addressing key** that reaches it.
|
|
15
|
-
*
|
|
16
|
-
* `members` is the identifier as core carries it — derived from
|
|
17
|
-
* `AdapterResource` rather than restated, so a change to core's identifier type
|
|
18
|
-
* is a compile error here. `key` is the same criterion's *Prisma* spelling: the
|
|
19
|
-
* key a `WhereUniqueInput` accepts, and the key the generated `AtLeast<…, K>`
|
|
20
|
-
* union lists.
|
|
21
|
-
*
|
|
22
|
-
* The two are different facts, and the whole reason this type exists is that
|
|
23
|
-
* they are not derivable from one another. Measured at 7.10.0 (2026-09-29):
|
|
24
|
-
*
|
|
25
|
-
* - a single-column criterion keys by the field name and does **not** wrap:
|
|
26
|
-
* `{ id: "x" }`;
|
|
27
|
-
* - a compound keys by its members joined with `_` **in attribute order** and
|
|
28
|
-
* does wrap: `@@unique([b, a])` → `{ b_a: { b, a } }`;
|
|
29
|
-
* - `name: "X"` **replaces** the positional form on both sides — `}, "id" |
|
|
30
|
-
* "altKey">` and `altKey?: Prisma.…CompoundUniqueInput`;
|
|
31
|
-
* - `map:` affects neither: it reaches only the 🛑 inline-schema blob.
|
|
32
|
-
*
|
|
33
|
-
* So `key` is stored at generate time and **matched** at runtime, never
|
|
34
|
-
* re-derived: `members.join("_")` is wrong for every named criterion, and
|
|
35
|
-
* whether it wraps is `members.length > 1`.
|
|
36
|
-
*/
|
|
20
|
+
/** One identifier, and the **Prisma addressing key** that reaches it. */
|
|
37
21
|
export type PrismaIdentifierAddress = {
|
|
38
22
|
readonly members: AdapterResource<PrismaAdapterModel>["identifiers"][number];
|
|
39
23
|
readonly key: string;
|
|
@@ -42,110 +26,42 @@ export type PrismaIdentifierAddress = {
|
|
|
42
26
|
export type PrismaResourceTranslation = {
|
|
43
27
|
readonly identifierKeys: readonly PrismaIdentifierAddress[];
|
|
44
28
|
};
|
|
45
|
-
/**
|
|
46
|
-
* The **translation table**: Prisma addressing for every discovered Resource.
|
|
47
|
-
*
|
|
48
|
-
* Keyed on `M`'s own resource keys, so a table missing a Resource — or naming
|
|
49
|
-
* one the model does not have — is a compile error rather than a runtime
|
|
50
|
-
* `undefined` at the first unique read.
|
|
51
|
-
*
|
|
52
|
-
* It sits **beside** {@link PrismaDiscoveryArtifact.model}, never inside it, and
|
|
53
|
-
* that placement is the point: the model is canonicalized, hashed and compiled
|
|
54
|
-
* into the Contract, so a Prisma addressing key in it would put a DB-side
|
|
55
|
-
* rename into the Contract hash and force every client to be regenerated for a
|
|
56
|
-
* change with zero semantic content. This half is adapter-private: the executor
|
|
57
|
-
* reads it, core never sees it.
|
|
58
|
-
*/
|
|
29
|
+
/** The **translation table**: Prisma addressing for every discovered Resource. */
|
|
59
30
|
export type PrismaTranslationTable<M extends PrismaAdapterModel = PrismaAdapterModel> = {
|
|
60
31
|
readonly [R in keyof M["resources"]]: PrismaResourceTranslation;
|
|
61
32
|
};
|
|
62
|
-
/**
|
|
63
|
-
* A referential action in Aventara's own vocabulary.
|
|
64
|
-
*
|
|
65
|
-
* Prisma's set — `Cascade | Restrict | NoAction | SetNull | SetDefault` — is the
|
|
66
|
-
* SQL-standard set spelled in PascalCase, so a faithful mapping exists and no
|
|
67
|
-
* Prisma spelling has to travel with the fact. These words are SQL's own
|
|
68
|
-
* (`ON DELETE SET NULL`), which is why the mapping is a rename and not a
|
|
69
|
-
* translation: nothing about it is Prisma-shaped, and the next adapter can state
|
|
70
|
-
* the same fact without inheriting this one's vocabulary.
|
|
71
|
-
*
|
|
72
|
-
* It is declared **here**, on the artifact side, rather than beside the PSL
|
|
73
|
-
* facts that resolve it: the artifact is what carries the vocabulary off the
|
|
74
|
-
* generate step and across the package boundary, and `schema-facts.interface.ts`
|
|
75
|
-
* imports it from here so that there is one spelling and the dependency runs
|
|
76
|
-
* generate-time → artifact rather than the other way about.
|
|
77
|
-
*/
|
|
33
|
+
/** A referential action, as `onDelete`/`onUpdate` name it in the schema. */
|
|
78
34
|
export type PrismaReferentialAction = "cascade" | "restrict" | "no-action" | "set-null" | "set-default";
|
|
79
|
-
/**
|
|
80
|
-
* One **owning** relation edge's resolved referential actions.
|
|
81
|
-
*
|
|
82
|
-
* # The direction, which is the whole point of this type
|
|
83
|
-
*
|
|
84
|
-
* `onDelete` is declared on the **child** — `Kid.parent @relation(…, onDelete:
|
|
85
|
-
* Cascade)` — and fires when the **parent** is deleted. PSL enforces that itself:
|
|
86
|
-
* `prisma validate` 7.10.0 refuses the action on the other edge, naming the
|
|
87
|
-
* opposite field. So an entry under resource `R` reads:
|
|
88
|
-
*
|
|
89
|
-
* > **deleting a `target` row does `onDelete` to rows of `R`.**
|
|
90
|
-
*
|
|
91
|
-
* A consumer asking *"what does deleting an `X` do?"* scans every resource's
|
|
92
|
-
* {@link PrismaResourceReferentialActions.owningEdges} for `target === "X"` and
|
|
93
|
-
* reads the enclosing resource key off the table. It never has to work out which
|
|
94
|
-
* side of a pair declared what.
|
|
95
|
-
*
|
|
96
|
-
* **Reversing that reading is silent.** Nothing in the shape distinguishes
|
|
97
|
-
* `resource` from `target`, both are resource names, and a planner built on the
|
|
98
|
-
* inverted reading cascades the wrong way with no type error and no runtime
|
|
99
|
-
* complaint. That is why the convention is restated here as well as on
|
|
100
|
-
* `field.mapper.ts`'s `PrismaRelationEdge`, which is where these values are
|
|
101
|
-
* carried out of field projection.
|
|
102
|
-
*
|
|
103
|
-
* # Both members are resolved, so neither is nullable
|
|
104
|
-
*
|
|
105
|
-
* An absent `@relation(onDelete:)` is **not** a third state: it is Prisma's
|
|
106
|
-
* effective default, measured at 7.10.0 with `prisma migrate diff` and identical
|
|
107
|
-
* on `postgresql` and `sqlite` — `SET NULL` when every scalar in `fields:` is
|
|
108
|
-
* optional, `RESTRICT` otherwise, and `CASCADE` on update
|
|
109
|
-
* (`schema.reader.ts`'s `resolvePrismaReferentialDefaults`). An action the reader
|
|
110
|
-
* cannot name is recorded in `unhandled` and fails the generate step, so no
|
|
111
|
-
* artifact ever carries an unresolved one.
|
|
112
|
-
*/
|
|
35
|
+
/** One **owning** relation edge's resolved referential actions. */
|
|
113
36
|
export type PrismaReferentialEdge = {
|
|
114
37
|
/** The edge's field name on the enclosing (child) resource. */
|
|
115
38
|
readonly field: string;
|
|
116
39
|
/** The resource the edge points AT — the parent, whose deletion fires `onDelete`. */
|
|
117
40
|
readonly target: string;
|
|
41
|
+
/** Whether the edge holds one related row or many. */
|
|
118
42
|
readonly cardinality: "one" | "many";
|
|
43
|
+
/**
|
|
44
|
+
* The scalar fields of the ENCLOSING resource that hold the reference — the
|
|
45
|
+
* relation's `@relation(fields: […])`, in schema order.
|
|
46
|
+
*/
|
|
47
|
+
readonly references: readonly string[];
|
|
119
48
|
/** What deleting a `target` row does to rows of the ENCLOSING resource. */
|
|
120
49
|
readonly onDelete: PrismaReferentialAction;
|
|
121
50
|
/** The same for an update of the referenced key. Read independently of `onDelete`. */
|
|
122
51
|
readonly onUpdate: PrismaReferentialAction;
|
|
123
52
|
};
|
|
124
53
|
/**
|
|
125
|
-
* One Resource's owning edges, in
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* which is what makes an edge the foreign-key side and the only side PSL lets
|
|
129
|
-
* carry an action. A back-reference's actions are its partner's, so an entry for
|
|
130
|
-
* it would carry two nulls and no fact; a resource with no owning edge carries an
|
|
131
|
-
* empty list, which is itself the answer that deleting anything leaves its rows
|
|
132
|
-
* alone.
|
|
54
|
+
* One Resource's owning edges, in schema field order: the relation fields that
|
|
55
|
+
* declare `@relation(fields: …)`. An empty list means deleting rows elsewhere
|
|
56
|
+
* leaves this Resource's rows alone.
|
|
133
57
|
*/
|
|
134
58
|
export type PrismaResourceReferentialActions = {
|
|
59
|
+
/** The Resource's owning edges, in schema order. */
|
|
135
60
|
readonly owningEdges: readonly PrismaReferentialEdge[];
|
|
136
61
|
};
|
|
137
62
|
/**
|
|
138
63
|
* The **referential-action table**: every discovered Resource's owning edges,
|
|
139
64
|
* resolved.
|
|
140
|
-
*
|
|
141
|
-
* Keyed on `M`'s own resource keys for the reason {@link PrismaTranslationTable}
|
|
142
|
-
* is — a resource the model carries and the table forgets is a compile error at
|
|
143
|
-
* the generate step rather than a runtime `undefined` — and it sits **beside**
|
|
144
|
-
* {@link PrismaDiscoveryArtifact.model} and outside `modelHash` for the reason
|
|
145
|
-
* that one does: `modelHash` digests the model, and a referential action is not
|
|
146
|
-
* part of the model. Putting it inside would push referential-action vocabulary
|
|
147
|
-
* into the compiled Contract and the ClientContract, where core has no word for
|
|
148
|
-
* it, and would move the Contract identity of every schema that has a relation.
|
|
149
65
|
*/
|
|
150
66
|
export type PrismaReferentialActionTable<M extends PrismaAdapterModel = PrismaAdapterModel> = {
|
|
151
67
|
readonly [R in keyof M["resources"]]: PrismaResourceReferentialActions;
|
|
@@ -154,26 +70,15 @@ export type PrismaReferentialActionTable<M extends PrismaAdapterModel = PrismaAd
|
|
|
154
70
|
* What the generated Prisma client exposed when the artifact was generated: each
|
|
155
71
|
* model it has a delegate for, keyed by the model's name, and that model's field
|
|
156
72
|
* references (`prisma.<model>.fields`) in code-unit order.
|
|
157
|
-
*
|
|
158
|
-
* It is the artifact's record of the client it was read from, and the runtime
|
|
159
|
-
* reads the same two facts off the client it is handed — the delegates the client
|
|
160
|
-
* carries, and each one's public `fields`. When they disagree, the schema changed
|
|
161
|
-
* and the client was regenerated without the artifact, and the adapter refuses to
|
|
162
|
-
* start rather than serve a model that no longer matches. Prisma excludes
|
|
163
|
-
* `Unsupported(…)` and `@ignore`d fields and `@@ignore`d models from both the
|
|
164
|
-
* generated types and the runtime client, so the two readings agree by
|
|
165
|
-
* construction on an unchanged schema.
|
|
166
|
-
*
|
|
167
|
-
* Beside the model and outside `modelHash`, like the translation table: it
|
|
168
|
-
* describes the provider client, not the semantic model.
|
|
169
73
|
*/
|
|
170
74
|
export type PrismaClientShape = Readonly<Record<string, readonly string[]>>;
|
|
171
75
|
/**
|
|
172
76
|
* What a create of each Resource cannot do without, already in core's words
|
|
173
77
|
* (`Adapter["createRequirements"]`): a column the client must write, or a
|
|
174
|
-
* required relation
|
|
175
|
-
* hold its reference
|
|
176
|
-
*
|
|
78
|
+
* required relation — which may instead be written through the scalar fields
|
|
79
|
+
* that hold its reference, read off the relation's referential edge
|
|
80
|
+
* (`references`), never restated here — and the relation's other end, through
|
|
81
|
+
* which a nested create supplies it.
|
|
177
82
|
*
|
|
178
83
|
* Read at generate time from the generated client's create inputs — the
|
|
179
84
|
* question marks on `<M>UncheckedCreateInput` and `<M>CreateInput` — and from the
|
|
@@ -182,54 +87,40 @@ export type PrismaClientShape = Readonly<Record<string, readonly string[]>>;
|
|
|
182
87
|
* model, and the Contract carries no foreign-key detail.
|
|
183
88
|
*/
|
|
184
89
|
export type PrismaCreateRequirementTable = NonNullable<Adapter["createRequirements"]>;
|
|
90
|
+
/** The current discovery artifact format. */
|
|
91
|
+
export declare const DISCOVERY_ARTIFACT_FORMAT = 2;
|
|
185
92
|
/**
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* It is still passive, serializable, introspectable, hashable and free of runtime
|
|
190
|
-
* callbacks: a `.ts` module whose only expression is a frozen object literal is
|
|
191
|
-
* JSON that carries its own type.
|
|
192
|
-
*
|
|
193
|
-
* `M` is the reason this type is generic at all. The emitted module's `model` is a
|
|
194
|
-
* literal type; if this alias pinned `model` to the general {@link
|
|
195
|
-
* PrismaAdapterModel}, every resource key and field key would be erased to
|
|
196
|
-
* `string` on the way to `createPrismaAdapter`, `EffectiveFieldKey` would
|
|
197
|
-
* degenerate, and core's whole config-typing workstream would buy nothing —
|
|
198
|
-
* silently, with no compile error anywhere. `model: M` is a bare property
|
|
199
|
-
* position, which is what makes `M` actually infer at the call site;
|
|
200
|
-
* `typed-artifact.test-d.ts` is what proves it still does.
|
|
201
|
-
*
|
|
202
|
-
* The default keeps the unparameterised spelling meaning what it always meant:
|
|
203
|
-
* "some Prisma adapter model".
|
|
93
|
+
* The discovery artifact: the module `aventara-prisma7-generate` writes from
|
|
94
|
+
* your schema and generated client. Import its `discovery` export and pass it
|
|
95
|
+
* to `createPrismaAdapter({ discovery })`.
|
|
204
96
|
*/
|
|
205
97
|
export type PrismaDiscoveryArtifact<M extends PrismaAdapterModel = PrismaAdapterModel> = {
|
|
206
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* The artifact's format. A TypeScript error naming this property means the
|
|
100
|
+
* artifact was written by another version of `@aventara/prisma7-adapter`:
|
|
101
|
+
* regenerate it with `npm run aventara:prepare` (or `aventara-prisma7-generate`).
|
|
102
|
+
*/
|
|
103
|
+
readonly regenerate_with_aventara_prepare: typeof DISCOVERY_ARTIFACT_FORMAT;
|
|
104
|
+
/** The Prisma version the artifact was generated with, such as `"7.10.0"`. */
|
|
207
105
|
readonly prismaVersion: string;
|
|
106
|
+
/** The database provider: `"sqlite"` or `"postgresql"`. */
|
|
208
107
|
readonly provider: PrismaProvider;
|
|
209
108
|
/** `sha256:<hex>` of the canonicalized `model` below — and of nothing else. */
|
|
210
109
|
readonly modelHash: string;
|
|
110
|
+
/** What the schema and the generated client agreed on. */
|
|
211
111
|
readonly model: M;
|
|
212
|
-
/**
|
|
213
|
-
* Prisma addressing beside the model, and outside `modelHash` by
|
|
214
|
-
* construction: see {@link PrismaTranslationTable}.
|
|
215
|
-
*/
|
|
112
|
+
/** Prisma addressing per Resource; not part of `modelHash`. */
|
|
216
113
|
readonly translation: PrismaTranslationTable<M>;
|
|
217
114
|
/**
|
|
218
|
-
* The
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* Direction, because getting it backwards is silent: an entry under resource
|
|
222
|
-
* `R` says **deleting a `target` row does `onDelete` to rows of `R`**.
|
|
115
|
+
* The schema's referential actions; not part of `modelHash`. An entry under
|
|
116
|
+
* resource `R` says **deleting a `target` row does `onDelete` to rows of `R`**.
|
|
223
117
|
*/
|
|
224
118
|
readonly referentialActions: PrismaReferentialActionTable<M>;
|
|
225
119
|
/**
|
|
226
|
-
* The
|
|
227
|
-
*
|
|
120
|
+
* The models and field references of the client the artifact was generated
|
|
121
|
+
* against; not part of `modelHash`.
|
|
228
122
|
*/
|
|
229
123
|
readonly client: PrismaClientShape;
|
|
230
|
-
/**
|
|
231
|
-
* The create requirements, beside the model and outside `modelHash`: see
|
|
232
|
-
* {@link PrismaCreateRequirementTable}.
|
|
233
|
-
*/
|
|
124
|
+
/** What a create of each Resource cannot do without; not part of `modelHash`. */
|
|
234
125
|
readonly createRequirements: PrismaCreateRequirementTable;
|
|
235
126
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const DISCOVERY_ARTIFACT_FORMAT = 2;
|
|
@@ -1,29 +1,9 @@
|
|
|
1
1
|
import type { EnumDefinition } from "@aventara/core";
|
|
2
2
|
import type { PrismaSchemaEnum } from "./schema-facts.interface.js";
|
|
3
3
|
import type { PrismaDiscoveryDiagnostic } from "./vocabulary.dictionary.js";
|
|
4
|
-
/**
|
|
5
|
-
* # Declaration order is semantic, so nothing here sorts
|
|
6
|
-
*
|
|
7
|
-
* # What a cross-check failure means
|
|
8
|
-
*
|
|
9
|
-
* `enums.ts` and `schema.prisma` were read in the same instant by the same
|
|
10
|
-
* generate step. If they disagree, one of them is stale, and the answer is
|
|
11
|
-
* `diverged` — a build stop — rather than a preference for either. The message
|
|
12
|
-
* prints both orders because "the enum diverged" without them costs the reader a
|
|
13
|
-
* second investigation.
|
|
14
|
-
*
|
|
15
|
-
* # A member-level `@map` never reaches here, from either side
|
|
16
|
-
*
|
|
17
|
-
* PSL's reader discards it as bucket 2 (a database name, which Aventara has no
|
|
18
|
-
* word for), and the generated `enums.ts` writes the member name on *both* sides
|
|
19
|
-
* of a mapped member — `GAMMA: 'GAMMA'`, not `GAMMA: 'g'` **[RUN8 — 2026-09-28,
|
|
20
|
-
* prisma 7.10.0]**. So the cross-check compares Aventara identities on both sides,
|
|
21
|
-
* and the mapper has nothing to resurrect.
|
|
22
|
-
*/
|
|
23
4
|
export type EnumMappingInput = {
|
|
24
5
|
/** PSL — the source of the values and their order. */
|
|
25
6
|
readonly declared: PrismaSchemaEnum;
|
|
26
|
-
/** TYPE — `enums.ts`'s member list, or `undefined` when the 🟢 types omit the enum entirely. */
|
|
27
7
|
readonly exposed: readonly string[] | undefined;
|
|
28
8
|
};
|
|
29
9
|
export type EnumMappingOutcome = {
|
|
@@ -37,10 +17,6 @@ export type EnumMappingOutcome = {
|
|
|
37
17
|
readonly outcome: "diverged";
|
|
38
18
|
readonly diagnostic: PrismaDiscoveryDiagnostic;
|
|
39
19
|
};
|
|
40
|
-
/**
|
|
41
|
-
* The `kind` is a literal `"scalar"`, so a third structural kind cannot be
|
|
42
|
-
* introduced here without the type changing.
|
|
43
|
-
*/
|
|
44
20
|
export type EnumBackedColumn = {
|
|
45
21
|
readonly kind: "scalar";
|
|
46
22
|
readonly type: {
|
|
@@ -1,95 +1,17 @@
|
|
|
1
|
-
import { type AdapterLifecycleTrait, type AdapterRelationField, type AdapterScalarField } from "@aventara/core";
|
|
1
|
+
import { type AdapterLifecycleTrait, type AdapterRelationField, type AdapterScalarField } from "@aventara/core/internal";
|
|
2
2
|
import type { PrismaResourceSurface } from "./capability-surface.interface.js";
|
|
3
3
|
import type { PrismaAdapterModel, PrismaReferentialAction } from "./discovery-artifact.interface.js";
|
|
4
4
|
import type { PrismaSchemaField, PrismaSchemaUnknown } from "./schema-facts.interface.js";
|
|
5
5
|
import { type PrismaDiscoveryDiagnostic, type PrismaEvidence } from "./vocabulary.dictionary.js";
|
|
6
|
-
/**
|
|
7
|
-
* # Nullability comes from PSL, and the required `Json` column is why
|
|
8
|
-
*
|
|
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.
|
|
19
|
-
*
|
|
20
|
-
* # The create-optional marker is a cross-check, never a source
|
|
21
|
-
*
|
|
22
|
-
* {@link crossCheckCreateOptional} is a separate function that *consumes* this
|
|
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.
|
|
30
|
-
*
|
|
31
|
-
* # `@default` is matched, never parsed
|
|
32
|
-
*
|
|
33
|
-
* {@link matchPrismaDefault} walks `PRISMA_DEFAULT_TABLE` over the raw argument
|
|
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
|
|
41
|
-
* `dbgenerated("gen_random_uuid()")` remains a key and `"a, b // not a comment"`
|
|
42
|
-
* remains opaque text.
|
|
43
|
-
*
|
|
44
|
-
* # A relation carries four members, and the FK is not one of them
|
|
45
|
-
*
|
|
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.
|
|
54
|
-
*
|
|
55
|
-
* # Two failure vocabularies, kept apart
|
|
56
|
-
*
|
|
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
|
|
60
|
-
* no diagnostic vocabulary describes. The types reader draws the same line.
|
|
61
|
-
*/
|
|
62
6
|
/** A reader-versus-reader disagreement: not a schema problem, and not recoverable. */
|
|
63
7
|
export declare class PrismaFieldProjectionError extends Error {
|
|
64
8
|
readonly name = "PrismaFieldProjectionError";
|
|
65
9
|
}
|
|
66
|
-
/**
|
|
67
|
-
* `Omit` rather than a restatement, so the composed field is the core type by
|
|
68
|
-
* construction.
|
|
69
|
-
*/
|
|
70
10
|
export type ProjectedScalarField = Omit<AdapterScalarField<PrismaAdapterModel>, "capabilities">;
|
|
71
11
|
export type ProjectedRelationField = Omit<AdapterRelationField<PrismaAdapterModel>, "capabilities">;
|
|
72
12
|
/**
|
|
73
13
|
* One relation edge as PSL states it — the pairing evidence and the referential
|
|
74
14
|
* actions, neither of which the projected field carries.
|
|
75
|
-
*
|
|
76
|
-
* # Why the actions ride here and not on `AdapterRelationField`
|
|
77
|
-
*
|
|
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.
|
|
81
|
-
*
|
|
82
|
-
* # The lookup is inverted, and the edge is shaped so a consumer need not invert
|
|
83
|
-
*
|
|
84
|
-
* `onDelete` is declared on the CHILD — `Kid.parent @relation(…, onDelete:
|
|
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.
|
|
90
|
-
*
|
|
91
|
-
* `null` on both members means this edge is not the owning side (it declares no
|
|
92
|
-
* `fields:`), so the actions are on its partner — never that no action applies.
|
|
93
15
|
*/
|
|
94
16
|
export type PrismaRelationEdge = {
|
|
95
17
|
/** The model DECLARING the edge — the child, where an owning edge is concerned. */
|
|
@@ -100,11 +22,6 @@ export type PrismaRelationEdge = {
|
|
|
100
22
|
readonly cardinality: "one" | "many";
|
|
101
23
|
/** `@relation("Name")`, or `null` where PSL wrote none. PSL-only. */
|
|
102
24
|
readonly relationName: string | null;
|
|
103
|
-
/**
|
|
104
|
-
* What deleting a `target` row does to this `resource` row, resolved (an
|
|
105
|
-
* absent PSL argument already carries Prisma's measured default). `null` on a
|
|
106
|
-
* non-owning edge.
|
|
107
|
-
*/
|
|
108
25
|
readonly onDelete: PrismaReferentialAction | null;
|
|
109
26
|
/** The same for an update of the referenced key. `null` on a non-owning edge. */
|
|
110
27
|
readonly onUpdate: PrismaReferentialAction | null;
|
|
@@ -115,7 +32,7 @@ export type FieldProjectionInput = {
|
|
|
115
32
|
readonly field: PrismaSchemaField;
|
|
116
33
|
/** The model's other fields: the only way to identify an FK scalar by name. */
|
|
117
34
|
readonly siblings: readonly PrismaSchemaField[];
|
|
118
|
-
/** TYPE — what this client exposes.
|
|
35
|
+
/** TYPE — what this client exposes. */
|
|
119
36
|
readonly surface: PrismaResourceSurface;
|
|
120
37
|
/** TYPE — `enums.ts`, which is what makes an enum name an enum name. */
|
|
121
38
|
readonly enums: Readonly<Record<string, readonly string[]>>;
|
|
@@ -126,7 +43,6 @@ export type FieldProjection = {
|
|
|
126
43
|
readonly outcome: "scalar";
|
|
127
44
|
readonly name: string;
|
|
128
45
|
readonly field: ProjectedScalarField;
|
|
129
|
-
/** Recorded unknowns; a non-empty list FAILS the generate step. */
|
|
130
46
|
readonly unknowns: readonly PrismaSchemaUnknown[];
|
|
131
47
|
} | {
|
|
132
48
|
readonly outcome: "relation";
|
|
@@ -158,10 +74,6 @@ export type PrismaDefaultOutcome =
|
|
|
158
74
|
readonly form: "unrecognised-generator";
|
|
159
75
|
readonly traits: readonly AdapterLifecycleTrait[];
|
|
160
76
|
};
|
|
161
|
-
/**
|
|
162
|
-
* The argument arrives as opaque text precisely so that this function can be a
|
|
163
|
-
* lookup.
|
|
164
|
-
*/
|
|
165
77
|
export declare function matchPrismaDefault(argument: string): PrismaDefaultOutcome;
|
|
166
78
|
/** Why a field is legitimately optional in `<M>UncheckedCreateInput`. */
|
|
167
79
|
export type CreateOptionalCause = AdapterLifecycleTrait | "nullable"
|
|
@@ -192,16 +104,7 @@ export type CreateOptionalCrossCheck = {
|
|
|
192
104
|
readonly unexplained: readonly string[];
|
|
193
105
|
readonly messages: readonly string[];
|
|
194
106
|
};
|
|
195
|
-
/**
|
|
196
|
-
* TYPE's only contribution to lifecycle: a check, run in one direction.
|
|
197
|
-
*
|
|
198
|
-
* Every field the 🟢 `<M>UncheckedCreateInput` marks optional must carry a PSL
|
|
199
|
-
* cause. A field the types mark optional with no cause means **one of the two
|
|
200
|
-
* readers is wrong** — that is the entire signal, and it is why the marker is
|
|
201
|
-
* never read as a trait. Names the types mark that this model does not project
|
|
202
|
-
* as a scalar (relation edges) are skipped rather than reported: their
|
|
203
|
-
* optionality is structural.
|
|
204
|
-
*/
|
|
107
|
+
/** TYPE's only contribution to lifecycle: a check, run in one direction. */
|
|
205
108
|
export declare function crossCheckCreateOptional(input: CreateOptionalCrossCheckInput): CreateOptionalCrossCheck;
|
|
206
109
|
export type PrismaRelationPair = readonly [
|
|
207
110
|
PrismaRelationEdge,
|
|
@@ -212,19 +115,5 @@ export type PrismaRelationPairing = {
|
|
|
212
115
|
/** Edges nothing pairs: a back-reference the schema never declared, or an ambiguity. */
|
|
213
116
|
readonly unpaired: readonly PrismaRelationEdge[];
|
|
214
117
|
};
|
|
215
|
-
/**
|
|
216
|
-
* Pairs each edge with its back-reference, which **only PSL can do**.
|
|
217
|
-
*
|
|
218
|
-
* An explicit `@relation("Name")` is the pairing evidence, and for the two
|
|
219
|
-
* `Author` → `Author` edges it is the *only* evidence: the name appears nowhere
|
|
220
|
-
* on the 🟢 surface, so from the types alone there are two self-edges and
|
|
221
|
-
* nothing to say they are one relation.
|
|
222
|
-
*
|
|
223
|
-
* Unnamed edges are paired by the unordered model pair, which is unambiguous
|
|
224
|
-
* because PSL requires a name as soon as two relations connect the same two
|
|
225
|
-
* models. Where an unordered pair still has more than two edges, they are left
|
|
226
|
-
* unpaired rather than guessed: the implicit name Prisma would compute lives in
|
|
227
|
-
* the 🛑 runtime data model, and re-deriving it here would be this module
|
|
228
|
-
* reimplementing the generator.
|
|
229
|
-
*/
|
|
118
|
+
/** Pairs each edge with its back-reference, which **only PSL can do**. */
|
|
230
119
|
export declare function pairPrismaRelationEdges(edges: readonly PrismaRelationEdge[]): PrismaRelationPairing;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LIFECYCLE_TRAITS, } from "@aventara/core";
|
|
1
|
+
import { LIFECYCLE_TRAITS, } from "@aventara/core/internal";
|
|
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";
|
|
@@ -44,10 +44,10 @@ function assertTokenAgrees(resource, field, token) {
|
|
|
44
44
|
const listed = token.endsWith("[]");
|
|
45
45
|
const base = listed ? token.slice(0, -2) : token;
|
|
46
46
|
if (base !== field.type) {
|
|
47
|
-
throw new PrismaFieldProjectionError(`${resource}.${field.name} is written \`${field.type}\` in schema.prisma and the
|
|
47
|
+
throw new PrismaFieldProjectionError(`${resource}.${field.name} is written \`${field.type}\` in schema.prisma and the generated client's types carry the FieldRef token \`${token}\`. The two readers describe one instant, so this is a stale generated client or a reader defect, not a schema the adapter can translate.`);
|
|
48
48
|
}
|
|
49
49
|
if (listed !== field.list) {
|
|
50
|
-
throw new PrismaFieldProjectionError(`${resource}.${field.name} is ${field.list ? "a list" : "not a list"} in schema.prisma and the
|
|
50
|
+
throw new PrismaFieldProjectionError(`${resource}.${field.name} is ${field.list ? "a list" : "not a list"} in schema.prisma and the generated client's types carry the FieldRef token \`${token}\`, which says the opposite.`);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
const ADAPTER_TRAIT_ORDER = LIFECYCLE_TRAITS.filter((trait) => trait === "DEFAULTED" ||
|
|
@@ -138,20 +138,20 @@ function projectRelationField(input) {
|
|
|
138
138
|
outcome: "excluded",
|
|
139
139
|
diagnostic: {
|
|
140
140
|
code: "not-exposed",
|
|
141
|
-
message: `${column} is declared in schema.prisma as an edge to \`${field.type}\` and absent from the generated
|
|
141
|
+
message: `${column} is declared in schema.prisma as an edge to \`${field.type}\` and absent from the generated client's types, so it is omitted: ${field.ignored ? "it carries `@ignore`" : "no schema attribute explains it — a provider drop or a preview-feature exclusion"}.`,
|
|
142
142
|
},
|
|
143
143
|
};
|
|
144
144
|
}
|
|
145
145
|
const cardinality = field.list ? "many" : "one";
|
|
146
146
|
const nullable = cardinality === "many" ? false : field.optional;
|
|
147
147
|
if (exposed.many !== field.list) {
|
|
148
|
-
throw new PrismaFieldProjectionError(`${column} is ${field.list ? "a list" : "a single edge"} in schema.prisma and the
|
|
148
|
+
throw new PrismaFieldProjectionError(`${column} is ${field.list ? "a list" : "a single edge"} in schema.prisma and the generated client's payload type says the opposite.`);
|
|
149
149
|
}
|
|
150
150
|
if (exposed.nullable !== nullable) {
|
|
151
|
-
throw new PrismaFieldProjectionError(`${column} is ${nullable ? "optional" : "required"} in schema.prisma and the
|
|
151
|
+
throw new PrismaFieldProjectionError(`${column} is ${nullable ? "optional" : "required"} in schema.prisma and the generated client's payload type ${exposed.nullable ? "admits" : "refuses"} \`null\`.`);
|
|
152
152
|
}
|
|
153
153
|
if (exposed.target !== field.type) {
|
|
154
|
-
throw new PrismaFieldProjectionError(`${column} targets \`${field.type}\` in schema.prisma and \`${exposed.target}\`
|
|
154
|
+
throw new PrismaFieldProjectionError(`${column} targets \`${field.type}\` in schema.prisma and \`${exposed.target}\` in the generated client's payload type.`);
|
|
155
155
|
}
|
|
156
156
|
assertForeignKeyAgrees(column, field, siblings, nullable);
|
|
157
157
|
return {
|