@aventara/core 1.0.0-rc.0 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/CHANGELOG.md +1140 -0
  2. package/README.md +224 -57
  3. package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
  4. package/dist/adapters/adapter-field.d.ts +11 -11
  5. package/dist/adapters/adapter-identifier.d.ts +6 -6
  6. package/dist/adapters/adapter-model.d.ts +9 -9
  7. package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
  8. package/dist/adapters/adapter-operation.d.ts +3 -3
  9. package/dist/adapters/adapter-resource.d.ts +18 -9
  10. package/dist/adapters/adapter-sql-statement.d.ts +19 -0
  11. package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
  12. package/dist/adapters/adapter.interface.d.ts +49 -29
  13. package/dist/avz/avz-check.factory.d.ts +83 -0
  14. package/dist/avz/avz-check.factory.js +116 -0
  15. package/dist/avz/avz-check.registry.d.ts +35 -0
  16. package/dist/avz/avz-check.registry.js +9 -0
  17. package/dist/avz/avz.d.ts +203 -0
  18. package/dist/{json → avz}/avz.js +150 -8
  19. package/dist/{json → avz}/index.d.ts +2 -1
  20. package/dist/{json → avz}/json-shape.registry.d.ts +7 -7
  21. package/dist/{json → avz}/zod-issue.translator.d.ts +8 -2
  22. package/dist/{json → avz}/zod-issue.translator.js +7 -0
  23. package/dist/compiler/contract-compiler-diagnostic.d.ts +31 -5
  24. package/dist/compiler/contract.compiler.d.ts +18 -18
  25. package/dist/compiler/contract.compiler.js +548 -441
  26. package/dist/compiler/create-requirements.projector.d.ts +49 -0
  27. package/dist/compiler/create-requirements.projector.js +423 -0
  28. package/dist/compiler/field-validator.compiler.d.ts +47 -0
  29. package/dist/compiler/field-validator.compiler.js +238 -0
  30. package/dist/config/any-framework-config.d.ts +19 -12
  31. package/dist/config/config-diagnostic.d.ts +11 -4
  32. package/dist/config/config-layer.d.ts +15 -6
  33. package/dist/config/config.resolver.d.ts +74 -31
  34. package/dist/config/config.resolver.js +108 -44
  35. package/dist/config/config.validator.d.ts +7 -7
  36. package/dist/config/config.validator.js +581 -72
  37. package/dist/config/define-resource.d.ts +57 -0
  38. package/dist/config/define-resource.js +14 -0
  39. package/dist/config/effective-fields.d.ts +19 -23
  40. package/dist/config/entrypoint.normalizer.d.ts +1 -1
  41. package/dist/config/exact-framework-config.d.ts +280 -0
  42. package/dist/config/field-compute.resolver.d.ts +19 -0
  43. package/dist/config/field-compute.resolver.js +115 -0
  44. package/dist/config/field-pipeline-config.d.ts +47 -0
  45. package/dist/config/forced-operation.validator.d.ts +10 -0
  46. package/dist/config/forced-operation.validator.js +66 -0
  47. package/dist/config/framework-config.d.ts +110 -39
  48. package/dist/config/framework-exemptions-config.d.ts +91 -0
  49. package/dist/config/framework-exemptions-config.js +1 -0
  50. package/dist/config/framework-field-definition.d.ts +78 -0
  51. package/dist/config/framework-field-definition.js +28 -0
  52. package/dist/config/framework-resources-config.d.ts +116 -0
  53. package/dist/config/framework-resources-config.js +1 -0
  54. package/dist/config/pipeline-config.d.ts +56 -20
  55. package/dist/config/pipeline-tree.resolver.d.ts +79 -0
  56. package/dist/config/pipeline-tree.resolver.js +220 -0
  57. package/dist/config/stage-type.eraser.d.ts +65 -0
  58. package/dist/config/stage-type.eraser.js +1 -0
  59. package/dist/contracts/capability.d.ts +11 -11
  60. package/dist/contracts/client-contract-structure.validator.d.ts +323 -28
  61. package/dist/contracts/client-contract-structure.validator.js +152 -8
  62. package/dist/contracts/contract-canonicalizer.d.ts +19 -3
  63. package/dist/contracts/contract-canonicalizer.js +141 -28
  64. package/dist/contracts/contract-hash.d.ts +12 -7
  65. package/dist/contracts/contract-hash.js +3 -2
  66. package/dist/contracts/contract-limits.d.ts +4 -1
  67. package/dist/contracts/contract-limits.js +1 -0
  68. package/dist/contracts/contract-lookup.d.ts +5 -5
  69. package/dist/contracts/contract-protocol.d.ts +4 -4
  70. package/dist/contracts/contract.d.ts +24 -16
  71. package/dist/contracts/create-requirements.d.ts +35 -0
  72. package/dist/contracts/create-requirements.js +1 -0
  73. package/dist/contracts/enum-definition.d.ts +1 -1
  74. package/dist/contracts/field-check.d.ts +101 -0
  75. package/dist/contracts/field-check.js +1 -0
  76. package/dist/contracts/field.d.ts +52 -42
  77. package/dist/contracts/identifier.d.ts +4 -4
  78. package/dist/contracts/json-shape.d.ts +12 -1
  79. package/dist/contracts/lifecycle-trait.d.ts +2 -2
  80. package/dist/contracts/resource-operations.d.ts +28 -28
  81. package/dist/contracts/resource.d.ts +15 -9
  82. package/dist/contracts/scalar-value-type.d.ts +15 -15
  83. package/dist/contracts/scalar.d.ts +3 -3
  84. package/dist/contracts/transaction-capability.d.ts +1 -1
  85. package/dist/contracts/wire-scalar-format.d.ts +4 -4
  86. package/dist/framework/contract-view.d.ts +213 -147
  87. package/dist/framework/framework-construction.error.d.ts +10 -10
  88. package/dist/framework/framework-construction.error.js +2 -2
  89. package/dist/framework/framework.d.ts +47 -23
  90. package/dist/framework/framework.factory.d.ts +8 -6
  91. package/dist/framework/framework.factory.js +40 -20
  92. package/dist/framework/operation-facade.d.ts +123 -52
  93. package/dist/framework/operation-facade.js +9 -1
  94. package/dist/index.d.ts +55 -50
  95. package/dist/index.js +6 -6
  96. package/dist/internal.d.ts +15 -10
  97. package/dist/internal.js +3 -1
  98. package/dist/operations/operation-arguments.d.ts +267 -202
  99. package/dist/operations/operation-call.d.ts +27 -28
  100. package/dist/operations/operation-identity.d.ts +10 -10
  101. package/dist/operations/operation-result.d.ts +21 -21
  102. package/dist/operations/operation-validator-core.d.ts +6 -6
  103. package/dist/operations/operation-validator-core.js +13 -2
  104. package/dist/operations/operation-validator.d.ts +9 -9
  105. package/dist/operations/projection-scope.resolver.d.ts +6 -6
  106. package/dist/operations/structural-limits.walker.d.ts +6 -6
  107. package/dist/operations/structural-limits.walker.js +145 -0
  108. package/dist/operations/validation-code.d.ts +11 -6
  109. package/dist/operations/validation-code.js +4 -0
  110. package/dist/operations/write-scope.resolver.d.ts +6 -6
  111. package/dist/protocol/absent-route.responder.d.ts +13 -12
  112. package/dist/protocol/absent-route.responder.js +19 -19
  113. package/dist/protocol/contract-document.responder.d.ts +3 -3
  114. package/dist/protocol/contract-document.responder.js +2 -2
  115. package/dist/protocol/http-request.decoder.d.ts +17 -13
  116. package/dist/protocol/http-request.decoder.js +10 -10
  117. package/dist/protocol/http-request.reader.d.ts +3 -3
  118. package/dist/protocol/http-response.encoder.d.ts +4 -4
  119. package/dist/protocol/http-response.encoder.js +94 -55
  120. package/dist/protocol/http-status.table.d.ts +2 -2
  121. package/dist/protocol/operation-response.guard.d.ts +2 -2
  122. package/dist/protocol/protocol-surface.d.ts +4 -4
  123. package/dist/protocol/protocol.binder.d.ts +50 -27
  124. package/dist/protocol/protocol.binder.js +12 -9
  125. package/dist/protocol/protocol.handler.d.ts +22 -13
  126. package/dist/protocol/protocol.handler.js +44 -9
  127. package/dist/protocol/unmounted-route.error.d.ts +4 -4
  128. package/dist/protocol/unmounted-route.error.js +2 -2
  129. package/dist/runtime/caller-data.copier.d.ts +5 -5
  130. package/dist/runtime/caller-data.copier.js +13 -13
  131. package/dist/runtime/caller-data.door.d.ts +6 -6
  132. package/dist/runtime/caller-data.door.js +6 -4
  133. package/dist/runtime/computed-read-runtime.d.ts +7 -6
  134. package/dist/runtime/computed-read-runtime.js +61 -48
  135. package/dist/runtime/computed-value.error.d.ts +6 -6
  136. package/dist/runtime/computed-value.error.js +3 -3
  137. package/dist/runtime/context-executor.runtime.d.ts +59 -0
  138. package/dist/runtime/context-executor.runtime.js +275 -0
  139. package/dist/runtime/create-requirement.d.ts +10 -81
  140. package/dist/runtime/create-requirement.js +80 -57
  141. package/dist/runtime/decimal.d.ts +2 -2
  142. package/dist/runtime/decimal.js +1 -1
  143. package/dist/runtime/diagnostics-reporter.d.ts +53 -27
  144. package/dist/runtime/diagnostics-reporter.js +24 -0
  145. package/dist/runtime/error-boundary.d.ts +65 -8
  146. package/dist/runtime/error-boundary.js +45 -19
  147. package/dist/runtime/execution-options.d.ts +10 -2
  148. package/dist/runtime/field-check.evaluator.d.ts +29 -0
  149. package/dist/runtime/field-check.evaluator.js +521 -0
  150. package/dist/runtime/field-compute.d.ts +64 -0
  151. package/dist/runtime/field-compute.js +1 -0
  152. package/dist/runtime/field-stage.plan.d.ts +15 -0
  153. package/dist/runtime/field-stage.plan.js +27 -0
  154. package/dist/runtime/field-stage.runtime.d.ts +35 -0
  155. package/dist/runtime/field-stage.runtime.js +143 -0
  156. package/dist/runtime/field-validator.runtime.d.ts +11 -0
  157. package/dist/runtime/field-validator.runtime.js +87 -0
  158. package/dist/runtime/framework-error-class.table.d.ts +33 -0
  159. package/dist/runtime/framework-error-class.table.js +25 -0
  160. package/dist/runtime/framework-error.d.ts +68 -8
  161. package/dist/runtime/framework-error.factory.d.ts +4 -0
  162. package/dist/runtime/framework-error.factory.js +15 -0
  163. package/dist/runtime/framework-error.js +23 -2
  164. package/dist/runtime/interceptor-next.error.d.ts +5 -0
  165. package/dist/runtime/interceptor-next.error.js +6 -0
  166. package/dist/runtime/json-value.d.ts +3 -3
  167. package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
  168. package/dist/runtime/level-lifecycle.runtime.js +42 -0
  169. package/dist/runtime/misuse-reporter.d.ts +12 -12
  170. package/dist/runtime/mutation-planner.d.ts +19 -10
  171. package/dist/runtime/mutation-planner.js +54 -30
  172. package/dist/runtime/nested-write.walker.d.ts +32 -0
  173. package/dist/runtime/nested-write.walker.js +73 -0
  174. package/dist/runtime/operation-request.d.ts +33 -6
  175. package/dist/runtime/operation-response.d.ts +17 -17
  176. package/dist/runtime/operation-runtime.d.ts +113 -25
  177. package/dist/runtime/operation-runtime.js +395 -74
  178. package/dist/runtime/outcome-mapper.d.ts +16 -5
  179. package/dist/runtime/outcome-mapper.js +23 -6
  180. package/dist/runtime/output-validator.d.ts +10 -10
  181. package/dist/runtime/output-validator.js +3 -3
  182. package/dist/runtime/owned-execution.registry.d.ts +6 -6
  183. package/dist/runtime/pipe-output.validator.d.ts +33 -18
  184. package/dist/runtime/pipe-output.validator.js +12 -12
  185. package/dist/runtime/pipeline.d.ts +196 -34
  186. package/dist/runtime/readonly-value.d.ts +32 -0
  187. package/dist/runtime/readonly-value.js +141 -0
  188. package/dist/runtime/request-envelope.validator.d.ts +3 -3
  189. package/dist/runtime/request-envelope.validator.js +12 -7
  190. package/dist/runtime/request-id.d.ts +1 -1
  191. package/dist/runtime/request-state.d.ts +14 -0
  192. package/dist/runtime/request-state.js +1 -0
  193. package/dist/runtime/request-state.resolver.d.ts +12 -0
  194. package/dist/runtime/request-state.resolver.js +20 -0
  195. package/dist/runtime/scalar-runtime.d.ts +31 -16
  196. package/dist/runtime/scalar-runtime.js +16 -18
  197. package/dist/transactions/deferred-operation.d.ts +35 -20
  198. package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
  199. package/dist/transactions/operation-arguments-encoder.js +2 -2
  200. package/dist/transactions/sql-statement.d.ts +77 -0
  201. package/dist/transactions/sql-statement.js +47 -0
  202. package/dist/transactions/sql-statement.validator.d.ts +44 -0
  203. package/dist/transactions/sql-statement.validator.js +134 -0
  204. package/dist/transactions/transaction-envelope.d.ts +4 -4
  205. package/dist/transactions/transaction-integrity-error.d.ts +6 -6
  206. package/dist/transactions/transaction-integrity-error.js +5 -2
  207. package/dist/transactions/transaction-plan-assembler.d.ts +21 -11
  208. package/dist/transactions/transaction-plan-assembler.js +148 -34
  209. package/dist/transactions/transaction-plan-validator.d.ts +29 -20
  210. package/dist/transactions/transaction-plan-validator.js +70 -38
  211. package/dist/transactions/transaction-reference-collector.d.ts +5 -5
  212. package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
  213. package/dist/transactions/transaction-reference-resolver.js +26 -14
  214. package/dist/transactions/transaction-reference-validator.d.ts +34 -4
  215. package/dist/transactions/transaction-reference-validator.js +58 -26
  216. package/dist/transactions/transaction-reference.d.ts +13 -13
  217. package/dist/transactions/transaction-request.d.ts +18 -5
  218. package/dist/transactions/transaction-response.d.ts +6 -6
  219. package/dist/transactions/transaction-runtime.d.ts +8 -7
  220. package/dist/transactions/transaction-runtime.js +166 -50
  221. package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
  222. package/dist/transactions/transaction-settlement.runtime.js +120 -0
  223. package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
  224. package/dist/transactions/transaction-slot-path-error.js +2 -2
  225. package/dist/transactions/transaction-slot-writer.js +2 -2
  226. package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
  227. package/dist/transactions/transaction-stage.runtime.js +123 -0
  228. package/package.json +6 -5
  229. package/src/runtime/field-check.evaluator.ts +720 -0
  230. package/dist/config/define-fields.d.ts +0 -42
  231. package/dist/config/define-fields.js +0 -3
  232. package/dist/config/framework-behaviors-config.d.ts +0 -30
  233. package/dist/config/framework-fields-config.d.ts +0 -42
  234. package/dist/config/framework-restrictions-config.d.ts +0 -142
  235. package/dist/json/avz.d.ts +0 -96
  236. package/dist/runtime/field-behavior.d.ts +0 -48
  237. /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
  238. /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
  239. /package/dist/{json → avz}/index.js +0 -0
  240. /package/dist/{json → avz}/json-shape.registry.js +0 -0
  241. /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
  242. /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
@@ -0,0 +1,64 @@
1
+ import type { AvAdapterModel } from "../adapters/adapter-model.js";
2
+ import type { AvAdapterFieldKey } from "../adapters/adapter-resource.js";
3
+ import type { AvConfigAny } from "../config/any-framework-config.js";
4
+ import type { AvEffectiveFieldKey } from "../config/effective-fields.js";
5
+ import type { AvConfigResourceKey } from "../config/framework-field-definition.js";
6
+ import type { AvComputeReads } from "../framework/operation-facade.js";
7
+ import type { AvContext } from "./pipeline.js";
8
+ import type { AvDeepReadonly } from "./readonly-value.js";
9
+ import type { AvState } from "./request-state.js";
10
+ /**
11
+ * The ctx a compute stage is told: the operation's `$` facts, a read-only view
12
+ * of the request's `$state`, and reads only (`ctx.<Resource>.find.<variant>(args)`,
13
+ * `find.count` included).
14
+ */
15
+ export type AvComputeContext<M extends AvAdapterModel = AvAdapterModel> = AvComputeReads<M> & Pick<AvContext<M>, "$operation" | "$args" | "$origin" | "$requestId" | "$transport" | "$contract"> & {
16
+ /** The request's state, read-only: a write throws a `TypeError`. */
17
+ readonly $state: AvDeepReadonly<AvState>;
18
+ };
19
+ /**
20
+ * What a write-stage compute callback is told about the field it is computing.
21
+ */
22
+ type ComputedWriteContext<M extends AvAdapterModel> = {
23
+ readonly field: string;
24
+ readonly value: unknown;
25
+ readonly data: Readonly<Record<string, unknown>>;
26
+ readonly ctx: AvComputeContext<M>;
27
+ };
28
+ /**
29
+ * Computes a field's value when a row is created, whether or not the caller sent
30
+ * it. It receives the incoming `data` and returns the value to store; returning
31
+ * `undefined` keeps the current value.
32
+ */
33
+ export type AvComputeCreate<M extends AvAdapterModel = AvAdapterModel> = (context: ComputedWriteContext<M>) => unknown | Promise<unknown>;
34
+ /**
35
+ * Computes a field's value when a row is updated, whether or not the caller sent
36
+ * it. It receives the incoming `data` and returns the value to store; returning
37
+ * `undefined` keeps the current value.
38
+ */
39
+ export type AvComputeUpdate<M extends AvAdapterModel = AvAdapterModel> = (context: ComputedWriteContext<M>) => unknown | Promise<unknown>;
40
+ /**
41
+ * Computes a field's value when a row is read. It receives the stored `record`
42
+ * and returns the value to return; returning `undefined` keeps the current value.
43
+ */
44
+ export type ComputeReadFunction<M extends AvAdapterModel = AvAdapterModel, Cfg = AvConfigAny, R extends AvConfigResourceKey<M> = AvConfigResourceKey<M>> = (context: {
45
+ readonly field: AvEffectiveFieldKey<M, Cfg, R>;
46
+ readonly value: unknown;
47
+ readonly record: Readonly<Record<AvEffectiveFieldKey<M, Cfg, R>, unknown>>;
48
+ readonly ctx: AvComputeContext<M>;
49
+ }) => unknown | Promise<unknown>;
50
+ /** The fields of Resource `R` the adapter stores as scalars: what `dependsOn` may name. */
51
+ type StoredScalarKey<M extends AvAdapterModel, R extends AvConfigResourceKey<M>> = string extends AvAdapterFieldKey<M, R> ? string : {
52
+ [F in AvAdapterFieldKey<M, R>]: M["resources"][R]["fields"][F] extends {
53
+ readonly kind: "scalar";
54
+ } ? F : never;
55
+ }[AvAdapterFieldKey<M, R>];
56
+ /**
57
+ * A read compute stage: a compute function, or `{ compute, dependsOn }` naming
58
+ * the fields it reads.
59
+ */
60
+ export type AvComputeRead<M extends AvAdapterModel = AvAdapterModel, Cfg = AvConfigAny, R extends AvConfigResourceKey<M> = AvConfigResourceKey<M>> = ComputeReadFunction<M, Cfg, R> | {
61
+ readonly dependsOn?: readonly StoredScalarKey<M, R>[];
62
+ readonly compute: ComputeReadFunction<M, Cfg, R>;
63
+ };
64
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import type { ResolvedPipelineTree } from "../config/pipeline-tree.resolver.js";
2
+ import type { AvContract } from "../contracts/contract.js";
3
+ /** One origin's field-stage dispatch, planned at startup. */
4
+ export type FieldStagePlan = {
5
+ /** Whether any Resource registers a field pipe. */
6
+ readonly anyPipes: boolean;
7
+ /** Whether any Resource registers a guard a nested target runs: its own, its family's or a field write guard. */
8
+ readonly nestedGuards: boolean;
9
+ /** `resource`'s fields with write guards, in the Resource's field order. */
10
+ readonly guarded: (resource: string) => readonly string[];
11
+ /** `resource`'s fields with field pipes, in the Resource's field order. */
12
+ readonly piped: (resource: string) => readonly string[];
13
+ };
14
+ /** Plans the field-stage dispatch of `tree` under `contract`. */
15
+ export declare function planFieldStages(tree: ResolvedPipelineTree, contract: AvContract): FieldStagePlan;
@@ -0,0 +1,27 @@
1
+ import { stagedFieldsOf } from "./field-stage.runtime.js";
2
+ const NONE = Object.freeze([]);
3
+ export function planFieldStages(tree, contract) {
4
+ const guarded = new Map();
5
+ const piped = new Map();
6
+ let nestedGuards = false;
7
+ for (const [resource, pipelines] of Object.entries(tree.resources)) {
8
+ const guards = stagedFieldsOf(contract, resource, pipelines.fields, "guards");
9
+ const pipes = stagedFieldsOf(contract, resource, pipelines.fields, "pipes");
10
+ if (guards.length > 0) {
11
+ guarded.set(resource, Object.freeze([...guards]));
12
+ }
13
+ if (pipes.length > 0) {
14
+ piped.set(resource, Object.freeze([...pipes]));
15
+ }
16
+ nestedGuards ||=
17
+ guards.length > 0 ||
18
+ pipelines.pipelines.some((level) => level.guards.length > 0) ||
19
+ Object.values(pipelines.families).some((family) => family.pipelines.some((level) => level.guards.length > 0));
20
+ }
21
+ return Object.freeze({
22
+ anyPipes: piped.size > 0,
23
+ nestedGuards,
24
+ guarded: (resource) => guarded.get(resource) ?? NONE,
25
+ piped: (resource) => piped.get(resource) ?? NONE,
26
+ });
27
+ }
@@ -0,0 +1,35 @@
1
+ import type { ResolvedFieldStages } from "../config/pipeline-tree.resolver.js";
2
+ import type { AvContract } from "../contracts/contract.js";
3
+ import type { AvOperationFamily } from "../operations/operation-identity.js";
4
+ import type { AvContext } from "./pipeline.js";
5
+ export type Path = readonly (string | number)[];
6
+ type UnknownRecord = Readonly<Record<string, unknown>>;
7
+ /** A plain object: what a write record, or a directive's operand, is. */
8
+ export declare function isPlainRecord(value: unknown): value is UnknownRecord;
9
+ /** The fields of `resource` with stages, in the Resource's field order. */
10
+ export declare function stagedFieldsOf(contract: AvContract, resource: string, stages: Readonly<Record<string, ResolvedFieldStages>>, kind: "pipes" | "guards"): readonly string[];
11
+ /** The value at `path` in `args`, or `undefined` when nothing is there. */
12
+ export declare function valueAt(args: unknown, path: Path): unknown;
13
+ /** `args` with the record at `path` replaced, every container on the way copied. */
14
+ export declare function replaceAt(args: unknown, path: Path, record: UnknownRecord): unknown;
15
+ export type PipedFields = {
16
+ readonly args: unknown;
17
+ /** Whether a field pipe returned a value: the arguments must be held to the server's rules again. */
18
+ readonly changed: boolean;
19
+ };
20
+ /** The paths of the top-level data records of a create, update or upsert. */
21
+ export declare function writeRecordPathsOf(family: AvOperationFamily, args: unknown): readonly Path[];
22
+ /**
23
+ * Runs the field pipes of every sent field `select` admits, in the records at
24
+ * `records`, field by field in the Resource's order, root's pipes then
25
+ * overlay's. A pipe returning `undefined` keeps the value. The arguments are
26
+ * copied where a value changes, never written.
27
+ */
28
+ export declare function pipeSentFields(stages: Readonly<Record<string, ResolvedFieldStages>>, fields: readonly string[], records: readonly Path[], args: unknown, select: (path: Path) => boolean): Promise<PipedFields>;
29
+ /**
30
+ * Runs the write guards of every sent field in the records at `records`, field
31
+ * by field in the Resource's order and, for several records, record by record;
32
+ * `false` as soon as one refuses.
33
+ */
34
+ export declare function guardSentFields(stages: Readonly<Record<string, ResolvedFieldStages>>, fields: readonly string[], records: readonly Path[], args: unknown, context: () => AvContext): Promise<boolean>;
35
+ export {};
@@ -0,0 +1,143 @@
1
+ export function isPlainRecord(value) {
2
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
3
+ return false;
4
+ }
5
+ const prototype = Object.getPrototypeOf(value);
6
+ return prototype === Object.prototype || prototype === null;
7
+ }
8
+ const WRITE_KEYS = {
9
+ create: ["data"],
10
+ update: ["data"],
11
+ upsert: ["create", "update"],
12
+ };
13
+ function writeRecordsOf(family, args) {
14
+ const keys = WRITE_KEYS[family];
15
+ if (keys === undefined || !isPlainRecord(args)) {
16
+ return [];
17
+ }
18
+ const records = [];
19
+ for (const key of keys) {
20
+ if (!Object.hasOwn(args, key)) {
21
+ continue;
22
+ }
23
+ const value = args[key];
24
+ if (Array.isArray(value)) {
25
+ for (const [index, entry] of value.entries()) {
26
+ if (isPlainRecord(entry)) {
27
+ records.push({ path: [key, index], record: entry });
28
+ }
29
+ }
30
+ }
31
+ else if (isPlainRecord(value)) {
32
+ records.push({ path: [key], record: value });
33
+ }
34
+ }
35
+ return records;
36
+ }
37
+ function sends(record, field) {
38
+ return Object.hasOwn(record, field) && record[field] !== undefined;
39
+ }
40
+ function isDirective(value) {
41
+ return (isPlainRecord(value) &&
42
+ Object.keys(value).some((key) => key.startsWith("$")));
43
+ }
44
+ export function stagedFieldsOf(contract, resource, stages, kind) {
45
+ const fields = Object.hasOwn(contract.resources, resource)
46
+ ? contract.resources[resource]?.fields
47
+ : undefined;
48
+ if (fields === undefined) {
49
+ return [];
50
+ }
51
+ return Object.keys(fields).filter((field) => Object.hasOwn(stages, field) && (stages[field]?.[kind].length ?? 0) > 0);
52
+ }
53
+ export function valueAt(args, path) {
54
+ let current = args;
55
+ for (const key of path) {
56
+ if (Array.isArray(current) && typeof key === "number") {
57
+ current = current[key];
58
+ }
59
+ else if (isPlainRecord(current) && Object.hasOwn(current, key)) {
60
+ current = current[key];
61
+ }
62
+ else {
63
+ return undefined;
64
+ }
65
+ }
66
+ return current;
67
+ }
68
+ export function replaceAt(args, path, record) {
69
+ const [head, ...rest] = path;
70
+ if (head === undefined) {
71
+ return record;
72
+ }
73
+ if (Array.isArray(args)) {
74
+ const copy = [...args];
75
+ copy[head] = replaceAt(copy[head], rest, record);
76
+ return copy;
77
+ }
78
+ const container = args;
79
+ return {
80
+ ...container,
81
+ [head]: replaceAt(container[head], rest, record),
82
+ };
83
+ }
84
+ export function writeRecordPathsOf(family, args) {
85
+ return writeRecordsOf(family, args).map((entry) => entry.path);
86
+ }
87
+ export async function pipeSentFields(stages, fields, records, args, select) {
88
+ if (fields.length === 0) {
89
+ return { args, changed: false };
90
+ }
91
+ let current = args;
92
+ let changed = false;
93
+ for (const path of records) {
94
+ const record = valueAt(current, path);
95
+ if (!isPlainRecord(record)) {
96
+ continue;
97
+ }
98
+ let piped;
99
+ for (const field of fields) {
100
+ if (!sends(record, field) ||
101
+ isDirective(record[field]) ||
102
+ !select([...path, field])) {
103
+ continue;
104
+ }
105
+ let value = record[field];
106
+ for (const pipe of stages[field]?.pipes ?? []) {
107
+ const next = await pipe(value);
108
+ if (next !== undefined) {
109
+ value = next;
110
+ changed = true;
111
+ }
112
+ }
113
+ if (value !== record[field]) {
114
+ piped ??= { ...record };
115
+ piped[field] = value;
116
+ }
117
+ }
118
+ if (piped !== undefined) {
119
+ current = replaceAt(current, path, piped);
120
+ }
121
+ }
122
+ return { args: current, changed };
123
+ }
124
+ export async function guardSentFields(stages, fields, records, args, context) {
125
+ if (fields.length === 0) {
126
+ return true;
127
+ }
128
+ const sent = records.map((path) => valueAt(args, path)).filter(isPlainRecord);
129
+ for (const field of fields) {
130
+ for (const record of sent) {
131
+ if (!sends(record, field)) {
132
+ continue;
133
+ }
134
+ for (const guard of stages[field]?.guards ?? []) {
135
+ const told = context();
136
+ if ((await guard(record[field], told)) === false) {
137
+ return false;
138
+ }
139
+ }
140
+ }
141
+ }
142
+ return true;
143
+ }
@@ -0,0 +1,11 @@
1
+ import type { AvContract } from "../contracts/contract.js";
2
+ import type { AvValidationIssue } from "../operations/validation-code.js";
3
+ import { type Path } from "./field-stage.runtime.js";
4
+ import type { OperationExecutionRequest } from "./operation-request.js";
5
+ import type { ReferentialActionTable } from "./referential-action.js";
6
+ /**
7
+ * Every way the sent fields `select` admits break their validators: the
8
+ * operation's own records, then each nested write target's. Paths are the
9
+ * request's: `argumentsPath` and then the value's place in the arguments.
10
+ */
11
+ export declare function fieldValidatorIssues(contract: AvContract, references: ReferentialActionTable, request: OperationExecutionRequest, args: unknown, argumentsPath: Path, select: (path: Path) => boolean): readonly AvValidationIssue[];
@@ -0,0 +1,87 @@
1
+ import { validatedFieldsOf, } from "../compiler/field-validator.compiler.js";
2
+ import { entryCheckIssues, fieldCheckIssues } from "./field-check.evaluator.js";
3
+ import { isPlainRecord, valueAt, writeRecordPathsOf, } from "./field-stage.runtime.js";
4
+ import { nestedWriteTargetsOf } from "./nested-write.walker.js";
5
+ import { jsonShapeIssues } from "./scalar-runtime.js";
6
+ function isDirective(value) {
7
+ return (isPlainRecord(value) &&
8
+ Object.keys(value).some((key) => key.startsWith("$")));
9
+ }
10
+ function rulesOf(contract, compiled, resource, name) {
11
+ const fields = Object.hasOwn(contract.resources, resource)
12
+ ? contract.resources[resource]?.fields
13
+ : undefined;
14
+ const field = fields !== undefined && Object.hasOwn(fields, name)
15
+ ? fields[name]
16
+ : undefined;
17
+ if (field?.kind === "scalar" && field.json !== undefined) {
18
+ return { kind: "shape", field };
19
+ }
20
+ const checks = Object.hasOwn(compiled, name) ? compiled[name] : undefined;
21
+ return checks === undefined
22
+ ? undefined
23
+ : { kind: "checks", compiled: checks };
24
+ }
25
+ function valueIssues(rules, value, at) {
26
+ if (rules.kind === "shape") {
27
+ return jsonShapeIssues(rules.field, value, at);
28
+ }
29
+ const { compiled } = rules;
30
+ return fieldCheckIssues(compiled.checks, value, compiled.subject ?? "string", at, compiled.list);
31
+ }
32
+ function entryIssues(rules, entry, at) {
33
+ if (rules.kind === "shape") {
34
+ return jsonShapeIssues(rules.field, entry, at, "entry");
35
+ }
36
+ const { compiled } = rules;
37
+ return entryCheckIssues(compiled.checks, entry, compiled.subject ?? "string", at);
38
+ }
39
+ function recordIssues(contract, resource, records, args, argumentsPath, select) {
40
+ const compiled = validatedFieldsOf(contract, resource);
41
+ const issues = [];
42
+ for (const path of records) {
43
+ const record = valueAt(args, path);
44
+ if (!isPlainRecord(record)) {
45
+ continue;
46
+ }
47
+ for (const name of Object.keys(record)) {
48
+ const rules = rulesOf(contract, compiled, resource, name);
49
+ if (rules === undefined) {
50
+ continue;
51
+ }
52
+ const fieldPath = [...path, name];
53
+ const value = record[name];
54
+ const at = [...argumentsPath, ...fieldPath];
55
+ if (!isDirective(value)) {
56
+ if (select(fieldPath)) {
57
+ issues.push(...valueIssues(rules, value, at));
58
+ }
59
+ continue;
60
+ }
61
+ const list = rules.kind === "shape" ? rules.field.list : rules.compiled.list;
62
+ if (!list || !Object.hasOwn(value, "$push")) {
63
+ continue;
64
+ }
65
+ const operand = value.$push;
66
+ const pushed = Array.isArray(operand)
67
+ ? operand.map((entry, index) => ({
68
+ entry,
69
+ path: [...fieldPath, "$push", index],
70
+ }))
71
+ : [{ entry: operand, path: [...fieldPath, "$push"] }];
72
+ for (const { entry, path: entryPath } of pushed) {
73
+ if (!isDirective(entry) && (select(fieldPath) || select(entryPath))) {
74
+ issues.push(...entryIssues(rules, entry, [...argumentsPath, ...entryPath]));
75
+ }
76
+ }
77
+ }
78
+ }
79
+ return issues;
80
+ }
81
+ export function fieldValidatorIssues(contract, references, request, args, argumentsPath, select) {
82
+ const issues = recordIssues(contract, request.resource, writeRecordPathsOf(request.family, args), args, argumentsPath, select);
83
+ for (const target of nestedWriteTargetsOf(contract, references, request, args)) {
84
+ issues.push(...recordIssues(contract, target.resource, target.records.map((site) => site.path), args, argumentsPath, select));
85
+ }
86
+ return issues;
87
+ }
@@ -0,0 +1,33 @@
1
+ import type { AvOperationErrorCode } from "./operation-response.js";
2
+ /** The `AvError` subclass every error code is thrown as, by class name. */
3
+ export declare const FRAMEWORK_ERROR_CLASS_OF: {
4
+ readonly A2000: "AvProtocolError";
5
+ readonly A2001: "AvProtocolError";
6
+ readonly A2002: "AvProtocolError";
7
+ readonly A2003: "AvNotFoundError";
8
+ readonly A2004: "AvValidationError";
9
+ readonly A2005: "AvContractMismatchError";
10
+ readonly A2006: "AvProtocolError";
11
+ readonly A2007: "AvValidationError";
12
+ readonly A2008: "AvConflictError";
13
+ readonly A2009: "AvValidationError";
14
+ readonly A2010: "AvProtocolError";
15
+ readonly A2011: "AvProtocolError";
16
+ readonly A2012: "AvProtocolError";
17
+ readonly A2013: "AvConflictError";
18
+ readonly A2014: "AvConflictError";
19
+ readonly A3000: "AvInternalError";
20
+ readonly A3001: "AvInternalError";
21
+ readonly A3002: "AvInternalError";
22
+ readonly A3003: "AvInternalError";
23
+ readonly A3004: "AvInternalError";
24
+ readonly A4000: "AvAuthError";
25
+ readonly A4001: "AvAuthError";
26
+ readonly A4002: "AvAuthError";
27
+ };
28
+ /** The name of one `AvError` subclass. */
29
+ export type AvErrorClassName = (typeof FRAMEWORK_ERROR_CLASS_OF)[AvOperationErrorCode];
30
+ /** The error codes `AvError` subclass `N` is thrown for. */
31
+ export type AvErrorCodeOf<N extends AvErrorClassName> = {
32
+ readonly [C in AvOperationErrorCode]: (typeof FRAMEWORK_ERROR_CLASS_OF)[C] extends N ? C : never;
33
+ }[AvOperationErrorCode];
@@ -0,0 +1,25 @@
1
+ export const FRAMEWORK_ERROR_CLASS_OF = {
2
+ A2000: "AvProtocolError",
3
+ A2001: "AvProtocolError",
4
+ A2002: "AvProtocolError",
5
+ A2003: "AvNotFoundError",
6
+ A2004: "AvValidationError",
7
+ A2005: "AvContractMismatchError",
8
+ A2006: "AvProtocolError",
9
+ A2007: "AvValidationError",
10
+ A2008: "AvConflictError",
11
+ A2009: "AvValidationError",
12
+ A2010: "AvProtocolError",
13
+ A2011: "AvProtocolError",
14
+ A2012: "AvProtocolError",
15
+ A2013: "AvConflictError",
16
+ A2014: "AvConflictError",
17
+ A3000: "AvInternalError",
18
+ A3001: "AvInternalError",
19
+ A3002: "AvInternalError",
20
+ A3003: "AvInternalError",
21
+ A3004: "AvInternalError",
22
+ A4000: "AvAuthError",
23
+ A4001: "AvAuthError",
24
+ A4002: "AvAuthError",
25
+ };
@@ -1,12 +1,19 @@
1
- import type { Cause, OperationErrorCode } from "./operation-response.js";
1
+ import type { AvErrorCodeOf } from "./framework-error-class.table.js";
2
+ import type { AvCause, AvOperationErrorCode } from "./operation-response.js";
2
3
  /**
3
- * Throw from a pipeline, behavior or Adapter to answer the request with one
4
- * framework error code and cause.
4
+ * Throw from a pipeline, compute stage or Adapter to answer the request with one
5
+ * framework error code and cause. Every in-process call that fails
6
+ * (`framework.application`, `framework.client`, `framework.transaction`, a ctx
7
+ * call) throws one too, as the subclass of its code's class:
8
+ * {@link AvValidationError}, {@link AvNotFoundError}, {@link AvConflictError},
9
+ * {@link AvAuthError}, {@link AvInternalError}, {@link AvProtocolError} or
10
+ * {@link AvContractMismatchError}. Branch on the class or on `code`, never on the
11
+ * message.
5
12
  *
6
13
  * For any code outside `A3xxx`, `cause.message` is sent to the caller exactly as
7
14
  * written: put nothing in it you would not show the caller (no database text,
8
15
  * SQL, stack, path or secret). So is each issue's `message`, when your own code
9
- * (a pipe, guard, hook, interceptor, filter or behavior) throws it and it is not
16
+ * (a pipe, guard, hook, interceptor, filter or compute stage) throws it and it is not
10
17
  * blank and at most 1,000 characters; otherwise, and for an issue an Adapter
11
18
  * throws, the caller receives the framework's own text for the issue's code. An
12
19
  * issue's `code` must be a validation code (`V1000` and up) and its `path` must
@@ -15,8 +22,61 @@ import type { Cause, OperationErrorCode } from "./operation-response.js";
15
22
  * message instead, and nothing of the cause; the error itself reaches the root
16
23
  * `diagnostics` sink once, marked `deliberate: true`.
17
24
  */
18
- export declare class FrameworkError extends Error {
19
- readonly code: OperationErrorCode;
20
- readonly cause: Cause;
21
- constructor(code: OperationErrorCode, cause: Cause | string);
25
+ export declare class AvError<C extends AvOperationErrorCode = AvOperationErrorCode> extends Error {
26
+ readonly name: string;
27
+ /** The outcome code. Branch on this, never on the message. */
28
+ readonly code: C;
29
+ /** The cause: its message, and its issues or failing step when there are any. */
30
+ readonly cause: AvCause;
31
+ constructor(code: C, cause: AvCause | string);
32
+ }
33
+ /**
34
+ * The request broke a Contract rule: a field, type, capability, projection,
35
+ * reference or limit. The cause's issues say which, by code and path. Thrown for
36
+ * A2004, A2007 and A2009.
37
+ */
38
+ export declare class AvValidationError extends AvError<AvErrorCodeOf<"AvValidationError">> {
39
+ readonly name: string;
40
+ }
41
+ /** A strict unique target does not exist. Thrown for A2003. */
42
+ export declare class AvNotFoundError extends AvError<AvErrorCodeOf<"AvNotFoundError">> {
43
+ readonly name: string;
44
+ }
45
+ /**
46
+ * The operation conflicted with stored state, and nothing was written. A2008
47
+ * fails the same way if sent again; A2013 and A2014 may succeed if sent again,
48
+ * and no retry is automatic. Thrown for A2008, A2013 and A2014.
49
+ */
50
+ export declare class AvConflictError extends AvError<AvErrorCodeOf<"AvConflictError">> {
51
+ readonly name: string;
52
+ }
53
+ /**
54
+ * The caller is not authenticated, or is not permitted to run the operation.
55
+ * Thrown for A4000, A4001 and A4002.
56
+ */
57
+ export declare class AvAuthError extends AvError<AvErrorCodeOf<"AvAuthError">> {
58
+ readonly name: string;
59
+ }
60
+ /**
61
+ * The server failed. Nothing in its cause is actionable; the diagnostics are in
62
+ * the `diagnostics` sink. Thrown for A3000 to A3004.
63
+ */
64
+ export declare class AvInternalError extends AvError<AvErrorCodeOf<"AvInternalError">> {
65
+ readonly name: string;
66
+ }
67
+ /**
68
+ * The request was not one the server could route or interpret: an unknown
69
+ * Resource or operation, a protocol version it does not speak, or a body, media
70
+ * type, size or method it refuses. Thrown for A2000, A2001, A2002, A2006, A2010,
71
+ * A2011 and A2012.
72
+ */
73
+ export declare class AvProtocolError extends AvError<AvErrorCodeOf<"AvProtocolError">> {
74
+ readonly name: string;
75
+ }
76
+ /**
77
+ * The caller's ClientContract hash is not the one the server serves (a stale
78
+ * generated client). Thrown for A2005.
79
+ */
80
+ export declare class AvContractMismatchError extends AvError<AvErrorCodeOf<"AvContractMismatchError">> {
81
+ readonly name: string;
22
82
  }
@@ -0,0 +1,4 @@
1
+ import { type AvError } from "./framework-error.js";
2
+ import type { AvOperationFailureResponse } from "./operation-response.js";
3
+ /** The `AvError` subclass a failure response throws in process, with its code and cause. */
4
+ export declare function thrownFailure(response: AvOperationFailureResponse): AvError;
@@ -0,0 +1,15 @@
1
+ import { AvAuthError, AvConflictError, AvContractMismatchError, AvInternalError, AvNotFoundError, AvProtocolError, AvValidationError, } from "./framework-error.js";
2
+ import { FRAMEWORK_ERROR_CLASS_OF, } from "./framework-error-class.table.js";
3
+ const ERROR_CLASSES = {
4
+ AvAuthError,
5
+ AvConflictError,
6
+ AvContractMismatchError,
7
+ AvInternalError,
8
+ AvNotFoundError,
9
+ AvProtocolError,
10
+ AvValidationError,
11
+ };
12
+ export function thrownFailure(response) {
13
+ const ErrorClass = ERROR_CLASSES[FRAMEWORK_ERROR_CLASS_OF[response.code]];
14
+ return new ErrorClass(response.code, response.cause);
15
+ }
@@ -1,11 +1,32 @@
1
- export class FrameworkError extends Error {
1
+ export class AvError extends Error {
2
+ name = "AvError";
2
3
  code;
3
4
  cause;
4
5
  constructor(code, cause) {
5
6
  const normalizedCause = typeof cause === "string" ? { message: cause } : cause;
6
7
  super(normalizedCause.message);
7
- this.name = "FrameworkError";
8
8
  this.code = code;
9
9
  this.cause = normalizedCause;
10
10
  }
11
11
  }
12
+ export class AvValidationError extends AvError {
13
+ name = "AvValidationError";
14
+ }
15
+ export class AvNotFoundError extends AvError {
16
+ name = "AvNotFoundError";
17
+ }
18
+ export class AvConflictError extends AvError {
19
+ name = "AvConflictError";
20
+ }
21
+ export class AvAuthError extends AvError {
22
+ name = "AvAuthError";
23
+ }
24
+ export class AvInternalError extends AvError {
25
+ name = "AvInternalError";
26
+ }
27
+ export class AvProtocolError extends AvError {
28
+ name = "AvProtocolError";
29
+ }
30
+ export class AvContractMismatchError extends AvError {
31
+ name = "AvContractMismatchError";
32
+ }
@@ -0,0 +1,5 @@
1
+ /** What the diagnostics channel receives when an operation interceptor calls `next()` twice. */
2
+ export declare class AvInterceptorNextError extends Error {
3
+ readonly name = "AvInterceptorNextError";
4
+ constructor();
5
+ }
@@ -0,0 +1,6 @@
1
+ export class AvInterceptorNextError extends Error {
2
+ name = "AvInterceptorNextError";
3
+ constructor() {
4
+ super("An interceptor called next() more than once; next() runs the operation once.");
5
+ }
6
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * A JSON value: the form of the `json` scalar, in arguments and results.
3
3
  *
4
- * A `Date`, a `bigint`, a `Uint8Array`, a {@link Decimal}, any other class
4
+ * A `Date`, a `bigint`, a `Uint8Array`, a {@link AvDecimal}, any other class
5
5
  * instance and an `undefined` property are refused (`V1001`), as are a
6
6
  * non-finite `number` (`V1004`) and a cycle (`V1001`).
7
7
  *
@@ -10,6 +10,6 @@
10
10
  * A `json` field's value is this without its top-level `null`: a field-level
11
11
  * `null` is the field's absence, which only `nullable: true` admits.
12
12
  */
13
- export type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
14
- readonly [key: string]: JsonValue;
13
+ export type AvJsonValue = string | number | boolean | null | readonly AvJsonValue[] | {
14
+ readonly [key: string]: AvJsonValue;
15
15
  };
@@ -0,0 +1,25 @@
1
+ /** One level's lifecycle registrations, as an operation or a transaction level holds them. */
2
+ export type LifecycleLevel<Context> = {
3
+ readonly hooks: {
4
+ readonly before: readonly ((context: Context) => unknown)[];
5
+ readonly after: readonly ((result: unknown, context: Context) => unknown)[];
6
+ };
7
+ readonly interceptors: readonly ((context: Context, next: () => Promise<unknown>) => Promise<unknown>)[];
8
+ };
9
+ /**
10
+ * The record of a `next()` called more than once: the first refusal, kept so a
11
+ * caller can refuse the whole call even when the interceptor swallowed it.
12
+ */
13
+ export type NextReuse<Refusal extends Error = Error> = {
14
+ refusal: Refusal | undefined;
15
+ };
16
+ /**
17
+ * Runs `levels`' lifecycle around `inner`: every interceptor, outer to inner,
18
+ * around every before hook, `inner`, and every after hook. `settle` turns what
19
+ * `inner` or an interceptor below resolved into what the stage above sees (the
20
+ * operation read-materializes it). An after hook is told a deep-frozen copy of
21
+ * the result; the result itself goes on to the interceptors, which may answer a
22
+ * different one. Every `next()` is single use: a second call rejects with
23
+ * `refuse()`'s error, recorded in `reuse`, and runs nothing.
24
+ */
25
+ export declare function runLevelLifecycle<Context, Refusal extends Error>(levels: readonly LifecycleLevel<Context>[], context: () => Context, inner: () => Promise<unknown>, settle: (result: unknown) => Promise<unknown>, reuse: NextReuse<Refusal>, refuse: () => Refusal): Promise<unknown>;