@aventara/core 1.0.0-rc.1 → 1.0.0-rc.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/CHANGELOG.md +295 -1
- package/ERRORS.md +142 -0
- package/README.md +24 -1
- package/dist/adapters/adapter-discovery-diagnostic.guidance.d.ts +6 -0
- package/dist/adapters/adapter-discovery-diagnostic.guidance.js +7 -0
- package/dist/adapters/adapter-field.d.ts +14 -0
- package/dist/adapters/adapter-model.d.ts +9 -0
- package/dist/adapters/adapter.interface.d.ts +10 -0
- package/dist/compiler/contract-compiler-diagnostic.guidance.d.ts +6 -0
- package/dist/compiler/contract-compiler-diagnostic.guidance.js +97 -0
- package/dist/compiler/contract.compiler.js +94 -6
- package/dist/config/computed-fields.d.ts +62 -0
- package/dist/config/computed-fields.js +80 -0
- package/dist/config/config-diagnostic.guidance.d.ts +6 -0
- package/dist/config/config-diagnostic.guidance.js +32 -0
- package/dist/config/define-resource.d.ts +1 -1
- package/dist/config/exact-framework-config.d.ts +48 -20
- package/dist/config/field-compute.resolver.d.ts +2 -0
- package/dist/config/field-compute.resolver.js +8 -0
- package/dist/config/framework-config.d.ts +7 -6
- package/dist/config/framework-resources-config.d.ts +18 -12
- package/dist/config/pipeline-config.d.ts +4 -4
- package/dist/contracts/capability.d.ts +10 -1
- package/dist/contracts/capability.js +2 -0
- package/dist/contracts/client-contract-structure.validator.d.ts +65 -35
- package/dist/contracts/client-contract-structure.validator.js +4 -2
- package/dist/contracts/contract-canonicalizer.d.ts +7 -0
- package/dist/contracts/contract-canonicalizer.js +18 -4
- package/dist/contracts/field.d.ts +30 -1
- package/dist/framework/contract-view.d.ts +4 -4
- package/dist/framework/framework-construction.error.d.ts +12 -3
- package/dist/framework/framework-construction.error.js +5 -1
- package/dist/framework/framework.factory.d.ts +1 -1
- package/dist/framework/framework.factory.js +9 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/internal.d.ts +2 -0
- package/dist/internal.js +1 -0
- package/dist/operations/database-refusal.d.ts +51 -0
- package/dist/operations/database-refusal.js +45 -0
- package/dist/operations/operation-arguments.d.ts +102 -31
- package/dist/operations/operation-validator-core.js +86 -4
- package/dist/operations/validation-code.guidance.d.ts +6 -0
- package/dist/operations/validation-code.guidance.js +90 -0
- package/dist/operations/write-scope.resolver.d.ts +4 -0
- package/dist/operations/write-scope.resolver.js +2 -0
- package/dist/protocol/protocol.handler.js +55 -16
- package/dist/runtime/caller-data.copier.d.ts +17 -0
- package/dist/runtime/caller-data.copier.js +18 -1
- package/dist/runtime/caller-data.door.d.ts +11 -5
- package/dist/runtime/computed-read-runtime.js +15 -2
- package/dist/runtime/computed-value.error.d.ts +3 -2
- package/dist/runtime/computed-value.error.js +3 -2
- package/dist/runtime/create-requirement.d.ts +41 -5
- package/dist/runtime/create-requirement.js +137 -20
- package/dist/runtime/diagnostics-reporter.d.ts +2 -1
- package/dist/runtime/error-boundary.js +4 -0
- package/dist/runtime/error-guidance.d.ts +12 -0
- package/dist/runtime/error-guidance.js +1 -0
- package/dist/runtime/error.catalog.d.ts +49 -0
- package/dist/runtime/error.catalog.js +49 -0
- package/dist/runtime/field-stage.runtime.d.ts +2 -0
- package/dist/runtime/field-stage.runtime.js +8 -4
- package/dist/runtime/field-validator.runtime.d.ts +9 -1
- package/dist/runtime/field-validator.runtime.js +11 -6
- package/dist/runtime/json-value.d.ts +10 -5
- package/dist/runtime/level-lifecycle.runtime.d.ts +2 -2
- package/dist/runtime/level-lifecycle.runtime.js +2 -5
- package/dist/runtime/mutation-planner.js +3 -14
- package/dist/runtime/operation-code.guidance.d.ts +6 -0
- package/dist/runtime/operation-code.guidance.js +167 -0
- package/dist/runtime/operation-runtime.d.ts +20 -6
- package/dist/runtime/operation-runtime.js +77 -35
- package/dist/runtime/owned-execution.registry.d.ts +12 -2
- package/dist/runtime/pipe-output.validator.d.ts +10 -0
- package/dist/runtime/pipe-output.validator.js +4 -2
- package/dist/runtime/pipeline.d.ts +35 -8
- package/dist/runtime/readonly-value.d.ts +14 -3
- package/dist/runtime/readonly-value.js +10 -1
- package/dist/runtime/scalar-runtime.d.ts +5 -1
- package/dist/runtime/scalar-runtime.js +86 -16
- package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
- package/dist/runtime/unsent-value.normalizer.js +128 -0
- package/dist/transactions/operation-fingerprint.js +2 -2
- package/dist/transactions/sql-statement.d.ts +8 -0
- package/dist/transactions/sql-statement.validator.js +36 -3
- package/dist/transactions/transaction-plan-assembler.d.ts +1 -1
- package/dist/transactions/transaction-plan-assembler.js +33 -10
- package/dist/transactions/transaction-plan-validator.d.ts +16 -0
- package/dist/transactions/transaction-plan-validator.js +27 -1
- package/dist/transactions/transaction-reference-validator.d.ts +42 -0
- package/dist/transactions/transaction-reference-validator.js +82 -56
- package/dist/transactions/transaction-reference.js +5 -0
- package/dist/transactions/transaction-runtime.js +47 -22
- package/dist/transactions/transaction-stage.runtime.d.ts +2 -1
- package/dist/transactions/transaction-stage.runtime.js +7 -4
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,300 @@
|
|
|
1
1
|
# @aventara/core
|
|
2
2
|
|
|
3
|
+
## 1.0.0-rc.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- A pipe may move a transaction step's `$ref`. A pipe sees a `$ref` as the reference it is, unresolved, and a
|
|
8
|
+
scoping pipe moves it: `({ args }) => ({ ...args, where: { AND: [args.where ?? {}, { published: true }] } })` puts
|
|
9
|
+
the caller's `where: { categoryId: cat.$ref("id") }` at `where.AND.0.categoryId`. The reference is now resolved
|
|
10
|
+
wherever the pipe put it: in an `AND`, an `OR` or a `NOT`, in a relation filter, under any operator, or copied from a
|
|
11
|
+
data value into another field or into a `$connect` key. Before, only a reference still standing where the caller
|
|
12
|
+
wrote it was resolved, and the step failed inside the transaction with `500 A3004` ("A server pipeline produced
|
|
13
|
+
invalid arguments."), the `diagnostics` sink naming the moved `$ref` (`V1006: Filter operator "$ref" is not
|
|
14
|
+
available on field "categoryId"`, or `V1001` under `gte`). A moved reference is held to the rules a reference the
|
|
15
|
+
caller writes at that place is held to (where a `$ref` may stand, its source step, and its source field's type
|
|
16
|
+
against its new place): one that breaks them, such as a `$ref` moved into an `orderBy` or onto a field of another
|
|
17
|
+
type, is still the server's `A3004`, and an operation pipe's is now answered before the transaction opens. A
|
|
18
|
+
transaction pipe that moves a `$ref` is held to the same rules at its step. A literal a pipe writes where the caller
|
|
19
|
+
had a `$ref` now goes through that field's pipes, as any other value does.
|
|
20
|
+
- A relation the request writes together with its reference field (`{ authorId: 1, author: { $connect: { id: 1 } } }`)
|
|
21
|
+
is refused by the framework validation, before any guard, pipe or compute stage runs and, in a plan, before the
|
|
22
|
+
transaction opens: `A2004`, `V1012` at the relation, as before. In rc.1 it was judged only after the guards, pipes
|
|
23
|
+
and write compute, so the developer's stages ran on the request first, and a plan opened its transaction and ran the
|
|
24
|
+
steps before it.
|
|
25
|
+
- The field validators run after write compute. The order of a call is now the framework validation, the guards, the
|
|
26
|
+
pipes and field pipes, write compute, then the re-check: Aventara's own checks again on what the pipes and compute
|
|
27
|
+
produced, then the field validators (a Json field's shape among them) on every value the adapter will be handed,
|
|
28
|
+
then the interceptors and hooks. So a validator judges what is stored: a value a `compute.create` or
|
|
29
|
+
`compute.update` stage replaces is judged as computed, not as the caller sent it. In rc.1 the validators ran after
|
|
30
|
+
the pipes and before write compute, and a computed value was not judged by them. A plan step and a forced call still
|
|
31
|
+
judge before the transaction opens the literal values no write compute stage of their phase changes, so a caller's
|
|
32
|
+
refusal there opens nothing; the rest is judged at the step, and a refusal rolls the transaction back.
|
|
33
|
+
- A computed write value its field's validators or Json shape refuse is answered `A2004` at the field, as any validator
|
|
34
|
+
refusal is (`{ code: "V1003", path: ["arguments", "data", "slug"], check: "string_format", … }`), at the top level,
|
|
35
|
+
in nested writes and in both branches of an upsert. In rc.1 such a value was not judged at all. A computed value of
|
|
36
|
+
the wrong type is still the server's error: `A3000`, its issues to the `diagnostics` sink on an
|
|
37
|
+
`AvComputedValueError`. `compute.read` values are output and are not judged.
|
|
38
|
+
- A field computed on read with no `dependsOn` (the function form, `read: () => "stamped"`) has the Resource's
|
|
39
|
+
identifier read for it, the adapter's first one (with Prisma, the primary key when the model declares it first), so
|
|
40
|
+
each row comes back whatever else is selected; a `dependsOn` field is read as before. The answer holds exactly what
|
|
41
|
+
was selected: `{ select: ["stamp"] }` answers `[{ stamp: "stamped" }]` on every reading variant and in a write's
|
|
42
|
+
`select`, and inside a relation projection (`{ select: [{ category: { select: ["stamp"] } }] }`) the relation's row
|
|
43
|
+
is read and computed. In rc.1 a select of only such fields handed the adapter an empty projection, which Prisma
|
|
44
|
+
cannot express: `500 A3001` at the top level, and inside a relation the row was not read (`500 A3000`).
|
|
45
|
+
- Every stage told a frozen view gets its own copy of a `Date` or a `Uint8Array`, which freezing cannot reach: a guard,
|
|
46
|
+
a field write guard, a pipe (its input and its `ctx.$args`), a field pipe, write compute (`data`, `value`,
|
|
47
|
+
`ctx.$args`), an interceptor, a before hook, each after hook (the result and `ctx.$args`), a filter and a Resource
|
|
48
|
+
transaction stage. Changing one in place changes only that stage's copy, never what a later stage is told, what is
|
|
49
|
+
written or what the caller receives; data changes only by returning it. In rc.1 every stage after write compute
|
|
50
|
+
shared one copy, so an after hook was told the `Date` a before hook had changed in place, and a guard or a pipe was
|
|
51
|
+
told the framework's own value. Arguments holding no such value are handed on as they are, at no cost per stage; a
|
|
52
|
+
copy is made only along the path to one (about 3 µs per stage for a record with a `Date` and a 64-byte
|
|
53
|
+
`Uint8Array`).
|
|
54
|
+
- A `compute.read` stage is told the row as its own deep-frozen copy, a `Date` or a `Uint8Array` in it copied, like
|
|
55
|
+
every stage above; its field's `value` too. It returns its field's value: writing into `record` throws a
|
|
56
|
+
`TypeError`, answered `A3000` when the stage lets it through, as a pipe's write into its arguments is. The answer is
|
|
57
|
+
built from the row as read. In rc.1 the stage was told a mutable copy of the row and the answer was built from it,
|
|
58
|
+
so `record.title = "x"` changed the title every surface answered. The copy costs about 1.3 µs per row and stage
|
|
59
|
+
(1.8 µs with a `Date` and 64 bytes): a 100-row read with one computed field took 0.33 ms instead of 0.19 ms.
|
|
60
|
+
- A record written through a relation cannot write that relation back. Inside a post created in `author.posts`,
|
|
61
|
+
`author` and `authorId` are what the write the post sits in supplies; inside a category created in a post's
|
|
62
|
+
`category`, `posts` is (`{ posts: { $create: { category: { $create: { posts: { $create: … } } } } } }`). The
|
|
63
|
+
framework validation refuses such a write before any guard, pipe or adapter work: `A2004` with one `V1012` issue at
|
|
64
|
+
the field (`["arguments", "data", "posts", "$create", "category", "$create", "posts"]`), message `Relation "posts"
|
|
65
|
+
is the relation this Category is written through, from its other end ("Post.category"): the write it sits in
|
|
66
|
+
supplies it.`, under every directive that carries a record (`$create`, `$connectOrCreate`, `$update`, `$upsert`),
|
|
67
|
+
to-one and to-many, and in a plan step. Only the edge walked is refused: a self-relation's other edge to the same
|
|
68
|
+
Resource stays writable. It is a type error on the façades, the builders and the generated client. In rc.1 the
|
|
69
|
+
Contract admitted these writes and the Prisma adapter answered `500 A3001`.
|
|
70
|
+
- The Contract states each relation's other end, `inverse` on a relation field (`User.posts` has `inverse: "author"`),
|
|
71
|
+
as the adapter pairs them, and only where the layer shows that other end. The write axis has a third position,
|
|
72
|
+
`"loopback"`: that other end inside a record written through the relation. An adapter states whether a relation is
|
|
73
|
+
writable there as it states the other two positions (an arm that lists `"loopback"`, or an unqualified arm). A
|
|
74
|
+
Contract whose adapter states a pairing has a new hash; one that states none is unchanged, and so is an arm that
|
|
75
|
+
lists every position. `AvAdapterRelationField.inverse` is how an adapter states the pairing.
|
|
76
|
+
- The Contract states which scalar fields hold each relation's reference: `referenceFields` on a relation field
|
|
77
|
+
(`Post.category` has `referenceFields: ["categoryId"]`; a composite reference lists every field, in the order the
|
|
78
|
+
data source pairs them with the target's key), on an optional relation as on a required one. It is absent on the
|
|
79
|
+
end that holds no reference (`Category.posts`), never an empty list. A layer keeps the fields it shows, in the
|
|
80
|
+
adapter's order, and leaves the property out when it shows none. The argument types now leave a loopback edge's
|
|
81
|
+
reference fields out for an optional relation too: `categoryId` in a post created in `Category.posts` is a type
|
|
82
|
+
error on the façades, the builders and the generated client, as the framework validation refuses it (`V1012`). In
|
|
83
|
+
rc.1 only a required relation's reference fields were left out, read off `createRequirements`.
|
|
84
|
+
`AvAdapterRelationField.referenceFields` is how an adapter states them. A Contract whose adapter states them has a
|
|
85
|
+
new hash.
|
|
86
|
+
- A computed value is held to every named `format` its field declares, not only `uuid`: a `compute.read`,
|
|
87
|
+
`compute.create` or `compute.update` value that breaks `email`, `url`, `ipv4` or any other of zod's named formats
|
|
88
|
+
is the server's error, `A3000`, its `V1003` issue to the `diagnostics` sink on an `AvComputedValueError`, as a value
|
|
89
|
+
of the wrong type is. Each entry of a list field is judged. In rc.1 only `uuid` was checked and any other format
|
|
90
|
+
reached the caller unchecked. A relative path is no URL: compute an absolute one into a `format: "url"` field
|
|
91
|
+
(`` `https://example.com/p/${record.slug}` ``), or leave `format` out. A caller's own value is still held only to
|
|
92
|
+
`uuid` (a format describes the value; a rule on caller input is a validator, `AvZ.url()`).
|
|
93
|
+
- `AvErrorCatalog` (and `ERRORS.md`): `A2004` and `V1012` list a record written through a relation that writes that
|
|
94
|
+
relation, or a field holding its reference, back. An adapter that meets one core cannot see (the layer hides the
|
|
95
|
+
relation's other end, so its Contract states no pairing) answers it with the new `V1012` sentence `The data source
|
|
96
|
+
refused this nested write: a record written through a relation cannot write that relation or its reference fields
|
|
97
|
+
back.`, which the response boundary keeps as it keeps a database refusal's.
|
|
98
|
+
- A nested `$update` on a to-many relation is a bulk write: its one `data` applies to every related row its `where`
|
|
99
|
+
matches, so it takes what `update.many`'s `data` takes. A relation the adapter does not offer in a bulk write is
|
|
100
|
+
refused before the adapter with `A2004` and `V1006` at the member
|
|
101
|
+
(`["arguments", "data", "posts", "$update", "data", "category"]`, `Relation "category" is not writable during
|
|
102
|
+
update.`), and is a type error on the façades, the builders and the generated client. A to-one `$update` is
|
|
103
|
+
unchanged. In rc.1 the Contract admitted it and the Prisma adapter refused it as `500 A3001`.
|
|
104
|
+
- `AvErrorCatalog` (and `ERRORS.md`): `A3002` lists a transaction that expired or was closed under a step among its
|
|
105
|
+
causes, and `V1006` a relation write in a to-many `$update`'s `data`.
|
|
106
|
+
- A relation a pipe or a compute stage writes together with its reference field is the server's error: the caller
|
|
107
|
+
gets `A3004` with the generic pipe-output message, and the `diagnostics` sink gets an `AvPipeOutputError` holding the
|
|
108
|
+
`V1012` issue and its path. In rc.1 the caller got `A2004`/`V1012`, as if it had sent both. A pipe's output that
|
|
109
|
+
breaks a field validator or a Json field's shape is still the caller's `A2004`: those are the developer's rules for
|
|
110
|
+
what may be stored, not Aventara's.
|
|
111
|
+
- In a transaction plan, a `$ref` holding no object (`{ payload: { $ref: "x" } }`, `{ $ref: null }`) is answered as a
|
|
112
|
+
single call answers it: `A2004` with `V1006` at the `$ref` key (`Mutation directive "$ref" is not available on field
|
|
113
|
+
"payload".`), at `["operations", i, "args", …, "$ref"]`. In rc.1 a plan read it as a malformed reference:
|
|
114
|
+
`A2007` with `V1016` at `…, "$ref", "operation"`. A `$ref` holding an object is still a reference, and a broken one
|
|
115
|
+
still `A2007`.
|
|
116
|
+
- `AvJsonValue` admits a key holding `undefined`, so an object with optional properties
|
|
117
|
+
(`{ readonly note?: string }`) is assignable to a `json` field; the key is left out at runtime. In rc.1 the type
|
|
118
|
+
refused it. An array element holding `undefined` is still a type error (at runtime it is `null`).
|
|
119
|
+
- `AvErrorCatalog` (and `ERRORS.md`): `V1001` lists two directives in one scalar action object among its causes,
|
|
120
|
+
`A2004` a computed value the validators refuse, `A3000` a computed value of the wrong type, and `A3004` a reference
|
|
121
|
+
a pipe or write compute wrote together with its relation.
|
|
122
|
+
- A scalar action object holds one directive. `{ score: { $increment: 1, $decrement: 2 } }` is refused before the
|
|
123
|
+
adapter with `A2004` and `V1001` at the field (`Scalar mutation action objects may contain only one directive.`),
|
|
124
|
+
and is a type error on the façades, the builders and the generated client. In rc.1 it passed validation and the
|
|
125
|
+
ORM refused it: `500 A3001`. A directive the field does not offer is still `V1006` at the directive.
|
|
126
|
+
- An `undefined` inside a `json` value is what JSON makes of it, on every surface: a key holding it is left out and an
|
|
127
|
+
array element holding it (or a hole) is `null`, as `JSON.stringify` writes them, so
|
|
128
|
+
`{ meta: { a: undefined, b: [undefined] } }` stores `{"b":[null]}` through `framework.application`,
|
|
129
|
+
`framework.client`, `framework.execute`, the ctx executor and a plan step, as it does through the generated client
|
|
130
|
+
and over HTTP. The same holds in a `json` filter operand, and an operation's fingerprint is computed from the value
|
|
131
|
+
as JSON carries it. In rc.1 the in-process surfaces refused the key with `A2004`/`V1001`, and a hole in an array
|
|
132
|
+
inside a `json` value was dropped, moving the elements after it down one index.
|
|
133
|
+
- `DATABASE_REFUSALS` has two more sentences an adapter answers a refused value with, both `V1004`:
|
|
134
|
+
`The database refused this value: it divides by zero.` and `The database refused this value: the write computes no
|
|
135
|
+
value for a column that requires one, as a division by zero does.`
|
|
136
|
+
- `AvErrorCatalog`'s `A2004` entry (and `ERRORS.md`) covers a value the database refused: an adapter answers it with
|
|
137
|
+
one issue at `["arguments"]`, `V1004` for a length or range, `V1001` otherwise, and the issue's message says what
|
|
138
|
+
the database refused (`The database refused this value: its text cannot contain the NUL character (U+0000).`).
|
|
139
|
+
The response boundary keeps such a message only when it is core's own sentence for that refusal under its own
|
|
140
|
+
code; every other adapter issue message is still replaced by the code's published text.
|
|
141
|
+
- An `appTx` or `clientTx` builder no longer runs a getter inside an object carrying `$ref` when it copies a step's
|
|
142
|
+
arguments: it mirrors the object as `framework.executeTransaction` does, and the run refuses it the same way
|
|
143
|
+
(`A2004`, `V1001` at the extra key). In rc.1 the builder ran the getter at the call.
|
|
144
|
+
- `AvProtocol.bind(framework).handleOperation` copies a `{ kind: "parsed" }` body at the call, before it awaits the
|
|
145
|
+
state seed, so a host that changes its parsed object after the call changes nothing, and a body that cannot be
|
|
146
|
+
copied is refused at the call (`A2004`, `V1001` at its path). In rc.1 the copy was taken after the first await.
|
|
147
|
+
- `AvProtocol.bind(framework).handleTransaction` takes a `{ kind: "parsed" }` plan at the call, before it awaits the
|
|
148
|
+
state seed, as `framework.executeTransaction` takes one: the plan's structure is mirrored by its property
|
|
149
|
+
descriptors, so no getter the plan validator refuses runs, and each step's arguments are copied, its `$ref` control
|
|
150
|
+
objects mirrored. A host that changes the plan after the call changes nothing, and a step whose arguments cannot be
|
|
151
|
+
copied is answered at its turn (`A2004`, `V1001` at its path). In rc.1 the plan was read after the first await, so a
|
|
152
|
+
step changed after the call failed its fingerprint (`A2007`, `V1017`) or ran as changed.
|
|
153
|
+
- `framework.appTx.$sql` mirrors a `$ref` control object among its values as the builders mirror one in a step's
|
|
154
|
+
arguments: no getter inside it runs, and a malformed one is refused as `framework.executeTransaction` refuses it
|
|
155
|
+
(`A2004`, `V1001` at its extra key). A well-formed hand-written `{ $ref: { operation, fingerprint, path } }` is read
|
|
156
|
+
as a reference to that step's field, held to the same checks as one in an operation step. In rc.1 the value was
|
|
157
|
+
copied by value at the call, which ran its getters, and bound as JSON text.
|
|
158
|
+
- An `orderBy` value that is no order says what is wrong with it. A `$ref` there is refused with `A transaction
|
|
159
|
+
reference cannot stand in orderBy: order "email" by "asc", "desc" or { sort, nulls }.`, and a malformed null-order
|
|
160
|
+
object with `Null ordering for "email" must be exactly { sort: "asc" | "desc", nulls: "first" | "last" }.`. The
|
|
161
|
+
code (`V1001`) and path are unchanged; rc.1 answered both with `Extended null ordering for "email" must contain sort
|
|
162
|
+
and nulls.`
|
|
163
|
+
- Each filter operator object stands alone: a `mode` or `path` qualifies only the operators written beside it, and an
|
|
164
|
+
object under `not` has its own. A `mode` or `path` with no comparing operator beside it is refused with `A2004` and
|
|
165
|
+
`V1001` at that key, before the adapter, with the message `mode on field "title" qualifies no operator: write it next
|
|
166
|
+
to the operator it applies to (inside "not" to apply to a negated one).`. That covers `{ mode: "insensitive", not: {
|
|
167
|
+
contains: "x" } }` (write `{ not: { contains: "x", mode: "insensitive" } }`), `{ path: ["label"], not: { … } }`
|
|
168
|
+
(write the `path` inside the `not`), and a `mode` or `path` alone, such as `{ title: { mode: "default" } }`, which
|
|
169
|
+
reached the adapter as no condition and matched every row. A value under `not` (`{ path: ["label"], not: "x" }`)
|
|
170
|
+
compares at its own level and is accepted. The typed calls refuse the same objects at compile time.
|
|
171
|
+
- An error catalog: `AvErrorCatalog`, exported from `@aventara/core`, maps every outcome code (`A1000` to `A4002`),
|
|
172
|
+
validation code (`V1000` to `V1021`) and startup code (`CONFIG_…`, `DISCOVERY_…`, `COMPILER_…`) to its `kind`,
|
|
173
|
+
`message`, `cause`, `fix` and `docs` URL, the code's entry on the error-codes page
|
|
174
|
+
(`https://aventara-framework.vercel.app/docs/error-codes#a2004`: the code in lower case). The package also ships the
|
|
175
|
+
catalog as `ERRORS.md`, one table per kind. New names: `AvErrorCatalog` (a value and its type),
|
|
176
|
+
`AvErrorCatalogEntry`, `AvErrorCatalogCode`, `AvErrorKind`.
|
|
177
|
+
- A startup diagnostic links to its documentation: every entry of `AvConstructionError`'s `diagnostics` is now
|
|
178
|
+
`{ code, path, message, docs }`, where `docs` is `AvErrorCatalog[code].docs`. Codes, paths and messages are
|
|
179
|
+
unchanged. A test that compares a whole diagnostic with `toEqual` now sees the `docs` key.
|
|
180
|
+
- A spread of the arguments compiles on a Resource with a `json` field. A variant pipe returning
|
|
181
|
+
`{ ...args, data: { ...args.data, slug } }`, or a guard spreading `ctx.$args.data`, was TS2589 ("Type instantiation
|
|
182
|
+
is excessively deep and possibly infinite"), inline and in `defineResource`: the deep read-only typing re-entered
|
|
183
|
+
`AvJsonValue` through its array member without end. `AvDeepReadonly` now maps an array through its own
|
|
184
|
+
`readonly …[]` branch (a tuple keeps its positions); the types a stage is told are unchanged, and a wrong value in a
|
|
185
|
+
pipe's return is still a compile error.
|
|
186
|
+
- An explicit `undefined` in write data means the key was not sent, as Prisma reads its data. In a create's or
|
|
187
|
+
update's `data`, an upsert's `create` and `update`, each record of a bulk create and every nested create or update,
|
|
188
|
+
`{ name: undefined }` is now `{}`: on a create an optional field takes what leaving it out gives it and a required one
|
|
189
|
+
is missing (`A2004` with `V1000` at its path); an update leaves the field untouched. Before, it was refused as
|
|
190
|
+
`A2004`/`V1001` "Value must be a string.". The same holds for the in-process façades, ctx calls, `appTx` plans and
|
|
191
|
+
the arguments a pipe or a transaction pipe returns: an optional field a pipe sets to `undefined` is omitted, a
|
|
192
|
+
required one is a dropped field (`A3004`). Filters and unique selectors are unchanged: `where: { id: undefined }` is
|
|
193
|
+
still refused (`V1001`), so an `update.many` or `delete.many` never widens to every row. The refusal says why, in the
|
|
194
|
+
generated client's words where they apply in process: `Value must not be undefined: it would be left out, and the
|
|
195
|
+
request would reach more rows.` (it said what the value is not, such as `Value must be a string.`). This holds at
|
|
196
|
+
any depth of a filter, a unique selector or a cursor: an operator's operand, an `in` entry, an `AND` entry, a `NOT`,
|
|
197
|
+
a relation filter, a relation's `where` under `select` or `include`, and a nested write's `$connect`, `$disconnect`,
|
|
198
|
+
`$set` and `$update` selectors. Code and path are unchanged.
|
|
199
|
+
- An `undefined` `mode`, `isEmpty` or `path` in a filter is refused (`A2004` with `V1001` at the operand), as every
|
|
200
|
+
other operator's `undefined` already was. These three are passed to the adapter as given, so
|
|
201
|
+
`{ tags: { isEmpty: undefined } }` reached it as no condition and matched every row.
|
|
202
|
+
- A filter operand of the wrong type is refused (`A2004` with `V1001` at the operand) before the adapter is called:
|
|
203
|
+
`mode` takes `"default"`, `"sensitive"` or `"insensitive"`, `isEmpty` a boolean, `path` an array of strings (a wrong
|
|
204
|
+
entry is named at its index) and a `json` field's `stringContains`, `stringStartsWith` and `stringEndsWith` a string.
|
|
205
|
+
Before, a value such as `mode: "loud"`, `isEmpty: 3`, `path: "a.b"` or `stringContains: 3` from an HTTP or MCP caller
|
|
206
|
+
reached the adapter, and the ORM refused it as `A3001`. The typed calls and the generated client type these operands
|
|
207
|
+
the same way: `mode`, `isEmpty` and `path` were `unknown`, and a `json` `string*` operand accepted any non-null JSON
|
|
208
|
+
value.
|
|
209
|
+
- Filter modes are advertised per field. `mode` has three values: `"default"` is the database's own comparison and
|
|
210
|
+
every field that offers `mode` takes it; `"sensitive"` and `"insensitive"` are an explicit promise, taken only where
|
|
211
|
+
the field's new `capabilities.filterModes` lists them (and, on a `json` field that offers `path`,
|
|
212
|
+
`capabilities.pathFilterModes` when the filter names a `path`). The Contract and the ClientContract carry both lists,
|
|
213
|
+
configuration never names them, and a layer that hides `mode` or `path` drops them. A mode the field does not offer is
|
|
214
|
+
refused like an unadvertised operator: `A2004` with `V1006` at the `mode`, `Filter mode "insensitive" is not available
|
|
215
|
+
on field "email".`; the typed calls offer only the field's modes, and on a `json` field the list that applies: an
|
|
216
|
+
operator object with `path` takes `pathFilterModes`, one without it `filterModes`. On Postgres
|
|
217
|
+
`where: { meta: { stringContains: "x", mode: "insensitive" } }` is a compile error (`Property 'path' is missing`),
|
|
218
|
+
and the same filter with `path: ["label"]` compiles. Contract hashes change for every field that offers `mode`:
|
|
219
|
+
regenerate the client.
|
|
220
|
+
- A `where`, `limit`, `offset` or `cursor` argument set to `undefined` is refused with a message that says what to
|
|
221
|
+
write: `where must not be undefined: leave the key out to mean no filter.` (and the same for `limit`, `offset` and
|
|
222
|
+
`cursor`), at the top level and in a relation under `select` or `include`. The code and path are unchanged
|
|
223
|
+
(`A2004` with `V1001` at the key; `V1009` for `cursor`). A wrong value keeps its message, such as
|
|
224
|
+
`where must be an object.` or `limit must be a non-negative integer.`.
|
|
225
|
+
- 9366e16: MCP endpoint polish:
|
|
226
|
+
|
|
227
|
+
- A request with no bearer credentials (no `Authorization` header, or another scheme such as `Basic`) is answered
|
|
228
|
+
`401` with `WWW-Authenticate: Bearer resource_metadata="…"` and no error code, as RFC 6750 §3.1 asks; its body no
|
|
229
|
+
longer says "Missing Authorization header". A bad bearer token keeps `error="invalid_token"`.
|
|
230
|
+
- A `mcp.authorization.verify` that throws is still answered `401`, and is now reported to the `diagnostics` sink as
|
|
231
|
+
an `AvMcpDiagnostic` holding the error.
|
|
232
|
+
- With `mcp` configured and `@aventara/mcp` missing, the startup error names the command that installs it at the
|
|
233
|
+
installed `@aventara/core`'s exact version: `npm install --save-exact @aventara/mcp@<version>`.
|
|
234
|
+
|
|
235
|
+
- 9366e16: A variant's guards, pipes and filters may leave out of a create exactly the fields your configuration's
|
|
236
|
+
`compute.create` stages fill. Write compute runs after those stages, so a field it fills may still be missing when
|
|
237
|
+
they run, and a pipe may return a create without it: in a pipe, `args.data.slug` is `string | undefined` when
|
|
238
|
+
`slug` has a `compute.create`, and every other required field stays required. A stage's type cannot read the
|
|
239
|
+
configuration it is written in, so the adapter's generate step reads the configuration's type (`aventara:prepare`
|
|
240
|
+
passes `--config src/aventara.config.ts`) and writes which fields each layer computes into the model's type. A
|
|
241
|
+
stage counts the layers whose compute runs for its requests: a root stage the root's, the application's, the
|
|
242
|
+
client's and the agent's; an `application` or `client` stage the root's and its own (and the agent's, when the agent
|
|
243
|
+
extends it); an `agent` stage the root's, its own and its parent's. This holds inline in `createFramework`, in
|
|
244
|
+
`AventaraModule`'s `config`, and in `defineResource` blocks and their overlays, for a create's `data`, an upsert's
|
|
245
|
+
`create` and a nested create. Values are still typed. Hooks and interceptors run after compute and keep every
|
|
246
|
+
requirement. A model generated without a configuration lifts every create requirement in those stages, as before.
|
|
247
|
+
- Breaking: a create missing a required field is refused before any pipeline stage runs. The framework's own
|
|
248
|
+
validation (types, unknown fields, structure) now also checks that every create position holds the fields its
|
|
249
|
+
layer requires (`A2004`/`V1000` at the field), before the guards, where it ran after the pipes and compute. A field a
|
|
250
|
+
`compute.create` stage fills is not required there and stays optional until compute. So a guard or a pipe is always
|
|
251
|
+
told a create that holds every other required field, which is what their types say, and a pipe can no longer fill a
|
|
252
|
+
required field the caller left out: give that field a `compute.create` stage or a database default. This holds for
|
|
253
|
+
a create's `data`, every bulk record, an upsert's `create` branch and nested creates, and for each step of a
|
|
254
|
+
transaction before it opens (a `$ref` counts as a value). Which fields are required is already public in the client
|
|
255
|
+
contract, so the earlier answer reveals nothing new.
|
|
256
|
+
- A required create field the caller sent and a pipe then dropped is now the server's error: the caller gets `A3004`
|
|
257
|
+
with the generic pipe-output message, and the `diagnostics` sink gets an `AvPipeOutputError` holding the `V1000`
|
|
258
|
+
issue. Until now the caller got `A2004`/`V1000`, as if it had left the field out.
|
|
259
|
+
- New: `AvConfigComputedFields`, the shape of that statement: per layer (`root`, `application`, `client`, `agent`),
|
|
260
|
+
per Resource, the fields with a `compute.create` and a `compute.update` stage, and the layer the agent extends. The
|
|
261
|
+
adapter hands it to the framework as `AvAdapter.computedFields`, and the framework compares it with the
|
|
262
|
+
configuration it starts with. When they differ (you changed a compute stage and did not run `aventara:prepare`),
|
|
263
|
+
startup emits one warning, `AVENTARA_STALE_GENERATED_TYPES`, naming each difference:
|
|
264
|
+
|
|
265
|
+
```text
|
|
266
|
+
(node:1234) [AVENTARA_STALE_GENERATED_TYPES] AventaraWarning: generated types are stale: run aventara:prepare. The computed fields the pre-compute stages are typed with differ from the configuration's:
|
|
267
|
+
- computed in the configuration, not in the types: root User.handle (update)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
It is a warning and changes nothing at runtime: the statement only types the stages.
|
|
271
|
+
- A `compute.create` stage that returns `undefined` for a field a create cannot do without (not nullable, no default,
|
|
272
|
+
not generated) and that the caller did not send now fails as the server's error: the caller gets `A3000`, and the
|
|
273
|
+
`diagnostics` sink gets an `AvComputedValueError` whose message is
|
|
274
|
+
`compute.create on <Resource>.<field> returned undefined for a required field`. The field left the create
|
|
275
|
+
requirements because the stage fills it; until now the create reached the database without it, and the
|
|
276
|
+
database's refusal answered `A3001`. This holds for a create's `data`, every record of a bulk create, an upsert's `create`
|
|
277
|
+
branch and a nested create. A reference field counts as missing only when the record writes neither its relation nor
|
|
278
|
+
every reference field, and a nested create's field the parent supplies is never missing. Updates are not checked:
|
|
279
|
+
nothing is required of an update.
|
|
280
|
+
- 9366e16: A Pick layer (`exposed: false`) that names what its parent hid now says why in the type error. A Resource, field,
|
|
281
|
+
family, variant or capability the root (or, for the agent, the layer it extends) hid is typed as
|
|
282
|
+
`readonly ["Error: client cannot expose 'Post.internalNote': hidden by root"]`, so the error TypeScript reports through
|
|
283
|
+
the scaffold's `aventaraConfig()` and `AventaraModule.forRootAsync` ends at:
|
|
284
|
+
|
|
285
|
+
```text
|
|
286
|
+
The types of 'client.exemptions.resources.Post.fields.internalNote' are incompatible between these types.
|
|
287
|
+
Type 'readonly ["select"]' is not assignable to type 'readonly ["Error: client cannot expose 'Post.internalNote': hidden by root"]'.
|
|
288
|
+
Type '"select"' is not assignable to type '"Error: client cannot expose 'Post.internalNote': hidden by root"'.
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
where it ended at `Type 'readonly ["select"]' is not assignable to type 'never'.` An entry written `true` names it
|
|
292
|
+
the same way (`Type 'boolean' is not assignable to type 'readonly ["Error: client cannot expose 'Comment': hidden by
|
|
293
|
+
root"]'.`), where it ended at `Type 'true' is not assignable to type 'never'.`. Written inline in `createFramework`
|
|
294
|
+
or `AventaraModule.forRoot`, the error at a `true` entry starts with the sentence:
|
|
295
|
+
`Type 'true' is not assignable to type 'true & (readonly ["Error: client cannot expose 'Comment': hidden by root"] & …)'`.
|
|
296
|
+
An inline literal's list still reads `not assignable to type 'never'` at the key.
|
|
297
|
+
|
|
3
298
|
## 1.0.0-rc.1
|
|
4
299
|
|
|
5
300
|
### Patch Changes
|
|
@@ -445,7 +740,6 @@
|
|
|
445
740
|
nullable: false,
|
|
446
741
|
list: false,
|
|
447
742
|
lifecycle: ["VIRTUAL"],
|
|
448
|
-
format: "url",
|
|
449
743
|
},
|
|
450
744
|
pipelines: {
|
|
451
745
|
compute: {
|
package/ERRORS.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Aventara error codes
|
|
2
|
+
|
|
3
|
+
Every code `@aventara/core` 1.0.0-rc.2 answers, reports in a validation issue, or refuses to start with. Generated from `AvErrorCatalog` (exported by `@aventara/core`); do not edit.
|
|
4
|
+
|
|
5
|
+
Branch on the code, never on the message. In code, `AvErrorCatalog[code]` gives the same entry: `{ code, kind, message, cause, fix, docs }`.
|
|
6
|
+
|
|
7
|
+
## Success codes (A1xxx)
|
|
8
|
+
|
|
9
|
+
An operation's `code` when it succeeded. The generated client resolves for these.
|
|
10
|
+
|
|
11
|
+
| Code | Meaning | Cause | Fix |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| [`A1000`](https://aventara-framework.vercel.app/docs/error-codes#a1000) | Success. | A read, a count or another operation without a more specific success code succeeded. | Nothing: the call succeeded. |
|
|
14
|
+
| [`A1001`](https://aventara-framework.vercel.app/docs/error-codes#a1001) | No match. | A first-style operation (find.first, update.first, delete.first) matched no row; data is null and the generated client resolves null. | Nothing: handle the null result. A unique operation answers A2003 instead. |
|
|
15
|
+
| [`A1002`](https://aventara-framework.vercel.app/docs/error-codes#a1002) | Created. | create.one created a row. | Nothing: the call succeeded. |
|
|
16
|
+
| [`A1003`](https://aventara-framework.vercel.app/docs/error-codes#a1003) | Created many. | create.many or create.count created rows. | Nothing: the call succeeded. |
|
|
17
|
+
| [`A1004`](https://aventara-framework.vercel.app/docs/error-codes#a1004) | Updated. | update.first or update.unique updated a row. | Nothing: the call succeeded. |
|
|
18
|
+
| [`A1005`](https://aventara-framework.vercel.app/docs/error-codes#a1005) | Updated many. | update.many or update.count updated rows. | Nothing: the call succeeded. |
|
|
19
|
+
| [`A1006`](https://aventara-framework.vercel.app/docs/error-codes#a1006) | Deleted. | delete.first or delete.unique deleted a row. | Nothing: the call succeeded. |
|
|
20
|
+
| [`A1007`](https://aventara-framework.vercel.app/docs/error-codes#a1007) | Deleted many. | delete.count deleted rows. | Nothing: the call succeeded. |
|
|
21
|
+
| [`A1008`](https://aventara-framework.vercel.app/docs/error-codes#a1008) | Upserted. | upsert.unique created or updated a row. | Nothing: the call succeeded. |
|
|
22
|
+
| [`A1009`](https://aventara-framework.vercel.app/docs/error-codes#a1009) | Transaction committed. | A whole transaction plan committed; data holds one result per step. | Nothing: the plan committed. |
|
|
23
|
+
|
|
24
|
+
## Request errors (A2xxx)
|
|
25
|
+
|
|
26
|
+
The caller can fix these. The generated client and server-side calls throw the code's `AvError` subclass.
|
|
27
|
+
|
|
28
|
+
| Code | Meaning | Cause | Fix |
|
|
29
|
+
|---|---|---|---|
|
|
30
|
+
| [`A2000`](https://aventara-framework.vercel.app/docs/error-codes#a2000) | Invalid request. | The HTTP request cannot be interpreted: malformed JSON, a body that is not a JSON object, an unknown `/_*` path, a body that does not decode in its Content-Encoding, or a missing Aventara-Protocol-Version or Aventara-Contract-Hash header (the message names it). | Send a JSON object with both identity headers. The generated client does this for you. |
|
|
31
|
+
| [`A2001`](https://aventara-framework.vercel.app/docs/error-codes#a2001) | Unknown Resource. | The Resource is not in the caller's contract: misspelt, hidden by exposure, or added to the schema after aventara:prepare last ran. cause.message is "The requested Resource is not available." and the issue is V1005 at `["resource"]`. | Use the model's exact name. After a schema change run the adapter's generate step (`npm run aventara:prepare` in a scaffolded project), restart, and regenerate the client. A Resource hidden on purpose is reached from server code (framework.application). |
|
|
32
|
+
| [`A2002`](https://aventara-framework.vercel.app/docs/error-codes#a2002) | Unknown operation. | The operation is not available on that Resource: a typo, removed by exposure, an operation the adapter does not offer (such as delete.many), or a ctx.$sql, appTx.$sql or ctx.$transaction the adapter or the layer cannot run. The issue is V1006 at `["family"]` or `["variant"]`. | Call an operation the contract lists (`GET <api>/_contract`), regenerate a stale client, and check the layer's exemptions and transactionsStyle. |
|
|
33
|
+
| [`A2003`](https://aventara-framework.vercel.app/docs/error-codes#a2003) | Not found. | A unique operation (find.unique, update.unique, delete.unique) matched no row, or a $connect names a record that does not exist. The operation fails, so no after hook runs. | Catch AvNotFoundError, or use a first-style operation, which answers A1001 with null. Check the $connect identifier. |
|
|
34
|
+
| [`A2004`](https://aventara-framework.vercel.app/docs/error-codes#a2004) | Validation failed. | The arguments break a contract rule or a field validator. cause.issues lists each problem by V-code and path; a validator's issue also has check and params. The validators judge the values after the pipes and write compute, so a computed value they refuse is here too, at its field. A required create field left out is here (V1000). So is a value the database itself refused: too long for its column or out of its range (V1004), or holding a character it cannot store or a pattern it cannot read (V1001), with the issue at `["arguments"]` and its message saying what the database refused. So is a record written through a relation that writes that relation, or a field holding its reference, back (V1012, at that field), where the adapter admits no such write. | Fix the argument each issue's path points at, as its V-code says. A field the server must fill takes a compute.create stage. For a value the database refused, send one its column holds. For a nested write that writes its relation back, leave that field out: the parent supplies it. |
|
|
35
|
+
| [`A2005`](https://aventara-framework.vercel.app/docs/error-codes#a2005) | Contract mismatch. | The caller's Aventara-Contract-Hash is not the server's current one: the schema, the configuration, the limits or the exposure changed, or the client targets another deployment. | Regenerate the client against the running server: `npm run avclient:generate`. |
|
|
36
|
+
| [`A2006`](https://aventara-framework.vercel.app/docs/error-codes#a2006) | Protocol mismatch. | Aventara-Protocol-Version is not 1. | Send `Aventara-Protocol-Version: 1`, or upgrade the client. |
|
|
37
|
+
| [`A2007`](https://aventara-framework.vercel.app/docs/error-codes#a2007) | Invalid transaction reference. | A transaction $ref or fingerprint is invalid: the issues are V1010, V1011, V1016, V1017 or V1018, and cause.operation is the step. A $ref with no fingerprint is A2004 / V1000. | Let the generated client or framework.appTx build plans; check the $ref path and that the referenced step comes earlier. |
|
|
38
|
+
| [`A2008`](https://aventara-framework.vercel.app/docs/error-codes#a2008) | Conflict. | A uniqueness or reference constraint failed: a duplicate value, a missing parent, or a $disconnect or $set that would orphan a required child (also through update.first or a developer SQL statement). Sending it again unchanged fails the same way. | Change the data. For a duplicate, update the existing row or use upsert.unique. |
|
|
39
|
+
| [`A2009`](https://aventara-framework.vercel.app/docs/error-codes#a2009) | Limit exceeded. | A contract limit was crossed: nesting depth (V1013), list or plan size (V1014), boolean nodes (V1015) or Json value depth (V1021). | Ask for less (paginate, split the plan), or raise the limit under `limits`. |
|
|
40
|
+
| [`A2010`](https://aventara-framework.vercel.app/docs/error-codes#a2010) | Payload too large. | The decoded request body exceeds maxRequestBytes (default 1048576). | Send less, or raise limits.maxRequestBytes. |
|
|
41
|
+
| [`A2011`](https://aventara-framework.vercel.app/docs/error-codes#a2011) | Unsupported media type. | The body is not application/json, or its Content-Encoding is not gzip, deflate or br. | Send `Content-Type: application/json`. |
|
|
42
|
+
| [`A2012`](https://aventara-framework.vercel.app/docs/error-codes#a2012) | Method not allowed. | The HTTP method does not fit the route; the Allow header names the right one (`POST` on operations and `/_transactions`, `GET` on `/_contract`). | Use the method the Allow header names. |
|
|
43
|
+
| [`A2013`](https://aventara-framework.vercel.app/docs/error-codes#a2013) | Concurrent modification. | A competing writer reached the state first; nothing was written. | Retry. No retry is automatic. |
|
|
44
|
+
| [`A2014`](https://aventara-framework.vercel.app/docs/error-codes#a2014) | Stale selection. | The selected state changed before the write; nothing was written. | Read again, then retry. |
|
|
45
|
+
|
|
46
|
+
## Server errors (A3xxx)
|
|
47
|
+
|
|
48
|
+
Not caller-actionable: `cause.message` is generic, and the real error goes to the root `diagnostics` sink.
|
|
49
|
+
|
|
50
|
+
| Code | Meaning | Cause | Fix |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| [`A3000`](https://aventara-framework.vercel.app/docs/error-codes#a3000) | Internal error. | An unexpected failure on the server: a stage threw an error that is not an AvError, an interceptor called next() twice, a compute stage reached a write through its read-only ctx, a compute stage returned a value of the wrong type, or a compute.create returned undefined for a required field. cause.message is always "Internal framework error." | Read the real error in the diagnostics sink (the root `diagnostics` key) or the server log line for the request id, and fix that stage. To answer with a code of your choice, throw an AvError. |
|
|
53
|
+
| [`A3001`](https://aventara-framework.vercel.app/docs/error-codes#a3001) | Adapter error. | A database or ORM failure that is not something the caller can fix. cause.message is always "Adapter execution failed." | Read the raw error in the diagnostics sink; check the database, its connection and the migration state. |
|
|
54
|
+
| [`A3002`](https://aventara-framework.vercel.app/docs/error-codes#a3002) | Transaction error. | Transaction infrastructure failed (commit or rollback, or the transaction expired or was closed under a step), or a Resource transaction interceptor called next() twice or resolved without calling it. | Read the diagnostics sink. Call next() exactly once in a transaction interceptor; retry an infrastructure failure. |
|
|
55
|
+
| [`A3003`](https://aventara-framework.vercel.app/docs/error-codes#a3003) | Serialization error. | A result could not be encoded for transport. | Read the diagnostics sink; make compute.read stages and Json values return plain data of the field's type. |
|
|
56
|
+
| [`A3004`](https://aventara-framework.vercel.app/docs/error-codes#a3004) | Pipe output invalid. | A server pipe, field pipe or transaction pipe (or write compute) returned arguments the server's own contract refuses: a field the Resource does not have, a wrong type, a dropped required field, a reference written together with its relation (V1012), or a changed plan. cause.message is always "A server pipeline produced invalid arguments." | Read the server log line, which names the pipe, the operation and the path, and fix the pipe. A pipe returns a new object of the same shape, or undefined to keep the arguments. |
|
|
57
|
+
|
|
58
|
+
## Authentication and authorization (A4xxx)
|
|
59
|
+
|
|
60
|
+
Produced by your guards; the framework never authenticates by itself.
|
|
61
|
+
|
|
62
|
+
| Code | Meaning | Cause | Fix |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| [`A4000`](https://aventara-framework.vercel.app/docs/error-codes#a4000) | Unauthenticated. | A guard threw new AvError("A4000", ...): authentication is required or failed. The framework never authenticates by itself. | Send the credentials your guards expect, such as the Authorization header. |
|
|
65
|
+
| [`A4001`](https://aventara-framework.vercel.app/docs/error-codes#a4001) | Forbidden. | A guard returned false ("Operation denied by a framework guard."), a field write guard or a nested write's target guard included, or a stage threw A4001. | Call as a caller the guard admits, or stop sending the guarded field. To answer another code, throw an AvError from the guard. |
|
|
66
|
+
| [`A4002`](https://aventara-framework.vercel.app/docs/error-codes#a4002) | Policy denied. | A guard or hook threw new AvError("A4002", ...) with a policy reason of your own. | Read cause.message for the policy's reason. |
|
|
67
|
+
|
|
68
|
+
## Validation issues (V1xxx)
|
|
69
|
+
|
|
70
|
+
Each entry of `cause.issues[]` under `A2001`, `A2002`, `A2004`, `A2007` or `A2009`: `{ code, path, message }`, plus `check` and `params` for a failed field validator.
|
|
71
|
+
|
|
72
|
+
| Code | Meaning | Cause | Fix |
|
|
73
|
+
|---|---|---|---|
|
|
74
|
+
| [`V1000`](https://aventara-framework.vercel.app/docs/error-codes#v1000) | Required value missing. | A required value is absent: a create leaves out a required field that has no database default and no compute.create (the path names the field), a required relation and its reference field are both missing, a typed Json field lacks a required key, or a $ref has no fingerprint. | Send the value at the issue's path. When the server must fill it, give the field a compute.create stage or a database default; a pipe cannot fill it. |
|
|
75
|
+
| [`V1001`](https://aventara-framework.vercel.app/docs/error-codes#v1001) | Invalid runtime/wire type. | A value of the wrong type: a string where a number is expected, a negative limit, a non-integer id, null given to an operator with no null meaning (gt, contains, in), two directives in one scalar action object (`{ $increment: 1, $decrement: 2 }`), or a server-side value that is not plain data. | Send the type the contract states at that path (the generated client's types say it). |
|
|
76
|
+
| [`V1002`](https://aventara-framework.vercel.app/docs/error-codes#v1002) | Invalid enum value. | A value that is not a member of the enum, or outside a typed Json field's AvZ.enum or AvZ.literal. | Send one of the enum's members. |
|
|
77
|
+
| [`V1003`](https://aventara-framework.vercel.app/docs/error-codes#v1003) | Invalid format (for example UUID/DateTime/base64). | A malformed datetime, decimal, base64 or UUID string, or a failed pattern or format validator (`check: "string_format"`). | Send the canonical encoding (ISO-8601 UTC datetime, decimal string, base64 bytes) and satisfy the validator that check and params name. |
|
|
78
|
+
| [`V1004`](https://aventara-framework.vercel.app/docs/error-codes#v1004) | Value outside accepted range. | A number, length or size outside its range: a failed bound, length or size validator, a BigInt outside signed 64 bits, an AvZ.int() outside the safe integer range, or a transaction with no operations. | Send a value inside the range; check and params name the validator that failed. |
|
|
79
|
+
| [`V1005`](https://aventara-framework.vercel.app/docs/error-codes#v1005) | Unknown or inaccessible field. | A field that does not exist or is hidden in the caller's layer, an unknown Resource (under A2001), or a key a typed Json field's shape does not declare. | Use a field the caller's contract exposes, expose it through exemptions, or make the call from server code. |
|
|
80
|
+
| [`V1006`](https://aventara-framework.vercel.app/docs/error-codes#v1006) | Unsupported filter/order/mutation directive. | An operator, directive or nested write the field does not offer (at any depth, inside not too; a to-many $update's data takes only what a bulk write takes), a mode it does not offer, or an operation that is not available (under A2002). | Use what the contract lists for that field or Resource, or expose it through exemptions. |
|
|
81
|
+
| [`V1007`](https://aventara-framework.vercel.app/docs/error-codes#v1007) | Invalid unique identifier shape. | The where of a unique operation is not exactly one complete identifier. | Pass exactly one identifier: the id, or every field of one @unique or @@unique. |
|
|
82
|
+
| [`V1008`](https://aventara-framework.vercel.app/docs/error-codes#v1008) | Invalid select/include/reducer projection. | Both select and include, a write-only field selected, or a relation that is not selectable there. | Use select or include, not both, and select only readable fields. |
|
|
83
|
+
| [`V1009`](https://aventara-framework.vercel.app/docs/error-codes#v1009) | Invalid or non-deterministic cursor. | A cursor without an explicit orderBy, or cursor keys that do not match the ordered fields. | Give an orderBy and a cursor holding exactly its ordered fields. |
|
|
84
|
+
| [`V1010`](https://aventara-framework.vercel.app/docs/error-codes#v1010) | Invalid transaction reference path. | A $ref path the earlier step does not return, a $ref to a step that is not in the transaction, or to a step with no single row (a many or count step). | Select the referenced field in the earlier step, and reference a single-row step. |
|
|
85
|
+
| [`V1011`](https://aventara-framework.vercel.app/docs/error-codes#v1011) | Transaction reference type mismatch. | The referenced value's type does not fit its target, a nullable source where only a non-null value is accepted included. | Reference a field of the target's type, non-null where the target needs it. |
|
|
86
|
+
| [`V1012`](https://aventara-framework.vercel.app/docs/error-codes#v1012) | Invalid relation action/cardinality. | A nested write that does not fit the relation (a list where one record is expected), a reference field set together with its own relation, one record mixing reference fields and relation writes where the adapter cannot run both, or a record written through a relation that writes that relation (or a field holding its reference) back, where the adapter admits no such write. | Write either the relation or its reference field, in the relation's cardinality. |
|
|
87
|
+
| [`V1013`](https://aventara-framework.vercel.app/docs/error-codes#v1013) | Maximum nesting depth exceeded. | Nested projections, filters or writes deeper than maxNestingDepth. | Nest less, or raise limits.maxNestingDepth. |
|
|
88
|
+
| [`V1014`](https://aventara-framework.vercel.app/docs/error-codes#v1014) | Configured limit exceeded. | A limit above maxListLimit, or more steps than maxTransactionOperations. | Paginate or split the plan, or raise the limit. |
|
|
89
|
+
| [`V1015`](https://aventara-framework.vercel.app/docs/error-codes#v1015) | Boolean/filter node limit exceeded. | A where tree with more nodes than maxBooleanNodes. | Simplify the filter (in instead of many OR clauses), or raise limits.maxBooleanNodes. |
|
|
90
|
+
| [`V1016`](https://aventara-framework.vercel.app/docs/error-codes#v1016) | Invalid transaction reference index. | A $ref to the referencing step itself or a later one, or to an index no step has. | Reference an earlier step only. |
|
|
91
|
+
| [`V1017`](https://aventara-framework.vercel.app/docs/error-codes#v1017) | Transaction fingerprint mismatch. | A hand-written plan whose fingerprint does not match its step. | Let the generated client, framework.appTx or framework.clientTx build the plan. |
|
|
92
|
+
| [`V1018`](https://aventara-framework.vercel.app/docs/error-codes#v1018) | Transaction reference resolved to no value. | The referenced step returned no value at that path, such as a first step that matched no row. | Make sure the earlier step returns a row, or use a unique step, which fails with A2003. |
|
|
93
|
+
| [`V1019`](https://aventara-framework.vercel.app/docs/error-codes#v1019) | Transaction step depends on a row removed by an earlier step's cascade | A later step uses a row that an earlier step's delete removed through a database cascade. | Reorder or drop the dependent step. |
|
|
94
|
+
| [`V1020`](https://aventara-framework.vercel.app/docs/error-codes#v1020) | Transaction mixes application and client steps. | One framework.transaction([...]) list mixes appTx and clientTx steps. | Build every step of a plan from one origin. |
|
|
95
|
+
| [`V1021`](https://aventara-framework.vercel.app/docs/error-codes#v1021) | Json value depth limit exceeded. | A Json field's value nests deeper than maxJsonDepth. | Flatten the value, or raise limits.maxJsonDepth. |
|
|
96
|
+
|
|
97
|
+
## Startup: configuration
|
|
98
|
+
|
|
99
|
+
`createFramework` (and `AventaraModule`) throws one `AvConstructionError` with `stage: "configuration"`; each diagnostic is `{ code, path, message, docs }`.
|
|
100
|
+
|
|
101
|
+
| Code | Meaning | Cause | Fix |
|
|
102
|
+
|---|---|---|---|
|
|
103
|
+
| [`CONFIG_REQUIRED`](https://aventara-framework.vercel.app/docs/error-codes#config_required) | A required configuration key is missing. | A key that must be present is absent: api, adapter, or an overlay's exposed when the overlay sets exemptions. | Add the key at the diagnostic's path. |
|
|
104
|
+
| [`CONFIG_UNKNOWN_PROPERTY`](https://aventara-framework.vercel.app/docs/error-codes#config_unknown_property) | Unknown configuration key. | A key the configuration does not define at that place: a typo, or a key written at the wrong level of the tree. | Remove it, or move it where it belongs (the configuration reference's "Where a key can go"). |
|
|
105
|
+
| [`CONFIG_INVALID_TYPE`](https://aventara-framework.vercel.app/docs/error-codes#config_invalid_type) | A configuration key has the wrong type. | A value of the wrong type, or a key set to an explicit undefined. | Give the value the type the reference states; leave a key out rather than setting it to undefined. |
|
|
106
|
+
| [`CONFIG_INVALID_VALUE`](https://aventara-framework.vercel.app/docs/error-codes#config_invalid_value) | A configuration value is not allowed. | A malformed api or mcp path, a negative limit, an unknown transactionsStyle, a format that is not a named zod format, or forceTransaction under an effective transactionsStyle "none". | Use a value the message allows. |
|
|
107
|
+
| [`CONFIG_MOVED_PROPERTY`](https://aventara-framework.vercel.app/docs/error-codes#config_moved_property) | A configuration key moved in rc.1. | Configuration written for 1.0.0-rc.0: restrictions, entrypoint, fields, behaviors, transactions or pipelines.before/after. | Move it where the message says: restrictions to exposed + exemptions, entrypoint to api, fields to `resources.<R>.fields.<f>.definition`, transactions to transactionsStyle, before/after to pipelines.hooks. |
|
|
108
|
+
| [`CONFIG_FORBIDDEN_IN_OVERLAY`](https://aventara-framework.vercel.app/docs/error-codes#config_forbidden_in_overlay) | A root-only key is written in an overlay. | A field definition in application, client or agent. | Define the field once at the root (`resources.<R>.fields.<f>.definition`); an overlay only chooses its visibility. |
|
|
109
|
+
|
|
110
|
+
## Startup: discovery
|
|
111
|
+
|
|
112
|
+
`AvConstructionError` with `stage: "discovery"`: what the adapter reported does not hold together.
|
|
113
|
+
|
|
114
|
+
| Code | Meaning | Cause | Fix |
|
|
115
|
+
|---|---|---|---|
|
|
116
|
+
| [`DISCOVERY_TRANSACTION_CAPABILITY_MISMATCH`](https://aventara-framework.vercel.app/docs/error-codes#discovery_transaction_capability_mismatch) | The adapter's transaction capability does not match it. | The adapter declares interactive transactions but provides no transaction function. | A defect of the adapter: install the adapter release that matches @aventara/core exactly. |
|
|
117
|
+
|
|
118
|
+
## Startup: contract compilation
|
|
119
|
+
|
|
120
|
+
`AvConstructionError` with `stage: "contract-compilation"`; a diagnostic's `path` starts with its layer (`root.`, `application.`, `client.`, `agent.`).
|
|
121
|
+
|
|
122
|
+
| Code | Meaning | Cause | Fix |
|
|
123
|
+
|---|---|---|---|
|
|
124
|
+
| [`COMPILER_UNKNOWN_RESOURCE`](https://aventara-framework.vercel.app/docs/error-codes#compiler_unknown_resource) | Unknown Resource. | A Resource name the adapter's model does not have, in resources or in exemptions. | Use the model's exact name. After a schema change run `npm run aventara:prepare`. |
|
|
125
|
+
| [`COMPILER_UNKNOWN_FIELD`](https://aventara-framework.vercel.app/docs/error-codes#compiler_unknown_field) | Unknown field. | A field that is neither in the schema nor defined at the root. | Fix the name, run aventara:prepare after a schema change, or add the field with a root definition. |
|
|
126
|
+
| [`COMPILER_RELATION_FIELD_CONFIG_UNSUPPORTED`](https://aventara-framework.vercel.app/docs/error-codes#compiler_relation_field_config_unsupported) | A relation field takes write guards only. | Field pipes or compute on a relation field. | Put the stage on the relation's reference field, or use an operation pipe. |
|
|
127
|
+
| [`COMPILER_INCOMPLETE_FIELD`](https://aventara-framework.vercel.app/docs/error-codes#compiler_incomplete_field) | An added field is incomplete. | A definition that adds a field leaves out type, nullable or list. | Spell out type, nullable and list, with lifecycle: ["VIRTUAL"] for a field that has no column. |
|
|
128
|
+
| [`COMPILER_STRUCTURAL_FIELD_OVERRIDE`](https://aventara-framework.vercel.app/docs/error-codes#compiler_structural_field_override) | A discovered field's structure was redefined. | A definition on a field the adapter discovered changes its type, format or capabilities. | Restate only its type, nullable and list. Change capabilities through exemptions and the column through the schema. |
|
|
129
|
+
| [`COMPILER_MISSING_SCALAR`](https://aventara-framework.vercel.app/docs/error-codes#compiler_missing_scalar) | Unknown scalar type. | A definition names a scalar type that does not exist. | Use a built-in scalar: string, boolean, int, float, bigint, decimal, datetime, bytes or json. |
|
|
130
|
+
| [`COMPILER_MISSING_ENUM`](https://aventara-framework.vercel.app/docs/error-codes#compiler_missing_enum) | Unknown enum. | A definition names an enum the model does not have. | Use an enum of the schema, and run aventara:prepare after adding one. |
|
|
131
|
+
| [`COMPILER_DANGLING_RELATION`](https://aventara-framework.vercel.app/docs/error-codes#compiler_dangling_relation) | A visible relation points at a hidden Resource. | A layer hides a Resource but still exposes a relation field that targets it. | Hide the relation field too (`exemptions.resources.<R>.fields.<relation>: true`), or expose the target. |
|
|
132
|
+
| [`COMPILER_UNAVAILABLE_OPERATION`](https://aventara-framework.vercel.app/docs/error-codes#compiler_unavailable_operation) | Operation not available. | An operation the adapter does not offer (such as delete.many), or an overlay granting one that its parent hid. | Remove it. To reach an operation from one overlay only, expose it at the root and hide it in the other overlay. |
|
|
133
|
+
| [`COMPILER_UNAVAILABLE_CAPABILITY`](https://aventara-framework.vercel.app/docs/error-codes#compiler_unavailable_capability) | Capability not available. | A capability or token the field does not offer, or an overlay (or the agent layer) granting one its parent hid. Overlays only narrow. | Remove it, or expose it in the parent layer and hide it in the layer it must not reach. |
|
|
134
|
+
| [`COMPILER_INVALID_LIFECYCLE`](https://aventara-framework.vercel.app/docs/error-codes#compiler_invalid_lifecycle) | Invalid field lifecycle. | An added field declares a lifecycle other than VIRTUAL, a VIRTUAL field claims a database-generated lifecycle or a provider-side capability, or a definition redefines a discovered field's lifecycle. | Give an added field lifecycle: ["VIRTUAL"] and only the capabilities it can serve; computed lifecycles come from compute stages. |
|
|
135
|
+
| [`COMPILER_MISSING_READ_COMPUTE`](https://aventara-framework.vercel.app/docs/error-codes#compiler_missing_read_compute) | A selectable virtual field has no compute.read. | A VIRTUAL field that can be selected has no pipelines.compute.read stage. | Add a compute.read stage, or make the field write-only with capabilities.select: false. |
|
|
136
|
+
| [`COMPILER_INVALID_COMPUTE_DEPENDENCY`](https://aventara-framework.vercel.app/docs/error-codes#compiler_invalid_compute_dependency) | Invalid compute.read dependency. | A dependsOn entry that is not a stored scalar field of the same Resource. | List only stored scalar fields of the Resource in dependsOn. |
|
|
137
|
+
| [`COMPILER_RESERVED_RESOURCE_NAME`](https://aventara-framework.vercel.app/docs/error-codes#compiler_reserved_resource_name) | Reserved Resource name. | A model name starts with $, which is reserved for the framework's own names (ctx.$sql, $ref). | Rename the model; the table it maps to can keep its name. |
|
|
138
|
+
| [`COMPILER_MISSING_IDENTIFIER`](https://aventara-framework.vercel.app/docs/error-codes#compiler_missing_identifier) | A unique operation has no identifier. | A layer exposes a unique variant (find.unique, update.unique, delete.unique, upsert.unique) and no complete identifier of the Resource: a Pick that did not pick one, or an Omit that hid every one. | Expose an identifier field (id, or every field of one unique), or exempt the unique variants. |
|
|
139
|
+
| [`COMPILER_HIDDEN_REQUIRED_FIELD`](https://aventara-framework.vercel.app/docs/error-codes#compiler_hidden_required_field) | A layer that creates a Resource hides a required field. | A layer still creates the Resource (a create or upsert variant, or a nested create) while a required field, or a required relation's reference field, is hidden or has its create capability exempted, and no compute.create fills it. | Expose the field, give it a compute.create stage, or exempt the layer's create operations. |
|
|
140
|
+
| [`COMPILER_INVALID_VALIDATOR`](https://aventara-framework.vercel.app/docs/error-codes#compiler_invalid_validator) | A field validator cannot apply. | A validator that is not built with AvZ, does not fit the field's type (AvZ.maxLength on an int, AvZ.maxItems on a field that is not a list), or sits on a relation or Json field. | Use an AvZ check that fits the field's type; a Json field's checks go in its json shape. |
|
|
141
|
+
| [`COMPILER_INVALID_JSON_SHAPE`](https://aventara-framework.vercel.app/docs/error-codes#compiler_invalid_json_shape) | A Json shape cannot apply. | A json shape on a field that is not a discovered Json field, a shape not built with AvZ, or one that admits null or absence at its top level. | Put the shape on a Json field, build it with AvZ, and keep its top level non-null (the column's nullability decides null). |
|
|
142
|
+
| [`COMPILER_INVALID_CLIENT_CONTRACT`](https://aventara-framework.vercel.app/docs/error-codes#compiler_invalid_client_contract) | The compiled client contract is invalid. | The finished client contract failed its own structural check: a framework defect. | Report it with the diagnostic's path and message. |
|
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ of generic JSON. So does a client generated with `avclient`, which emits the typ
|
|
|
53
53
|
|
|
54
54
|
The shape is also one of `meta`'s validators. Every write of `meta` is checked against it before the database is
|
|
55
55
|
asked: a create, update, upsert, nested write or transaction step, including a value a transaction step takes from an
|
|
56
|
-
earlier step. It is checked where validators run, after every pipe and before any interceptor or hook, and its issues
|
|
56
|
+
earlier step. It is checked where validators run, after every pipe and write compute and before any interceptor or hook, and its issues
|
|
57
57
|
come in field order with the other validators'. A value out of shape is refused with `A2004`, one issue per problem at its
|
|
58
58
|
exact path:
|
|
59
59
|
|
|
@@ -118,6 +118,19 @@ try {
|
|
|
118
118
|
generated client throws the same classes by the same table. `framework.execute` and `framework.executeTransaction`, the
|
|
119
119
|
entries a transport calls, still answer the envelope.
|
|
120
120
|
|
|
121
|
+
## Every code, explained
|
|
122
|
+
|
|
123
|
+
`AvErrorCatalog` holds every code this package answers, reports in a validation issue, or refuses to start with: its
|
|
124
|
+
`kind`, `message`, `cause`, `fix` and `docs` URL. The same catalog ships beside `dist` as `ERRORS.md`, and each entry
|
|
125
|
+
of a startup `AvConstructionError`'s `diagnostics` carries its code's `docs` URL.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { AvErrorCatalog } from "@aventara/core";
|
|
129
|
+
|
|
130
|
+
AvErrorCatalog.A2004.fix; // "Fix the argument each issue's path points at, …"
|
|
131
|
+
AvErrorCatalog.V1000.docs; // "https://aventara-framework.vercel.app/docs/error-codes#v1000"
|
|
132
|
+
```
|
|
133
|
+
|
|
121
134
|
## Required create fields are typed
|
|
122
135
|
|
|
123
136
|
Each Resource's Contract states what a create cannot do without (`createRequirements`), as the adapter reads it off
|
|
@@ -149,6 +162,11 @@ it a `compute.create`, or exempt `create.many` and `create.count` in that layer
|
|
|
149
162
|
A field your operation pipe fills server-side is now required of the caller. Give it a `compute.create` stage (or a
|
|
150
163
|
database default) so callers can leave it out, or have them pass it.
|
|
151
164
|
|
|
165
|
+
Inside the pipeline the requirements hold from write compute on. A variant's guards, pipes and filters run before
|
|
166
|
+
compute, and their typed arguments require no field of a create, since compute may still fill it: `args.data.slug` is
|
|
167
|
+
`string | undefined` in a pipe, and a pipe may return a create without it. The variant's hooks and interceptors run
|
|
168
|
+
after compute and see every required field present.
|
|
169
|
+
|
|
152
170
|
## Returning a façade from a method
|
|
153
171
|
|
|
154
172
|
A Nest project emits declarations (`declaration: true`), so a method without a return type has its type written out.
|
|
@@ -181,6 +199,11 @@ A variant (`this.framework.application.Post.find.many`) is a call type the decla
|
|
|
181
199
|
return type, or return the call's result, whose row type is small. An application that publishes no declarations can
|
|
182
200
|
set `"declaration": false` and skip the question.
|
|
183
201
|
|
|
202
|
+
The configuration itself costs almost nothing to declare. The Prisma adapter's discovery artifact names the model's
|
|
203
|
+
type (`AvModel`), so the scaffold's `aventaraConfig()`, a `buildFramework` and its `AppFramework` alias write
|
|
204
|
+
`AvAdapter<AvModel>` and `AvFramework<AvModel, …>`: about 0.5 KB at any schema size, where rc.1 wrote
|
|
205
|
+
the model out once per mention (1.2 MB over 50 Resources).
|
|
206
|
+
|
|
184
207
|
An exported `defineResource(...)` result, and an exported configuration or Framework builder holding typed stages,
|
|
185
208
|
need no annotation: their stages are written by their public types (`readonly AvGuard[]`, `readonly AvPipe[]`, …),
|
|
186
209
|
never with the model the inline stages were typed from.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ErrorCodeDescription } from "../runtime/error-guidance.js";
|
|
2
|
+
import type { AvAdapterDiscoveryDiagnosticCode } from "./adapter-discovery-diagnostic.js";
|
|
3
|
+
/** Meaning, cause and fix of every discovery diagnostic code. */
|
|
4
|
+
export declare const ADAPTER_DISCOVERY_DIAGNOSTIC_GUIDANCE: {
|
|
5
|
+
readonly [C in AvAdapterDiscoveryDiagnosticCode]: ErrorCodeDescription;
|
|
6
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const ADAPTER_DISCOVERY_DIAGNOSTIC_GUIDANCE = {
|
|
2
|
+
DISCOVERY_TRANSACTION_CAPABILITY_MISMATCH: {
|
|
3
|
+
message: "The adapter's transaction capability does not match it.",
|
|
4
|
+
cause: "The adapter declares interactive transactions but provides no transaction function.",
|
|
5
|
+
fix: "A defect of the adapter: install the adapter release that matches @aventara/core exactly.",
|
|
6
|
+
},
|
|
7
|
+
};
|
|
@@ -24,6 +24,20 @@ export type AvAdapterRelationField<M extends AvAdapterModel> = {
|
|
|
24
24
|
readonly cardinality: "one" | "many";
|
|
25
25
|
readonly nullable: boolean;
|
|
26
26
|
readonly capabilities: AvRelationFieldCapabilities;
|
|
27
|
+
/**
|
|
28
|
+
* The target's relation field that is this relation's other end, when the data
|
|
29
|
+
* source pairs them (`User.posts` ↔ `Post.author`; a self-relation's two ends
|
|
30
|
+
* name each other). Absent: no pairing is stated, and no write is read as
|
|
31
|
+
* walking a relation back.
|
|
32
|
+
*/
|
|
33
|
+
readonly inverse?: string;
|
|
34
|
+
/**
|
|
35
|
+
* The scalar fields of this Resource that hold the relation's reference, in
|
|
36
|
+
* the order the data source pairs them with the target's key
|
|
37
|
+
* (`Post.author` → `["authorId"]`), on an optional relation as on a required
|
|
38
|
+
* one. Absent on the end that holds no reference (`User.posts`); never empty.
|
|
39
|
+
*/
|
|
40
|
+
readonly referenceFields?: readonly [string, ...string[]];
|
|
27
41
|
};
|
|
28
42
|
/** One field the adapter discovered: a scalar or a relation. */
|
|
29
43
|
export type AvAdapterField<M extends AvAdapterModel> = AvAdapterScalarField<M> | AvAdapterRelationField<M>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AvConfigComputedFields } from "../config/computed-fields.js";
|
|
1
2
|
import type { AvEnumDefinition } from "../contracts/enum-definition.js";
|
|
2
3
|
import type { ReferenceWriteLimit } from "../contracts/reference-write-limit.js";
|
|
3
4
|
import type { AvScalarRegistry } from "../contracts/scalar.js";
|
|
@@ -23,4 +24,12 @@ export interface AvAdapterModel {
|
|
|
23
24
|
* either way. Compiled into every Contract.
|
|
24
25
|
*/
|
|
25
26
|
readonly referenceWrites?: ReferenceWriteLimit;
|
|
27
|
+
/**
|
|
28
|
+
* Type-only, never present at runtime: which fields the configuration's write
|
|
29
|
+
* compute fills, as an adapter's generate step read them off the
|
|
30
|
+
* configuration's type. A variant's guards, pipes and filters may leave
|
|
31
|
+
* exactly those out of a create. Absent: any create field may be computed, so
|
|
32
|
+
* those stages require none.
|
|
33
|
+
*/
|
|
34
|
+
readonly "~computedFields"?: AvConfigComputedFields;
|
|
26
35
|
}
|