@aventara/prisma7-adapter 0.1.0-pilot.0 → 0.1.0-pilot.2
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 +39 -28
- package/dist/discovery/artifact.loader.d.ts +6 -2
- package/dist/discovery/artifact.loader.js +12 -57
- package/dist/discovery/capability-surface.interface.d.ts +29 -40
- package/dist/discovery/capability-surface.interface.js +0 -12
- package/dist/discovery/capability.deriver.d.ts +10 -40
- package/dist/discovery/capability.deriver.js +0 -179
- package/dist/discovery/client-shape.verifier.d.ts +13 -0
- package/dist/discovery/client-shape.verifier.js +93 -0
- package/dist/discovery/discovery-artifact.interface.d.ts +55 -30
- package/dist/discovery/enum.mapper.d.ts +6 -14
- package/dist/discovery/enum.mapper.js +1 -2
- package/dist/discovery/field.mapper.d.ts +49 -63
- package/dist/discovery/field.mapper.js +0 -161
- package/dist/discovery/identifier.mapper.d.ts +38 -77
- package/dist/discovery/identifier.mapper.js +0 -64
- package/dist/discovery/model.discoverer.d.ts +18 -35
- package/dist/discovery/model.discoverer.js +66 -141
- package/dist/discovery/operations.deriver.d.ts +10 -38
- package/dist/discovery/operations.deriver.js +0 -40
- package/dist/discovery/scalar.mapper.d.ts +12 -35
- package/dist/discovery/scalar.mapper.js +0 -8
- package/dist/discovery/schema-facts.interface.d.ts +26 -25
- package/dist/discovery/schema-facts.interface.js +0 -10
- package/dist/discovery/schema.reader.d.ts +53 -64
- package/dist/discovery/schema.reader.js +0 -174
- package/dist/discovery/types.reader.d.ts +0 -4
- package/dist/discovery/types.reader.js +2 -177
- package/dist/discovery/vocabulary.dictionary.d.ts +36 -75
- package/dist/discovery/vocabulary.dictionary.js +2 -152
- package/dist/errors/error.normalizer.d.ts +85 -135
- package/dist/errors/error.normalizer.js +1 -70
- package/dist/errors/prisma-error.reader.d.ts +53 -68
- package/dist/errors/prisma-error.reader.js +0 -91
- package/dist/execution/first-variant.emulator.d.ts +11 -22
- package/dist/execution/first-variant.emulator.js +0 -142
- package/dist/execution/operation.executor.d.ts +12 -19
- package/dist/execution/operation.executor.js +0 -92
- package/dist/execution/result.shaper.d.ts +22 -31
- package/dist/execution/result.shaper.js +0 -24
- package/dist/execution/transaction.executor.d.ts +36 -73
- package/dist/generate/artifact.emitter.d.ts +27 -27
- package/dist/generate/artifact.emitter.js +30 -13
- package/dist/generate/declared-support.reader.d.ts +11 -11
- package/dist/generate/declared-support.reader.js +0 -12
- package/dist/generate/discovery.bin.js +0 -10
- package/dist/generate/discovery.cli.js +0 -55
- package/dist/generate/existence.gate.d.ts +26 -32
- package/dist/generate/existence.gate.js +0 -6
- package/dist/generate/index.d.ts +10 -12
- package/dist/generate/index.js +0 -42
- package/dist/generate/node-version.guard.js +0 -12
- package/dist/generate/prisma-cli.resolver.d.ts +9 -11
- package/dist/generate/prisma-cli.resolver.js +0 -13
- package/dist/generate/prisma.generate.d.ts +49 -65
- package/dist/generate/prisma.generate.js +13 -123
- package/dist/generate/reader-crosscheck.d.ts +36 -60
- package/dist/generate/reader-crosscheck.js +5 -41
- package/dist/prisma-client.interface.d.ts +14 -19
- package/dist/prisma-client.interface.js +0 -6
- package/dist/prisma.adapter.d.ts +9 -17
- package/dist/prisma.adapter.js +11 -53
- package/dist/provider/provider-profile.d.ts +6 -23
- package/dist/provider/provider-profile.js +0 -64
- package/dist/translation/identifier.addressing.d.ts +15 -22
- package/dist/translation/identifier.addressing.js +0 -10
- package/dist/translation/mutation.translator.d.ts +19 -43
- package/dist/translation/mutation.translator.js +1 -209
- package/dist/translation/operation.translator.d.ts +36 -63
- package/dist/translation/operation.translator.js +1 -51
- package/dist/translation/order-pagination.translator.d.ts +24 -31
- package/dist/translation/order-pagination.translator.js +0 -43
- package/dist/translation/projection.translator.d.ts +25 -58
- package/dist/translation/projection.translator.js +0 -27
- package/dist/translation/scalar.conversion.d.ts +15 -26
- package/dist/translation/scalar.conversion.js +1 -129
- package/dist/translation/where.translator.d.ts +17 -28
- package/dist/translation/where.translator.js +0 -83
- package/package.json +3 -3
|
@@ -1,97 +1,14 @@
|
|
|
1
1
|
import { Decimal, } from "@aventara/core";
|
|
2
|
-
/**
|
|
3
|
-
* C-834 — the scalar value conversion, in **one** table and **both**
|
|
4
|
-
* directions.
|
|
5
|
-
*
|
|
6
|
-
* # Why this is a module and not three private helpers
|
|
7
|
-
*
|
|
8
|
-
* Three consumers cross the same boundary with the same values: a write's `data`
|
|
9
|
-
* (`mutation.translator.ts`), a filter operand (`where.translator.ts`) and a row
|
|
10
|
-
* coming back (`execution/result.shaper.ts`). The emission table was born inside
|
|
11
|
-
* the mutation translator, which is where the first of the three needed it; a
|
|
12
|
-
* copy in each consumer would be three places to answer "what does a `decimal`
|
|
13
|
-
* look like on the wire", and the second copy to drift would be the one nobody
|
|
14
|
-
* reads. {@link SCALAR_BOUNDARY_CROSSINGS} is therefore the single owner, and
|
|
15
|
-
* Task 10's `identifier.addressing.ts` is the precedent — extracted for exactly
|
|
16
|
-
* this reason.
|
|
17
|
-
*
|
|
18
|
-
* The table is TOTAL over core's `BuiltInScalar` by a mapped type, so a tenth
|
|
19
|
-
* scalar added to the protocol is a compile error here rather than a value that
|
|
20
|
-
* silently crosses unconverted in one direction only.
|
|
21
|
-
*
|
|
22
|
-
* # `decimal` is the one real conversion, and that is measured
|
|
23
|
-
*
|
|
24
|
-
* Eight of the nine scalars pass through in both directions, re-measured on both
|
|
25
|
-
* driver adapters at `@prisma/client` 7.10.0 (2026-10-02): a `bigint` primitive,
|
|
26
|
-
* a `Date` and a `Uint8Array` are what the generated input types accept and what
|
|
27
|
-
* a read returns (`Buffer.isBuffer(v) === false`, `v instanceof Uint8Array ===
|
|
28
|
-
* true`), `Json` is written and returned as a plain object (its prototype IS
|
|
29
|
-
* `Object.prototype`, which is what core's own `isPlainRecord` output check
|
|
30
|
-
* requires **[CORE, runtime/scalar-runtime.ts]**), and an enum member is its own
|
|
31
|
-
* name on both sides. Those are the same forms core's runtime scope requires
|
|
32
|
-
* (`normalizeScalarAtom`'s `"application"` branch), so a pass-through is a
|
|
33
|
-
* decision, not an omission.
|
|
34
|
-
*
|
|
35
|
-
* `decimal` is the exception §6.2 names: *the public `Decimal` MUST NOT be
|
|
36
|
-
* `Prisma.Decimal`*, and it is not — core's `Decimal` is a class with one
|
|
37
|
-
* private `#value` and a `toString`, while Prisma's is its bundled decimal.js,
|
|
38
|
-
* whose constructor is named `Decimal2`. Neither accepts the other, so the
|
|
39
|
-
* crossing is a real conversion both ways: out, the base-10 string; in, core's
|
|
40
|
-
* own `Decimal`.
|
|
41
|
-
*
|
|
42
|
-
* # Outbound: three different failures, none of them a type error
|
|
43
|
-
*
|
|
44
|
-
* Passing core's `Decimal` through unconverted fails *differently* depending on
|
|
45
|
-
* where it lands, and none of the three is a plain rejection (measured on both
|
|
46
|
-
* engines, 2026-10-02):
|
|
47
|
-
*
|
|
48
|
-
* - as a **write value**: refused at the wire — *"Invalid value for argument
|
|
49
|
-
* `toString`: We could not serialize [object Function] value."*;
|
|
50
|
-
* - as a **comparison operand** (`gt`, `equals`, …): read as a **FieldRef**, not
|
|
51
|
-
* as a value — *"PrismaClientValidationError: Argument `_ref` is missing."*;
|
|
52
|
-
* - as a **bare equality operand**: *accepted*, and silently wrong. Core's
|
|
53
|
-
* `Decimal` keeps its digits in a private field, so `Object.keys` is empty and
|
|
54
|
-
* Prisma reads the object as an **empty `DecimalFilter`** — `findMany` returned
|
|
55
|
-
* **2 of 2** rows, byte-identical to passing no predicate at all, where the
|
|
56
|
-
* string `"1"` returned 1. A misinterpretation that validates is the failure
|
|
57
|
-
* this module exists to make impossible.
|
|
58
|
-
*
|
|
59
|
-
* # Inbound: the value core's output validator will accept
|
|
60
|
-
*
|
|
61
|
-
* Core validates adapter output in its `"runtime"` scope, which requires the
|
|
62
|
-
* *runtime* form of every scalar — `value instanceof Decimal` for a decimal
|
|
63
|
-
* **[CORE, runtime/output-validator.ts → normalizeScalarFieldValue]**. A
|
|
64
|
-
* `Decimal2` is not one, so an unconverted read fails core's own validation and
|
|
65
|
-
* the caller is told `A3000`. Converting here is therefore not a courtesy: it is
|
|
66
|
-
* the adapter's half of §6.2.
|
|
67
|
-
*
|
|
68
|
-
* # The scalar identity comes from the model, never from the value
|
|
69
|
-
*
|
|
70
|
-
* Both directions are keyed by the field's discovered scalar, never by the
|
|
71
|
-
* runtime shape of the value. A `Json` column holding `{ "x": 1 }` and a
|
|
72
|
-
* `Decimal` reaching Prisma are both "an object", and a `Json` column holding
|
|
73
|
-
* the string `"12.5"` is indistinguishable from a decimal string: shape-sniffing
|
|
74
|
-
* would convert one of those sooner or later, and silently. The discovered model
|
|
75
|
-
* already knows, so it is asked.
|
|
76
|
-
*/
|
|
77
|
-
/** A value this adapter will not convert rather than misrepresent. */
|
|
78
2
|
export class PrismaScalarConversionError extends Error {
|
|
79
3
|
name = "PrismaScalarConversionError";
|
|
80
4
|
}
|
|
81
5
|
function identity(value) {
|
|
82
6
|
return value;
|
|
83
7
|
}
|
|
84
|
-
/** A scalar whose Prisma representation IS its Aventara runtime form. */
|
|
85
8
|
const PASS_THROUGH = {
|
|
86
9
|
outbound: identity,
|
|
87
10
|
inbound: identity,
|
|
88
11
|
};
|
|
89
|
-
/**
|
|
90
|
-
* What each Aventara scalar becomes on the way to Prisma and on the way back.
|
|
91
|
-
*
|
|
92
|
-
* Total over `BuiltInScalar`: a scalar added to the protocol cannot reach a
|
|
93
|
-
* provider until both of its directions are decided here.
|
|
94
|
-
*/
|
|
95
12
|
const SCALAR_BOUNDARY_CROSSINGS = {
|
|
96
13
|
string: PASS_THROUGH,
|
|
97
14
|
boolean: PASS_THROUGH,
|
|
@@ -103,16 +20,6 @@ const SCALAR_BOUNDARY_CROSSINGS = {
|
|
|
103
20
|
bytes: PASS_THROUGH,
|
|
104
21
|
json: PASS_THROUGH,
|
|
105
22
|
};
|
|
106
|
-
/**
|
|
107
|
-
* Core's `Decimal` → the base-10 string every Prisma decimal input accepts
|
|
108
|
-
* (`DecimalFieldUpdateOperationsInput` takes `runtime.Decimal | DecimalJsLike |
|
|
109
|
-
* number | string`, and `DecimalFilter`'s operands the same).
|
|
110
|
-
*
|
|
111
|
-
* A value that is not core's `Decimal` is refused rather than coerced: core
|
|
112
|
-
* normalizes every decimal to its own `Decimal` before the adapter is called
|
|
113
|
-
* (C-845), so a string or a number arriving here means the request bypassed
|
|
114
|
-
* validation, and guessing at it would be the adapter inventing precision.
|
|
115
|
-
*/
|
|
116
23
|
function emitDecimal(value, path) {
|
|
117
24
|
if (value === null) {
|
|
118
25
|
return null;
|
|
@@ -120,24 +27,13 @@ function emitDecimal(value, path) {
|
|
|
120
27
|
if (value instanceof Decimal) {
|
|
121
28
|
return value.toString();
|
|
122
29
|
}
|
|
123
|
-
throw new PrismaScalarConversionError(`${path}: a decimal value reaches the adapter as an Aventara Decimal
|
|
30
|
+
throw new PrismaScalarConversionError(`${path}: a decimal value reaches the adapter as an Aventara Decimal; the adapter will not guess at a ${describeValue(value)}`);
|
|
124
31
|
}
|
|
125
|
-
/**
|
|
126
|
-
* Prisma's decimal → core's `Decimal`, which is the only decimal form core's
|
|
127
|
-
* output validator accepts in its runtime scope.
|
|
128
|
-
*
|
|
129
|
-
* The text is read through `String(value)`, which is exactly what RC-10
|
|
130
|
-
* measured as lossless for 7.10.0's `Decimal2` on both driver adapters
|
|
131
|
-
* (`String(v) === "12345.6789"`). A text core's own `Decimal` constructor
|
|
132
|
-
* rejects is reported as a conversion failure naming the form observed, rather
|
|
133
|
-
* than passed on to fail later as core's generic `A3000`.
|
|
134
|
-
*/
|
|
135
32
|
function receiveDecimal(value, path) {
|
|
136
33
|
if (value === null) {
|
|
137
34
|
return null;
|
|
138
35
|
}
|
|
139
36
|
if (value instanceof Decimal) {
|
|
140
|
-
// Already core's: a fake adapter, or a value this adapter round-tripped.
|
|
141
37
|
return value;
|
|
142
38
|
}
|
|
143
39
|
if (typeof value !== "object" &&
|
|
@@ -153,28 +49,14 @@ function receiveDecimal(value, path) {
|
|
|
153
49
|
throw new PrismaScalarConversionError(`${path}: the provider returned ${describeValue(value)} for a decimal column, whose text "${text}" is not a decimal core accepts`);
|
|
154
50
|
}
|
|
155
51
|
}
|
|
156
|
-
/**
|
|
157
|
-
* One value out of Aventara and into a Prisma argument.
|
|
158
|
-
*
|
|
159
|
-
* @throws {PrismaScalarConversionError} for a value the adapter will not
|
|
160
|
-
* represent rather than let a provider misread.
|
|
161
|
-
*/
|
|
162
52
|
export function emitScalarValue(model, field, value, path) {
|
|
163
53
|
return convert("outbound", model, field, value, path);
|
|
164
54
|
}
|
|
165
|
-
/**
|
|
166
|
-
* One value out of a Prisma result and into the runtime form core's output
|
|
167
|
-
* validator accepts.
|
|
168
|
-
*
|
|
169
|
-
* @throws {PrismaScalarConversionError} for a provider value the adapter cannot
|
|
170
|
-
* honestly read as that scalar.
|
|
171
|
-
*/
|
|
172
55
|
export function receiveScalarValue(model, field, value, path) {
|
|
173
56
|
return convert("inbound", model, field, value, path);
|
|
174
57
|
}
|
|
175
58
|
function convert(direction, model, field, value, path) {
|
|
176
59
|
if (!("scalar" in field.type)) {
|
|
177
|
-
// An enum member is its own name on both sides.
|
|
178
60
|
return value;
|
|
179
61
|
}
|
|
180
62
|
const scalar = field.type.scalar;
|
|
@@ -186,12 +68,6 @@ function convert(direction, model, field, value, path) {
|
|
|
186
68
|
}
|
|
187
69
|
return convertAtoms(SCALAR_BOUNDARY_CROSSINGS[scalar][direction], value, path);
|
|
188
70
|
}
|
|
189
|
-
/**
|
|
190
|
-
* A scalar list's members convert one by one, and the array is rebuilt rather
|
|
191
|
-
* than written into: a translator that mutated its argument would corrupt the
|
|
192
|
-
* second attempt of a retried operation, and a shaper that mutated a provider
|
|
193
|
-
* row would corrupt whatever else holds it.
|
|
194
|
-
*/
|
|
195
71
|
function convertAtoms(crossing, value, path) {
|
|
196
72
|
if (!Array.isArray(value)) {
|
|
197
73
|
return crossing(value, path);
|
|
@@ -202,15 +78,11 @@ function convertAtoms(crossing, value, path) {
|
|
|
202
78
|
changed ||= result !== member;
|
|
203
79
|
return result;
|
|
204
80
|
});
|
|
205
|
-
// The same array, by identity, when no member needed converting: the shaper's
|
|
206
|
-
// copy-on-write depends on being able to tell.
|
|
207
81
|
return changed ? converted : value;
|
|
208
82
|
}
|
|
209
|
-
/** Whether a discovered scalar key is one this adapter has a crossing for. */
|
|
210
83
|
function isBuiltInScalar(key) {
|
|
211
84
|
return Object.hasOwn(SCALAR_BOUNDARY_CROSSINGS, key);
|
|
212
85
|
}
|
|
213
|
-
/** Names what arrived, without quoting a value that may be large or secret. */
|
|
214
86
|
function describeValue(value) {
|
|
215
87
|
if (value === null) {
|
|
216
88
|
return "null";
|
|
@@ -1,42 +1,35 @@
|
|
|
1
1
|
import type { AdapterModel } from "@aventara/core";
|
|
2
2
|
/**
|
|
3
|
-
* T-7 — `where` translation: one validated Aventara predicate in, one Prisma
|
|
4
|
-
* `<M>WhereInput` out.
|
|
5
|
-
*
|
|
6
3
|
* # Pure, and that is the property to protect
|
|
7
4
|
*
|
|
8
5
|
* No client, no database, no I/O, no provider name. The only inputs are the
|
|
9
6
|
* discovered model and plain data, which is what makes the whole filter grammar
|
|
10
|
-
* exhaustively testable without an engine
|
|
11
|
-
* decision").
|
|
7
|
+
* exhaustively testable without an engine.
|
|
12
8
|
*
|
|
13
9
|
* # One direction, and no Prisma token the framework did not ask for
|
|
14
10
|
*
|
|
15
11
|
* Every emitted key comes from one of the two tables below, both of which are
|
|
16
12
|
* TOTAL over core's own capability unions — a member added to
|
|
17
13
|
* `ScalarFilterOperator` or `RelationFilterCapability` with no row here is a
|
|
18
|
-
* compile error, not a silently dropped predicate.
|
|
19
|
-
* *"capability derivation advertises an argument only when a named translator
|
|
20
|
-
* step emits it"*, and this module is that step for the filter vocabulary.
|
|
14
|
+
* compile error, not a silently dropped predicate.
|
|
21
15
|
*
|
|
22
16
|
* The inverse matters more. A missing key loses a predicate and shows up as a
|
|
23
17
|
* wrong row count; an INVENTED key silently changes what the query means. So
|
|
24
18
|
* nothing is ever added to the output that the request did not carry: no `equals`
|
|
25
|
-
* wrapper around a bare value, no `is`/`isNot` wrapper around a to-one predicate
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* # Values cross the boundary too (C-834)
|
|
19
|
+
* wrapper around a bare value, no `is`/`isNot` wrapper around a to-one predicate,
|
|
20
|
+
* no `mode` nobody asked for.
|
|
29
21
|
*
|
|
30
22
|
* A predicate is a key AND an operand, and the operand is a value of the field's
|
|
31
23
|
* scalar type. Those values go out through `scalar.conversion.ts`, the one table
|
|
32
24
|
* that knows how each scalar crosses — the same answers `mutation.translator.ts`
|
|
33
|
-
* uses for a write, because it is the same boundary. {@link
|
|
34
|
-
* records, per operator, *whether* its operand is such a
|
|
35
|
-
* list of them, `isEmpty` carries a boolean, `mode` carries
|
|
36
|
-
* `path`/`string*`/`array*` carry a position inside a JSON
|
|
37
|
-
* value of the column. Converting indiscriminately would
|
|
38
|
-
* converting none of them is what let a core `Decimal`
|
|
39
|
-
* not rejected but **misread** (measured on both
|
|
25
|
+
* uses for a write, because it is the same boundary. {@link
|
|
26
|
+
* SCALAR_FILTER_EMISSIONS} records, per operator, *whether* its operand is such a
|
|
27
|
+
* value: `in` carries a list of them, `isEmpty` carries a boolean, `mode` carries
|
|
28
|
+
* a Prisma keyword, and `path`/`string*`/`array*` carry a position inside a JSON
|
|
29
|
+
* document rather than a value of the column. Converting indiscriminately would
|
|
30
|
+
* corrupt the second kind; converting none of them is what let a core `Decimal`
|
|
31
|
+
* reach Prisma, where it was not rejected but **misread** (measured on both
|
|
32
|
+
* engines, 2026-10-02):
|
|
40
33
|
*
|
|
41
34
|
* - `{ sDecimal: { gt: new Decimal("1") } }` → *"PrismaClientValidationError:
|
|
42
35
|
* Argument `_ref` is missing."* — Prisma read the object as a **FieldRef**;
|
|
@@ -52,15 +45,11 @@ import type { AdapterModel } from "@aventara/core";
|
|
|
52
45
|
*
|
|
53
46
|
* # Refusing is part of honouring
|
|
54
47
|
*
|
|
55
|
-
* The adapter assumes validated input
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* discovery decided not to advertise cannot reach a provider by another route.
|
|
61
|
-
* Core has already rejected such a request with `V1006`; if one ever arrives
|
|
62
|
-
* here, the model and the Contract have diverged and that is a defect, not a
|
|
63
|
-
* query.
|
|
48
|
+
* The adapter assumes validated input and performs no limit checking. It does,
|
|
49
|
+
* however, refuse to EMIT an operator the discovered model does not advertise for
|
|
50
|
+
* that field. Core has already rejected such a request with `V1006`; if one ever
|
|
51
|
+
* arrives here, the model and the Contract have diverged and that is a defect, not
|
|
52
|
+
* a query.
|
|
64
53
|
*/
|
|
65
54
|
/** A predicate the model does not license, or a key no vocabulary recognises. */
|
|
66
55
|
export declare class PrismaWhereTranslationError extends Error {
|
|
@@ -1,68 +1,4 @@
|
|
|
1
1
|
import { emitScalarValue } from "./scalar.conversion.js";
|
|
2
|
-
/**
|
|
3
|
-
* T-7 — `where` translation: one validated Aventara predicate in, one Prisma
|
|
4
|
-
* `<M>WhereInput` out.
|
|
5
|
-
*
|
|
6
|
-
* # Pure, and that is the property to protect
|
|
7
|
-
*
|
|
8
|
-
* No client, no database, no I/O, no provider name. The only inputs are the
|
|
9
|
-
* discovered model and plain data, which is what makes the whole filter grammar
|
|
10
|
-
* exhaustively testable without an engine (T-7's "single largest testability
|
|
11
|
-
* decision").
|
|
12
|
-
*
|
|
13
|
-
* # One direction, and no Prisma token the framework did not ask for
|
|
14
|
-
*
|
|
15
|
-
* Every emitted key comes from one of the two tables below, both of which are
|
|
16
|
-
* TOTAL over core's own capability unions — a member added to
|
|
17
|
-
* `ScalarFilterOperator` or `RelationFilterCapability` with no row here is a
|
|
18
|
-
* compile error, not a silently dropped predicate. That is C-855's emission half:
|
|
19
|
-
* *"capability derivation advertises an argument only when a named translator
|
|
20
|
-
* step emits it"*, and this module is that step for the filter vocabulary.
|
|
21
|
-
*
|
|
22
|
-
* The inverse matters more. A missing key loses a predicate and shows up as a
|
|
23
|
-
* wrong row count; an INVENTED key silently changes what the query means. So
|
|
24
|
-
* nothing is ever added to the output that the request did not carry: no `equals`
|
|
25
|
-
* wrapper around a bare value, no `is`/`isNot` wrapper around a to-one predicate
|
|
26
|
-
* (§18.5), no `mode` nobody asked for.
|
|
27
|
-
*
|
|
28
|
-
* # Values cross the boundary too (C-834)
|
|
29
|
-
*
|
|
30
|
-
* A predicate is a key AND an operand, and the operand is a value of the field's
|
|
31
|
-
* scalar type. Those values go out through `scalar.conversion.ts`, the one table
|
|
32
|
-
* that knows how each scalar crosses — the same answers `mutation.translator.ts`
|
|
33
|
-
* uses for a write, because it is the same boundary. {@link SCALAR_FILTER_EMISSIONS}
|
|
34
|
-
* records, per operator, *whether* its operand is such a value: `in` carries a
|
|
35
|
-
* list of them, `isEmpty` carries a boolean, `mode` carries a Prisma keyword, and
|
|
36
|
-
* `path`/`string*`/`array*` carry a position inside a JSON document rather than a
|
|
37
|
-
* value of the column. Converting indiscriminately would corrupt the second kind;
|
|
38
|
-
* converting none of them is what let a core `Decimal` reach Prisma, where it was
|
|
39
|
-
* not rejected but **misread** (measured on both engines, 2026-10-02):
|
|
40
|
-
*
|
|
41
|
-
* - `{ sDecimal: { gt: new Decimal("1") } }` → *"PrismaClientValidationError:
|
|
42
|
-
* Argument `_ref` is missing."* — Prisma read the object as a **FieldRef**;
|
|
43
|
-
* - `{ sDecimal: new Decimal("1") }` → **no error and 2 of 2 rows**, the same as
|
|
44
|
-
* no predicate at all, because core's `Decimal` keeps its digits in a private
|
|
45
|
-
* field and `Object.keys` is therefore empty: Prisma read an **empty
|
|
46
|
-
* `DecimalFilter`**. The string `"1"` returned 1 row.
|
|
47
|
-
*
|
|
48
|
-
* The second is why the bare-value branch below follows core's own discriminant —
|
|
49
|
-
* `isPlainRecord`, not "is an object" **[CORE, runtime/scalar-runtime.ts
|
|
50
|
-
* `normalizeScalarFilter`]**. A class instance is a VALUE; reading one as an
|
|
51
|
-
* operator record produced `{}`, and an empty filter constrains nothing.
|
|
52
|
-
*
|
|
53
|
-
* # Refusing is part of honouring
|
|
54
|
-
*
|
|
55
|
-
* The adapter assumes validated input (C-845) and performs no limit checking.
|
|
56
|
-
* It does, however, refuse to EMIT an operator the discovered model does not
|
|
57
|
-
* advertise for that field. That is not a second validation pass for the
|
|
58
|
-
* caller's benefit: it is the mechanism C-855 names — an argument is honoured or
|
|
59
|
-
* the variant is omitted — enforced at the emission site, so an operator that
|
|
60
|
-
* discovery decided not to advertise cannot reach a provider by another route.
|
|
61
|
-
* Core has already rejected such a request with `V1006`; if one ever arrives
|
|
62
|
-
* here, the model and the Contract have diverged and that is a defect, not a
|
|
63
|
-
* query.
|
|
64
|
-
*/
|
|
65
|
-
/** A predicate the model does not license, or a key no vocabulary recognises. */
|
|
66
2
|
export class PrismaWhereTranslationError extends Error {
|
|
67
3
|
name = "PrismaWhereTranslationError";
|
|
68
4
|
}
|
|
@@ -99,7 +35,6 @@ const RELATION_FILTER_EMISSIONS = {
|
|
|
99
35
|
every: { kind: "key", token: "every" },
|
|
100
36
|
none: { kind: "key", token: "none" },
|
|
101
37
|
};
|
|
102
|
-
/** Core's boolean nodes, whose spelling Prisma shares. */
|
|
103
38
|
const BOOLEAN_NODES = { AND: "AND", OR: "OR", NOT: "NOT" };
|
|
104
39
|
export function translateWhere(input) {
|
|
105
40
|
return translateNode(input.model, input.resource, input.where, "where");
|
|
@@ -122,8 +57,6 @@ function translateNode(model, resourceKey, node, path) {
|
|
|
122
57
|
continue;
|
|
123
58
|
}
|
|
124
59
|
if (key === BOOLEAN_NODES.NOT) {
|
|
125
|
-
// Aventara's `NOT` is ONE node. Prisma admits an array too; emitting one
|
|
126
|
-
// for a single node would be a shape the request never carried.
|
|
127
60
|
emitted[key] = translateNode(model, resourceKey, value, `${path}.${key}`);
|
|
128
61
|
continue;
|
|
129
62
|
}
|
|
@@ -143,9 +76,6 @@ function translateScalarPredicate(model, field, value, path) {
|
|
|
143
76
|
if (advertised === undefined || advertised.length === 0) {
|
|
144
77
|
throw new PrismaWhereTranslationError(`${path}: filtering is not available on this field`);
|
|
145
78
|
}
|
|
146
|
-
// Core's own rule: a value that is not a PLAIN record — `null` and a class
|
|
147
|
-
// instance included — IS equality, and Prisma spells that the same way.
|
|
148
|
-
// Wrapping it in `{ equals: … }` would emit a token the request never carried.
|
|
149
79
|
if (!isPlainRecord(value)) {
|
|
150
80
|
requireOperator(advertised, "equals", path);
|
|
151
81
|
return emitScalarValue(model, field, value, path);
|
|
@@ -161,7 +91,6 @@ function translateScalarPredicate(model, field, value, path) {
|
|
|
161
91
|
}
|
|
162
92
|
return operand;
|
|
163
93
|
}
|
|
164
|
-
/** One operator's operand, converted as the operator's shape says it must be. */
|
|
165
94
|
function emitOperand(model, field, shape, argument, path) {
|
|
166
95
|
switch (shape) {
|
|
167
96
|
case "opaque":
|
|
@@ -172,13 +101,8 @@ function emitOperand(model, field, shape, argument, path) {
|
|
|
172
101
|
if (!Array.isArray(argument)) {
|
|
173
102
|
throw new PrismaWhereTranslationError(`${path} must be an array of values`);
|
|
174
103
|
}
|
|
175
|
-
// `emitScalarValue` converts an array elementwise, which is what a list
|
|
176
|
-
// operand is — and what a LIST column's single value would be too, so the
|
|
177
|
-
// array is handed over whole rather than mapped here.
|
|
178
104
|
return emitScalarValue(model, field, argument, path);
|
|
179
105
|
case "value-or-filter":
|
|
180
|
-
// Core recurses on `not` exactly when its operand is a plain record, so
|
|
181
|
-
// the adapter does too; anything else is a value.
|
|
182
106
|
return isPlainRecord(argument)
|
|
183
107
|
? translateScalarPredicate(model, field, argument, path)
|
|
184
108
|
: emitScalarValue(model, field, argument, path);
|
|
@@ -194,7 +118,6 @@ function translateRelationPredicate(model, field, value, path) {
|
|
|
194
118
|
if (field.cardinality === "one") {
|
|
195
119
|
requireRelationFilter(advertised, "match", path);
|
|
196
120
|
assertEmission("match", path);
|
|
197
|
-
// §18.5: the nested predicate, unwrapped. No `is`.
|
|
198
121
|
return translateNode(model, field.target, value, path);
|
|
199
122
|
}
|
|
200
123
|
if (!isRecord(value)) {
|
|
@@ -214,7 +137,6 @@ function translateRelationPredicate(model, field, value, path) {
|
|
|
214
137
|
}
|
|
215
138
|
return operand;
|
|
216
139
|
}
|
|
217
|
-
/** The two capabilities whose emission is a shape rather than a key. */
|
|
218
140
|
function assertEmission(capability, path) {
|
|
219
141
|
const emission = RELATION_FILTER_EMISSIONS[capability];
|
|
220
142
|
if (emission.kind === "key" || emission.kind === "edge-argument") {
|
|
@@ -240,11 +162,6 @@ function isRelationFilterCapability(key) {
|
|
|
240
162
|
function isRecord(value) {
|
|
241
163
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
242
164
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Core's discriminant between a filter record and a VALUE, mirrored
|
|
245
|
-
* **[CORE, runtime/scalar-runtime.ts `isPlainRecord`]**: a class instance — a
|
|
246
|
-
* core `Decimal`, a `Date`, a `Uint8Array` — is a value, not a bag of operators.
|
|
247
|
-
*/
|
|
248
165
|
function isPlainRecord(value) {
|
|
249
166
|
if (!isRecord(value)) {
|
|
250
167
|
return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aventara/prisma7-adapter",
|
|
3
|
-
"version": "0.1.0-pilot.
|
|
3
|
+
"version": "0.1.0-pilot.2",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "The Prisma 7 adapter for Aventara.",
|
|
6
6
|
"type": "module",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"LICENSE-ADDITIONAL-PERMISSION.md"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@aventara/core": "0.1.0-pilot.
|
|
32
|
+
"@aventara/core": "0.1.0-pilot.2"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"@prisma/adapter-better-sqlite3": "^7.10.0",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"typescript-6": "npm:typescript@6.0.3"
|
|
91
91
|
},
|
|
92
92
|
"scripts": {
|
|
93
|
-
"build": "node node_modules/typescript/bin/tsc -p tsconfig.json",
|
|
93
|
+
"build": "node node_modules/typescript/bin/tsc -p tsconfig.json --removeComments --declaration false && node node_modules/typescript/bin/tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist",
|
|
94
94
|
"typecheck": "node node_modules/typescript/bin/tsc -p tsconfig.typecheck.json --noEmit",
|
|
95
95
|
"generate": "node node_modules/typescript/bin/tsc -p tsconfig.generate.json && node .generate/generate/discovery.generate.js && prisma generate --schema src/discovery/__fixtures__/ignore.prisma",
|
|
96
96
|
"db:push": "prisma db push --config prisma.config.sqlite.ts && sh -c 'if [ -n \"$AVENTARA_TEST_POSTGRES_URL\" ]; then prisma db push --config prisma.config.postgres.ts; else echo \"AVENTARA_TEST_POSTGRES_URL is unset: skipping the PostgreSQL schema push\"; fi'",
|