@aventara/prisma7-adapter 1.0.0-rc.1 → 1.0.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +179 -0
- package/README.md +68 -20
- package/dist/discovery/artifact.loader.d.ts +6 -2
- package/dist/discovery/artifact.loader.js +6 -1
- package/dist/discovery/capability.deriver.d.ts +4 -1
- package/dist/discovery/capability.deriver.js +38 -3
- package/dist/discovery/discovery-artifact.interface.d.ts +10 -2
- package/dist/discovery/discovery-artifact.interface.js +1 -1
- package/dist/discovery/installed-client.locator.d.ts +13 -0
- package/dist/discovery/installed-client.locator.js +43 -0
- package/dist/discovery/model.discoverer.d.ts +3 -0
- package/dist/discovery/model.discoverer.js +14 -2
- package/dist/discovery/schema-facts.interface.d.ts +8 -0
- package/dist/discovery/schema.reader.js +23 -3
- package/dist/errors/back-reference.locator.d.ts +25 -0
- package/dist/errors/back-reference.locator.js +100 -0
- package/dist/errors/bigint-binding.locator.d.ts +7 -0
- package/dist/errors/bigint-binding.locator.js +27 -0
- package/dist/errors/error.normalizer.d.ts +34 -2
- package/dist/errors/error.normalizer.js +70 -2
- package/dist/errors/prisma-error.reader.d.ts +4 -0
- package/dist/errors/prisma-error.reader.js +2 -0
- package/dist/execution/operation.executor.d.ts +6 -0
- package/dist/execution/operation.executor.js +21 -8
- package/dist/execution/transaction.executor.d.ts +4 -0
- package/dist/execution/transaction.executor.js +2 -0
- package/dist/generate/artifact.emitter.d.ts +6 -0
- package/dist/generate/artifact.emitter.js +56 -2
- package/dist/generate/computed-fields.reader.d.ts +19 -0
- package/dist/generate/computed-fields.reader.js +210 -0
- package/dist/generate/declared-support.reader.d.ts +1 -1
- package/dist/generate/discovery.cli.d.ts +1 -0
- package/dist/generate/discovery.cli.js +20 -4
- package/dist/generate/index.d.ts +8 -0
- package/dist/generate/index.js +3 -0
- package/dist/generate/prisma.generate.d.ts +17 -0
- package/dist/generate/prisma.generate.js +100 -7
- package/dist/prisma.adapter.d.ts +4 -4
- package/dist/prisma.adapter.js +37 -2
- package/dist/provider/provider-profile.d.ts +19 -3
- package/dist/provider/provider-profile.js +21 -0
- package/dist/translation/json-null.sentinel.d.ts +7 -0
- package/dist/translation/json-null.sentinel.js +17 -0
- package/dist/translation/mutation.translator.js +7 -0
- package/dist/translation/where.translator.js +100 -17
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,184 @@
|
|
|
1
1
|
# @aventara/prisma7-adapter
|
|
2
2
|
|
|
3
|
+
## 1.0.0-rc.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b38034d: `aventara-prisma7-generate` refuses a `--provider` that the schema's `datasource` block contradicts, or a block naming
|
|
8
|
+
a provider the adapter does not support, before `prisma generate` runs, so neither the Prisma client nor the artifact
|
|
9
|
+
is written:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
aventara-prisma7-generate: --provider postgresql contradicts the schema's datasource `db` (prisma/schema.prisma:12), which names provider "sqlite"; pass --provider sqlite.
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
PSL's `postgres` spelling counts as `postgresql`, and a provider that is not a string literal is not compared.
|
|
16
|
+
Before, the flag was taken on trust, and a wrong one wrote the wrong provider into the artifact and the wrong filter
|
|
17
|
+
modes into the Contract.
|
|
18
|
+
|
|
19
|
+
When the file system refuses the artifact file (`--out`), the step says so in one sentence naming the file and why,
|
|
20
|
+
such as
|
|
21
|
+
`the artifact file /app/src/generated/aventara/discovery.artifact.ts (--out) cannot be written: permission denied (EACCES).`,
|
|
22
|
+
and leaves no staged file behind. Before, every such failure except a directory printed a stack trace.
|
|
23
|
+
|
|
24
|
+
- b38034d: On PostgreSQL, a `@unique` value too long for its index (an index row past the B-tree's 8191 bytes, SQLSTATE
|
|
25
|
+
`54000`) is answered `422 A2004`, not `500 A3001`: one `V1004` issue at `["arguments"]`, as a value too long for its
|
|
26
|
+
column already was. The adapter reads the SQLSTATE, never the message. The issue's message for a value too long now
|
|
27
|
+
reads `The database refused this value: it is longer than the database can hold for its column.`
|
|
28
|
+
(it said `longer than its column can hold`, which an index refusal is not).
|
|
29
|
+
- Updated dependencies [b38034d]
|
|
30
|
+
- Updated dependencies [b38034d]
|
|
31
|
+
- Updated dependencies [b38034d]
|
|
32
|
+
- Updated dependencies [b38034d]
|
|
33
|
+
- Updated dependencies [b38034d]
|
|
34
|
+
- Updated dependencies [b38034d]
|
|
35
|
+
- @aventara/core@1.0.0-rc.3
|
|
36
|
+
|
|
37
|
+
## 1.0.0-rc.2
|
|
38
|
+
|
|
39
|
+
### Patch Changes
|
|
40
|
+
|
|
41
|
+
- On Cloudflare D1, a `BigInt` value beyond ±(2^53 − 1) is answered `A2004`, not `A3001`. `@prisma/adapter-d1`
|
|
42
|
+
binds a bigint as a JavaScript number and refuses one beyond that range before the statement runs, written,
|
|
43
|
+
compared in a filter or used in an `$increment`, and Prisma reports it as a database error (`P2039`). The adapter
|
|
44
|
+
answers that refusal as a value the database refused: `422 A2004` with one `V1004` issue, `The database refused
|
|
45
|
+
this value: it is outside the range its column can hold.`, at the value in the arguments (for example
|
|
46
|
+
`["arguments", "where", "views", "gt"]`). Nothing is written by the refused statement. It reads the arguments only
|
|
47
|
+
after the driver refused them, and only for the D1 driver; every other D1 database error stays `A3001`, and so does
|
|
48
|
+
a raw `ctx.$sql` statement binding such a value. Within ±(2^53 − 1) a bigint round-trips exactly on D1.
|
|
49
|
+
In rc.1 the refusal answered `500 A3001`. Measured on 7.10.0 through Miniflare's D1 binding.
|
|
50
|
+
- The README's D1 notes are corrected: D1 is measured in process through Miniflare, where Prisma's client refuses
|
|
51
|
+
an interactive `$transaction` and the D1 driver runs Prisma's implicit transaction for a nested write as single
|
|
52
|
+
statements, so a nested write is not atomic on D1.
|
|
53
|
+
- The optional `typescript` peer range is `>=5.7.0 <7` (it was `>=5.5.0 <7`). Under TypeScript 5.5 and 5.6 a
|
|
54
|
+
variant's stages written inline in `createFramework` are told `unknown` arguments, so `ctx.$args.data` and the
|
|
55
|
+
documented spread pipe `(args) => ({ ...args, data: { ...args.data, title: "t" } })` do not compile; TypeScript 5.7
|
|
56
|
+
and later type them. A project on TypeScript 5.5 or 5.6 upgrades to 5.7 or later.
|
|
57
|
+
- `createPrismaAdapter` checks the installed `@prisma/client`'s major against the discovery artifact's, as the
|
|
58
|
+
README says. It reads the version from `@prisma/client/package.json`, resolved from the adapter's own location and
|
|
59
|
+
then from the working directory, and refuses an artifact generated against another major with
|
|
60
|
+
`AvPrismaDiscoveryArtifactError`. In rc.1 the artifact's version stood in for the installed one when no
|
|
61
|
+
`installedPrismaVersion` was passed, which a scaffolded project never does, so the check compared the artifact with
|
|
62
|
+
itself. Where the version cannot be read (a runtime without Node's module loader, a bundle deployed without
|
|
63
|
+
`@prisma/client`), the adapter starts and emits a warning, code `AVENTARA_PRISMA_VERSION_UNCHECKED`, saying the
|
|
64
|
+
majors were not compared. `installedPrismaVersion` still overrides the version read.
|
|
65
|
+
- `aventara-prisma7-generate` refuses an `--out` inside `--client`, before `prisma generate` runs: exit 1 and one
|
|
66
|
+
sentence naming both paths. Prisma's next `prisma generate` clears the client's directory, so an artifact written
|
|
67
|
+
there was deleted. In rc.1 the step wrote it and exited 0. The same refusal applies to
|
|
68
|
+
`generatePrismaDiscoveryArtifact` and `runPrismaGenerateStep`.
|
|
69
|
+
- `aventara-prisma7-generate` refuses an `--out` that names a directory in one sentence (exit 1), before
|
|
70
|
+
`prisma generate` runs. In rc.1 it ran Prisma and then failed with an `EISDIR` stack trace.
|
|
71
|
+
- A nested write that writes its relation back is answered `A2004`, not `A3001`. Prisma writes a record reached
|
|
72
|
+
through a relation without that relation's other end, so it refuses a post created in `author.posts` that names
|
|
73
|
+
`author` or `authorId`, and a category created in a post's `category` that nests `posts`. The discovery artifact
|
|
74
|
+
now states each relation's other end (`inverse`, paired from the schema by relation name) and no relation writable
|
|
75
|
+
at the `"loopback"` position, so `@aventara/core` refuses such a write before any guard or SQL: `A2004`, `V1012` at
|
|
76
|
+
the field written back. It holds for `$create`, `$connectOrCreate`, `$update` and `$upsert`, to-one and to-many,
|
|
77
|
+
self-relations and one-to-ones. Where a layer hides the relation's other end, its Contract states no pairing and
|
|
78
|
+
Prisma's client is the one that refuses: the adapter answers that refusal as the caller's too, `A2004` with a
|
|
79
|
+
`V1012` issue at the field and the message `The data source refused this nested write: a record written through a
|
|
80
|
+
relation cannot write that relation or its reference fields back.` It reads the arguments only after Prisma
|
|
81
|
+
refused them; every other refusal of Prisma's client stays `A3001`. In rc.1 every case answered `500 A3001`.
|
|
82
|
+
Measured on 7.10.0, SQLite and PostgreSQL. Run `npm run aventara:prepare` to regenerate the artifact: the model's
|
|
83
|
+
hash and every Contract's hash change.
|
|
84
|
+
- The discovery artifact states each relation's reference fields (`referenceFields`, read from the schema's
|
|
85
|
+
`@relation(fields: …)`), on optional and required relations alike, and none on the end that holds no reference.
|
|
86
|
+
So the types leave `categoryId` out of a post created in `Category.posts`, as `@aventara/core` refuses it. Run
|
|
87
|
+
`npm run aventara:prepare` to regenerate the artifact: the model's hash and every Contract's hash change.
|
|
88
|
+
- A `$divide` by zero is answered `A2004`, not `A3001`. It reaches the database as given: PostgreSQL refuses the
|
|
89
|
+
division (`P2039`, `22012`) and the adapter answers `V1004` `The database refused this value: it divides by zero.`;
|
|
90
|
+
SQLite computes NULL and the `NOT NULL` column refuses it (`P2011`), answered `V1004` `The database refused this
|
|
91
|
+
value: the write computes no value for a column that requires one, as a division by zero does.` Both at
|
|
92
|
+
`["arguments"]`, nothing written. `P2011` reaches the adapter only this way: core refuses a `null` for a required
|
|
93
|
+
field and a missing one, and so does Prisma's client before the query. In rc.1 both answered `500 A3001`. Measured
|
|
94
|
+
on 7.10.0 for `Int`, `BigInt`, `Float` and `Decimal`, in `update` and `updateMany`.
|
|
95
|
+
- `null` on a nullable `Json` field is the database NULL, as on every other nullable field. A write of `null`
|
|
96
|
+
(create, update, upsert, bulk, nested, a plan step) is sent as Prisma's `DbNull`; `{ meta: null }` and
|
|
97
|
+
`{ meta: { equals: null } }` are sent as Prisma's `AnyNull` and match every row that reads back `null` (a database
|
|
98
|
+
NULL or a JSON `null` stored by other means), and `{ meta: { not: null } }` matches the other rows. A JSON `null`
|
|
99
|
+
inside a value is sent as written, and so is a `null` beside a `path`: Prisma matches the documents whose value at
|
|
100
|
+
that path is a JSON `null`. In rc.1 a write of `null` stored a JSON `null`, `equals: null` matched only the stored
|
|
101
|
+
JSON nulls, and `{ meta: null }` failed with `500 A3001`. Reads are unchanged. Measured on 7.10.0, SQLite and
|
|
102
|
+
PostgreSQL.
|
|
103
|
+
- A to-one relation filter is sent under Prisma's `is`. `{ category: {} }` matches the rows that have a related row and
|
|
104
|
+
`{ NOT: { category: {} } }` the rows that have none. In rc.1 the empty predicate was sent unwrapped, which Prisma
|
|
105
|
+
reads as no condition: both matched every row, and `Post.delete.count({ where: { NOT: { author: {} } } })` deleted
|
|
106
|
+
every post although each has its required author. A non-empty nested `where` and `null` match what they did.
|
|
107
|
+
- A value the database refuses is answered `A2004`, not `A3001`. Text-match operands and written values reach Prisma
|
|
108
|
+
as given (`%` and `_` are what the engine's `LIKE` makes of them, a column rounds or keeps what it does); when
|
|
109
|
+
PostgreSQL refuses a caller's value, the adapter answers `A2004` with one issue at `["arguments"]` (Prisma's error
|
|
110
|
+
names no column): `V1004` for a value too long for its column (`P2000`), out of its column's range (`P2020`: a
|
|
111
|
+
`Decimal` past its precision, `Number.MAX_VALUE` in a `Float`, an `Int` `$increment` past 2^31) or a `DateTime`
|
|
112
|
+
outside PostgreSQL's range (`P2039`, `22009`); `V1001` for U+0000 in text or `json` (`P2039`, `22021` and `22P05`),
|
|
113
|
+
a lone surrogate in `json` (`P2007`) and a `LIKE` pattern ending in `\` (`P2039`, `22025`). The issue's message
|
|
114
|
+
says which of these the database refused, in core's words and none of the database's (`The database refused this
|
|
115
|
+
value: it is longer than its column can hold.`), not the code's generic text. Inside a plan the
|
|
116
|
+
issue stands at the step (`["operations", i, "args"]`) and the plan rolls back. Every other database failure stays
|
|
117
|
+
`A3001`. In rc.1 each of these answered `500 A3001`. Measured on 7.10.0, SQLite and PostgreSQL; SQLite refused none
|
|
118
|
+
of these values.
|
|
119
|
+
- A field's `not` holding an operator object now means the negation of the whole object, on every field type, on
|
|
120
|
+
both engines, in reads and in bulk writes. `{ f: { not: X } }` matches exactly the rows `{ NOT: { f: X } }` matches:
|
|
121
|
+
a row where `X` is neither true nor false (the column is `NULL`, a `json` `path` is not in the document, a `json`
|
|
122
|
+
value is not the kind the operator reads) matches neither `X` nor `not: X`, as SQL's `NOT` reads it. The adapter
|
|
123
|
+
sends it to Prisma as `NOT` of the field's filter, ANDed with the rest of the `where`, because Prisma's own
|
|
124
|
+
field-level `not` says less (measured on 7.10.0, SQLite and PostgreSQL):
|
|
125
|
+
- In rc.1, a `json` field's `not` holding operators matched every row on both engines, because Prisma reads that
|
|
126
|
+
`not` as a JSON value to compare the document with. That covers `{ meta: { not: { path: ["label"],
|
|
127
|
+
stringContains: "x" } } }`, `{ meta: { not: { equals: { a: 1 } } } }` and every other operator under it, and an
|
|
128
|
+
`update.many`, `update.count`, `delete.count` or `delete.first` under such a filter wrote or deleted every row
|
|
129
|
+
the rest of the filter allowed.
|
|
130
|
+
- In rc.1, two or more operators under `not` were negated one at a time and ANDed, so
|
|
131
|
+
`{ views: { not: { gt: 2, lte: 5 } } }` matched no row. It now matches the rows outside that range.
|
|
132
|
+
- In rc.1 on PostgreSQL, a `mode` inside `not` (`{ title: { not: { contains: "x", mode: "insensitive" } } }`)
|
|
133
|
+
failed with `A3001`, because Prisma's filter under `not` has no `mode`. It now matches the titles that do not
|
|
134
|
+
contain `x` in any case.
|
|
135
|
+
|
|
136
|
+
The object under `not` is its own level: nothing from the object around it reaches into it. In rc.1 on
|
|
137
|
+
PostgreSQL, `{ title: { mode: "insensitive", not: { contains: "x" } } }` excluded `X` as well as `x`, because Prisma
|
|
138
|
+
applied the outer `mode` inside its own `not`. Core now refuses that filter (`A2004`, `V1001` at the `mode`); write
|
|
139
|
+
`{ title: { not: { contains: "x", mode: "insensitive" } } }`. A plain value under `not` (`{ not: "x" }`,
|
|
140
|
+
`{ not: null }`) is equality negated and is sent as before.
|
|
141
|
+
- A `json` field's bare value in a filter is equality on both engines: `{ meta: "x" }` and `{ meta: [1, 2] }` are sent
|
|
142
|
+
to Prisma as `{ equals: … }`. In rc.1 they failed with `A3001`, because Prisma's Json filter takes no bare value.
|
|
143
|
+
- Filter modes, measured on each engine. PostgreSQL offers `"default"`, `"sensitive"` and `"insensitive"` on a string
|
|
144
|
+
column and on a `json` value at a `path`; on a whole `json` document it offers `"default"` and `"sensitive"`, because
|
|
145
|
+
Prisma accepts an insensitive `stringContains` there and PostgreSQL then fails (`42883 function lower(jsonb) does not
|
|
146
|
+
exist`, answered `A3001`). That filter is now refused before any query (`A2004`, `V1006` at the `mode`). SQLite, and
|
|
147
|
+
so D1, offers `"default"` only: its `LIKE` folds ASCII case and nothing else, and `equals` folds none, so neither
|
|
148
|
+
promise holds. Every string field with a text operator now offers `mode` (on SQLite with `"default"` alone), and
|
|
149
|
+
`"default"` and `"sensitive"` are sent to Prisma as no `mode` at all. Regenerate the discovery artifact
|
|
150
|
+
(`aventara:prepare`).
|
|
151
|
+
- 9366e16: The discovery artifact names the model's type: it exports `AvModel`, and `model` is declared with it
|
|
152
|
+
(`typeof model` is `AvModel`). A project that emits declarations (Nest's `declaration: true`) now writes that
|
|
153
|
+
name wherever an exported declaration holds the model, instead of the whole model. Over 50 models the scaffold's
|
|
154
|
+
`aventara.config.d.ts` goes from 485 KB to 0.4 KB, and a `buildFramework` / `AppFramework` module from 931 KB to
|
|
155
|
+
0.6 KB (1.2 MB to 0.6 KB, and 2.4 MB to 22 KB for exported configurations holding typed stages, in the consumer-build
|
|
156
|
+
gate). Regenerate the artifact (`aventara:prepare`, which runs on install, build and start) to get it; Contract bytes
|
|
157
|
+
and hashes are unchanged.
|
|
158
|
+
- New: `aventara-prisma7-generate --config <file>`, optional. Given your Aventara configuration module
|
|
159
|
+
(`src/aventara.config.ts` in a project set up by `aventara`; its `aventaraConfig` export, a function or a value, or
|
|
160
|
+
its default export), the artifact states which fields it computes on create and on update, per layer. They are read
|
|
161
|
+
off the configuration's TypeScript type, never by running it, so nothing connects to a database and no environment
|
|
162
|
+
is needed. The artifact exports them as `computedFields` (type `AvComputedFields`), carries them on `AvModel` so a
|
|
163
|
+
variant's guards, pipes and filters may leave exactly those fields out of a create, and hands them to the framework
|
|
164
|
+
through `discovery.computedFields`, which it compares with the configuration at startup. A configuration that is
|
|
165
|
+
not a literal where a compute stage is written (one annotated with the wide `AvConfig`, a conditional spread) is not
|
|
166
|
+
read: the step prints where, and the artifact states nothing, so those stages may leave any create field out. A
|
|
167
|
+
`--config` file that does not exist, or exports neither `aventaraConfig` nor a default, stops the step.
|
|
168
|
+
`generatePrismaDiscoveryArtifact` and `runPrismaGenerateStep` take the same `config`, and
|
|
169
|
+
`readConfigComputedFields` is exported from `@aventara/prisma7-adapter/generate`.
|
|
170
|
+
- The discovery artifact's format is now 4 (`computedFields` is new): an artifact written before it is refused with
|
|
171
|
+
the regeneration instruction. `aventara:prepare` regenerates it on install, build and start.
|
|
172
|
+
- A transaction that expired (or was closed) under a step is answered `A3002`, as when the COMMIT finds it expired:
|
|
173
|
+
Prisma's `P2028`, raised by the step's own call, is the transaction's failure, at that step (`cause.operation`),
|
|
174
|
+
and the plan rolls back. In rc.1 it was unclassified and answered `500 A3001`, an adapter failure. Measured on
|
|
175
|
+
7.10.0, SQLite and PostgreSQL.
|
|
176
|
+
- Updated dependencies [9366e16]
|
|
177
|
+
- Updated dependencies [9366e16]
|
|
178
|
+
- Updated dependencies [9366e16]
|
|
179
|
+
- Updated dependencies [9366e16]
|
|
180
|
+
- @aventara/core@1.0.0-rc.2
|
|
181
|
+
|
|
3
182
|
## 1.0.0-rc.1
|
|
4
183
|
|
|
5
184
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ suggest. Everything you need is in the body of this file, not in footnotes.
|
|
|
11
11
|
- Status: release candidate. Two engines are proven, PostgreSQL and SQLite. Every other Prisma provider is
|
|
12
12
|
unmeasured, and this file says so where it matters.
|
|
13
13
|
- Peer dependencies: `@prisma/client` `^7.10.0` (required); `prisma`, `@prisma/adapter-better-sqlite3`
|
|
14
|
-
and `@prisma/adapter-pg` `^7.10.0` and `typescript` `>=5.
|
|
14
|
+
and `@prisma/adapter-pg` `^7.10.0` and `typescript` `>=5.7.0 <7` (optional). The error map targets
|
|
15
15
|
7.10.0, and the adapter is bound to Prisma 7's error surface; see [Prisma 7 only](#prisma-7-only).
|
|
16
16
|
|
|
17
17
|
---
|
|
@@ -82,11 +82,13 @@ npx aventara-prisma7-generate \
|
|
|
82
82
|
--client src/generated \
|
|
83
83
|
--provider postgresql \
|
|
84
84
|
--driver @prisma/adapter-pg \
|
|
85
|
-
--out src/discovery.artifact.ts
|
|
85
|
+
--out src/discovery.artifact.ts \
|
|
86
|
+
--config src/aventara.config.ts
|
|
86
87
|
```
|
|
87
88
|
|
|
88
|
-
`--provider` and `--driver` are declared, never sniffed. The provider is recorded in the artifact
|
|
89
|
-
|
|
89
|
+
`--provider` and `--driver` are declared, never sniffed. The provider is recorded in the artifact, and the
|
|
90
|
+
step refuses a `--provider` that the schema's `datasource` block contradicts (`provider = "sqlite"` with
|
|
91
|
+
`--provider postgresql`), naming the flag to pass. The driver decides `AvAdapterModel.transactions`, a driver fact the schema cannot state. Declare the driver
|
|
90
92
|
the application will actually construct the adapter with. The two cannot drift silently, but they can
|
|
91
93
|
disagree, and core refuses construction when they do.
|
|
92
94
|
|
|
@@ -97,9 +99,20 @@ installed keeps `@prisma/prisma7` beside it. With neither usable, the step refus
|
|
|
97
99
|
both. TypeScript 6 is supported; TypeScript 7, which has no classic compiler API, is refused in one
|
|
98
100
|
sentence.
|
|
99
101
|
|
|
100
|
-
`--
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
`--config` is optional. It names your Aventara configuration module: its `aventaraConfig` export (a
|
|
103
|
+
function, as the `aventara` scaffold writes it, or a value) or its default export. The step reads the
|
|
104
|
+
configuration's TypeScript type, never runs it, and states in the artifact which fields its
|
|
105
|
+
`compute.create` and `compute.update` stages fill, per layer (`computedFields`). A variant's guards,
|
|
106
|
+
pipes and filters, which run before write compute, may then leave exactly those fields out of a create;
|
|
107
|
+
every other required field stays required. Without `--config`, or when the configuration is not a
|
|
108
|
+
literal where a compute stage is written (the step prints where), the artifact states nothing and those
|
|
109
|
+
stages may leave any create field out. When the framework starts, it compares the statement with the
|
|
110
|
+
configuration and warns (`AVENTARA_STALE_GENERATED_TYPES`) if you changed a compute stage without
|
|
111
|
+
running the step again.
|
|
112
|
+
|
|
113
|
+
`--out` must not be inside `--client`: Prisma's next `prisma generate` clears that directory and would
|
|
114
|
+
delete the artifact. The step refuses such an `--out`, and one that names a directory, in one sentence
|
|
115
|
+
before Prisma runs.
|
|
103
116
|
|
|
104
117
|
The same step can run from code, for a project that already has a build script:
|
|
105
118
|
|
|
@@ -146,19 +159,31 @@ at runtime, after your pipes and computed behaviours have run, it is answered `A
|
|
|
146
159
|
issue at its path (`["arguments", "data", "title"]`, or `["arguments", "data", "author"]` for a
|
|
147
160
|
required relation) before the database can refuse it.
|
|
148
161
|
|
|
162
|
+
The module also exports the model's type by name, `AvModel` (`typeof model` is that type). A
|
|
163
|
+
project that emits declarations (Nest's `declaration: true`) writes that name wherever an exported
|
|
164
|
+
declaration holds the model, such as the scaffold's `aventaraConfig()`, an `AppFramework` alias or
|
|
165
|
+
`defineResource<typeof model>()`, so those declarations stay under a kilobyte at any schema size. Before
|
|
166
|
+
the name, each mention wrote the whole model out, about 0.5 MB over 50 models.
|
|
167
|
+
|
|
149
168
|
`modelHash` digests the model and nothing else. The addressing table, the referential actions and the
|
|
150
169
|
client record sit beside it and outside the hash. A database-side rename or a foreign-key action
|
|
151
170
|
changes no semantics; inside the hash, either would move the Contract identity and force every
|
|
152
171
|
generated client to be regenerated for nothing.
|
|
153
172
|
|
|
154
173
|
On the runtime path there is no TypeScript program, no PSL parser and no Prisma tooling. The loader
|
|
155
|
-
reads no file: the artifact arrives as a value you import.
|
|
174
|
+
reads no file: the artifact arrives as a value you import. The adapter reads one: the installed
|
|
175
|
+
`@prisma/client`'s `package.json`, for its version.
|
|
156
176
|
|
|
157
177
|
At construction, before the application listens, the adapter checks three things and fails hard with
|
|
158
178
|
an instruction to regenerate if any is wrong:
|
|
159
179
|
|
|
160
180
|
1. the artifact matches itself: the `modelHash` over its own canonicalized model;
|
|
161
|
-
2. the installed `@prisma/client` major is the one the artifact was generated against
|
|
181
|
+
2. the installed `@prisma/client` major is the one the artifact was generated against. The version is
|
|
182
|
+
read from `@prisma/client/package.json`, resolved from the adapter's own location and then from the
|
|
183
|
+
working directory. Where it cannot be read (a runtime without Node's module loader, a bundle
|
|
184
|
+
deployed without `@prisma/client`), the adapter starts and emits a warning, code
|
|
185
|
+
`AVENTARA_PRISMA_VERSION_UNCHECKED`, saying the majors were not compared;
|
|
186
|
+
`installedPrismaVersion` overrides the version read;
|
|
162
187
|
3. the Prisma client you pass is the one the artifact was generated from: the same models, and the
|
|
163
188
|
same field references (`prisma.<model>.fields`) on each. A schema change followed by
|
|
164
189
|
`prisma generate` but not by the generate step (a model or field added, removed or renamed) is
|
|
@@ -213,10 +238,13 @@ On PostgreSQL and SQLite, read from the generated types:
|
|
|
213
238
|
| `create.many` / `update.many` (row-returning bulk writes) | advertised | advertised | `createManyAndReturn` / `updateManyAndReturn` on the delegate |
|
|
214
239
|
| Transactions | `"interactive"` | `"interactive"` | the driver, not the provider (see below) |
|
|
215
240
|
|
|
216
|
-
Nothing beyond PostgreSQL and SQLite is measured.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
241
|
+
Nothing beyond PostgreSQL and SQLite is measured. Cloudflare D1 is SQLite through `@prisma/adapter-d1`:
|
|
242
|
+
it reads the same generated client, so its capabilities are SQLite's, and it is measured in process
|
|
243
|
+
through Miniflare's D1 binding, where it differs from SQLite in two ways (no transactions, and a bigint
|
|
244
|
+
within ±(2^53 − 1); see below). MySQL, SQL Server, MongoDB and CockroachDB were not generated against.
|
|
245
|
+
Because every capability is read from the client you actually generated, a provider that lacks
|
|
246
|
+
something advertises less instead of failing at runtime, but this release makes no claim about those
|
|
247
|
+
providers.
|
|
220
248
|
|
|
221
249
|
### `update.many` and `create.many` cannot project a to-many relation
|
|
222
250
|
|
|
@@ -372,8 +400,7 @@ commits, do not perform it in an after hook.
|
|
|
372
400
|
### `P2028` covers seven situations, and three of them are separable
|
|
373
401
|
|
|
374
402
|
`P2028` is Prisma 7's transaction code, and seven internal classes share it. The adapter answers
|
|
375
|
-
`A3002 TRANSACTION_ERROR`
|
|
376
|
-
to decide anything.
|
|
403
|
+
`A3002 TRANSACTION_ERROR` wherever it is met, and never reads the message text to decide anything.
|
|
377
404
|
|
|
378
405
|
What can be told apart from the error's structure, measured on PostgreSQL and SQLite:
|
|
379
406
|
|
|
@@ -391,8 +418,9 @@ timeout cannot be told apart from them. That limit is Prisma's, and it is record
|
|
|
391
418
|
works around it by parsing messages.
|
|
392
419
|
|
|
393
420
|
A `P2028` that ends the transaction rejects out of `$transaction` and becomes `A3002`. A `P2028`
|
|
394
|
-
raised by a delegate call inside the callback
|
|
395
|
-
attributed to that operation
|
|
421
|
+
raised by a delegate call (or a raw statement) inside the callback, such as a step that runs after the
|
|
422
|
+
`timeout` passed, is in the closed code table and also becomes `A3002`, attributed to that operation
|
|
423
|
+
(`cause.operation`). In rc.1 that second case was unclassified and answered `A3001`.
|
|
396
424
|
|
|
397
425
|
### Everything unrecognised becomes `A3001`
|
|
398
426
|
|
|
@@ -438,9 +466,12 @@ observe that it did. A commit failure is different: it rejects like a callback t
|
|
|
438
466
|
|
|
439
467
|
### `transactions` is a driver fact, and it reaches the ClientContract hash
|
|
440
468
|
|
|
441
|
-
`@prisma/adapter-d1` declares `transactions: "none"`:
|
|
442
|
-
|
|
443
|
-
`
|
|
469
|
+
`@prisma/adapter-d1` declares `transactions: "none"`: Prisma's client refuses an interactive
|
|
470
|
+
`$transaction` on D1 itself. Prisma still opens its own implicit transaction for a nested write (a
|
|
471
|
+
`$create` under a relation, a `$connect`), and the D1 driver runs it as single statements, so a nested
|
|
472
|
+
write is not atomic on D1: when a later statement fails, the rows the earlier ones wrote are kept.
|
|
473
|
+
`@prisma/adapter-pg` and `@prisma/adapter-better-sqlite3` declare `"interactive"`, observed by running
|
|
474
|
+
one.
|
|
444
475
|
|
|
445
476
|
Plan for the consequence. The `transactions` capability is part of the ClientContract hash, and a
|
|
446
477
|
generated client fails hard on a hash mismatch. So one schema, one framework configuration and two
|
|
@@ -597,6 +628,23 @@ Resource or operation for one to wrap.
|
|
|
597
628
|
- `limit`/`offset`/`cursor` are the public names. Prisma's `take`/`skip` never surface.
|
|
598
629
|
- Decimal values are converted in both directions. Prisma's bundled decimal object is not an Aventara
|
|
599
630
|
public type and does not reach a caller as one.
|
|
631
|
+
- Values and filter operands reach Prisma as given: a `contains`, `startsWith` or `endsWith` operand is the
|
|
632
|
+
engine's `LIKE` pattern (`%` and `_` included), and a column stores, rounds or widens a value as the engine does.
|
|
633
|
+
A value PostgreSQL refuses (too long for its column, out of its range, U+0000 in text, a `LIKE` pattern ending in
|
|
634
|
+
`\`) answers `A2004` with one issue at `["arguments"]` (`V1004` or `V1001`), not `A3001`.
|
|
635
|
+
- On D1, a `BigInt` value must lie within ±(2^53 − 1) (±9,007,199,254,740,991), not the full signed 64-bit
|
|
636
|
+
range: the D1 driver binds a bigint as a JavaScript number and refuses one beyond that before the
|
|
637
|
+
statement runs. A value beyond it, written, compared in a filter or used in an `$increment`, answers
|
|
638
|
+
`A2004` with one `V1004` issue at the value (`["arguments", "data", "views"]`), and the statement that
|
|
639
|
+
carries it writes nothing. A `ctx.$sql` statement binding such a value answers `A3001`: its SQL is
|
|
640
|
+
yours.
|
|
641
|
+
- A record written through a relation cannot write that relation, or its reference fields, back: Prisma's nested
|
|
642
|
+
input for it leaves them out (a post created in `author.posts` has no `author` or `authorId`; a category created in
|
|
643
|
+
a post's `category` has no `posts`). The artifact states each relation's other end (`inverse`, paired by relation
|
|
644
|
+
name), the fields that hold its reference (`referenceFields`, from `@relation(fields: …)`) and no relation
|
|
645
|
+
writable at the `"loopback"` position, so `@aventara/core` refuses such a write before any
|
|
646
|
+
guard or SQL, `A2004` with a `V1012` issue at the field written back, and the types do not offer it. Where a layer
|
|
647
|
+
hides the other end, Prisma's client refuses the write instead and the adapter answers that refusal the same way.
|
|
600
648
|
- Count variants return numbers, including zero. `create.many` and `update.many` return arrays.
|
|
601
649
|
- `find.unique`, `update.unique` and `delete.unique` answer `A2003 NOT_FOUND` on a missing target;
|
|
602
650
|
`find.first`, `update.first` and `delete.first` answer `A1001 NO_MATCH` with `null` data. The
|
|
@@ -9,8 +9,12 @@ export declare class AvPrismaDiscoveryArtifactError extends Error {
|
|
|
9
9
|
}
|
|
10
10
|
export type PrismaDiscoveryArtifactLoad = {
|
|
11
11
|
readonly artifact: unknown;
|
|
12
|
-
/**
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The `version` of the `@prisma/client` actually installed beside the caller;
|
|
14
|
+
* `undefined` when it could not be read, and the majors are then not compared
|
|
15
|
+
* (the adapter says so at startup).
|
|
16
|
+
*/
|
|
17
|
+
readonly installedPrismaVersion: string | undefined;
|
|
14
18
|
};
|
|
15
19
|
/**
|
|
16
20
|
* The identity of a discovered model: one canonicalization, one digest — over
|
|
@@ -49,6 +49,9 @@ function assertArtifactShape(artifact) {
|
|
|
49
49
|
throw new AvPrismaDiscoveryArtifactError(`The discovery artifact's \`${key}\` is not a JSON object.`);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
if (artifact.computedFields !== null && !isRecord(artifact.computedFields)) {
|
|
53
|
+
throw new AvPrismaDiscoveryArtifactError("The discovery artifact's `computedFields` is neither a JSON object nor null.");
|
|
54
|
+
}
|
|
52
55
|
for (const key of ["scalars", "enums", "resources"]) {
|
|
53
56
|
if (!isRecord(artifact.model[key])) {
|
|
54
57
|
throw new AvPrismaDiscoveryArtifactError(`The discovery artifact's \`model.${key}\` is not a JSON object.`);
|
|
@@ -66,7 +69,9 @@ export async function loadDiscoveryArtifact(load) {
|
|
|
66
69
|
const { artifact, installedPrismaVersion } = load;
|
|
67
70
|
assertArtifactShape(artifact);
|
|
68
71
|
const artifactMajor = majorOf(artifact.prismaVersion);
|
|
69
|
-
const installedMajor =
|
|
72
|
+
const installedMajor = installedPrismaVersion === undefined
|
|
73
|
+
? artifactMajor
|
|
74
|
+
: majorOf(installedPrismaVersion);
|
|
70
75
|
if (artifactMajor === undefined || installedMajor === undefined) {
|
|
71
76
|
throw new AvPrismaDiscoveryArtifactError(`A Prisma version is not a major.minor.patch version: the artifact declares \`${artifact.prismaVersion}\` and the installed client reports \`${installedPrismaVersion}\`.`);
|
|
72
77
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AvRelationFieldCapabilities, AvScalarFieldCapabilities, AvScalarFilterOperator } from "@aventara/core";
|
|
2
|
+
import type { PrismaFilterModeFacts } from "../provider/provider-profile.js";
|
|
2
3
|
import type { PrismaRelationSurface, PrismaScalarSurface } from "./capability-surface.interface.js";
|
|
3
4
|
/** A Prisma spelling that no dictionary table recognises: a build stop, never a drop. */
|
|
4
5
|
export declare class AvPrismaCapabilityDerivationError extends Error {
|
|
@@ -6,6 +7,8 @@ export declare class AvPrismaCapabilityDerivationError extends Error {
|
|
|
6
7
|
}
|
|
7
8
|
export type ScalarCapabilityInput = {
|
|
8
9
|
readonly surface: PrismaScalarSurface;
|
|
10
|
+
/** The `mode`s the engine honours (see this module's header). */
|
|
11
|
+
readonly filterModes: PrismaFilterModeFacts;
|
|
9
12
|
};
|
|
10
13
|
/** A relation's capabilities are answered for the field, not for an operation. */
|
|
11
14
|
export type RelationCapabilityInput = {
|
|
@@ -13,5 +16,5 @@ export type RelationCapabilityInput = {
|
|
|
13
16
|
};
|
|
14
17
|
export declare function deriveScalarCapabilities(input: ScalarCapabilityInput): AvScalarFieldCapabilities;
|
|
15
18
|
/** The Prisma operand for each ADVERTISED operator, keyed by the Aventara name. */
|
|
16
|
-
export declare function deriveScalarFilterOperands(input: ScalarCapabilityInput): Readonly<Partial<Record<AvScalarFilterOperator, string>>>;
|
|
19
|
+
export declare function deriveScalarFilterOperands(input: Pick<ScalarCapabilityInput, "surface">): Readonly<Partial<Record<AvScalarFilterOperator, string>>>;
|
|
17
20
|
export declare function deriveRelationCapabilities(input: RelationCapabilityInput): AvRelationFieldCapabilities;
|
|
@@ -61,18 +61,50 @@ const PAGINATION_ORDER = [
|
|
|
61
61
|
export function deriveScalarCapabilities(input) {
|
|
62
62
|
const { surface } = input;
|
|
63
63
|
const operands = deriveScalarFilterOperands(input);
|
|
64
|
-
const
|
|
64
|
+
const derived = surface.filters
|
|
65
65
|
.map((key) => look(PRISMA_FILTER_OPERATORS, key, "a scalar filter operator"))
|
|
66
|
-
.filter((operator) => translatable(operator, operands))
|
|
66
|
+
.filter((operator) => translatable(operator, operands));
|
|
67
|
+
const modes = filterModes(derived, input.filterModes);
|
|
68
|
+
const filter = canonical(FILTER_OPERATOR_ORDER, [
|
|
69
|
+
...derived,
|
|
70
|
+
...(modes === undefined ? [] : ["mode"]),
|
|
71
|
+
]);
|
|
67
72
|
return {
|
|
68
73
|
select: true,
|
|
69
74
|
...(filter.length > 0 ? { filter } : {}),
|
|
75
|
+
...(modes ?? {}),
|
|
70
76
|
order: surface.orderable,
|
|
71
77
|
...(surface.nullsOrderable ? { orderNulls: true } : {}),
|
|
72
78
|
...directives("create", surface.createOperations),
|
|
73
79
|
...directives("update", surface.updateOperations),
|
|
74
80
|
};
|
|
75
81
|
}
|
|
82
|
+
const TEXT_OPERATORS = [
|
|
83
|
+
"contains",
|
|
84
|
+
"startsWith",
|
|
85
|
+
"endsWith",
|
|
86
|
+
];
|
|
87
|
+
const JSON_TEXT_OPERATORS = [
|
|
88
|
+
"stringContains",
|
|
89
|
+
"stringStartsWith",
|
|
90
|
+
"stringEndsWith",
|
|
91
|
+
];
|
|
92
|
+
function filterModes(operators, facts) {
|
|
93
|
+
const admitted = (modes) => modes.filter((mode) => mode !== "insensitive" || operators.includes("mode"));
|
|
94
|
+
if (operators.some((operator) => JSON_TEXT_OPERATORS.includes(operator))) {
|
|
95
|
+
return {
|
|
96
|
+
filterModes: admitted(facts.jsonDocument),
|
|
97
|
+
...(operators.includes("path")
|
|
98
|
+
? { pathFilterModes: admitted(facts.jsonPath) }
|
|
99
|
+
: {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (operators.includes("mode") ||
|
|
103
|
+
operators.some((operator) => TEXT_OPERATORS.includes(operator))) {
|
|
104
|
+
return { filterModes: admitted(facts.text) };
|
|
105
|
+
}
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
76
108
|
export function deriveScalarFilterOperands(input) {
|
|
77
109
|
const operands = {};
|
|
78
110
|
for (const operator of FILTER_OPERATOR_ORDER) {
|
|
@@ -126,7 +158,10 @@ function reducersIn(reducers, bulk) {
|
|
|
126
158
|
return bulk ? reducers : { reducers, scopes: ["record"] };
|
|
127
159
|
}
|
|
128
160
|
function writableIn(directives, bulk) {
|
|
129
|
-
return
|
|
161
|
+
return {
|
|
162
|
+
directives,
|
|
163
|
+
scopes: bulk ? ["nested", "bulkWrite"] : ["nested"],
|
|
164
|
+
};
|
|
130
165
|
}
|
|
131
166
|
export function deriveRelationCapabilities(input) {
|
|
132
167
|
const { surface } = input;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AvAdapterModel, AvEnumDefinition, AvScalarRegistry, AvTransactionCapability } from "@aventara/core";
|
|
1
|
+
import type { AvAdapterModel, AvConfigComputedFields, AvEnumDefinition, AvScalarRegistry, AvTransactionCapability } from "@aventara/core";
|
|
2
2
|
import type { AvAdapterResource } from "@aventara/core/internal";
|
|
3
3
|
import type { AvPrismaProvider } from "../provider/provider-profile.js";
|
|
4
4
|
/** The adapter model a discovery artifact carries, in Aventara's vocabulary. */
|
|
@@ -73,7 +73,7 @@ export type AvPrismaReferentialActionTable<M extends AvPrismaAdapterModel = AvPr
|
|
|
73
73
|
*/
|
|
74
74
|
export type PrismaClientShape = Readonly<Record<string, readonly string[]>>;
|
|
75
75
|
/** The current discovery artifact format. */
|
|
76
|
-
export declare const DISCOVERY_ARTIFACT_FORMAT =
|
|
76
|
+
export declare const DISCOVERY_ARTIFACT_FORMAT = 4;
|
|
77
77
|
/**
|
|
78
78
|
* The discovery artifact: the module `aventara-prisma7-generate` writes from
|
|
79
79
|
* your schema and generated client. Import its `discovery` export and pass it
|
|
@@ -106,4 +106,12 @@ export type AvPrismaDiscoveryArtifact<M extends AvPrismaAdapterModel = AvPrismaA
|
|
|
106
106
|
* against; not part of `modelHash`.
|
|
107
107
|
*/
|
|
108
108
|
readonly client: PrismaClientShape;
|
|
109
|
+
/**
|
|
110
|
+
* Which fields the configuration's write compute fills, per layer, as the
|
|
111
|
+
* generate step read them off the configuration it was given (`--config`);
|
|
112
|
+
* `null` when it was given none. The model's type carries the same statement
|
|
113
|
+
* for the pipeline stages' types; this value is compared with the
|
|
114
|
+
* configuration when the framework starts. Not part of `modelHash`.
|
|
115
|
+
*/
|
|
116
|
+
readonly computedFields: AvConfigComputedFields | null;
|
|
109
117
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DISCOVERY_ARTIFACT_FORMAT =
|
|
1
|
+
export const DISCOVERY_ARTIFACT_FORMAT = 4;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** What the locator found: the version, or why it has none. */
|
|
2
|
+
export type InstalledPrismaClientVersion = {
|
|
3
|
+
readonly kind: "read";
|
|
4
|
+
readonly version: string;
|
|
5
|
+
} | {
|
|
6
|
+
readonly kind: "unread";
|
|
7
|
+
readonly reason: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Reads the installed `@prisma/client`'s version, resolved as described above.
|
|
11
|
+
* Never throws: a version it cannot read is answered `unread` with the reason.
|
|
12
|
+
*/
|
|
13
|
+
export declare function locateInstalledPrismaClientVersion(): InstalledPrismaClientVersion;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const MANIFEST = "@prisma/client/package.json";
|
|
2
|
+
function resolutionBases(process) {
|
|
3
|
+
const bases = [];
|
|
4
|
+
try {
|
|
5
|
+
if (typeof import.meta.url === "string") {
|
|
6
|
+
bases.push(import.meta.url);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
}
|
|
11
|
+
const cwd = process?.cwd?.();
|
|
12
|
+
if (typeof cwd === "string") {
|
|
13
|
+
bases.push(`${cwd.replace(/[\\/]+$/, "")}/package.json`);
|
|
14
|
+
}
|
|
15
|
+
return bases;
|
|
16
|
+
}
|
|
17
|
+
export function locateInstalledPrismaClientVersion() {
|
|
18
|
+
const process = globalThis.process;
|
|
19
|
+
const moduleApi = process?.getBuiltinModule?.("node:module");
|
|
20
|
+
if (typeof moduleApi?.createRequire !== "function") {
|
|
21
|
+
return {
|
|
22
|
+
kind: "unread",
|
|
23
|
+
reason: "this runtime has no Node module loader to resolve it with",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const tried = [];
|
|
27
|
+
for (const base of resolutionBases(process)) {
|
|
28
|
+
try {
|
|
29
|
+
const manifest = moduleApi.createRequire(base)(MANIFEST);
|
|
30
|
+
if (typeof manifest.version === "string") {
|
|
31
|
+
return { kind: "read", version: manifest.version };
|
|
32
|
+
}
|
|
33
|
+
tried.push(`${base} (its package.json states no version)`);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
tried.push(base);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
kind: "unread",
|
|
41
|
+
reason: `\`${MANIFEST}\` does not resolve from ${tried.length === 0 ? "anywhere this runtime names" : tried.join(" or ")}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AvTransactionCapability } from "@aventara/core";
|
|
2
2
|
import type { PrismaComposedArtifact, PrismaModelComposer } from "../generate/prisma.generate.js";
|
|
3
|
+
import type { PrismaFilterModeFacts } from "../provider/provider-profile.js";
|
|
3
4
|
import type { PrismaCapabilitySurface } from "./capability-surface.interface.js";
|
|
4
5
|
import type { PrismaSchemaFacts } from "./schema-facts.interface.js";
|
|
5
6
|
/** Thrown when the schema cannot be composed into a model; lists every problem. */
|
|
@@ -10,6 +11,8 @@ export declare class AvPrismaModelCompositionError extends Error {
|
|
|
10
11
|
}
|
|
11
12
|
export type PrismaModelCompositionOptions = {
|
|
12
13
|
readonly transactions: AvTransactionCapability;
|
|
14
|
+
/** The `mode`s the engine's comparisons honour (`provider-profile.ts`). */
|
|
15
|
+
readonly filterModes: PrismaFilterModeFacts;
|
|
13
16
|
};
|
|
14
17
|
export type PrismaModelDiscoveryInput = PrismaModelCompositionOptions & {
|
|
15
18
|
/** PSL — what the schema says. */
|
|
@@ -42,8 +42,10 @@ export function discoverPrismaModel(input) {
|
|
|
42
42
|
modelNames,
|
|
43
43
|
admitted,
|
|
44
44
|
transactions: input.transactions,
|
|
45
|
+
filterModes: input.filterModes,
|
|
45
46
|
diagnostics,
|
|
46
47
|
usedScalars,
|
|
48
|
+
declared,
|
|
47
49
|
});
|
|
48
50
|
resources[name] = composed.resource;
|
|
49
51
|
translation[name] = composed.translation;
|
|
@@ -215,7 +217,10 @@ function composeResource(input) {
|
|
|
215
217
|
}
|
|
216
218
|
fields[projection.name] = {
|
|
217
219
|
...projection.field,
|
|
218
|
-
capabilities: deriveScalarCapabilities({
|
|
220
|
+
capabilities: deriveScalarCapabilities({
|
|
221
|
+
surface: scalarSurface,
|
|
222
|
+
filterModes: input.filterModes,
|
|
223
|
+
}),
|
|
219
224
|
};
|
|
220
225
|
projectedScalars.push({
|
|
221
226
|
name: projection.name,
|
|
@@ -235,8 +240,15 @@ function composeResource(input) {
|
|
|
235
240
|
diagnostics.push(`\`${model.name}.${field.name}\` targets \`${projection.field.target}\`, which the existence gate did not admit, so the edge would point at a Resource the model does not carry.`);
|
|
236
241
|
continue;
|
|
237
242
|
}
|
|
243
|
+
const inverse = inverseOf(input.declared, model.name, field);
|
|
244
|
+
const [firstReference, ...moreReferences] = field.relation?.fields ?? [];
|
|
245
|
+
const referenceFields = firstReference === undefined
|
|
246
|
+
? undefined
|
|
247
|
+
: [firstReference, ...moreReferences];
|
|
238
248
|
fields[projection.name] = {
|
|
239
249
|
...projection.field,
|
|
250
|
+
...(inverse === undefined ? {} : { inverse: inverse.relation }),
|
|
251
|
+
...(referenceFields === undefined ? {} : { referenceFields }),
|
|
240
252
|
capabilities: deriveRelationCapabilities({
|
|
241
253
|
surface: relationSurface,
|
|
242
254
|
}),
|
|
@@ -247,7 +259,7 @@ function composeResource(input) {
|
|
|
247
259
|
field: projection.edge.field,
|
|
248
260
|
target: projection.edge.target,
|
|
249
261
|
cardinality: projection.edge.cardinality,
|
|
250
|
-
references: [...(
|
|
262
|
+
references: [...(referenceFields ?? [])],
|
|
251
263
|
onDelete: projection.edge.onDelete,
|
|
252
264
|
onUpdate: projection.edge.onUpdate,
|
|
253
265
|
});
|