@aventara/core 0.1.0-pilot.4 → 1.0.0-rc.1
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 +1478 -0
- package/README.md +275 -11
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
- package/dist/adapters/adapter-field.d.ts +11 -11
- package/dist/adapters/adapter-identifier.d.ts +6 -6
- package/dist/adapters/adapter-model.d.ts +17 -9
- package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
- package/dist/adapters/adapter-operation.d.ts +3 -3
- package/dist/adapters/adapter-resource.d.ts +18 -9
- package/dist/adapters/adapter-sql-statement.d.ts +19 -0
- package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
- package/dist/adapters/adapter.interface.d.ts +49 -25
- package/dist/avz/avz-check.factory.d.ts +83 -0
- package/dist/avz/avz-check.factory.js +116 -0
- package/dist/avz/avz-check.registry.d.ts +35 -0
- package/dist/avz/avz-check.registry.js +9 -0
- package/dist/avz/avz.d.ts +203 -0
- package/dist/avz/avz.js +354 -0
- package/dist/avz/index.d.ts +7 -0
- package/dist/avz/index.js +1 -0
- package/dist/avz/json-shape.registry.d.ts +22 -0
- package/dist/avz/json-shape.registry.js +16 -0
- package/dist/avz/zod-issue.translator.d.ts +57 -0
- package/dist/avz/zod-issue.translator.js +149 -0
- package/dist/compiler/contract-compiler-diagnostic.d.ts +37 -5
- package/dist/compiler/contract.compiler.d.ts +18 -18
- package/dist/compiler/contract.compiler.js +595 -421
- package/dist/compiler/create-requirements.projector.d.ts +49 -0
- package/dist/compiler/create-requirements.projector.js +423 -0
- package/dist/compiler/field-validator.compiler.d.ts +47 -0
- package/dist/compiler/field-validator.compiler.js +238 -0
- package/dist/config/any-framework-config.d.ts +19 -12
- package/dist/config/config-diagnostic.d.ts +11 -4
- package/dist/config/config-layer.d.ts +15 -6
- package/dist/config/config.resolver.d.ts +75 -29
- package/dist/config/config.resolver.js +109 -44
- package/dist/config/config.validator.d.ts +7 -7
- package/dist/config/config.validator.js +586 -71
- package/dist/config/define-resource.d.ts +57 -0
- package/dist/config/define-resource.js +14 -0
- package/dist/config/effective-fields.d.ts +19 -23
- package/dist/config/entrypoint.normalizer.d.ts +1 -1
- package/dist/config/exact-framework-config.d.ts +280 -0
- package/dist/config/field-compute.resolver.d.ts +19 -0
- package/dist/config/field-compute.resolver.js +115 -0
- package/dist/config/field-pipeline-config.d.ts +47 -0
- package/dist/config/forced-operation.validator.d.ts +10 -0
- package/dist/config/forced-operation.validator.js +66 -0
- package/dist/config/framework-config.d.ts +111 -37
- package/dist/config/framework-exemptions-config.d.ts +91 -0
- package/dist/config/framework-exemptions-config.js +1 -0
- package/dist/config/framework-field-definition.d.ts +78 -0
- package/dist/config/framework-field-definition.js +28 -0
- package/dist/config/framework-resources-config.d.ts +116 -0
- package/dist/config/framework-resources-config.js +1 -0
- package/dist/config/pipeline-config.d.ts +56 -20
- package/dist/config/pipeline-tree.resolver.d.ts +79 -0
- package/dist/config/pipeline-tree.resolver.js +220 -0
- package/dist/config/stage-type.eraser.d.ts +65 -0
- package/dist/config/stage-type.eraser.js +1 -0
- package/dist/contracts/capability.d.ts +11 -11
- package/dist/contracts/client-contract-structure.validator.d.ts +344 -29
- package/dist/contracts/client-contract-structure.validator.js +248 -7
- package/dist/contracts/contract-canonicalizer.d.ts +19 -3
- package/dist/contracts/contract-canonicalizer.js +172 -27
- package/dist/contracts/contract-hash.d.ts +12 -7
- package/dist/contracts/contract-hash.js +3 -2
- package/dist/contracts/contract-limits.d.ts +4 -1
- package/dist/contracts/contract-limits.js +1 -0
- package/dist/contracts/contract-lookup.d.ts +5 -5
- package/dist/contracts/contract-protocol.d.ts +4 -4
- package/dist/contracts/contract.d.ts +31 -16
- package/dist/contracts/create-requirements.d.ts +35 -0
- package/dist/contracts/create-requirements.js +1 -0
- package/dist/contracts/enum-definition.d.ts +1 -1
- package/dist/contracts/field-check.d.ts +101 -0
- package/dist/contracts/field-check.js +1 -0
- package/dist/contracts/field.d.ts +61 -41
- package/dist/contracts/identifier.d.ts +4 -4
- package/dist/contracts/json-shape.d.ts +76 -0
- package/dist/contracts/json-shape.js +1 -0
- package/dist/contracts/lifecycle-trait.d.ts +2 -2
- package/dist/contracts/reference-write-limit.d.ts +7 -0
- package/dist/contracts/reference-write-limit.js +1 -0
- package/dist/contracts/resource-operations.d.ts +28 -28
- package/dist/contracts/resource.d.ts +15 -9
- package/dist/contracts/scalar-value-type.d.ts +25 -14
- package/dist/contracts/scalar.d.ts +3 -3
- package/dist/contracts/transaction-capability.d.ts +1 -1
- package/dist/contracts/wire-scalar-format.d.ts +4 -4
- package/dist/framework/contract-view.d.ts +234 -63
- package/dist/framework/framework-construction.error.d.ts +10 -10
- package/dist/framework/framework-construction.error.js +2 -2
- package/dist/framework/framework.d.ts +50 -24
- package/dist/framework/framework.factory.d.ts +8 -6
- package/dist/framework/framework.factory.js +95 -32
- package/dist/framework/operation-facade.d.ts +123 -52
- package/dist/framework/operation-facade.js +9 -1
- package/dist/index.d.ts +55 -82
- package/dist/index.js +6 -26
- package/dist/internal.d.ts +40 -0
- package/dist/internal.js +18 -0
- package/dist/operations/operation-arguments.d.ts +316 -201
- package/dist/operations/operation-call.d.ts +27 -28
- package/dist/operations/operation-identity.d.ts +12 -10
- package/dist/operations/operation-identity.js +3 -0
- package/dist/operations/operation-result.d.ts +21 -21
- package/dist/operations/operation-validator-core.d.ts +6 -6
- package/dist/operations/operation-validator-core.js +41 -12
- package/dist/operations/operation-validator.d.ts +24 -9
- package/dist/operations/operation-validator.js +26 -10
- package/dist/operations/projection-scope.resolver.d.ts +7 -7
- package/dist/operations/structural-limits.walker.d.ts +19 -0
- package/dist/operations/structural-limits.walker.js +489 -0
- package/dist/operations/validation-code.d.ts +11 -6
- package/dist/operations/validation-code.js +4 -0
- package/dist/operations/write-scope.resolver.d.ts +6 -6
- package/dist/protocol/absent-route.responder.d.ts +13 -11
- package/dist/protocol/absent-route.responder.js +42 -16
- package/dist/protocol/contract-document.responder.d.ts +3 -3
- package/dist/protocol/contract-document.responder.js +2 -2
- package/dist/protocol/http-request.decoder.d.ts +23 -13
- package/dist/protocol/http-request.decoder.js +19 -11
- package/dist/protocol/http-request.reader.d.ts +3 -3
- package/dist/protocol/http-response.encoder.d.ts +4 -4
- package/dist/protocol/http-response.encoder.js +94 -55
- package/dist/protocol/http-status.table.d.ts +2 -2
- package/dist/protocol/operation-response.guard.d.ts +2 -2
- package/dist/protocol/protocol-surface.d.ts +4 -4
- package/dist/protocol/protocol.binder.d.ts +52 -27
- package/dist/protocol/protocol.binder.js +14 -9
- package/dist/protocol/protocol.handler.d.ts +28 -15
- package/dist/protocol/protocol.handler.js +55 -10
- package/dist/protocol/unmounted-route.error.d.ts +19 -0
- package/dist/protocol/unmounted-route.error.js +10 -0
- package/dist/runtime/caller-data.copier.d.ts +59 -0
- package/dist/runtime/caller-data.copier.js +470 -0
- package/dist/runtime/caller-data.door.d.ts +56 -0
- package/dist/runtime/caller-data.door.js +67 -0
- package/dist/runtime/computed-read-runtime.d.ts +7 -6
- package/dist/runtime/computed-read-runtime.js +61 -55
- package/dist/runtime/computed-value.error.d.ts +13 -0
- package/dist/runtime/computed-value.error.js +9 -0
- package/dist/runtime/context-executor.runtime.d.ts +59 -0
- package/dist/runtime/context-executor.runtime.js +275 -0
- package/dist/runtime/create-requirement.d.ts +22 -57
- package/dist/runtime/create-requirement.js +140 -37
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +1 -1
- package/dist/runtime/diagnostics-reporter.d.ts +64 -22
- package/dist/runtime/diagnostics-reporter.js +35 -4
- package/dist/runtime/error-boundary.d.ts +73 -8
- package/dist/runtime/error-boundary.js +104 -48
- package/dist/runtime/execution-options.d.ts +10 -2
- package/dist/runtime/field-check.evaluator.d.ts +29 -0
- package/dist/runtime/field-check.evaluator.js +521 -0
- package/dist/runtime/field-compute.d.ts +64 -0
- package/dist/runtime/field-compute.js +1 -0
- package/dist/runtime/field-stage.plan.d.ts +15 -0
- package/dist/runtime/field-stage.plan.js +27 -0
- package/dist/runtime/field-stage.runtime.d.ts +35 -0
- package/dist/runtime/field-stage.runtime.js +143 -0
- package/dist/runtime/field-validator.runtime.d.ts +11 -0
- package/dist/runtime/field-validator.runtime.js +87 -0
- package/dist/runtime/framework-error-class.table.d.ts +33 -0
- package/dist/runtime/framework-error-class.table.js +25 -0
- package/dist/runtime/framework-error.d.ts +79 -7
- package/dist/runtime/framework-error.factory.d.ts +4 -0
- package/dist/runtime/framework-error.factory.js +15 -0
- package/dist/runtime/framework-error.js +23 -2
- package/dist/runtime/interceptor-next.error.d.ts +5 -0
- package/dist/runtime/interceptor-next.error.js +6 -0
- package/dist/runtime/json-value.d.ts +3 -3
- package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
- package/dist/runtime/level-lifecycle.runtime.js +42 -0
- package/dist/runtime/misuse-reporter.d.ts +32 -0
- package/dist/runtime/misuse-reporter.js +19 -0
- package/dist/runtime/mutation-planner.d.ts +19 -10
- package/dist/runtime/mutation-planner.js +62 -36
- package/dist/runtime/nested-write.walker.d.ts +32 -0
- package/dist/runtime/nested-write.walker.js +73 -0
- package/dist/runtime/operation-request.d.ts +35 -15
- package/dist/runtime/operation-response.d.ts +17 -17
- package/dist/runtime/operation-runtime.d.ts +125 -30
- package/dist/runtime/operation-runtime.js +423 -83
- package/dist/runtime/outcome-mapper.d.ts +16 -5
- package/dist/runtime/outcome-mapper.js +35 -8
- package/dist/runtime/output-validator.d.ts +16 -6
- package/dist/runtime/output-validator.js +12 -1
- package/dist/runtime/owned-execution.registry.d.ts +13 -0
- package/dist/runtime/owned-execution.registry.js +7 -0
- package/dist/runtime/pipe-output.validator.d.ts +33 -18
- package/dist/runtime/pipe-output.validator.js +12 -12
- package/dist/runtime/pipeline.d.ts +202 -28
- package/dist/runtime/readonly-value.d.ts +32 -0
- package/dist/runtime/readonly-value.js +141 -0
- package/dist/runtime/referential-action.d.ts +7 -0
- package/dist/runtime/request-envelope.validator.d.ts +5 -0
- package/dist/runtime/request-envelope.validator.js +123 -0
- package/dist/runtime/request-id.d.ts +1 -1
- package/dist/runtime/request-state.d.ts +14 -0
- package/dist/runtime/request-state.js +1 -0
- package/dist/runtime/request-state.resolver.d.ts +12 -0
- package/dist/runtime/request-state.resolver.js +20 -0
- package/dist/runtime/scalar-runtime.d.ts +56 -13
- package/dist/runtime/scalar-runtime.js +167 -86
- package/dist/transactions/deferred-operation.d.ts +35 -20
- package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
- package/dist/transactions/operation-arguments-encoder.js +2 -2
- package/dist/transactions/sql-statement.d.ts +77 -0
- package/dist/transactions/sql-statement.js +47 -0
- package/dist/transactions/sql-statement.validator.d.ts +44 -0
- package/dist/transactions/sql-statement.validator.js +134 -0
- package/dist/transactions/transaction-envelope.d.ts +4 -4
- package/dist/transactions/transaction-integrity-error.d.ts +6 -6
- package/dist/transactions/transaction-integrity-error.js +5 -2
- package/dist/transactions/transaction-plan-assembler.d.ts +23 -9
- package/dist/transactions/transaction-plan-assembler.js +195 -82
- package/dist/transactions/transaction-plan-validator.d.ts +53 -21
- package/dist/transactions/transaction-plan-validator.js +246 -163
- package/dist/transactions/transaction-reference-collector.d.ts +30 -0
- package/dist/transactions/transaction-reference-collector.js +204 -0
- package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
- package/dist/transactions/transaction-reference-resolver.js +27 -10
- package/dist/transactions/transaction-reference-validator.d.ts +34 -4
- package/dist/transactions/transaction-reference-validator.js +82 -43
- package/dist/transactions/transaction-reference.d.ts +30 -58
- package/dist/transactions/transaction-request.d.ts +20 -7
- package/dist/transactions/transaction-response.d.ts +12 -7
- package/dist/transactions/transaction-response.js +24 -1
- package/dist/transactions/transaction-runtime.d.ts +11 -8
- package/dist/transactions/transaction-runtime.js +167 -50
- package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
- package/dist/transactions/transaction-settlement.runtime.js +120 -0
- package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
- package/dist/transactions/transaction-slot-path-error.js +2 -2
- package/dist/transactions/transaction-slot-writer.js +2 -2
- package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
- package/dist/transactions/transaction-stage.runtime.js +123 -0
- package/package.json +18 -3
- package/src/runtime/field-check.evaluator.ts +720 -0
- package/dist/config/define-fields.d.ts +0 -35
- package/dist/config/define-fields.js +0 -3
- package/dist/config/framework-behaviors-config.d.ts +0 -30
- package/dist/config/framework-fields-config.d.ts +0 -36
- package/dist/config/framework-restrictions-config.d.ts +0 -142
- package/dist/operations/operation-boolean-nodes.d.ts +0 -11
- package/dist/operations/operation-boolean-nodes.js +0 -283
- package/dist/operations/operation-nesting.d.ts +0 -8
- package/dist/operations/operation-nesting.js +0 -307
- package/dist/runtime/field-behavior.d.ts +0 -48
- /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
- /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
- /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
- /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
package/README.md
CHANGED
|
@@ -2,29 +2,293 @@
|
|
|
2
2
|
|
|
3
3
|
The Aventara framework itself: the framework factory (`createFramework`), configuration and its resolution, the
|
|
4
4
|
adapter interfaces an ORM adapter implements, Contract compilation and identity, validation, operation semantics, the
|
|
5
|
-
pipeline runtime, transactions and execution. Its HTTP protocol
|
|
6
|
-
|
|
5
|
+
pipeline runtime, transactions and execution. Its HTTP protocol is the separate entry point `@aventara/core/protocol`,
|
|
6
|
+
which hosts such as `@aventara/nest` serve. The protocol is passive data and pure functions, and it mounts nothing.
|
|
7
7
|
|
|
8
|
-
Core
|
|
9
|
-
|
|
8
|
+
Core imports neither Prisma nor NestJS. An application uses it through an adapter (`@aventara/prisma7-adapter`) and a
|
|
9
|
+
host (`@aventara/nest`), and the usual way to set that up is the CLI.
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npx @aventara/cli@
|
|
15
|
-
npx @aventara/cli@
|
|
16
|
-
npm install @aventara/core@
|
|
14
|
+
npx @aventara/cli@rc new my-api # a new NestJS project with Prisma and Aventara
|
|
15
|
+
npx @aventara/cli@rc init # or: add Aventara to an existing NestJS 12 project
|
|
16
|
+
npm install @aventara/core@rc # the package on its own
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
Node `^22.18.0 || >=24.2.0`. ES modules, with a CommonJS `default` condition.
|
|
20
20
|
|
|
21
21
|
## Documentation
|
|
22
22
|
|
|
23
|
-
Start from the `@aventara/cli` README (what `new` and `init` write), then `@aventara/nest` (mounting
|
|
24
|
-
and `@aventara/prisma7-adapter` (the Prisma 7 adapter and its generate step)
|
|
23
|
+
Start from the `@aventara/cli` README (what `new` and `init` write), then `@aventara/nest` (mounting an `AvFramework`)
|
|
24
|
+
and `@aventara/prisma7-adapter` (the Prisma 7 adapter and its generate step). Each package's README ships in its
|
|
25
25
|
tarball and shows on its npm page.
|
|
26
26
|
|
|
27
|
+
## Typed Json fields
|
|
28
|
+
|
|
29
|
+
A `Json` field is any JSON by default. To give it a shape, build one with `AvZ` from `@aventara/core/avz` and declare
|
|
30
|
+
it as the field's `definition.json` in the configuration's `resources` tree:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { AvZ } from "@aventara/core/avz";
|
|
34
|
+
|
|
35
|
+
const postMeta = AvZ.object({
|
|
36
|
+
seo: AvZ.object({ title: AvZ.string(), keywords: AvZ.array(AvZ.string()) }),
|
|
37
|
+
status: AvZ.enum(["draft", "published"]),
|
|
38
|
+
rating: AvZ.optional(AvZ.int()),
|
|
39
|
+
cover: AvZ.nullable(AvZ.string()),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export type PostMeta = AvZ.infer<typeof postMeta>;
|
|
43
|
+
|
|
44
|
+
const framework = await createFramework({
|
|
45
|
+
api: "/api",
|
|
46
|
+
adapter,
|
|
47
|
+
resources: { Post: { fields: { meta: { definition: { json: postMeta } } } } },
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`framework.application`, `framework.client`, `appTx` and `clientTx` read and write `Post.meta` as `PostMeta` instead
|
|
52
|
+
of generic JSON. So does a client generated with `avclient`, which emits the type and needs no Zod.
|
|
53
|
+
|
|
54
|
+
The shape is also one of `meta`'s validators. Every write of `meta` is checked against it before the database is
|
|
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
|
|
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
|
+
exact path:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{ "code": "V1001", "path": ["arguments", "data", "meta", "seo", "title"], "message": "Value must be a string." }
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
A missing key is `V1000`, a value outside an enum or a literal `V1002`, an integer outside the safe range `V1004`,
|
|
65
|
+
and a key the shape does not declare `V1005`, once per key. Reads and filters are never checked against a shape, so
|
|
66
|
+
rows written before the shape existed stay readable.
|
|
67
|
+
|
|
68
|
+
`AvZ` has `string`, `number`, `int`, `boolean`, `null`, `literal`, `enum`, `object`, `array`, `tuple`, `record`,
|
|
69
|
+
`union`, `discriminatedUnion`, `optional` and `nullable`. A shape node takes checks with `.check(...)`: a string takes
|
|
70
|
+
length, pattern and format checks, a number or an integer takes bounds and `multipleOf`, and an array takes
|
|
71
|
+
`AvZ.minItems`/`AvZ.maxItems`, as in `AvZ.string().check(AvZ.maxLength(80))`. There are no refinements, transforms or
|
|
72
|
+
defaults. Objects are closed: a key the shape does not declare is refused. `optional` marks an object key that may be
|
|
73
|
+
left out.
|
|
74
|
+
|
|
75
|
+
Only a field the adapter discovered as `Json` takes a shape. The shape cannot admit `null` or absence at its top
|
|
76
|
+
level; whether the field may be `null` comes from your schema. A shape is built with `AvZ` only. A Zod schema is
|
|
77
|
+
refused, both as a type error and when the framework starts.
|
|
78
|
+
|
|
79
|
+
The shape is part of the ClientContract, as JSON Schema. Adding or changing one changes the ClientContract hash, so
|
|
80
|
+
regenerate the client.
|
|
81
|
+
|
|
82
|
+
The shape types `AvZShape`, `AvZObject`, `AvZOptional` and `AvZNullable` are exported so a project that emits
|
|
83
|
+
declarations can name the shapes it exports.
|
|
84
|
+
|
|
85
|
+
`@aventara/core/avz` is the only entry that loads Zod (`zod/mini`); an application that declares no shape never loads
|
|
86
|
+
it.
|
|
87
|
+
|
|
88
|
+
## Server-side calls resolve their data and throw their failures
|
|
89
|
+
|
|
90
|
+
`framework.application`, `framework.client`, `framework.transaction([...])` and a pipeline's `ctx` calls settle the way
|
|
91
|
+
a generated client does: they resolve the data (a row, a list, a count, `null` for a `first` that matched nothing; a
|
|
92
|
+
committed transaction its steps' results in order) and throw every failure as the `AvError` subclass of its code's
|
|
93
|
+
class, exported from `@aventara/core`:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { AvNotFoundError, AvValidationError } from "@aventara/core";
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const post = await framework.application.Post.update.unique({ where: { id }, data: { title } });
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error instanceof AvNotFoundError) return null; // A2003
|
|
102
|
+
if (error instanceof AvValidationError) throw badRequest(error.cause.issues);
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| Class | Codes |
|
|
108
|
+
| ------------------------- | ------------------------------------------- |
|
|
109
|
+
| `AvProtocolError` | A2000, A2001, A2002, A2006, A2010–A2012 |
|
|
110
|
+
| `AvNotFoundError` | A2003 |
|
|
111
|
+
| `AvValidationError` | A2004, A2007, A2009 |
|
|
112
|
+
| `AvContractMismatchError` | A2005 |
|
|
113
|
+
| `AvConflictError` | A2008, A2013, A2014 |
|
|
114
|
+
| `AvInternalError` | A3000–A3004 |
|
|
115
|
+
| `AvAuthError` | A4000–A4002 |
|
|
116
|
+
|
|
117
|
+
`error.code` and `error.cause` are the envelope's (a transaction's `cause.operation` names the failing step). The
|
|
118
|
+
generated client throws the same classes by the same table. `framework.execute` and `framework.executeTransaction`, the
|
|
119
|
+
entries a transport calls, still answer the envelope.
|
|
120
|
+
|
|
121
|
+
## Required create fields are typed
|
|
122
|
+
|
|
123
|
+
Each Resource's Contract states what a create cannot do without (`createRequirements`), as the adapter reads it off
|
|
124
|
+
the database schema: the fields every create must write, and each required relation, which a create writes either as
|
|
125
|
+
the relation or through the fields that hold its reference, never both. The façades, `appTx`/`clientTx`, a stage's
|
|
126
|
+
`ctx` and the generated client type every create payload by it:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
await framework.application.Post.create.one({ data: { title: "Hi", authorId } }); // ok
|
|
130
|
+
await framework.application.Post.create.one({ data: { title: "Hi", author: { $connect: { id: authorId } } } }); // ok
|
|
131
|
+
// @ts-expect-error neither `author` nor `authorId`
|
|
132
|
+
await framework.application.Post.create.one({ data: { title: "Hi" } });
|
|
133
|
+
// @ts-expect-error `author` and `authorId` both
|
|
134
|
+
await framework.application.Post.create.one({ data: { title: "Hi", authorId, author: { $connect: { id: authorId } } } });
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
It covers `create.one`'s `data`, every `create.many` record, `upsert`'s `create`, and nested `$create`,
|
|
138
|
+
`$connectOrCreate` and `$upsert` payloads, where the relation the create is nested through is supplied by its parent.
|
|
139
|
+
A `$ref` satisfies a field. Each layer's Contract states only what that layer lets a caller write: hide `authorId` and
|
|
140
|
+
`author` alone is required; hide `author` and `authorId` is. A field a `compute.create` stage fills is not required,
|
|
141
|
+
whether the stage is written inline in `createFramework({...})`, in a `defineResource` block or in a typed config.
|
|
142
|
+
The runtime still refuses what the types refuse (`A2004`, `V1000` or `V1012`).
|
|
143
|
+
|
|
144
|
+
A `create.many` or `create.count` record cannot write a relation, so there `authorId` is required. A layer that hides
|
|
145
|
+
`authorId` while it keeps those variants fails at startup (`COMPILER_HIDDEN_REQUIRED_FIELD`): expose `authorId`, give
|
|
146
|
+
it a `compute.create`, or exempt `create.many` and `create.count` in that layer
|
|
147
|
+
(`exemptions: { resources: { Post: { fields: { authorId: true }, operations: { create: ["many", "count"] } } } }`).
|
|
148
|
+
|
|
149
|
+
A field your operation pipe fills server-side is now required of the caller. Give it a `compute.create` stage (or a
|
|
150
|
+
database default) so callers can leave it out, or have them pass it.
|
|
151
|
+
|
|
152
|
+
## Returning a façade from a method
|
|
153
|
+
|
|
154
|
+
A Nest project emits declarations (`declaration: true`), so a method without a return type has its type written out.
|
|
155
|
+
Every node of the typed façades has a name for that: a whole surface (`AvApplicationFacade`), one Resource
|
|
156
|
+
(`AvApplicationResourceFacade`), one family (`AvApplicationFamilyFacade`), and the same three for `client`, `appTx` and
|
|
157
|
+
`clientTx` (`AvClientFacade`, `AvAppTxResourceFacade`, …). The declaration writes the name, with the Contract view as
|
|
158
|
+
its argument, so this compiles:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
@Injectable()
|
|
162
|
+
export class PostsService {
|
|
163
|
+
constructor(@InjectFramework() private readonly framework: AppFramework) {}
|
|
164
|
+
|
|
165
|
+
posts() {
|
|
166
|
+
return this.framework.application.Post;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The view is written out in full, about 400 KB of declaration per such method over 50 Resources. Give the method a
|
|
172
|
+
return type to keep it small: `AppFramework["application"]["Post"]` is written as that reference, about 0.2 KB.
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
posts(): AppFramework["application"]["Post"] {
|
|
176
|
+
return this.framework.application.Post;
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
A variant (`this.framework.application.Post.find.many`) is a call type the declaration cannot name: give that method a
|
|
181
|
+
return type, or return the call's result, whose row type is small. An application that publishes no declarations can
|
|
182
|
+
set `"declaration": false` and skip the question.
|
|
183
|
+
|
|
184
|
+
An exported `defineResource(...)` result, and an exported configuration or Framework builder holding typed stages,
|
|
185
|
+
need no annotation: their stages are written by their public types (`readonly AvGuard[]`, `readonly AvPipe[]`, …),
|
|
186
|
+
never with the model the inline stages were typed from.
|
|
187
|
+
|
|
188
|
+
## Server-side calls copy their input
|
|
189
|
+
|
|
190
|
+
`framework.application`, `framework.client`, `framework.execute`, `framework.executeTransaction`, and the `appTx` /
|
|
191
|
+
`clientTx` builders read what you pass them once, when you call them, and copy it into plain data the framework owns.
|
|
192
|
+
Validation, your guards and pipes, computed values and the adapter all see only the copy, and your stages see it
|
|
193
|
+
frozen: only a pipe changes the arguments, by returning new ones, so what the validators checked is what the adapter
|
|
194
|
+
writes. In practice:
|
|
195
|
+
|
|
196
|
+
- Changing your object afterwards changes nothing. An Immer draft revoked after `produce`, or a reactive store that
|
|
197
|
+
moves on, is safe to pass, because the call already has its own copy.
|
|
198
|
+
- A live Proxy (Vue `reactive`, MobX) works: it is read through its traps once.
|
|
199
|
+
- `Date`, `AvDecimal` and `Uint8Array` (a Node `Buffer` too) are copied as themselves; `bigint` and other primitives
|
|
200
|
+
are values.
|
|
201
|
+
- Arrays and plain objects (`{}` or `Object.create(null)`) are copied with their own enumerable string keys, like JSON:
|
|
202
|
+
inherited, non-enumerable and symbol keys are dropped. Any other object, such as a class instance or a `Map`, is
|
|
203
|
+
refused.
|
|
204
|
+
- A value that cannot be read (a revoked Proxy, a getter that throws) is refused too. Both refusals are errors in your
|
|
205
|
+
request, not the server's: `A2004` with one `V1001` at the value's path (`["arguments", …]`, or
|
|
206
|
+
`["operations", i, "args", …]` with `cause.operation` in a transaction), before any guard, pipe or adapter runs:
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{ "code": "V1001", "path": ["arguments", "where", "id"], "message": "Value could not be read." }
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
A builder never throws: `appTx`/`clientTx` keep the refusal, and `framework.transaction([...])` throws it as an
|
|
213
|
+
`AvValidationError`.
|
|
214
|
+
|
|
215
|
+
To count these refusals, set `misuse` at the root of the configuration, beside `diagnostics`:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
const framework = await createFramework({
|
|
219
|
+
api: "/api",
|
|
220
|
+
adapter,
|
|
221
|
+
misuse: (event) => metrics.increment("aventara.misuse", { kind: event.kind }),
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
It receives one `AvMisuseEvent` per refusal, never the value itself. The event has `kind` (`"unreadable-value"` or
|
|
226
|
+
`"non-plain-value"`), the `path` the caller received, and `origin`, `resource`, `family`, `variant` and the
|
|
227
|
+
transaction step (`operation`) when they could be read. It cannot change the response: an error it throws, or a
|
|
228
|
+
promise it rejects, is ignored. `diagnostics` keeps receiving the server's own unexpected failures, and nothing is
|
|
229
|
+
reported to both.
|
|
230
|
+
|
|
231
|
+
HTTP requests are not copied twice. A body that `protocol.handleOperation` / `handleTransaction` parse from raw content
|
|
232
|
+
(as `@aventara/nest` hands it to them) is already plain data. A body your host parsed itself, passed as
|
|
233
|
+
`{ kind: "parsed" }`, is copied like any server-side input.
|
|
234
|
+
|
|
235
|
+
## Developer SQL in an application transaction
|
|
236
|
+
|
|
237
|
+
`framework.appTx.$sql` adds a SQL statement to an application transaction. It is written only as a tagged template, and
|
|
238
|
+
every interpolated value is a bound parameter, never part of the SQL text:
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
const post = framework.appTx.Post.create.one({ data: { title, authorId }, select: ["id"] });
|
|
242
|
+
const job = framework.appTx.$sql<{ msg_id: bigint }>`
|
|
243
|
+
select pgmq.send('jobs', jsonb_build_object('postId', ${post.$ref("id")}))`;
|
|
244
|
+
|
|
245
|
+
const [created, rows] = await framework.transaction([post, job]);
|
|
246
|
+
// rows is { msg_id: bigint }[]; without <Row> it is unknown[]
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
- The statement runs in plan order, in the same database transaction as the other steps. If it fails, or a later step
|
|
250
|
+
fails, everything rolls back.
|
|
251
|
+
- A value is a string, number, boolean, `bigint`, `null`, `Date`, `AvDecimal`, `Uint8Array`, a JSON value (a plain
|
|
252
|
+
object or array, bound as JSON text, so cast it: `${value}::jsonb`), or a `$ref` to an earlier step's field, which
|
|
253
|
+
is read after that step ran and then bound. Values are copied like every other input (see above).
|
|
254
|
+
- The SQL text is static. A table or column name cannot come from a value.
|
|
255
|
+
- A later step cannot `$ref` a SQL step's rows (`A2007`/`V1010`).
|
|
256
|
+
- No guard, pipe, hook, interceptor or filter runs around a SQL step. A SQL error is answered as the adapter
|
|
257
|
+
classifies it (a uniqueness or reference violation is `A2008`, anything else `A3001`).
|
|
258
|
+
- A stage's `ctx.$sql` runs the same bound template at once. Inside a transaction it joins it; outside one it is a plain
|
|
259
|
+
statement, with no transaction opened around it.
|
|
260
|
+
- It exists only here. `clientTx` has no `$sql`, the ClientContract carries nothing of it, and `/_transactions` cannot
|
|
261
|
+
express it. The adapter must support it (`@aventara/prisma7-adapter` does wherever it runs transactions); otherwise a
|
|
262
|
+
plan holding one is refused with `A2002`.
|
|
263
|
+
|
|
264
|
+
Aventara itself never writes raw SQL. This tagged template is the only way SQL reaches the database, and the SQL is
|
|
265
|
+
yours.
|
|
266
|
+
|
|
267
|
+
## The agent layer
|
|
268
|
+
|
|
269
|
+
`agent` is a layer for calls an AI agent makes, served over MCP. It narrows the view of the layer it `extends`
|
|
270
|
+
(`"client"` when left out, or `"application"`, or `"root"`) and never widens it; its stages run after that layer's, and
|
|
271
|
+
each one sees `ctx.$origin === "agent"`:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
const framework = await createFramework({
|
|
275
|
+
api: "/api",
|
|
276
|
+
mcp: "/mcp",
|
|
277
|
+
adapter,
|
|
278
|
+
agent: {
|
|
279
|
+
exposed: true,
|
|
280
|
+
exemptions: { resources: { Post: { operations: { delete: true } } } },
|
|
281
|
+
pipelines: { guards: [(ctx) => ctx.$state.user !== undefined] },
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
It compiles to `framework.contracts.agent`, identified by its own hash, and `framework.mcp` is the resolved MCP
|
|
287
|
+
endpoint. A host runs agent calls through `AvProtocol.bind(framework, { origin: "agent" })`, which validates them
|
|
288
|
+
exactly as client calls are validated, against the Agent Contract.
|
|
289
|
+
|
|
27
290
|
## License
|
|
28
291
|
|
|
29
|
-
PolyForm Shield 1.0.0 with an additional permission
|
|
30
|
-
use it to build a product that competes with Aventara itself. See `LICENSE` and
|
|
292
|
+
PolyForm Shield 1.0.0 with an additional permission. It is free to use, including in commercial applications, but you
|
|
293
|
+
may not use it to build a product that competes with Aventara itself. See `LICENSE` and
|
|
294
|
+
`LICENSE-ADDITIONAL-PERMISSION.md`.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/** Codes of {@link
|
|
2
|
-
export type
|
|
1
|
+
/** Codes of {@link AvAdapterDiscoveryDiagnostic}. */
|
|
2
|
+
export type AvAdapterDiscoveryDiagnosticCode = "DISCOVERY_TRANSACTION_CAPABILITY_MISMATCH";
|
|
3
3
|
/** A problem found in what the adapter discovered, reported at framework construction. */
|
|
4
|
-
export type
|
|
5
|
-
readonly code:
|
|
4
|
+
export type AvAdapterDiscoveryDiagnostic = {
|
|
5
|
+
readonly code: AvAdapterDiscoveryDiagnosticCode;
|
|
6
6
|
readonly path: "adapter.transactions";
|
|
7
7
|
readonly message: string;
|
|
8
8
|
};
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
1
|
+
import type { AvRelationFieldCapabilities, AvScalarFieldCapabilities, AvScalarFormat } from "../contracts/field.js";
|
|
2
|
+
import type { AvLifecycleTrait } from "../contracts/lifecycle-trait.js";
|
|
3
|
+
import type { AvAdapterModel } from "./adapter-model.js";
|
|
4
4
|
/** Provider-observable lifecycle facts available during adapter discovery. */
|
|
5
|
-
export type
|
|
5
|
+
export type AvAdapterLifecycleTrait = Extract<AvLifecycleTrait, "DEFAULTED" | "GENERATED_ON_CREATE" | "GENERATED_ON_UPDATE">;
|
|
6
6
|
/** Scalar-shaped provider field expressed only in Aventara vocabulary. */
|
|
7
|
-
export type
|
|
7
|
+
export type AvAdapterScalarField<M extends AvAdapterModel> = {
|
|
8
8
|
readonly kind: "scalar";
|
|
9
9
|
readonly type: {
|
|
10
10
|
readonly scalar: Extract<keyof M["scalars"], string>;
|
|
11
11
|
} | {
|
|
12
12
|
readonly enum: Extract<keyof M["enums"], string>;
|
|
13
13
|
};
|
|
14
|
-
readonly format?:
|
|
14
|
+
readonly format?: AvScalarFormat;
|
|
15
15
|
readonly nullable: boolean;
|
|
16
16
|
readonly list: boolean;
|
|
17
|
-
readonly lifecycle: readonly
|
|
18
|
-
readonly capabilities:
|
|
17
|
+
readonly lifecycle: readonly AvAdapterLifecycleTrait[];
|
|
18
|
+
readonly capabilities: AvScalarFieldCapabilities;
|
|
19
19
|
};
|
|
20
20
|
/** A discovered relation field; the `target` Resource describes the related row. */
|
|
21
|
-
export type
|
|
21
|
+
export type AvAdapterRelationField<M extends AvAdapterModel> = {
|
|
22
22
|
readonly kind: "relation";
|
|
23
23
|
readonly target: Extract<keyof M["resources"], string>;
|
|
24
24
|
readonly cardinality: "one" | "many";
|
|
25
25
|
readonly nullable: boolean;
|
|
26
|
-
readonly capabilities:
|
|
26
|
+
readonly capabilities: AvRelationFieldCapabilities;
|
|
27
27
|
};
|
|
28
28
|
/** One field the adapter discovered: a scalar or a relation. */
|
|
29
|
-
export type
|
|
29
|
+
export type AvAdapterField<M extends AvAdapterModel> = AvAdapterScalarField<M> | AvAdapterRelationField<M>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { AvAdapterField } from "./adapter-field.js";
|
|
2
|
+
import type { AvAdapterModel } from "./adapter-model.js";
|
|
3
3
|
/** Keys of `TFields` whose discovered field can be scalar-shaped. */
|
|
4
|
-
export type
|
|
4
|
+
export type AvAdapterScalarFieldKey<TFields extends Readonly<Record<string, AvAdapterField<AvAdapterModel>>>> = Extract<{
|
|
5
5
|
[K in keyof TFields]: [
|
|
6
6
|
Extract<TFields[K], {
|
|
7
7
|
readonly kind: "scalar";
|
|
@@ -9,7 +9,7 @@ export type AdapterScalarFieldKey<TFields extends Readonly<Record<string, Adapte
|
|
|
9
9
|
] extends [never] ? never : K;
|
|
10
10
|
}[keyof TFields], string>;
|
|
11
11
|
/** Non-empty scalar-field tuple discovered as provider uniqueness. */
|
|
12
|
-
export type
|
|
13
|
-
|
|
14
|
-
...
|
|
12
|
+
export type AvAdapterIdentifier<TFields extends Readonly<Record<string, AvAdapterField<AvAdapterModel>>>> = readonly [
|
|
13
|
+
AvAdapterScalarFieldKey<TFields>,
|
|
14
|
+
...AvAdapterScalarFieldKey<TFields>[]
|
|
15
15
|
];
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
1
|
+
import type { AvEnumDefinition } from "../contracts/enum-definition.js";
|
|
2
|
+
import type { ReferenceWriteLimit } from "../contracts/reference-write-limit.js";
|
|
3
|
+
import type { AvScalarRegistry } from "../contracts/scalar.js";
|
|
4
|
+
import type { AvTransactionCapability } from "../contracts/transaction-capability.js";
|
|
5
|
+
import type { AvAdapterResource } from "./adapter-resource.js";
|
|
5
6
|
/**
|
|
6
7
|
* Canonical provider-discovery facts before framework configuration or
|
|
7
8
|
* Contract compilation is applied.
|
|
8
9
|
*/
|
|
9
|
-
export interface
|
|
10
|
+
export interface AvAdapterModel {
|
|
10
11
|
/** The scalar types the data source supports. */
|
|
11
|
-
readonly scalars:
|
|
12
|
+
readonly scalars: AvScalarRegistry;
|
|
12
13
|
/** The enums the data source declares, by name. */
|
|
13
|
-
readonly enums: Readonly<Record<string,
|
|
14
|
+
readonly enums: Readonly<Record<string, AvEnumDefinition>>;
|
|
14
15
|
/** The discovered Resources, by name. */
|
|
15
|
-
readonly resources: Readonly<Record<string,
|
|
16
|
+
readonly resources: Readonly<Record<string, AvAdapterResource<this>>>;
|
|
16
17
|
/** Whether the adapter can run interactive transactions. */
|
|
17
|
-
readonly transactions:
|
|
18
|
+
readonly transactions: AvTransactionCapability;
|
|
19
|
+
/**
|
|
20
|
+
* `"uniform"` when the data source takes one record's references only one
|
|
21
|
+
* way at a time: every relation that holds a reference written as a
|
|
22
|
+
* relation, or every one through its reference fields. Absent: each relation
|
|
23
|
+
* either way. Compiled into every Contract.
|
|
24
|
+
*/
|
|
25
|
+
readonly referenceWrites?: ReferenceWriteLimit;
|
|
18
26
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { AvAdapterModel } from "./adapter-model.js";
|
|
2
|
+
import type { AvAdapterOperation } from "./adapter-operation.js";
|
|
3
3
|
/**
|
|
4
4
|
* Executes one operation against the provider, directly or inside a provider
|
|
5
5
|
* transaction.
|
|
6
6
|
*/
|
|
7
|
-
export interface
|
|
7
|
+
export interface AvAdapterOperationExecutor<M extends AvAdapterModel = AvAdapterModel> {
|
|
8
8
|
/** Runs one validated operation and resolves its raw result. */
|
|
9
|
-
execute(operation:
|
|
9
|
+
execute(operation: AvAdapterOperation<M>): Promise<unknown>;
|
|
10
10
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvOperationIdentity } from "../operations/operation-identity.js";
|
|
2
2
|
import type { SemanticOperationArguments } from "../operations/operation-validator.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { AvAdapterModel } from "./adapter-model.js";
|
|
4
4
|
/**
|
|
5
5
|
* Provider execution request after framework semantic validation.
|
|
6
6
|
* Operation identity and arguments remain provider-neutral; adapters receive
|
|
7
7
|
* only validated semantic argument objects, never raw transport input.
|
|
8
8
|
*/
|
|
9
|
-
export type
|
|
9
|
+
export type AvAdapterOperation<M extends AvAdapterModel = AvAdapterModel> = AvOperationIdentity & {
|
|
10
10
|
readonly resource: Extract<keyof M["resources"], string>;
|
|
11
11
|
readonly arguments: SemanticOperationArguments;
|
|
12
12
|
};
|
|
@@ -1,20 +1,29 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
1
|
+
import type { AvCreateRequirements } from "../contracts/create-requirements.js";
|
|
2
|
+
import type { AvResourceOperations } from "../contracts/resource-operations.js";
|
|
3
|
+
import type { AvAdapterField } from "./adapter-field.js";
|
|
4
|
+
import type { AvAdapterIdentifier } from "./adapter-identifier.js";
|
|
5
|
+
import type { AvAdapterModel } from "./adapter-model.js";
|
|
5
6
|
/** A discovered Resource's fields, by name. */
|
|
6
|
-
export type
|
|
7
|
+
export type AvAdapterFieldMap<M extends AvAdapterModel> = Readonly<Record<string, AvAdapterField<M>>>;
|
|
7
8
|
/**
|
|
8
9
|
* The field names the adapter discovered for Resource `R`, before any
|
|
9
10
|
* configuration adds to them.
|
|
10
11
|
*/
|
|
11
|
-
export type
|
|
12
|
+
export type AvAdapterFieldKey<M extends AvAdapterModel, R extends Extract<keyof M["resources"], string>> = Extract<keyof M["resources"][R]["fields"], string>;
|
|
12
13
|
/** Provider-discovered facts for one Aventara Resource candidate. */
|
|
13
|
-
export type
|
|
14
|
+
export type AvAdapterResource<M extends AvAdapterModel, TFields extends AvAdapterFieldMap<M> = AvAdapterFieldMap<M>> = {
|
|
14
15
|
/** The Resource's fields, by name. */
|
|
15
16
|
readonly fields: TFields;
|
|
16
17
|
/** The field tuples that identify at most one row. */
|
|
17
|
-
readonly identifiers: readonly
|
|
18
|
+
readonly identifiers: readonly AvAdapterIdentifier<TFields>[];
|
|
18
19
|
/** The standard operations the data source can run for this Resource. */
|
|
19
|
-
readonly operations:
|
|
20
|
+
readonly operations: AvResourceOperations;
|
|
21
|
+
/**
|
|
22
|
+
* What a create cannot do without, in the store's own terms: every field it
|
|
23
|
+
* requires, every required to-one relation with its reference fields, and
|
|
24
|
+
* the relation (`parentSupplies`) through which a nested create gets them
|
|
25
|
+
* from its parent. Absent when it requires nothing, or when the adapter
|
|
26
|
+
* cannot say; nothing is then checked or typed before the store.
|
|
27
|
+
*/
|
|
28
|
+
readonly createRequirements?: AvCreateRequirements;
|
|
20
29
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AvDecimal } from "../runtime/decimal.js";
|
|
2
|
+
import type { AvJsonValue } from "../runtime/json-value.js";
|
|
3
|
+
/**
|
|
4
|
+
* One value a developer SQL statement binds, after every `$ref` in it was
|
|
5
|
+
* resolved: a primitive, a `Date`, a {@link AvDecimal}, a `Uint8Array`, or a
|
|
6
|
+
* plain object or array, which is a JSON value and is bound as JSON text. Never
|
|
7
|
+
* spliced into the statement's text.
|
|
8
|
+
*/
|
|
9
|
+
export type AvAdapterSqlValue = string | number | boolean | bigint | null | Date | AvDecimal | Uint8Array | AvJsonValue;
|
|
10
|
+
/**
|
|
11
|
+
* One developer SQL statement, as `framework.appTx.$sql` wrote it: the static
|
|
12
|
+
* text of its tagged template, and the values written between those pieces, in
|
|
13
|
+
* order. `strings` holds exactly one entry more than `values`. The adapter binds
|
|
14
|
+
* every value as a parameter of the statement; no value ever becomes SQL text.
|
|
15
|
+
*/
|
|
16
|
+
export type AvAdapterSqlStatement = {
|
|
17
|
+
readonly strings: readonly string[];
|
|
18
|
+
readonly values: readonly AvAdapterSqlValue[];
|
|
19
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AvAdapterModel } from "./adapter-model.js";
|
|
2
|
+
import type { AvAdapterOperationExecutor } from "./adapter-operation-executor.interface.js";
|
|
3
|
+
import type { AvAdapterSqlStatement } from "./adapter-sql-statement.js";
|
|
4
|
+
/**
|
|
5
|
+
* What an Adapter's `transaction` callback is handed: the transaction-scoped
|
|
6
|
+
* operation executor, and — when the Adapter declares `sqlStatements` — the
|
|
7
|
+
* statement runner `framework.appTx.$sql` steps run through.
|
|
8
|
+
*/
|
|
9
|
+
export interface AvAdapterTransactionExecutor<M extends AvAdapterModel = AvAdapterModel> extends AvAdapterOperationExecutor<M> {
|
|
10
|
+
/**
|
|
11
|
+
* Runs one developer SQL statement on the open transaction, every value bound
|
|
12
|
+
* as a parameter, and resolves the rows it returned (none for a statement that
|
|
13
|
+
* returns no rows). Present exactly when the Adapter declares `sqlStatements`.
|
|
14
|
+
*
|
|
15
|
+
* A failure is thrown like an operation's: a deliberate `AvError` for
|
|
16
|
+
* an outcome the Adapter classified, anything else to be answered `A3001`.
|
|
17
|
+
*/
|
|
18
|
+
executeSql?(statement: AvAdapterSqlStatement): Promise<readonly unknown[]>;
|
|
19
|
+
}
|
|
@@ -1,46 +1,70 @@
|
|
|
1
|
-
import type { CreateRequirementTable } from "../runtime/create-requirement.js";
|
|
2
1
|
import type { ReferentialActionTable } from "../runtime/referential-action.js";
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
2
|
+
import type { AvAdapterModel } from "./adapter-model.js";
|
|
3
|
+
import type { AvAdapterOperationExecutor } from "./adapter-operation-executor.interface.js";
|
|
4
|
+
import type { AvAdapterSqlStatement } from "./adapter-sql-statement.js";
|
|
5
|
+
import type { AvAdapterTransactionExecutor } from "./adapter-transaction-executor.interface.js";
|
|
5
6
|
/** Provider-specific implementation of Aventara discovery and execution. */
|
|
6
|
-
export interface
|
|
7
|
+
export interface AvAdapter<M extends AvAdapterModel = AvAdapterModel> extends AvAdapterOperationExecutor<M> {
|
|
7
8
|
/**
|
|
8
9
|
* What the adapter discovered about the data source, in Aventara's vocabulary.
|
|
9
10
|
* Read once, synchronously, when the framework is constructed.
|
|
10
11
|
*/
|
|
11
12
|
readonly model: M;
|
|
12
13
|
/**
|
|
13
|
-
* Optional referential actions: for each Resource that holds a reference,
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Optional referential actions: for each Resource that holds a reference, every
|
|
15
|
+
* relation holding one: the fields that hold it (`references`) and what deleting a
|
|
16
|
+
* referenced row does to its rows. Read once when the framework is constructed;
|
|
17
|
+
* never part of a Contract or its hash.
|
|
18
|
+
*
|
|
19
|
+
* An edge listed under Resource `R` says that deleting a `target` row does
|
|
20
|
+
* `onDelete` to rows of `R`, and that `R`'s `references` fields hold the relation
|
|
21
|
+
* `field`'s reference.
|
|
16
22
|
*
|
|
17
|
-
*
|
|
18
|
-
* `
|
|
23
|
+
* The same edges say which side of a relation holds its key: a nested `$connect`,
|
|
24
|
+
* `$disconnect` or `$set` on a to-one relation the parent holds runs none of the
|
|
25
|
+
* target's stages, and otherwise runs the target's `update` guards.
|
|
19
26
|
*
|
|
20
|
-
* When absent, no cascade is planned and no transaction plan is refused
|
|
21
|
-
*
|
|
27
|
+
* When absent, no cascade is planned and no transaction plan is refused because of
|
|
28
|
+
* one (`V1019`); a write that sets a relation and a field holding its reference
|
|
29
|
+
* both is not refused before the store; and every nested `$connect`, `$disconnect`
|
|
30
|
+
* or `$set` runs the target's `update` guards.
|
|
22
31
|
*/
|
|
23
32
|
readonly referentialActions?: ReferentialActionTable;
|
|
24
33
|
/**
|
|
25
|
-
* Optional
|
|
26
|
-
*
|
|
27
|
-
*
|
|
34
|
+
* Optional: `true` when the Adapter runs developer SQL statements: standalone
|
|
35
|
+
* through its own {@link executeSql}, and, when it has `transaction`, inside a
|
|
36
|
+
* transaction through the `executeSql` of the executor that callback is handed
|
|
37
|
+
* (`AvAdapterTransactionExecutor.executeSql`). `framework.appTx.$sql` steps and
|
|
38
|
+
* `ctx.$sql` need it. Read once when the framework is constructed; never part
|
|
39
|
+
* of a Contract or its hash.
|
|
40
|
+
*
|
|
41
|
+
* When absent, a plan holding an `appTx.$sql` step is refused before the
|
|
42
|
+
* transaction opens (`A2002`/`V1006` at the step), and so is every `ctx.$sql`.
|
|
43
|
+
*/
|
|
44
|
+
readonly sqlStatements?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Runs one developer SQL statement outside any transaction, as a plain
|
|
47
|
+
* statement, every value bound as a parameter, and resolves the rows it
|
|
48
|
+
* returned (none for a statement that returns no rows). Present exactly when
|
|
49
|
+
* the Adapter declares `sqlStatements`. `ctx.$sql` outside a transaction runs
|
|
50
|
+
* through it.
|
|
28
51
|
*
|
|
29
|
-
*
|
|
30
|
-
* the
|
|
52
|
+
* A failure is thrown like an operation's: a deliberate `AvError` for an
|
|
53
|
+
* outcome the Adapter classified, anything else to be answered `A3001`.
|
|
31
54
|
*/
|
|
32
|
-
readonly
|
|
55
|
+
executeSql?(statement: AvAdapterSqlStatement): Promise<readonly unknown[]>;
|
|
33
56
|
/**
|
|
34
57
|
* Optional callback transaction capability, required exactly when the
|
|
35
58
|
* discovered model declares `transactions: "interactive"`. The callback
|
|
36
|
-
* receives only a transaction-scoped
|
|
37
|
-
*
|
|
59
|
+
* receives only a transaction-scoped executor, never the provider client
|
|
60
|
+
* itself, and every step of a plan executes through it.
|
|
38
61
|
*
|
|
39
|
-
* The implementation must reject with the exact value `work` threw
|
|
40
|
-
* never a wrapper
|
|
41
|
-
* implied by that rejection.
|
|
42
|
-
*
|
|
43
|
-
* not perform network I/O or unrelated slow work.
|
|
62
|
+
* The implementation must reject with the exact value `work` threw (by identity,
|
|
63
|
+
* never a wrapper) and must not resolve after `work` rejected; rollback is
|
|
64
|
+
* implied by that rejection. Every step's interceptors and hooks, and the touched
|
|
65
|
+
* Resources' transaction stages, run inside `work` while the provider transaction
|
|
66
|
+
* is open, so they must not perform network I/O or unrelated slow work. A step's
|
|
67
|
+
* guards, pipes and validators run before it opens.
|
|
44
68
|
*/
|
|
45
|
-
transaction?<T>(work: (executor:
|
|
69
|
+
transaction?<T>(work: (executor: AvAdapterTransactionExecutor<M>) => Promise<T>): Promise<T>;
|
|
46
70
|
}
|