@aventara/core 0.1.0-pilot.3 → 1.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/CHANGELOG.md +338 -0
  2. package/README.md +100 -3
  3. package/dist/adapters/adapter-discovery-diagnostic.d.ts +2 -0
  4. package/dist/adapters/adapter-field.d.ts +3 -26
  5. package/dist/adapters/adapter-identifier.d.ts +1 -11
  6. package/dist/adapters/adapter-model.d.ts +12 -0
  7. package/dist/adapters/adapter-operation-executor.interface.d.ts +5 -1
  8. package/dist/adapters/adapter-resource.d.ts +7 -28
  9. package/dist/adapters/adapter.interface.d.ts +25 -26
  10. package/dist/compiler/contract-compiler-diagnostic.d.ts +8 -0
  11. package/dist/compiler/contract.compiler.d.ts +11 -22
  12. package/dist/compiler/contract.compiler.js +67 -0
  13. package/dist/config/any-framework-config.d.ts +5 -26
  14. package/dist/config/config-diagnostic.d.ts +2 -0
  15. package/dist/config/config-layer.d.ts +6 -19
  16. package/dist/config/config.resolver.d.ts +9 -14
  17. package/dist/config/config.resolver.js +1 -0
  18. package/dist/config/config.validator.d.ts +6 -3
  19. package/dist/config/config.validator.js +6 -0
  20. package/dist/config/define-fields.d.ts +15 -48
  21. package/dist/config/effective-fields.d.ts +6 -29
  22. package/dist/config/entrypoint.normalizer.d.ts +6 -18
  23. package/dist/config/framework-behaviors-config.d.ts +11 -10
  24. package/dist/config/framework-config.d.ts +19 -16
  25. package/dist/config/framework-fields-config.d.ts +20 -7
  26. package/dist/config/framework-restrictions-config.d.ts +41 -61
  27. package/dist/config/pipeline-config.d.ts +7 -17
  28. package/dist/contracts/capability.d.ts +13 -49
  29. package/dist/contracts/client-contract-structure.validator.d.ts +28 -54
  30. package/dist/contracts/client-contract-structure.validator.js +99 -2
  31. package/dist/contracts/contract-canonicalizer.d.ts +4 -6
  32. package/dist/contracts/contract-canonicalizer.js +32 -0
  33. package/dist/contracts/contract-hash.d.ts +3 -2
  34. package/dist/contracts/contract-limits.d.ts +7 -1
  35. package/dist/contracts/contract-protocol.d.ts +7 -8
  36. package/dist/contracts/contract.d.ts +20 -4
  37. package/dist/contracts/enum-definition.d.ts +2 -0
  38. package/dist/contracts/field.d.ts +40 -167
  39. package/dist/contracts/identifier.d.ts +1 -16
  40. package/dist/contracts/json-shape.d.ts +65 -0
  41. package/dist/contracts/json-shape.js +1 -0
  42. package/dist/contracts/lifecycle-trait.d.ts +4 -3
  43. package/dist/contracts/reference-write-limit.d.ts +7 -0
  44. package/dist/contracts/reference-write-limit.js +1 -0
  45. package/dist/contracts/resource-operations.d.ts +2 -62
  46. package/dist/contracts/resource.d.ts +2 -0
  47. package/dist/contracts/scalar-value-type.d.ts +28 -48
  48. package/dist/contracts/scalar.d.ts +4 -3
  49. package/dist/contracts/transaction-capability.d.ts +2 -0
  50. package/dist/contracts/wire-scalar-format.d.ts +6 -21
  51. package/dist/framework/contract-view.d.ts +152 -130
  52. package/dist/framework/framework-construction.error.d.ts +6 -1
  53. package/dist/framework/framework.d.ts +33 -46
  54. package/dist/framework/framework.factory.d.ts +3 -17
  55. package/dist/framework/framework.factory.js +56 -13
  56. package/dist/framework/operation-facade.d.ts +3 -7
  57. package/dist/index.d.ts +8 -40
  58. package/dist/index.js +0 -20
  59. package/dist/internal.d.ts +35 -0
  60. package/dist/internal.js +16 -0
  61. package/dist/json/avz.d.ts +96 -0
  62. package/dist/json/avz.js +212 -0
  63. package/dist/json/index.d.ts +6 -0
  64. package/dist/json/index.js +1 -0
  65. package/dist/json/json-shape.registry.d.ts +22 -0
  66. package/dist/json/json-shape.registry.js +16 -0
  67. package/dist/json/zod-issue.translator.d.ts +51 -0
  68. package/dist/json/zod-issue.translator.js +142 -0
  69. package/dist/operations/operation-arguments.d.ts +198 -303
  70. package/dist/operations/operation-call.d.ts +13 -15
  71. package/dist/operations/operation-identity.d.ts +6 -2
  72. package/dist/operations/operation-identity.js +3 -0
  73. package/dist/operations/operation-result.d.ts +15 -61
  74. package/dist/operations/operation-validator-core.d.ts +6 -5
  75. package/dist/operations/operation-validator-core.js +28 -10
  76. package/dist/operations/operation-validator.d.ts +22 -9
  77. package/dist/operations/operation-validator.js +26 -10
  78. package/dist/operations/projection-scope.resolver.d.ts +1 -11
  79. package/dist/operations/structural-limits.walker.d.ts +19 -0
  80. package/dist/operations/structural-limits.walker.js +344 -0
  81. package/dist/operations/validation-code.d.ts +5 -17
  82. package/dist/operations/write-scope.resolver.d.ts +3 -28
  83. package/dist/protocol/absent-route.responder.d.ts +25 -39
  84. package/dist/protocol/absent-route.responder.js +32 -6
  85. package/dist/protocol/av-protocol.d.ts +2 -8
  86. package/dist/protocol/contract-document.responder.d.ts +5 -18
  87. package/dist/protocol/http-request.decoder.d.ts +18 -59
  88. package/dist/protocol/http-request.decoder.js +11 -3
  89. package/dist/protocol/http-request.reader.d.ts +5 -5
  90. package/dist/protocol/http-response.encoder.d.ts +14 -20
  91. package/dist/protocol/http-status.table.d.ts +0 -8
  92. package/dist/protocol/index.d.ts +2 -7
  93. package/dist/protocol/operation-response.guard.d.ts +0 -15
  94. package/dist/protocol/protocol-failure.d.ts +8 -27
  95. package/dist/protocol/protocol-headers.d.ts +15 -42
  96. package/dist/protocol/protocol-surface.d.ts +0 -22
  97. package/dist/protocol/protocol.binder.d.ts +19 -27
  98. package/dist/protocol/protocol.binder.js +3 -1
  99. package/dist/protocol/protocol.handler.d.ts +19 -23
  100. package/dist/protocol/protocol.handler.js +14 -4
  101. package/dist/protocol/unmounted-route.error.d.ts +19 -0
  102. package/dist/protocol/unmounted-route.error.js +10 -0
  103. package/dist/runtime/caller-data.copier.d.ts +59 -0
  104. package/dist/runtime/caller-data.copier.js +470 -0
  105. package/dist/runtime/caller-data.door.d.ts +56 -0
  106. package/dist/runtime/caller-data.door.js +65 -0
  107. package/dist/runtime/computed-read-runtime.js +3 -10
  108. package/dist/runtime/computed-value.error.d.ts +13 -0
  109. package/dist/runtime/computed-value.error.js +9 -0
  110. package/dist/runtime/create-requirement.d.ts +43 -7
  111. package/dist/runtime/create-requirement.js +89 -9
  112. package/dist/runtime/decimal.d.ts +5 -3
  113. package/dist/runtime/diagnostics-reporter.d.ts +33 -37
  114. package/dist/runtime/diagnostics-reporter.js +11 -4
  115. package/dist/runtime/error-boundary.d.ts +14 -10
  116. package/dist/runtime/error-boundary.js +65 -35
  117. package/dist/runtime/execution-options.d.ts +3 -7
  118. package/dist/runtime/field-behavior.d.ts +14 -21
  119. package/dist/runtime/framework-error.d.ts +14 -2
  120. package/dist/runtime/json-value.d.ts +7 -15
  121. package/dist/runtime/misuse-reporter.d.ts +32 -0
  122. package/dist/runtime/misuse-reporter.js +19 -0
  123. package/dist/runtime/mutation-planner.d.ts +6 -12
  124. package/dist/runtime/mutation-planner.js +13 -11
  125. package/dist/runtime/operation-request.d.ts +11 -11
  126. package/dist/runtime/operation-response.d.ts +18 -23
  127. package/dist/runtime/operation-runtime.d.ts +25 -33
  128. package/dist/runtime/operation-runtime.js +37 -18
  129. package/dist/runtime/outcome-mapper.js +12 -2
  130. package/dist/runtime/output-validator.d.ts +10 -0
  131. package/dist/runtime/output-validator.js +11 -0
  132. package/dist/runtime/owned-execution.registry.d.ts +13 -0
  133. package/dist/runtime/owned-execution.registry.js +7 -0
  134. package/dist/runtime/pipeline.d.ts +33 -36
  135. package/dist/runtime/referential-action.d.ts +8 -26
  136. package/dist/runtime/request-envelope.validator.d.ts +5 -0
  137. package/dist/runtime/request-envelope.validator.js +118 -0
  138. package/dist/runtime/request-id.d.ts +3 -10
  139. package/dist/runtime/scalar-runtime.d.ts +42 -14
  140. package/dist/runtime/scalar-runtime.js +162 -79
  141. package/dist/transactions/deferred-operation.d.ts +10 -21
  142. package/dist/transactions/operation-arguments-encoder.d.ts +1 -0
  143. package/dist/transactions/operation-fingerprint.d.ts +3 -1
  144. package/dist/transactions/transaction-envelope.d.ts +2 -4
  145. package/dist/transactions/transaction-integrity-error.d.ts +4 -15
  146. package/dist/transactions/transaction-plan-assembler.d.ts +6 -1
  147. package/dist/transactions/transaction-plan-assembler.js +74 -75
  148. package/dist/transactions/transaction-plan-validator.d.ts +33 -24
  149. package/dist/transactions/transaction-plan-validator.js +204 -153
  150. package/dist/transactions/transaction-reference-collector.d.ts +30 -0
  151. package/dist/transactions/transaction-reference-collector.js +204 -0
  152. package/dist/transactions/transaction-reference-resolver.d.ts +9 -12
  153. package/dist/transactions/transaction-reference-resolver.js +10 -5
  154. package/dist/transactions/transaction-reference-validator.js +24 -17
  155. package/dist/transactions/transaction-reference.d.ts +31 -64
  156. package/dist/transactions/transaction-request.d.ts +6 -8
  157. package/dist/transactions/transaction-response.d.ts +10 -7
  158. package/dist/transactions/transaction-response.js +24 -1
  159. package/dist/transactions/transaction-runtime.d.ts +7 -5
  160. package/dist/transactions/transaction-runtime.js +4 -3
  161. package/dist/transactions/transaction-slot-path-error.d.ts +5 -15
  162. package/dist/transactions/transaction-slot-writer.d.ts +3 -13
  163. package/package.json +17 -3
  164. package/dist/operations/operation-boolean-nodes.d.ts +0 -11
  165. package/dist/operations/operation-boolean-nodes.js +0 -283
  166. package/dist/operations/operation-nesting.d.ts +0 -8
  167. package/dist/operations/operation-nesting.js +0 -307
package/CHANGELOG.md ADDED
@@ -0,0 +1,338 @@
1
+ # @aventara/core
2
+
3
+ ## 1.0.0-rc.0
4
+
5
+ ### Major Changes
6
+
7
+ - First release candidate of Aventara 1.0. The public API is now considered stable; changes before 1.0.0 are possible
8
+ based on feedback.
9
+
10
+ ### Minor Changes
11
+
12
+ - 24ffbab: A `Json` field can now have a type. Build its shape with `AvZ` from the new entry `@aventara/core/json` and declare it
13
+ on the field: `fields: { Post: { meta: { json: AvZ.object({ title: AvZ.string() }) } } }`. `framework.application`,
14
+ `framework.client`, `appTx`/`clientTx` and the generated client then read and write the field as that type instead of
15
+ generic JSON, and every write to it — create, update, upsert, nested writes and transaction steps, a value taken from
16
+ an earlier step included — is checked before the database is asked: a value out of shape is refused with `A2004` and
17
+ one issue per problem at its exact path (`V1000` missing key, `V1001` wrong type, `V1002` outside an enum or literal,
18
+ `V1004` integer out of range, `V1005` unknown key). Shapes are types only and their objects are closed; reads and
19
+ filters are never checked. Only a field the adapter discovered as `Json` takes a shape, and a Zod schema is not
20
+ accepted. The shape is part of the ClientContract (as JSON Schema), so declaring or changing one changes its hash:
21
+ regenerate the client. `@aventara/core` now depends on `zod` (`^4.6.5`), which only `@aventara/core/json` loads. The
22
+ shape types `AvZShape`, `AvZObject`, `AvZOptional` and `AvZNullable` are exported too, so a project that emits
23
+ declarations can export a shape or a configuration holding one.
24
+ - 24ffbab: Transaction steps can now reference earlier results in `$connect`, `$disconnect`, `$set`, `$connectOrCreate`'s `where`,
25
+ a `cursor`, a unique `where`, and anywhere in a filter — `AND`/`OR`/`NOT` and relation filters included, with every
26
+ scalar operator. `framework.appTx` and `framework.clientTx` type each position by what it holds: a filter's value,
27
+ `equals` and `not` take a source of the field's own type (nullable only where the field is, and a `null` then matches
28
+ the rows where the field is unset, as a literal `null` does); a unique key, a `cursor`, a comparison or a text operator a
29
+ non-null source; `in`, `notIn`, `hasEvery` and `hasSome` a list field; `has` one value. The server checks the same rule
30
+ before anything runs and refuses a mismatch with `A2007`/`V1011`. Nested relation writes take references at any depth
31
+ too: the values and keys of nested `$create`, `$update` and `$upsert` data, and the `where` of a nested `$update`,
32
+ `$delete` or `$upsert`. A reference in an ordering or a projection's nested `where` is still refused.
33
+ - 24ffbab: **Behaviour changes:** server-side calls now copy what you pass them. `framework.application`, `framework.client`,
34
+ `framework.execute`, `framework.executeTransaction` and the `appTx`/`clientTx` builders read your arguments once, when
35
+ you call them, into plain data the framework owns; validation, guards, pipes, computed values and the adapter see only
36
+ that copy, so changing or revoking your object afterwards (an Immer draft after `produce`) no longer affects the
37
+ operation. A live Proxy (Vue `reactive`, MobX) still works.
38
+
39
+ A value that cannot be read — a revoked Proxy, a getter that throws — or that is not plain data — a class instance or a
40
+ `Map`; `Date`, `Decimal` and `Uint8Array` are fine — is now refused as your request's error: `A2004` with one `V1001` at
41
+ the value's path (`["arguments", …]`, or `["operations", i, "args", …]` with `cause.operation` in a transaction), before
42
+ any guard, pipe or adapter runs. Before, it was `A3000` (an internal error) or, from `appTx`/`clientTx`, a `TypeError`
43
+ thrown at the builder call; a builder now never throws, and `framework.transaction([...])` answers the refusal. The same
44
+ holds for a `framework.execute` / `executeTransaction` request object that cannot be read. Two answers change for
45
+ inputs that were never supported: a class instance where an object of arguments belongs (`where`, `data`, …) was
46
+ accepted or failed as `A3000`, and is now refused; inherited, non-enumerable and symbol keys are dropped, as JSON drops
47
+ them.
48
+
49
+ New optional root setting `misuse` (types `FrameworkMisuseSink`, `FrameworkMisuseEvent`), beside `diagnostics`: it
50
+ receives one event per refused value — its `kind` (`"unreadable-value"` or `"non-plain-value"`), the `path` the caller
51
+ received, and the operation's `origin`, `resource`, `family`, `variant` and transaction step (`operation`) when they
52
+ could be read — never the value itself. It cannot change a response; an error it throws or a promise it rejects is
53
+ ignored. `diagnostics` keeps receiving only unexpected failures.
54
+
55
+ HTTP requests are not copied again: a body the protocol parses from raw content is already plain data.
56
+
57
+ The generated client reads your arguments through one guard too: a value that cannot be read is refused with a
58
+ `TypeError` naming where (`… the value at /where/id could not be read.`) before anything is sent, and a `tx` builder
59
+ never throws — `transaction([...])` rejects with that error instead. Regenerate the client to pick this up.
60
+
61
+ - 24ffbab: A nested `$update` or `$delete` through a to-many relation filters plain fields only on Prisma 7, and Aventara now says
62
+ so up front: a relation filter in its `where` — `{ posts: { $update: { where: { category: { name: "x" } }, … } } }`, at
63
+ any depth, under `AND`/`OR`/`NOT` too — is refused with `A2004` / `V1006` before the database is asked, and is a type
64
+ error in `framework.application`, `framework.client`, `appTx`/`clientTx` and the generated client. Before, it reached
65
+ Prisma and fail as `A3001`. A to-one relation's nested filter keeps relation filters. The limit is stated by the adapter
66
+ in the Contract (`nestedWriteFilter: "scalar"`), so the ClientContract hash changes: regenerate the client.
67
+
68
+ ### Patch Changes
69
+
70
+ - 24ffbab: **Behaviour changes:** `@aventara/core` exports fewer names. Every name it no longer exports was undocumented; every
71
+ documented name (`createFramework`, `FrameworkConfig`, `defineFields`, `Decimal`, `FrameworkError`, the pipeline types and
72
+ the rest) is still exported. Types shown in a hover or an error message still resolve; what changes is only which names
73
+ your own code can import. `OperationScope` was a deprecated alias: use `RequestOrigin`.
74
+
75
+ What Aventara's own packages need from core moved to `@aventara/core/internal`. It is not public API: it carries no
76
+ stability promise, and any of its exports may change or disappear in any release. Applications import from
77
+ `@aventara/core`.
78
+
79
+ Moved to `@aventara/core/internal`: `AdapterField`, `AdapterFieldKey`, `AdapterFieldMap`, `AdapterIdentifier`, `AdapterLifecycleTrait`, `AdapterOperation`, `AdapterOperationExecutor`, `AdapterRelationField`, `AdapterResource`, `AdapterScalarField`, `AdapterScalarFieldKey`, `advertisedDirectives`, `advertisedReducers`, `ApplicationContractView`, `ApplicationOperationRequest`, `ApplicationTransactionRequest`, `AVENTARA_PROTOCOL_VERSION`, `BUILT_IN_SCALARS`, `BuiltinNonRow`, `canonicalizeContract`, `canonicalizeJson`, `CanonicalJsonError`, `ClientContractHashInput`, `ClientOperationRequest`, `ClientProtocolIdentity`, `ClientTransactionRequest`, `compileContracts`, `computeClientContractHash`, `computeOperationFingerprint`, `DEFAULT_CONTRACT_LIMITS`, `FrameworkOperationRequest`, `FrameworkTransactionRequest`, `isOperationVariantAvailable`, `LIFECYCLE_TRAITS`, `OPERATION_CODES`, `OperationFingerprint`, `PROJECTION_SCOPES`, `projectionScopeOf`, `resolveFrameworkConfig`, `SemanticOperationArguments`, `TRANSACTION_CAPABILITIES`, `validateClientContractStructure`, `VALIDATION_CODES`, `verifyClientContractHash`, `WRITE_SCOPES`, `writeScopeOf`.
80
+
81
+ No longer exported from any entry: `advertisedDirectivesAt`, `advertisedReducersAt`, `ClientContractStructureVerdict`, `ContractCompilationResult`, `DeferredOperation`, `EffectiveFieldKeyAt`, `holdsAt`, `holdsInAnyScope`, `isOperationFingerprint`, `isTransactionReferenceCandidate`, `isTransactionReferenceObject`, `operationFailure`, `OperationFingerprintNode`, `OperationInternalErrorCode`, `operationNoMatch`, `OperationScope`, `operationSuccess`, `OperationValidationResult`, `RelationMutationAction`, `ResolvedFrameworkConfig`, `ResolvedFrameworkScopeConfig`, `ResolvedPipelineConfig`, `ScalarFilterInput`, `ScalarMutationAction`, `transactionCommitted`, `TransactionCreateDataInput`, `transactionInfrastructureFailure`, `TransactionOperationArgumentsFor`, `transactionOperationFailure`, `TransactionPlan`, `TransactionReferenceCandidate`, `TransactionScalarValue`, `TransactionUpdateDataInput`, `validateFrameworkConfig`, `validateOperationArguments`, `VALIDATION_CODE_MESSAGES`.
82
+
83
+ - 24ffbab: Each package ships its `CHANGELOG.md`, so the release notes are in the installed package beside the README.
84
+ - 24ffbab: A `null` given to a filter operator that has no `null` meaning — `gt`, `contains`, `in`, `has`, `mode`, `isEmpty`,
85
+ `path`, a JSON `string*` operator and the rest — is now refused as `A2004` / `V1001` at that operator, before the
86
+ database is asked, and is a type error. Before, it reached the database and failed as `A3001`. `equals: null` and
87
+ `not: null` still match the rows where a nullable field is unset or set.
88
+ - 24ffbab: **Diagnostics:** if your own HTTP host mounts only some of `protocol.surface()` and a request for a route it left out
89
+ reaches `protocol.answerAbsent`, your `diagnostics` sink now receives one `A3000` diagnostic naming the route: an
90
+ `"operation"` diagnostic with the Resource, family and variant (a `"transaction"` one for `/_transactions`), whose
91
+ `error` is named `"UnmountedRouteError"` and says which `surface()` entry to mount. The caller's `500 A3000` answer is
92
+ unchanged, and a route your ClientContract does not offer is still answered without a report. `@aventara/nest` mounts
93
+ every route, so its applications are not affected.
94
+ - 24ffbab: **Diagnostics:** a `FrameworkError` you throw on purpose with an `A3xxx` code — from a guard, pipe, hook, interceptor,
95
+ computed field or adapter, or returned by a filter — now reaches your `diagnostics` sink once, as the error you threw,
96
+ under its code, with `deliberate: true` on the diagnostic. Before, the caller received the generic `A3000`/`A3001` and
97
+ your message and issues were reported nowhere. A failure Aventara did not expect carries no `deliberate` field, so a sink
98
+ can tell the two apart. What the caller receives is unchanged, and `A2xxx`/`A4xxx` outcomes are still not reported.
99
+ - 24ffbab: **Diagnostics:** a result that does not match its Contract — an adapter row with a wrong value, a missing field, a
100
+ result of the wrong shape — now reaches your `diagnostics` sink as one `A3000` diagnostic whose `error` (`name`
101
+ `"OutputValidationError"`) names the operation and lists the problems (`error.issues`). Before, the caller got `A3000`
102
+ and the sink got nothing unless reading the result threw. The same now holds for a computed field: a read or write
103
+ behavior that returns a value its field refuses is reported as an error named `"ComputedValueError"` with the issues
104
+ (before, the sink received only the field's location). What the caller receives is unchanged. A filter that inspects
105
+ the error now sees that error instead of a `FrameworkError` with code `A3000`.
106
+
107
+ The `FrameworkError` documentation now states the message rule: for any code outside `A3xxx`, the message is sent to
108
+ the caller exactly as written, so it must not carry database text, SQL, a stack, a path or a secret.
109
+
110
+ - 24ffbab: **Behaviour changes:** Pipelines and guards read where a request came from as `request.origin` (was `request.scope`).
111
+ The same property is renamed everywhere it means `"application"` or `"client"`: the pipeline context
112
+ (`context.origin`), the request objects `framework.execute` and `framework.executeTransaction` take
113
+ (`{ origin: "client", … }`), the context a computed field's behaviour receives, and every diagnostic the diagnostics
114
+ sink receives. There is no `scope` alias: replace `scope` with `origin` in those places. Nothing sent over HTTP
115
+ changes.
116
+
117
+ The Nest host's log line for an internal failure now reads `(client origin, request …)` instead of
118
+ `(client scope, request …)`.
119
+
120
+ - 24ffbab: **Types:** a guard, pipe, hook, interceptor or filter typed with `Guard`, `Pipe`, `BeforeHook`, `AfterHook`,
121
+ `Interceptor` or `Filter` without a model type now type-checks wherever one typed for your model is expected, so you
122
+ can put it in an `as const` configuration (the shape `aventara init` writes), in `client.pipelines` or
123
+ `application.pipelines`, and mix it with model-typed callbacks in one list. Before, TypeScript refused it there, and
124
+ the way around it was a structural parameter type such as `Pick<PipelineContext, "family" | "transport">`, which you no
125
+ longer need (it keeps working). A callback typed for a different model is still refused.
126
+ - a973e89: An issue your own code throws now reaches the caller with the message you wrote. When a pipe, guard, hook, interceptor,
127
+ filter or computed behavior throws `new FrameworkError("A2004", { message, issues: [{ path, code, message }] })`, each
128
+ issue's `message` is sent as written instead of the generic text for its code — standalone, inside a transaction plan,
129
+ and over HTTP. The message must have a visible character and be at most 1,000 characters; otherwise the caller receives
130
+ the generic text for the code and your message goes to the `diagnostics` sink. The rest is unchanged: an issue whose
131
+ `code` is not a validation code (`V1000` and up) is dropped, a `path` that does not name a member of the request's own
132
+ arguments is dropped, an `A3xxx` outcome still carries no issues, and an issue an Adapter throws still carries the
133
+ generic text for its code.
134
+ - 1e65e9a: `framework.execute(request)` and `framework.executeTransaction(request)` now check the request object itself before
135
+ running it. A wrong one — not an object, a missing or empty `requestId`, an `origin` other than `"application"` or
136
+ `"client"`, a client request without a `protocol` of `{ version, hash }`, malformed `transport`, `protocol` or
137
+ `transport` on an application request, or a missing or non-string `resource`, `family` or `variant` — is answered
138
+ `A2004` with one issue per wrong member at its own path (`["requestId"]`, `["protocol", "hash"]`, …), and nothing
139
+ runs: no guard, pipe or hook, no adapter call. Before, most of these answered `A3000` (an internal error) or ran as
140
+ though the request were well formed. A well-formed request is unchanged, including `A2005`/`A2006` for a stale
141
+ `protocol` and `A2001` for an unknown Resource.
142
+ - a973e89: A write that sets an optional relation both ways — its reference field and the relation itself, such as `categoryId`
143
+ and `category: { $connect: … }` in the same record — is now refused before it reaches the database, exactly like a
144
+ required relation: `A2004` with a `V1012` issue at the relation, message `Set "categoryId" or "category", not both.`
145
+ Before, the database refused it and the caller received `A3001`. It applies to `create`, `update` and `upsert`, to
146
+ nested writes at any depth, and to each step of a transaction.
147
+
148
+ The discovery artifact now records, for each relation, the fields that hold its reference (`references` on each edge
149
+ of `referentialActions`), and a create requirement no longer repeats them. Regenerate your artifact with
150
+ `npm run aventara:prepare` (or `aventara-prisma7-generate`); an artifact from an earlier version is refused with a
151
+ message that says so. An adapter's `referentialActions` edges carry the same `references` member.
152
+
153
+ - a973e89: With the Prisma adapter, a record that writes one relation through its reference field and another relation directly —
154
+ such as `authorId` beside `category: { $connect: … }` — is now refused before it reaches the database: `A2004` with a
155
+ `V1012` issue at the relation write, message
156
+ `Use relation fields only, or reference fields only, in one record: "authorId" is a reference field and "category" is a relation.`
157
+ Before, Prisma refused it and the caller received `A3001`. Write the record's relations all as relations
158
+ (`author: { $connect: … }, category: { $connect: … }`) or all through their fields (`authorId`, `categoryId`). A
159
+ reference field beside a relation that holds no reference on that side — `mentorId` beside `posts: { $create: … }` on
160
+ an author — is unchanged and still accepted. It applies to `create`, `update` and `upsert`, to nested writes at any
161
+ depth, and to each step of a transaction.
162
+
163
+ The limit is part of the Contract: a Contract whose data source has it carries `referenceWrites: "uniform"`, so the
164
+ Contract hash of every Prisma-backed application changes. Regenerate your discovery artifact
165
+ (`npm run aventara:prepare`) and your generated client (`avclient generate`). An adapter states the limit with
166
+ `referenceWrites: "uniform"` in its model; without it, nothing changes.
167
+
168
+ - 1e65e9a: A write that sets a required relation both ways — its reference field and the relation itself, such as `authorId` and
169
+ `author: { $connect: … }` in the same `data` — is now refused before it reaches the database: `A2004` with a `V1012`
170
+ issue at the relation (`["arguments", "data", "author"]`), message `Set "authorId" or "author", not both.` It applies to
171
+ `create`, `update` and `upsert`, to nested writes at any depth, and to each step of a transaction, and nothing is
172
+ written. Before, the database refused it and the caller received `A3001`. Writing either one alone is unchanged.
173
+ - 1e65e9a: A guard, pipe, hook, interceptor or filter typed for any Resource — `Guard`, `Pipe`, `BeforeHook` and the rest without a
174
+ model argument, or a callback whose context is a `Pick<PipelineContext, …>` — in the root `pipelines` no longer erases
175
+ `framework.application`, `framework.client`, `framework.appTx` and `framework.clientTx` to an untyped object. They stay
176
+ typed from your adapter's model with any mix of stage types, at the root and in the `application` and `client`
177
+ overlays, with or without `as const`, and a callback written inline is still told your Resource names.
178
+ - 24ffbab: **Behaviour changes, transactions:** an object carrying `$ref` in a step's `args` is now copied when you call
179
+ `framework.executeTransaction`, like the rest of your arguments. Where a `$ref` belongs (a field value, a filter
180
+ value, a `$connect` key) it is answered exactly as before; changing it after the call no longer affects the plan.
181
+ Placed anywhere else — the whole `where`, a whole relation value, the whole `data` — a member of it that cannot be
182
+ read (a getter that throws, a revoked Proxy) is now your request's error, `A2004` with one `V1001` at that member's
183
+ path and `cause.operation`, reported to `misuse`; before, it was `A3000`. The framework never runs a getter inside an
184
+ object that carries `$ref`, wherever it stands, so such a getter is refused unread.
185
+
186
+ A value whose first read throws is now refused even if reading it again would succeed: `A2004`/`V1001` at the object
187
+ or array whose read failed. Before, it was copied from the second read.
188
+ - 2d00ad7: An issue your own code throws from a transaction step now keeps its `path`. A pipe, guard, hook, interceptor,
189
+ filter or computed behavior does not know where its operation stands in a plan, so it writes the path as it would for
190
+ a single call — `["arguments", "data", "name"]`. Inside `framework.transaction`, `framework.executeTransaction` and
191
+ `POST /_transactions` that path is now checked against that step's own arguments and sent as
192
+ `["operations", i, "args", "data", "name"]` beside `cause.operation`; before, it was dropped and the issue reached the
193
+ caller without a path. A path written as `["operations", i, "args", …]` with the step's own index is accepted as
194
+ before, and a path that does not name a member of the step's arguments is still dropped.
195
+ - 384a50c: **Behaviour changes, transactions:** every issue about a transaction step's arguments now names where it is
196
+ in the plan, `["operations", i, "args", …]`, beside `cause.operation: i`. Before, the framework's own checks — a wrong
197
+ type, an unknown field or operator, a structural limit, a Json value out of its `AvZ` shape, a value that is not valid
198
+ JSON — answered `["arguments", …]` inside a plan while a bad `$ref`, a refused value and a step that depends on a
199
+ cascaded delete already answered `["operations", i, "args", …]`; now one plan uses one spelling, through
200
+ `framework.transaction`, `framework.executeTransaction` and `POST /_transactions` alike. A single call is unchanged
201
+ (`["arguments", …]`). If your code reads `issue.path` from a transaction's answer, drop the `"operations", i, "args"`
202
+ prefix to get the path inside that step's arguments. `@aventara/testing`'s protocol corpus row "a client-hidden
203
+ field — /_transactions" and `fixtures/protocol-v1.json` carry the new path.
204
+ - d43d1cc: **Types:** `diagnostic.deliberate` now type-checks on any `FrameworkDiagnostic`, without checking
205
+ `diagnostic.kind` first, so a `diagnostics` sink can log it for every report. It is `true` only on an `"operation"`
206
+ diagnostic for a `FrameworkError` you threw on purpose with an `A3xxx` code, and always absent on a `"transaction"`
207
+ diagnostic, which reports the transaction's own failure (`A3002`). Before, reading it on the union was a TypeScript
208
+ error (`TS2339`).
209
+ - 3f98749: **Types:** `framework.application`, `framework.client`, `framework.appTx` and `framework.clientTx` now follow
210
+ your `restrictions` the way the generated client does. A field whose capability you turn off — `select`, `filter`,
211
+ `order`, `create` or `update` — is refused in that position and left out of the result types; an operation you turn
212
+ off (`operations: { delete: { unique: false } }`) is not on the façade; and a unique operation disappears when its
213
+ only identifier is hidden. Each façade reads its own layer: `framework.client` the root restrictions with
214
+ `client.restrictions` over them, `framework.application` the root restrictions with `application.restrictions` over
215
+ them — so `hidden` now applies to `framework.application` too. Before, only `client`'s `hidden` was reflected, so with
216
+ `passwordHash: { capabilities: { select: false, … } }` a call such as
217
+ `framework.client.User.find.many({ select: ["passwordHash"] })` compiled and every result type carried `passwordHash`.
218
+ Nothing changes at runtime: the server refused those calls before and still does. A restriction value typed as a plain
219
+ `boolean` rather than `true`/`false` still leaves the field as it was.
220
+ - 032f792: **Behaviour changes, transactions:** an error about a transaction step itself — rather than its arguments — now
221
+ names the step too, so every path in a transaction's answer is a path through the request you sent: an unknown or
222
+ hidden Resource is at `["operations", i, "resource"]`, a fingerprint that does not match at
223
+ `["operations", i, "fingerprint"]`, a missing or unknown key on the step at `["operations", i, "<key>"]`, and a step
224
+ that is not an object at `["operations", i]`. An operation the step cannot run points at the key that is wrong:
225
+ `["operations", i, "family"]` when the family is not a string, not a standard family, or one the Resource offers no
226
+ variant of; `["operations", i, "variant"]` when the family is right and the variant is not (unknown, not offered or
227
+ restricted for it). Before, these answered `["resource"]`, `["operation"]`, `["fingerprint"]` and `["<key>"]`.
228
+ `cause.operation` and the outcome code are unchanged: an unknown Resource is still `A2001`, an unavailable operation
229
+ `A2002`. `framework.transaction([...])` refuses an entry that is not a handle, or one handle placed twice, at
230
+ `["operations", i]`. A single call is unchanged. `@aventara/testing`'s protocol corpus and `fixtures/protocol-v1.json`
231
+ carry the new paths.
232
+ - dbe6961: **Behaviour changes:** a single call to an operation a Resource does not offer now names the key of the
233
+ request that is wrong instead of `["operation"]`, which no request has: `["family"]` when the family is not a standard
234
+ one or the Resource offers none of its operations, `["variant"]` when the family is right and the variant is not. This
235
+ holds for `framework.execute` and for an HTTP route, whose `/_resources/:resource/:family/:variant` segments are named
236
+ by those same keys. The code is unchanged (`A2002` / `V1006`), and so is `["resource"]` for an unknown Resource.
237
+ `@aventara/testing`'s protocol corpus and `fixtures/protocol-v1.json` carry the new paths.
238
+
239
+ ## 0.1.0-pilot.4
240
+
241
+ ### Patch Changes
242
+
243
+ - f37dcd6: The documentation shipped with the package — the comments an editor shows on hover, and the README — now describes
244
+ what each export does, how to use it, what it returns and what it throws. Notes about how the package was built and
245
+ why are no longer part of what is published.
246
+
247
+ The declarations a generated client copies from `@aventara/core` carry the same documentation.
248
+
249
+ ## 0.1.0-pilot.3
250
+
251
+ ## 0.1.0-pilot.2
252
+
253
+ ### Patch Changes
254
+
255
+ - c2a3b7c: A `bigint` value outside the signed 64-bit range is refused as `A2004` with a `V1004` issue at its path, from the wire
256
+ and from the application alike, before any pipeline or adapter runs. Before this release it reached the database and
257
+ answered `A3001`.
258
+ - e69e88d: The published packages are written for the people who use them. The JavaScript ships without comments; the type
259
+ declarations keep their documentation, and it, the error messages, the protocol fixtures' labels and the comments in a
260
+ generated client no longer carry the project's internal development notes.
261
+ - ab58424: A pipe's output is checked against the server's own rules before it reaches the adapter. The request is validated
262
+ against the caller's Contract; after each pipe, the arguments are validated against the Core Contract (plus fields the
263
+ caller's scope adds), so a pipe may still write what the caller cannot — a field the client Contract hides, such as a
264
+ password hash — but not what the server cannot store. A refusal is the new internal code `A3004 PIPE_OUTPUT_INVALID` (HTTP
265
+ 500). The caller receives only the generic `A server pipeline produced invalid arguments.`, like every internal
266
+ failure; the pipe (`pipes[i]` and its function name), the operation, the offending field and its V-code go to the
267
+ diagnostics sink as a `PipeOutputError`, and `@aventara/nest` writes them into its error log line. Nothing reaches the
268
+ adapter. This holds for operations and transaction steps alike (a plan answers with `cause.operation`). Generated
269
+ clients map `A3004` to `InternalError`; the protocol fixtures in `@aventara/testing` include an `A3004` row.
270
+ - 23937f8: A create that lacks a value the database requires is refused as `A2004` with a `V1000` issue at the value's path,
271
+ instead of reaching the database and answering `A3001`. It covers `create.one`, every row of `create.many`, `upsert`'s
272
+ `create` branch, nested `$create` (and `$connectOrCreate`/`$upsert` creates) at any depth, and transaction steps
273
+ (`["operations", i, "args", …]`). A required relation is satisfied by writing it or every scalar that holds its
274
+ reference; a nested create never needs the relation it is created through. Defaults, `@updatedAt`, autoincrement,
275
+ optional and list columns are never required. The check runs after pipes and computed behaviours, so a value they
276
+ supply counts.
277
+
278
+ An adapter states the requirements through the new optional `Adapter.createRequirements`; the Prisma 7 adapter reads
279
+ them from the generated client's create inputs at generate time and records them in the discovery artifact, so the
280
+ artifact must be regenerated (`aventara:prepare`).
281
+
282
+ ## 0.1.0-pilot.1
283
+
284
+ ### Patch Changes
285
+
286
+ - a45f8a8: The `400 A2000` for a request without its identity headers now says which one: `cause.message` is "The
287
+ Aventara-Contract-Hash header is missing." (or "The Aventara-Protocol-Version header is missing.", or both, naming both),
288
+ instead of the generic "The request is malformed and cannot be interpreted." It names headers only and echoes no value.
289
+ The code, status and every other `A2000` are unchanged. The protocol fixtures in `@aventara/testing` check the new messages, with a row
290
+ for a request with neither header.
291
+ - 799346e: The package READMEs (each package's npm page) are rewritten for the people who use the packages. The Prisma 7
292
+ adapter's README now states the actual behaviour of a to-many relation or `$count` projected on
293
+ `update.many`/`create.many`: a validation refusal, `A2004` with `V1008`, before any statement runs (it said `A3001`).
294
+ Every install or run command names the `@pilot` dist-tag.
295
+
296
+ ## 0.1.0-pilot.0
297
+
298
+ ### Minor Changes
299
+
300
+ - First pilot release of `@aventara/core`: the framework itself — configuration, Contracts, validation, operations,
301
+ pipelines and transactions — independent of any HTTP server or ORM.
302
+
303
+ - **`createFramework(config)`** builds a Framework from an adapter and a configuration. The configuration is typed
304
+ against the adapter's model, so restriction keys, behaviors, `dependsOn` and pipeline `resource` guards are checked
305
+ against the Resources and fields that exist; `defineFields` declares the fields a configuration adds so the rest of
306
+ it can name them.
307
+ - **Contracts.** The adapter's capabilities and your configuration compile into the Core, Application and Client
308
+ Contracts at startup. The ClientContract is what clients are generated from, and its hash identifies it. A scoped
309
+ configuration (`application`, `client`) only narrows what the root allows, and a configuration asking for a
310
+ capability the root or the adapter does not offer fails at startup with `COMPILER_UNAVAILABLE_CAPABILITY` at its
311
+ path. A value the Contract cannot represent, or an invalid ClientContract (`COMPILER_INVALID_CLIENT_CONTRACT`), also
312
+ fails at startup.
313
+ - **Capabilities by argument position.** A relation the database projects or writes only in some operations is
314
+ advertised only there; asking for it elsewhere is refused with `A2004` (`V1008` for a projection, `V1006` for a
315
+ write) before any query, and is a type error.
316
+ - **Typed calls.** `framework.application.<resource>.<family>.<variant>(args)` and `framework.client.…` take arguments
317
+ and return results typed from the Contract (`OperationResultFor`); an optional second argument, `ExecutionOptions`
318
+ (`{ requestId }`), sets the request id every pipeline context, adapter call and diagnostic of the call carries.
319
+ - **Transactions.** `framework.appTx` and `framework.clientTx` build typed plans whose steps can use earlier steps'
320
+ results through `$ref`; `framework.transaction(steps, options)` runs them. A step that depends on rows an earlier
321
+ step's cascading delete removed is refused before the plan runs (`A2004`/`V1019`).
322
+ - **Fields and pipelines.** Computed and virtual fields under `application.fields` and `client.fields`; guards, pipes,
323
+ before and after hooks, interceptors and filters.
324
+ - **Scalars.** `Decimal` (string-backed; `toJSON()` writes its digits; a non-string argument throws a `TypeError`),
325
+ `JsonValue`, and the `ApplicationScalarForms` / `WireScalarForms` maps. A `Date`, `Decimal` or `Uint8Array` at a
326
+ filter position is shorthand for equality.
327
+ - **`@aventara/core/protocol`**, a second entry point: the HTTP protocol as data and pure functions a host mounts —
328
+ `AvProtocol.headers`, `httpStatus`, `scalarFormats`, `normalizeEntrypoint` and `isOperationResponse`, and
329
+ `AvProtocol.bind(framework)` with `surface()` (the routes to mount), `handleOperation`, `handleTransaction`,
330
+ `encodeContract` (`GET /_contract`, with `ETag` and `304`), `answerAbsent`, `failure`, and the decoders and encoders
331
+ under them. A request without an identity header answers `400 A2000`, a wrong protocol version `A2006`, a stale
332
+ contract hash `A2005`; a body larger than `limits.maxRequestBytes` `413 A2010`, a content type other than
333
+ `application/json` `415 A2011`; every `405 A2012` carries `Allow`. A malformed request never throws: it answers
334
+ `400 A2000`.
335
+ - **The entrypoint** is normalized once: `"api"`, `"/api/"` and `"//api//"` become `"/api"`, `"/"` becomes `""`, and an
336
+ entrypoint that is not a plain path (whitespace, a scheme, a query, a `..` segment, a `%` and the like) fails at
337
+ startup with `CONFIG_INVALID_VALUE`.
338
+ - Every export has a `default` condition, so a CommonJS project can `require()` the package.
package/README.md CHANGED
@@ -11,9 +11,9 @@ Core is ORM- and transport-agnostic: it imports neither Prisma nor NestJS. An ap
11
11
  ## Install
12
12
 
13
13
  ```bash
14
- npx @aventara/cli@pilot new my-api # a new NestJS project with Prisma and Aventara
15
- npx @aventara/cli@pilot init # or: add Aventara to an existing NestJS 12 project
16
- npm install @aventara/core@pilot # the package on its own
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.
@@ -24,6 +24,103 @@ Start from the `@aventara/cli` README (what `new` and `init` write), then `@aven
24
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/json` and declare
30
+ it on the field in the configuration:
31
+
32
+ ```ts
33
+ import { AvZ } from "@aventara/core/json";
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
+ entrypoint: "/api",
46
+ adapter,
47
+ fields: { Post: { meta: { json: postMeta } } },
48
+ });
49
+ ```
50
+
51
+ - **Typed.** `framework.application`, `framework.client`, `appTx` and `clientTx` read and write `Post.meta` as
52
+ `PostMeta`, not as generic JSON, and so does a client generated with `avclient` (it emits the type; it needs no
53
+ Zod).
54
+ - **Checked on every write.** A create, update, upsert, nested write or transaction step that writes `meta` — a value
55
+ a transaction step takes from an earlier step included — is checked against the shape before the database is
56
+ asked. A value out of shape is refused with `A2004`, one issue per problem at its exact path:
57
+
58
+ ```json
59
+ { "code": "V1001", "path": ["arguments", "data", "meta", "seo", "title"], "message": "Value must be a string." }
60
+ ```
61
+
62
+ A missing key is `V1000`, a value outside an enum or a literal `V1002`, an integer outside the safe range `V1004`,
63
+ and a key the shape does not declare `V1005`, once per key. Reads and filters are never checked against a shape, so
64
+ rows written before the shape existed stay readable.
65
+ - **Types only.** `AvZ` has `string`, `number`, `int`, `boolean`, `null`, `literal`, `enum`, `object`, `array`,
66
+ `tuple`, `record`, `union`, `discriminatedUnion`, `optional` and `nullable` — no lengths, ranges, patterns,
67
+ refinements, transforms or defaults. Objects are closed: a key the shape does not declare is refused. `optional`
68
+ marks an object key that may be left out.
69
+ - **Where it applies.** Only a field the adapter discovered as `Json` takes a shape, and the shape itself cannot admit
70
+ `null` or absence at its top level — whether the field may be `null` comes from your schema. A shape is built with
71
+ `AvZ` only: a Zod schema is not accepted, as a type error and when the framework starts.
72
+ - **In the Contract.** The shape is part of the ClientContract, as JSON Schema; adding or changing one changes the
73
+ ClientContract hash, so regenerate the client.
74
+
75
+ The shape types `AvZShape`, `AvZObject`, `AvZOptional` and `AvZNullable` are exported so a project that emits
76
+ declarations can name the shapes it exports.
77
+
78
+ `@aventara/core/json` is the only entry that loads Zod (`zod/mini`); an application that declares no shape never loads
79
+ it.
80
+
81
+ ## Server-side calls copy their input
82
+
83
+ `framework.application`, `framework.client`, `framework.execute`, `framework.executeTransaction`, and the `appTx` /
84
+ `clientTx` builders read what you pass them once, when you call them, and copy it into plain data the framework owns.
85
+ Everything after that — validation, your guards and pipes, computed values, the adapter — sees only the copy:
86
+
87
+ - **Changing your object afterwards changes nothing.** An Immer draft revoked after `produce`, or a reactive store that
88
+ moves on, is safe to pass: the call already has its own copy.
89
+ - **A live Proxy works** (Vue `reactive`, MobX): it is read through its traps once.
90
+ - **`Date`, `Decimal` and `Uint8Array`** (a Node `Buffer` too) are copied as themselves; `bigint` and other primitives are
91
+ values.
92
+ - **Only plain data.** Arrays and plain objects (`{}` or `Object.create(null)`) are copied with their own enumerable
93
+ string keys, like JSON: inherited, non-enumerable and symbol keys are dropped. Any other object — a class instance, a
94
+ `Map` — is refused.
95
+ - **A value that cannot be read** (a revoked Proxy, a getter that throws) is refused too. Both refusals are your
96
+ request's error, not the server's: `A2004` with one `V1001` at the value's path — `["arguments", …]`, or
97
+ `["operations", i, "args", …]` with `cause.operation` in a transaction — before any guard, pipe or adapter runs:
98
+
99
+ ```json
100
+ { "code": "V1001", "path": ["arguments", "where", "id"], "message": "Value could not be read." }
101
+ ```
102
+
103
+ A builder never throws: `appTx`/`clientTx` keep the refusal, and `framework.transaction([...])` answers it.
104
+
105
+ To count these refusals, set `misuse` at the root of the configuration, beside `diagnostics`:
106
+
107
+ ```ts
108
+ const framework = await createFramework({
109
+ entrypoint: "/api",
110
+ adapter,
111
+ misuse: (event) => metrics.increment("aventara.misuse", { kind: event.kind }),
112
+ });
113
+ ```
114
+
115
+ It receives one `FrameworkMisuseEvent` per refusal — `kind` (`"unreadable-value"` or `"non-plain-value"`), the `path`
116
+ the caller received, and `origin`, `resource`, `family`, `variant` and the transaction step (`operation`) when they could
117
+ be read — never the value itself. It cannot change the response: an error it throws, or a promise it rejects, is
118
+ ignored. `diagnostics` keeps receiving the server's own unexpected failures, and nothing is reported to both.
119
+
120
+ HTTP requests cost nothing extra: a body that `protocol.handleOperation` / `handleTransaction` parse from raw content
121
+ (as `@aventara/nest` hands it to them) is already plain data and is not copied again. A body your host parsed itself,
122
+ passed as `{ kind: "parsed" }`, is copied like any server-side input.
123
+
27
124
  ## License
28
125
 
29
126
  PolyForm Shield 1.0.0 with an additional permission — free to use, including in commercial applications; you may not
@@ -1,4 +1,6 @@
1
+ /** Codes of {@link AdapterDiscoveryDiagnostic}. */
1
2
  export type AdapterDiscoveryDiagnosticCode = "DISCOVERY_TRANSACTION_CAPABILITY_MISMATCH";
3
+ /** A problem found in what the adapter discovered, reported at framework construction. */
2
4
  export type AdapterDiscoveryDiagnostic = {
3
5
  readonly code: AdapterDiscoveryDiagnosticCode;
4
6
  readonly path: "adapter.transactions";
@@ -3,13 +3,7 @@ import type { LifecycleTrait } from "../contracts/lifecycle-trait.js";
3
3
  import type { AdapterModel } from "./adapter-model.js";
4
4
  /** Provider-observable lifecycle facts available during adapter discovery. */
5
5
  export type AdapterLifecycleTrait = Extract<LifecycleTrait, "DEFAULTED" | "GENERATED_ON_CREATE" | "GENERATED_ON_UPDATE">;
6
- /**
7
- * Scalar-shaped provider field expressed only in Aventara vocabulary.
8
- *
9
- * Scalar capabilities carry no scope qualifier, measured and not assumed: all six
10
- * members are uniform across every operation variant on both pilot engines, so a
11
- * discoverer has nothing per-position to answer here.
12
- */
6
+ /** Scalar-shaped provider field expressed only in Aventara vocabulary. */
13
7
  export type AdapterScalarField<M extends AdapterModel> = {
14
8
  readonly kind: "scalar";
15
9
  readonly type: {
@@ -23,25 +17,7 @@ export type AdapterScalarField<M extends AdapterModel> = {
23
17
  readonly lifecycle: readonly AdapterLifecycleTrait[];
24
18
  readonly capabilities: ScalarFieldCapabilities;
25
19
  };
26
- /**
27
- * Provider relation edge; the target AdapterResource owns object shape.
28
- *
29
- * The capabilities are the Contract's own {@link RelationFieldCapabilities}, scope
30
- * qualifier included, and that sharing is the point: the ADAPTER is the only party
31
- * that can measure which argument positions a relation is reachable in, because
32
- * the answer depends on which provider call each operation variant is routed to.
33
- * Core tabulating it would be a tabulated provider fact and would be wrong for the
34
- * next adapter.
35
- *
36
- * The qualifier is OPTIONAL at this boundary, as it is in the Contract: an adapter
37
- * that has not measured the axis states the unqualified answer and is believed,
38
- * which is what keeps a surface that predates the measurement meaningful rather
39
- * than silently narrowed.
40
- *
41
- * **A scope qualifies the TOP LEVEL of an argument only** — the measurement and
42
- * the reason are recorded on `ProjectionScope`, and a discoverer that narrows
43
- * recursively would advertise less than the provider accepts.
44
- */
20
+ /** A discovered relation field; the `target` Resource describes the related row. */
45
21
  export type AdapterRelationField<M extends AdapterModel> = {
46
22
  readonly kind: "relation";
47
23
  readonly target: Extract<keyof M["resources"], string>;
@@ -49,4 +25,5 @@ export type AdapterRelationField<M extends AdapterModel> = {
49
25
  readonly nullable: boolean;
50
26
  readonly capabilities: RelationFieldCapabilities;
51
27
  };
28
+ /** One field the adapter discovered: a scalar or a relation. */
52
29
  export type AdapterField<M extends AdapterModel> = AdapterScalarField<M> | AdapterRelationField<M>;
@@ -1,16 +1,6 @@
1
1
  import type { AdapterField } from "./adapter-field.js";
2
2
  import type { AdapterModel } from "./adapter-model.js";
3
- /**
4
- * Keys of `TFields` whose discovered field can be scalar-shaped.
5
- *
6
- * `TFields[K]` is an indexed access, not a naked type parameter, so a bare
7
- * conditional would not distribute over a union-valued field. With the
8
- * abstract `AdapterFieldMap` default every key maps to the whole
9
- * `AdapterField` union, which is not a subtype of the scalar branch, so the
10
- * bare form collapses to `never` and makes `AdapterIdentifier` uninhabitable.
11
- * `Extract` selects the scalar branch out of the union instead, so a key is
12
- * kept unless it is provably relation-only.
13
- */
3
+ /** Keys of `TFields` whose discovered field can be scalar-shaped. */
14
4
  export type AdapterScalarFieldKey<TFields extends Readonly<Record<string, AdapterField<AdapterModel>>>> = Extract<{
15
5
  [K in keyof TFields]: [
16
6
  Extract<TFields[K], {
@@ -1,4 +1,5 @@
1
1
  import type { EnumDefinition } from "../contracts/enum-definition.js";
2
+ import type { ReferenceWriteLimit } from "../contracts/reference-write-limit.js";
2
3
  import type { ScalarRegistry } from "../contracts/scalar.js";
3
4
  import type { TransactionCapability } from "../contracts/transaction-capability.js";
4
5
  import type { AdapterResource } from "./adapter-resource.js";
@@ -7,8 +8,19 @@ import type { AdapterResource } from "./adapter-resource.js";
7
8
  * Contract compilation is applied.
8
9
  */
9
10
  export interface AdapterModel {
11
+ /** The scalar types the data source supports. */
10
12
  readonly scalars: ScalarRegistry;
13
+ /** The enums the data source declares, by name. */
11
14
  readonly enums: Readonly<Record<string, EnumDefinition>>;
15
+ /** The discovered Resources, by name. */
12
16
  readonly resources: Readonly<Record<string, AdapterResource<this>>>;
17
+ /** Whether the adapter can run interactive transactions. */
13
18
  readonly transactions: TransactionCapability;
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;
14
26
  }
@@ -1,6 +1,10 @@
1
1
  import type { AdapterModel } from "./adapter-model.js";
2
2
  import type { AdapterOperation } from "./adapter-operation.js";
3
- /** Small execution seam usable both directly and inside provider transactions. */
3
+ /**
4
+ * Executes one operation against the provider, directly or inside a provider
5
+ * transaction.
6
+ */
4
7
  export interface AdapterOperationExecutor<M extends AdapterModel = AdapterModel> {
8
+ /** Runs one validated operation and resolves its raw result. */
5
9
  execute(operation: AdapterOperation<M>): Promise<unknown>;
6
10
  }
@@ -2,40 +2,19 @@ import type { ResourceOperations } from "../contracts/resource-operations.js";
2
2
  import type { AdapterField } from "./adapter-field.js";
3
3
  import type { AdapterIdentifier } from "./adapter-identifier.js";
4
4
  import type { AdapterModel } from "./adapter-model.js";
5
+ /** A discovered Resource's fields, by name. */
5
6
  export type AdapterFieldMap<M extends AdapterModel> = Readonly<Record<string, AdapterField<M>>>;
6
7
  /**
7
- * The field names the adapter discovered for `R`.
8
- *
9
- * This is the lower layer of `EffectiveFieldKey`, in
10
- * `config/effective-fields.ts`: everything the provider already knows about,
11
- * before any configuration is applied. Named in prose rather than with a
12
- * `{@link}` because a resolvable link would need an import, and `adapters/`
13
- * must not reach into `config/`.
14
- *
15
- * `R`'s constraint is written out rather than imported as
16
- * `FrameworkResourceKey<M>` — the identical type — because `adapters/` owns
17
- * the Base layer and must not depend on `config/`.
8
+ * The field names the adapter discovered for Resource `R`, before any
9
+ * configuration adds to them.
18
10
  */
19
11
  export type AdapterFieldKey<M extends AdapterModel, R extends Extract<keyof M["resources"], string>> = Extract<keyof M["resources"][R]["fields"], string>;
20
- /**
21
- * Provider-discovered facts for one Aventara Resource candidate.
22
- *
23
- * `TFields` is never supplied by a caller — every in-repo site writes
24
- * `AdapterResource<M>` — but it is **load-bearing and must not be inlined**. It is
25
- * the indirection that keeps `M` out of this type's body.
26
- *
27
- * `AdapterField<M>` is invariant in `M` (it reads `keyof M["scalars"]` and `keyof
28
- * M["resources"]`). If `M` reached the body, `AdapterResource` would acquire an
29
- * invariant variance marker, and every concrete model would stop satisfying the
30
- * self-typed `AdapterModel.resources: Readonly<Record<string,
31
- * AdapterResource<this>>>` constraint. Measured: 43 type errors across the adapter
32
- * and index type tests, and identically 43 whether the parameter is deleted or
33
- * merely bypassed — so it is the body occurrence that matters, not the arity.
34
- *
35
- * Deleting it is not a cleanup.
36
- */
12
+ /** Provider-discovered facts for one Aventara Resource candidate. */
37
13
  export type AdapterResource<M extends AdapterModel, TFields extends AdapterFieldMap<M> = AdapterFieldMap<M>> = {
14
+ /** The Resource's fields, by name. */
38
15
  readonly fields: TFields;
16
+ /** The field tuples that identify at most one row. */
39
17
  readonly identifiers: readonly AdapterIdentifier<TFields>[];
18
+ /** The standard operations the data source can run for this Resource. */
40
19
  readonly operations: ResourceOperations;
41
20
  };