@aventara/prisma7-adapter 0.1.0-pilot.4 → 1.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,190 @@
1
+ # @aventara/prisma7-adapter
2
+
3
+ ## 1.0.0-rc.0
4
+
5
+ ### Major Changes
6
+
7
+ - First release candidate of Aventara 1.0. The public API is now considered stable; changes before 1.0.0 are possible
8
+ based on feedback.
9
+
10
+ ### Minor Changes
11
+
12
+ - 24ffbab: A nested `$update` or `$delete` through a to-many relation filters plain fields only on Prisma 7, and Aventara now says
13
+ so up front: a relation filter in its `where` — `{ posts: { $update: { where: { category: { name: "x" } }, … } } }`, at
14
+ any depth, under `AND`/`OR`/`NOT` too — is refused with `A2004` / `V1006` before the database is asked, and is a type
15
+ error in `framework.application`, `framework.client`, `appTx`/`clientTx` and the generated client. Before, it reached
16
+ Prisma and fail as `A3001`. A to-one relation's nested filter keeps relation filters. The limit is stated by the adapter
17
+ in the Contract (`nestedWriteFilter: "scalar"`), so the ClientContract hash changes: regenerate the client.
18
+
19
+ ### Patch Changes
20
+
21
+ - 24ffbab: Each package ships its `CHANGELOG.md`, so the release notes are in the installed package beside the README.
22
+ - a973e89: A write that sets an optional relation both ways — its reference field and the relation itself, such as `categoryId`
23
+ and `category: { $connect: … }` in the same record — is now refused before it reaches the database, exactly like a
24
+ required relation: `A2004` with a `V1012` issue at the relation, message `Set "categoryId" or "category", not both.`
25
+ Before, the database refused it and the caller received `A3001`. It applies to `create`, `update` and `upsert`, to
26
+ nested writes at any depth, and to each step of a transaction.
27
+
28
+ The discovery artifact now records, for each relation, the fields that hold its reference (`references` on each edge
29
+ of `referentialActions`), and a create requirement no longer repeats them. Regenerate your artifact with
30
+ `npm run aventara:prepare` (or `aventara-prisma7-generate`); an artifact from an earlier version is refused with a
31
+ message that says so. An adapter's `referentialActions` edges carry the same `references` member.
32
+
33
+ - a973e89: With the Prisma adapter, a record that writes one relation through its reference field and another relation directly —
34
+ such as `authorId` beside `category: { $connect: … }` — is now refused before it reaches the database: `A2004` with a
35
+ `V1012` issue at the relation write, message
36
+ `Use relation fields only, or reference fields only, in one record: "authorId" is a reference field and "category" is a relation.`
37
+ Before, Prisma refused it and the caller received `A3001`. Write the record's relations all as relations
38
+ (`author: { $connect: … }, category: { $connect: … }`) or all through their fields (`authorId`, `categoryId`). A
39
+ reference field beside a relation that holds no reference on that side — `mentorId` beside `posts: { $create: … }` on
40
+ an author — is unchanged and still accepted. It applies to `create`, `update` and `upsert`, to nested writes at any
41
+ depth, and to each step of a transaction.
42
+
43
+ The limit is part of the Contract: a Contract whose data source has it carries `referenceWrites: "uniform"`, so the
44
+ Contract hash of every Prisma-backed application changes. Regenerate your discovery artifact
45
+ (`npm run aventara:prepare`) and your generated client (`avclient generate`). An adapter states the limit with
46
+ `referenceWrites: "uniform"` in its model; without it, nothing changes.
47
+
48
+ - Updated dependencies [24ffbab]
49
+ - Updated dependencies [24ffbab]
50
+ - Updated dependencies [24ffbab]
51
+ - Updated dependencies [24ffbab]
52
+ - Updated dependencies [24ffbab]
53
+ - Updated dependencies [24ffbab]
54
+ - Updated dependencies [24ffbab]
55
+ - Updated dependencies [24ffbab]
56
+ - Updated dependencies [24ffbab]
57
+ - Updated dependencies [24ffbab]
58
+ - Updated dependencies [24ffbab]
59
+ - Updated dependencies [24ffbab]
60
+ - Updated dependencies [a973e89]
61
+ - Updated dependencies [1e65e9a]
62
+ - Updated dependencies
63
+ - Updated dependencies [a973e89]
64
+ - Updated dependencies [a973e89]
65
+ - Updated dependencies [1e65e9a]
66
+ - Updated dependencies [1e65e9a]
67
+ - Updated dependencies [24ffbab]
68
+ - Updated dependencies [2d00ad7]
69
+ - Updated dependencies [384a50c]
70
+ - Updated dependencies [d43d1cc]
71
+ - Updated dependencies [3f98749]
72
+ - Updated dependencies [032f792]
73
+ - Updated dependencies [dbe6961]
74
+ - @aventara/core@1.0.0-rc.0
75
+
76
+ ## 0.1.0-pilot.4
77
+
78
+ ### Patch Changes
79
+
80
+ - f37dcd6: A discovery artifact written by an older version of the adapter now fails the TypeScript build with an error that
81
+ says what to do: `Property 'regenerate_with_aventara_prepare' is missing`. Run `npm run aventara:prepare`
82
+ (or `aventara-prisma7-generate`) to regenerate it. Every newly generated artifact carries the property, and
83
+ `createPrismaAdapter` refuses an out-of-date artifact at startup with the same instruction.
84
+ - f37dcd6: The documentation shipped with the package — the comments an editor shows on hover, and the README — now describes
85
+ what each export does, how to use it, what it returns and what it throws. Notes about how the package was built and
86
+ why are no longer part of what is published.
87
+
88
+ The generate step's error messages name "the generated client's types" in words instead of Prisma's emoji labels.
89
+
90
+ - Updated dependencies [f37dcd6]
91
+ - @aventara/core@0.1.0-pilot.4
92
+
93
+ ## 0.1.0-pilot.3
94
+
95
+ ### Patch Changes
96
+
97
+ - The manifest declares the adapter's CLI alias, `aventara.adapter.alias: "prisma7"`: the value `aventara new --orm` and `aventara init --orm` take.
98
+ - @aventara/core@0.1.0-pilot.3
99
+
100
+ ## 0.1.0-pilot.2
101
+
102
+ ### Patch Changes
103
+
104
+ - e69e88d: The published packages are written for the people who use them. The JavaScript ships without comments; the type
105
+ declarations keep their documentation, and it, the error messages, the protocol fixtures' labels and the comments in a
106
+ generated client no longer carry the project's internal development notes.
107
+ - 06ed5d8: A relation write that would leave a child without the parent its required relation demands — `$set: []` or
108
+ `$disconnect` on a to-many whose children require the parent — now answers `A2008` (the same conflict a violated
109
+ reference constraint answers) instead of `A3001`. Nothing is written.
110
+ - 23937f8: A create that lacks a value the database requires is refused as `A2004` with a `V1000` issue at the value's path,
111
+ instead of reaching the database and answering `A3001`. It covers `create.one`, every row of `create.many`, `upsert`'s
112
+ `create` branch, nested `$create` (and `$connectOrCreate`/`$upsert` creates) at any depth, and transaction steps
113
+ (`["operations", i, "args", …]`). A required relation is satisfied by writing it or every scalar that holds its
114
+ reference; a nested create never needs the relation it is created through. Defaults, `@updatedAt`, autoincrement,
115
+ optional and list columns are never required. The check runs after pipes and computed behaviours, so a value they
116
+ supply counts.
117
+
118
+ An adapter states the requirements through the new optional `Adapter.createRequirements`; the Prisma 7 adapter reads
119
+ them from the generated client's create inputs at generate time and records them in the discovery artifact, so the
120
+ artifact must be regenerated (`aventara:prepare`).
121
+
122
+ - 287fc27: A discovery artifact older than the Prisma client is refused at startup. The artifact now records the client it was
123
+ generated from (each model and its field references), and `createPrismaAdapter` — which runs before the application
124
+ listens — compares that record with the client it is given: a model or field added, removed or renamed since the
125
+ artifact was generated stops the server with one sentence naming the difference and `aventara:prepare`. An artifact
126
+ generated by an earlier version carries no record and is refused with the same instruction; a project set up by
127
+ `aventara` regenerates it on install.
128
+ - Updated dependencies [c2a3b7c]
129
+ - Updated dependencies [e69e88d]
130
+ - Updated dependencies [ab58424]
131
+ - Updated dependencies [23937f8]
132
+ - @aventara/core@0.1.0-pilot.2
133
+
134
+ ## 0.1.0-pilot.1
135
+
136
+ ### Patch Changes
137
+
138
+ - 799346e: The package READMEs (each package's npm page) are rewritten for the people who use the packages. The Prisma 7
139
+ adapter's README now states the actual behaviour of a to-many relation or `$count` projected on
140
+ `update.many`/`create.many`: a validation refusal, `A2004` with `V1008`, before any statement runs (it said `A3001`).
141
+ Every install or run command names the `@pilot` dist-tag.
142
+ - Updated dependencies [a45f8a8]
143
+ - Updated dependencies [799346e]
144
+ - @aventara/core@0.1.0-pilot.1
145
+
146
+ ## 0.1.0-pilot.0
147
+
148
+ ### Minor Changes
149
+
150
+ - First pilot release of `@aventara/prisma7-adapter`, the Prisma 7 adapter. Peers: `@prisma/client`, `prisma` and the
151
+ driver adapters `^7.10.0`; `typescript` `>=5.5.0 <7` for the generate step. `typescript` and `prisma` are optional
152
+ peers used only by the generate step, so neither enters a deployed install.
153
+
154
+ - **`createPrismaAdapter({ client, discovery, provider, driver, transactionOptions? })`** is the entry point. Your
155
+ application owns the Prisma client, built with a driver adapter; the adapter never builds one, reads a connection
156
+ string or reads an environment variable. `provider` and `driver` are stated by you, and the driver decides whether
157
+ transactions are offered. It is `async` because it verifies the discovery artifact.
158
+ - **Discovery runs at generate time.** `aventara-prisma7-generate` (or the `@aventara/prisma7-adapter/generate`
159
+ export) reads `schema.prisma` and the generated client's types and writes one module, the discovery artifact: the
160
+ model, how each Resource is addressed, its referential actions, a `modelHash` and the Prisma version. At startup the
161
+ adapter checks that the artifact matches its `modelHash` and that the installed `@prisma/client` major is the one it
162
+ was generated against, and stops with an instruction to regenerate otherwise. The artifact is rewritten only when its
163
+ bytes change, and replaced atomically, so a reader never sees a half-written file.
164
+ - **The Contract says what Prisma serves.** `createMany`/`updateMany` results project fewer relations than a read
165
+ (the rule is which side holds the foreign key, not cardinality); the Contract advertises that per argument position,
166
+ and an out-of-position projection or nested write is refused with `A2004` (`V1008` / `V1006`) before any query.
167
+ - **Cascading deletes.** The schema's referential actions are stated to the Framework, so a transaction step that
168
+ depends on rows an earlier step's cascading delete removed is refused before the plan runs (`A2004`/`V1019`).
169
+ - **Conflicts are told apart:** `A2008 CONFLICT` for a uniqueness or reference constraint, `A2013
170
+ CONCURRENT_MODIFICATION` when another operation changed the same state, `A2014 STALE_SELECTION` when the selected
171
+ record changed before the write.
172
+ - **Behaviours to know:**
173
+ - `delete.many` is not offered (Prisma 7 has no `deleteManyAndReturn`); use `delete.count`.
174
+ - `update.first` and `delete.first` are a read followed by a write narrowed to the record the read selected, and are
175
+ not atomic: the record written is one the read selected, or nothing is written and you are told (`A2014`). A
176
+ record that comes to sort first between the two statements is not seen. If firstness at the instant of the write
177
+ matters, select the record yourself and use `update.unique`/`delete.unique`.
178
+ - A serialization failure (`P2034`) is never retried automatically: a retry would re-run your pipeline callbacks.
179
+ Retrying is yours to decide.
180
+ - Whether transactions are offered depends on the driver and is part of the ClientContract hash, so generate the
181
+ client against the deployment's own driver.
182
+ - No raw SQL is ever issued.
183
+ - PostgreSQL and SQLite are supported.
184
+ - Every export has a `default` condition, so a CommonJS project can `require()` it. The manifest declares what the
185
+ adapter supports (providers, drivers, generators), which `@aventara/cli` reads.
186
+
187
+ ### Patch Changes
188
+
189
+ - Updated dependencies
190
+ - @aventara/core@0.1.0-pilot.0
package/README.md CHANGED
@@ -9,7 +9,7 @@ does, what it deliberately does **not** offer, and the two places where a correc
9
9
  has a weaker guarantee than its name suggests. Everything a consumer has to know is in the body of
10
10
  this file; nothing load-bearing is in a footnote.
11
11
 
12
- - **Status:** pilot. Two engines are proven — PostgreSQL and SQLite. Every other Prisma provider is
12
+ - **Status:** release candidate. Two engines are proven — PostgreSQL and SQLite. Every other Prisma provider is
13
13
  unmeasured, and this file says so where it matters.
14
14
  - **Peer dependencies:** `@prisma/client` `^7.10.0` (required); `prisma`, `@prisma/adapter-better-sqlite3`
15
15
  and `@prisma/adapter-pg` `^7.10.0` and `typescript` `>=5.5.0 <7` (optional). 7.10.0 is the version the
@@ -127,14 +127,15 @@ project's `devDependencies` — if you are generating a Prisma client at all, yo
127
127
  The step reads two sources in the same instant — the `schema.prisma` text and the generated client's
128
128
  own public types — and cross-checks them against each other. What it emits is a TypeScript module
129
129
  exporting one `discovery` value with eight members: the canonical `AdapterModel` (scalars, enums,
130
- Resources with fields, identifiers and available operations, plus the `transactions` capability), the
130
+ Resources with fields, identifiers and available operations, plus the `transactions` capability and the
131
+ `referenceWrites` limit), the
131
132
  Prisma addressing each Resource is reached by, the resolved referential actions per Resource, a record
132
133
  of the generated client it was read from (each model and its field references), the create
133
134
  requirements per Resource, a `modelHash`, and the `prismaVersion` it was generated against.
134
135
 
135
136
  The create requirements are what a create cannot do without — a column with no default, or a required
136
- relation, which may be written instead through the scalar fields that hold its reference — read off the
137
- question marks of the generated create inputs. The framework checks every create against them after
137
+ relation, which may be written instead through the scalar fields that hold its reference (the edge's
138
+ `references`, below) — read off the question marks of the generated create inputs. The framework checks every create against them after
138
139
  your pipes and computed behaviours have run, and answers a missing value as `A2004` with a `V1000`
139
140
  issue at its path (`["arguments", "data", "title"]`, or `["arguments", "data", "author"]` for a
140
141
  required relation) instead of letting the database refuse it. A nested create does not need the
@@ -477,10 +478,10 @@ discovery.referentialActions;
477
478
  // {
478
479
  // Shop: { owningEdges: [] },
479
480
  // Item: { owningEdges: [{ field: "shop", target: "Shop",
480
- // cardinality: "one",
481
+ // cardinality: "one", references: ["shopId"],
481
482
  // onDelete: "cascade", onUpdate: "cascade" }] },
482
483
  // Clerk: { owningEdges: [{ field: "shop", target: "Shop",
483
- // cardinality: "one",
484
+ // cardinality: "one", references: ["shopId"],
484
485
  // onDelete: "set-null", onUpdate: "cascade" }] },
485
486
  // }
486
487
  ```
@@ -492,6 +493,20 @@ To answer *"what does deleting a `Shop` do?"*, scan every Resource's `owningEdge
492
493
  (set-null). Both ends of that sentence are Resource names and nothing in the shape tells them apart, so
493
494
  the inverted reading produces no error at all, only a plan that reasons backwards.
494
495
 
496
+ `references` are the edge's `@relation(fields: […])` — the scalars that hold the reference. A record
497
+ that writes a relation and one of its reference fields both (`shopId` and `shop: { $connect: … }`,
498
+ required relation or optional) is refused before the database is reached: `A2004` with a `V1012` issue
499
+ at the relation, message `Set "shopId" or "shop", not both.` They are also what a required relation may
500
+ be written through instead of the relation itself.
501
+
502
+ Prisma takes one record's relations either all as relation writes or all through their reference
503
+ fields — its checked and unchecked inputs — so every artifact's model states `referenceWrites: "uniform"`,
504
+ and every Contract compiled from it carries the same member. A record that writes `shopId` beside
505
+ another relation's write (`supplier: { $connect: … }`) is refused before the database is reached:
506
+ `A2004` with a `V1012` issue at the relation write. A reference field beside a back-reference (a
507
+ relation that holds no reference on that side, such as a shop's `items`) mixes nothing and is
508
+ accepted.
509
+
495
510
  Only **owning** edges appear: PSL forbids the non-owning side of a relation from declaring an action,
496
511
  so a back-reference's answer is its partner's and carries no entry of its own. Both actions are
497
512
  *resolved* — an absent PSL argument is not a third state but Prisma's effective default (`set-null`
@@ -499,7 +514,7 @@ where every foreign-key scalar is optional, `restrict` otherwise, `cascade` on u
499
514
  PostgreSQL and SQLite. An action spelling the reader cannot name stops the generate step rather than reaching
500
515
  the artifact.
501
516
 
502
- Neither the model nor the compiled Contracts carry a referential action — the actions are beside the
517
+ Neither the model nor the compiled Contracts carry a referential action or a reference field — the edges are beside the
503
518
  model and outside `modelHash`, so they move no Contract value and no hash. `createPrismaAdapter` states
504
519
  them to core through the adapter's optional `referentialActions` member, translated one to one into core's
505
520
  five words (mirroring Prisma's and SQL's actions; direction unchanged):
@@ -1,4 +1,4 @@
1
- import { canonicalizeJson } from "@aventara/core";
1
+ import { canonicalizeJson } from "@aventara/core/internal";
2
2
  import { DISCOVERY_ARTIFACT_FORMAT, } from "./discovery-artifact.interface.js";
3
3
  const HASH_PREFIX = "sha256:";
4
4
  const REGENERATION_INSTRUCTION = "Regenerate the discovery artifact — `npm run aventara:prepare` in a project " +
@@ -63,6 +63,9 @@ function assertArtifactShape(artifact) {
63
63
  artifact.model.transactions !== "interactive") {
64
64
  throw new PrismaDiscoveryArtifactError("The discovery artifact's `model.transactions` is not a transaction capability.");
65
65
  }
66
+ if (artifact.model.referenceWrites !== "uniform") {
67
+ throw new PrismaDiscoveryArtifactError("The discovery artifact's `model.referenceWrites` is not `uniform`, which every Prisma model is.");
68
+ }
66
69
  }
67
70
  export async function loadDiscoveryArtifact(load) {
68
71
  const { artifact, installedPrismaVersion } = load;
@@ -76,6 +76,13 @@ export type PrismaRelationSurface = {
76
76
  */
77
77
  readonly createRequired: boolean;
78
78
  readonly updateMutations: readonly string[];
79
+ /**
80
+ * Whether the update stage's `deleteMany` takes a `<T>ScalarWhereInput` — a filter of
81
+ * the target's scalar fields only, which `updateMany`'s `where` shares. Read off the
82
+ * nested input's own member type; `false` where the input has no `deleteMany` (a
83
+ * to-one edge, whose `update`/`delete` take a full `<T>WhereInput`).
84
+ */
85
+ readonly scalarNestedWriteFilter: boolean;
79
86
  readonly edgeArguments: readonly string[];
80
87
  readonly pagination: readonly string[];
81
88
  /** The edge is admitted by `<M>OrderByWithRelationInput`. */
@@ -157,6 +157,9 @@ export function deriveRelationCapabilities(input) {
157
157
  : {}),
158
158
  create: writableIn(directives(surface.createMutations), surface.bulkCreatable),
159
159
  update: writableIn(directives(surface.updateMutations), surface.bulkUpdatable),
160
+ ...(surface.scalarNestedWriteFilter
161
+ ? { nestedWriteFilter: "scalar" }
162
+ : {}),
160
163
  };
161
164
  }
162
165
  function look(table, key, what) {
@@ -1,4 +1,5 @@
1
- import type { Adapter, AdapterResource, EnumDefinition, ScalarRegistry, TransactionCapability } from "@aventara/core";
1
+ import type { Adapter, AdapterModel, EnumDefinition, ScalarRegistry, TransactionCapability } from "@aventara/core";
2
+ import type { AdapterResource } from "@aventara/core/internal";
2
3
  import type { PrismaProvider } from "../provider/provider-profile.js";
3
4
  /** The adapter model a discovery artifact carries, in Aventara's vocabulary. */
4
5
  export type PrismaAdapterModel = {
@@ -10,6 +11,11 @@ export type PrismaAdapterModel = {
10
11
  readonly resources: Readonly<Record<string, AdapterResource<PrismaAdapterModel>>>;
11
12
  /** Whether the driver supports interactive transactions. */
12
13
  readonly transactions: TransactionCapability;
14
+ /**
15
+ * Prisma takes one record's references one way at a time (its checked and
16
+ * unchecked write inputs), so every artifact states `"uniform"`.
17
+ */
18
+ readonly referenceWrites: NonNullable<AdapterModel["referenceWrites"]>;
13
19
  };
14
20
  /** One identifier, and the **Prisma addressing key** that reaches it. */
15
21
  export type PrismaIdentifierAddress = {
@@ -34,6 +40,11 @@ export type PrismaReferentialEdge = {
34
40
  readonly target: string;
35
41
  /** Whether the edge holds one related row or many. */
36
42
  readonly cardinality: "one" | "many";
43
+ /**
44
+ * The scalar fields of the ENCLOSING resource that hold the reference — the
45
+ * relation's `@relation(fields: […])`, in schema order.
46
+ */
47
+ readonly references: readonly string[];
37
48
  /** What deleting a `target` row does to rows of the ENCLOSING resource. */
38
49
  readonly onDelete: PrismaReferentialAction;
39
50
  /** The same for an update of the referenced key. Read independently of `onDelete`. */
@@ -64,9 +75,10 @@ export type PrismaClientShape = Readonly<Record<string, readonly string[]>>;
64
75
  /**
65
76
  * What a create of each Resource cannot do without, already in core's words
66
77
  * (`Adapter["createRequirements"]`): a column the client must write, or a
67
- * required relation that may instead be written through the scalar fields that
68
- * hold its reference — and the relation's other end, through which a nested
69
- * create supplies it.
78
+ * required relation — which may instead be written through the scalar fields
79
+ * that hold its reference, read off the relation's referential edge
80
+ * (`references`), never restated here — and the relation's other end, through
81
+ * which a nested create supplies it.
70
82
  *
71
83
  * Read at generate time from the generated client's create inputs — the
72
84
  * question marks on `<M>UncheckedCreateInput` and `<M>CreateInput` — and from the
@@ -76,7 +88,7 @@ export type PrismaClientShape = Readonly<Record<string, readonly string[]>>;
76
88
  */
77
89
  export type PrismaCreateRequirementTable = NonNullable<Adapter["createRequirements"]>;
78
90
  /** The current discovery artifact format. */
79
- export declare const DISCOVERY_ARTIFACT_FORMAT = 1;
91
+ export declare const DISCOVERY_ARTIFACT_FORMAT = 2;
80
92
  /**
81
93
  * The discovery artifact: the module `aventara-prisma7-generate` writes from
82
94
  * your schema and generated client. Import its `discovery` export and pass it
@@ -1 +1 @@
1
- export const DISCOVERY_ARTIFACT_FORMAT = 1;
1
+ export const DISCOVERY_ARTIFACT_FORMAT = 2;
@@ -1,4 +1,4 @@
1
- import { type AdapterLifecycleTrait, type AdapterRelationField, type AdapterScalarField } from "@aventara/core";
1
+ import { type AdapterLifecycleTrait, type AdapterRelationField, type AdapterScalarField } from "@aventara/core/internal";
2
2
  import type { PrismaResourceSurface } from "./capability-surface.interface.js";
3
3
  import type { PrismaAdapterModel, PrismaReferentialAction } from "./discovery-artifact.interface.js";
4
4
  import type { PrismaSchemaField, PrismaSchemaUnknown } from "./schema-facts.interface.js";
@@ -1,4 +1,4 @@
1
- import { LIFECYCLE_TRAITS, } from "@aventara/core";
1
+ import { LIFECYCLE_TRAITS, } from "@aventara/core/internal";
2
2
  import { projectEnumBackedColumn } from "./enum.mapper.js";
3
3
  import { mapPrismaScalarField } from "./scalar.mapper.js";
4
4
  import { PRISMA_DEFAULT_TABLE, PRISMA_UNMATCHED_DEFAULT_TRAITS, } from "./vocabulary.dictionary.js";
@@ -1,4 +1,4 @@
1
- import { type TransactionCapability } from "@aventara/core";
1
+ import type { TransactionCapability } from "@aventara/core";
2
2
  import type { PrismaComposedArtifact, PrismaModelComposer } from "../generate/prisma.generate.js";
3
3
  import type { PrismaCapabilitySurface } from "./capability-surface.interface.js";
4
4
  import type { PrismaSchemaFacts } from "./schema-facts.interface.js";
@@ -1,4 +1,4 @@
1
- import { BUILT_IN_SCALARS, } from "@aventara/core";
1
+ import { BUILT_IN_SCALARS, } from "@aventara/core/internal";
2
2
  import { deriveRelationCapabilities, deriveScalarCapabilities, } from "./capability.deriver.js";
3
3
  import { mapPrismaEnum } from "./enum.mapper.js";
4
4
  import { crossCheckCreateOptional, projectPrismaField, } from "./field.mapper.js";
@@ -58,12 +58,14 @@ export function discoverPrismaModel(input) {
58
58
  enums,
59
59
  resources,
60
60
  transactions: input.transactions,
61
+ referenceWrites: PRISMA_REFERENCE_WRITES,
61
62
  },
62
63
  translation,
63
64
  referentialActions,
64
65
  createRequirements: composeCreateRequirements(declared, input.surface, resources),
65
66
  };
66
67
  }
68
+ const PRISMA_REFERENCE_WRITES = "uniform";
67
69
  function composeCreateRequirements(declared, surface, resources) {
68
70
  const table = {};
69
71
  for (const [name, resource] of Object.entries(resources)) {
@@ -95,15 +97,13 @@ function composeCreateRequirements(declared, surface, resources) {
95
97
  if (exposed.relationFields[field.name]?.createRequired !== true) {
96
98
  continue;
97
99
  }
98
- const orFields = owned.filter((scalar) => requiredScalars.has(scalar));
99
- if (orFields.length === 0) {
100
+ if (!owned.some((scalar) => requiredScalars.has(scalar))) {
100
101
  continue;
101
102
  }
102
103
  const inverse = inverseOf(declared, name, field);
103
104
  requirements.push({
104
105
  kind: "relation",
105
106
  relation: field.name,
106
- orFields,
107
107
  ...(inverse === undefined ? {} : { inverse }),
108
108
  });
109
109
  }
@@ -217,6 +217,7 @@ function composeResource(input) {
217
217
  field: projection.edge.field,
218
218
  target: projection.edge.target,
219
219
  cardinality: projection.edge.cardinality,
220
+ references: [...(field.relation?.fields ?? [])],
220
221
  onDelete: projection.edge.onDelete,
221
222
  onUpdate: projection.edge.onUpdate,
222
223
  });
@@ -1,4 +1,5 @@
1
- import type { AdapterResource, ResourceOperations, TransactionCapability } from "@aventara/core";
1
+ import type { ResourceOperations, TransactionCapability } from "@aventara/core";
2
+ import type { AdapterResource } from "@aventara/core/internal";
2
3
  import type { PrismaAdapterModel } from "./discovery-artifact.interface.js";
3
4
  type DiscoveredIdentifiers = AdapterResource<PrismaAdapterModel>["identifiers"];
4
5
  export type ResourceOperationsInput = {
@@ -201,6 +201,16 @@ function keysOfReferenced(index, node, accept) {
201
201
  }
202
202
  return memberNames(members);
203
203
  }
204
+ function deleteManyTakesScalarWhere(index, node) {
205
+ const input = findReference(node, (n) => n.endsWith("NestedInput"));
206
+ if (input === null) {
207
+ return false;
208
+ }
209
+ const deleteMany = (membersOf(index, referencedName(input)) ?? []).find((member) => memberName(member) === "deleteMany");
210
+ return (deleteMany !== undefined &&
211
+ findReference(deleteMany.type, (n) => n.endsWith("ScalarWhereInput")) !==
212
+ null);
213
+ }
204
214
  function readEnums(program, root) {
205
215
  const source = program.getSourceFile(path.join(root, "enums.ts"));
206
216
  if (source === undefined) {
@@ -386,6 +396,7 @@ function readRelationFields(checker, index, payload, where, create, update, incl
386
396
  createRequired: create.get(name) !== undefined &&
387
397
  create.get(name)?.questionToken === undefined,
388
398
  updateMutations: keysOfReferenced(index, update.get(name)?.type, (n) => n.endsWith("NestedInput")),
399
+ scalarNestedWriteFilter: deleteManyTakesScalarWhere(index, update.get(name)?.type),
389
400
  edgeArguments,
390
401
  pagination: edgeArguments.filter((argument) => !NON_PAGINATION_EDGE_ARGUMENTS.has(argument)),
391
402
  orderable: orderBy.has(name),
@@ -1,4 +1,5 @@
1
- import type { AdapterLifecycleTrait, BuiltInScalar, PaginationCapability, RelationFilterCapability, RelationMutationDirective, ScalarFilterOperator, ScalarFormat, ScalarMutationDirective } from "@aventara/core";
1
+ import type { BuiltInScalar, PaginationCapability, RelationFilterCapability, RelationMutationDirective, ScalarFilterOperator, ScalarFormat, ScalarMutationDirective } from "@aventara/core";
2
+ import type { AdapterLifecycleTrait } from "@aventara/core/internal";
2
3
  export type VocabularySource = "PSL" | "TYPE" | "RUN" | "DRIVER" | "CFG";
3
4
  export type VocabularyEntry = {
4
5
  readonly fact: string;
@@ -1,4 +1,5 @@
1
- import type { AdapterModel, AdapterOperationExecutor } from "@aventara/core";
1
+ import type { AdapterModel } from "@aventara/core";
2
+ import type { AdapterOperationExecutor } from "@aventara/core/internal";
2
3
  import type { PrismaResourceTranslation } from "../discovery/discovery-artifact.interface.js";
3
4
  import { type PrismaDelegateHost, type PrismaTransactionOptions } from "../prisma-client.interface.js";
4
5
  /** A call the executor cannot issue against the client it was given. */
@@ -1,4 +1,5 @@
1
- import type { AdapterModel, AdapterOperationExecutor } from "@aventara/core";
1
+ import type { AdapterModel } from "@aventara/core";
2
+ import type { AdapterOperationExecutor } from "@aventara/core/internal";
2
3
  import type { PrismaResourceTranslation } from "../discovery/discovery-artifact.interface.js";
3
4
  import type { PrismaDelegateHost, PrismaTransactionOptions } from "../prisma-client.interface.js";
4
5
  export type PrismaTransactionExecutorDependencies = {
@@ -1,4 +1,4 @@
1
- import { canonicalizeJson } from "@aventara/core";
1
+ import { canonicalizeJson } from "@aventara/core/internal";
2
2
  import { computeModelHash } from "../discovery/artifact.loader.js";
3
3
  import { DISCOVERY_ARTIFACT_FORMAT, } from "../discovery/discovery-artifact.interface.js";
4
4
  const BANNER = [
@@ -18,8 +18,9 @@ const TRANSLATION_DOC = [
18
18
  const REFERENTIAL_ACTIONS_DOC = [
19
19
  "/**",
20
20
  " * The schema's referential actions, per Resource: the relation fields that",
21
- " * declare `@relation(fields: …)`, each with its resolved `onDelete` and",
22
- " * `onUpdate`. An edge under Resource `R` reads:",
21
+ " * declare `@relation(fields: …)`, each with the fields holding its reference",
22
+ " * (`references`) and its resolved `onDelete` and `onUpdate`. An edge under",
23
+ " * Resource `R` reads:",
23
24
  " *",
24
25
  " * deleting a `target` row does `onDelete` to rows of `R`.",
25
26
  " *",
@@ -42,9 +43,9 @@ const CLIENT_DOC = [
42
43
  const CREATE_REQUIREMENTS_DOC = [
43
44
  "/**",
44
45
  " * What a create of each Resource cannot do without: a field with no default,",
45
- " * or a required relation, which may instead be written through `orFields` —",
46
- " * the scalar fields holding its reference — and which a nested create",
47
- " * reached through its `inverse` gets from the parent.",
46
+ " * or a required relation, which may instead be written through the fields",
47
+ " * holding its reference (its edge's `references` above) and which a nested",
48
+ " * create reached through its `inverse` gets from the parent.",
48
49
  " *",
49
50
  " * Every create is checked against it and a missing value is answered as",
50
51
  " * `A2004`/`V1000` at its path, before the database is reached. Not part of",
@@ -16,6 +16,7 @@ function translateReferentialActions(table) {
16
16
  owningEdges.map((edge) => ({
17
17
  field: edge.field,
18
18
  target: edge.target,
19
+ references: edge.references,
19
20
  onDelete: DELETE_EFFECTS[edge.onDelete],
20
21
  })),
21
22
  ]));
@@ -1,4 +1,4 @@
1
- import { type AdapterModel, type OperationIdentity } from "@aventara/core";
1
+ import type { AdapterModel, OperationIdentity } from "@aventara/core";
2
2
  import type { PrismaIdentifierAddress } from "../discovery/discovery-artifact.interface.js";
3
3
  /** A mutation the model, the stage or the active operation does not license. */
4
4
  export declare class PrismaMutationTranslationError extends Error {
@@ -1,4 +1,4 @@
1
- import { advertisedDirectives, } from "@aventara/core";
1
+ import { advertisedDirectives, } from "@aventara/core/internal";
2
2
  import { addressIdentifier } from "./identifier.addressing.js";
3
3
  import { emitScalarValue } from "./scalar.conversion.js";
4
4
  export class PrismaMutationTranslationError extends Error {
@@ -1,4 +1,5 @@
1
- import type { AdapterModel, AdapterOperation } from "@aventara/core";
1
+ import type { AdapterModel } from "@aventara/core";
2
+ import type { AdapterOperation } from "@aventara/core/internal";
2
3
  import type { PrismaResourceTranslation } from "../discovery/discovery-artifact.interface.js";
3
4
  import { type PrismaProjectionPlan } from "./projection.translator.js";
4
5
  /** The `ModelAction` subset this adapter uses. See the header for the exclusions. */
@@ -1,4 +1,4 @@
1
- import { type AdapterModel } from "@aventara/core";
1
+ import type { AdapterModel } from "@aventara/core";
2
2
  import type { PrismaIdentifierAddress } from "../discovery/discovery-artifact.interface.js";
3
3
  /** An order or cursor the discovered model does not license. */
4
4
  export declare class PrismaOrderTranslationError extends Error {
@@ -1,4 +1,4 @@
1
- import { advertisedReducers, } from "@aventara/core";
1
+ import { advertisedReducers } from "@aventara/core/internal";
2
2
  import { addressIdentifier } from "./identifier.addressing.js";
3
3
  export class PrismaOrderTranslationError extends Error {
4
4
  name = "PrismaOrderTranslationError";
@@ -1,4 +1,5 @@
1
- import { type AdapterField, type AdapterModel } from "@aventara/core";
1
+ import { type AdapterModel } from "@aventara/core";
2
+ import type { AdapterField } from "@aventara/core/internal";
2
3
  /** A value this adapter will not convert rather than misrepresent. */
3
4
  export declare class PrismaScalarConversionError extends Error {
4
5
  readonly name = "PrismaScalarConversionError";
@@ -1,4 +1,4 @@
1
- import { Decimal, } from "@aventara/core";
1
+ import { Decimal } from "@aventara/core";
2
2
  export class PrismaScalarConversionError extends Error {
3
3
  name = "PrismaScalarConversionError";
4
4
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aventara/prisma7-adapter",
3
- "version": "0.1.0-pilot.4",
3
+ "version": "1.0.0-rc.0",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "The Prisma 7 adapter for Aventara.",
6
6
  "type": "module",
@@ -26,10 +26,11 @@
26
26
  },
27
27
  "files": [
28
28
  "dist",
29
- "LICENSE-ADDITIONAL-PERMISSION.md"
29
+ "LICENSE-ADDITIONAL-PERMISSION.md",
30
+ "CHANGELOG.md"
30
31
  ],
31
32
  "dependencies": {
32
- "@aventara/core": "0.1.0-pilot.4"
33
+ "@aventara/core": "1.0.0-rc.0"
33
34
  },
34
35
  "peerDependencies": {
35
36
  "@prisma/adapter-better-sqlite3": "^7.10.0",
@@ -91,7 +92,7 @@
91
92
  "typescript-6": "npm:typescript@6.0.3"
92
93
  },
93
94
  "scripts": {
94
- "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",
95
+ "build": "node ../../scripts/build/build-output.cleaner.ts dist && 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",
95
96
  "typecheck": "node node_modules/typescript/bin/tsc -p tsconfig.typecheck.json --noEmit",
96
97
  "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",
97
98
  "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'",