@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.
- package/LICENSE +91 -0
- package/LICENSE-ADDITIONAL-PERMISSION.md +9 -0
- package/README.md +564 -0
- package/dist/discovery/artifact.loader.d.ts +26 -0
- package/dist/discovery/artifact.loader.js +124 -0
- package/dist/discovery/capability-surface.interface.d.ts +191 -0
- package/dist/discovery/capability-surface.interface.js +13 -0
- package/dist/discovery/capability.deriver.d.ts +92 -0
- package/dist/discovery/capability.deriver.js +350 -0
- package/dist/discovery/discovery-artifact.interface.d.ts +210 -0
- package/dist/discovery/discovery-artifact.interface.js +1 -0
- package/dist/discovery/enum.mapper.d.ts +59 -0
- package/dist/discovery/enum.mapper.js +32 -0
- package/dist/discovery/field.mapper.d.ts +244 -0
- package/dist/discovery/field.mapper.js +387 -0
- package/dist/discovery/identifier.mapper.d.ts +188 -0
- package/dist/discovery/identifier.mapper.js +132 -0
- package/dist/discovery/model.discoverer.d.ts +108 -0
- package/dist/discovery/model.discoverer.js +341 -0
- package/dist/discovery/operations.deriver.d.ts +75 -0
- package/dist/discovery/operations.deriver.js +92 -0
- package/dist/discovery/scalar.mapper.d.ts +81 -0
- package/dist/discovery/scalar.mapper.js +51 -0
- package/dist/discovery/schema-facts.interface.d.ts +174 -0
- package/dist/discovery/schema-facts.interface.js +11 -0
- package/dist/discovery/schema.reader.d.ts +162 -0
- package/dist/discovery/schema.reader.js +692 -0
- package/dist/discovery/types.reader.d.ts +24 -0
- package/dist/discovery/types.reader.js +617 -0
- package/dist/discovery/vocabulary.dictionary.d.ts +258 -0
- package/dist/discovery/vocabulary.dictionary.js +429 -0
- package/dist/errors/error.normalizer.d.ts +250 -0
- package/dist/errors/error.normalizer.js +135 -0
- package/dist/errors/prisma-error.reader.d.ts +144 -0
- package/dist/errors/prisma-error.reader.js +167 -0
- package/dist/execution/first-variant.emulator.d.ts +64 -0
- package/dist/execution/first-variant.emulator.js +205 -0
- package/dist/execution/operation.executor.d.ts +87 -0
- package/dist/execution/operation.executor.js +210 -0
- package/dist/execution/result.shaper.d.ts +103 -0
- package/dist/execution/result.shaper.js +141 -0
- package/dist/execution/transaction.executor.d.ts +147 -0
- package/dist/execution/transaction.executor.js +13 -0
- package/dist/generate/artifact.emitter.d.ts +50 -0
- package/dist/generate/artifact.emitter.js +105 -0
- package/dist/generate/declared-support.reader.d.ts +33 -0
- package/dist/generate/declared-support.reader.js +74 -0
- package/dist/generate/discovery.bin.d.ts +2 -0
- package/dist/generate/discovery.bin.js +15 -0
- package/dist/generate/discovery.cli.d.ts +24 -0
- package/dist/generate/discovery.cli.js +163 -0
- package/dist/generate/existence.gate.d.ts +73 -0
- package/dist/generate/existence.gate.js +91 -0
- package/dist/generate/index.d.ts +81 -0
- package/dist/generate/index.js +65 -0
- package/dist/generate/node-version.guard.d.ts +8 -0
- package/dist/generate/node-version.guard.js +59 -0
- package/dist/generate/prisma-cli.resolver.d.ts +48 -0
- package/dist/generate/prisma-cli.resolver.js +95 -0
- package/dist/generate/prisma.generate.d.ts +164 -0
- package/dist/generate/prisma.generate.js +278 -0
- package/dist/generate/reader-crosscheck.d.ts +156 -0
- package/dist/generate/reader-crosscheck.js +202 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1 -0
- package/dist/prisma-client.interface.d.ts +48 -0
- package/dist/prisma-client.interface.js +9 -0
- package/dist/prisma.adapter.d.ts +55 -0
- package/dist/prisma.adapter.js +105 -0
- package/dist/provider/provider-profile.d.ts +53 -0
- package/dist/provider/provider-profile.js +91 -0
- package/dist/translation/identifier.addressing.d.ts +80 -0
- package/dist/translation/identifier.addressing.js +30 -0
- package/dist/translation/mutation.translator.d.ts +115 -0
- package/dist/translation/mutation.translator.js +580 -0
- package/dist/translation/operation.translator.d.ts +273 -0
- package/dist/translation/operation.translator.js +377 -0
- package/dist/translation/order-pagination.translator.d.ts +86 -0
- package/dist/translation/order-pagination.translator.js +159 -0
- package/dist/translation/projection.translator.d.ts +115 -0
- package/dist/translation/projection.translator.js +151 -0
- package/dist/translation/scalar.conversion.d.ts +101 -0
- package/dist/translation/scalar.conversion.js +221 -0
- package/dist/translation/where.translator.d.ts +79 -0
- package/dist/translation/where.translator.js +254 -0
- package/package.json +102 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { canonicalizeJson } from "@aventara/core";
|
|
2
|
+
/**
|
|
3
|
+
* T-3.2 — `discover()`'s whole runtime half.
|
|
4
|
+
*
|
|
5
|
+
* This module builds no TypeScript program, parses no PSL and calls no Prisma
|
|
6
|
+
* tooling, and that is guaranteed by construction rather than by assertion:
|
|
7
|
+
* its transitive import graph is this file, `discovery-artifact.interface.ts`
|
|
8
|
+
* and `@aventara/core`. Nothing under `generate/` is reachable from here, and
|
|
9
|
+
* neither is `typescript`, `prisma`, `@prisma/*`, `node:child_process` or
|
|
10
|
+
* `node:fs`. `artifact.loader.spec.ts` walks that graph from source and fails
|
|
11
|
+
* if a specifier appears that should not.
|
|
12
|
+
*
|
|
13
|
+
* The loader reads no file at all: the artifact arrives as a value, and hashing
|
|
14
|
+
* uses `globalThis.crypto.subtle`, so it needs no import either. It checks the
|
|
15
|
+
* two facts the runtime can still get wrong — that the artifact matches itself
|
|
16
|
+
* (`modelHash`) and that the installed `@prisma/client` major is the one it was
|
|
17
|
+
* generated against (C-846) — and nothing else. The model is derived at
|
|
18
|
+
* generate time from the schema and the generated client read in the same
|
|
19
|
+
* instant and cross-checked there, so the runtime never sees the schema and
|
|
20
|
+
* never asks after it.
|
|
21
|
+
*/
|
|
22
|
+
/** The `sha256:<hex>` convention core already uses for Contract identity. */
|
|
23
|
+
const HASH_PREFIX = "sha256:";
|
|
24
|
+
/**
|
|
25
|
+
* A stale or corrupt artifact is answered the way cross-phase invariant 15
|
|
26
|
+
* answers a Contract-hash mismatch: fail hard, and say what to run.
|
|
27
|
+
*/
|
|
28
|
+
const REGENERATION_INSTRUCTION = "Regenerate the discovery artifact with the adapter's generate step " +
|
|
29
|
+
"(`aventara-prisma7-generate`) and redeploy it " +
|
|
30
|
+
"alongside the schema and client it was generated from.";
|
|
31
|
+
/** Every artifact failure is this class, and every one of them says how to fix it. */
|
|
32
|
+
export class PrismaDiscoveryArtifactError extends Error {
|
|
33
|
+
constructor(reason) {
|
|
34
|
+
super(`${reason} ${REGENERATION_INSTRUCTION}`);
|
|
35
|
+
this.name = "PrismaDiscoveryArtifactError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function hex(digest) {
|
|
39
|
+
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
40
|
+
}
|
|
41
|
+
async function sha256(text) {
|
|
42
|
+
const bytes = new TextEncoder().encode(text);
|
|
43
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
44
|
+
return `${HASH_PREFIX}${hex(digest)}`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The identity of a discovered model: one canonicalization, one digest — over
|
|
48
|
+
* the model and **nothing else**. The artifact's `translation` half is Prisma
|
|
49
|
+
* addressing, and hashing it would put a DB-side rename into the Contract
|
|
50
|
+
* identity; its `referentialActions` half is what a delete does below the ORM
|
|
51
|
+
* entrypoint, and hashing it would put a foreign-key action into the Contract
|
|
52
|
+
* identity and into every generated client.
|
|
53
|
+
*/
|
|
54
|
+
export async function computeModelHash(model) {
|
|
55
|
+
return sha256(canonicalizeJson(model));
|
|
56
|
+
}
|
|
57
|
+
function isRecord(value) {
|
|
58
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
59
|
+
}
|
|
60
|
+
function majorOf(version) {
|
|
61
|
+
const major = /^(\d+)\./.exec(version)?.[1];
|
|
62
|
+
return major;
|
|
63
|
+
}
|
|
64
|
+
/** Shape validation, before any hash is computed over a value that may not have one. */
|
|
65
|
+
function assertArtifactShape(artifact) {
|
|
66
|
+
if (!isRecord(artifact)) {
|
|
67
|
+
throw new PrismaDiscoveryArtifactError("The discovery artifact is not a JSON object.");
|
|
68
|
+
}
|
|
69
|
+
for (const key of ["prismaVersion", "provider", "modelHash"]) {
|
|
70
|
+
if (typeof artifact[key] !== "string") {
|
|
71
|
+
throw new PrismaDiscoveryArtifactError(`The discovery artifact's \`${key}\` is not a string.`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!isRecord(artifact.model)) {
|
|
75
|
+
throw new PrismaDiscoveryArtifactError("The discovery artifact's `model` is not a JSON object.");
|
|
76
|
+
}
|
|
77
|
+
// The two beside-the-model halves are checked as members, not as content: this
|
|
78
|
+
// function asserts the artifact IS a `PrismaDiscoveryArtifact`, and an
|
|
79
|
+
// assertion that skipped a declared member would be a lie its consumer pays
|
|
80
|
+
// for. Their entries are not walked here — the runtime half hashes and returns
|
|
81
|
+
// the model, the executor reads its own addressing keys, and a cascade planner
|
|
82
|
+
// reads its own edges.
|
|
83
|
+
//
|
|
84
|
+
// `referentialActions` is checked for the same reason as `translation` and with
|
|
85
|
+
// the same consequence: an artifact emitted before the key existed is refused
|
|
86
|
+
// here and regenerated, rather than reaching a planner as `undefined`.
|
|
87
|
+
for (const key of ["translation", "referentialActions"]) {
|
|
88
|
+
if (!isRecord(artifact[key])) {
|
|
89
|
+
throw new PrismaDiscoveryArtifactError(`The discovery artifact's \`${key}\` is not a JSON object.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
for (const key of ["scalars", "enums", "resources"]) {
|
|
93
|
+
if (!isRecord(artifact.model[key])) {
|
|
94
|
+
throw new PrismaDiscoveryArtifactError(`The discovery artifact's \`model.${key}\` is not a JSON object.`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (artifact.model.transactions !== "none" &&
|
|
98
|
+
artifact.model.transactions !== "interactive") {
|
|
99
|
+
throw new PrismaDiscoveryArtifactError("The discovery artifact's `model.transactions` is not a transaction capability.");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Validates an artifact and hands back its model **unchanged**.
|
|
104
|
+
*
|
|
105
|
+
* Three checks, in the order that makes each one's failure legible: shape, then
|
|
106
|
+
* the `prismaVersion` major fence (C-846), then `modelHash`.
|
|
107
|
+
*/
|
|
108
|
+
export async function loadDiscoveryArtifact(load) {
|
|
109
|
+
const { artifact, installedPrismaVersion } = load;
|
|
110
|
+
assertArtifactShape(artifact);
|
|
111
|
+
const artifactMajor = majorOf(artifact.prismaVersion);
|
|
112
|
+
const installedMajor = majorOf(installedPrismaVersion);
|
|
113
|
+
if (artifactMajor === undefined || installedMajor === undefined) {
|
|
114
|
+
throw new PrismaDiscoveryArtifactError(`A Prisma version is not a major.minor.patch version: the artifact declares \`${artifact.prismaVersion}\` and the installed client reports \`${installedPrismaVersion}\`.`);
|
|
115
|
+
}
|
|
116
|
+
if (artifactMajor !== installedMajor) {
|
|
117
|
+
throw new PrismaDiscoveryArtifactError(`The discovery artifact was generated against Prisma major ${artifactMajor}, and the installed client is major ${installedMajor}. The P-code surface and the generated-client grammar are version-bound (C-846).`);
|
|
118
|
+
}
|
|
119
|
+
const modelHash = await computeModelHash(artifact.model);
|
|
120
|
+
if (modelHash !== artifact.modelHash) {
|
|
121
|
+
throw new PrismaDiscoveryArtifactError("The discovery artifact's `modelHash` does not match its own `model`, so the artifact is corrupt or was edited by hand.");
|
|
122
|
+
}
|
|
123
|
+
return artifact.model;
|
|
124
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T-3, source **TYPE** — the output of the reader over the generated client's
|
|
3
|
+
* 🟢 public types.
|
|
4
|
+
*
|
|
5
|
+
* Nothing here is imported from `@prisma/client`: these are this adapter's own
|
|
6
|
+
* facts over public generated types, which is what keeps discovery a pure
|
|
7
|
+
* function of plain data and unit-testable without a client (T-3).
|
|
8
|
+
*
|
|
9
|
+
* Read `types.reader.ts`'s header for the two rules that give these facts their
|
|
10
|
+
* meaning: only the 🟢 files are opened, and token-shaped facts come from the
|
|
11
|
+
* **written** type arguments rather than from the instantiated reference.
|
|
12
|
+
*/
|
|
13
|
+
export type PrismaCapabilitySurface = {
|
|
14
|
+
/** The EXISTENCE GATE: an entity is discoverable iff it appears here (T-3.1 invariant 3). */
|
|
15
|
+
readonly resources: Readonly<Record<string, PrismaResourceSurface>>;
|
|
16
|
+
/** `enums.ts`'s `as const` objects, in property order. */
|
|
17
|
+
readonly enums: Readonly<Record<string, readonly string[]>>;
|
|
18
|
+
};
|
|
19
|
+
export type PrismaResourceSurface = {
|
|
20
|
+
/** `$<M>Payload.scalars` ∪ `<M>FieldRefs`. */
|
|
21
|
+
readonly scalarFields: Readonly<Record<string, PrismaScalarSurface>>;
|
|
22
|
+
readonly relationFields: Readonly<Record<string, PrismaRelationSurface>>;
|
|
23
|
+
/** `AtLeast<…, K>`'s key union — cross-check only; order unused. */
|
|
24
|
+
readonly uniqueKeys: readonly string[];
|
|
25
|
+
/** `<M>Delegate`'s own methods → `ResourceOperations`. */
|
|
26
|
+
readonly operations: readonly string[];
|
|
27
|
+
/** e.g. `updateMany` → `["data", "where", "limit"]`. */
|
|
28
|
+
readonly manyWriteArguments: Readonly<Record<string, readonly string[]>>;
|
|
29
|
+
};
|
|
30
|
+
export type PrismaScalarSurface = {
|
|
31
|
+
/** `FieldRef<"M", 'String'>` — the scalar token, or the enum's name. */
|
|
32
|
+
readonly token: string;
|
|
33
|
+
/** `<Kind>Filter` keys, in Prisma's spelling — NAMES ONLY. */
|
|
34
|
+
readonly filters: readonly string[];
|
|
35
|
+
/**
|
|
36
|
+
* The operand SHAPE per filter key, as written in the generated type — because
|
|
37
|
+
* two engines can carry the same key with different operands, and a name set
|
|
38
|
+
* cannot express that (D-802): `path` is `string[]` on PostgreSQL and `string`
|
|
39
|
+
* on SQLite, and `in`/`notIn` admit `List<Kind>FieldRefInput` on PostgreSQL
|
|
40
|
+
* only. Read, not tabulated — T-3.1 invariant 6.
|
|
41
|
+
*/
|
|
42
|
+
readonly filterOperands: Readonly<Record<string, string>>;
|
|
43
|
+
/**
|
|
44
|
+
* `<M>UncheckedCreateInput`'s entry for this field, read as the keys of the
|
|
45
|
+
* `…Input` it references: `[]` for a plain value, and **`null` where the client
|
|
46
|
+
* exposes no entry at all**, which is the only evidence that a column is not
|
|
47
|
+
* creatable.
|
|
48
|
+
*
|
|
49
|
+
* The UNCHECKED input is the source because it is what the delegate accepts:
|
|
50
|
+
* `create`'s argument is `XOR<<M>CreateInput, <M>UncheckedCreateInput>`, and the
|
|
51
|
+
* checked half omits every foreign-key scalar (the relation owns it there). Read
|
|
52
|
+
* from the checked half, `Post.authorId` and `Post.categoryId` come back absent
|
|
53
|
+
* and Aventara would refuse writes Prisma accepts.
|
|
54
|
+
*/
|
|
55
|
+
readonly createOperations: readonly string[] | null;
|
|
56
|
+
/**
|
|
57
|
+
* Whether `<M>UncheckedCreateInput` writes this member with a `?`.
|
|
58
|
+
*
|
|
59
|
+
* A **different fact** from {@link createOperations}, which is why both are
|
|
60
|
+
* recorded: `createOperations` answers *what may be written* — the directive
|
|
61
|
+
* keys, `[]` for a plain value and `null` where the client exposes no entry at
|
|
62
|
+
* all — and erases the question mark on the way. `createOptional` answers
|
|
63
|
+
* *whether a value may be omitted*, and nothing else on this surface carries
|
|
64
|
+
* it.
|
|
65
|
+
*
|
|
66
|
+
* It is **one bit over several causes** — `@default(…)`, `@updatedAt`, plain
|
|
67
|
+
* nullability, and a scalar list, which needs no value because the empty list
|
|
68
|
+
* is one — so it is never read as a lifecycle trait. Its only consumer is
|
|
69
|
+
* `field.mapper.ts`'s `crossCheckCreateOptional`, which runs it as a check in
|
|
70
|
+
* one direction: every member the types mark optional must have a PSL cause.
|
|
71
|
+
* Deriving a trait from it would make the two readers agree by construction
|
|
72
|
+
* and detect nothing.
|
|
73
|
+
*
|
|
74
|
+
* `false` where the client exposes no entry for the column at all, which
|
|
75
|
+
* {@link createOperations}`=== null` is the evidence for; the two never
|
|
76
|
+
* disagree about presence because they are read from the same member.
|
|
77
|
+
*/
|
|
78
|
+
readonly createOptional: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* `<M>UncheckedUpdateInput`'s entry, read the same way — the keys of
|
|
81
|
+
* `<Kind>FieldUpdateOperationsInput`, or of `<M>Update<field>Input` for a scalar
|
|
82
|
+
* list (`set`, `push`), or `[]` for a plain value (`Json`), or `null` where the
|
|
83
|
+
* client exposes no entry.
|
|
84
|
+
*
|
|
85
|
+
* Measured at 7.10.0 (2026-10-02): reading `<M>UpdateInput` instead under-reports
|
|
86
|
+
* six of the twenty-five scalar columns per engine — `Author.mentorId`,
|
|
87
|
+
* `Metric.authorId`, `Post.authorId`, `Post.categoryId`, `Category.id` and
|
|
88
|
+
* `Post.id` — every one of which `<M>UncheckedUpdateInput` admits.
|
|
89
|
+
*/
|
|
90
|
+
readonly updateOperations: readonly string[] | null;
|
|
91
|
+
readonly orderable: boolean;
|
|
92
|
+
/** `SortOrderInput` admitted for this field. */
|
|
93
|
+
readonly nullsOrderable: boolean;
|
|
94
|
+
};
|
|
95
|
+
export type PrismaRelationSurface = {
|
|
96
|
+
/** Read off `$<Target>Payload`'s own `name` literal, never off the alias's spelling. */
|
|
97
|
+
readonly target: string;
|
|
98
|
+
readonly many: boolean;
|
|
99
|
+
readonly nullable: boolean;
|
|
100
|
+
/** `ListRelationFilter` vs `[Nullable]ScalarRelationFilter`. */
|
|
101
|
+
readonly filters: readonly string[];
|
|
102
|
+
/**
|
|
103
|
+
* `<T>CreateNested{One,Many}Without<M>Input` keys — the **create** stage's own
|
|
104
|
+
* nested-write input, read off `<M>CreateInput`'s entry for this edge.
|
|
105
|
+
*
|
|
106
|
+
* A SEPARATE fact from {@link updateMutations}, and the separation is the C-855
|
|
107
|
+
* invariant: the create-stage input carries `create`, `connectOrCreate`,
|
|
108
|
+
* `createMany` (to-many only) and `connect` and nothing else, where the
|
|
109
|
+
* update-stage input carries eight more. Reusing one list for both stages
|
|
110
|
+
* advertises five directives per to-many edge, and two per to-one edge, that
|
|
111
|
+
* Prisma has no key for at the create stage.
|
|
112
|
+
*/
|
|
113
|
+
readonly createMutations: readonly string[];
|
|
114
|
+
/** `<T>Update*Without<M>NestedInput` keys — the **update** stage's own input. */
|
|
115
|
+
readonly updateMutations: readonly string[];
|
|
116
|
+
/**
|
|
117
|
+
* `<M>$<edge>Args`'s own member names, as written. The edge's whole argument
|
|
118
|
+
* surface, because four separate capabilities are read off it — pagination,
|
|
119
|
+
* ordering, nested filtering of the returned edge, and projection — and a
|
|
120
|
+
* residual cannot answer the other three.
|
|
121
|
+
*/
|
|
122
|
+
readonly edgeArguments: readonly string[];
|
|
123
|
+
/**
|
|
124
|
+
* {@link edgeArguments} minus what projection and predicate already own — a
|
|
125
|
+
* derived projection kept because it is the one a to-one/to-many split reads at
|
|
126
|
+
* a glance: a to-one edge leaves nothing.
|
|
127
|
+
*/
|
|
128
|
+
readonly pagination: readonly string[];
|
|
129
|
+
/** The edge is admitted by `<M>OrderByWithRelationInput`. */
|
|
130
|
+
readonly orderable: boolean;
|
|
131
|
+
/** `_count` on `<M>Include` / `<M>CountOutputType`. */
|
|
132
|
+
readonly reducers: readonly string[];
|
|
133
|
+
/**
|
|
134
|
+
* Whether **every** `<M>Select{Create,Update}ManyAndReturn` the client declares
|
|
135
|
+
* lists this edge.
|
|
136
|
+
*
|
|
137
|
+
* # Presence, read. Not foreign-key ownership, computed
|
|
138
|
+
*
|
|
139
|
+
* The *explanation* for which edges survive into a bulk-returning write's
|
|
140
|
+
* projection is foreign-key ownership rather than cardinality — a to-one that
|
|
141
|
+
* does not hold its own FK is absent, which is what the `User`/`Profile`
|
|
142
|
+
* fixture exists to prove. That explanation is **not** the mechanism. The
|
|
143
|
+
* mechanism is this member: the edge is in the type, or it is not.
|
|
144
|
+
*
|
|
145
|
+
* Deriving FK ownership here instead would be a tabulated provider-dependent
|
|
146
|
+
* fact — the shape §18.4 forbids and D-802 exists to prevent — and it would be
|
|
147
|
+
* wrong the first time Prisma changes which relations it admits into the
|
|
148
|
+
* projection, while a presence read simply tells the truth again.
|
|
149
|
+
*
|
|
150
|
+
* **Every**, not *any*: one {@link ProjectionScope} covers both bulk-returning
|
|
151
|
+
* variants, so a scope the adapter advertises must hold in both. At 7.10.0 the
|
|
152
|
+
* two types agree member-for-member on both pilot engines, so the conjunction
|
|
153
|
+
* is currently indistinguishable from either half — `types.reader.spec.ts`
|
|
154
|
+
* asserts that agreement, so the day it stops holding is the day a reader
|
|
155
|
+
* reports it instead of silently picking a side. **Zero declared types answer
|
|
156
|
+
* `false`**, never vacuously `true`: a client with no `*AndReturn` type admits
|
|
157
|
+
* nothing there.
|
|
158
|
+
*/
|
|
159
|
+
readonly bulkSelect: boolean;
|
|
160
|
+
/** The {@link bulkSelect} question asked of `<M>Include{Create,Update}ManyAndReturn`. */
|
|
161
|
+
readonly bulkInclude: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Whether `_count` is listed by every one of those **four** types — the same
|
|
164
|
+
* presence question, asked of the reducer key rather than of the edge.
|
|
165
|
+
*
|
|
166
|
+
* A model-level fact carried on each edge because the capability it qualifies
|
|
167
|
+
* is the edge's: `RelationFieldCapabilities.reducers` says which reducers *this
|
|
168
|
+
* relation* admits, and `_count` is the one projection key all of them are
|
|
169
|
+
* asked for through.
|
|
170
|
+
*/
|
|
171
|
+
readonly bulkReducers: boolean;
|
|
172
|
+
/**
|
|
173
|
+
* Whether `<M>CreateManyInput` — the data argument of the bulk create — exposes
|
|
174
|
+
* an entry for this edge.
|
|
175
|
+
*
|
|
176
|
+
* The write-side mirror of {@link bulkSelect}, and read the same way: C-818's
|
|
177
|
+
* rule (*no nested `create`/`connect` inside a top-level `createMany()` or
|
|
178
|
+
* `createManyAndReturn()`*) is a **consequence** of the bulk input carrying no
|
|
179
|
+
* relation member, never a row this adapter keeps.
|
|
180
|
+
*/
|
|
181
|
+
readonly bulkCreatable: boolean;
|
|
182
|
+
/**
|
|
183
|
+
* Whether `<M>UpdateManyMutationInput` or `<M>UncheckedUpdateManyInput` exposes
|
|
184
|
+
* an entry for this edge.
|
|
185
|
+
*
|
|
186
|
+
* Both halves, because `updateMany`'s `data` is the `XOR` of them — exactly the
|
|
187
|
+
* reason {@link PrismaScalarSurface.updateOperations} reads the unchecked half:
|
|
188
|
+
* reading one input alone under-reports what the delegate accepts.
|
|
189
|
+
*/
|
|
190
|
+
readonly bulkUpdatable: boolean;
|
|
191
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T-3, source **TYPE** — the output of the reader over the generated client's
|
|
3
|
+
* 🟢 public types.
|
|
4
|
+
*
|
|
5
|
+
* Nothing here is imported from `@prisma/client`: these are this adapter's own
|
|
6
|
+
* facts over public generated types, which is what keeps discovery a pure
|
|
7
|
+
* function of plain data and unit-testable without a client (T-3).
|
|
8
|
+
*
|
|
9
|
+
* Read `types.reader.ts`'s header for the two rules that give these facts their
|
|
10
|
+
* meaning: only the 🟢 files are opened, and token-shaped facts come from the
|
|
11
|
+
* **written** type arguments rather than from the instantiated reference.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { RelationFieldCapabilities, ScalarFieldCapabilities, ScalarFilterOperator } from "@aventara/core";
|
|
2
|
+
import type { PrismaRelationSurface, PrismaScalarSurface } from "./capability-surface.interface.js";
|
|
3
|
+
/**
|
|
4
|
+
* T-6 — field capabilities, derived from the generated client's own types.
|
|
5
|
+
*
|
|
6
|
+
* # The input is narrower than T-6 writes it, and the narrowing IS the invariant
|
|
7
|
+
*
|
|
8
|
+
* T-6 spells `ScalarCapabilityInput` as `{ field, surface, mapping, profile }`.
|
|
9
|
+
* This module accepts `surface` alone, and that is deliberate: dictionary
|
|
10
|
+
* invariant 6 says a capability is read from the client's own types and never
|
|
11
|
+
* tabulated from a provider row, and the strongest available mechanism for that is
|
|
12
|
+
* to make the provider **unrepresentable** rather than merely unread. A
|
|
13
|
+
* `PrismaProviderProfile` in this input is a `provider` member one keystroke from
|
|
14
|
+
* a branch; with no such member, `if (provider === …)` is a compile error, and the
|
|
15
|
+
* three per-engine differences this pass exists to prove — `mode`, `JsonFilter`'s
|
|
16
|
+
* ordering operators, and the `path` / `in` / `notIn` operand shapes — have to come
|
|
17
|
+
* from the surface because nothing else is in scope.
|
|
18
|
+
*
|
|
19
|
+
* Dropping `field` has the same effect on C-812: a lifecycle trait cannot grant a
|
|
20
|
+
* capability if no lifecycle trait can be passed. `mapping` went with them because
|
|
21
|
+
* nothing here reads an Aventara scalar identity to answer a capability question.
|
|
22
|
+
*
|
|
23
|
+
* **This is a deviation from T-6 and is reported as one.** It predicted that the
|
|
24
|
+
* `path` question — omit, or translate, where the operand is a JSONPath string —
|
|
25
|
+
* would need `mapping` back. **Task 7 settled it and did not:** see
|
|
26
|
+
* {@link translatable}. The answer is read off the recorded operand shape, which
|
|
27
|
+
* is already on the surface; the Aventara scalar identity would have been a
|
|
28
|
+
* second, weaker source for the same fact. `mapping` stays out.
|
|
29
|
+
*
|
|
30
|
+
* # Order is canonical, not incidental
|
|
31
|
+
*
|
|
32
|
+
* Every list is emitted in **core's own union declaration order**, held below as a
|
|
33
|
+
* closed ordered array. Prisma's declaration order is an input, not a semantic
|
|
34
|
+
* fact, and C-843 wants two generate runs byte-identical; ordering by the Aventara
|
|
35
|
+
* union makes the output independent of how a generator happens to write a member
|
|
36
|
+
* list, while keeping the array reviewable against `contracts/capability.ts`.
|
|
37
|
+
*
|
|
38
|
+
* # Absent and empty are different answers
|
|
39
|
+
*
|
|
40
|
+
* `create: []` / `update: []` mean **writable with no directive** — plain
|
|
41
|
+
* assignment, which is what `set` alone is. An absent `create` / `update` means
|
|
42
|
+
* **not writable**, and core enforces exactly that: `validateScalarMutation`
|
|
43
|
+
* answers `V1006` *"is not writable during <stage>"* when the member is
|
|
44
|
+
* `undefined` **[CORE, operations/operation-validator-core.ts]**. So the two must
|
|
45
|
+
* not be conflated, and the surface's `null` (the client exposes no entry for this
|
|
46
|
+
* column at this stage) is the only thing that produces the absent form.
|
|
47
|
+
*/
|
|
48
|
+
/** A Prisma spelling that no dictionary table recognises: a build stop, never a drop. */
|
|
49
|
+
export declare class PrismaCapabilityDerivationError extends Error {
|
|
50
|
+
readonly name = "PrismaCapabilityDerivationError";
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* T-6's `ScalarCapabilityInput`, narrowed — see this module's header.
|
|
54
|
+
*
|
|
55
|
+
* An object rather than a bare parameter because T-6 names an input type and
|
|
56
|
+
* because a later pass may legitimately add `mapping` back.
|
|
57
|
+
*/
|
|
58
|
+
export type ScalarCapabilityInput = {
|
|
59
|
+
readonly surface: PrismaScalarSurface;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* A relation's capabilities are answered for the field, not for an operation.
|
|
63
|
+
*
|
|
64
|
+
* The `operation: "create.one" | "create.many"` parameter this input used to carry
|
|
65
|
+
* is **gone**. It existed because C-818's answer differs between the singular and
|
|
66
|
+
* the bulk create and the capability shape had nowhere to put the difference, so
|
|
67
|
+
* composition had to pick one variant's answer and the other was unrepresentable —
|
|
68
|
+
* `model.discoverer.ts` recorded that as an open conflict. The scope-qualified
|
|
69
|
+
* members hold both answers at once, so there is nothing left to pick and no caller
|
|
70
|
+
* left to pick it.
|
|
71
|
+
*/
|
|
72
|
+
export type RelationCapabilityInput = {
|
|
73
|
+
readonly surface: PrismaRelationSurface;
|
|
74
|
+
};
|
|
75
|
+
export declare function deriveScalarCapabilities(input: ScalarCapabilityInput): ScalarFieldCapabilities;
|
|
76
|
+
/**
|
|
77
|
+
* The Prisma operand for each ADVERTISED operator, keyed by the Aventara name.
|
|
78
|
+
*
|
|
79
|
+
* T-6's three signatures return core types unchanged, and `ScalarFieldCapabilities`
|
|
80
|
+
* has nowhere to put an operand shape — yet the shape is exactly where two engines
|
|
81
|
+
* differ on keys they both carry (`JsonFilter.path` is `string[]` on one and
|
|
82
|
+
* `string` on the other; `in`/`notIn` admit `List<Kind>FieldRefInput` on one only).
|
|
83
|
+
* A deriver that answered operator NAMES alone would report the two engines
|
|
84
|
+
* identical where they are not, so the shape is carried beside the capability
|
|
85
|
+
* rather than discarded.
|
|
86
|
+
*
|
|
87
|
+
* Keyed by the Aventara operator so Task 7 reads a shape without re-running the
|
|
88
|
+
* rename — and so a provider branch has nothing to be for (§Open question,
|
|
89
|
+
* "How the `path` filter operator is emitted per provider").
|
|
90
|
+
*/
|
|
91
|
+
export declare function deriveScalarFilterOperands(input: ScalarCapabilityInput): Readonly<Partial<Record<ScalarFilterOperator, string>>>;
|
|
92
|
+
export declare function deriveRelationCapabilities(input: RelationCapabilityInput): RelationFieldCapabilities;
|