@aventara/core 1.0.0-rc.1 → 1.0.0-rc.10
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 +943 -16
- package/ERRORS.md +142 -0
- package/README.md +274 -16
- 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 +22 -0
- package/dist/adapters/adapter-identifier.d.ts +9 -0
- package/dist/adapters/adapter-identifier.js +6 -1
- package/dist/adapters/adapter-model.d.ts +9 -0
- package/dist/adapters/adapter-operation.d.ts +18 -3
- package/dist/adapters/adapter-resource.d.ts +5 -0
- package/dist/adapters/adapter.interface.d.ts +10 -0
- package/dist/avz/avz.d.ts +1 -1
- package/dist/avz/json-shape.registry.d.ts +2 -1
- 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.d.ts +6 -0
- package/dist/compiler/contract.compiler.js +227 -24
- package/dist/compiler/managed-elsewhere.projector.d.ts +52 -0
- package/dist/compiler/managed-elsewhere.projector.js +179 -0
- package/dist/compiler/managed-elsewhere.validator.d.ts +9 -0
- package/dist/compiler/managed-elsewhere.validator.js +74 -0
- package/dist/config/computed-fields.d.ts +62 -0
- package/dist/config/computed-fields.js +73 -0
- package/dist/config/config-diagnostic.guidance.d.ts +6 -0
- package/dist/config/config-diagnostic.guidance.js +32 -0
- package/dist/config/config.resolver.d.ts +24 -0
- package/dist/config/config.resolver.js +65 -0
- package/dist/config/config.validator.js +222 -8
- package/dist/config/define-resource.d.ts +1 -5
- package/dist/config/entrypoint.normalizer.d.ts +3 -2
- package/dist/config/exact-framework-config.d.ts +67 -22
- 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 +53 -15
- package/dist/config/framework-resources-config.d.ts +25 -14
- package/dist/config/pipeline-config.d.ts +4 -4
- package/dist/config/pipeline-tree.resolver.d.ts +2 -2
- package/dist/contracts/access-route.rules.d.ts +10 -0
- package/dist/contracts/access-route.rules.js +45 -0
- package/dist/contracts/capability.d.ts +26 -1
- package/dist/contracts/capability.js +20 -0
- package/dist/contracts/client-contract-structure.validator.d.ts +301 -89
- package/dist/contracts/client-contract-structure.validator.js +37 -2
- package/dist/contracts/contract-access.d.ts +29 -0
- package/dist/contracts/contract-access.js +1 -0
- package/dist/contracts/contract-canonicalizer.d.ts +9 -1
- package/dist/contracts/contract-canonicalizer.js +30 -4
- package/dist/contracts/contract-hash.d.ts +0 -1
- package/dist/contracts/contract-hash.js +1 -1
- package/dist/contracts/contract.d.ts +27 -1
- package/dist/contracts/field.d.ts +58 -1
- package/dist/contracts/json-schema.projector.d.ts +11 -0
- package/dist/contracts/json-schema.projector.js +78 -0
- package/dist/contracts/scalar-value-type.d.ts +3 -1
- package/dist/framework/access-facade.d.ts +116 -0
- package/dist/framework/access-facade.js +120 -0
- package/dist/framework/contract-view.d.ts +120 -16
- package/dist/framework/framework-construction.error.d.ts +14 -4
- package/dist/framework/framework-construction.error.js +18 -2
- package/dist/framework/framework.d.ts +26 -5
- package/dist/framework/framework.factory.d.ts +1 -1
- package/dist/framework/framework.factory.js +115 -23
- package/dist/framework/operation-facade.d.ts +80 -13
- package/dist/framework/operation-facade.js +2 -1
- package/dist/index.d.ts +17 -5
- package/dist/index.js +1 -0
- package/dist/internal.d.ts +7 -2
- package/dist/internal.js +6 -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 +148 -60
- package/dist/operations/operation-call.d.ts +9 -5
- package/dist/operations/operation-result.d.ts +6 -4
- package/dist/operations/operation-validator-core.js +159 -36
- package/dist/operations/operation-validator.d.ts +1 -1
- package/dist/operations/structural-limits.walker.d.ts +1 -1
- 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/policy/access-provider.interface.d.ts +91 -0
- package/dist/policy/access-provider.interface.js +1 -0
- package/dist/policy/access-provider.registry.d.ts +17 -0
- package/dist/policy/access-provider.registry.js +15 -0
- package/dist/policy/access-question.d.ts +20 -0
- package/dist/policy/access-question.js +1 -0
- package/dist/policy/access-question.registry.d.ts +5 -0
- package/dist/policy/access-question.registry.js +19 -0
- package/dist/policy/access-question.runtime.d.ts +36 -0
- package/dist/policy/access-question.runtime.js +297 -0
- package/dist/policy/access-verb.table.d.ts +56 -0
- package/dist/policy/access-verb.table.js +21 -0
- package/dist/policy/access-verbs.d.ts +46 -0
- package/dist/policy/access-verbs.js +1 -0
- package/dist/policy/policy-admission.d.ts +20 -0
- package/dist/policy/policy-admission.js +409 -0
- package/dist/policy/policy-after-update.runtime.d.ts +79 -0
- package/dist/policy/policy-after-update.runtime.js +208 -0
- package/dist/policy/policy-condition.d.ts +15 -0
- package/dist/policy/policy-condition.js +36 -0
- package/dist/policy/policy-engine.d.ts +205 -0
- package/dist/policy/policy-engine.js +1 -0
- package/dist/policy/policy-named-rows.d.ts +18 -0
- package/dist/policy/policy-named-rows.js +51 -0
- package/dist/policy/policy-nested-write.runtime.d.ts +44 -0
- package/dist/policy/policy-nested-write.runtime.js +545 -0
- package/dist/policy/policy-read.runtime.d.ts +81 -0
- package/dist/policy/policy-read.runtime.js +503 -0
- package/dist/policy/policy-reference-write.runtime.d.ts +51 -0
- package/dist/policy/policy-reference-write.runtime.js +345 -0
- package/dist/policy/policy-request.d.ts +41 -0
- package/dist/policy/policy-request.js +63 -0
- package/dist/policy/policy-session.d.ts +106 -0
- package/dist/policy/policy-session.js +295 -0
- package/dist/policy/policy-startup.validator.d.ts +10 -0
- package/dist/policy/policy-startup.validator.js +112 -0
- package/dist/policy/policy-write.runtime.d.ts +39 -0
- package/dist/policy/policy-write.runtime.js +532 -0
- package/dist/prebuilt/core-version.generated.d.ts +2 -0
- package/dist/prebuilt/core-version.generated.js +1 -0
- package/dist/prebuilt/prebuilt-changes.d.ts +29 -0
- package/dist/prebuilt/prebuilt-changes.js +65 -0
- package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
- package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
- package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
- package/dist/prebuilt/prebuilt-contracts.js +1 -0
- package/dist/prebuilt/prebuilt-contracts.loader.d.ts +19 -0
- package/dist/prebuilt/prebuilt-contracts.loader.js +77 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.js +229 -0
- package/dist/protocol/absent-route.responder.d.ts +9 -27
- package/dist/protocol/absent-route.responder.js +36 -3
- package/dist/protocol/contract-document.responder.d.ts +2 -1
- package/dist/protocol/http-request.decoder.d.ts +19 -3
- package/dist/protocol/http-request.decoder.js +24 -1
- package/dist/protocol/http-request.reader.d.ts +6 -3
- package/dist/protocol/http-request.reader.js +1 -0
- package/dist/protocol/http-response.encoder.js +2 -2
- package/dist/protocol/protocol-headers.d.ts +3 -3
- package/dist/protocol/protocol-surface.d.ts +6 -1
- package/dist/protocol/protocol-surface.js +7 -0
- package/dist/protocol/protocol.binder.d.ts +16 -1
- package/dist/protocol/protocol.binder.js +4 -1
- package/dist/protocol/protocol.handler.d.ts +12 -1
- package/dist/protocol/protocol.handler.js +86 -17
- package/dist/runtime/caller-data.copier.d.ts +18 -3
- package/dist/runtime/caller-data.copier.js +21 -8
- package/dist/runtime/caller-data.door.d.ts +13 -6
- package/dist/runtime/computed-read-runtime.d.ts +6 -1
- package/dist/runtime/computed-read-runtime.js +232 -45
- package/dist/runtime/computed-value.error.d.ts +3 -2
- package/dist/runtime/computed-value.error.js +3 -2
- package/dist/runtime/context-executor.runtime.d.ts +4 -3
- package/dist/runtime/context-executor.runtime.js +4 -2
- package/dist/runtime/core-class.brand.d.ts +10 -0
- package/dist/runtime/core-class.brand.js +21 -0
- package/dist/runtime/create-requirement.d.ts +31 -5
- package/dist/runtime/create-requirement.js +129 -21
- package/dist/runtime/decimal.d.ts +14 -0
- package/dist/runtime/decimal.js +21 -0
- package/dist/runtime/diagnostics-reporter.d.ts +44 -9
- package/dist/runtime/diagnostics-reporter.js +13 -1
- package/dist/runtime/error-boundary.d.ts +9 -2
- package/dist/runtime/error-boundary.js +12 -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/execution-options.d.ts +8 -0
- package/dist/runtime/field-check.evaluator.d.ts +0 -2
- package/dist/runtime/field-check.evaluator.js +1 -1
- package/dist/runtime/field-compute.d.ts +11 -10
- package/dist/runtime/field-stage.runtime.d.ts +3 -1
- package/dist/runtime/field-stage.runtime.js +8 -4
- package/dist/runtime/field-validator.runtime.d.ts +19 -1
- package/dist/runtime/field-validator.runtime.js +47 -8
- package/dist/runtime/framework-error.js +9 -0
- package/dist/runtime/framework-warning.emitter.d.ts +6 -0
- package/dist/runtime/framework-warning.emitter.js +8 -0
- package/dist/runtime/json-value.d.ts +10 -5
- package/dist/runtime/level-lifecycle.runtime.d.ts +4 -3
- package/dist/runtime/level-lifecycle.runtime.js +3 -5
- package/dist/runtime/mutation-planner.d.ts +1 -6
- 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-request.d.ts +16 -9
- package/dist/runtime/operation-runtime.d.ts +35 -17
- package/dist/runtime/operation-runtime.js +220 -49
- package/dist/runtime/outcome-mapper.d.ts +2 -1
- package/dist/runtime/output-validator.d.ts +1 -1
- package/dist/runtime/owned-execution.registry.d.ts +13 -3
- 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 +47 -22
- package/dist/runtime/readonly-value.d.ts +20 -5
- package/dist/runtime/readonly-value.js +74 -26
- package/dist/runtime/referential-action.d.ts +1 -1
- package/dist/runtime/scalar-runtime.d.ts +4 -11
- package/dist/runtime/scalar-runtime.js +91 -22
- package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
- package/dist/runtime/unsent-value.normalizer.js +128 -0
- package/dist/transactions/operation-arguments-encoder.js +8 -3
- package/dist/transactions/operation-fingerprint.js +2 -2
- package/dist/transactions/sql-statement.d.ts +10 -2
- package/dist/transactions/sql-statement.validator.d.ts +2 -2
- package/dist/transactions/sql-statement.validator.js +38 -5
- package/dist/transactions/transaction-envelope.d.ts +5 -4
- package/dist/transactions/transaction-integrity-error.js +2 -0
- package/dist/transactions/transaction-plan-assembler.d.ts +12 -5
- package/dist/transactions/transaction-plan-assembler.js +48 -10
- package/dist/transactions/transaction-plan-validator.d.ts +23 -7
- package/dist/transactions/transaction-plan-validator.js +27 -1
- package/dist/transactions/transaction-reference-resolver.d.ts +3 -2
- package/dist/transactions/transaction-reference-validator.d.ts +43 -3
- package/dist/transactions/transaction-reference-validator.js +83 -57
- package/dist/transactions/transaction-reference.d.ts +1 -1
- package/dist/transactions/transaction-reference.js +5 -0
- package/dist/transactions/transaction-request.d.ts +8 -8
- package/dist/transactions/transaction-runtime.d.ts +5 -2
- package/dist/transactions/transaction-runtime.js +79 -27
- package/dist/transactions/transaction-settlement.runtime.d.ts +4 -3
- package/dist/transactions/transaction-slot-path-error.js +2 -0
- package/dist/transactions/transaction-stage.runtime.d.ts +5 -3
- package/dist/transactions/transaction-stage.runtime.js +7 -4
- package/package.json +6 -5
- package/src/runtime/field-check.evaluator.ts +1 -1
package/ERRORS.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Aventara error codes
|
|
2
|
+
|
|
3
|
+
Every code `@aventara/core` 1.0.0-rc.10 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. Under a policy, a row outside the caller's read scope is a miss too, and so is a single-row update or delete of a row the caller cannot read, so the answer does not reveal that the row exists. A row the caller can read but not change answers A4002. | 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 its index, 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"]`, or at the value where the adapter can place it (a bigint its driver cannot bind), 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. | The configuration's policy refused the request: its engine does not allow the operation, a field the request names, writes or returns, a row it reaches, a row it creates, or the row a change leaves (moved out of what the caller reads, or failing its grant's after condition, which rolls the write back); on a database without interactive transactions, a write the policy can only check after it runs; or a stage threw new AvError("A4002", ...). | Read cause.message, which names what the policy refused. Call as a principal the policy allows, leave the refused field or relation out (a governed one is named only where every row grants it), or change only rows the caller may change and still read afterwards. |
|
|
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. Also a value that would make the call reach more rows: `undefined` in a filter, a unique selector or a cursor, an empty boolean node (`NOT: {}`, `AND: []`, `OR: []`) in the where of a write (update or delete many, count or first, a to-many $update or $delete). | 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. Under A2004 it is also a value the database refused as too long or out of its range, a BigInt beyond what the data source's driver can bind among them (some bind at most ±(2^53 − 1)). | 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. Also a to-one $update or $upsert beside $connect, $connectOrCreate, $create, $set or $disconnect on the same relation: the row it would update is the newly linked one. | Write either the relation or its reference field, in the relation's cardinality. Relink a to-one relation in one write and update the linked row in another. |
|
|
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, or policy.engine or policy.principal when policy is set. | 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. `policy` and `prebuilt` are root-only keys; in an overlay they read as unknown. | 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, forceTransaction under an effective transactionsStyle "none", an after check on any action but update, or a policy engine's governed, paths or afterChecks naming a Resource, field or path the Core Contract does not have, or its unheld naming a Resource the data source's model does not have (the message names the Resource or the rule). | 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; or a client or agent entry for a Resource managed elsewhere, which those layers never have. | Use the model's exact name. After a schema change run `npm run aventara:prepare`. Configure a Resource managed elsewhere at the root or in application only. |
|
|
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), or, on a host that adds members beside the Resources, is one of those members' names (on Next.js: tx and transaction on avClient, appTx and transaction on avApp). | Rename the model; the table it maps to can keep its name. A host member's name is also left free when the layer it belongs to hides the Resource. |
|
|
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
|
@@ -16,7 +16,7 @@ npx @aventara/cli@rc init # or: add Aventara to an existing NestJS 12
|
|
|
16
16
|
npm install @aventara/core@rc # the package on its own
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
Node `^22.
|
|
19
|
+
Node `^22.23.4 || >=24.22.1`. ES modules, with a CommonJS `default` condition.
|
|
20
20
|
|
|
21
21
|
## Documentation
|
|
22
22
|
|
|
@@ -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,40 @@ 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
|
+
## The client layer, called with your own values
|
|
122
|
+
|
|
123
|
+
`framework.client` reads its arguments in the wire forms a decoded request body carries: a `datetime` is its ISO string,
|
|
124
|
+
a `bigint` its base-10 string. That suits a test that should behave like a remote caller. Server code that acts for a
|
|
125
|
+
remote caller, such as a Server Component or a Server Action, holds `Date`s and `bigint`s instead. It calls
|
|
126
|
+
`framework.serverClient`:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
const recent = await framework.serverClient.Post.find.many({
|
|
130
|
+
where: { createdAt: { gt: new Date(Date.now() - 86_400_000) } },
|
|
131
|
+
select: ["id", "title"],
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`serverClient` has the client layer's types and rules: what the client layer hides is absent, and the client
|
|
136
|
+
pipelines, validation, exposure and policy run. Its arguments are the application forms (`Date`, `bigint`,
|
|
137
|
+
`AvDecimal`, `Uint8Array`), the same ones `framework.application` takes, and its results are `framework.client`'s.
|
|
138
|
+
Each call copies its arguments, encodes them into the wire forms and runs the request `framework.client` would run.
|
|
139
|
+
`framework.serverClientTx` builds client-layer transaction handles the same way; one `framework.transaction([...])`
|
|
140
|
+
may hold them beside `clientTx` handles, never beside `appTx` ones.
|
|
141
|
+
|
|
142
|
+
## Every code, explained
|
|
143
|
+
|
|
144
|
+
`AvErrorCatalog` holds every code this package answers, reports in a validation issue, or refuses to start with: its
|
|
145
|
+
`kind`, `message`, `cause`, `fix` and `docs` URL. The same catalog ships beside `dist` as `ERRORS.md`, and each entry
|
|
146
|
+
of a startup `AvConstructionError`'s `diagnostics` carries its code's `docs` URL.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { AvErrorCatalog } from "@aventara/core";
|
|
150
|
+
|
|
151
|
+
AvErrorCatalog.A2004.fix; // "Fix the argument each issue's path points at, …"
|
|
152
|
+
AvErrorCatalog.V1000.docs; // "https://aventara-framework.vercel.app/docs/error-codes#v1000"
|
|
153
|
+
```
|
|
154
|
+
|
|
121
155
|
## Required create fields are typed
|
|
122
156
|
|
|
123
157
|
Each Resource's Contract states what a create cannot do without (`createRequirements`), as the adapter reads it off
|
|
@@ -149,13 +183,16 @@ it a `compute.create`, or exempt `create.many` and `create.count` in that layer
|
|
|
149
183
|
A field your operation pipe fills server-side is now required of the caller. Give it a `compute.create` stage (or a
|
|
150
184
|
database default) so callers can leave it out, or have them pass it.
|
|
151
185
|
|
|
186
|
+
Inside the pipeline the requirements hold from write compute on. A variant's guards, pipes and filters run before
|
|
187
|
+
compute, and their typed arguments require no field of a create, since compute may still fill it: `args.data.slug` is
|
|
188
|
+
`string | undefined` in a pipe, and a pipe may return a create without it. The variant's hooks and interceptors run
|
|
189
|
+
after compute and see every required field present.
|
|
190
|
+
|
|
152
191
|
## Returning a façade from a method
|
|
153
192
|
|
|
154
193
|
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
|
|
156
|
-
|
|
157
|
-
`clientTx` (`AvClientFacade`, `AvAppTxResourceFacade`, …). The declaration writes the name, with the Contract view as
|
|
158
|
-
its argument, so this compiles:
|
|
194
|
+
Every node of the typed façades has a name for that, and so does the Contract view they are typed over, so a method can
|
|
195
|
+
return any of them without an annotation:
|
|
159
196
|
|
|
160
197
|
```ts
|
|
161
198
|
@Injectable()
|
|
@@ -165,21 +202,48 @@ export class PostsService {
|
|
|
165
202
|
posts() {
|
|
166
203
|
return this.framework.application.Post;
|
|
167
204
|
}
|
|
205
|
+
|
|
206
|
+
findMany() {
|
|
207
|
+
return this.framework.application.Post.find.many;
|
|
208
|
+
}
|
|
168
209
|
}
|
|
169
210
|
```
|
|
170
211
|
|
|
171
|
-
The
|
|
172
|
-
return type to keep it small: `AppFramework["application"]["Post"]` is written as that reference, about 0.2 KB.
|
|
212
|
+
The declaration file writes a short reference, under half a kilobyte per method at any schema size:
|
|
173
213
|
|
|
174
214
|
```ts
|
|
175
|
-
posts():
|
|
176
|
-
|
|
177
|
-
|
|
215
|
+
posts(): import("@aventara/core").AvApplicationResourceFacade<
|
|
216
|
+
import("@aventara/core").AvContractView<import("…/discovery.artifact.js").AvModel, never, "omit", never, "omit", never, never>,
|
|
217
|
+
"Post">;
|
|
218
|
+
findMany(): import("@aventara/core").AvApplicationProjectingCall<
|
|
219
|
+
import("@aventara/core").AvContractView<import("…/discovery.artifact.js").AvModel, never, "omit", never, "omit", never, never>,
|
|
220
|
+
"Post", { readonly family: "find"; readonly variant: "many" }>;
|
|
178
221
|
```
|
|
179
222
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
223
|
+
The names, one set per surface (`application`, `client`, `serverClient`, `appTx`, `clientTx`, `serverClientTx`):
|
|
224
|
+
|
|
225
|
+
| Node | `framework.application` | the others |
|
|
226
|
+
| --- | --- | --- |
|
|
227
|
+
| the whole surface | `AvApplicationFacade` | `AvClientFacade`, `AvServerClientFacade`, `AvAppTxFacade`, `AvClientTxFacade`, `AvServerClientTxFacade` |
|
|
228
|
+
| one Resource | `AvApplicationResourceFacade` | `AvClientResourceFacade`, … |
|
|
229
|
+
| one family | `AvApplicationFamilyFacade` | `AvClientFamilyFacade`, … |
|
|
230
|
+
| a variant that takes a `select` or `include` | `AvApplicationProjectingCall` | `AvClientProjectingCall`, `AvServerClientProjectingCall`, `AvAppTxProjectingCall`, `AvClientTxProjectingCall`, `AvServerClientTxProjectingCall` |
|
|
231
|
+
| a variant that does not (the `count` variants: `find.count`, `update.count`, …) | `AvApplicationPlainCall` | `AvClientPlainCall`, `AvServerClientPlainCall`, `AvAppTxPlainCall`, `AvClientTxPlainCall`, `AvServerClientTxPlainCall` |
|
|
232
|
+
|
|
233
|
+
Each takes the view, `AvContractView`, as its first argument. The view's arguments are your model by its name, the root's
|
|
234
|
+
and the overlay's exposure as written, and the `fields` entries your configuration's `resources` blocks hold. A
|
|
235
|
+
configuration with no `fields` entries writes `never` there. One that has them writes those entries, stages erased, after
|
|
236
|
+
every reference: measured at about 23 KB per method over 50 Resources that each configure a field. Annotate the method
|
|
237
|
+
(`posts(): AppFramework["application"]["Post"]`) if that matters to you; the annotation is written as that reference.
|
|
238
|
+
|
|
239
|
+
You never need to write these names yourself. They exist so a declaration file can name what a method returns, and a
|
|
240
|
+
project that reads only your declarations types the method exactly as your Framework does. An application that publishes
|
|
241
|
+
no declarations can set `"declaration": false` and skip the question.
|
|
242
|
+
|
|
243
|
+
The configuration itself costs almost nothing to declare. The Prisma adapter's discovery artifact names the model's
|
|
244
|
+
type (`AvModel`), so the scaffold's `aventaraConfig()`, a `buildFramework` and its `AppFramework` alias write
|
|
245
|
+
`AvAdapter<AvModel>` and `AvFramework<AvModel, …>`: about 0.5 KB at any schema size, where rc.1 wrote
|
|
246
|
+
the model out once per mention (1.2 MB over 50 Resources).
|
|
183
247
|
|
|
184
248
|
An exported `defineResource(...)` result, and an exported configuration or Framework builder holding typed stages,
|
|
185
249
|
need no annotation: their stages are written by their public types (`readonly AvGuard[]`, `readonly AvPipe[]`, …),
|
|
@@ -187,8 +251,8 @@ never with the model the inline stages were typed from.
|
|
|
187
251
|
|
|
188
252
|
## Server-side calls copy their input
|
|
189
253
|
|
|
190
|
-
`framework.application`, `framework.client`, `framework.
|
|
191
|
-
`clientTx` builders read what you pass them once, when you call them, and copy it into plain data the framework owns.
|
|
254
|
+
`framework.application`, `framework.client`, `framework.serverClient`, `framework.execute`,
|
|
255
|
+
`framework.executeTransaction`, and the `appTx` / `clientTx` / `serverClientTx` builders read what you pass them once, when you call them, and copy it into plain data the framework owns.
|
|
192
256
|
Validation, your guards and pipes, computed values and the adapter all see only the copy, and your stages see it
|
|
193
257
|
frozen: only a pipe changes the arguments, by returning new ones, so what the validators checked is what the adapter
|
|
194
258
|
writes. In practice:
|
|
@@ -287,6 +351,200 @@ It compiles to `framework.contracts.agent`, identified by its own hash, and `fra
|
|
|
287
351
|
endpoint. A host runs agent calls through `AvProtocol.bind(framework, { origin: "agent" })`, which validates them
|
|
288
352
|
exactly as client calls are validated, against the Agent Contract.
|
|
289
353
|
|
|
354
|
+
## A policy engine
|
|
355
|
+
|
|
356
|
+
`policy` hands authorization to an engine Aventara never imports. The engine answers three questions per
|
|
357
|
+
Resource — may the principal run this action (`can`), which rows (`scopeOf`, a `where` fragment), which fields
|
|
358
|
+
(`fieldsOf`) — and core enforces the answers on every client and agent request:
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
const framework = await createFramework({
|
|
362
|
+
api: "/api",
|
|
363
|
+
adapter,
|
|
364
|
+
policy: {
|
|
365
|
+
engine, // satisfies AvPolicyEngine
|
|
366
|
+
principal: ({ $state }) => $state.user ?? null,
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
await framework.application.Note.find.many({}, { as: user }); // checked as `user`
|
|
371
|
+
await framework.application.Note.find.many({}); // server code: not checked
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
The read scope is applied at the adapter call, after every developer stage, to the root `where`, every to-many include
|
|
375
|
+
and its `$count`, and every relation filter. A field the engine governs (its `governed` list) is never in a default
|
|
376
|
+
projection; name it to get it, `select: ["$defaults", "email"]`. A refusal answers `403 A4002`.
|
|
377
|
+
|
|
378
|
+
Writes are checked too. A create asks `can` about each new row, whose rules may look rows up through `exists`. An
|
|
379
|
+
update or delete acts only on rows the caller may change and read; a single-row one that misses answers `A4002` when
|
|
380
|
+
the caller can read the row and `A2003` when it cannot. A field the write sends must be in
|
|
381
|
+
`fieldsOf({ access: "write", action })`, `action` being `"create"` or `"update"` (an upsert's branches and a nested
|
|
382
|
+
record's directive each their own), so a field can be locked on update and still written at create. A rule that
|
|
383
|
+
reaches another Resource through a relation (a note only in a campaign its author plays in) reads which of the
|
|
384
|
+
target's fields the reference names from the Core Contract (`framework.contracts.core`, a relation's `references`
|
|
385
|
+
beside its `referenceFields`); no Contract a caller is served states them. An update that moves a row out of what the caller reads, or fails an `after` condition
|
|
386
|
+
(`can` asked about the row the update returned), is rolled back. For an update the engine lists in `afterChecks`, that
|
|
387
|
+
question also carries `before`, the row as it was before the update; it is present only then. An engine that also
|
|
388
|
+
answers the optional `grantsOf` is asked, before the write, which of its grants cover that row (`exists` then sees
|
|
389
|
+
nothing the request writes), and gets the answer back on the after check as `beforeGrants`. Such an engine refuses
|
|
390
|
+
an after check that carries `before` without `beforeGrants` rather than deciding `before` after the write.
|
|
391
|
+
A typed literal in a fragment the engine answers may be in its application form (`Date`, `AvDecimal`, `bigint`) or its
|
|
392
|
+
wire form (an ISO string, a decimal string, a digit string), as a policy read from JSON states it. Where `transactionsStyle` is `"interactive"`, a
|
|
393
|
+
checked write with a check after it (an update, a nested write) runs in a transaction of its own, and a create or
|
|
394
|
+
delete checked before it runs is one statement; where it is `"none"`, a write that could only be checked after it runs
|
|
395
|
+
is refused (for after conditions, the updates the engine lists in `afterChecks`).
|
|
396
|
+
|
|
397
|
+
Nested writes (`$create`, `$connect` and the other relation directives) run as one call inside that transaction. Before
|
|
398
|
+
it, every row they link is held to the right scope: a linked row that holds the reference must be one the caller may
|
|
399
|
+
change, one whose parent holds it one the caller may read. Writing a reference field itself (`authorId: "u2"`) links
|
|
400
|
+
the same row, and is held the same way: the row it names must be one the caller may read. Where the adapter can link
|
|
401
|
+
the row in that write, core hands it the reference as a `$connect` carrying the read scope, so the write itself refuses
|
|
402
|
+
a row outside it (`A4002`), with no query of its own; where it cannot (a bulk write on Prisma), one `exists` per target
|
|
403
|
+
Resource asks before the write. Nothing is asked where that Resource is read by everyone, and a `null` reference is not
|
|
404
|
+
asked about. Only a reference to a Resource the Core Contract shows is held; one the root's exemptions hide is not, nor
|
|
405
|
+
one the engine lists in its optional `unheld`: a Resource its rules read in conditions and decide no read of (a
|
|
406
|
+
Resource another service owns, say), so its read scope is `false` for every caller. A reference to one of those, and a
|
|
407
|
+
`$connect` or `$set` that links one by the reference's own values where the writing row holds the reference, is
|
|
408
|
+
decided by the write's own grant
|
|
409
|
+
(`can`, and the after check `afterChecks` lists); reads of it are decided by `scopeOf` as always. An engine that has the
|
|
410
|
+
optional `hosted` is told the held Resources once the Framework is built (`hosted({ held })`), so it can check its own
|
|
411
|
+
rules against them; it answers nothing differently. A to-many `$update` or
|
|
412
|
+
`$delete` acts only on the rows the caller may change or delete, as `update.many` does. After the call, every row they changed is checked as an update is
|
|
413
|
+
(the after check, the move-out re-read), and every row they created is read back as stored and asked `can`; one refusal
|
|
414
|
+
rolls the whole write back. Where `transactionsStyle` is `"none"`, a nested write under a policy is refused.
|
|
415
|
+
|
|
416
|
+
## An access provider
|
|
417
|
+
|
|
418
|
+
`access` takes the policy engine and the request state from one object, an `AvAccessProvider`. Aventara Access
|
|
419
|
+
provides one, and you can write your own. It has two members:
|
|
420
|
+
|
|
421
|
+
- `policy`: the engine, as `policy.engine` takes it.
|
|
422
|
+
- `state({ headers, request })`: seeds each request's `ctx.$state`. Its `me` is the principal the engine is asked
|
|
423
|
+
about (`null` for an anonymous caller).
|
|
424
|
+
|
|
425
|
+
The host calls `state` once per request: `@aventara/nest` with the platform request's headers and the request itself,
|
|
426
|
+
`@aventara/next` with the request's headers (and the route's `Request` on a route call). With `access`, leave out
|
|
427
|
+
`policy` and the host's own `state` option; either one beside it is refused at startup, and as a type error where the
|
|
428
|
+
configuration's type shows both. Server code that calls the Framework directly passes the state itself,
|
|
429
|
+
`{ state: await access.state({ headers }) }`.
|
|
430
|
+
|
|
431
|
+
A point-of-sale till with its own PIN sign-in, written by hand:
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
import type { AvAccessProvider } from "@aventara/core";
|
|
435
|
+
|
|
436
|
+
type Cashier = { id: string; role: "cashier" | "manager" };
|
|
437
|
+
|
|
438
|
+
declare module "@aventara/core" {
|
|
439
|
+
interface AvState {
|
|
440
|
+
me?: Cashier | null;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export const tillAccess = {
|
|
445
|
+
// The till sends the PIN the cashier typed as `x-pos-pin`.
|
|
446
|
+
state: async ({ headers }) => ({ me: await cashierByPin(headers.get("x-pos-pin")) }),
|
|
447
|
+
policy: {
|
|
448
|
+
can: ({ principal, resource, action }) => {
|
|
449
|
+
const me = principal as Cashier | null;
|
|
450
|
+
if (me === null) return false; // no PIN, no till
|
|
451
|
+
if (resource === "Refund" && action !== "read") return me.role === "manager";
|
|
452
|
+
return true;
|
|
453
|
+
},
|
|
454
|
+
scopeOf: () => true,
|
|
455
|
+
fieldsOf: () => ({ always: "all", perRow: {} }),
|
|
456
|
+
},
|
|
457
|
+
} satisfies AvAccessProvider;
|
|
458
|
+
|
|
459
|
+
const framework = await createFramework({ api: "/api", adapter, access: tillAccess });
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Every stage reads the cashier as `ctx.$state.me`, typed by the `AvState` declaration above.
|
|
463
|
+
|
|
464
|
+
### `$access` and the provider's `client`
|
|
465
|
+
|
|
466
|
+
A provider may also state `client`: where the app mounted Aventara Access (`url`, absolute `http(s)` or root-relative)
|
|
467
|
+
and the verbs it offers (`signIn`, `signUp`, `signOut`, `resetPassword`, `me`, and on Access's `kiosk` profile
|
|
468
|
+
`switchUser`, `lock`, `setPin`, `enrollDevice`, `revokeDevice`; each optional, any other name refused), each with its route
|
|
469
|
+
(`method`, `path`), the JSON Schema of its arguments and result (the subset a `json` field's shape uses), and `call`,
|
|
470
|
+
the verb run in process. Aventara Access fills it in. It is compiled into the Client Contract's `access` section with
|
|
471
|
+
each `call` dropped, so the hash changes only for apps whose provider states it. Every client then carries `$access`:
|
|
472
|
+
|
|
473
|
+
- `can(questions, options?)`: the batched `POST {api}/_access/can`, one boolean per question. An empty list answers
|
|
474
|
+
`[]` (`200 A1000`), not a refusal: a page with nothing to ask is no client bug, unlike an empty `/_transactions` plan.
|
|
475
|
+
- `me()`: the token identity `{ sub, claims }`, or `null`; and the sign-in verbs the provider offers. A verb the
|
|
476
|
+
provider leaves out (a `service` profile has no `signIn`/`signUp`; only a `kiosk` profile has `switchUser`, `lock`,
|
|
477
|
+
`setPin`, `enrollDevice`, `revokeDevice`) is no member of `$access`. `switchUser` and `enrollDevice` resolve
|
|
478
|
+
Access's answer, `lock`, `setPin` and `revokeDevice` nothing, as `signIn` and `signOut` do.
|
|
479
|
+
|
|
480
|
+
`framework.serverClient.$access` answers `can` in process under the call's `state` (or, with `headers` and no
|
|
481
|
+
`state`, the state the provider seeds from them; with neither, anonymously), and runs each verb through its `call` with
|
|
482
|
+
the headers you pass. The section's `url` and each verb's `path` are checked wherever a ClientContract is read: an
|
|
483
|
+
absolute `http(s)` URL or a root-relative path other than `/`, a path starting with one `/`, neither holding a control
|
|
484
|
+
character, a space or a `.`/`..` segment (percent-encoded too). Under Nest, hand the platform request and response too, so Access can read the
|
|
485
|
+
session and set a sign-in's cookie:
|
|
486
|
+
|
|
487
|
+
```ts
|
|
488
|
+
const me = await framework.serverClient.$access.me({ headers: req.headers });
|
|
489
|
+
await framework.serverClient.$access.signIn(body, { headers: req.headers, request: req, response: res });
|
|
490
|
+
const [canRefund] = await framework.serverClient.$access.can([{ resource: "Refund", action: "create" }], { state });
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
Without `access`, `serverClient` has no `$access` at all; a provider without `client` offers `can` alone.
|
|
494
|
+
|
|
495
|
+
On Access's `kiosk` profile, `switchUser`, `lock` and `enrollDevice` rotate the device's credential cookie: under
|
|
496
|
+
Nest, call them with the platform `response` (`{ headers, request, response }`), or the rotated cookie never reaches
|
|
497
|
+
the device and its next switch is refused. Under Next, call them from a Server Action. Never call a verb that changes
|
|
498
|
+
anything inside a cache (Next's `"use cache"`, a memoized helper): a cached answer replays one caller's result to
|
|
499
|
+
another and skips the call. Every verb's answer is projected onto its result schema, so no session token or device
|
|
500
|
+
secret reaches server code either.
|
|
501
|
+
|
|
502
|
+
## A model managed elsewhere
|
|
503
|
+
|
|
504
|
+
Some tables belong to another service: Better Auth's `user`, or another service's schema in the same database. Your
|
|
505
|
+
service joins into them and must not write or migrate them. Mark such a model at the root:
|
|
506
|
+
|
|
507
|
+
```ts
|
|
508
|
+
const framework = await createFramework({
|
|
509
|
+
api: "/api",
|
|
510
|
+
adapter,
|
|
511
|
+
resources: { Account: { managedElsewhere: true } },
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
- `Account` has no `create`, `update`, `delete` or `upsert` in any layer, and its fields take no write. A call is a
|
|
516
|
+
type error, and an untyped one is refused with `A2002`.
|
|
517
|
+
- A relation into it keeps only `$connect` and `$disconnect`, and only where your model holds the key (`Post.authorId`
|
|
518
|
+
for `Post.author`). A nested `$create`, `$update` or `$upsert` into it is a type error and `A2004` (`V1006`).
|
|
519
|
+
- `framework.application` reads `Account` and filters and includes through `Post.author`. The client and agent layers
|
|
520
|
+
leave out `Account` and every relation into it, so `/_contract`, the generated client and the MCP tools never show
|
|
521
|
+
it. An overlay can't bring it back.
|
|
522
|
+
- A stage on one of its write families is refused at startup (`COMPILER_UNAVAILABLE_OPERATION`), and
|
|
523
|
+
`managedElsewhere` in an overlay is `CONFIG_FORBIDDEN_IN_OVERLAY`. So is any `client.resources.Account` or
|
|
524
|
+
`agent.resources.Account` entry (`COMPILER_UNKNOWN_RESOURCE`): those layers never have it, so nothing there runs.
|
|
525
|
+
- A delete that would write it through a referential action is refused at startup (`CONFIG_INVALID_VALUE` at
|
|
526
|
+
`resources.Account.managedElsewhere`): if any layer can delete a row whose deletion cascades into `Account` or sets
|
|
527
|
+
its reference to null or its default, directly or through other cascades. Take those deletes away at the root, or
|
|
528
|
+
make the reference `onDelete: Restrict`.
|
|
529
|
+
- A create that only the hidden relation could satisfy is refused at startup (`COMPILER_HIDDEN_REQUIRED_FIELD`), naming
|
|
530
|
+
the reference field to expose instead, so no client advertises a create that can't succeed.
|
|
531
|
+
- `framework.contracts.core` marks it `managedElsewhere: true`, and states every Resource's database `schema` where
|
|
532
|
+
the adapter knows it (`@@schema("auth")` with `@aventara/prisma7-adapter`). No other Contract carries either.
|
|
533
|
+
|
|
534
|
+
Two things stay yours. A key update follows the owner's foreign key (`ON UPDATE CASCADE` rewrites `Account`'s rows;
|
|
535
|
+
core has no `onUpdate` facts). And your own code — `ctx.$sql`, an application transaction's SQL step, a stage that
|
|
536
|
+
calls Prisma directly — can still write `Account`.
|
|
537
|
+
|
|
538
|
+
Aventara doesn't migrate anything, so tell Prisma too: list the table as external in `prisma.config.ts`.
|
|
539
|
+
|
|
540
|
+
```ts
|
|
541
|
+
export default defineConfig({
|
|
542
|
+
schema: "prisma/schema.prisma",
|
|
543
|
+
experimental: { externalTables: true },
|
|
544
|
+
tables: { external: ["auth.Account"] },
|
|
545
|
+
});
|
|
546
|
+
```
|
|
547
|
+
|
|
290
548
|
## License
|
|
291
549
|
|
|
292
550
|
PolyForm Shield 1.0.0 with an additional permission. It is free to use, including in commercial applications, but you
|
|
@@ -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,28 @@ 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[]];
|
|
41
|
+
/**
|
|
42
|
+
* The target's scalar fields that `referenceFields` name, pair by pair in
|
|
43
|
+
* the same order (`Post.author`: `referenceFields: ["authorId"]`,
|
|
44
|
+
* `references: ["id"]`, so `Post.authorId` holds `User.id`). Stated only
|
|
45
|
+
* beside `referenceFields`, with as many entries; absent on the end that
|
|
46
|
+
* holds no reference, where the other end (`inverse`) states the pairs.
|
|
47
|
+
*/
|
|
48
|
+
readonly references?: readonly [string, ...string[]];
|
|
27
49
|
};
|
|
28
50
|
/** One field the adapter discovered: a scalar or a relation. */
|
|
29
51
|
export type AvAdapterField<M extends AvAdapterModel> = AvAdapterScalarField<M> | AvAdapterRelationField<M>;
|
|
@@ -13,3 +13,12 @@ export type AvAdapterIdentifier<TFields extends Readonly<Record<string, AvAdapte
|
|
|
13
13
|
AvAdapterScalarFieldKey<TFields>,
|
|
14
14
|
...AvAdapterScalarFieldKey<TFields>[]
|
|
15
15
|
];
|
|
16
|
+
/**
|
|
17
|
+
* The Resource's identifier as the Adapter states it first: in the Adapter's
|
|
18
|
+
* own order (an ORM adapter's is its schema's, so the primary key). The Core
|
|
19
|
+
* Contract sorts identifiers for its hash, so its first one is the
|
|
20
|
+
* alphabetically first (`email` before `id`) and says nothing about which is
|
|
21
|
+
* the primary key; the model keeps the order it declared. Empty for a Resource
|
|
22
|
+
* the model does not hold or that has no identifier.
|
|
23
|
+
*/
|
|
24
|
+
export declare function adapterPrimaryIdentifier(model: AvAdapterModel, resource: string): readonly string[];
|
|
@@ -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
|
}
|