@aventara/core 0.1.0-pilot.0 → 0.1.0-pilot.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 (140) hide show
  1. package/README.md +3 -3
  2. package/dist/adapters/adapter-field.d.ts +4 -4
  3. package/dist/adapters/adapter-resource.d.ts +10 -12
  4. package/dist/adapters/adapter.interface.d.ts +20 -10
  5. package/dist/compiler/contract-compiler-diagnostic.d.ts +3 -4
  6. package/dist/compiler/contract.compiler.d.ts +5 -4
  7. package/dist/compiler/contract.compiler.js +0 -132
  8. package/dist/config/config.resolver.d.ts +2 -2
  9. package/dist/config/config.resolver.js +0 -5
  10. package/dist/config/config.validator.d.ts +3 -3
  11. package/dist/config/config.validator.js +0 -8
  12. package/dist/config/define-fields.d.ts +2 -7
  13. package/dist/config/define-fields.js +0 -15
  14. package/dist/config/effective-fields.d.ts +4 -8
  15. package/dist/config/entrypoint.normalizer.d.ts +7 -7
  16. package/dist/config/entrypoint.normalizer.js +0 -22
  17. package/dist/config/framework-behaviors-config.d.ts +6 -6
  18. package/dist/config/framework-config.d.ts +3 -5
  19. package/dist/config/framework-restrictions-config.d.ts +19 -29
  20. package/dist/config/pipeline-config.d.ts +8 -18
  21. package/dist/contracts/capability.d.ts +18 -21
  22. package/dist/contracts/capability.js +0 -75
  23. package/dist/contracts/client-contract-structure.validator.d.ts +15 -22
  24. package/dist/contracts/client-contract-structure.validator.js +0 -27
  25. package/dist/contracts/contract-canonicalizer.js +0 -96
  26. package/dist/contracts/contract-hash.js +0 -2
  27. package/dist/contracts/contract-limits.js +0 -1
  28. package/dist/contracts/contract-lookup.js +0 -10
  29. package/dist/contracts/contract-protocol.js +0 -10
  30. package/dist/contracts/field.d.ts +23 -26
  31. package/dist/contracts/field.js +0 -107
  32. package/dist/contracts/lifecycle-trait.js +0 -5
  33. package/dist/contracts/resource-operations.d.ts +17 -18
  34. package/dist/contracts/resource-operations.js +0 -63
  35. package/dist/contracts/scalar-value-type.d.ts +12 -22
  36. package/dist/contracts/scalar.js +0 -1
  37. package/dist/contracts/wire-scalar-format.d.ts +20 -20
  38. package/dist/contracts/wire-scalar-format.js +0 -26
  39. package/dist/framework/contract-view.d.ts +7 -8
  40. package/dist/framework/framework-construction.error.js +0 -1
  41. package/dist/framework/framework.d.ts +26 -27
  42. package/dist/framework/framework.factory.js +0 -49
  43. package/dist/framework/operation-facade.js +0 -17
  44. package/dist/operations/operation-arguments.d.ts +36 -46
  45. package/dist/operations/operation-boolean-nodes.js +0 -16
  46. package/dist/operations/operation-call.d.ts +4 -9
  47. package/dist/operations/operation-identity.js +0 -1
  48. package/dist/operations/operation-nesting.js +0 -5
  49. package/dist/operations/operation-result.d.ts +4 -4
  50. package/dist/operations/operation-validator-core.d.ts +2 -2
  51. package/dist/operations/operation-validator-core.js +6 -71
  52. package/dist/operations/operation-validator.d.ts +4 -4
  53. package/dist/operations/operation-validator.js +0 -12
  54. package/dist/operations/projection-scope.resolver.d.ts +8 -10
  55. package/dist/operations/projection-scope.resolver.js +0 -35
  56. package/dist/operations/validation-code.d.ts +11 -18
  57. package/dist/operations/validation-code.js +0 -24
  58. package/dist/operations/write-scope.resolver.d.ts +4 -7
  59. package/dist/operations/write-scope.resolver.js +0 -37
  60. package/dist/protocol/absent-route.responder.d.ts +33 -48
  61. package/dist/protocol/absent-route.responder.js +13 -57
  62. package/dist/protocol/av-protocol.d.ts +6 -6
  63. package/dist/protocol/av-protocol.js +0 -10
  64. package/dist/protocol/contract-document.responder.d.ts +15 -18
  65. package/dist/protocol/contract-document.responder.js +0 -33
  66. package/dist/protocol/http-request.decoder.d.ts +55 -61
  67. package/dist/protocol/http-request.decoder.js +0 -62
  68. package/dist/protocol/http-request.reader.d.ts +12 -13
  69. package/dist/protocol/http-request.reader.js +0 -45
  70. package/dist/protocol/http-response.encoder.d.ts +17 -18
  71. package/dist/protocol/http-response.encoder.js +0 -59
  72. package/dist/protocol/http-status.table.d.ts +7 -11
  73. package/dist/protocol/http-status.table.js +1 -15
  74. package/dist/protocol/index.d.ts +5 -5
  75. package/dist/protocol/index.js +0 -9
  76. package/dist/protocol/operation-response.guard.d.ts +12 -19
  77. package/dist/protocol/operation-response.guard.js +0 -29
  78. package/dist/protocol/protocol-failure.d.ts +26 -23
  79. package/dist/protocol/protocol-failure.js +6 -6
  80. package/dist/protocol/protocol-headers.d.ts +30 -27
  81. package/dist/protocol/protocol-headers.js +3 -61
  82. package/dist/protocol/protocol-surface.d.ts +25 -29
  83. package/dist/protocol/protocol-surface.js +0 -19
  84. package/dist/protocol/protocol.binder.d.ts +58 -71
  85. package/dist/protocol/protocol.binder.js +0 -8
  86. package/dist/protocol/protocol.handler.d.ts +28 -32
  87. package/dist/protocol/protocol.handler.js +0 -43
  88. package/dist/runtime/computed-read-runtime.js +0 -29
  89. package/dist/runtime/create-requirement.d.ts +65 -0
  90. package/dist/runtime/create-requirement.js +141 -0
  91. package/dist/runtime/decimal.d.ts +2 -2
  92. package/dist/runtime/decimal.js +0 -8
  93. package/dist/runtime/diagnostics-reporter.d.ts +6 -6
  94. package/dist/runtime/diagnostics-reporter.js +0 -35
  95. package/dist/runtime/error-boundary.d.ts +3 -3
  96. package/dist/runtime/error-boundary.js +2 -167
  97. package/dist/runtime/execution-options.d.ts +4 -4
  98. package/dist/runtime/field-behavior.d.ts +6 -9
  99. package/dist/runtime/framework-error.js +0 -4
  100. package/dist/runtime/mutation-planner.d.ts +5 -6
  101. package/dist/runtime/mutation-planner.js +0 -23
  102. package/dist/runtime/operation-response.d.ts +1 -1
  103. package/dist/runtime/operation-response.js +1 -17
  104. package/dist/runtime/operation-runtime.d.ts +24 -22
  105. package/dist/runtime/operation-runtime.js +18 -52
  106. package/dist/runtime/outcome-mapper.js +0 -22
  107. package/dist/runtime/output-validator.js +0 -1
  108. package/dist/runtime/pipe-output.validator.d.ts +48 -0
  109. package/dist/runtime/pipe-output.validator.js +79 -0
  110. package/dist/runtime/pipeline.d.ts +20 -26
  111. package/dist/runtime/referential-action.d.ts +14 -19
  112. package/dist/runtime/referential-action.js +0 -23
  113. package/dist/runtime/request-id.js +0 -21
  114. package/dist/runtime/scalar-runtime.d.ts +14 -16
  115. package/dist/runtime/scalar-runtime.js +12 -57
  116. package/dist/transactions/deferred-operation.d.ts +9 -14
  117. package/dist/transactions/operation-arguments-encoder.d.ts +0 -1
  118. package/dist/transactions/operation-arguments-encoder.js +0 -14
  119. package/dist/transactions/operation-fingerprint.js +0 -1
  120. package/dist/transactions/transaction-envelope.js +0 -14
  121. package/dist/transactions/transaction-integrity-error.d.ts +10 -11
  122. package/dist/transactions/transaction-integrity-error.js +0 -5
  123. package/dist/transactions/transaction-plan-assembler.d.ts +0 -1
  124. package/dist/transactions/transaction-plan-assembler.js +0 -35
  125. package/dist/transactions/transaction-plan-validator.d.ts +5 -5
  126. package/dist/transactions/transaction-plan-validator.js +0 -63
  127. package/dist/transactions/transaction-reference-resolver.d.ts +12 -13
  128. package/dist/transactions/transaction-reference-resolver.js +0 -16
  129. package/dist/transactions/transaction-reference-validator.js +0 -8
  130. package/dist/transactions/transaction-reference.d.ts +19 -43
  131. package/dist/transactions/transaction-reference.js +0 -11
  132. package/dist/transactions/transaction-request.d.ts +4 -5
  133. package/dist/transactions/transaction-response.d.ts +1 -1
  134. package/dist/transactions/transaction-runtime.d.ts +2 -3
  135. package/dist/transactions/transaction-runtime.js +5 -44
  136. package/dist/transactions/transaction-slot-path-error.d.ts +10 -11
  137. package/dist/transactions/transaction-slot-path-error.js +0 -7
  138. package/dist/transactions/transaction-slot-writer.d.ts +12 -12
  139. package/dist/transactions/transaction-slot-writer.js +0 -34
  140. package/package.json +2 -2
@@ -0,0 +1,65 @@
1
+ import type { Contract } from "../contracts/contract.js";
2
+ import type { OperationIdentity } from "../operations/operation-identity.js";
3
+ import type { ValidationIssue } from "../operations/validation-code.js";
4
+ /**
5
+ * Core's create-requirement vocabulary, and the check that reads it.
6
+ *
7
+ * **The facts are an Adapter's.** Which values a store cannot create a row
8
+ * without is a property of the store's schema — a column with no default, a
9
+ * reference that must point somewhere — and only the Adapter can read it. The
10
+ * words are Aventara's, so an Adapter translates its provider's facts into them,
11
+ * and the reasoning over them is core's, so no Adapter re-implements it.
12
+ *
13
+ * None of this enters a Contract. A Contract carries no foreign-key detail and
14
+ * no knowledge of which relation is the other end of which, and a requirement
15
+ * needs both; it is a server-side fact, read once when the Framework is
16
+ * constructed, like the referential actions.
17
+ *
18
+ * **When the check runs.** After guards, pipes and computed write behaviours —
19
+ * the moment the arguments are what the Adapter will be handed — and before
20
+ * any `before` hook or Adapter work. Earlier would refuse a value a pipe or a
21
+ * computed behaviour was about to supply; later is the store's own refusal,
22
+ * which reaches the caller as an internal failure. A missing value is the
23
+ * caller's to fix, so it answers `A2004` with a `V1000` issue at the path where
24
+ * the value belongs.
25
+ */
26
+ /** A relation edge, named by the Resource that holds it and its field there. */
27
+ export type CreateRequirementEdge = {
28
+ readonly resource: string;
29
+ readonly relation: string;
30
+ };
31
+ /**
32
+ * One value a create of a Resource cannot do without.
33
+ *
34
+ * - `field` — a scalar with no default, generated value or other source: it
35
+ * must be written.
36
+ * - `relation` — a required to-one relation: it must be written (connected or
37
+ * created), or every one of `orFields` must be — the scalar fields that hold
38
+ * the reference. When the row is created **through** the relation's other end
39
+ * (`inverse`), the parent supplies it, and it is not required.
40
+ */
41
+ export type CreateRequirement = {
42
+ readonly kind: "field";
43
+ readonly field: string;
44
+ } | {
45
+ readonly kind: "relation";
46
+ readonly relation: string;
47
+ readonly orFields: readonly string[];
48
+ readonly inverse?: CreateRequirementEdge;
49
+ };
50
+ /**
51
+ * Every Resource's create requirements, keyed by Resource. A Resource that is
52
+ * absent, or carries an empty list, requires nothing core can check.
53
+ */
54
+ export type CreateRequirementTable = Readonly<Record<string, readonly CreateRequirement[]>>;
55
+ type Path = readonly (string | number)[];
56
+ /**
57
+ * The `V1000` issues for every create position in one operation's arguments
58
+ * that lacks a value the Adapter requires — `create.*`'s `data`, `upsert`'s
59
+ * `create`, and every nested create under any write — in argument order.
60
+ * Empty when nothing is missing. Total over malformed input.
61
+ */
62
+ export declare function missingCreateValues(contract: Contract, table: CreateRequirementTable, request: OperationIdentity & {
63
+ readonly resource: string;
64
+ }, args: unknown, argumentsPath: Path): readonly ValidationIssue[];
65
+ export {};
@@ -0,0 +1,141 @@
1
+ import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
2
+ import { advertisedDirectivesAt } from "../contracts/field.js";
3
+ import { NESTED_WRITE_SCOPE, resolveWriteScope, } from "../operations/write-scope.resolver.js";
4
+ import { isRelationMutationDirective, RELATION_MUTATION_RECORD_SITES, } from "./mutation-planner.js";
5
+ function isRecord(value) {
6
+ return value !== null && typeof value === "object" && !Array.isArray(value);
7
+ }
8
+ function written(record, key) {
9
+ return Object.hasOwn(record, key) && record[key] !== undefined;
10
+ }
11
+ function requirementsOf(table, resource) {
12
+ const listed = Object.hasOwn(table, resource) ? table[resource] : undefined;
13
+ return Array.isArray(listed) ? listed : [];
14
+ }
15
+ function creatableScalar(contract, resource, field) {
16
+ const found = lookupField(lookupResource(contract, resource), field);
17
+ return found?.kind === "scalar" && found.capabilities.create !== undefined;
18
+ }
19
+ function creatableRelation(contract, resource, relation, scope) {
20
+ const found = lookupField(lookupResource(contract, resource), relation);
21
+ return (found?.kind === "relation" &&
22
+ advertisedDirectivesAt(found.capabilities.create, scope) !== undefined);
23
+ }
24
+ function checkCreateRecord(context, resource, record, path, scope, via) {
25
+ if (!isRecord(record)) {
26
+ return;
27
+ }
28
+ const { contract, issues } = context;
29
+ for (const requirement of requirementsOf(context.table, resource)) {
30
+ if (requirement.kind === "field") {
31
+ if (!written(record, requirement.field) &&
32
+ creatableScalar(contract, resource, requirement.field)) {
33
+ issues.push({
34
+ code: "V1000",
35
+ path: [...path, requirement.field],
36
+ message: `Field "${requirement.field}" is required to create ${resource}.`,
37
+ });
38
+ }
39
+ continue;
40
+ }
41
+ if (via !== undefined &&
42
+ requirement.inverse?.resource === via.resource &&
43
+ requirement.inverse.relation === via.relation) {
44
+ continue;
45
+ }
46
+ const orFields = Array.isArray(requirement.orFields)
47
+ ? requirement.orFields
48
+ : [];
49
+ if (written(record, requirement.relation) ||
50
+ (orFields.length > 0 && orFields.every((field) => written(record, field)))) {
51
+ continue;
52
+ }
53
+ if (creatableRelation(contract, resource, requirement.relation, scope)) {
54
+ issues.push({
55
+ code: "V1000",
56
+ path: [...path, requirement.relation],
57
+ message: `Relation "${requirement.relation}" is required to create ${resource}${orFields.length > 0 ? `: write it, or ${orFields.map((field) => `"${field}"`).join(" and ")}` : ""}.`,
58
+ });
59
+ continue;
60
+ }
61
+ const missing = orFields.find((field) => !written(record, field) && creatableScalar(contract, resource, field));
62
+ if (missing !== undefined) {
63
+ issues.push({
64
+ code: "V1000",
65
+ path: [...path, missing],
66
+ message: `Field "${missing}" is required to create ${resource}.`,
67
+ });
68
+ }
69
+ }
70
+ walkNestedCreates(context, resource, record, path);
71
+ }
72
+ function walkNestedCreates(context, resource, record, path) {
73
+ if (!isRecord(record)) {
74
+ return;
75
+ }
76
+ const owner = lookupResource(context.contract, resource);
77
+ for (const [key, value] of Object.entries(record)) {
78
+ const field = lookupField(owner, key);
79
+ if (field?.kind !== "relation" || !isRecord(value)) {
80
+ continue;
81
+ }
82
+ const via = { resource, relation: key };
83
+ for (const [directive, operand] of Object.entries(value)) {
84
+ if (!isRelationMutationDirective(directive)) {
85
+ continue;
86
+ }
87
+ const entries = Array.isArray(operand)
88
+ ? operand.map((entry, index) => ({ entry, at: [index] }))
89
+ : [{ entry: operand, at: [] }];
90
+ for (const { entry, at } of entries) {
91
+ const entryPath = [...path, key, directive, ...at];
92
+ for (const site of RELATION_MUTATION_RECORD_SITES[directive]) {
93
+ const siteRecord = site.key === undefined
94
+ ? entry
95
+ : isRecord(entry)
96
+ ? entry[site.key]
97
+ : undefined;
98
+ const sitePath = site.key === undefined ? entryPath : [...entryPath, site.key];
99
+ if (site.stage === "create") {
100
+ checkCreateRecord(context, field.target, siteRecord, sitePath, NESTED_WRITE_SCOPE, via);
101
+ }
102
+ else {
103
+ walkNestedCreates(context, field.target, siteRecord, sitePath);
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ export function missingCreateValues(contract, table, request, args, argumentsPath) {
111
+ if (!isRecord(args)) {
112
+ return [];
113
+ }
114
+ const context = { contract, table, issues: [] };
115
+ const scope = resolveWriteScope(lookupResource(contract, request.resource), request);
116
+ if (request.family === "create" && written(args, "data")) {
117
+ const data = args.data;
118
+ if (Array.isArray(data)) {
119
+ data.forEach((entry, index) => {
120
+ checkCreateRecord(context, request.resource, entry, [...argumentsPath, "data", index], scope, undefined);
121
+ });
122
+ }
123
+ else {
124
+ checkCreateRecord(context, request.resource, data, [...argumentsPath, "data"], scope, undefined);
125
+ }
126
+ }
127
+ if (request.family === "upsert") {
128
+ checkCreateRecord(context, request.resource, args.create, [...argumentsPath, "create"], scope, undefined);
129
+ walkNestedCreates(context, request.resource, args.update, [
130
+ ...argumentsPath,
131
+ "update",
132
+ ]);
133
+ }
134
+ if (request.family === "update") {
135
+ walkNestedCreates(context, request.resource, args.data, [
136
+ ...argumentsPath,
137
+ "data",
138
+ ]);
139
+ }
140
+ return context.issues;
141
+ }
@@ -5,8 +5,8 @@ export declare class Decimal {
5
5
  toString(): string;
6
6
  /**
7
7
  * The decimal string, exactly as given: what `JSON.stringify` writes for a
8
- * Decimal (Q16), so a result a developer serializes by hand carries its
9
- * digits rather than `{}`. The protocol's encoders do not rely on it.
8
+ * Decimal, so a result a developer serializes by hand carries its digits rather
9
+ * than `{}`. The protocol's encoders do not rely on it.
10
10
  */
11
11
  toJSON(): string;
12
12
  }
@@ -1,10 +1,7 @@
1
1
  import { WIRE_SCALAR_PATTERNS } from "../contracts/wire-scalar-format.js";
2
- /** Provider-neutral exact decimal runtime value owned by Aventara. */
3
2
  export class Decimal {
4
3
  #value;
5
4
  constructor(value) {
6
- // `RegExp#test` coerces its argument, so without this guard a number from
7
- // untyped code passed as `"5"` and `toString()` then returned the number.
8
5
  if (typeof value !== "string") {
9
6
  throw new TypeError("Decimal value must be a string.");
10
7
  }
@@ -16,11 +13,6 @@ export class Decimal {
16
13
  toString() {
17
14
  return this.#value;
18
15
  }
19
- /**
20
- * The decimal string, exactly as given: what `JSON.stringify` writes for a
21
- * Decimal (Q16), so a result a developer serializes by hand carries its
22
- * digits rather than `{}`. The protocol's encoders do not rely on it.
23
- */
24
16
  toJSON() {
25
17
  return this.#value;
26
18
  }
@@ -5,12 +5,12 @@ import type { FrameworkOperationRequest } from "./operation-request.js";
5
5
  import type { OperationErrorCode } from "./operation-response.js";
6
6
  /**
7
7
  * One unplanned failure as the framework saw it before sanitization: the raw
8
- * value, the outcome code actually emitted, and enough identity to act on
9
- * it. The code is usually an internal one, because the channel fires for
10
- * unplanned failures; it is the thrower's own code when the framework
11
- * dropped something it could not vouch for from an otherwise honoured
12
- * deliberate outcome (ADR 0008 decision 4), which changes no outcome. Server-side only — a diagnostic is never projected
13
- * onto the wire, which is precisely why it may carry the raw error.
8
+ * value, the outcome code actually emitted, and enough identity to act on it. The
9
+ * code is usually an internal one, because the channel fires for unplanned
10
+ * failures; it is the thrower's own code when the framework dropped something it
11
+ * could not vouch for from an otherwise honoured deliberate outcome, which changes
12
+ * no outcome. Server-side only — a diagnostic is never projected onto the wire,
13
+ * which is precisely why it may carry the raw error.
14
14
  */
15
15
  export type OperationDiagnostic = {
16
16
  readonly kind: "operation";
@@ -1,9 +1,4 @@
1
- /** The default channel: no sink configured, so nothing is observed. */
2
1
  const IGNORE_DIAGNOSTIC = () => undefined;
3
- /**
4
- * A sink may return a promise. Reading `then` off an arbitrary return value
5
- * can itself throw, so this runs inside the reporter's own guard.
6
- */
7
2
  function isThenable(value) {
8
3
  if ((typeof value !== "object" || value === null) &&
9
4
  typeof value !== "function") {
@@ -11,12 +6,6 @@ function isThenable(value) {
11
6
  }
12
7
  return typeof value.then === "function";
13
8
  }
14
- /**
15
- * The channel bound to one diagnostic identity, as the reporters below bind
16
- * it. Exported for `@aventara/core/protocol`, whose binding reports a failure
17
- * only it can meet — a result with no wire form (`A3003`) — under the
18
- * identity it holds; never exported from the main entry.
19
- */
20
9
  export function createReporter(sink, identity) {
21
10
  if (sink === undefined) {
22
11
  return IGNORE_DIAGNOSTIC;
@@ -33,38 +22,15 @@ export function createReporter(sink, identity) {
33
22
  }
34
23
  };
35
24
  }
36
- /**
37
- * The sink each constructed Framework was configured with, keyed by the
38
- * Framework object `createFramework` returned. The protocol's binding
39
- * (`AvProtocol.bind(framework)`, `@aventara/core/protocol`) reads it here, so
40
- * the direction stays one-way — no execution module imports `protocol/` — and
41
- * the public `Framework` type gains no member. A WeakMap: it keeps no
42
- * Framework alive. A Framework built without a sink has no entry.
43
- */
44
25
  const CONFIGURED_SINKS = new WeakMap();
45
- /** Records the sink `framework` was configured with; called once, at construction. */
46
26
  export function recordDiagnosticsSink(framework, sink) {
47
27
  if (sink !== undefined) {
48
28
  CONFIGURED_SINKS.set(framework, sink);
49
29
  }
50
30
  }
51
- /** The sink `framework` was configured with, or `undefined` for none. */
52
31
  export function diagnosticsSinkOf(framework) {
53
32
  return CONFIGURED_SINKS.get(framework);
54
33
  }
55
- /**
56
- * Binds the configured sink to one operation request.
57
- *
58
- * Identity is captured on entry rather than at report time: by the moment a
59
- * failure is reported the request may be exactly what broke, and a diagnostic
60
- * naming no operation is not actionable. A request whose own identity cannot
61
- * be read is past observing, so it silently yields the no-op channel — the
62
- * outermost totality guard still answers with the generic envelope.
63
- *
64
- * The response must be unaffected by observation, so the reporter neither
65
- * awaits the sink nor lets anything the sink does escape: a throw, a rejected
66
- * promise and a booby-trapped thenable all end here.
67
- */
68
34
  export function createDiagnosticsReporter(sink, request, operation) {
69
35
  let identity;
70
36
  try {
@@ -83,7 +49,6 @@ export function createDiagnosticsReporter(sink, request, operation) {
83
49
  }
84
50
  return createReporter(sink, identity);
85
51
  }
86
- /** Binds the configured sink to one transaction request and its plan operations. */
87
52
  export function createTransactionDiagnosticsChannel(sink, request) {
88
53
  let identity;
89
54
  try {
@@ -17,9 +17,9 @@ export declare class AdapterBoundary {
17
17
  originOf(error: unknown): FailureOrigin;
18
18
  }
19
19
  /**
20
- * The one result-serialization text; every A3003 carries it. Its producer is
21
- * the protocol's response encoder (`@aventara/core/protocol`, P1), which
22
- * imports it from here so the code has one text wherever it is emitted.
20
+ * The one result-serialization text; every A3003 carries it. Its producer is the
21
+ * protocol's response encoder, which imports it from here so the code has one text
22
+ * wherever it is emitted.
23
23
  */
24
24
  export declare const SERIALIZATION_FAILURE_MESSAGE = "Result serialization failed.";
25
25
  /** Where a standalone operation's arguments sit in a framework issue path. */
@@ -1,16 +1,10 @@
1
1
  import { VALIDATION_CODE_MESSAGES, VALIDATION_CODES, } from "../operations/validation-code.js";
2
2
  import { FrameworkError } from "./framework-error.js";
3
3
  import { INTERNAL_FAILURE_MESSAGE, internalFailure, OPERATION_CODES, operationFailure, } from "./operation-response.js";
4
- /** The one generic Adapter-failure text; every unexpected A3001 carries it. */
4
+ import { PIPE_OUTPUT_FAILURE_MESSAGE } from "./pipe-output.validator.js";
5
5
  export const ADAPTER_FAILURE_MESSAGE = "Adapter execution failed.";
6
- /**
7
- * Structurally records every value thrown across the Adapter seam so the
8
- * final error boundary can classify origin by identity, never by inspecting
9
- * error text. One boundary instance belongs to one operation execution.
10
- */
11
6
  export class AdapterBoundary {
12
7
  #thrown = new Set();
13
- /** Runs Adapter work, remembering anything it throws before re-throwing it. */
14
8
  async execute(work) {
15
9
  try {
16
10
  return await work();
@@ -24,29 +18,17 @@ export class AdapterBoundary {
24
18
  return this.#thrown.has(error) ? "adapter" : "runtime";
25
19
  }
26
20
  }
27
- /**
28
- * The one result-serialization text; every A3003 carries it. Its producer is
29
- * the protocol's response encoder (`@aventara/core/protocol`, P1), which
30
- * imports it from here so the code has one text wherever it is emitted.
31
- */
32
21
  export const SERIALIZATION_FAILURE_MESSAGE = "Result serialization failed.";
33
- /**
34
- * The framework's own text for each internal-failure code. An A3xxx cause is
35
- * never caller-actionable, so whatever text its thrower supplied is replaced
36
- * by this table — the realistic thrower is a careless Adapter forwarding a
37
- * provider message, and spec 13.5 forbids that text on the wire.
38
- */
39
22
  const INTERNAL_FAILURE_MESSAGES = {
40
23
  A3000: INTERNAL_FAILURE_MESSAGE,
41
24
  A3001: ADAPTER_FAILURE_MESSAGE,
42
25
  A3002: "Transaction failed.",
43
26
  A3003: SERIALIZATION_FAILURE_MESSAGE,
27
+ A3004: PIPE_OUTPUT_FAILURE_MESSAGE,
44
28
  };
45
- /** The internal code an unexpected failure of one origin is emitted under. */
46
29
  function unexpectedCode(origin) {
47
30
  return origin === "adapter" ? "A3001" : "A3000";
48
31
  }
49
- /** The generic, text-free envelope for an unexpected failure of one origin. */
50
32
  function unexpectedFailure(origin) {
51
33
  return origin === "adapter"
52
34
  ? operationFailure("A3001", { message: ADAPTER_FAILURE_MESSAGE })
@@ -58,10 +40,6 @@ function isInternalCode(code) {
58
40
  function isPathSegment(value) {
59
41
  return typeof value === "string" || typeof value === "number";
60
42
  }
61
- /**
62
- * Whether an untrusted value is a list. `Array.isArray` is not itself total:
63
- * it throws on a revoked Proxy, and a thrown value may be one.
64
- */
65
43
  function isList(value) {
66
44
  try {
67
45
  return Array.isArray(value);
@@ -70,15 +48,6 @@ function isList(value) {
70
48
  return false;
71
49
  }
72
50
  }
73
- /**
74
- * Copies an untrusted list into a plain array the framework owns. Traversal,
75
- * not just property reads, is untrusted here: an index accessor, a Proxy
76
- * trap, an overridden `map` and a hijacked `Symbol.species` all run
77
- * thrower-supplied code, and none of it may become the boundary's own
78
- * failure. Elements are read one at a time through {@link readProperty}, so
79
- * an unreadable element is `undefined` — a value every projection below
80
- * already rejects. `undefined` means the value is no list at all.
81
- */
82
51
  function readList(value) {
83
52
  if (!isList(value)) {
84
53
  return undefined;
@@ -93,12 +62,6 @@ function readList(value) {
93
62
  }
94
63
  return items;
95
64
  }
96
- /**
97
- * Projects an issue path onto a plain array of segments the framework owns.
98
- * Copying rather than forwarding is the point: a planted path arrives as a
99
- * live handle to a caller-held array, and its non-index properties would
100
- * otherwise ride into the envelope for every in-memory consumer of it.
101
- */
102
65
  function projectPath(value) {
103
66
  const items = readList(value);
104
67
  if (items === undefined) {
@@ -113,31 +76,13 @@ function projectPath(value) {
113
76
  }
114
77
  return path;
115
78
  }
116
- /** Where a standalone operation's arguments sit in a framework issue path. */
117
79
  export const OPERATION_ARGUMENTS_PATH = [
118
80
  "arguments",
119
81
  ];
120
- /** Where one plan operation's arguments sit in a framework issue path. */
121
82
  export function planOperationArgumentsPath(index) {
122
83
  return ["operations", index, "args"];
123
84
  }
124
- /** A segment that named no own, data-backed member of its container. */
125
85
  const UNRESOLVED = Symbol("unresolved-path-segment");
126
- /**
127
- * Reads one own, data-backed member of an untrusted container.
128
- *
129
- * Own only: satisfying a segment from the prototype chain would let `toString`
130
- * or `constructor` pose as caller data. Data-backed only: the descriptor is
131
- * consulted and only its `value` used, so a getter never runs — running a
132
- * caller's accessor while sanitizing an error is a side effect in the worst
133
- * possible place.
134
- *
135
- * The reflection itself can throw: a revoked Proxy rejects
136
- * `getOwnPropertyDescriptor`. That is not caught here. The whole walk has one
137
- * containment point, in {@link resolvesAgainstArguments}, so there is exactly
138
- * one place where an unwalkable path and an unresolvable one become the same
139
- * answer.
140
- */
141
86
  function readOwnMember(container, key) {
142
87
  if (typeof container !== "object" || container === null) {
143
88
  return UNRESOLVED;
@@ -147,7 +92,6 @@ function readOwnMember(container, key) {
147
92
  ? descriptor.value
148
93
  : UNRESOLVED;
149
94
  }
150
- /** Resolves one path segment; a numeric segment indexes an array and nothing else. */
151
95
  function readSegment(container, segment) {
152
96
  if (typeof segment === "number") {
153
97
  return Number.isInteger(segment) && segment >= 0 && isList(container)
@@ -156,22 +100,6 @@ function readSegment(container, segment) {
156
100
  }
157
101
  return readOwnMember(container, segment);
158
102
  }
159
- /**
160
- * Whether a thrower-supplied path denotes a member of the caller's own
161
- * arguments — the one thing that makes it safe to emit.
162
- *
163
- * A path that resolves echoes back a key sequence the caller already holds;
164
- * anything else is unvouched content on the same channel spec 13.5 closes for
165
- * the rest of `cause` (ADR 0008 decision 4, applied to the third axis). The
166
- * prefix must be the framework's own address for these arguments, and every
167
- * remaining segment must resolve: a resolving prefix does not license the
168
- * rest, so a partial match is no match.
169
- *
170
- * Total, like every step the boundary takes on a thrown value. The walk runs
171
- * over data the framework does not own — a revoked Proxy on it throws from
172
- * reflection alone — so the whole walk sits inside one guard: an unresolvable
173
- * path and an unwalkable one are the same answer.
174
- */
175
103
  function resolvesAgainstArguments(path, view) {
176
104
  try {
177
105
  if (path.length < view.path.length) {
@@ -199,41 +127,16 @@ function resolvesAgainstArguments(path, view) {
199
127
  return false;
200
128
  }
201
129
  }
202
- /**
203
- * Only an allocated Validation code (spec 13.3, amended by ADR 0006 DA-4) may
204
- * be emitted. This is ADR 0005's rule on the axis it did not cover: the union
205
- * is a compile-time promise a separately compiled Adapter or a developer
206
- * filter never made, so the set is tested by identity before emission.
207
- */
208
130
  function isValidationCode(value) {
209
131
  return (typeof value === "string" &&
210
132
  VALIDATION_CODES.includes(value));
211
133
  }
212
- /**
213
- * Projects one issue onto the spec ValidationIssue shape; unknown keys drop.
214
- *
215
- * All three fields are framework-owned by the time they are emitted.
216
- * An unrecognised `code` drops the whole issue rather than riding onto the
217
- * wire in a field the spec defines as a closed set — and rather than being
218
- * rewritten to a plausible neighbour, which would misreport what was wrong.
219
- * The `message` is replaced by the framework's own published text for that
220
- * code, because a thrower-supplied message is the same provider-text channel
221
- * spec 13.5 closes for the rest of `cause`. The `path` is emitted only when
222
- * every segment of it resolves against the caller's own arguments; it is an
223
- * open-ended string channel otherwise, and spec 13.1 makes the field
224
- * optional, so a path that cannot be vouched for is simply absent — the
225
- * issue, its code and the outcome are unchanged. Each dropped value is
226
- * appended to `unvouched`, so the boundary can hand it to the ADR 0004
227
- * diagnostics channel — the only place it still exists.
228
- */
229
134
  function projectIssue(value, unvouched, view) {
230
135
  if (typeof value !== "object" || value === null) {
231
136
  return undefined;
232
137
  }
233
138
  const code = readProperty(value, "code");
234
139
  if (!isValidationCode(code)) {
235
- // `undefined` is an unreadable or absent code, not a supplied one:
236
- // reporting it would be noise, and the issue is malformed either way.
237
140
  if (code !== undefined) {
238
141
  unvouched.push(code);
239
142
  }
@@ -255,18 +158,6 @@ function projectIssue(value, unvouched, view) {
255
158
  }
256
159
  return { path, code, message: owned };
257
160
  }
258
- /**
259
- * Projects a thrower-supplied cause onto the spec 13 `Cause` shape:
260
- * `{ message, issues?, operation? }`. Unknown keys are dropped rather than
261
- * forwarded, so a provider error assigned wholesale as a cause loses its
262
- * `sql`, `stack`, DSN and whatever else it carried. A cause without a string
263
- * message is no cause at all.
264
- *
265
- * Total, like every step the boundary takes on a thrown value: a cause it
266
- * cannot traverse yields the parts it could read, never an exception. A
267
- * throw here would escape the boundary that was sanitizing it and cost the
268
- * thrower the very outcome it deliberately named.
269
- */
270
161
  function projectCause(value, unvouched, view) {
271
162
  const message = readProperty(value, "message");
272
163
  if (typeof message !== "string") {
@@ -279,29 +170,16 @@ function projectCause(value, unvouched, view) {
279
170
  .filter((issue) => issue !== undefined);
280
171
  return {
281
172
  message,
282
- // An empty list is no list: dropping every issue leaves the cause
283
- // without issues rather than fabricating one to fill the field.
284
173
  ...(issues !== undefined && issues.length > 0 ? { issues } : {}),
285
174
  ...(Number.isInteger(rawOperation) && rawOperation >= 0
286
175
  ? { operation: rawOperation }
287
176
  : {}),
288
177
  };
289
178
  }
290
- /**
291
- * Internal failures carry no caller-actionable diagnostics: the message is
292
- * the framework's own and `issues` raised while validating computed/Adapter
293
- * output describe server internals and stay behind the boundary (available
294
- * to filters, never emitted).
295
- */
296
179
  function internalCause(code, cause) {
297
180
  const { issues: _issues, message: _message, ...emitted } = cause;
298
181
  return { ...emitted, message: INTERNAL_FAILURE_MESSAGES[code] };
299
182
  }
300
- /**
301
- * Reads one property of a thrown value. Thrown values are untrusted: a
302
- * getter or Proxy trap may itself throw, and that must never become the
303
- * boundary's own failure.
304
- */
305
183
  function readProperty(value, key) {
306
184
  try {
307
185
  return value[key];
@@ -318,34 +196,16 @@ function isFrameworkErrorInstance(error) {
318
196
  return false;
319
197
  }
320
198
  }
321
- /**
322
- * A {@link FrameworkError} that fails vetting. Its thrower deliberately named
323
- * an outcome the framework cannot honour, so the failure is the broken
324
- * outcome contract itself — an internal error, whatever the origin — and the
325
- * offending value is replaced, never echoed.
326
- */
327
199
  const MALFORMED_OUTCOME = {
328
200
  code: "A3000",
329
201
  cause: { message: INTERNAL_FAILURE_MESSAGE },
330
202
  unvouched: [],
331
203
  };
332
- /**
333
- * Only a recognised error code (spec 13.2) is a deliberate outcome. An
334
- * unknown code, or a success code arriving on the failure path, would break
335
- * the ADR 0003 union on the wire.
336
- */
337
204
  function isOperationErrorCode(value) {
338
205
  return (typeof value === "string" &&
339
206
  OPERATION_CODES.includes(value) &&
340
207
  !value.startsWith("A1"));
341
208
  }
342
- /**
343
- * Vets a value as a deliberate {@link FrameworkError}. `undefined` means it
344
- * is not one at all. Passing the `instanceof` check is not enough:
345
- * `Object.create(FrameworkError.prototype)`, a booby-trapped accessor, an
346
- * unrecognised code or a cause without a message all reach here, and each
347
- * is {@link MALFORMED_OUTCOME} rather than something to dereference or emit.
348
- */
349
209
  function vetDeliberateOutcome(error, view) {
350
210
  if (!isFrameworkErrorInstance(error)) {
351
211
  return undefined;
@@ -361,31 +221,16 @@ function vetDeliberateOutcome(error, view) {
361
221
  const cause = projectCause(rawCause, unvouched, view);
362
222
  return cause === undefined ? MALFORMED_OUTCOME : { code, cause, unvouched };
363
223
  }
364
- /**
365
- * Hands every dropped issue code and issue path to the ADR 0004 channel under
366
- * the outcome actually emitted. Dropping something the framework cannot vouch
367
- * for is no grounds to escalate the thrower's chosen outcome, so the code
368
- * stands and the channel carries the whole consequence. Reporting cannot
369
- * affect the response: the reporter is total and swallows whatever the sink
370
- * does.
371
- */
372
224
  function reportUnvouched(outcome, report) {
373
225
  for (const value of outcome.unvouched) {
374
226
  report(value, outcome.code);
375
227
  }
376
228
  }
377
- /** A deliberate outcome: the stable code and cause its thrower chose. */
378
229
  function deliberateFailure(outcome) {
379
230
  return operationFailure(outcome.code, isInternalCode(outcome.code)
380
231
  ? internalCause(outcome.code, outcome.cause)
381
232
  : outcome.cause);
382
233
  }
383
- /**
384
- * Final framework sanitization, shared by every boundary. A deliberate
385
- * {@link FrameworkError} carries its own stable outcome; anything else is
386
- * replaced by a generic cause so no provider text, stack, path, or secret
387
- * can cross the response boundary. Total: it never throws.
388
- */
389
234
  export function sanitizeOperationFailure(error, origin, report, view) {
390
235
  const outcome = vetDeliberateOutcome(error, view);
391
236
  if (outcome === undefined) {
@@ -398,13 +243,6 @@ export function sanitizeOperationFailure(error, origin, report, view) {
398
243
  reportUnvouched(outcome, report);
399
244
  return deliberateFailure(outcome);
400
245
  }
401
- /**
402
- * Post-validation exception flow (spec 17.3): effective filters are stored as
403
- * `[root..., scoped...]`, so reverse iteration tries scoped filters in reverse
404
- * registration order, then root filters in reverse, then final sanitization.
405
- * A filter handles an error only by returning a {@link FrameworkError};
406
- * returning `undefined` or throwing leaves the original error unhandled.
407
- */
408
246
  export async function resolveOperationFailure(error, filters, context, boundary, report, view) {
409
247
  for (let index = filters.length - 1; index >= 0; index -= 1) {
410
248
  const filter = filters[index];
@@ -413,9 +251,6 @@ export async function resolveOperationFailure(error, filters, context, boundary,
413
251
  }
414
252
  const handled = await attemptFilter(filter, error, context, view);
415
253
  if (handled !== undefined) {
416
- // A filter that names an outcome the framework cannot honour has
417
- // swallowed the original error on the developer's behalf; the
418
- // diagnostics channel is the only place it still exists.
419
254
  if (handled === MALFORMED_OUTCOME) {
420
255
  report(error, "A3000");
421
256
  }