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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/CHANGELOG.md +943 -16
  2. package/ERRORS.md +142 -0
  3. package/README.md +274 -16
  4. package/dist/adapters/adapter-discovery-diagnostic.guidance.d.ts +6 -0
  5. package/dist/adapters/adapter-discovery-diagnostic.guidance.js +7 -0
  6. package/dist/adapters/adapter-field.d.ts +22 -0
  7. package/dist/adapters/adapter-identifier.d.ts +9 -0
  8. package/dist/adapters/adapter-identifier.js +6 -1
  9. package/dist/adapters/adapter-model.d.ts +9 -0
  10. package/dist/adapters/adapter-operation.d.ts +18 -3
  11. package/dist/adapters/adapter-resource.d.ts +5 -0
  12. package/dist/adapters/adapter.interface.d.ts +10 -0
  13. package/dist/avz/avz.d.ts +1 -1
  14. package/dist/avz/json-shape.registry.d.ts +2 -1
  15. package/dist/compiler/contract-compiler-diagnostic.guidance.d.ts +6 -0
  16. package/dist/compiler/contract-compiler-diagnostic.guidance.js +97 -0
  17. package/dist/compiler/contract.compiler.d.ts +6 -0
  18. package/dist/compiler/contract.compiler.js +227 -24
  19. package/dist/compiler/managed-elsewhere.projector.d.ts +52 -0
  20. package/dist/compiler/managed-elsewhere.projector.js +179 -0
  21. package/dist/compiler/managed-elsewhere.validator.d.ts +9 -0
  22. package/dist/compiler/managed-elsewhere.validator.js +74 -0
  23. package/dist/config/computed-fields.d.ts +62 -0
  24. package/dist/config/computed-fields.js +73 -0
  25. package/dist/config/config-diagnostic.guidance.d.ts +6 -0
  26. package/dist/config/config-diagnostic.guidance.js +32 -0
  27. package/dist/config/config.resolver.d.ts +24 -0
  28. package/dist/config/config.resolver.js +65 -0
  29. package/dist/config/config.validator.js +222 -8
  30. package/dist/config/define-resource.d.ts +1 -5
  31. package/dist/config/entrypoint.normalizer.d.ts +3 -2
  32. package/dist/config/exact-framework-config.d.ts +67 -22
  33. package/dist/config/field-compute.resolver.d.ts +2 -0
  34. package/dist/config/field-compute.resolver.js +8 -0
  35. package/dist/config/framework-config.d.ts +53 -15
  36. package/dist/config/framework-resources-config.d.ts +25 -14
  37. package/dist/config/pipeline-config.d.ts +4 -4
  38. package/dist/config/pipeline-tree.resolver.d.ts +2 -2
  39. package/dist/contracts/access-route.rules.d.ts +10 -0
  40. package/dist/contracts/access-route.rules.js +45 -0
  41. package/dist/contracts/capability.d.ts +26 -1
  42. package/dist/contracts/capability.js +20 -0
  43. package/dist/contracts/client-contract-structure.validator.d.ts +301 -89
  44. package/dist/contracts/client-contract-structure.validator.js +37 -2
  45. package/dist/contracts/contract-access.d.ts +29 -0
  46. package/dist/contracts/contract-access.js +1 -0
  47. package/dist/contracts/contract-canonicalizer.d.ts +9 -1
  48. package/dist/contracts/contract-canonicalizer.js +30 -4
  49. package/dist/contracts/contract-hash.d.ts +0 -1
  50. package/dist/contracts/contract-hash.js +1 -1
  51. package/dist/contracts/contract.d.ts +27 -1
  52. package/dist/contracts/field.d.ts +58 -1
  53. package/dist/contracts/json-schema.projector.d.ts +11 -0
  54. package/dist/contracts/json-schema.projector.js +78 -0
  55. package/dist/contracts/scalar-value-type.d.ts +3 -1
  56. package/dist/framework/access-facade.d.ts +116 -0
  57. package/dist/framework/access-facade.js +120 -0
  58. package/dist/framework/contract-view.d.ts +120 -16
  59. package/dist/framework/framework-construction.error.d.ts +14 -4
  60. package/dist/framework/framework-construction.error.js +18 -2
  61. package/dist/framework/framework.d.ts +26 -5
  62. package/dist/framework/framework.factory.d.ts +1 -1
  63. package/dist/framework/framework.factory.js +115 -23
  64. package/dist/framework/operation-facade.d.ts +80 -13
  65. package/dist/framework/operation-facade.js +2 -1
  66. package/dist/index.d.ts +17 -5
  67. package/dist/index.js +1 -0
  68. package/dist/internal.d.ts +7 -2
  69. package/dist/internal.js +6 -0
  70. package/dist/operations/database-refusal.d.ts +51 -0
  71. package/dist/operations/database-refusal.js +45 -0
  72. package/dist/operations/operation-arguments.d.ts +148 -60
  73. package/dist/operations/operation-call.d.ts +9 -5
  74. package/dist/operations/operation-result.d.ts +6 -4
  75. package/dist/operations/operation-validator-core.js +159 -36
  76. package/dist/operations/operation-validator.d.ts +1 -1
  77. package/dist/operations/structural-limits.walker.d.ts +1 -1
  78. package/dist/operations/validation-code.guidance.d.ts +6 -0
  79. package/dist/operations/validation-code.guidance.js +90 -0
  80. package/dist/operations/write-scope.resolver.d.ts +4 -0
  81. package/dist/operations/write-scope.resolver.js +2 -0
  82. package/dist/policy/access-provider.interface.d.ts +91 -0
  83. package/dist/policy/access-provider.interface.js +1 -0
  84. package/dist/policy/access-provider.registry.d.ts +17 -0
  85. package/dist/policy/access-provider.registry.js +15 -0
  86. package/dist/policy/access-question.d.ts +20 -0
  87. package/dist/policy/access-question.js +1 -0
  88. package/dist/policy/access-question.registry.d.ts +5 -0
  89. package/dist/policy/access-question.registry.js +19 -0
  90. package/dist/policy/access-question.runtime.d.ts +36 -0
  91. package/dist/policy/access-question.runtime.js +297 -0
  92. package/dist/policy/access-verb.table.d.ts +56 -0
  93. package/dist/policy/access-verb.table.js +21 -0
  94. package/dist/policy/access-verbs.d.ts +46 -0
  95. package/dist/policy/access-verbs.js +1 -0
  96. package/dist/policy/policy-admission.d.ts +20 -0
  97. package/dist/policy/policy-admission.js +409 -0
  98. package/dist/policy/policy-after-update.runtime.d.ts +79 -0
  99. package/dist/policy/policy-after-update.runtime.js +208 -0
  100. package/dist/policy/policy-condition.d.ts +15 -0
  101. package/dist/policy/policy-condition.js +36 -0
  102. package/dist/policy/policy-engine.d.ts +205 -0
  103. package/dist/policy/policy-engine.js +1 -0
  104. package/dist/policy/policy-named-rows.d.ts +18 -0
  105. package/dist/policy/policy-named-rows.js +51 -0
  106. package/dist/policy/policy-nested-write.runtime.d.ts +44 -0
  107. package/dist/policy/policy-nested-write.runtime.js +545 -0
  108. package/dist/policy/policy-read.runtime.d.ts +81 -0
  109. package/dist/policy/policy-read.runtime.js +503 -0
  110. package/dist/policy/policy-reference-write.runtime.d.ts +51 -0
  111. package/dist/policy/policy-reference-write.runtime.js +345 -0
  112. package/dist/policy/policy-request.d.ts +41 -0
  113. package/dist/policy/policy-request.js +63 -0
  114. package/dist/policy/policy-session.d.ts +106 -0
  115. package/dist/policy/policy-session.js +295 -0
  116. package/dist/policy/policy-startup.validator.d.ts +10 -0
  117. package/dist/policy/policy-startup.validator.js +112 -0
  118. package/dist/policy/policy-write.runtime.d.ts +39 -0
  119. package/dist/policy/policy-write.runtime.js +532 -0
  120. package/dist/prebuilt/core-version.generated.d.ts +2 -0
  121. package/dist/prebuilt/core-version.generated.js +1 -0
  122. package/dist/prebuilt/prebuilt-changes.d.ts +29 -0
  123. package/dist/prebuilt/prebuilt-changes.js +65 -0
  124. package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
  125. package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
  126. package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
  127. package/dist/prebuilt/prebuilt-contracts.js +1 -0
  128. package/dist/prebuilt/prebuilt-contracts.loader.d.ts +19 -0
  129. package/dist/prebuilt/prebuilt-contracts.loader.js +77 -0
  130. package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
  131. package/dist/prebuilt/prebuilt-inputs.fingerprint.js +229 -0
  132. package/dist/protocol/absent-route.responder.d.ts +9 -27
  133. package/dist/protocol/absent-route.responder.js +36 -3
  134. package/dist/protocol/contract-document.responder.d.ts +2 -1
  135. package/dist/protocol/http-request.decoder.d.ts +19 -3
  136. package/dist/protocol/http-request.decoder.js +24 -1
  137. package/dist/protocol/http-request.reader.d.ts +6 -3
  138. package/dist/protocol/http-request.reader.js +1 -0
  139. package/dist/protocol/http-response.encoder.js +2 -2
  140. package/dist/protocol/protocol-headers.d.ts +3 -3
  141. package/dist/protocol/protocol-surface.d.ts +6 -1
  142. package/dist/protocol/protocol-surface.js +7 -0
  143. package/dist/protocol/protocol.binder.d.ts +16 -1
  144. package/dist/protocol/protocol.binder.js +4 -1
  145. package/dist/protocol/protocol.handler.d.ts +12 -1
  146. package/dist/protocol/protocol.handler.js +86 -17
  147. package/dist/runtime/caller-data.copier.d.ts +18 -3
  148. package/dist/runtime/caller-data.copier.js +21 -8
  149. package/dist/runtime/caller-data.door.d.ts +13 -6
  150. package/dist/runtime/computed-read-runtime.d.ts +6 -1
  151. package/dist/runtime/computed-read-runtime.js +232 -45
  152. package/dist/runtime/computed-value.error.d.ts +3 -2
  153. package/dist/runtime/computed-value.error.js +3 -2
  154. package/dist/runtime/context-executor.runtime.d.ts +4 -3
  155. package/dist/runtime/context-executor.runtime.js +4 -2
  156. package/dist/runtime/core-class.brand.d.ts +10 -0
  157. package/dist/runtime/core-class.brand.js +21 -0
  158. package/dist/runtime/create-requirement.d.ts +31 -5
  159. package/dist/runtime/create-requirement.js +129 -21
  160. package/dist/runtime/decimal.d.ts +14 -0
  161. package/dist/runtime/decimal.js +21 -0
  162. package/dist/runtime/diagnostics-reporter.d.ts +44 -9
  163. package/dist/runtime/diagnostics-reporter.js +13 -1
  164. package/dist/runtime/error-boundary.d.ts +9 -2
  165. package/dist/runtime/error-boundary.js +12 -0
  166. package/dist/runtime/error-guidance.d.ts +12 -0
  167. package/dist/runtime/error-guidance.js +1 -0
  168. package/dist/runtime/error.catalog.d.ts +49 -0
  169. package/dist/runtime/error.catalog.js +49 -0
  170. package/dist/runtime/execution-options.d.ts +8 -0
  171. package/dist/runtime/field-check.evaluator.d.ts +0 -2
  172. package/dist/runtime/field-check.evaluator.js +1 -1
  173. package/dist/runtime/field-compute.d.ts +11 -10
  174. package/dist/runtime/field-stage.runtime.d.ts +3 -1
  175. package/dist/runtime/field-stage.runtime.js +8 -4
  176. package/dist/runtime/field-validator.runtime.d.ts +19 -1
  177. package/dist/runtime/field-validator.runtime.js +47 -8
  178. package/dist/runtime/framework-error.js +9 -0
  179. package/dist/runtime/framework-warning.emitter.d.ts +6 -0
  180. package/dist/runtime/framework-warning.emitter.js +8 -0
  181. package/dist/runtime/json-value.d.ts +10 -5
  182. package/dist/runtime/level-lifecycle.runtime.d.ts +4 -3
  183. package/dist/runtime/level-lifecycle.runtime.js +3 -5
  184. package/dist/runtime/mutation-planner.d.ts +1 -6
  185. package/dist/runtime/mutation-planner.js +3 -14
  186. package/dist/runtime/operation-code.guidance.d.ts +6 -0
  187. package/dist/runtime/operation-code.guidance.js +167 -0
  188. package/dist/runtime/operation-request.d.ts +16 -9
  189. package/dist/runtime/operation-runtime.d.ts +35 -17
  190. package/dist/runtime/operation-runtime.js +220 -49
  191. package/dist/runtime/outcome-mapper.d.ts +2 -1
  192. package/dist/runtime/output-validator.d.ts +1 -1
  193. package/dist/runtime/owned-execution.registry.d.ts +13 -3
  194. package/dist/runtime/pipe-output.validator.d.ts +10 -0
  195. package/dist/runtime/pipe-output.validator.js +4 -2
  196. package/dist/runtime/pipeline.d.ts +47 -22
  197. package/dist/runtime/readonly-value.d.ts +20 -5
  198. package/dist/runtime/readonly-value.js +74 -26
  199. package/dist/runtime/referential-action.d.ts +1 -1
  200. package/dist/runtime/scalar-runtime.d.ts +4 -11
  201. package/dist/runtime/scalar-runtime.js +91 -22
  202. package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
  203. package/dist/runtime/unsent-value.normalizer.js +128 -0
  204. package/dist/transactions/operation-arguments-encoder.js +8 -3
  205. package/dist/transactions/operation-fingerprint.js +2 -2
  206. package/dist/transactions/sql-statement.d.ts +10 -2
  207. package/dist/transactions/sql-statement.validator.d.ts +2 -2
  208. package/dist/transactions/sql-statement.validator.js +38 -5
  209. package/dist/transactions/transaction-envelope.d.ts +5 -4
  210. package/dist/transactions/transaction-integrity-error.js +2 -0
  211. package/dist/transactions/transaction-plan-assembler.d.ts +12 -5
  212. package/dist/transactions/transaction-plan-assembler.js +48 -10
  213. package/dist/transactions/transaction-plan-validator.d.ts +23 -7
  214. package/dist/transactions/transaction-plan-validator.js +27 -1
  215. package/dist/transactions/transaction-reference-resolver.d.ts +3 -2
  216. package/dist/transactions/transaction-reference-validator.d.ts +43 -3
  217. package/dist/transactions/transaction-reference-validator.js +83 -57
  218. package/dist/transactions/transaction-reference.d.ts +1 -1
  219. package/dist/transactions/transaction-reference.js +5 -0
  220. package/dist/transactions/transaction-request.d.ts +8 -8
  221. package/dist/transactions/transaction-runtime.d.ts +5 -2
  222. package/dist/transactions/transaction-runtime.js +79 -27
  223. package/dist/transactions/transaction-settlement.runtime.d.ts +4 -3
  224. package/dist/transactions/transaction-slot-path-error.js +2 -0
  225. package/dist/transactions/transaction-stage.runtime.d.ts +5 -3
  226. package/dist/transactions/transaction-stage.runtime.js +7 -4
  227. package/package.json +6 -5
  228. package/src/runtime/field-check.evaluator.ts +1 -1
@@ -1,6 +1,6 @@
1
1
  import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
2
2
  import { advertisedDirectivesAt } from "../contracts/field.js";
3
- import { NESTED_WRITE_SCOPE, resolveWriteScope, } from "../operations/write-scope.resolver.js";
3
+ import { LOOPBACK_WRITE_SCOPE, NESTED_WRITE_SCOPE, resolveWriteScope, } from "../operations/write-scope.resolver.js";
4
4
  import { isRelationMutationDirective, RELATION_MUTATION_RECORD_SITES, } from "./mutation-planner.js";
5
5
  function isRecord(value) {
6
6
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -13,7 +13,7 @@ function edgesOf(table, resource) {
13
13
  return Array.isArray(listed) ? listed : [];
14
14
  }
15
15
  function referencesOf(edge) {
16
- return Array.isArray(edge.references) ? edge.references : [];
16
+ return Array.isArray(edge.referenceFields) ? edge.referenceFields : [];
17
17
  }
18
18
  function creatableScalar(contract, resource, field) {
19
19
  const found = lookupField(lookupResource(contract, resource), field);
@@ -63,9 +63,19 @@ function checkCreateRecord(context, resource, record, path, scope, via) {
63
63
  if (!isRecord(record)) {
64
64
  return;
65
65
  }
66
+ if (context.computed !== undefined) {
67
+ computedRequirementGaps(context, resource, record, path, via);
68
+ walkNestedCreates(context, resource, record, path);
69
+ return;
70
+ }
66
71
  const { contract, issues } = context;
67
- const conflicted = referenceConflicts(context, resource, record, path);
68
- mixedReferenceWrites(context, resource, record, path, conflicted);
72
+ const looped = loopbackWrites(context, resource, record, path, via, "create");
73
+ const conflicted = looped.size > 0
74
+ ? looped
75
+ : referenceConflicts(context, resource, record, path);
76
+ if (looped.size === 0) {
77
+ mixedReferenceWrites(context, resource, record, path, conflicted);
78
+ }
69
79
  const requirements = requirementsOf(contract, resource);
70
80
  if (requirements !== undefined) {
71
81
  const supplied = suppliedThrough(requirements, via);
@@ -157,14 +167,93 @@ function mixedReferenceWrites(context, resource, record, path, conflicted) {
157
167
  });
158
168
  }
159
169
  }
160
- function checkUpdateRecord(context, resource, record, path) {
170
+ function checkUpdateRecord(context, resource, record, path, via) {
161
171
  if (!isRecord(record)) {
162
172
  return;
163
173
  }
164
- const conflicted = referenceConflicts(context, resource, record, path);
165
- mixedReferenceWrites(context, resource, record, path, conflicted);
174
+ if (context.computed === undefined &&
175
+ loopbackWrites(context, resource, record, path, via, "update").size === 0) {
176
+ const conflicted = referenceConflicts(context, resource, record, path);
177
+ mixedReferenceWrites(context, resource, record, path, conflicted);
178
+ }
166
179
  walkNestedCreates(context, resource, record, path);
167
180
  }
181
+ function loopbackWrites(context, resource, record, path, via, stage) {
182
+ const refused = new Set();
183
+ if (via === undefined) {
184
+ return refused;
185
+ }
186
+ const { contract } = context;
187
+ const edge = lookupField(lookupResource(contract, via.resource), via.relation);
188
+ if (edge?.kind !== "relation" || typeof edge.inverse !== "string") {
189
+ return refused;
190
+ }
191
+ const inverse = edge.inverse;
192
+ const other = lookupField(lookupResource(contract, resource), inverse);
193
+ if (other?.kind === "relation" &&
194
+ advertisedDirectivesAt(other.capabilities[stage], LOOPBACK_WRITE_SCOPE) !==
195
+ undefined) {
196
+ return refused;
197
+ }
198
+ const references = edgesOf(context.references, resource).find((candidate) => candidate.field === inverse) ?? undefined;
199
+ for (const field of [
200
+ inverse,
201
+ ...(references === undefined ? [] : referencesOf(references)),
202
+ ]) {
203
+ if (!written(record, field)) {
204
+ continue;
205
+ }
206
+ refused.add(field);
207
+ context.issues.push({
208
+ code: "V1012",
209
+ path: [...path, field],
210
+ message: field === inverse
211
+ ? `Relation "${field}" is the relation this ${resource} is written through, from its other end ("${via.resource}.${via.relation}"): the write it sits in supplies it.`
212
+ : `Field "${field}" holds the reference of "${inverse}", the relation this ${resource} is written through ("${via.resource}.${via.relation}"): the write it sits in supplies it.`,
213
+ });
214
+ }
215
+ return refused;
216
+ }
217
+ function computedRequirementGaps(context, resource, record, path, via) {
218
+ const reader = context.computed;
219
+ const computed = reader?.computed(resource);
220
+ const stated = reader?.stated(resource);
221
+ if (computed === undefined || computed.size === 0 || stated === undefined) {
222
+ return;
223
+ }
224
+ const supplied = suppliedThrough(stated, via);
225
+ const gap = (field) => {
226
+ context.gaps?.push({
227
+ resource,
228
+ field,
229
+ issue: {
230
+ code: "V1000",
231
+ path: [...path, field],
232
+ message: `compute.create on ${resource}.${field} returned undefined for a required field.`,
233
+ },
234
+ });
235
+ };
236
+ for (const field of namesOf(stated.required)) {
237
+ if (computed.has(field) &&
238
+ !supplied.has(field) &&
239
+ !written(record, field)) {
240
+ gap(field);
241
+ }
242
+ }
243
+ const groups = Array.isArray(stated.oneOf) ? stated.oneOf : [];
244
+ for (const group of groups) {
245
+ const [relation, ...references] = namesOf(group);
246
+ const left = references.find((field) => computed.has(field) && !written(record, field));
247
+ if (relation === undefined ||
248
+ left === undefined ||
249
+ supplied.has(relation) ||
250
+ references.some((field) => supplied.has(field)) ||
251
+ written(record, relation)) {
252
+ continue;
253
+ }
254
+ gap(left);
255
+ }
256
+ }
168
257
  function walkNestedCreates(context, resource, record, path) {
169
258
  if (!isRecord(record)) {
170
259
  return;
@@ -196,7 +285,7 @@ function walkNestedCreates(context, resource, record, path) {
196
285
  checkCreateRecord(context, field.target, siteRecord, sitePath, NESTED_WRITE_SCOPE, via);
197
286
  }
198
287
  else {
199
- checkUpdateRecord(context, field.target, siteRecord, sitePath);
288
+ checkUpdateRecord(context, field.target, siteRecord, sitePath, via);
200
289
  }
201
290
  }
202
291
  }
@@ -204,17 +293,43 @@ function walkNestedCreates(context, resource, record, path) {
204
293
  }
205
294
  }
206
295
  export function recordWriteFactsOf(adapter) {
207
- return { references: adapter.referentialActions ?? {} };
296
+ const resources = adapter.model?.resources ?? {};
297
+ return {
298
+ references: adapter.referentialActions ?? {},
299
+ stated: (resource) => {
300
+ const found = Object.hasOwn(resources, resource)
301
+ ? resources[resource]
302
+ : undefined;
303
+ const stated = isRecord(found) ? found.createRequirements : undefined;
304
+ return isRecord(stated) ? stated : undefined;
305
+ },
306
+ };
208
307
  }
209
308
  export function createRequirementIssues(contract, facts, request, args, argumentsPath) {
210
- if (!isRecord(args)) {
211
- return [];
212
- }
213
309
  const context = {
214
310
  contract,
215
311
  references: facts.references,
216
312
  issues: [],
217
313
  };
314
+ walkOperation(context, request, args, argumentsPath);
315
+ return context.issues;
316
+ }
317
+ export function computedRequirementGapsOf(contract, facts, request, args, argumentsPath, computed) {
318
+ const gaps = [];
319
+ walkOperation({
320
+ contract,
321
+ references: facts.references,
322
+ issues: [],
323
+ computed: { stated: facts.stated, computed },
324
+ gaps,
325
+ }, request, args, argumentsPath);
326
+ return gaps;
327
+ }
328
+ function walkOperation(context, request, args, argumentsPath) {
329
+ if (!isRecord(args)) {
330
+ return;
331
+ }
332
+ const contract = context.contract;
218
333
  const scope = resolveWriteScope(lookupResource(contract, request.resource), request);
219
334
  if (request.family === "create" && written(args, "data")) {
220
335
  const data = args.data;
@@ -229,16 +344,9 @@ export function createRequirementIssues(contract, facts, request, args, argument
229
344
  }
230
345
  if (request.family === "upsert") {
231
346
  checkCreateRecord(context, request.resource, args.create, [...argumentsPath, "create"], scope, undefined);
232
- checkUpdateRecord(context, request.resource, args.update, [
233
- ...argumentsPath,
234
- "update",
235
- ]);
347
+ checkUpdateRecord(context, request.resource, args.update, [...argumentsPath, "update"], undefined);
236
348
  }
237
349
  if (request.family === "update") {
238
- checkUpdateRecord(context, request.resource, args.data, [
239
- ...argumentsPath,
240
- "data",
241
- ]);
350
+ checkUpdateRecord(context, request.resource, args.data, [...argumentsPath, "data"], undefined);
242
351
  }
243
- return context.issues;
244
352
  }
@@ -12,3 +12,17 @@ export declare class AvDecimal {
12
12
  */
13
13
  toJSON(): string;
14
14
  }
15
+ /**
16
+ * Whether `value` is an `AvDecimal`, made by this copy of core or by another of
17
+ * its major: one `in`, for the hot paths that ask about every value.
18
+ */
19
+ export declare function isAvDecimal(value: unknown): value is AvDecimal;
20
+ /**
21
+ * The digits of `value`, read without running anything the caller could have put
22
+ * on it when this copy made it. Another copy's decimal is read through its own
23
+ * `toString`, which only that copy can answer; what it returns is checked as a
24
+ * decimal string, so the caller's copy keeps the grammar.
25
+ *
26
+ * @throws TypeError when another copy's decimal does not answer a decimal string.
27
+ */
28
+ export declare function decimalDigitsOf(value: AvDecimal): string;
@@ -1,6 +1,11 @@
1
1
  import { WIRE_SCALAR_PATTERNS } from "../contracts/wire-scalar-format.js";
2
+ import { brandCoreClass, hasCoreBrand } from "./core-class.brand.js";
3
+ let ownDigitsOf;
2
4
  export class AvDecimal {
3
5
  #value;
6
+ static {
7
+ ownDigitsOf = (value) => (#value in value ? value.#value : undefined);
8
+ }
4
9
  constructor(value) {
5
10
  if (typeof value !== "string") {
6
11
  throw new TypeError("Decimal value must be a string.");
@@ -17,3 +22,19 @@ export class AvDecimal {
17
22
  return this.#value;
18
23
  }
19
24
  }
25
+ const DECIMAL_BRAND = brandCoreClass(AvDecimal, "AvDecimal");
26
+ export function isAvDecimal(value) {
27
+ return hasCoreBrand(value, DECIMAL_BRAND);
28
+ }
29
+ export function decimalDigitsOf(value) {
30
+ const own = ownDigitsOf(value);
31
+ if (own !== undefined) {
32
+ return own;
33
+ }
34
+ const digits = value.toString();
35
+ if (typeof digits !== "string" ||
36
+ !WIRE_SCALAR_PATTERNS.decimal.test(digits)) {
37
+ throw new TypeError("Decimal value must be a valid decimal string.");
38
+ }
39
+ return digits;
40
+ }
@@ -1,7 +1,7 @@
1
1
  import type { AvRequestOrigin } from "../config/config-layer.js";
2
2
  import type { AvOperationFamily } from "../operations/operation-identity.js";
3
- import type { FrameworkTransactionRequest } from "../transactions/transaction-request.js";
4
- import type { FrameworkOperationRequest } from "./operation-request.js";
3
+ import type { AvTransactionRequest } from "../transactions/transaction-request.js";
4
+ import type { AvOperationRequest } from "./operation-request.js";
5
5
  import { type AvOperationErrorCode, type AvOperationFailureResponse } from "./operation-response.js";
6
6
  /**
7
7
  * One unplanned failure of an operation: the raw error, the code the caller
@@ -47,7 +47,8 @@ export type AvTransactionDiagnostic = {
47
47
  };
48
48
  /**
49
49
  * One unplanned failure of the MCP endpoint itself, outside any tool call's
50
- * operation: the raw error, answered `500` with no detail.
50
+ * operation: the raw error, answered `500` with no detail, or `401` when it is
51
+ * the throw of `mcp.authorization.verify`.
51
52
  */
52
53
  export type AvMcpDiagnostic = {
53
54
  readonly kind: "mcp";
@@ -62,8 +63,40 @@ export type AvMcpDiagnostic = {
62
63
  /** Never present: only an operation diagnostic is marked `deliberate`. */
63
64
  readonly deliberate?: never;
64
65
  };
65
- /** One diagnostic: of an operation, of a transaction as a whole, or of the MCP endpoint. */
66
- export type AvDiagnostic = AvOperationDiagnostic | AvTransactionDiagnostic | AvMcpDiagnostic;
66
+ /**
67
+ * One unplanned failure of a `POST /_access/can` request: the raw error, the
68
+ * code the caller received, and, when one question met it, that question's
69
+ * index (`question`), Resource and action; none of the three when the batch
70
+ * as a whole failed.
71
+ */
72
+ export type AvCanDiagnostic = {
73
+ readonly kind: "can";
74
+ readonly error: unknown;
75
+ readonly code: AvOperationErrorCode;
76
+ /** The route is the clients': an agent has no `can` route. */
77
+ readonly origin: "client";
78
+ readonly requestId: string;
79
+ /** Never present: no operation of a plan is involved (see `question`). */
80
+ readonly operation?: never;
81
+ /** Never present: only an operation diagnostic is marked `deliberate`. */
82
+ readonly deliberate?: never;
83
+ } & ({
84
+ /** The index of the question in `questions` the failure is blamed on. */
85
+ readonly question: number;
86
+ /** That question's Resource. */
87
+ readonly resource: string;
88
+ /** That question's action. */
89
+ readonly action: "read" | "create" | "update" | "delete";
90
+ } | {
91
+ readonly question?: never;
92
+ readonly resource?: never;
93
+ readonly action?: never;
94
+ });
95
+ /**
96
+ * One diagnostic: of an operation, of a transaction as a whole, of a `can`
97
+ * request, or of the MCP endpoint.
98
+ */
99
+ export type AvDiagnostic = AvOperationDiagnostic | AvTransactionDiagnostic | AvCanDiagnostic | AvMcpDiagnostic;
67
100
  /**
68
101
  * Receives a diagnostic for every unplanned failure. Set once, as the root
69
102
  * configuration's `diagnostics`.
@@ -80,8 +113,10 @@ export type DiagnosticMarks = Pick<AvOperationDiagnostic, "deliberate">;
80
113
  /** The identifying half of a diagnostic, captured once per request. */
81
114
  type OperationDiagnosticIdentity = Omit<AvOperationDiagnostic, "error" | "code">;
82
115
  type TransactionDiagnosticIdentity = Omit<AvTransactionDiagnostic, "error" | "code">;
116
+ /** A `can` request's identity: its batch's, or one question's. */
117
+ type CanDiagnosticIdentity = AvCanDiagnostic extends infer Diagnostic ? Diagnostic extends unknown ? Omit<Diagnostic, "error" | "code"> : never : never;
83
118
  /** The diagnostics channel bound to one diagnostic identity. */
84
- export declare function createReporter(sink: AvDiagnosticsSink | undefined, identity: OperationDiagnosticIdentity | TransactionDiagnosticIdentity): DiagnosticsReporter;
119
+ export declare function createReporter(sink: AvDiagnosticsSink | undefined, identity: OperationDiagnosticIdentity | TransactionDiagnosticIdentity | CanDiagnosticIdentity): DiagnosticsReporter;
85
120
  /**
86
121
  * The MCP endpoint's failure channel for `framework`: each call reports one
87
122
  * {@link AvMcpDiagnostic} to its configured sink, under a minted request id, and
@@ -99,14 +134,14 @@ export declare function diagnosticsSinkOf(framework: object): AvDiagnosticsSink
99
134
  * Binds the configured sink to one operation request. The sink is not awaited,
100
135
  * and nothing it throws or rejects with reaches the response.
101
136
  */
102
- export declare function createDiagnosticsReporter(sink: AvDiagnosticsSink | undefined, request: FrameworkOperationRequest, operation?: number): DiagnosticsReporter;
137
+ export declare function createDiagnosticsReporter(sink: AvDiagnosticsSink | undefined, request: AvOperationRequest, operation?: number): DiagnosticsReporter;
103
138
  /** The diagnostics channel of one transaction request. */
104
139
  export type TransactionDiagnosticsChannel = {
105
140
  /** A plan-level report; `operation` attributes it to a recorded failing index. */
106
141
  readonly transaction: (operation?: number) => DiagnosticsReporter;
107
142
  /** A report carrying one plan operation's own identity and index. */
108
- readonly operation: (request: FrameworkOperationRequest, operation: number) => DiagnosticsReporter;
143
+ readonly operation: (request: AvOperationRequest, operation: number) => DiagnosticsReporter;
109
144
  };
110
145
  /** Binds the configured sink to one transaction request and its plan operations. */
111
- export declare function createTransactionDiagnosticsChannel(sink: AvDiagnosticsSink | undefined, request: FrameworkTransactionRequest): TransactionDiagnosticsChannel;
146
+ export declare function createTransactionDiagnosticsChannel(sink: AvDiagnosticsSink | undefined, request: AvTransactionRequest): TransactionDiagnosticsChannel;
112
147
  export {};
@@ -1,3 +1,4 @@
1
+ import { coreBrandOf } from "./core-class.brand.js";
1
2
  import { internalFailure, } from "./operation-response.js";
2
3
  import { requestIdOf } from "./request-id.js";
3
4
  const IGNORE_DIAGNOSTIC = () => undefined;
@@ -31,7 +32,18 @@ export function createReporter(sink, identity) {
31
32
  }
32
33
  };
33
34
  }
34
- const CONFIGURED_SINKS = new WeakMap();
35
+ const CONFIGURED_SINKS = sharedSinkRegistry();
36
+ function sharedSinkRegistry() {
37
+ const key = coreBrandOf("diagnostics");
38
+ const holder = globalThis;
39
+ const existing = holder[key];
40
+ if (existing instanceof WeakMap) {
41
+ return existing;
42
+ }
43
+ const registry = new WeakMap();
44
+ Object.defineProperty(holder, key, { value: registry });
45
+ return registry;
46
+ }
35
47
  export function mcpFailureReporterOf(framework) {
36
48
  const sink = CONFIGURED_SINKS.get(framework);
37
49
  return (error) => {
@@ -3,7 +3,7 @@ import { AvError } from "./framework-error.js";
3
3
  import type { AvCause, AvOperationErrorCode, AvOperationFailureResponse } from "./operation-response.js";
4
4
  import type { AvContext } from "./pipeline.js";
5
5
  /** Where an unexpected post-validation exception originated. */
6
- export type FailureOrigin = "adapter" | "runtime";
6
+ type FailureOrigin = "adapter" | "runtime";
7
7
  /** The one generic Adapter-failure text; every unexpected A3001 carries it. */
8
8
  export declare const ADAPTER_FAILURE_MESSAGE = "Adapter execution failed.";
9
9
  /** One boundary instance belongs to one operation execution. */
@@ -50,6 +50,13 @@ type DeliberateOutcome = {
50
50
  */
51
51
  readonly unvouched: readonly unknown[];
52
52
  };
53
+ /**
54
+ * The refusal code `error` deliberately carries, vetted as every boundary vets
55
+ * it ({@link vetDeliberateOutcome}): a recognised error code that is no
56
+ * internal (`A3xxx`) one, with a well-formed cause. `undefined` for anything
57
+ * else, which the caller answers as the defect it is.
58
+ */
59
+ export declare function deliberateRefusalCode(error: unknown): AvOperationErrorCode | undefined;
53
60
  /**
54
61
  * Final framework sanitization, shared by every boundary. A deliberate
55
62
  * {@link AvError} carries its own stable outcome; anything else is
@@ -100,7 +107,7 @@ export declare function resolveUnhandledFailure(failure: UnhandledFailure, chain
100
107
  * A chain of filters and the context they are told, tried in reverse: the first
101
108
  * that returns an {@link AvError} handles the error.
102
109
  */
103
- export type FilterChain = {
110
+ type FilterChain = {
104
111
  readonly attempt: (error: unknown, view: CallerArgumentsView, boundary: AdapterBoundary) => Promise<FilteredOutcome | undefined>;
105
112
  };
106
113
  /** The chain of `filters` (stored outer to inner, tried in reverse), told `context`. */
@@ -1,3 +1,4 @@
1
+ import { isDatabaseRefusalMessage } from "../operations/database-refusal.js";
1
2
  import { VALIDATION_CODE_MESSAGES, VALIDATION_CODES, } from "../operations/validation-code.js";
2
3
  import { AvError } from "./framework-error.js";
3
4
  import { INTERNAL_FAILURE_MESSAGE, internalFailure, OPERATION_CODES, operationFailure, } from "./operation-response.js";
@@ -164,6 +165,9 @@ function projectIssue(value, unvouched, view, author) {
164
165
  unvouched.push(supplied);
165
166
  }
166
167
  }
168
+ else if (isDatabaseRefusalMessage(code, supplied)) {
169
+ message = supplied;
170
+ }
167
171
  if (rawPath === undefined) {
168
172
  return { code, message };
169
173
  }
@@ -238,6 +242,14 @@ function vetDeliberateOutcome(error, view, author) {
238
242
  const cause = projectCause(rawCause, unvouched, view, author);
239
243
  return cause === undefined ? MALFORMED_OUTCOME : { code, cause, unvouched };
240
244
  }
245
+ export function deliberateRefusalCode(error) {
246
+ const outcome = vetDeliberateOutcome(error, { path: [], arguments: undefined }, "developer");
247
+ return outcome === undefined ||
248
+ outcome === MALFORMED_OUTCOME ||
249
+ isInternalCode(outcome.code)
250
+ ? undefined
251
+ : outcome.code;
252
+ }
241
253
  function reportUnvouched(outcome, report) {
242
254
  for (const value of outcome.unvouched) {
243
255
  report(value, outcome.code);
@@ -0,0 +1,12 @@
1
+ /** Why a code is answered, and what the one who meets it should do. */
2
+ export type ErrorCodeGuidance = {
3
+ /** What typically produced the code, in a sentence or two. */
4
+ readonly cause: string;
5
+ /** What to change so it does not happen again. */
6
+ readonly fix: string;
7
+ };
8
+ /** Guidance for a code whose table holds no message of its own. */
9
+ export type ErrorCodeDescription = ErrorCodeGuidance & {
10
+ /** The code's meaning, in one short sentence. */
11
+ readonly message: string;
12
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ import type { AvAdapterDiscoveryDiagnosticCode } from "../adapters/adapter-discovery-diagnostic.js";
2
+ import type { AvContractCompilerDiagnosticCode } from "../compiler/contract-compiler-diagnostic.js";
3
+ import type { AvConfigDiagnosticCode } from "../config/config-diagnostic.js";
4
+ import type { AvConstructionStage } from "../framework/framework-construction.error.js";
5
+ import { type AvValidationCode } from "../operations/validation-code.js";
6
+ import { type AvOperationCode } from "./operation-response.js";
7
+ /**
8
+ * The documentation page every catalog entry links into. Each code has its own
9
+ * anchor there: the code in lower case (`#a2004`, `#config_required`).
10
+ */
11
+ export declare const ERROR_CODES_DOCS_URL = "https://aventara-framework.vercel.app/docs/error-codes";
12
+ /**
13
+ * What kind of code an entry is: an outcome code's family (`success` `A1xxx`,
14
+ * `request` `A2xxx`, `internal` `A3xxx`, `auth` `A4xxx`), a `validation` issue
15
+ * code (`V1xxx`), or a startup code, by the construction stage that reports it.
16
+ */
17
+ export type AvErrorKind = "success" | "request" | "internal" | "auth" | "validation" | AvConstructionStage;
18
+ /** Every code the catalog describes: outcome, validation and startup codes. */
19
+ export type AvErrorCatalogCode = AvOperationCode | AvValidationCode | AvConfigDiagnosticCode | AvAdapterDiscoveryDiagnosticCode | AvContractCompilerDiagnosticCode;
20
+ /** One code: what it means, why it happens, what to do, and where it is documented. */
21
+ export type AvErrorCatalogEntry<C extends AvErrorCatalogCode = AvErrorCatalogCode> = {
22
+ readonly code: C;
23
+ readonly kind: AvErrorKind;
24
+ /** Its meaning in one sentence; for a V-code, the framework's message for it. */
25
+ readonly message: string;
26
+ /** What typically produces it. */
27
+ readonly cause: string;
28
+ /** What to change. */
29
+ readonly fix: string;
30
+ /** Its entry on the error-codes page. */
31
+ readonly docs: string;
32
+ };
33
+ /** The error catalog's type: one entry per code. */
34
+ export type AvErrorCatalog = {
35
+ readonly [C in AvErrorCatalogCode]: AvErrorCatalogEntry<C>;
36
+ };
37
+ /** The documentation URL of one code: the error-codes page at its anchor. */
38
+ export declare function errorDocsUrl(code: AvErrorCatalogCode): string;
39
+ /**
40
+ * Every code Aventara answers or refuses with, by code: its `kind`, `message`,
41
+ * `cause`, `fix` and `docs` URL. An operation's `code`, a validation issue's
42
+ * `code` and a startup diagnostic's `code` are all keys of it:
43
+ *
44
+ * ```ts
45
+ * AvErrorCatalog[error.code].fix;
46
+ * AvErrorCatalog.V1000.docs; // https://aventara-framework.vercel.app/docs/error-codes#v1000
47
+ * ```
48
+ */
49
+ export declare const AvErrorCatalog: AvErrorCatalog;
@@ -0,0 +1,49 @@
1
+ import { ADAPTER_DISCOVERY_DIAGNOSTIC_GUIDANCE } from "../adapters/adapter-discovery-diagnostic.guidance.js";
2
+ import { CONTRACT_COMPILER_DIAGNOSTIC_GUIDANCE } from "../compiler/contract-compiler-diagnostic.guidance.js";
3
+ import { CONFIG_DIAGNOSTIC_GUIDANCE } from "../config/config-diagnostic.guidance.js";
4
+ import { VALIDATION_CODE_GUIDANCE } from "../operations/validation-code.guidance.js";
5
+ import { VALIDATION_CODE_MESSAGES, VALIDATION_CODES, } from "../operations/validation-code.js";
6
+ import { OPERATION_CODE_GUIDANCE } from "./operation-code.guidance.js";
7
+ import { OPERATION_CODES } from "./operation-response.js";
8
+ export const ERROR_CODES_DOCS_URL = "https://aventara-framework.vercel.app/docs/error-codes";
9
+ export function errorDocsUrl(code) {
10
+ return `${ERROR_CODES_DOCS_URL}#${code.toLowerCase()}`;
11
+ }
12
+ const OUTCOME_KIND = {
13
+ "1": "success",
14
+ "2": "request",
15
+ "3": "internal",
16
+ "4": "auth",
17
+ };
18
+ function entry(code, kind, description) {
19
+ return Object.freeze({
20
+ code,
21
+ kind,
22
+ message: description.message,
23
+ cause: description.cause,
24
+ fix: description.fix,
25
+ docs: errorDocsUrl(code),
26
+ });
27
+ }
28
+ function entriesOf(table, kind) {
29
+ return Object.keys(table).map((code) => [
30
+ code,
31
+ entry(code, kind, table[code]),
32
+ ]);
33
+ }
34
+ export const AvErrorCatalog = Object.freeze(Object.fromEntries([
35
+ ...OPERATION_CODES.map((code) => [
36
+ code,
37
+ entry(code, OUTCOME_KIND[code[1]], OPERATION_CODE_GUIDANCE[code]),
38
+ ]),
39
+ ...VALIDATION_CODES.map((code) => [
40
+ code,
41
+ entry(code, "validation", {
42
+ message: VALIDATION_CODE_MESSAGES[code],
43
+ ...VALIDATION_CODE_GUIDANCE[code],
44
+ }),
45
+ ]),
46
+ ...entriesOf(CONFIG_DIAGNOSTIC_GUIDANCE, "configuration"),
47
+ ...entriesOf(ADAPTER_DISCOVERY_DIAGNOSTIC_GUIDANCE, "discovery"),
48
+ ...entriesOf(CONTRACT_COMPILER_DIAGNOSTIC_GUIDANCE, "contract-compilation"),
49
+ ]));
@@ -18,4 +18,12 @@ export type AvExecutionOptions = {
18
18
  * object), the Framework starts the request with `{}`.
19
19
  */
20
20
  readonly state?: AvState & object;
21
+ /**
22
+ * The principal the call or run is checked as by the configuration's
23
+ * `policy`. An application call names one to opt in: left out, it is server
24
+ * code and the policy does not check it. A `framework.client.*` call is
25
+ * checked either way, and `as` replaces the principal `policy.principal`
26
+ * would derive from its state. `null` is the anonymous principal.
27
+ */
28
+ readonly as?: unknown;
21
29
  };
@@ -24,6 +24,4 @@ export declare function fieldCheckIssue(check: AvFieldCheck, subject: FieldCheck
24
24
  export declare function fieldCheckIssues(checks: readonly AvFieldCheck[], value: unknown, subject: FieldCheckSubject, path: Path, list: boolean): readonly AvValidationIssue[];
25
25
  /** The issues of the checks one value — a whole value, or a list's entry — breaks. */
26
26
  export declare function entryCheckIssues(checks: readonly AvFieldCheck[], value: unknown, subject: FieldCheckSubject, path: Path): readonly AvValidationIssue[];
27
- /** Whether a check judges a list's own length (`AvZ.minItems`/`AvZ.maxItems`). */
28
- export declare function isListCheck(check: AvFieldCheck): boolean;
29
27
  export {};
@@ -515,7 +515,7 @@ export function entryCheckIssues(checks, value, subject, path) {
515
515
  }
516
516
  return issues;
517
517
  }
518
- export function isListCheck(check) {
518
+ function isListCheck(check) {
519
519
  return ((check.check === "max_length" || check.check === "min_length") &&
520
520
  check.list === true);
521
521
  }
@@ -17,26 +17,27 @@ export type AvComputeContext<M extends AvAdapterModel = AvAdapterModel> = AvComp
17
17
  readonly $state: AvDeepReadonly<AvState>;
18
18
  };
19
19
  /**
20
- * What a write-stage compute callback is told about the field it is computing.
20
+ * Computes a field's value when a row is created, whether or not the caller sent
21
+ * it. It receives the incoming `data` and returns the value to store; returning
22
+ * `undefined` keeps the current value.
21
23
  */
22
- type ComputedWriteContext<M extends AvAdapterModel> = {
24
+ export type AvComputeCreate<M extends AvAdapterModel = AvAdapterModel> = (context: {
23
25
  readonly field: string;
24
26
  readonly value: unknown;
25
27
  readonly data: Readonly<Record<string, unknown>>;
26
28
  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>;
29
+ }) => unknown | Promise<unknown>;
34
30
  /**
35
31
  * Computes a field's value when a row is updated, whether or not the caller sent
36
32
  * it. It receives the incoming `data` and returns the value to store; returning
37
33
  * `undefined` keeps the current value.
38
34
  */
39
- export type AvComputeUpdate<M extends AvAdapterModel = AvAdapterModel> = (context: ComputedWriteContext<M>) => unknown | Promise<unknown>;
35
+ export type AvComputeUpdate<M extends AvAdapterModel = AvAdapterModel> = (context: {
36
+ readonly field: string;
37
+ readonly value: unknown;
38
+ readonly data: Readonly<Record<string, unknown>>;
39
+ readonly ctx: AvComputeContext<M>;
40
+ }) => unknown | Promise<unknown>;
40
41
  /**
41
42
  * Computes a field's value when a row is read. It receives the stored `record`
42
43
  * and returns the value to return; returning `undefined` keeps the current value.
@@ -6,13 +6,15 @@ export type Path = readonly (string | number)[];
6
6
  type UnknownRecord = Readonly<Record<string, unknown>>;
7
7
  /** A plain object: what a write record, or a directive's operand, is. */
8
8
  export declare function isPlainRecord(value: unknown): value is UnknownRecord;
9
+ /** The containers a family writes, by argument key. */
10
+ export declare const WRITE_KEYS: Readonly<Partial<Record<AvOperationFamily, readonly string[]>>>;
9
11
  /** The fields of `resource` with stages, in the Resource's field order. */
10
12
  export declare function stagedFieldsOf(contract: AvContract, resource: string, stages: Readonly<Record<string, ResolvedFieldStages>>, kind: "pipes" | "guards"): readonly string[];
11
13
  /** The value at `path` in `args`, or `undefined` when nothing is there. */
12
14
  export declare function valueAt(args: unknown, path: Path): unknown;
13
15
  /** `args` with the record at `path` replaced, every container on the way copied. */
14
16
  export declare function replaceAt(args: unknown, path: Path, record: UnknownRecord): unknown;
15
- export type PipedFields = {
17
+ type PipedFields = {
16
18
  readonly args: unknown;
17
19
  /** Whether a field pipe returned a value: the arguments must be held to the server's rules again. */
18
20
  readonly changed: boolean;