@aventara/prisma7-adapter 0.1.0-pilot.2 → 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.
- package/README.md +22 -22
- package/dist/discovery/artifact.loader.d.ts +2 -7
- package/dist/discovery/artifact.loader.js +4 -0
- package/dist/discovery/capability-surface.interface.d.ts +2 -66
- package/dist/discovery/capability.deriver.d.ts +2 -47
- package/dist/discovery/discovery-artifact.interface.d.ts +34 -155
- 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 +3 -114
- package/dist/discovery/field.mapper.js +6 -6
- package/dist/discovery/identifier.mapper.d.ts +4 -84
- package/dist/discovery/model.discoverer.d.ts +2 -69
- package/dist/discovery/operations.deriver.d.ts +0 -36
- 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 +7 -7
- package/dist/discovery/vocabulary.dictionary.d.ts +6 -133
- 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 +1 -62
- package/dist/execution/result.shaper.d.ts +0 -56
- package/dist/execution/transaction.executor.d.ts +0 -98
- package/dist/generate/artifact.emitter.d.ts +1 -35
- package/dist/generate/artifact.emitter.js +26 -43
- 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/provider/provider-profile.d.ts +6 -14
- package/dist/translation/identifier.addressing.d.ts +0 -38
- package/dist/translation/mutation.translator.d.ts +5 -51
- package/dist/translation/operation.translator.d.ts +1 -102
- package/dist/translation/order-pagination.translator.d.ts +1 -47
- package/dist/translation/projection.translator.d.ts +1 -46
- package/dist/translation/scalar.conversion.d.ts +0 -64
- package/dist/translation/where.translator.d.ts +0 -52
- package/package.json +3 -2
|
@@ -1,66 +1,13 @@
|
|
|
1
1
|
import type { AdapterLifecycleTrait, BuiltInScalar, PaginationCapability, RelationFilterCapability, RelationMutationDirective, ScalarFilterOperator, ScalarFormat, ScalarMutationDirective } from "@aventara/core";
|
|
2
|
-
/**
|
|
3
|
-
* # It is data, not conditionals
|
|
4
|
-
*
|
|
5
|
-
* # Every entry names its source class (invariant 1)
|
|
6
|
-
*
|
|
7
|
-
* `VOCABULARY` carries one row per Aventara fact with its source code — `PSL`,
|
|
8
|
-
* `TYPE`, `RUN`, `DRIVER` or `CFG`. `VocabularyEntry.sources` is a **non-empty**
|
|
9
|
-
* tuple, so "an entry with no source" is a compile error rather than a silent
|
|
10
|
-
* default.
|
|
11
|
-
*
|
|
12
|
-
* # `@default` is matched, never parsed (invariant 5)
|
|
13
|
-
*
|
|
14
|
-
* `PRISMA_DEFAULT_TABLE` is looked up with the raw text the PSL reader carried
|
|
15
|
-
* across unchanged. `dbgenerated("gen_random_uuid()")` is a **key**, not an
|
|
16
|
-
* expression: the day this module evaluates one, the reader behind it has become a
|
|
17
|
-
* parser and needs a different decision. An unmatched form falls back to
|
|
18
|
-
* `PRISMA_UNMATCHED_DEFAULT_TRAITS` **plus** a recorded unknown that fails the
|
|
19
|
-
* generate step — never an inferred trait.
|
|
20
|
-
*
|
|
21
|
-
* `@db.*` is matched the same way, by its **exact written spelling**
|
|
22
|
-
* (`PRISMA_NATIVE_FORMATS`). `@db.Decimal(10, 2)` matches no key and therefore
|
|
23
|
-
* proves no format, which is the right answer arrived at without splitting an
|
|
24
|
-
* argument list.
|
|
25
|
-
*
|
|
26
|
-
* # This dictionary proposes no `CONTEXT.md` term
|
|
27
|
-
*
|
|
28
|
-
* It is adapter-internal data, not framework vocabulary. Its job is the opposite:
|
|
29
|
-
* no Prisma term reaches `AdapterModel` (invariant 2) — `AtLeast`,
|
|
30
|
-
* `WhereUniqueInput`, `FieldRef`, `@@map`, `take`/`skip` all die here.
|
|
31
|
-
*/
|
|
32
|
-
/**
|
|
33
|
-
* `PSL` — `schema.prisma` · `TYPE` — the 🟢 generated types ·
|
|
34
|
-
* `RUN` — observed runtime behaviour · `DRIVER` — the configured Prisma driver
|
|
35
|
-
* adapter · `CFG` — framework configuration, which is policy and not discovery.
|
|
36
|
-
*/
|
|
37
2
|
export type VocabularySource = "PSL" | "TYPE" | "RUN" | "DRIVER" | "CFG";
|
|
38
3
|
export type VocabularyEntry = {
|
|
39
4
|
readonly fact: string;
|
|
40
|
-
/** Non-empty by construction: invariant 1 is enforced by the type, not by a comment. */
|
|
41
5
|
readonly sources: readonly [VocabularySource, ...VocabularySource[]];
|
|
42
6
|
readonly rule: string;
|
|
43
7
|
};
|
|
44
|
-
/**
|
|
45
|
-
* So the source is `PSL` **and** `CFG`: discovery emits what the provider's own
|
|
46
|
-
* column type proves, configuration owns everything else. `scalar.mapper.ts`'s
|
|
47
|
-
* header carries the full argument.
|
|
48
|
-
*/
|
|
49
8
|
export declare const VOCABULARY: readonly VocabularyEntry[];
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
* refusal is the dictionary's own answer: a construct it has no word for.
|
|
53
|
-
*/
|
|
54
|
-
export type PrismaDiscoveryDiagnosticCode =
|
|
55
|
-
/** A Prisma type name in neither PRISMA_SCALAR_TABLE nor `enums.ts`. */
|
|
56
|
-
"unmapped-prisma-type"
|
|
57
|
-
/**
|
|
58
|
-
* PSL declares it, the 🟢 types do not expose it: omitted, with the reason said
|
|
59
|
-
* out loud.
|
|
60
|
-
*/
|
|
61
|
-
| "not-exposed"
|
|
62
|
-
/** PSL and `enums.ts` disagree about an enum's members or their order. */
|
|
63
|
-
| "enum-diverged";
|
|
9
|
+
/** The closed set of ways a mapper refuses. */
|
|
10
|
+
export type PrismaDiscoveryDiagnosticCode = "unmapped-prisma-type" | "not-exposed" | "enum-diverged";
|
|
64
11
|
export type PrismaDiscoveryDiagnostic = {
|
|
65
12
|
readonly code: PrismaDiscoveryDiagnosticCode;
|
|
66
13
|
/** Written for the engineer who has to fix the schema, naming the entity it is about. */
|
|
@@ -71,39 +18,13 @@ export type PrismaScalarMapping = {
|
|
|
71
18
|
readonly format?: ScalarFormat;
|
|
72
19
|
};
|
|
73
20
|
export type PrismaScalarTable = Readonly<Record<string, PrismaScalarMapping>>;
|
|
74
|
-
/**
|
|
75
|
-
* Total and 1:1 over `BUILT_IN_SCALARS` **[CORE]**. There is no default arm: a
|
|
76
|
-
* Prisma type name absent from this table is a diagnostic, not a guess.
|
|
77
|
-
*
|
|
78
|
-
* **No row carries a `format`**, and that is the point: a format is a property of
|
|
79
|
-
* a *column*, not of a Prisma *type name*. `String` alone proves nothing, so the
|
|
80
|
-
* one thing that can prove something — the native column type — is a separate
|
|
81
|
-
* lookup (`PRISMA_NATIVE_FORMATS`).
|
|
82
|
-
*/
|
|
21
|
+
/** Total and 1:1 over `BUILT_IN_SCALARS` **[CORE]**. */
|
|
83
22
|
export declare const PRISMA_SCALAR_TABLE: PrismaScalarTable;
|
|
84
23
|
/**
|
|
85
24
|
* The only `@db.*` spellings that **prove** a format, matched by exact written
|
|
86
25
|
* spelling and never parsed.
|
|
87
|
-
*
|
|
88
|
-
* The bar for a row here is not "the name suggests a format". It is: **the column
|
|
89
|
-
* type itself rejects the values `format` would later reject.** `format` is
|
|
90
|
-
* enforced, not decorative — `normalizeScalarFieldValue` turns `format: "uuid"`
|
|
91
|
-
* into a `V1003` rejection of request input **[CORE,
|
|
92
|
-
* `runtime/scalar-runtime.ts`]** — so a row that is merely probable would have
|
|
93
|
-
* Aventara refuse writes the database accepts.
|
|
94
|
-
*
|
|
95
|
-
* `@db.Uuid` clears that bar: PostgreSQL's `uuid` column rejects `'not-a-uuid'`
|
|
96
|
-
* (`ERROR: invalid input syntax for type uuid`) where a `text` column takes it
|
|
97
|
-
* **[RUN8 — 2026-09-28, `aventara-postgres`, postgres:18]**.
|
|
98
|
-
*
|
|
99
|
-
* That intent belongs in `FrameworkScalarFieldConfig.format` **[CORE]**, which the
|
|
100
|
-
* Contract compiler already applies over the adapter's value.
|
|
101
|
-
*
|
|
102
|
-
* `@db.Decimal(10, 2)` and `@db.VarChar(255)` are absent because they prove no
|
|
103
|
-
* *format*; they carry precision and width, which Aventara has no word for.
|
|
104
26
|
*/
|
|
105
27
|
export declare const PRISMA_NATIVE_FORMATS: Readonly<Record<string, ScalarFormat>>;
|
|
106
|
-
/** Whether a row was exercised against 7.10.0 or is carried forward unproven. */
|
|
107
28
|
export type PrismaEvidence = "RUN8" | "UNVERIFIED";
|
|
108
29
|
export type PrismaDefaultRow = {
|
|
109
30
|
/**
|
|
@@ -119,13 +40,6 @@ export type PrismaDefaultRow = {
|
|
|
119
40
|
readonly traits: readonly AdapterLifecycleTrait[];
|
|
120
41
|
readonly evidence: PrismaEvidence;
|
|
121
42
|
};
|
|
122
|
-
/**
|
|
123
|
-
* This table states the answer rather than half of it, so no consumer has to
|
|
124
|
-
* remember to add the other half.
|
|
125
|
-
*
|
|
126
|
-
* The lookup that walks this table lives with the lifecycle projection, where it
|
|
127
|
-
* can be driven red; this pass owns the data only.
|
|
128
|
-
*/
|
|
129
43
|
export declare const PRISMA_DEFAULT_TABLE: readonly PrismaDefaultRow[];
|
|
130
44
|
/**
|
|
131
45
|
* Any other text — `"BRONZE"`, `"x"`, `sequence()`, `nanoid()`, a spelling added
|
|
@@ -138,42 +52,12 @@ export declare const PRISMA_UNMATCHED_DEFAULT_TRAITS: readonly AdapterLifecycleT
|
|
|
138
52
|
/**
|
|
139
53
|
* The dictionary's other half: the point at which Prisma's spelling stops and
|
|
140
54
|
* Aventara's begins.
|
|
141
|
-
*
|
|
142
|
-
* **One untyped `PRISMA_RENAMES` became six typed tables.** The single
|
|
143
|
-
* `Record<string, string>` could not make a mistake a compile error: a rename to a
|
|
144
|
-
* word core does not have, or a Prisma key nobody decided about, both typechecked.
|
|
145
|
-
* Each table below is keyed on the Prisma spelling and valued in the ONE Aventara
|
|
146
|
-
* vocabulary that spelling can land in, so `"arrayContains"` misspelt fails to
|
|
147
|
-
* compile. The table had no consumer, so nothing was broken by splitting it.
|
|
148
|
-
*
|
|
149
|
-
* `null` is a value, not an omission: it means **recognised, and Aventara has no
|
|
150
|
-
* word for it** — the nested bulk writes, `distinct`, Prisma's `set` assignment.
|
|
151
|
-
*
|
|
152
|
-
* This is the vocabulary half only.
|
|
153
|
-
*/
|
|
154
|
-
/**
|
|
155
|
-
* `<Kind>Filter` / `<Kind>NullableFilter` / `Json[Nullable]Filter` keys.
|
|
156
|
-
*
|
|
157
|
-
* Total and 1:1 over `ScalarFilterOperator` **[CORE]** at 7.10.0, measured over
|
|
158
|
-
* both generated clients: 23 Prisma keys, 23 Aventara operators, no survivor on
|
|
159
|
-
* either side.
|
|
160
55
|
*/
|
|
56
|
+
/** `<Kind>Filter` / `<Kind>NullableFilter` / `Json[Nullable]Filter` keys. */
|
|
161
57
|
export declare const PRISMA_FILTER_OPERATORS: Readonly<Record<string, ScalarFilterOperator>>;
|
|
162
|
-
/**
|
|
163
|
-
* `[Nullable]ScalarRelationFilter` and `ListRelationFilter` keys.
|
|
164
|
-
*
|
|
165
|
-
* `nested` and `null` are **not** keys of any filter input — they are read off the
|
|
166
|
-
* edge's own `where` argument and off the edge's nullability — so they are absent
|
|
167
|
-
* here by construction rather than by omission.
|
|
168
|
-
*/
|
|
58
|
+
/** `[Nullable]ScalarRelationFilter` and `ListRelationFilter` keys. */
|
|
169
59
|
export declare const PRISMA_RELATION_FILTERS: Readonly<Record<string, RelationFilterCapability>>;
|
|
170
|
-
/**
|
|
171
|
-
* `<Kind>FieldUpdateOperationsInput` and `<M><Stage><field>Input` keys.
|
|
172
|
-
*
|
|
173
|
-
* `set` is `null` because plain assignment is not a directive in Aventara: the
|
|
174
|
-
* value is written directly, and `ScalarMutationDirective` has no `$set`
|
|
175
|
-
* **[CORE]**.
|
|
176
|
-
*/
|
|
60
|
+
/** `<Kind>FieldUpdateOperationsInput` and `<M><Stage><field>Input` keys. */
|
|
177
61
|
export declare const PRISMA_SCALAR_DIRECTIVES: Readonly<Record<string, ScalarMutationDirective | null>>;
|
|
178
62
|
/**
|
|
179
63
|
* `<M>Update<edge>Without<M>NestedInput` / `<M>Create<edge>…NestedInput` keys.
|
|
@@ -205,15 +89,4 @@ export type ObservedValueShape = {
|
|
|
205
89
|
readonly engines: readonly string[];
|
|
206
90
|
readonly evidence: PrismaEvidence;
|
|
207
91
|
};
|
|
208
|
-
/**
|
|
209
|
-
* Identical through both driver adapters, so the representation is not
|
|
210
|
-
* driver-dependent *between these two engines* — which is a narrower claim than
|
|
211
|
-
* "not driver-dependent", and the narrower one is what was measured.
|
|
212
|
-
*
|
|
213
|
-
* A row is `RUN8` only where a read-back value was actually asserted. `String`,
|
|
214
|
-
* `Boolean`, `Int` and `Float` were written into a row and never read back under
|
|
215
|
-
* assertion, so they stay `[UNVERIFIED]`: a plausible value shape is not an
|
|
216
|
-
* observed one, and this table exists precisely so that nobody has to guess which
|
|
217
|
-
* is which.
|
|
218
|
-
*/
|
|
219
92
|
export declare const OBSERVED_VALUE_SHAPES: readonly ObservedValueShape[];
|
|
@@ -7,7 +7,7 @@ export const VOCABULARY = [
|
|
|
7
7
|
{
|
|
8
8
|
fact: "nullable",
|
|
9
9
|
sources: ["PSL"],
|
|
10
|
-
rule: "Never TYPE
|
|
10
|
+
rule: "Never TYPE: `JsonValue` contains `null` because JSON null is a value, so a types-derived nullable is wrong for every Json field.",
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
fact: "list",
|
|
@@ -32,7 +32,7 @@ export const VOCABULARY = [
|
|
|
32
32
|
{
|
|
33
33
|
fact: "relation pairing",
|
|
34
34
|
sources: ["PSL"],
|
|
35
|
-
rule: '`@relation("Mentorship")` on both sides is the only pairing evidence; the name appears nowhere
|
|
35
|
+
rule: '`@relation("Mentorship")` on both sides is the only pairing evidence; the name appears nowhere in the generated client.',
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
fact: "lifecycle traits",
|
|
@@ -1,130 +1,5 @@
|
|
|
1
1
|
import { FrameworkError, type OperationErrorCode, type OperationIdentity } from "@aventara/core";
|
|
2
2
|
import { type PrismaConstraintSubject } from "./prisma-error.reader.js";
|
|
3
|
-
/**
|
|
4
|
-
* The table's keys are P-codes, which are **7.x's** error surface. Prisma 8
|
|
5
|
-
* replaced them with dotted `NAMESPACE.SUBCODE` codes, so a Prisma 8 adapter
|
|
6
|
-
* re-maps this module rather than patching it. `error.normalizer.spec.ts` reads
|
|
7
|
-
* `@prisma/client`'s own `package.json` and fails if the installed major is not
|
|
8
|
-
* `7`, so the bound is enforced rather than described.
|
|
9
|
-
*
|
|
10
|
-
* ## Every code in the table was induced, not inherited
|
|
11
|
-
*
|
|
12
|
-
* Nothing here rests on documentation. Each rule was produced by throwing a real
|
|
13
|
-
* statement at a real engine on this repository's own schema, on
|
|
14
|
-
* `@prisma/adapter-better-sqlite3` and `@prisma/adapter-pg`, both 7.10.0. The
|
|
15
|
-
* measured payloads, inlined because the source measurement lives outside the
|
|
16
|
-
* repository:
|
|
17
|
-
*
|
|
18
|
-
* ```text
|
|
19
|
-
* P2002 author.create duplicate @unique email
|
|
20
|
-
* sqlite meta = { driverAdapterError: { name: "DriverAdapterError", cause: {
|
|
21
|
-
* originalCode: "SQLITE_CONSTRAINT_UNIQUE",
|
|
22
|
-
* originalMessage: "UNIQUE constraint failed: Author.email",
|
|
23
|
-
* kind: "UniqueConstraintViolation",
|
|
24
|
-
* constraint: { fields: ["email"] }, table: "Author" } },
|
|
25
|
-
* modelName: "Author" }
|
|
26
|
-
* pg meta = { driverAdapterError: { name: "DriverAdapterError", cause: {
|
|
27
|
-
* originalCode: "23505",
|
|
28
|
-
* originalMessage: "duplicate key value violates unique constraint \"Author_email_key\"",
|
|
29
|
-
* kind: "UniqueConstraintViolation",
|
|
30
|
-
* constraint: { index: "Author_email_key" }, table: "Author" } },
|
|
31
|
-
* modelName: "Author" }
|
|
32
|
-
*
|
|
33
|
-
* P2002 author.create violating @@unique([tenantId, handle])
|
|
34
|
-
* sqlite constraint: { fields: ["tenantId", "handle"] }
|
|
35
|
-
* pg constraint: { index: "Author_tenantId_handle_key" }
|
|
36
|
-
*
|
|
37
|
-
* P2003 post.create with an authorId that does not exist
|
|
38
|
-
* sqlite meta = { modelName: "Post", driverAdapterError: { cause: {
|
|
39
|
-
* originalCode: "SQLITE_CONSTRAINT_FOREIGNKEY",
|
|
40
|
-
* originalMessage: "FOREIGN KEY constraint failed",
|
|
41
|
-
* kind: "ForeignKeyConstraintViolation",
|
|
42
|
-
* constraint: { foreignKey: {} } } } }
|
|
43
|
-
* pg constraint: { index: "Post_authorId_fkey" }, originalCode "23503"
|
|
44
|
-
*
|
|
45
|
-
* P2025 author.update / author.delete on a missing unique selector
|
|
46
|
-
* both meta = { modelName: "Author", operation: "an update" }
|
|
47
|
-
* both meta = { modelName: "Author", operation: "a delete" }
|
|
48
|
-
*
|
|
49
|
-
* P2025 nested connect whose target does not exist (create, update, upsert)
|
|
50
|
-
* both meta = { modelName: "Post", model: "Author",
|
|
51
|
-
* relation: "AuthorToPost", relationType: "one-to-many",
|
|
52
|
-
* operation: "a nested connect",
|
|
53
|
-
* neededFor: "inline the relation on 'Post' record(s)" }
|
|
54
|
-
*
|
|
55
|
-
* P2014 author.update with posts.set [] (or posts.disconnect) while a post
|
|
56
|
-
* still requires that author
|
|
57
|
-
* both meta = { modelName: "Author", relation: "AuthorToPost",
|
|
58
|
-
* modelA: "Post", modelB: "Author" }
|
|
59
|
-
*
|
|
60
|
-
* P2018 author.update with posts.connect to a missing post
|
|
61
|
-
* sqlite meta = { modelName: "Author", expectedRows: 1,
|
|
62
|
-
* relation: "AuthorToPost", relationType: "one-to-many" }
|
|
63
|
-
*
|
|
64
|
-
* P2034 two SERIALIZABLE transactions, both reading then both writing
|
|
65
|
-
* pg meta = { modelName: "Author", driverAdapterError: { cause: {
|
|
66
|
-
* originalCode: "40001",
|
|
67
|
-
* originalMessage: "could not serialize access due to concurrent update",
|
|
68
|
-
* kind: "TransactionWriteConflict" } } }
|
|
69
|
-
* ```
|
|
70
|
-
*
|
|
71
|
-
* ## Two inherited claims that the measurement refutes
|
|
72
|
-
*
|
|
73
|
-
* 1. It is **absent from every payload above**; the identifiers are internal rule
|
|
74
|
-
* names, not a `meta` key. What 7.10.0 actually emits is two different *key
|
|
75
|
-
* sets*, and the discriminant is the presence of `meta.relation` (with
|
|
76
|
-
* `meta.model`): present → the failure is about a relation reference; absent →
|
|
77
|
-
* the record the call selected was not there.
|
|
78
|
-
*
|
|
79
|
-
* Also unobserved: the `{}` meta that `expectNonEmpty` was expected to carry.
|
|
80
|
-
* `findUniqueOrThrow` and `findFirstOrThrow` both report `{ modelName, operation:
|
|
81
|
-
* "a query" }`, so no empty-meta P2025 was produced by any statement tried here.
|
|
82
|
-
* Either arm classifies identically under the rule above (no `relation` → root
|
|
83
|
-
* record), so the rule is safe either way, but the shape itself stays unverified.
|
|
84
|
-
*
|
|
85
|
-
* Three distinct situations used to answer the one `A2008 CONFLICT`. Coarse but
|
|
86
|
-
* correct, so nothing provisional ever reached the wire and the discriminant was
|
|
87
|
-
* purely additive. The observed surface each decision rests on is this:
|
|
88
|
-
*
|
|
89
|
-
* | Situation | Code | `meta` keys observed | Structurally separable? |
|
|
90
|
-
* |---|---|---|---| | Uniqueness violation | `P2002` | `modelName`,
|
|
91
|
-
* `driverAdapterError` | **Yes** — distinct P-code; `cause.kind` is
|
|
92
|
-
* `"UniqueConstraintViolation"`; `cause.constraint` is `{fields}` (SQLite) or
|
|
93
|
-
* `{index}` (PostgreSQL) | | Write conflict / deadlock | `P2034` | `modelName`,
|
|
94
|
-
* `driverAdapterError` | **Yes** from a constraint violation — distinct P-code;
|
|
95
|
-
* `cause.kind` is `"TransactionWriteConflict"`; **no** `constraint` at all. **No**
|
|
96
|
-
* from each other: `cause.originalCode` is `40001` for a write conflict and
|
|
97
|
-
* `40P01` for a deadlock, and that field is provider-owned and never crosses the
|
|
98
|
-
* boundary | | Lost-row race | *none* | *none* | **Yes, trivially** — there is no
|
|
99
|
-
* provider error behind it: it is a zero-row count in a sequence the framework
|
|
100
|
-
* manufactured, and it is raised in `execution/first-variant.emulator.ts`, never
|
|
101
|
-
* here |
|
|
102
|
-
*
|
|
103
|
-
* The two provider-backed ones differ by P-code; `driverAdapterError.cause.kind`
|
|
104
|
-
* is a second, independent witness.
|
|
105
|
-
*
|
|
106
|
-
* **A write conflict and a deadlock share `A2013` deliberately.** Both arrive as
|
|
107
|
-
* `P2034` with identical `meta` keys, differing only in a provider-owned
|
|
108
|
-
* `originalCode` (`40001` versus `40P01`) that never crosses the boundary. They
|
|
109
|
-
* are not separable at this seam, so one outcome is the only honest answer;
|
|
110
|
-
* splitting them later would need a discriminant that does not exist.
|
|
111
|
-
* `describePrismaConflict` is the recorded surface, and `PrismaConstraintSubject`
|
|
112
|
-
* records that the two engines do not agree on *which* identifier they can name —
|
|
113
|
-
* a uniqueness discriminant that promised the caller the constraint's name would
|
|
114
|
-
* be unsatisfiable on SQLite.
|
|
115
|
-
*
|
|
116
|
-
* **No Prisma path observed here threw a non-`Error`.** Every induced failure
|
|
117
|
-
* arrived as a `PrismaClientKnownRequestError`, which extends `Error` (`error
|
|
118
|
-
* instanceof Error` was asserted true for all of them), and the one non-Prisma
|
|
119
|
-
* failure the engines produced — `better-sqlite3` refusing a missing directory —
|
|
120
|
-
* was a `TypeError`. The adapter half is discharged here: `prismaOutcomeFailure`
|
|
121
|
-
* returns a `FrameworkError`, so every failure this module originates is an
|
|
122
|
-
* `Error` subclass, enforced by a spec.
|
|
123
|
-
*
|
|
124
|
-
* What keeps it from leaking is this module's own rule: the cause is framework
|
|
125
|
-
* text, and the reader never reads `message` at all, so there is no provider
|
|
126
|
-
* string in scope to forward.
|
|
127
|
-
*/
|
|
128
3
|
/** The three answers a Prisma failure can get. */
|
|
129
4
|
export type PrismaErrorClassification = {
|
|
130
5
|
readonly kind: "absence";
|
|
@@ -134,62 +9,24 @@ export type PrismaErrorClassification = {
|
|
|
134
9
|
} | {
|
|
135
10
|
readonly kind: "unclassified";
|
|
136
11
|
};
|
|
137
|
-
/**
|
|
138
|
-
* When a code is a `null` result rather than a failure.
|
|
139
|
-
*
|
|
140
|
-
* `subject` is the measured `meta` shape that may be forgiven. The same P2025
|
|
141
|
-
* reaches `update.unique` for a second reason — a nested `connect` whose target is
|
|
142
|
-
* missing — and that one is a caller error about the *arguments*, not an absent
|
|
143
|
-
* record. `"root-record"` means `meta` carries no relation reference; the
|
|
144
|
-
* single-member union says that is the one shape measured, and leaves room for a
|
|
145
|
-
* second if one is ever observed.
|
|
146
|
-
*/
|
|
147
12
|
type PrismaAbsenceRule = {
|
|
148
13
|
readonly variants: readonly `${string}.${string}`[];
|
|
149
14
|
readonly subject: "root-record";
|
|
150
15
|
};
|
|
151
16
|
type PrismaErrorRule = {
|
|
152
|
-
/**
|
|
153
|
-
* The outcome this code answers. Typed as core's own `OperationErrorCode`, so the
|
|
154
|
-
* adapter cannot widen the code space — a code the table does not contain has no
|
|
155
|
-
* rule at all.
|
|
156
|
-
*/
|
|
17
|
+
/** The outcome this code answers. */
|
|
157
18
|
readonly outcome: OperationErrorCode;
|
|
158
19
|
readonly absence?: PrismaAbsenceRule;
|
|
159
20
|
};
|
|
160
|
-
/** One closed table, scoped to `@prisma/client` 7.x. */
|
|
161
21
|
export type PrismaErrorTable = Readonly<Record<string, PrismaErrorRule>>;
|
|
162
|
-
/**
|
|
163
|
-
* The installed-major this table is written against. The spec asserts the
|
|
164
|
-
* installed `@prisma/client` agrees.
|
|
165
|
-
*/
|
|
22
|
+
/** The installed-major this table is written against. */
|
|
166
23
|
export declare const PRISMA_ERROR_TABLE_MAJOR = "7";
|
|
167
|
-
/**
|
|
168
|
-
* Every code here was induced above. Adding a row later is additive and costs
|
|
169
|
-
* nothing; shipping a wrong one is not recoverable.
|
|
170
|
-
*/
|
|
24
|
+
/** Every code here was induced above. */
|
|
171
25
|
export declare const PRISMA_ERROR_TABLE: PrismaErrorTable;
|
|
172
|
-
/**
|
|
173
|
-
* Classifies one failed Prisma call.
|
|
174
|
-
*
|
|
175
|
-
* `error` is `unknown` and stays `unknown`: the reader is the only thing that
|
|
176
|
-
* touches it, and the rules see nothing but measured facts.
|
|
177
|
-
*/
|
|
26
|
+
/** Classifies one failed Prisma call. */
|
|
178
27
|
export declare function classifyPrismaError(error: unknown, operation: Pick<OperationIdentity, "family" | "variant">): PrismaErrorClassification;
|
|
179
|
-
/**
|
|
180
|
-
* The deliberate failure an `outcome` classification stands for.
|
|
181
|
-
*
|
|
182
|
-
* The cause carries framework text and nothing else: no issues, and that is now
|
|
183
|
-
* the **rule** rather than an absence. And no provider text, because a deliberate
|
|
184
|
-
* outcome's top-level `cause.message` is forwarded verbatim. Nothing read off the
|
|
185
|
-
* Prisma error reaches this value.
|
|
186
|
-
*/
|
|
28
|
+
/** The deliberate failure an `outcome` classification stands for. */
|
|
187
29
|
export declare function prismaOutcomeFailure(code: OperationErrorCode): FrameworkError;
|
|
188
|
-
/**
|
|
189
|
-
* **Server-side only.** Every field is provider-owned, so this value never reaches
|
|
190
|
-
* a `cause` — which is why it is a separate function and not a field on
|
|
191
|
-
* `PrismaErrorClassification`: nothing can carry it onto the wire by accident.
|
|
192
|
-
*/
|
|
193
30
|
export type PrismaConflictSubject = {
|
|
194
31
|
readonly code: string;
|
|
195
32
|
readonly model: string | undefined;
|
|
@@ -1,72 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The total read of one hostile value.
|
|
3
|
-
*
|
|
4
|
-
* Everything on the failure path arrives as `unknown`. `@prisma/client` 7.10.0
|
|
5
|
-
* raises `PrismaClientKnownRequestError`, whose `meta` is typed `Record<string,
|
|
6
|
-
* unknown>` and is **not** a discriminated union, so a reader that trusts the type
|
|
7
|
-
* would be reading a shape nobody promised. Worse, the value need not come from
|
|
8
|
-
* Prisma at all: a developer callback, a driver, or a proxy can hand the adapter
|
|
9
|
-
* anything.
|
|
10
|
-
*
|
|
11
|
-
* So this module reads structurally and defensively, under two rules it never
|
|
12
|
-
* relaxes — the same totality discipline as core's diagnostics reporter, which
|
|
13
|
-
* also observes values it cannot vouch for:
|
|
14
|
-
*
|
|
15
|
-
* 1. **No accessor is ever invoked.** Every read goes through
|
|
16
|
-
* `Object.getOwnPropertyDescriptor` and takes `descriptor.value`. An accessor
|
|
17
|
-
* descriptor yields `undefined` and its getter is never called, so a
|
|
18
|
-
* booby-trapped property cannot run code, mutate state, or throw from inside
|
|
19
|
-
* the adapter's failure path.
|
|
20
|
-
* 2. **Nothing escapes.** Every reflective call is guarded, because a Proxy may
|
|
21
|
-
* throw from its `getOwnPropertyDescriptor` or `ownKeys` trap, and a reader
|
|
22
|
-
* that throws while classifying a failure would replace a legible outcome with
|
|
23
|
-
* an illegible one.
|
|
24
|
-
*
|
|
25
|
-
* Reading only *own* properties is deliberate rather than incidental: 7.10.0
|
|
26
|
-
* carries `code` and `meta` as own data properties (observed own keys:
|
|
27
|
-
* `["code","meta","clientVersion","name"]`), so the own read is sufficient for
|
|
28
|
-
* every real error, and a `code` that exists only as an inherited accessor is
|
|
29
|
-
* precisely the case rule 1 refuses to touch.
|
|
30
|
-
*
|
|
31
|
-
* ## What this module does NOT read
|
|
32
|
-
*
|
|
33
|
-
* `message` is never read. At 7.10.0 a `PrismaClientKnownRequestError` message is
|
|
34
|
-
* not a sentence — it is a rendered call site. Measured verbatim on this
|
|
35
|
-
* repository's own schema:
|
|
36
|
-
*
|
|
37
|
-
* ```text
|
|
38
|
-
* Invalid `client.author.create()` invocation in
|
|
39
|
-
* /home/.../packages/prisma7-adapter/src/errors/__probe__.spec.ts:50:18
|
|
40
|
-
*
|
|
41
|
-
* 49 await capture("P2002 duplicate email", () =>
|
|
42
|
-
* → 50 client.author.create(
|
|
43
|
-
* Unique constraint failed on the fields: (`email`)
|
|
44
|
-
* ```
|
|
45
|
-
*
|
|
46
|
-
* That single string carries an absolute file path, a source excerpt and the
|
|
47
|
-
* violated constraint. The cheapest guarantee is not to read the message at all,
|
|
48
|
-
* so the leak has no source; the raw error still reaches core's diagnostics sink,
|
|
49
|
-
* which is server-side and may carry it.
|
|
50
|
-
*
|
|
51
|
-
* Everything below is scoped to `@prisma/client` **7.x**. Prisma 8 replaced
|
|
52
|
-
* P-codes with dotted `NAMESPACE.SUBCODE` codes, which makes a Prisma 8 adapter a
|
|
53
|
-
* re-map of this module rather than a patch to it.
|
|
54
|
-
*/
|
|
55
|
-
/**
|
|
56
|
-
* The violated constraint as 7.10.0's driver adapters actually report it, at
|
|
57
|
-
* `meta.driverAdapterError.cause.constraint`.
|
|
58
|
-
*
|
|
59
|
-
* The arms are **provider-shaped, measured on both engines**, not guessed:
|
|
60
|
-
*
|
|
61
|
-
* | Engine | Unique (P2002) | Foreign key (P2003) |
|
|
62
|
-
* |-----------------------------------|-------------------------------|-------------------------|
|
|
63
|
-
* | `@prisma/adapter-better-sqlite3` | `{ fields: ["email"] }` | `{ foreignKey: {}
|
|
64
|
-
* }` | | `@prisma/adapter-pg` | `{ index: "Author_email_key" }` | `{ index:
|
|
65
|
-
* "Post_authorId_fkey" }` |
|
|
66
|
-
*
|
|
67
|
-
* It is therefore **not a string**, and the two engines do not agree on which
|
|
68
|
-
* identifier they can name — SQLite names columns, PostgreSQL names the index.
|
|
69
|
-
*/
|
|
70
1
|
export type PrismaConstraintSubject = {
|
|
71
2
|
readonly kind: "fields";
|
|
72
3
|
readonly fields: readonly string[];
|
|
@@ -78,23 +9,9 @@ export type PrismaConstraintSubject = {
|
|
|
78
9
|
} | {
|
|
79
10
|
readonly kind: "unreadable";
|
|
80
11
|
};
|
|
81
|
-
/**
|
|
82
|
-
* Everything the normalizer is allowed to know about one failed call.
|
|
83
|
-
*
|
|
84
|
-
* Narrow and flat on purpose: a classification rule reads these fields and
|
|
85
|
-
* nothing else, so no rule can reach back into the raw value and no rule
|
|
86
|
-
* can depend on a shape this module has not measured.
|
|
87
|
-
*/
|
|
12
|
+
/** Everything the normalizer is allowed to know about one failed call. */
|
|
88
13
|
export type PrismaErrorFacts = {
|
|
89
|
-
/**
|
|
90
|
-
* The P-code, when it is an own string property. A missing or non-string `code`
|
|
91
|
-
* leaves this `undefined`, which the table answers as `unclassified` — never as a
|
|
92
|
-
* guess.
|
|
93
|
-
*/
|
|
94
14
|
readonly code: string | undefined;
|
|
95
|
-
/**
|
|
96
|
-
* The own, enumerable key names of `meta`, in the order 7.10.0 wrote them.
|
|
97
|
-
*/
|
|
98
15
|
readonly metaKeys: readonly string[];
|
|
99
16
|
/** `meta.modelName` — the Prisma model the failure occurred on. */
|
|
100
17
|
readonly modelName: string | undefined;
|
|
@@ -107,15 +24,6 @@ export type PrismaErrorFacts = {
|
|
|
107
24
|
readonly relatedModel: string | undefined;
|
|
108
25
|
/** `meta.driverAdapterError.cause.constraint`, read structurally. */
|
|
109
26
|
readonly constraint: PrismaConstraintSubject | undefined;
|
|
110
|
-
/**
|
|
111
|
-
* `meta.target` — **read so that it is measured, depended on by nothing**.
|
|
112
|
-
*
|
|
113
|
-
* It was induced on both engines here and **`target` was absent from every
|
|
114
|
-
* payload** — P2002 (single and compound unique), P2003, P2025 (three shapes),
|
|
115
|
-
* P2018 and P2034, on SQLite and on PostgreSQL. The docs are stale; the installed
|
|
116
|
-
* package governs. This field exists so the next reader sees that measured, and
|
|
117
|
-
* does not "fix" the code to match the documentation.
|
|
118
|
-
*/
|
|
119
27
|
readonly legacyTarget: readonly string[] | undefined;
|
|
120
28
|
};
|
|
121
29
|
/**
|
|
@@ -2,41 +2,18 @@ import { FrameworkError } from "@aventara/core";
|
|
|
2
2
|
import type { PrismaIdentifierAddress } from "../discovery/discovery-artifact.interface.js";
|
|
3
3
|
import type { PrismaCall } from "../translation/operation.translator.js";
|
|
4
4
|
/**
|
|
5
|
-
* The lost-row conflict: the read selected a row and the re-asserted write
|
|
6
|
-
* none, so **nothing was written** and a retry may succeed.
|
|
7
|
-
*
|
|
8
|
-
* `A2014 STALE_SELECTION`, and this is the only place that names it.
|
|
5
|
+
* The lost-row conflict: the read selected a row and the re-asserted write
|
|
6
|
+
* matched none, so **nothing was written** and a retry may succeed.
|
|
9
7
|
*/
|
|
10
8
|
export declare function lostRowConflict(): FrameworkError;
|
|
11
|
-
/**
|
|
12
|
-
* Issues one of the emulation's two statements.
|
|
13
|
-
*/
|
|
9
|
+
/** Issues one of the emulation's two statements. */
|
|
14
10
|
export type FirstVariantIssuer = (call: PrismaCall, step: "read" | "write") => Promise<unknown>;
|
|
15
|
-
/**
|
|
16
|
-
* How the emulation's two statements are issued, and against which client.
|
|
17
|
-
*
|
|
18
|
-
* An earlier draft of this module took the two separately — a `unit` that opened a
|
|
19
|
-
* transaction and an `issue` already bound to a client — and the standalone case
|
|
20
|
-
* then opened a transaction whose two statements ran on the **root** client,
|
|
21
|
-
* outside it: the emulation would have looked atomic and been nothing of the kind,
|
|
22
|
-
* and no assertion about the issued `where` would have noticed. Making the unit
|
|
23
|
-
* the thing that hands out the issuer removes the gap rather than documenting it.
|
|
24
|
-
*
|
|
25
|
-
* This module therefore never sees a Prisma client, a provider name or a
|
|
26
|
-
* transaction; it sees one issuer, already in the right unit.
|
|
27
|
-
*/
|
|
11
|
+
/** How the emulation's two statements are issued, and against which client. */
|
|
28
12
|
export type FirstVariantEmulation = {
|
|
29
|
-
/** The translated plan's two steps: the read, then the write template. */
|
|
30
13
|
readonly steps: readonly PrismaCall[];
|
|
31
14
|
/** The Resource's recorded Prisma addressing, for the write's identifier. */
|
|
32
15
|
readonly identifiers: readonly PrismaIdentifierAddress[];
|
|
33
|
-
/**
|
|
34
|
-
* Opens the one adapter-internal unit and runs `work` inside it.
|
|
35
|
-
*
|
|
36
|
-
* Standalone this opens an interactive transaction and the issuer it yields is
|
|
37
|
-
* scoped to it; inside a plan it runs `work` against the outer transaction client,
|
|
38
|
-
* so the emulated steps roll back **with** the plan rather than independently.
|
|
39
|
-
*/
|
|
16
|
+
/** Opens the one adapter-internal unit and runs `work` inside it. */
|
|
40
17
|
readonly inUnit: <T>(work: (issue: FirstVariantIssuer) => Promise<T>) => Promise<T>;
|
|
41
18
|
};
|
|
42
19
|
/** A plan that is not the two-step shape this module manufactures. */
|
|
@@ -1,80 +1,19 @@
|
|
|
1
1
|
import type { AdapterModel, AdapterOperationExecutor } from "@aventara/core";
|
|
2
2
|
import type { PrismaResourceTranslation } from "../discovery/discovery-artifact.interface.js";
|
|
3
3
|
import { type PrismaDelegateHost, type PrismaTransactionOptions } from "../prisma-client.interface.js";
|
|
4
|
-
/**
|
|
5
|
-
* Translation is pure and lives in `translation/operation.translator.ts`; this
|
|
6
|
-
* module owns the three things that need a client and nothing else:
|
|
7
|
-
*
|
|
8
|
-
* 1. **Reaching the delegate.** See {@link delegateKeyOf} — the one derivation in
|
|
9
|
-
* this package, and the one place it is made.
|
|
10
|
-
* 2. **Classifying a failure.** The closed P-code table in
|
|
11
|
-
* `errors/error.normalizer.ts` answers `absence` → `null`, `outcome` → a
|
|
12
|
-
* deliberate `FrameworkError`, or `unclassified` → propagate, so core's
|
|
13
|
-
* `AdapterBoundary` reports `A3001` with core's own text and sends the raw
|
|
14
|
-
* error to diagnostics. The adapter never guesses a code it did not measure.
|
|
15
|
-
* 3. **Shaping the answer.** `execution/result.shaper.ts` places every `$count`
|
|
16
|
-
* envelope, converts every returned value to the runtime form core's output
|
|
17
|
-
* validator accepts and reads the number a count variant owes; which of its
|
|
18
|
-
* three entry points runs is the *variant's* `result` row in the dispatch
|
|
19
|
-
* table, not a guess from the shape Prisma returned. The shaper is handed the
|
|
20
|
-
* model and the Resource as well as the plan, because the scalar a returned
|
|
21
|
-
* value belongs to is a fact of the model and not of the value.
|
|
22
|
-
*
|
|
23
|
-
* `scope: "root"` is the adapter's own `execute`. `scope: "transaction"` is what
|
|
24
|
-
* `Adapter.transaction`'s callback hands core: the same code over a transaction
|
|
25
|
-
* client. The scope decides exactly one thing — whether a first-style emulation
|
|
26
|
-
* opens its own interactive transaction or reuses the one already open — and a
|
|
27
|
-
* transaction-scoped executor closes over the transaction client only, so the root
|
|
28
|
-
* client is unreachable from inside it.
|
|
29
|
-
*
|
|
30
|
-
* All three value crossings read ONE table, `translation/scalar.conversion.ts`: a
|
|
31
|
-
* write's data through `mutation.translator.ts`, a filter operand through
|
|
32
|
-
* `where.translator.ts`, and a returned row through `execution/result.shaper.ts`.
|
|
33
|
-
* None of them is converted here. Converting at this seam would have put the
|
|
34
|
-
* scalar table in a fourth place and hidden the gap from the module that owns it.
|
|
35
|
-
*
|
|
36
|
-
* **Core's output validation runs after this module, and strips.** For a
|
|
37
|
-
* projection-carrying variant `planReadOperation` sets `materialize: true` and
|
|
38
|
-
* `materializeRecord` ends in `Object.fromEntries(plan.requested…)` **[CORE]**, so
|
|
39
|
-
* a spare key the adapter returned would be removed before the validator saw it.
|
|
40
|
-
* That is why the emulator's own return value is asserted directly in
|
|
41
|
-
* `first-variant.emulator.spec.ts` as well as end-to-end here: an end-to-end
|
|
42
|
-
* assertion about extra keys cannot fail on that path.
|
|
43
|
-
*/
|
|
44
4
|
/** A call the executor cannot issue against the client it was given. */
|
|
45
5
|
export declare class PrismaExecutionError extends Error {
|
|
46
6
|
readonly name = "PrismaExecutionError";
|
|
47
7
|
}
|
|
48
8
|
export type PrismaExecutionScope = "root" | "transaction";
|
|
49
9
|
export type PrismaOperationExecutorDependencies = {
|
|
50
|
-
/**
|
|
51
|
-
* The client in force: the application's own client at `"root"` scope, the
|
|
52
|
-
* transaction client inside a plan.
|
|
53
|
-
*/
|
|
54
10
|
readonly client: PrismaDelegateHost;
|
|
55
11
|
readonly model: AdapterModel;
|
|
56
12
|
/** The generate-time translation table (Prisma addressing per Resource). */
|
|
57
13
|
readonly translation: Readonly<Record<string, PrismaResourceTranslation>>;
|
|
58
14
|
readonly scope: PrismaExecutionScope;
|
|
59
|
-
/** Adapter-owned, never Aventara public configuration. */
|
|
60
15
|
readonly transactionOptions?: PrismaTransactionOptions;
|
|
61
16
|
};
|
|
62
|
-
/**
|
|
63
|
-
* Prisma's delegate property for a model.
|
|
64
|
-
*
|
|
65
|
-
* **Derived, and that is a recorded residual rather than a comfortable choice.**
|
|
66
|
-
* The authoritative token is readable — `generated/<engine>/models/<M>.ts`, a 🟢
|
|
67
|
-
* file, writes it as the extension key: `GetSelect<{ … },
|
|
68
|
-
* ExtArgs["result"]["author"]>` for model `Author` — but `types.reader.ts` reads
|
|
69
|
-
* the 🟢 files for *capability* facts and does not record it, and the client
|
|
70
|
-
* property itself is declared only in `internal/class.ts`, which that reader
|
|
71
|
-
* refuses by design. So the rule applied here is Prisma's own: lower-case the
|
|
72
|
-
* first character and nothing else. `operation.executor.spec.ts` asserts the
|
|
73
|
-
* derivation against the real generated client for **every** model in the schema,
|
|
74
|
-
* so it is observed for this repository's six models; a model name whose Prisma
|
|
75
|
-
* property does not follow the rule would be caught there and not in production.
|
|
76
|
-
* The clean owner of the fact is the generate-time translation table, beside
|
|
77
|
-
* `identifierKeys`, and that is left as a pointer rather than taken here.
|
|
78
|
-
*/
|
|
17
|
+
/** Prisma's delegate property for a model. */
|
|
79
18
|
export declare function delegateKeyOf(model: string): string;
|
|
80
19
|
export declare function createPrismaOperationExecutor<M extends AdapterModel>(dependencies: PrismaOperationExecutorDependencies): AdapterOperationExecutor<M>;
|