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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/CHANGELOG.md +1434 -0
  2. package/ERRORS.md +142 -0
  3. package/README.md +247 -57
  4. package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
  5. package/dist/adapters/adapter-discovery-diagnostic.guidance.d.ts +6 -0
  6. package/dist/adapters/adapter-discovery-diagnostic.guidance.js +7 -0
  7. package/dist/adapters/adapter-field.d.ts +25 -11
  8. package/dist/adapters/adapter-identifier.d.ts +6 -6
  9. package/dist/adapters/adapter-model.d.ts +18 -9
  10. package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
  11. package/dist/adapters/adapter-operation.d.ts +3 -3
  12. package/dist/adapters/adapter-resource.d.ts +18 -9
  13. package/dist/adapters/adapter-sql-statement.d.ts +19 -0
  14. package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
  15. package/dist/adapters/adapter.interface.d.ts +59 -29
  16. package/dist/avz/avz-check.factory.d.ts +83 -0
  17. package/dist/avz/avz-check.factory.js +116 -0
  18. package/dist/avz/avz-check.registry.d.ts +35 -0
  19. package/dist/avz/avz-check.registry.js +9 -0
  20. package/dist/avz/avz.d.ts +203 -0
  21. package/dist/{json → avz}/avz.js +150 -8
  22. package/dist/{json → avz}/index.d.ts +2 -1
  23. package/dist/{json → avz}/json-shape.registry.d.ts +7 -7
  24. package/dist/{json → avz}/zod-issue.translator.d.ts +8 -2
  25. package/dist/{json → avz}/zod-issue.translator.js +7 -0
  26. package/dist/compiler/contract-compiler-diagnostic.d.ts +31 -5
  27. package/dist/compiler/contract-compiler-diagnostic.guidance.d.ts +6 -0
  28. package/dist/compiler/contract-compiler-diagnostic.guidance.js +97 -0
  29. package/dist/compiler/contract.compiler.d.ts +18 -18
  30. package/dist/compiler/contract.compiler.js +637 -442
  31. package/dist/compiler/create-requirements.projector.d.ts +49 -0
  32. package/dist/compiler/create-requirements.projector.js +423 -0
  33. package/dist/compiler/field-validator.compiler.d.ts +47 -0
  34. package/dist/compiler/field-validator.compiler.js +238 -0
  35. package/dist/config/any-framework-config.d.ts +19 -12
  36. package/dist/config/computed-fields.d.ts +62 -0
  37. package/dist/config/computed-fields.js +80 -0
  38. package/dist/config/config-diagnostic.d.ts +11 -4
  39. package/dist/config/config-diagnostic.guidance.d.ts +6 -0
  40. package/dist/config/config-diagnostic.guidance.js +32 -0
  41. package/dist/config/config-layer.d.ts +15 -6
  42. package/dist/config/config.resolver.d.ts +74 -31
  43. package/dist/config/config.resolver.js +108 -44
  44. package/dist/config/config.validator.d.ts +7 -7
  45. package/dist/config/config.validator.js +581 -72
  46. package/dist/config/define-resource.d.ts +57 -0
  47. package/dist/config/define-resource.js +14 -0
  48. package/dist/config/effective-fields.d.ts +19 -23
  49. package/dist/config/entrypoint.normalizer.d.ts +1 -1
  50. package/dist/config/exact-framework-config.d.ts +308 -0
  51. package/dist/config/field-compute.resolver.d.ts +21 -0
  52. package/dist/config/field-compute.resolver.js +123 -0
  53. package/dist/config/field-pipeline-config.d.ts +47 -0
  54. package/dist/config/forced-operation.validator.d.ts +10 -0
  55. package/dist/config/forced-operation.validator.js +66 -0
  56. package/dist/config/framework-config.d.ts +111 -39
  57. package/dist/config/framework-exemptions-config.d.ts +91 -0
  58. package/dist/config/framework-exemptions-config.js +1 -0
  59. package/dist/config/framework-field-definition.d.ts +78 -0
  60. package/dist/config/framework-field-definition.js +28 -0
  61. package/dist/config/framework-resources-config.d.ts +122 -0
  62. package/dist/config/framework-resources-config.js +1 -0
  63. package/dist/config/pipeline-config.d.ts +56 -20
  64. package/dist/config/pipeline-tree.resolver.d.ts +79 -0
  65. package/dist/config/pipeline-tree.resolver.js +220 -0
  66. package/dist/config/stage-type.eraser.d.ts +65 -0
  67. package/dist/config/stage-type.eraser.js +1 -0
  68. package/dist/contracts/capability.d.ts +21 -12
  69. package/dist/contracts/capability.js +2 -0
  70. package/dist/contracts/client-contract-structure.validator.d.ts +388 -63
  71. package/dist/contracts/client-contract-structure.validator.js +156 -10
  72. package/dist/contracts/contract-canonicalizer.d.ts +26 -3
  73. package/dist/contracts/contract-canonicalizer.js +156 -29
  74. package/dist/contracts/contract-hash.d.ts +12 -7
  75. package/dist/contracts/contract-hash.js +3 -2
  76. package/dist/contracts/contract-limits.d.ts +4 -1
  77. package/dist/contracts/contract-limits.js +1 -0
  78. package/dist/contracts/contract-lookup.d.ts +5 -5
  79. package/dist/contracts/contract-protocol.d.ts +4 -4
  80. package/dist/contracts/contract.d.ts +24 -16
  81. package/dist/contracts/create-requirements.d.ts +35 -0
  82. package/dist/contracts/create-requirements.js +1 -0
  83. package/dist/contracts/enum-definition.d.ts +1 -1
  84. package/dist/contracts/field-check.d.ts +101 -0
  85. package/dist/contracts/field-check.js +1 -0
  86. package/dist/contracts/field.d.ts +81 -42
  87. package/dist/contracts/identifier.d.ts +4 -4
  88. package/dist/contracts/json-shape.d.ts +12 -1
  89. package/dist/contracts/lifecycle-trait.d.ts +2 -2
  90. package/dist/contracts/resource-operations.d.ts +28 -28
  91. package/dist/contracts/resource.d.ts +15 -9
  92. package/dist/contracts/scalar-value-type.d.ts +15 -15
  93. package/dist/contracts/scalar.d.ts +3 -3
  94. package/dist/contracts/transaction-capability.d.ts +1 -1
  95. package/dist/contracts/wire-scalar-format.d.ts +4 -4
  96. package/dist/framework/contract-view.d.ts +213 -147
  97. package/dist/framework/framework-construction.error.d.ts +19 -10
  98. package/dist/framework/framework-construction.error.js +7 -3
  99. package/dist/framework/framework.d.ts +47 -23
  100. package/dist/framework/framework.factory.d.ts +8 -6
  101. package/dist/framework/framework.factory.js +49 -22
  102. package/dist/framework/operation-facade.d.ts +123 -52
  103. package/dist/framework/operation-facade.js +9 -1
  104. package/dist/index.d.ts +57 -50
  105. package/dist/index.js +7 -6
  106. package/dist/internal.d.ts +17 -10
  107. package/dist/internal.js +4 -1
  108. package/dist/operations/database-refusal.d.ts +51 -0
  109. package/dist/operations/database-refusal.js +45 -0
  110. package/dist/operations/operation-arguments.d.ts +343 -207
  111. package/dist/operations/operation-call.d.ts +27 -28
  112. package/dist/operations/operation-identity.d.ts +10 -10
  113. package/dist/operations/operation-result.d.ts +21 -21
  114. package/dist/operations/operation-validator-core.d.ts +6 -6
  115. package/dist/operations/operation-validator-core.js +99 -6
  116. package/dist/operations/operation-validator.d.ts +9 -9
  117. package/dist/operations/projection-scope.resolver.d.ts +6 -6
  118. package/dist/operations/structural-limits.walker.d.ts +6 -6
  119. package/dist/operations/structural-limits.walker.js +145 -0
  120. package/dist/operations/validation-code.d.ts +11 -6
  121. package/dist/operations/validation-code.guidance.d.ts +6 -0
  122. package/dist/operations/validation-code.guidance.js +90 -0
  123. package/dist/operations/validation-code.js +4 -0
  124. package/dist/operations/write-scope.resolver.d.ts +10 -6
  125. package/dist/operations/write-scope.resolver.js +2 -0
  126. package/dist/protocol/absent-route.responder.d.ts +13 -12
  127. package/dist/protocol/absent-route.responder.js +19 -19
  128. package/dist/protocol/contract-document.responder.d.ts +3 -3
  129. package/dist/protocol/contract-document.responder.js +2 -2
  130. package/dist/protocol/http-request.decoder.d.ts +17 -13
  131. package/dist/protocol/http-request.decoder.js +10 -10
  132. package/dist/protocol/http-request.reader.d.ts +3 -3
  133. package/dist/protocol/http-response.encoder.d.ts +4 -4
  134. package/dist/protocol/http-response.encoder.js +94 -55
  135. package/dist/protocol/http-status.table.d.ts +2 -2
  136. package/dist/protocol/operation-response.guard.d.ts +2 -2
  137. package/dist/protocol/protocol-surface.d.ts +4 -4
  138. package/dist/protocol/protocol.binder.d.ts +50 -27
  139. package/dist/protocol/protocol.binder.js +12 -9
  140. package/dist/protocol/protocol.handler.d.ts +22 -13
  141. package/dist/protocol/protocol.handler.js +91 -17
  142. package/dist/protocol/unmounted-route.error.d.ts +4 -4
  143. package/dist/protocol/unmounted-route.error.js +2 -2
  144. package/dist/runtime/caller-data.copier.d.ts +22 -5
  145. package/dist/runtime/caller-data.copier.js +31 -14
  146. package/dist/runtime/caller-data.door.d.ts +17 -11
  147. package/dist/runtime/caller-data.door.js +6 -4
  148. package/dist/runtime/computed-read-runtime.d.ts +7 -6
  149. package/dist/runtime/computed-read-runtime.js +76 -50
  150. package/dist/runtime/computed-value.error.d.ts +7 -6
  151. package/dist/runtime/computed-value.error.js +5 -4
  152. package/dist/runtime/context-executor.runtime.d.ts +59 -0
  153. package/dist/runtime/context-executor.runtime.js +275 -0
  154. package/dist/runtime/create-requirement.d.ts +44 -79
  155. package/dist/runtime/create-requirement.js +213 -73
  156. package/dist/runtime/decimal.d.ts +2 -2
  157. package/dist/runtime/decimal.js +1 -1
  158. package/dist/runtime/diagnostics-reporter.d.ts +54 -27
  159. package/dist/runtime/diagnostics-reporter.js +24 -0
  160. package/dist/runtime/error-boundary.d.ts +65 -8
  161. package/dist/runtime/error-boundary.js +49 -19
  162. package/dist/runtime/error-guidance.d.ts +12 -0
  163. package/dist/runtime/error-guidance.js +1 -0
  164. package/dist/runtime/error.catalog.d.ts +49 -0
  165. package/dist/runtime/error.catalog.js +49 -0
  166. package/dist/runtime/execution-options.d.ts +10 -2
  167. package/dist/runtime/field-check.evaluator.d.ts +29 -0
  168. package/dist/runtime/field-check.evaluator.js +521 -0
  169. package/dist/runtime/field-compute.d.ts +64 -0
  170. package/dist/runtime/field-compute.js +1 -0
  171. package/dist/runtime/field-stage.plan.d.ts +15 -0
  172. package/dist/runtime/field-stage.plan.js +27 -0
  173. package/dist/runtime/field-stage.runtime.d.ts +37 -0
  174. package/dist/runtime/field-stage.runtime.js +147 -0
  175. package/dist/runtime/field-validator.runtime.d.ts +19 -0
  176. package/dist/runtime/field-validator.runtime.js +92 -0
  177. package/dist/runtime/framework-error-class.table.d.ts +33 -0
  178. package/dist/runtime/framework-error-class.table.js +25 -0
  179. package/dist/runtime/framework-error.d.ts +68 -8
  180. package/dist/runtime/framework-error.factory.d.ts +4 -0
  181. package/dist/runtime/framework-error.factory.js +15 -0
  182. package/dist/runtime/framework-error.js +23 -2
  183. package/dist/runtime/interceptor-next.error.d.ts +5 -0
  184. package/dist/runtime/interceptor-next.error.js +6 -0
  185. package/dist/runtime/json-value.d.ts +11 -6
  186. package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
  187. package/dist/runtime/level-lifecycle.runtime.js +39 -0
  188. package/dist/runtime/misuse-reporter.d.ts +12 -12
  189. package/dist/runtime/mutation-planner.d.ts +19 -10
  190. package/dist/runtime/mutation-planner.js +48 -35
  191. package/dist/runtime/nested-write.walker.d.ts +32 -0
  192. package/dist/runtime/nested-write.walker.js +73 -0
  193. package/dist/runtime/operation-code.guidance.d.ts +6 -0
  194. package/dist/runtime/operation-code.guidance.js +167 -0
  195. package/dist/runtime/operation-request.d.ts +33 -6
  196. package/dist/runtime/operation-response.d.ts +17 -17
  197. package/dist/runtime/operation-runtime.d.ts +127 -25
  198. package/dist/runtime/operation-runtime.js +441 -78
  199. package/dist/runtime/outcome-mapper.d.ts +16 -5
  200. package/dist/runtime/outcome-mapper.js +23 -6
  201. package/dist/runtime/output-validator.d.ts +10 -10
  202. package/dist/runtime/output-validator.js +3 -3
  203. package/dist/runtime/owned-execution.registry.d.ts +16 -6
  204. package/dist/runtime/pipe-output.validator.d.ts +43 -18
  205. package/dist/runtime/pipe-output.validator.js +15 -13
  206. package/dist/runtime/pipeline.d.ts +223 -34
  207. package/dist/runtime/readonly-value.d.ts +43 -0
  208. package/dist/runtime/readonly-value.js +150 -0
  209. package/dist/runtime/request-envelope.validator.d.ts +3 -3
  210. package/dist/runtime/request-envelope.validator.js +12 -7
  211. package/dist/runtime/request-id.d.ts +1 -1
  212. package/dist/runtime/request-state.d.ts +14 -0
  213. package/dist/runtime/request-state.js +1 -0
  214. package/dist/runtime/request-state.resolver.d.ts +12 -0
  215. package/dist/runtime/request-state.resolver.js +20 -0
  216. package/dist/runtime/scalar-runtime.d.ts +35 -16
  217. package/dist/runtime/scalar-runtime.js +102 -34
  218. package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
  219. package/dist/runtime/unsent-value.normalizer.js +128 -0
  220. package/dist/transactions/deferred-operation.d.ts +35 -20
  221. package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
  222. package/dist/transactions/operation-arguments-encoder.js +2 -2
  223. package/dist/transactions/operation-fingerprint.js +2 -2
  224. package/dist/transactions/sql-statement.d.ts +85 -0
  225. package/dist/transactions/sql-statement.js +47 -0
  226. package/dist/transactions/sql-statement.validator.d.ts +44 -0
  227. package/dist/transactions/sql-statement.validator.js +167 -0
  228. package/dist/transactions/transaction-envelope.d.ts +4 -4
  229. package/dist/transactions/transaction-integrity-error.d.ts +6 -6
  230. package/dist/transactions/transaction-integrity-error.js +5 -2
  231. package/dist/transactions/transaction-plan-assembler.d.ts +21 -11
  232. package/dist/transactions/transaction-plan-assembler.js +169 -32
  233. package/dist/transactions/transaction-plan-validator.d.ts +45 -20
  234. package/dist/transactions/transaction-plan-validator.js +96 -38
  235. package/dist/transactions/transaction-reference-collector.d.ts +5 -5
  236. package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
  237. package/dist/transactions/transaction-reference-resolver.js +26 -14
  238. package/dist/transactions/transaction-reference-validator.d.ts +76 -4
  239. package/dist/transactions/transaction-reference-validator.js +129 -71
  240. package/dist/transactions/transaction-reference.d.ts +13 -13
  241. package/dist/transactions/transaction-reference.js +5 -0
  242. package/dist/transactions/transaction-request.d.ts +18 -5
  243. package/dist/transactions/transaction-response.d.ts +6 -6
  244. package/dist/transactions/transaction-runtime.d.ts +8 -7
  245. package/dist/transactions/transaction-runtime.js +191 -50
  246. package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
  247. package/dist/transactions/transaction-settlement.runtime.js +120 -0
  248. package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
  249. package/dist/transactions/transaction-slot-path-error.js +2 -2
  250. package/dist/transactions/transaction-slot-writer.js +2 -2
  251. package/dist/transactions/transaction-stage.runtime.d.ts +82 -0
  252. package/dist/transactions/transaction-stage.runtime.js +126 -0
  253. package/package.json +9 -7
  254. package/src/runtime/field-check.evaluator.ts +720 -0
  255. package/dist/config/define-fields.d.ts +0 -42
  256. package/dist/config/define-fields.js +0 -3
  257. package/dist/config/framework-behaviors-config.d.ts +0 -30
  258. package/dist/config/framework-fields-config.d.ts +0 -42
  259. package/dist/config/framework-restrictions-config.d.ts +0 -142
  260. package/dist/json/avz.d.ts +0 -96
  261. package/dist/runtime/field-behavior.d.ts +0 -48
  262. /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
  263. /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
  264. /package/dist/{json → avz}/index.js +0 -0
  265. /package/dist/{json → avz}/json-shape.registry.js +0 -0
  266. /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
  267. /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
@@ -0,0 +1,147 @@
1
+ import { frozenCopy } from "./readonly-value.js";
2
+ export function isPlainRecord(value) {
3
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
4
+ return false;
5
+ }
6
+ const prototype = Object.getPrototypeOf(value);
7
+ return prototype === Object.prototype || prototype === null;
8
+ }
9
+ export const WRITE_KEYS = {
10
+ create: ["data"],
11
+ update: ["data"],
12
+ upsert: ["create", "update"],
13
+ };
14
+ function writeRecordsOf(family, args) {
15
+ const keys = WRITE_KEYS[family];
16
+ if (keys === undefined || !isPlainRecord(args)) {
17
+ return [];
18
+ }
19
+ const records = [];
20
+ for (const key of keys) {
21
+ if (!Object.hasOwn(args, key)) {
22
+ continue;
23
+ }
24
+ const value = args[key];
25
+ if (Array.isArray(value)) {
26
+ for (const [index, entry] of value.entries()) {
27
+ if (isPlainRecord(entry)) {
28
+ records.push({ path: [key, index], record: entry });
29
+ }
30
+ }
31
+ }
32
+ else if (isPlainRecord(value)) {
33
+ records.push({ path: [key], record: value });
34
+ }
35
+ }
36
+ return records;
37
+ }
38
+ function sends(record, field) {
39
+ return Object.hasOwn(record, field) && record[field] !== undefined;
40
+ }
41
+ function isDirective(value) {
42
+ return (isPlainRecord(value) &&
43
+ Object.keys(value).some((key) => key.startsWith("$")));
44
+ }
45
+ export function stagedFieldsOf(contract, resource, stages, kind) {
46
+ const fields = Object.hasOwn(contract.resources, resource)
47
+ ? contract.resources[resource]?.fields
48
+ : undefined;
49
+ if (fields === undefined) {
50
+ return [];
51
+ }
52
+ return Object.keys(fields).filter((field) => Object.hasOwn(stages, field) && (stages[field]?.[kind].length ?? 0) > 0);
53
+ }
54
+ export function valueAt(args, path) {
55
+ let current = args;
56
+ for (const key of path) {
57
+ if (Array.isArray(current) && typeof key === "number") {
58
+ current = current[key];
59
+ }
60
+ else if (isPlainRecord(current) && Object.hasOwn(current, key)) {
61
+ current = current[key];
62
+ }
63
+ else {
64
+ return undefined;
65
+ }
66
+ }
67
+ return current;
68
+ }
69
+ export function replaceAt(args, path, record) {
70
+ const [head, ...rest] = path;
71
+ if (head === undefined) {
72
+ return record;
73
+ }
74
+ if (Array.isArray(args)) {
75
+ const copy = [...args];
76
+ copy[head] = replaceAt(copy[head], rest, record);
77
+ return copy;
78
+ }
79
+ const container = args;
80
+ return {
81
+ ...container,
82
+ [head]: replaceAt(container[head], rest, record),
83
+ };
84
+ }
85
+ export function writeRecordPathsOf(family, args) {
86
+ return writeRecordsOf(family, args).map((entry) => entry.path);
87
+ }
88
+ export async function pipeSentFields(stages, fields, records, args, select) {
89
+ if (fields.length === 0) {
90
+ return { args, changed: false };
91
+ }
92
+ let current = args;
93
+ let changed = false;
94
+ for (const path of records) {
95
+ const record = valueAt(current, path);
96
+ if (!isPlainRecord(record)) {
97
+ continue;
98
+ }
99
+ let piped;
100
+ for (const field of fields) {
101
+ if (!sends(record, field) ||
102
+ isDirective(record[field]) ||
103
+ !select([...path, field])) {
104
+ continue;
105
+ }
106
+ let value = record[field];
107
+ for (const pipe of stages[field]?.pipes ?? []) {
108
+ const told = frozenCopy(value);
109
+ const next = await pipe(told);
110
+ if (next !== undefined) {
111
+ if (next !== told) {
112
+ value = next;
113
+ }
114
+ changed = true;
115
+ }
116
+ }
117
+ if (value !== record[field]) {
118
+ piped ??= { ...record };
119
+ piped[field] = value;
120
+ }
121
+ }
122
+ if (piped !== undefined) {
123
+ current = replaceAt(current, path, piped);
124
+ }
125
+ }
126
+ return { args: current, changed };
127
+ }
128
+ export async function guardSentFields(stages, fields, records, args, context) {
129
+ if (fields.length === 0) {
130
+ return true;
131
+ }
132
+ const sent = records.map((path) => valueAt(args, path)).filter(isPlainRecord);
133
+ for (const field of fields) {
134
+ for (const record of sent) {
135
+ if (!sends(record, field)) {
136
+ continue;
137
+ }
138
+ for (const guard of stages[field]?.guards ?? []) {
139
+ const told = context();
140
+ if ((await guard(frozenCopy(record[field]), told)) === false) {
141
+ return false;
142
+ }
143
+ }
144
+ }
145
+ }
146
+ return true;
147
+ }
@@ -0,0 +1,19 @@
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
+ /** The write phase of a record: a created row (an upsert's `create` branch too) or an updated one. */
7
+ export type WriteStage = "create" | "update";
8
+ /**
9
+ * The fields a judgment leaves for later: a plan step's (or a forced call's)
10
+ * literal values are judged before BEGIN, except a field a write compute stage
11
+ * of that phase may still change, which is judged after write compute.
12
+ */
13
+ export type DeferredField = (resource: string, stage: WriteStage, field: string) => boolean;
14
+ /**
15
+ * Every way the sent fields `select` admits break their validators: the
16
+ * operation's own records, then each nested write target's. Paths are the
17
+ * request's: `argumentsPath` and then the value's place in the arguments.
18
+ */
19
+ export declare function fieldValidatorIssues(contract: AvContract, references: ReferentialActionTable, request: OperationExecutionRequest, args: unknown, argumentsPath: Path, select: (path: Path) => boolean, deferred?: DeferredField): readonly AvValidationIssue[];
@@ -0,0 +1,92 @@
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, deferred) {
40
+ const compiled = validatedFieldsOf(contract, resource);
41
+ const issues = [];
42
+ for (const { path, stage } 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 || deferred?.(resource, stage, name) === true) {
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, deferred) {
82
+ const issues = recordIssues(contract, request.resource, writeRecordPathsOf(request.family, args).map((path) => ({
83
+ path,
84
+ stage: request.family === "create" || path[0] === "create"
85
+ ? "create"
86
+ : "update",
87
+ })), args, argumentsPath, select, deferred);
88
+ for (const target of nestedWriteTargetsOf(contract, references, request, args)) {
89
+ issues.push(...recordIssues(contract, target.resource, target.records, args, argumentsPath, select, deferred));
90
+ }
91
+ return issues;
92
+ }
@@ -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,15 +1,20 @@
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
5
- * instance and an `undefined` property are refused (`V1001`), as are a
6
- * non-finite `number` (`V1004`) and a cycle (`V1001`).
4
+ * A `Date`, a `bigint`, a `Uint8Array`, a {@link AvDecimal} and any other class
5
+ * instance are refused (`V1001`), as are a non-finite `number` (`V1004`) and a
6
+ * cycle (`V1001`). An `undefined` inside the value is what JSON makes of it: a key
7
+ * holding it is left out, an array element holding it (or a hole) is `null`. The
8
+ * type admits a key holding `undefined`, so an object with an optional property is
9
+ * a JSON value; an array element stays typed without it, as JSON has no such
10
+ * value.
7
11
  *
8
- * Results are fresh plain copies, so mutating one is safe.
12
+ * Results are fresh plain copies, so mutating one is safe; a result never holds a
13
+ * key whose value is `undefined`.
9
14
  *
10
15
  * A `json` field's value is this without its top-level `null`: a field-level
11
16
  * `null` is the field's absence, which only `nullable: true` admits.
12
17
  */
13
- export type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
14
- readonly [key: string]: JsonValue;
18
+ export type AvJsonValue = string | number | boolean | null | readonly AvJsonValue[] | {
19
+ readonly [key: string]: AvJsonValue | undefined;
15
20
  };
@@ -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). Each after hook is told its own deep-frozen
21
+ * copy of 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>;
@@ -0,0 +1,39 @@
1
+ import { frozenCopy } from "./readonly-value.js";
2
+ export async function runLevelLifecycle(levels, context, inner, settle, reuse, refuse) {
3
+ const once = (run) => {
4
+ let called = false;
5
+ return () => {
6
+ if (called) {
7
+ const refusal = refuse();
8
+ reuse.refusal ??= refusal;
9
+ return Promise.reject(refusal);
10
+ }
11
+ called = true;
12
+ return run();
13
+ };
14
+ };
15
+ const hooked = async () => {
16
+ for (const level of levels) {
17
+ for (const hook of level.hooks.before) {
18
+ await hook(context());
19
+ }
20
+ }
21
+ const result = await settle(await inner());
22
+ const after = [...levels].reverse().flatMap((level) => level.hooks.after);
23
+ for (const hook of after) {
24
+ await hook(frozenCopy(result), context());
25
+ }
26
+ return result;
27
+ };
28
+ const interceptors = levels.flatMap((level) => level.interceptors);
29
+ let next = hooked;
30
+ for (let index = interceptors.length - 1; index >= 0; index -= 1) {
31
+ const interceptor = interceptors[index];
32
+ if (interceptor === undefined) {
33
+ continue;
34
+ }
35
+ const continuation = once(next);
36
+ next = () => interceptor(context(), async () => settle(await continuation()));
37
+ }
38
+ return next();
39
+ }
@@ -1,32 +1,32 @@
1
- import type { RequestOrigin } from "../config/config-layer.js";
2
- import type { OperationFamily } from "../operations/operation-identity.js";
1
+ import type { AvRequestOrigin } from "../config/config-layer.js";
2
+ import type { AvOperationFamily } from "../operations/operation-identity.js";
3
3
  /**
4
4
  * One caller value the framework refused because it could not be read
5
5
  * (`"unreadable-value"`: a revoked Proxy, a throwing getter or trap) or is not plain
6
- * data (`"non-plain-value"`: a class instance other than `Date`, `Decimal` or
6
+ * data (`"non-plain-value"`: a class instance other than `Date`, `AvDecimal` or
7
7
  * `Uint8Array`). The caller received `A2004` with one `V1001` at `path`.
8
8
  *
9
- * The operation's identity is present when the framework could read it — absent for
10
- * a request whose own envelope could not be read — and `operation` is the step's
9
+ * The operation's identity is present when the framework could read it (absent for
10
+ * a request whose own envelope could not be read), and `operation` is the step's
11
11
  * index in a transaction plan.
12
12
  */
13
- export type FrameworkMisuseEvent = {
13
+ export type AvMisuseEvent = {
14
14
  readonly kind: "unreadable-value" | "non-plain-value";
15
15
  /** The issue path the caller received. */
16
16
  readonly path: readonly (string | number)[];
17
- readonly origin?: RequestOrigin;
17
+ readonly origin?: AvRequestOrigin;
18
18
  readonly resource?: string;
19
- readonly family?: OperationFamily;
19
+ readonly family?: AvOperationFamily;
20
20
  readonly variant?: string;
21
21
  readonly operation?: number;
22
22
  };
23
23
  /**
24
- * Receives a {@link FrameworkMisuseEvent} for every caller value the framework
24
+ * Receives a {@link AvMisuseEvent} for every caller value the framework
25
25
  * refused as unreadable or not plain data. Set once, as the root configuration's
26
26
  * `misuse`. What it returns, throws or rejects with never reaches the response.
27
27
  */
28
- export type FrameworkMisuseSink = (event: FrameworkMisuseEvent) => void | Promise<void>;
28
+ export type AvMisuseSink = (event: AvMisuseEvent) => void | Promise<void>;
29
29
  /** The channel as the runtime holds it: total, never awaited. */
30
- export type MisuseReporter = (event: FrameworkMisuseEvent) => void;
30
+ export type MisuseReporter = (event: AvMisuseEvent) => void;
31
31
  /** Binds the configured sink; without one, nothing is observed. */
32
- export declare function createMisuseReporter(sink: FrameworkMisuseSink | undefined): MisuseReporter;
32
+ export declare function createMisuseReporter(sink: AvMisuseSink | undefined): MisuseReporter;