@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
@@ -0,0 +1,344 @@
1
+ import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
2
+ import { advertisedDirectives, holdsAt, } from "../contracts/field.js";
3
+ import { NESTED_PROJECTION_SCOPE, resolveProjectionScope, } from "./projection-scope.resolver.js";
4
+ const UNTRACKED = [];
5
+ const RELATION_FILTER_OPERATORS = ["some", "every", "none"];
6
+ const DIRECTIVE_PAYLOADS = new Map([
7
+ ["$connectOrCreate", [["create", "create"]]],
8
+ ["$update", [["data", "update"]]],
9
+ [
10
+ "$upsert",
11
+ [
12
+ ["create", "create"],
13
+ ["update", "update"],
14
+ ],
15
+ ],
16
+ ]);
17
+ const NO_PAYLOADS = [];
18
+ function isRecord(value) {
19
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20
+ }
21
+ function extend(state, path, segment) {
22
+ return state.tracking ? [...path, segment] : UNTRACKED;
23
+ }
24
+ function nestingExceeded(state) {
25
+ state.nesting = {
26
+ path: ["arguments", state.rootKey],
27
+ code: "V1013",
28
+ message: `Request nesting exceeds maxNestingDepth (${state.nestingLimit}).`,
29
+ };
30
+ }
31
+ function countBooleanNode(state, path, key) {
32
+ if (state.booleanExceeded) {
33
+ return;
34
+ }
35
+ state.booleanCount += 1;
36
+ if (state.booleanCount > state.booleanLimit) {
37
+ state.booleanExceeded = true;
38
+ if (state.tracking) {
39
+ state.booleanNodes = {
40
+ path: [...path, key],
41
+ code: "V1015",
42
+ message: `Boolean/filter node count exceeds maxBooleanNodes (${state.booleanLimit}).`,
43
+ };
44
+ }
45
+ }
46
+ }
47
+ function walkWhere(state, resourceName, value, depth, path, counting) {
48
+ if (depth > state.nestingLimit) {
49
+ nestingExceeded(state);
50
+ return;
51
+ }
52
+ const resource = lookupResource(state.contract, resourceName);
53
+ if (!isRecord(value) || resource === undefined) {
54
+ return;
55
+ }
56
+ for (const key of Object.keys(value)) {
57
+ if (counting) {
58
+ countBooleanNode(state, path, key);
59
+ }
60
+ const child = value[key];
61
+ if (key === "AND" || key === "OR") {
62
+ if (Array.isArray(child)) {
63
+ for (let index = 0; index < child.length; index += 1) {
64
+ walkWhere(state, resourceName, child[index], depth + 1, state.tracking ? [...path, key, index] : UNTRACKED, counting);
65
+ if (state.nesting !== undefined) {
66
+ return;
67
+ }
68
+ }
69
+ }
70
+ continue;
71
+ }
72
+ if (key === "NOT") {
73
+ walkWhere(state, resourceName, child, depth + 1, extend(state, path, key), counting);
74
+ }
75
+ else {
76
+ const field = lookupField(resource, key);
77
+ if (field?.kind !== "relation") {
78
+ continue;
79
+ }
80
+ if (field.cardinality === "one") {
81
+ walkWhere(state, field.target, child, depth + 1, extend(state, path, key), counting);
82
+ }
83
+ else if (isRecord(child)) {
84
+ for (const operator of RELATION_FILTER_OPERATORS) {
85
+ if (!(operator in child)) {
86
+ continue;
87
+ }
88
+ walkWhere(state, field.target, child[operator], depth + 1, state.tracking ? [...path, key, operator] : UNTRACKED, counting);
89
+ if (state.nesting !== undefined) {
90
+ return;
91
+ }
92
+ }
93
+ }
94
+ }
95
+ if (state.nesting !== undefined) {
96
+ return;
97
+ }
98
+ }
99
+ }
100
+ function walkOrderBy(state, resourceName, value, depth) {
101
+ const resource = lookupResource(state.contract, resourceName);
102
+ if (resource === undefined) {
103
+ return;
104
+ }
105
+ const entries = Array.isArray(value) ? value : [value];
106
+ for (let index = 0; index < entries.length; index += 1) {
107
+ const entry = entries[index];
108
+ if (!isRecord(entry)) {
109
+ continue;
110
+ }
111
+ for (const fieldName of Object.keys(entry)) {
112
+ const field = lookupField(resource, fieldName);
113
+ if (field?.kind !== "relation" || field.cardinality === "many") {
114
+ continue;
115
+ }
116
+ const relationDepth = depth + 1;
117
+ if (relationDepth > state.nestingLimit) {
118
+ nestingExceeded(state);
119
+ return;
120
+ }
121
+ walkOrderBy(state, field.target, entry[fieldName], relationDepth);
122
+ if (state.nesting !== undefined) {
123
+ return;
124
+ }
125
+ }
126
+ }
127
+ }
128
+ function soleKey(record) {
129
+ const keys = Object.keys(record);
130
+ return keys.length === 1 ? keys[0] : undefined;
131
+ }
132
+ function walkProjection(state, resourceName, value, depth, path, mode, scope) {
133
+ const resource = lookupResource(state.contract, resourceName);
134
+ if (!Array.isArray(value) || resource === undefined) {
135
+ return;
136
+ }
137
+ for (let index = 0; index < value.length; index += 1) {
138
+ const entry = value[index];
139
+ const relationName = isRecord(entry) ? soleKey(entry) : undefined;
140
+ const edge = relationName === undefined
141
+ ? undefined
142
+ : lookupField(resource, relationName);
143
+ if (relationName === undefined || edge?.kind !== "relation") {
144
+ continue;
145
+ }
146
+ const relationDepth = depth + 1;
147
+ if (relationDepth > state.nestingLimit) {
148
+ nestingExceeded(state);
149
+ return;
150
+ }
151
+ const projection = entry[relationName];
152
+ if (!isRecord(projection)) {
153
+ continue;
154
+ }
155
+ const relationPath = state.tracking
156
+ ? [...path, index, relationName]
157
+ : UNTRACKED;
158
+ const counted = scope !== undefined &&
159
+ holdsAt(mode === "select"
160
+ ? edge.capabilities.select
161
+ : edge.capabilities.include, scope);
162
+ if ("where" in projection) {
163
+ walkWhere(state, edge.target, projection.where, relationDepth, extend(state, relationPath, "where"), counted && edge.capabilities.filter?.includes("nested") === true);
164
+ if (state.nesting !== undefined) {
165
+ return;
166
+ }
167
+ }
168
+ if ("orderBy" in projection) {
169
+ walkOrderBy(state, edge.target, projection.orderBy, relationDepth);
170
+ if (state.nesting !== undefined) {
171
+ return;
172
+ }
173
+ }
174
+ for (const nestedMode of ["select", "include"]) {
175
+ if (!(nestedMode in projection)) {
176
+ continue;
177
+ }
178
+ walkProjection(state, edge.target, projection[nestedMode], relationDepth, extend(state, relationPath, nestedMode), nestedMode, counted ? NESTED_PROJECTION_SCOPE : undefined);
179
+ if (state.nesting !== undefined) {
180
+ return;
181
+ }
182
+ }
183
+ }
184
+ }
185
+ function walkMutationData(state, resourceName, value, depth, path, stage, counting) {
186
+ if (depth > state.nestingLimit) {
187
+ nestingExceeded(state);
188
+ return;
189
+ }
190
+ const resource = lookupResource(state.contract, resourceName);
191
+ if (!isRecord(value) || resource === undefined) {
192
+ return;
193
+ }
194
+ for (const fieldName of Object.keys(value)) {
195
+ const edge = lookupField(resource, fieldName);
196
+ const directives = value[fieldName];
197
+ if (edge?.kind !== "relation" || !isRecord(directives)) {
198
+ continue;
199
+ }
200
+ walkRelationDirectives(state, edge, directives, depth + 1, extend(state, path, fieldName), stage, counting);
201
+ if (state.nesting !== undefined) {
202
+ return;
203
+ }
204
+ }
205
+ }
206
+ function walkRelationDirectives(state, edge, directives, relationDepth, path, stage, counting) {
207
+ const supported = advertisedDirectives(edge.capabilities[stage]);
208
+ for (const directive of Object.keys(directives)) {
209
+ if (relationDepth > state.nestingLimit) {
210
+ nestingExceeded(state);
211
+ return;
212
+ }
213
+ const directivePath = extend(state, path, directive);
214
+ const directiveCounting = counting && supported?.includes(directive) === true;
215
+ const directiveValue = directives[directive];
216
+ if (Array.isArray(directiveValue)) {
217
+ for (let index = 0; index < directiveValue.length; index += 1) {
218
+ walkDirectiveEntry(state, edge.target, directive, directiveValue[index], relationDepth, extend(state, directivePath, index), directiveCounting);
219
+ if (state.nesting !== undefined) {
220
+ return;
221
+ }
222
+ }
223
+ }
224
+ else {
225
+ walkDirectiveEntry(state, edge.target, directive, directiveValue, relationDepth, directivePath, directiveCounting);
226
+ if (state.nesting !== undefined) {
227
+ return;
228
+ }
229
+ }
230
+ }
231
+ }
232
+ function walkDirectiveEntry(state, target, directive, entry, relationDepth, path, counting) {
233
+ if (directive === "$create") {
234
+ walkMutationData(state, target, entry, relationDepth, path, "create", counting);
235
+ return;
236
+ }
237
+ if (!isRecord(entry)) {
238
+ return;
239
+ }
240
+ if ((directive === "$update" || directive === "$delete") &&
241
+ "where" in entry) {
242
+ walkWhere(state, target, entry.where, relationDepth, extend(state, path, "where"), counting);
243
+ if (state.nesting !== undefined) {
244
+ return;
245
+ }
246
+ }
247
+ for (const [key, stage] of DIRECTIVE_PAYLOADS.get(directive) ?? NO_PAYLOADS) {
248
+ if (!(key in entry)) {
249
+ continue;
250
+ }
251
+ walkMutationData(state, target, entry[key], relationDepth, extend(state, path, key), stage, counting);
252
+ if (state.nesting !== undefined) {
253
+ return;
254
+ }
255
+ }
256
+ }
257
+ function isUniqueOperation(request) {
258
+ return (request.variant === "unique" &&
259
+ (request.family === "find" ||
260
+ request.family === "update" ||
261
+ request.family === "delete" ||
262
+ request.family === "upsert"));
263
+ }
264
+ function walkArguments(state, request, args) {
265
+ const resource = request.resource;
266
+ const rootPath = (key) => {
267
+ state.rootKey = key;
268
+ return state.tracking ? ["arguments", key] : UNTRACKED;
269
+ };
270
+ if ("where" in args) {
271
+ walkWhere(state, resource, args.where, 1, rootPath("where"), !isUniqueOperation(request));
272
+ if (state.nesting !== undefined) {
273
+ return;
274
+ }
275
+ }
276
+ if ("orderBy" in args) {
277
+ rootPath("orderBy");
278
+ walkOrderBy(state, resource, args.orderBy, 0);
279
+ if (state.nesting !== undefined) {
280
+ return;
281
+ }
282
+ }
283
+ for (const mode of ["select", "include"]) {
284
+ if (!(mode in args)) {
285
+ continue;
286
+ }
287
+ walkProjection(state, resource, args[mode], 0, rootPath(mode), mode, resolveProjectionScope(lookupResource(state.contract, resource), request));
288
+ if (state.nesting !== undefined) {
289
+ return;
290
+ }
291
+ }
292
+ if ("data" in args) {
293
+ const path = rootPath("data");
294
+ const data = args.data;
295
+ if (Array.isArray(data)) {
296
+ for (let index = 0; index < data.length; index += 1) {
297
+ walkMutationData(state, resource, data[index], 0, extend(state, path, index), "create", request.family === "create");
298
+ if (state.nesting !== undefined) {
299
+ return;
300
+ }
301
+ }
302
+ }
303
+ else {
304
+ walkMutationData(state, resource, data, 0, path, request.family === "update" ? "update" : "create", request.family === "create" || request.family === "update");
305
+ if (state.nesting !== undefined) {
306
+ return;
307
+ }
308
+ }
309
+ }
310
+ for (const branch of ["create", "update"]) {
311
+ if (!(branch in args)) {
312
+ continue;
313
+ }
314
+ walkMutationData(state, resource, args[branch], 0, rootPath(branch), branch, request.family === "upsert");
315
+ if (state.nesting !== undefined) {
316
+ return;
317
+ }
318
+ }
319
+ }
320
+ function walk(contract, request, args, tracking) {
321
+ const state = {
322
+ contract,
323
+ nestingLimit: contract.limits.maxNestingDepth,
324
+ booleanLimit: contract.limits.maxBooleanNodes,
325
+ tracking,
326
+ rootKey: "",
327
+ booleanCount: 0,
328
+ nesting: undefined,
329
+ booleanNodes: undefined,
330
+ booleanExceeded: false,
331
+ };
332
+ walkArguments(state, request, args);
333
+ return state;
334
+ }
335
+ export function findStructuralLimitIssues(contract, request, args) {
336
+ const untracked = walk(contract, request, args, false);
337
+ if (untracked.nesting !== undefined || !untracked.booleanExceeded) {
338
+ return { nesting: untracked.nesting, booleanNodes: undefined };
339
+ }
340
+ return {
341
+ nesting: undefined,
342
+ booleanNodes: walk(contract, request, args, true).booleanNodes,
343
+ };
344
+ }
@@ -1,26 +1,14 @@
1
- /**
2
- * It is a runtime array for the same reason `OPERATION_CODES` is one: the union
3
- * below is the compiler's view of the set, and the response boundary needs the set
4
- * itself to test a thrower-supplied code by identity before emitting it.
5
- */
1
+ /** The set of allocated Validation codes, as a runtime array. */
6
2
  export declare const VALIDATION_CODES: readonly ["V1000", "V1001", "V1002", "V1003", "V1004", "V1005", "V1006", "V1007", "V1008", "V1009", "V1010", "V1011", "V1012", "V1013", "V1014", "V1015", "V1016", "V1017", "V1018", "V1019"];
3
+ /** A validation code (`V1000` and up), one per kind of argument problem. */
7
4
  export type ValidationCode = (typeof VALIDATION_CODES)[number];
8
- /**
9
- * This is the text the response boundary emits in place of a thrower-supplied
10
- * issue message, which is untrusted and may carry provider text that specification
11
- * 13.5 forbids in `cause`.
12
- *
13
- * It is deliberately not the only message a `V` code may ever carry. A framework
14
- * producer knows the offending field, limit or reference and says so; those issues
15
- * are built here and mapped straight to their envelope by `mapValidationOutcome`,
16
- * never through the sanitization boundary. This table is the floor for an issue
17
- * the framework did not author, not a ceiling on the ones it did.
18
- */
5
+ /** The framework's message for each validation code. */
19
6
  export declare const VALIDATION_CODE_MESSAGES: Readonly<Record<ValidationCode, string>>;
7
+ /** One validation problem: its code, message and the argument path it is at. */
20
8
  export type ValidationIssue = {
21
9
  readonly path?: readonly (string | number)[];
22
10
  readonly code: ValidationCode;
23
11
  readonly message: string;
24
12
  };
25
- /** Builds one Framework Validation Layer issue at its path. */
13
+ /** Builds one validation issue at its path. */
26
14
  export declare function validationIssue(code: ValidationCode, path: readonly (string | number)[], message: string): ValidationIssue;
@@ -3,38 +3,13 @@ import type { Contract } from "../contracts/contract.js";
3
3
  import type { Resource } from "../contracts/resource.js";
4
4
  import type { OperationIdentity } from "./operation-identity.js";
5
5
  /**
6
- * Which write position one request's **top-level** `data` (or `create`/`update`)
7
- * argument sits in, read off the variant the Resource advertises.
8
- *
9
- * The write mirror of `resolveProjectionScope`, and a separate function for the
10
- * reason `resource-operations.ts` gives for `projectionScopeOf`/`writeScopeOf`
11
- * being two: the axes are measured and consulted independently — the flat-bulk
12
- * write scope covers four variants where the bulk-returning projection scope
13
- * covers two — and a caller that needs one position should not be handed the other
14
- * to discard.
15
- *
16
- * `"nested"` is the answer wherever nothing narrower is stated — an absent
17
- * Resource, an unadvertised variant, `true`, or a descriptor that qualifies only
18
- * its projection argument. Availability is **not** decided here: an unadvertised
19
- * operation is refused by `operation-validator.ts` before the mutation walk runs,
20
- * and answering `"nested"` for one keeps this function a write question rather than
21
- * a second, weaker copy of the availability gate.
6
+ * Which write position one request's top-level `data` (or `create`/`update`)
7
+ * argument sits in, read off the variant the Resource advertises. `"nested"`
8
+ * wherever nothing narrower is stated.
22
9
  */
23
10
  export declare function resolveWriteScope(resource: Resource<Contract> | undefined, request: OperationIdentity): WriteScope;
24
11
  /**
25
12
  * The position a write NESTED beneath an admitted relation directive sits in: the
26
13
  * unrestricted one, at every depth, under every top-level scope.
27
- *
28
- * It is a constant and not a parameter the recursion threads, because there is no
29
- * depth at which the answer differs. A relation write reached through `$create`,
30
- * `$connectOrCreate.create`, `$update.data` or `$upsert`'s two branches lands in
31
- * the unrestricted write input on both pilot engines, whatever the top-level
32
- * argument was. The measurement behind that is the flat bulk write inputs carrying
33
- * **no relation member at all**, so a nested write is only ever reachable
34
- * *through* a relation the top-level scope already admitted — and past that point
35
- * the full write surface returns.
36
- *
37
- * Naming it is the point, exactly as `NESTED_PROJECTION_SCOPE` names the same
38
- * decision on the read axis.
39
14
  */
40
15
  export declare const NESTED_WRITE_SCOPE: WriteScope;
@@ -1,16 +1,18 @@
1
1
  import type { FrameworkContracts } from "../compiler/contract.compiler.js";
2
2
  import { type OperationIdentity } from "../operations/operation-identity.js";
3
+ import { type FrameworkDiagnosticsSink } from "../runtime/diagnostics-reporter.js";
3
4
  import type { ClientProtocolIdentity } from "../runtime/operation-request.js";
4
5
  import { type OperationFailureResponse } from "../runtime/operation-response.js";
5
6
  import type { AvProtocolRequest } from "./http-request.decoder.js";
6
7
  import { type ReadRequest } from "./http-request.reader.js";
7
8
  import { type AvProtocolResponse } from "./http-response.encoder.js";
8
9
  /**
9
- * "Removed" and "never existed" are not distinguished; a stale client hears
10
- * `A2005` first.
11
- *
12
- * It reads the compiled Contracts only — passive data — so nothing it depends on
13
- * can execute an operation.
10
+ * The absence answers: the checks a framework route makes before it reads a body, runs
11
+ * a pipeline or reaches an Adapter — the protocol version (`A2006`), the
12
+ * ClientContract hash (`A2005`), then whether the ClientContract advertises the route
13
+ * at all (`A2001`, `A2002`). Every envelope is the one the Framework Validation Layer
14
+ * answers for the same request, so a caller cannot tell which layer answered. Reads
15
+ * the compiled Contracts only.
14
16
  */
15
17
  /** A route's three decoded segments, still untrusted strings. */
16
18
  export type OperationRouteSegments = {
@@ -28,18 +30,18 @@ export type OperationRouteResolution = {
28
30
  };
29
31
  /**
30
32
  * An operation route `/_resources/<resource>/<family>/<variant>` under one client
31
- * protocol identity: version → hash → Resource → operation. A Resource key is
32
- * matched exactly and only as the ClientContract's own key; a family/variant pair
33
- * only when it is a standard operation the Resource advertises, read through
33
+ * protocol identity: version → hash → Resource → operation. A Resource key is matched
34
+ * exactly and only as the ClientContract's own key; a family/variant pair only when it
35
+ * is a standard operation the Resource advertises, read through
34
36
  * `isOperationVariantAvailable` alone.
35
37
  */
36
38
  export declare function resolveOperationRoute(contracts: FrameworkContracts, protocol: ClientProtocolIdentity, route: OperationRouteSegments): OperationRouteResolution;
37
39
  /**
38
- * The `/_transactions` route under one client protocol identity: version → hash →
39
- * the ClientContract's transaction capability. The route exists only when that
40
- * capability is `"interactive"` — tested against the value itself, as
41
- * `protocol.surface()` tests it — and is otherwise absent with core's own refusal
42
- * of a plan. `undefined` when the route is there.
40
+ * The `/_transactions` route under one client protocol identity: version → hash → the
41
+ * ClientContract's transaction capability. The route exists only when that capability
42
+ * is `"interactive"` — tested against the value itself, as `protocol.surface()` tests
43
+ * it — and is otherwise absent with core's own refusal of a plan. `undefined` when the
44
+ * route is there.
43
45
  */
44
46
  export declare function transactionsRouteFailure(contracts: FrameworkContracts, protocol: ClientProtocolIdentity): OperationFailureResponse | undefined;
45
47
  /**
@@ -54,12 +56,12 @@ type RouteCheck<Route> = ({
54
56
  readonly response: AvProtocolResponse;
55
57
  };
56
58
  /**
57
- * An operation route's checks before its body (decoder steps 2–4): the path
58
- * (`A2000` unless `/_resources/<r>/<f>/<v>`, each segment decodable and none
59
- * empty), the identity headers' presence (`A2000`), then {@link
60
- * resolveOperationRoute} — version, hash, Resource, operation — and the method
61
- * (`A2012`, `Allow: POST`). One sequence for both route styles: `decodeOperation`
62
- * reads the body only after it passes, `answerAbsent` never.
59
+ * An operation route's checks before its body (decoder steps 2–4): the path (`A2000`
60
+ * unless `/_resources/<r>/<f>/<v>`, each segment decodable and none empty), the
61
+ * identity headers' presence (`A2000`), then {@link resolveOperationRoute} — version,
62
+ * hash, Resource, operation — and the method (`A2012`, `Allow: POST`). One sequence
63
+ * for both route styles: `decodeOperation` reads the body only after it passes,
64
+ * `answerAbsent` never.
63
65
  */
64
66
  export declare function checkOperationRoute(contracts: FrameworkContracts, read: ReadRequest): RouteCheck<{
65
67
  readonly resource: string;
@@ -74,25 +76,9 @@ export declare function checkOperationRoute(contracts: FrameworkContracts, read:
74
76
  export declare function checkTransactionsRoute(contracts: FrameworkContracts, read: ReadRequest): RouteCheck<object>;
75
77
  /**
76
78
  * `protocol.answerAbsent(request)` — the fallback a host that mounts one route per
77
- * `protocol.surface()` entry mounts for every path it did not. Never a catch-all
78
- * into framework handling: it reads no body, runs no pipeline, calls no Adapter,
79
- * and takes only the compiled Contracts. In order:
80
- *
81
- * 1. a value that is not an `AvProtocolRequest` → `400 A2000`, minted id;
82
- * 2. a path outside the protocol's underscore namespace → `undefined`: the host
83
- * answers it itself — `/favicon.ico` is not the framework's;
84
- * 3. `/_contract`: any method but `GET` → `405 A2012` with `Allow: GET`; no
85
- * identity header is read;
86
- * 4. `/_transactions`, or else an operation route: exactly what the catch-all
87
- * host's decoder answers before its body — path syntax and identity presence
88
- * (`A2000`), version (`A2006`), hash (`A2005`), Resource (`A2001`), operation
89
- * or the `"none"` capability (`A2002`), method (`A2012`, `Allow: POST`) — each
90
- * core's own envelope;
91
- * 5. a route that EXISTS, reached with its own method — the host mounted only part
92
- * of the surface — → `500 A3000`: a host wiring bug made visible, never a
93
- * silent 404.
94
- *
95
- * Every framework answer echoes `Aventara-Request-Id`.
79
+ * `protocol.surface()` entry mounts for every path it did not. Never a catch-all into
80
+ * framework handling: it reads no body, runs no pipeline, calls no Adapter, and takes
81
+ * only the compiled Contracts. In order:
96
82
  */
97
- export declare function answerAbsentRequest(contracts: FrameworkContracts, received: AvProtocolRequest): AvProtocolResponse | undefined;
83
+ export declare function answerAbsentRequest(contracts: FrameworkContracts, received: AvProtocolRequest, sink?: FrameworkDiagnosticsSink): AvProtocolResponse | undefined;
98
84
  export {};
@@ -3,6 +3,7 @@ import { isOperationVariantAvailable, } from "../contracts/resource-operations.j
3
3
  import { parseOperationIdentity, } from "../operations/operation-identity.js";
4
4
  import { operationUnavailableIssue } from "../operations/operation-validator.js";
5
5
  import { resourceUnavailableIssue } from "../operations/operation-validator-core.js";
6
+ import { createReporter, } from "../runtime/diagnostics-reporter.js";
6
7
  import { INTERNAL_FAILURE_MESSAGE, operationFailure, } from "../runtime/operation-response.js";
7
8
  import { protocolIdentityFailure } from "../runtime/operation-runtime.js";
8
9
  import { mapValidationOutcome } from "../runtime/outcome-mapper.js";
@@ -11,6 +12,7 @@ import { CONTRACT_ROUTE, GET_ONLY, isProtocolPath, operationRouteSegments, POST_
11
12
  import { encodeOperationResponse, } from "./http-response.encoder.js";
12
13
  import { identityHeadersMissing, transportFailure, } from "./protocol-failure.js";
13
14
  import { missingIdentityHeaders } from "./protocol-headers.js";
15
+ import { UnmountedRouteError } from "./unmounted-route.error.js";
14
16
  export function resolveOperationRoute(contracts, protocol, route) {
15
17
  const identityFailure = protocolIdentityFailure(contracts, protocol);
16
18
  if (identityFailure !== undefined) {
@@ -29,7 +31,9 @@ export function resolveOperationRoute(contracts, protocol, route) {
29
31
  !isOperationVariantAvailable(lookupOwn(lookupOwn(advertised, route.family), route.variant))) {
30
32
  return {
31
33
  ok: false,
32
- failure: mapValidationOutcome([operationUnavailableIssue(route)]),
34
+ failure: mapValidationOutcome([
35
+ operationUnavailableIssue(contracts.client, route),
36
+ ]),
33
37
  };
34
38
  }
35
39
  return { ok: true, operation };
@@ -82,7 +86,7 @@ function identityFailureOf(read) {
82
86
  function refusedWith(response) {
83
87
  return { ok: false, response };
84
88
  }
85
- export function answerAbsentRequest(contracts, received) {
89
+ export function answerAbsentRequest(contracts, received, sink) {
86
90
  const read = readRequest(received);
87
91
  if (!read.ok) {
88
92
  return read.response;
@@ -96,10 +100,32 @@ export function answerAbsentRequest(contracts, received) {
96
100
  ? unmounted(read.requestId)
97
101
  : transportFailure("A2012", read.requestId, GET_ONLY);
98
102
  }
99
- const route = path === TRANSACTIONS_ROUTE
100
- ? checkTransactionsRoute(contracts, read)
101
- : checkOperationRoute(contracts, read);
102
- return route.ok ? unmounted(read.requestId) : route.response;
103
+ if (path === TRANSACTIONS_ROUTE) {
104
+ const route = checkTransactionsRoute(contracts, read);
105
+ if (!route.ok) {
106
+ return route.response;
107
+ }
108
+ createReporter(sink, {
109
+ kind: "transaction",
110
+ origin: "client",
111
+ requestId: read.requestId,
112
+ })(new UnmountedRouteError("transactions", `POST ${TRANSACTIONS_ROUTE}`), "A3000");
113
+ return unmounted(read.requestId);
114
+ }
115
+ const route = checkOperationRoute(contracts, read);
116
+ if (!route.ok) {
117
+ return route.response;
118
+ }
119
+ const { family, variant } = route.operation;
120
+ createReporter(sink, {
121
+ kind: "operation",
122
+ origin: "client",
123
+ resource: route.resource,
124
+ family,
125
+ variant,
126
+ requestId: read.requestId,
127
+ })(new UnmountedRouteError(`${route.resource}.${family}.${variant}`, `POST ${path}`), "A3000");
128
+ return unmounted(read.requestId);
103
129
  }
104
130
  function unmounted(requestId) {
105
131
  return encodeOperationResponse(operationFailure("A3000", { message: INTERNAL_FAILURE_MESSAGE }), requestId);
@@ -1,12 +1,6 @@
1
1
  /**
2
- * The members of `AvProtocol`, as this module's own named exports. The entry
3
- * (`./index.ts`) publishes this module whole as the namespace `AvProtocol`: the
4
- * language freezes a module namespace, and a bundler drops the members a consumer
5
- * does not read.
6
- *
7
- * Re-exports only — no logic lives here. Every member is pinned, by name and by
8
- * type, in `./index.test-d.ts`, whose exact `keyof` set fails when a member is
9
- * added or removed without a row.
2
+ * The HTTP protocol: pure functions and data a host uses to serve a Framework.
3
+ * Start with `AvProtocol.bind(framework)`.
10
4
  */
11
5
  export { normalizeEntrypoint } from "../config/entrypoint.normalizer.js";
12
6
  export { scalarFormats } from "../contracts/wire-scalar-format.js";
@@ -2,9 +2,9 @@ import type { ClientContract } from "../contracts/contract.js";
2
2
  import type { AvProtocolRequest } from "./http-request.decoder.js";
3
3
  import type { AvProtocolResponse } from "./http-response.encoder.js";
4
4
  /**
5
- * The served ClientContract, written once per binding: its JCS-canonical bytes and
6
- * its entity tag, the quoted contract hash. The bytes include `protocol.hash`,
7
- * although the hash's own input excludes it.
5
+ * The served ClientContract, written once per binding: its JCS-canonical bytes and its
6
+ * entity tag, the quoted contract hash. The bytes include `protocol.hash`, although
7
+ * the hash's own input excludes it.
8
8
  */
9
9
  export type ContractDocument = {
10
10
  readonly body: string;
@@ -13,20 +13,7 @@ export type ContractDocument = {
13
13
  /** The bound ClientContract as the document `_contract` serves. */
14
14
  export declare function contractDocumentOf(client: ClientContract): ContractDocument;
15
15
  /**
16
- * `protocol.encodeContract(request)`: the `GET <entrypoint>/_contract` responder.
17
- * In order:
18
- *
19
- * 1. a value that is not an `AvProtocolRequest` → `400 A2000` under a minted id
20
- * (as the decoders answer it);
21
- * 3. a path other than `/_contract` → `400 A2000` (path syntax this route cannot
22
- * read, as a decoder answers one);
23
- * 4. a method other than `GET` → `405 A2012` with `Allow: GET`;
24
- * 5. `If-None-Match` matching the document's entity tag — RFC 9110 §13.1.2's weak
25
- * comparison, over a comma-separated list, or `*` — → `304 Not Modified`, no
26
- * body, still carrying `ETag` and `Cache-Control`; otherwise `200` with the
27
- * canonical bytes, `ETag`, `Cache-Control: no-cache` and `Content-Type:
28
- * application/json`.
29
- *
30
- * No identity header is required or read, and the body is never read.
16
+ * `protocol.encodeContract(request)`: the `GET <entrypoint>/_contract` responder. In
17
+ * order:
31
18
  */
32
19
  export declare function encodeContractRequest(document: ContractDocument, received: AvProtocolRequest): AvProtocolResponse;