@aventara/core 0.1.0-pilot.1 → 0.1.0-pilot.3

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 (139) hide show
  1. package/dist/adapters/adapter-field.d.ts +4 -4
  2. package/dist/adapters/adapter-resource.d.ts +10 -12
  3. package/dist/adapters/adapter.interface.d.ts +20 -10
  4. package/dist/compiler/contract-compiler-diagnostic.d.ts +3 -4
  5. package/dist/compiler/contract.compiler.d.ts +5 -4
  6. package/dist/compiler/contract.compiler.js +0 -132
  7. package/dist/config/config.resolver.d.ts +2 -2
  8. package/dist/config/config.resolver.js +0 -5
  9. package/dist/config/config.validator.d.ts +3 -3
  10. package/dist/config/config.validator.js +0 -8
  11. package/dist/config/define-fields.d.ts +2 -7
  12. package/dist/config/define-fields.js +0 -15
  13. package/dist/config/effective-fields.d.ts +4 -8
  14. package/dist/config/entrypoint.normalizer.d.ts +7 -7
  15. package/dist/config/entrypoint.normalizer.js +0 -22
  16. package/dist/config/framework-behaviors-config.d.ts +6 -6
  17. package/dist/config/framework-config.d.ts +3 -5
  18. package/dist/config/framework-restrictions-config.d.ts +19 -29
  19. package/dist/config/pipeline-config.d.ts +8 -18
  20. package/dist/contracts/capability.d.ts +18 -21
  21. package/dist/contracts/capability.js +0 -75
  22. package/dist/contracts/client-contract-structure.validator.d.ts +15 -22
  23. package/dist/contracts/client-contract-structure.validator.js +0 -27
  24. package/dist/contracts/contract-canonicalizer.js +0 -96
  25. package/dist/contracts/contract-hash.js +0 -2
  26. package/dist/contracts/contract-limits.js +0 -1
  27. package/dist/contracts/contract-lookup.js +0 -10
  28. package/dist/contracts/contract-protocol.js +0 -10
  29. package/dist/contracts/field.d.ts +23 -26
  30. package/dist/contracts/field.js +0 -107
  31. package/dist/contracts/lifecycle-trait.js +0 -5
  32. package/dist/contracts/resource-operations.d.ts +17 -18
  33. package/dist/contracts/resource-operations.js +0 -63
  34. package/dist/contracts/scalar-value-type.d.ts +12 -22
  35. package/dist/contracts/scalar.js +0 -1
  36. package/dist/contracts/wire-scalar-format.d.ts +20 -20
  37. package/dist/contracts/wire-scalar-format.js +0 -26
  38. package/dist/framework/contract-view.d.ts +7 -8
  39. package/dist/framework/framework-construction.error.js +0 -1
  40. package/dist/framework/framework.d.ts +26 -27
  41. package/dist/framework/framework.factory.js +0 -49
  42. package/dist/framework/operation-facade.js +0 -17
  43. package/dist/operations/operation-arguments.d.ts +36 -46
  44. package/dist/operations/operation-boolean-nodes.js +0 -16
  45. package/dist/operations/operation-call.d.ts +4 -9
  46. package/dist/operations/operation-identity.js +0 -1
  47. package/dist/operations/operation-nesting.js +0 -5
  48. package/dist/operations/operation-result.d.ts +4 -4
  49. package/dist/operations/operation-validator-core.d.ts +2 -2
  50. package/dist/operations/operation-validator-core.js +6 -71
  51. package/dist/operations/operation-validator.d.ts +4 -4
  52. package/dist/operations/operation-validator.js +0 -12
  53. package/dist/operations/projection-scope.resolver.d.ts +8 -10
  54. package/dist/operations/projection-scope.resolver.js +0 -35
  55. package/dist/operations/validation-code.d.ts +11 -18
  56. package/dist/operations/validation-code.js +0 -24
  57. package/dist/operations/write-scope.resolver.d.ts +4 -7
  58. package/dist/operations/write-scope.resolver.js +0 -37
  59. package/dist/protocol/absent-route.responder.d.ts +33 -48
  60. package/dist/protocol/absent-route.responder.js +0 -55
  61. package/dist/protocol/av-protocol.d.ts +6 -6
  62. package/dist/protocol/av-protocol.js +0 -10
  63. package/dist/protocol/contract-document.responder.d.ts +15 -18
  64. package/dist/protocol/contract-document.responder.js +0 -33
  65. package/dist/protocol/http-request.decoder.d.ts +55 -61
  66. package/dist/protocol/http-request.decoder.js +0 -62
  67. package/dist/protocol/http-request.reader.d.ts +12 -13
  68. package/dist/protocol/http-request.reader.js +0 -45
  69. package/dist/protocol/http-response.encoder.d.ts +17 -18
  70. package/dist/protocol/http-response.encoder.js +0 -59
  71. package/dist/protocol/http-status.table.d.ts +7 -11
  72. package/dist/protocol/http-status.table.js +1 -15
  73. package/dist/protocol/index.d.ts +5 -5
  74. package/dist/protocol/index.js +0 -9
  75. package/dist/protocol/operation-response.guard.d.ts +12 -19
  76. package/dist/protocol/operation-response.guard.js +0 -29
  77. package/dist/protocol/protocol-failure.d.ts +21 -26
  78. package/dist/protocol/protocol-failure.js +0 -12
  79. package/dist/protocol/protocol-headers.d.ts +25 -30
  80. package/dist/protocol/protocol-headers.js +0 -66
  81. package/dist/protocol/protocol-surface.d.ts +25 -29
  82. package/dist/protocol/protocol-surface.js +0 -19
  83. package/dist/protocol/protocol.binder.d.ts +58 -71
  84. package/dist/protocol/protocol.binder.js +0 -8
  85. package/dist/protocol/protocol.handler.d.ts +28 -32
  86. package/dist/protocol/protocol.handler.js +0 -43
  87. package/dist/runtime/computed-read-runtime.js +0 -29
  88. package/dist/runtime/create-requirement.d.ts +65 -0
  89. package/dist/runtime/create-requirement.js +141 -0
  90. package/dist/runtime/decimal.d.ts +2 -2
  91. package/dist/runtime/decimal.js +0 -8
  92. package/dist/runtime/diagnostics-reporter.d.ts +6 -6
  93. package/dist/runtime/diagnostics-reporter.js +0 -35
  94. package/dist/runtime/error-boundary.d.ts +3 -3
  95. package/dist/runtime/error-boundary.js +2 -167
  96. package/dist/runtime/execution-options.d.ts +4 -4
  97. package/dist/runtime/field-behavior.d.ts +6 -9
  98. package/dist/runtime/framework-error.js +0 -4
  99. package/dist/runtime/mutation-planner.d.ts +5 -6
  100. package/dist/runtime/mutation-planner.js +0 -23
  101. package/dist/runtime/operation-response.d.ts +1 -1
  102. package/dist/runtime/operation-response.js +1 -17
  103. package/dist/runtime/operation-runtime.d.ts +24 -22
  104. package/dist/runtime/operation-runtime.js +18 -52
  105. package/dist/runtime/outcome-mapper.js +0 -22
  106. package/dist/runtime/output-validator.js +0 -1
  107. package/dist/runtime/pipe-output.validator.d.ts +48 -0
  108. package/dist/runtime/pipe-output.validator.js +79 -0
  109. package/dist/runtime/pipeline.d.ts +20 -26
  110. package/dist/runtime/referential-action.d.ts +14 -19
  111. package/dist/runtime/referential-action.js +0 -23
  112. package/dist/runtime/request-id.js +0 -21
  113. package/dist/runtime/scalar-runtime.d.ts +14 -16
  114. package/dist/runtime/scalar-runtime.js +12 -57
  115. package/dist/transactions/deferred-operation.d.ts +9 -14
  116. package/dist/transactions/operation-arguments-encoder.d.ts +0 -1
  117. package/dist/transactions/operation-arguments-encoder.js +0 -14
  118. package/dist/transactions/operation-fingerprint.js +0 -1
  119. package/dist/transactions/transaction-envelope.js +0 -14
  120. package/dist/transactions/transaction-integrity-error.d.ts +10 -11
  121. package/dist/transactions/transaction-integrity-error.js +0 -5
  122. package/dist/transactions/transaction-plan-assembler.d.ts +0 -1
  123. package/dist/transactions/transaction-plan-assembler.js +0 -35
  124. package/dist/transactions/transaction-plan-validator.d.ts +5 -5
  125. package/dist/transactions/transaction-plan-validator.js +0 -63
  126. package/dist/transactions/transaction-reference-resolver.d.ts +12 -13
  127. package/dist/transactions/transaction-reference-resolver.js +0 -16
  128. package/dist/transactions/transaction-reference-validator.js +0 -8
  129. package/dist/transactions/transaction-reference.d.ts +19 -43
  130. package/dist/transactions/transaction-reference.js +0 -11
  131. package/dist/transactions/transaction-request.d.ts +4 -5
  132. package/dist/transactions/transaction-response.d.ts +1 -1
  133. package/dist/transactions/transaction-runtime.d.ts +2 -3
  134. package/dist/transactions/transaction-runtime.js +5 -44
  135. package/dist/transactions/transaction-slot-path-error.d.ts +10 -11
  136. package/dist/transactions/transaction-slot-path-error.js +0 -7
  137. package/dist/transactions/transaction-slot-writer.d.ts +12 -12
  138. package/dist/transactions/transaction-slot-writer.js +0 -34
  139. package/package.json +2 -2
@@ -6,6 +6,7 @@ import type { ResolvedFrameworkConfig } from "../config/config.resolver.js";
6
6
  import type { Contract } from "../contracts/contract.js";
7
7
  import type { OperationValidationOptions, SemanticOperationArguments } from "../operations/operation-validator.js";
8
8
  import type { FrameworkTransactionRequest } from "../transactions/transaction-request.js";
9
+ import { type CreateRequirementTable } from "./create-requirement.js";
9
10
  import type { DiagnosticsReporter } from "./diagnostics-reporter.js";
10
11
  import type { ClientProtocolIdentity, FrameworkOperationRequest, OperationExecutionRequest } from "./operation-request.js";
11
12
  import type { OperationFailureResponse, OperationResponse } from "./operation-response.js";
@@ -15,12 +16,14 @@ export type OperationRuntimeDependencies<M extends AdapterModel> = {
15
16
  readonly config: ResolvedFrameworkConfig<M>;
16
17
  };
17
18
  /**
18
- * What a validated operation's pipeline needs: everything except the root
19
- * Adapter. Its single Adapter call goes through the executor passed beside
20
- * it (D-706), so inside a transaction plan the root Adapter is unreachable by
21
- * type (C-724).
19
+ * What a validated operation's pipeline needs: everything except the root Adapter.
20
+ * Its single Adapter call goes through the executor passed beside it, so inside a
21
+ * transaction plan the root Adapter is unreachable by type.
22
22
  */
23
- export type OperationPipelineDependencies<M extends AdapterModel> = Omit<OperationRuntimeDependencies<M>, "adapter">;
23
+ export type OperationPipelineDependencies<M extends AdapterModel> = Omit<OperationRuntimeDependencies<M>, "adapter"> & {
24
+ /** The Adapter's create requirements, read once at construction. */
25
+ readonly createRequirements: CreateRequirementTable;
26
+ };
24
27
  /**
25
28
  * The executor core owns no diagnostics channel: the framework factory owns
26
29
  * it and hands in one reporter already bound to this request, so every
@@ -35,11 +38,11 @@ export declare function createOperationRuntime<M extends AdapterModel>(dependenc
35
38
  /** Validates the request-level protocol identity shared by both execution seams. */
36
39
  export declare function validateProtocolIdentity(contracts: FrameworkContracts, request: FrameworkOperationRequest | FrameworkTransactionRequest): OperationFailureResponse | undefined;
37
40
  /**
38
- * The stale-client check (§19.4, cross-phase invariant 15) on one client
39
- * protocol identity: the protocol version first (`A2006`), then the
40
- * ClientContract hash (`A2005`). Exact match only. The one implementation —
41
- * the protocol's decoders and absence answers (`@aventara/core/protocol`) call
42
- * it too, so a stale client hears the same envelope whichever layer answers.
41
+ * The stale-client check on one client protocol identity: the protocol version
42
+ * first (`A2006`), then the ClientContract hash (`A2005`). Exact match only. The
43
+ * one implementation — the protocol's decoders and absence answers
44
+ * (`@aventara/core/protocol`) call it too, so a stale client hears the same
45
+ * envelope whichever layer answers.
43
46
  */
44
47
  export declare function protocolIdentityFailure(contracts: FrameworkContracts, protocol: ClientProtocolIdentity): OperationFailureResponse | undefined;
45
48
  export type ValidatedOperationArguments = {
@@ -53,23 +56,22 @@ export type ValidatedOperationArguments = {
53
56
  export declare function validateOperationRequest(contract: Contract, request: FrameworkOperationRequest, options?: OperationValidationOptions): ValidatedOperationArguments;
54
57
  /**
55
58
  * Everything after the Framework Validation Layer: the developer/runtime path
56
- * wrapped in the post-validation error boundary. Validation failures never
57
- * reach this function, so developer filters never see them.
59
+ * wrapped in the post-validation error boundary. Validation failures never reach
60
+ * this function, so developer filters never see them.
58
61
  *
59
- * `executor` is the operation's single Adapter call site (D-706): standalone
60
- * execution passes the Adapter itself, a transaction plan passes the
61
- * transaction-scoped executor its Adapter callback received. Every call owns
62
- * one AdapterBoundary, so A3001/A3000 origin stays classified per operation.
62
+ * `executor` is the operation's single Adapter call site: standalone execution
63
+ * passes the Adapter itself, a transaction plan passes the transaction-scoped
64
+ * executor its Adapter callback received. Every call owns one AdapterBoundary, so
65
+ * A3001/A3000 origin stays classified per operation.
63
66
  *
64
- * The parameter types keep two values out of reach: `dependencies` carries no
65
- * root Adapter, so the executor is the only Adapter this function can call,
66
- * and `request` carries no `arguments`, so no stage can read the unvalidated
67
- * view instead of `validatedArguments`.
67
+ * The parameter types keep two values out of reach: `dependencies` carries no root
68
+ * Adapter, so the executor is the only Adapter this function can call, and
69
+ * `request` carries no `arguments`, so no stage can read the unvalidated view
70
+ * instead of `validatedArguments`.
68
71
  *
69
72
  * `argumentsPath` is the framework's own address for these arguments —
70
73
  * `["arguments"]` standalone, `["operations", i, "args"]` inside a plan. The
71
74
  * response boundary needs it to decide whether a thrower-supplied issue path
72
- * denotes the caller's own arguments or is unvouched text (ADR 0008
73
- * decision 4).
75
+ * denotes the caller's own arguments or is unvouched text.
74
76
  */
75
77
  export declare function executeValidatedOperation<M extends AdapterModel>(dependencies: OperationPipelineDependencies<M>, request: OperationExecutionRequest, contract: Contract, validatedArguments: unknown, argumentsPath: readonly (string | number)[], executor: AdapterOperationExecutor<M>, report: DiagnosticsReporter): Promise<OperationResponse>;
@@ -1,13 +1,14 @@
1
1
  import { AVENTARA_PROTOCOL_VERSION } from "../contracts/contract-protocol.js";
2
2
  import { validateOperationArguments } from "../operations/operation-validator.js";
3
3
  import { materializeReadOperation, planReadOperation, } from "./computed-read-runtime.js";
4
+ import { missingCreateValues, } from "./create-requirement.js";
4
5
  import { AdapterBoundary, OPERATION_ARGUMENTS_PATH, resolveOperationFailure, sanitizeOperationFailure, } from "./error-boundary.js";
5
6
  import { applyWriteFieldBehaviors, stripVirtualMutationInput, } from "./mutation-planner.js";
6
7
  import { internalFailure, operationFailure } from "./operation-response.js";
7
8
  import { mapOperationOutcome, mapValidationOutcome } from "./outcome-mapper.js";
8
9
  import { validateOperationOutput } from "./output-validator.js";
10
+ import { refusePipeOutput } from "./pipe-output.validator.js";
9
11
  import { normalizeOperationScalarValues } from "./scalar-runtime.js";
10
- /** Selects the Contract of the request's execution scope for both execution seams. */
11
12
  export function activeContract(contracts, request) {
12
13
  return request.scope === "application"
13
14
  ? contracts.application
@@ -35,13 +36,6 @@ function forbidden() {
35
36
  message: "Operation denied by a framework guard.",
36
37
  });
37
38
  }
38
- /**
39
- * Output validation is framework validation (spec 16.5), so its failure is
40
- * framework-owned however it arrives. Reading the result can itself throw -
41
- * an Adapter row may carry an accessor that explodes - and without this
42
- * boundary that throw would leave through the post-validation boundary and be
43
- * offered to developer filters, which could relabel it as any outcome.
44
- */
45
39
  function outputAccepted(contract, request, projection, data, report) {
46
40
  try {
47
41
  return validateOperationOutput(contract, request, projection, data).ok;
@@ -51,8 +45,12 @@ function outputAccepted(contract, request, projection, data, report) {
51
45
  return false;
52
46
  }
53
47
  }
54
- /** Creates the transport-independent operation executor owned by core. */
55
48
  export function createOperationRuntime(dependencies) {
49
+ const pipelineDependencies = {
50
+ contracts: dependencies.contracts,
51
+ config: dependencies.config,
52
+ createRequirements: dependencies.adapter.createRequirements ?? {},
53
+ };
56
54
  return async (request, report) => {
57
55
  let validated;
58
56
  try {
@@ -67,14 +65,9 @@ export function createOperationRuntime(dependencies) {
67
65
  if (!validated.ok) {
68
66
  return validated.response;
69
67
  }
70
- return executeValidatedOperation(dependencies, request, validated.contract, validated.arguments, OPERATION_ARGUMENTS_PATH, dependencies.adapter, report);
68
+ return executeValidatedOperation(pipelineDependencies, request, validated.contract, validated.arguments, OPERATION_ARGUMENTS_PATH, dependencies.adapter, report);
71
69
  };
72
70
  }
73
- /**
74
- * The Framework Validation Layer (spec 16), inside its own boundary: an
75
- * unexpected throw here is sanitized directly and reaches zero developer
76
- * filters, exactly like a validation failure (spec 16.5, 17.3).
77
- */
78
71
  function validateOperation(dependencies, request) {
79
72
  const protocolFailure = validateProtocolIdentity(dependencies.contracts, request);
80
73
  if (protocolFailure !== undefined) {
@@ -87,19 +80,11 @@ function validateOperation(dependencies, request) {
87
80
  }
88
81
  return { ok: true, contract, arguments: validation.arguments };
89
82
  }
90
- /** Validates the request-level protocol identity shared by both execution seams. */
91
83
  export function validateProtocolIdentity(contracts, request) {
92
84
  return request.scope === "client"
93
85
  ? protocolIdentityFailure(contracts, request.protocol)
94
86
  : undefined;
95
87
  }
96
- /**
97
- * The stale-client check (§19.4, cross-phase invariant 15) on one client
98
- * protocol identity: the protocol version first (`A2006`), then the
99
- * ClientContract hash (`A2005`). Exact match only. The one implementation —
100
- * the protocol's decoders and absence answers (`@aventara/core/protocol`) call
101
- * it too, so a stale client hears the same envelope whichever layer answers.
102
- */
103
88
  export function protocolIdentityFailure(contracts, protocol) {
104
89
  if (protocol.version !== AVENTARA_PROTOCOL_VERSION) {
105
90
  return operationFailure("A2006", {
@@ -113,7 +98,6 @@ export function protocolIdentityFailure(contracts, protocol) {
113
98
  }
114
99
  return undefined;
115
100
  }
116
- /** Reusable per-operation Framework Validation Layer argument seam. */
117
101
  export function validateOperationRequest(contract, request, options) {
118
102
  const validation = validateOperationArguments(contract, request, options);
119
103
  if (!validation.ok) {
@@ -130,27 +114,6 @@ export function validateOperationRequest(contract, request, options) {
130
114
  function rejected(response) {
131
115
  return { ok: false, response };
132
116
  }
133
- /**
134
- * Everything after the Framework Validation Layer: the developer/runtime path
135
- * wrapped in the post-validation error boundary. Validation failures never
136
- * reach this function, so developer filters never see them.
137
- *
138
- * `executor` is the operation's single Adapter call site (D-706): standalone
139
- * execution passes the Adapter itself, a transaction plan passes the
140
- * transaction-scoped executor its Adapter callback received. Every call owns
141
- * one AdapterBoundary, so A3001/A3000 origin stays classified per operation.
142
- *
143
- * The parameter types keep two values out of reach: `dependencies` carries no
144
- * root Adapter, so the executor is the only Adapter this function can call,
145
- * and `request` carries no `arguments`, so no stage can read the unvalidated
146
- * view instead of `validatedArguments`.
147
- *
148
- * `argumentsPath` is the framework's own address for these arguments —
149
- * `["arguments"]` standalone, `["operations", i, "args"]` inside a plan. The
150
- * response boundary needs it to decide whether a thrower-supplied issue path
151
- * denotes the caller's own arguments or is unvouched text (ADR 0008
152
- * decision 4).
153
- */
154
117
  export async function executeValidatedOperation(dependencies, request, contract, validatedArguments, argumentsPath, executor, report) {
155
118
  const scopeConfig = activeScopeConfig(dependencies.config, request);
156
119
  const pipelines = scopeConfig.pipelines;
@@ -169,13 +132,22 @@ export async function executeValidatedOperation(dependencies, request, contract,
169
132
  return forbidden();
170
133
  }
171
134
  }
172
- for (const pipe of pipelines.pipes) {
135
+ for (const [index, pipe] of pipelines.pipes.entries()) {
173
136
  const nextArgs = await pipe(args, pipelineContext(contract, request, args));
174
137
  if (nextArgs !== undefined) {
175
138
  args = nextArgs;
176
139
  }
140
+ const refused = refusePipeOutput({ core: dependencies.contracts.core, active: contract }, request, args, pipe, index);
141
+ if (refused !== undefined) {
142
+ report(refused.error, "A3004");
143
+ return refused.response;
144
+ }
177
145
  }
178
146
  args = await applyWriteFieldBehaviors({ core: dependencies.contracts.core, active: contract }, scopeConfig.behaviors, request, args, () => pipelineContext(contract, request, args));
147
+ const missing = missingCreateValues(contract, dependencies.createRequirements, request, args, argumentsPath);
148
+ if (missing.length > 0) {
149
+ return mapValidationOutcome(missing);
150
+ }
179
151
  for (const hook of pipelines.hooks.before) {
180
152
  await hook(pipelineContext(contract, request, args));
181
153
  }
@@ -189,12 +161,6 @@ export async function executeValidatedOperation(dependencies, request, contract,
189
161
  let invoke = async () => {
190
162
  const readPlan = planReadOperation(dependencies.contracts.core, contract, scopeConfig.behaviors, request, args);
191
163
  const adapterArguments = await stripVirtualMutationInput({ core: dependencies.contracts.core, active: contract }, request, readPlan.arguments);
192
- // `family` and `variant` are a correlated pair, so they travel as one
193
- // OperationIdentity value: naming them separately would let any family
194
- // pair with any variant. Only `resource` is cast, and only to `M`'s
195
- // key union - the runtime check standing in for the compiler is
196
- // `validateOperationArguments`, which has already rejected any
197
- // resource name the active Contract does not carry.
198
164
  const identity = request;
199
165
  const adapterData = await adapterBoundary.execute(() => executor.execute({
200
166
  ...identity,
@@ -1,8 +1,4 @@
1
1
  import { internalFailure, operationFailure, operationNoMatch, operationSuccess, } from "./operation-response.js";
2
- /**
3
- * Spec section 13.2 success table, resource-agnostic. The mapped key type
4
- * makes the compiler reject a missing or unknown variant.
5
- */
6
2
  const OUTCOME_TABLE = {
7
3
  "find.first": { success: "A1000", absence: "no-match" },
8
4
  "find.unique": { success: "A1000", absence: "not-found" },
@@ -21,12 +17,6 @@ const OUTCOME_TABLE = {
21
17
  "delete.count": { success: "A1007" },
22
18
  "upsert.unique": { success: "A1008" },
23
19
  };
24
- /**
25
- * `family` and `variant` are correlated on `OperationIdentity`, but the
26
- * template built from them is not: it spans every family/variant pair, of
27
- * which most are not operations. So the lookup is a genuine miss-or-hit and
28
- * says so, rather than asserting a key it cannot prove.
29
- */
30
20
  function outcomeRule(operation) {
31
21
  const table = OUTCOME_TABLE;
32
22
  return table[`${operation.family}.${operation.variant}`];
@@ -43,11 +33,6 @@ function absenceOutcome(absence) {
43
33
  return internalFailure();
44
34
  }
45
35
  }
46
- /**
47
- * Maps one validated operation result to its stable outcome envelope. The
48
- * result must already have passed output validation, so `null` can only
49
- * reach a variant whose cardinality admits it.
50
- */
51
36
  export function mapOperationOutcome(operation, data) {
52
37
  const rule = outcomeRule(operation);
53
38
  if (rule === undefined) {
@@ -57,12 +42,6 @@ export function mapOperationOutcome(operation, data) {
57
42
  ? absenceOutcome(rule.absence)
58
43
  : operationSuccess(data, rule.success);
59
44
  }
60
- /**
61
- * Spec section 13.2 failure table for the Framework Validation Layer: which
62
- * A-code a set of validation issues reports. The most specific rejection
63
- * wins - an unknown Resource before an unavailable operation, and a
64
- * structural-limit breach before an ordinary argument rejection.
65
- */
66
45
  function validationCode(issues) {
67
46
  if (issues.some((issue) => issue.path?.[0] === "resource")) {
68
47
  return "A2001";
@@ -84,7 +63,6 @@ function validationCode(issues) {
84
63
  }
85
64
  return "A2004";
86
65
  }
87
- /** Maps framework validation issues to their stable failure envelope. */
88
66
  export function mapValidationOutcome(issues) {
89
67
  return operationFailure(validationCode(issues), {
90
68
  message: "Operation arguments failed framework validation.",
@@ -168,7 +168,6 @@ function validateProjection(contract, operation, projection, value) {
168
168
  ? validateExactRecord(contract, value, projection, ["data"])
169
169
  : { ok: true };
170
170
  }
171
- /** Validate final operation output against fixed cardinality and executed projection. */
172
171
  export function validateOperationOutput(contract, operation, projection, value) {
173
172
  const cardinality = validateOperationOutputCardinality(operation, value);
174
173
  return cardinality.ok
@@ -0,0 +1,48 @@
1
+ import type { Contract, CoreContract } from "../contracts/contract.js";
2
+ import type { OperationIdentity } from "../operations/operation-identity.js";
3
+ import type { ValidationIssue } from "../operations/validation-code.js";
4
+ import type { OperationFailureResponse } from "./operation-response.js";
5
+ /**
6
+ * The check a pipe's output passes before it can reach the Adapter.
7
+ *
8
+ * The request was validated against the caller's own Contract. A pipe then
9
+ * rewrites it, and may legitimately write what the caller cannot: a field the
10
+ * client Contract hides (a password hash, an owner id taken from the session),
11
+ * a directive the caller's scope does not advertise. So the rewritten arguments
12
+ * are held to the **server's** rules — the Core Contract, plus the fields the
13
+ * active scope itself adds (they are virtual: computed, then stripped before
14
+ * the Adapter call) — in the values' runtime forms (`bigint`, `Date`,
15
+ * `Decimal`, `Uint8Array`), which is what every pipe receives.
16
+ *
17
+ * A refusal is the server's defect, not the caller's: `A3004
18
+ * PIPE_OUTPUT_INVALID`, a 500 that tells the caller no more than any internal
19
+ * failure does — {@link PIPE_OUTPUT_FAILURE_MESSAGE}, and the step in a plan.
20
+ * The pipe, the operation, the first offending field and its V-code go to the
21
+ * diagnostics channel only, as a {@link PipeOutputError}: they name server
22
+ * internals — a pipe's function name, a field the caller's Contract hides.
23
+ */
24
+ /** The one text every `A3004` carries on the wire. */
25
+ export declare const PIPE_OUTPUT_FAILURE_MESSAGE = "A server pipeline produced invalid arguments.";
26
+ /** The error the diagnostics channel receives for a refused pipe output. */
27
+ export declare class PipeOutputError extends Error {
28
+ readonly issues: readonly ValidationIssue[];
29
+ readonly name = "PipeOutputError";
30
+ constructor(message: string, issues: readonly ValidationIssue[]);
31
+ }
32
+ /**
33
+ * `undefined` when the pipe's output passes the server's rules; otherwise the
34
+ * `A3004` failure, and the error the diagnostics channel is told.
35
+ */
36
+ export declare function refusePipeOutput(contracts: {
37
+ readonly core: CoreContract;
38
+ readonly active: Contract;
39
+ }, request: OperationIdentity & {
40
+ readonly resource: string;
41
+ }, args: unknown,
42
+ /** The pipe that produced `args`, named in the message by its function name. */
43
+ pipe: unknown,
44
+ /** Its position in the effective pipe list (root pipes first). */
45
+ index: number): {
46
+ readonly response: OperationFailureResponse;
47
+ readonly error: PipeOutputError;
48
+ } | undefined;
@@ -0,0 +1,79 @@
1
+ import { validateOperationArguments } from "../operations/operation-validator.js";
2
+ import { operationFailure } from "./operation-response.js";
3
+ import { normalizeOperationScalarValues } from "./scalar-runtime.js";
4
+ export const PIPE_OUTPUT_FAILURE_MESSAGE = "A server pipeline produced invalid arguments.";
5
+ function serverContract(core, active) {
6
+ const resources = {};
7
+ for (const [name, resource] of Object.entries(core.resources)) {
8
+ const scoped = Object.hasOwn(active.resources, name)
9
+ ? active.resources[name]
10
+ : undefined;
11
+ const added = scoped === undefined
12
+ ? {}
13
+ : Object.fromEntries(Object.entries(scoped.fields).filter(([field]) => !Object.hasOwn(resource.fields, field)));
14
+ resources[name] =
15
+ Object.keys(added).length === 0
16
+ ? resource
17
+ : { ...resource, fields: { ...resource.fields, ...added } };
18
+ }
19
+ return { ...core, resources };
20
+ }
21
+ const SERVER_VIEWS = new WeakMap();
22
+ function serverContractFor(core, active) {
23
+ let view = SERVER_VIEWS.get(active);
24
+ if (view === undefined) {
25
+ view = serverContract(core, active);
26
+ SERVER_VIEWS.set(active, view);
27
+ }
28
+ return view;
29
+ }
30
+ function serverIssues(contract, request, args) {
31
+ const validation = validateOperationArguments(contract, {
32
+ ...request,
33
+ arguments: args,
34
+ });
35
+ if (!validation.ok) {
36
+ return validation.issues;
37
+ }
38
+ const normalization = normalizeOperationScalarValues(contract, { ...request, arguments: validation.arguments }, "application");
39
+ return normalization.ok ? [] : normalization.issues;
40
+ }
41
+ function pipeLabel(pipe, index) {
42
+ let name;
43
+ try {
44
+ name = pipe.name;
45
+ }
46
+ catch {
47
+ name = undefined;
48
+ }
49
+ return typeof name === "string" && name !== ""
50
+ ? `pipes[${index}] "${name}"`
51
+ : `pipes[${index}] (anonymous)`;
52
+ }
53
+ function fieldOf(issue) {
54
+ const path = issue.path ?? [];
55
+ const relative = path[0] === "arguments" ? path.slice(1) : path;
56
+ return relative.length === 0 ? "(arguments)" : relative.join(".");
57
+ }
58
+ export class PipeOutputError extends Error {
59
+ issues;
60
+ name = "PipeOutputError";
61
+ constructor(message, issues) {
62
+ super(message);
63
+ this.issues = issues;
64
+ }
65
+ }
66
+ export function refusePipeOutput(contracts, request, args, pipe, index) {
67
+ const issues = serverIssues(serverContractFor(contracts.core, contracts.active), request, args);
68
+ const [first] = issues;
69
+ if (first === undefined) {
70
+ return undefined;
71
+ }
72
+ const message = `Pipe ${pipeLabel(pipe, index)} returned arguments for ${request.resource}.${request.family}.${request.variant} that the server contract refuses at "${fieldOf(first)}" (${first.code}: ${first.message})`;
73
+ return {
74
+ response: operationFailure("A3004", {
75
+ message: PIPE_OUTPUT_FAILURE_MESSAGE,
76
+ }),
77
+ error: new PipeOutputError(message, issues),
78
+ };
79
+ }
@@ -5,16 +5,13 @@ import type { Contract } from "../contracts/contract.js";
5
5
  import type { OperationFamily } from "../operations/operation-identity.js";
6
6
  import type { FrameworkError } from "./framework-error.js";
7
7
  /**
8
- * What a pipeline is told about the HTTP request a client-origin operation
9
- * arrived on — a **deliberate exception** to core's transport-agnosticism
10
- * (F-609; the architect, Phase 10 Q7; ADR 0010). Specification §17.2 states
11
- * this shape, and developers may read it in their pipelines: the verb, always
12
- * `POST` on a framework route, and the request's headers.
8
+ * What a pipeline is told about the HTTP request a client-origin operation arrived
9
+ * on — a **deliberate exception** to core's transport-agnosticism.
13
10
  *
14
- * Core never parses HTTP to fill it. `@aventara/core/protocol`'s decoders do,
15
- * with header names **lower-cased** and a repeated header's values joined with
16
- * `", "` (RFC 9110 §5.3), so a pipeline reads one spelling. An application-scope
17
- * request carries none (`ClientOperationRequest.transport` is optional).
11
+ * Core never parses HTTP to fill it. `@aventara/core/protocol`'s decoders do, with
12
+ * header names **lower-cased** and a repeated header's values joined with `", "`
13
+ * (RFC 9110 §5.3), so a pipeline reads one spelling. An application-scope request
14
+ * carries none (`ClientOperationRequest.transport` is optional).
18
15
  */
19
16
  export type PipelineTransportContext = {
20
17
  readonly method: "POST";
@@ -23,27 +20,24 @@ export type PipelineTransportContext = {
23
20
  /**
24
21
  * What a pipeline callback is told about the operation it is wrapping.
25
22
  *
26
- * `resource` reads its key set from the **AdapterModel**, not from a
27
- * `Contract`: `Contract["resources"]` is an open `Record<string, …>`, so the
28
- * `C extends Contract` parameter this type used to take could never name the
29
- * Resources an author actually has — it erased to `string`. `M` can name
30
- * them, and configuration cannot add Resources, so `M` is the whole answer.
23
+ * `resource` reads its key set from the **AdapterModel**, not from a `Contract`:
24
+ * `Contract["resources"]` is an open `Record<string, …>`, so the `C extends
25
+ * Contract` parameter this type used to take could never name the Resources an
26
+ * author actually has — it erased to `string`. `M` can name them, and
27
+ * configuration cannot add Resources, so `M` is the whole answer.
31
28
  *
32
- * **No `Cfg`, unlike B8 as written.** `Cfg` narrows *field-key* positions,
33
- * and this type has none: `contract` is passive compiled data, `args` is
34
- * `unknown` until Phase 9 (F-701), and `resource` comes from `M` alone. A
35
- * `Cfg` parameter here would be referenced nowhere — `biome` flags it
29
+ * A `Cfg` parameter here would be referenced nowhere — `biome` flags it
36
30
  * (`lint/correctness/noUnusedVariables`) and it would encode no semantic
37
- * dependency. `FrameworkScopeConfig` already has the precedent one line
38
- * above `pipelines`: `fields` takes `M` only.
31
+ * dependency. `FrameworkScopeConfig` already has the precedent one line above
32
+ * `pipelines`: `fields` takes `M` only.
39
33
  *
40
- * `contract` stays the erased {@link Contract} deliberately: a compiled
41
- * Contract is passive semantic data whose result "stays untyped by design"
42
- * (ADR 0009). Threading configuration into it would rebuild the
43
- * authoring-config-to-compiled-contract bridge this work bypasses.
34
+ * `contract` stays the erased {@link Contract} deliberately: a compiled Contract
35
+ * is passive semantic data whose result "stays untyped by design". Threading
36
+ * configuration into it would rebuild the authoring-config-to-compiled-contract
37
+ * bridge this work bypasses.
44
38
  *
45
- * Under the default `M` the key set degenerates to `string`, so an
46
- * unannotated `PipelineContext` is what it was before (B4).
39
+ * Under the default `M` the key set degenerates to `string`, so an unannotated
40
+ * `PipelineContext` is what it was before.
47
41
  */
48
42
  export type PipelineContext<M extends AdapterModel = AdapterModel> = {
49
43
  readonly contract: Contract;
@@ -1,35 +1,30 @@
1
1
  /**
2
2
  * Core's referential-action vocabulary, and the transitive cascade closure the
3
- * transaction planner reads (F-806, Phase 9 S10).
3
+ * transaction planner reads.
4
4
  *
5
5
  * The facts are an Adapter's: which relation edges exist, and what deleting a
6
- * referenced row does to the rows that reference it. The reasoning over them is
7
- * core's — the architect rejected keeping it below the seam on 2026-10-02,
8
- * because every Adapter would then re-implement it. So the words here are
6
+ * referenced row does to the rows that reference it. So the words here are
9
7
  * Aventara's, stated as the effect on the referencing rows, and an Adapter
10
8
  * translates its own provider's vocabulary into them.
11
9
  *
12
- * None of this enters a Contract. It is not part of the semantic model a
13
- * Contract is compiled from, so it moves no Contract value and no hash; it is a
14
- * planning fact, read once when the Framework is constructed.
10
+ * None of this enters a Contract. It is not part of the semantic model a Contract
11
+ * is compiled from, so it moves no Contract value and no hash; it is a planning
12
+ * fact, read once when the Framework is constructed.
15
13
  */
16
14
  /**
17
- * What deleting a referenced row does to the rows that reference it. The five
18
- * words are the architect's (binding, 2026-10-04), and mirror SQL's
19
- * referential actions one to one, so no adapter has to choose between two of
20
- * them:
15
+ * What deleting a referenced row does to the rows that reference it.
21
16
  *
22
- * - `cascade` — they are deleted with it, and their own referencing rows in
23
- * turn (SQL `ON DELETE CASCADE`).
17
+ * - `cascade` — they are deleted with it, and their own referencing rows in turn
18
+ * (SQL `ON DELETE CASCADE`).
24
19
  * - `restrict` — the delete is refused while they reference it (`RESTRICT`).
25
20
  * - `noAction` — the delete is refused while they reference it (`NO ACTION`).
26
- * Measured identical to `restrict` on both pilot engines (Phase 9 S0, U4):
27
- * neither declares the constraint `DEFERRABLE`, so the end-of-statement check
28
- * answers one delete exactly as `RESTRICT` does. It stays a distinct word
29
- * because it is a distinct schema fact.
21
+ * Measured identical to `restrict` on both pilot engines: neither declares the
22
+ * constraint `DEFERRABLE`, so the end-of-statement check answers one delete
23
+ * exactly as `RESTRICT` does. It stays a distinct word because it is a distinct
24
+ * schema fact.
30
25
  * - `setNull` — they are kept, the reference set to null (`SET NULL`).
31
- * - `setDefault` — they are kept, the reference set to its column default
32
- * (`SET DEFAULT`).
26
+ * - `setDefault` — they are kept, the reference set to its column default (`SET
27
+ * DEFAULT`).
33
28
  *
34
29
  * Only `cascade` takes rows away, so only `cascade` is followed by the closure.
35
30
  */
@@ -1,26 +1,5 @@
1
- /**
2
- * Core's referential-action vocabulary, and the transitive cascade closure the
3
- * transaction planner reads (F-806, Phase 9 S10).
4
- *
5
- * The facts are an Adapter's: which relation edges exist, and what deleting a
6
- * referenced row does to the rows that reference it. The reasoning over them is
7
- * core's — the architect rejected keeping it below the seam on 2026-10-02,
8
- * because every Adapter would then re-implement it. So the words here are
9
- * Aventara's, stated as the effect on the referencing rows, and an Adapter
10
- * translates its own provider's vocabulary into them.
11
- *
12
- * None of this enters a Contract. It is not part of the semantic model a
13
- * Contract is compiled from, so it moves no Contract value and no hash; it is a
14
- * planning fact, read once when the Framework is constructed.
15
- */
16
1
  const NO_CASCADE = new Set();
17
- /**
18
- * Builds the closure over an Adapter's table, once. An absent table is the
19
- * answer of an Adapter that cannot state its referential actions; it is
20
- * believed, so nothing cascades.
21
- */
22
2
  export function resolveCascadeClosure(table) {
23
- // target → the Resources a delete of it cascades into, one hop.
24
3
  const removedBy = new Map();
25
4
  for (const [resource, edges] of Object.entries(table ?? {})) {
26
5
  for (const edge of edges) {
@@ -41,8 +20,6 @@ export function resolveCascadeClosure(table) {
41
20
  const closure = new Set();
42
21
  const frontier = [...(removedBy.get(resource) ?? [])];
43
22
  for (let next = frontier.pop(); next !== undefined; next = frontier.pop()) {
44
- // The visited set is what makes a self-referential or cyclic cascade
45
- // terminate: each Resource is expanded at most once.
46
23
  if (closure.has(next)) {
47
24
  continue;
48
25
  }
@@ -1,27 +1,6 @@
1
- /**
2
- * The one place core mints a request id (Q10 = B; F-833). The typed façades and
3
- * the typed transaction runner call it when the caller supplies no usable id.
4
- * It is internal: neither the main entry nor `@aventara/core/protocol` exports
5
- * a minting helper. A host's correlation id reaches core as data —
6
- * `ExecutionOptions.requestId` in process, or the `requestId` of a request the
7
- * protocol decoders build from an adopted `Aventara-Request-Id` (Phase 10 S7,
8
- * which mints through this module when none was adopted).
9
- */
10
1
  function createRequestId() {
11
2
  return globalThis.crypto.randomUUID();
12
3
  }
13
- /**
14
- * The request id of one façade call or transaction run: the caller's
15
- * `ExecutionOptions.requestId` when it is a non-empty string, otherwise a minted
16
- * one. Nothing is refused (the call runs either way); the empty string is read
17
- * as absent, because it names nothing a log line or a diagnostic could be
18
- * correlated by.
19
- *
20
- * Read as `unknown` because the façades are total for untyped callers too: a
21
- * value the type refuses — a non-string id, a non-object options argument, a
22
- * getter that throws — is ignored rather than carried into a `string` slot or
23
- * thrown out of a call that must resolve.
24
- */
25
4
  export function requestIdOf(options) {
26
5
  if (typeof options === "object" && options !== null) {
27
6
  let requestId;
@@ -20,14 +20,13 @@ export type ScalarValueNormalizationResult = {
20
20
  };
21
21
  type Path = readonly (string | number)[];
22
22
  /**
23
- * The one discriminant between an argument OBJECT and a VALUE wherever a
24
- * position admits both — a filter position above all: a **plain** record is an
25
- * operator object, and anything else — a `Date`, a core `Decimal`, a
26
- * `Uint8Array` — is a value, which equality shorthand then holds to the field's
27
- * scalar (spec §9.1). The validator (`operation-validator-core.ts`) reads a
28
- * filter position through this same answer, and an adapter's filter translation
29
- * is expected to, so a position cannot be operators to one reader and a value to
30
- * another.
23
+ * The one discriminant between an argument OBJECT and a VALUE wherever a position
24
+ * admits both — a filter position above all: a **plain** record is an operator
25
+ * object, and anything else — a `Date`, a core `Decimal`, a `Uint8Array` — is a
26
+ * value, which equality shorthand then holds to the field's scalar. The validator
27
+ * (`operation-validator-core.ts`) reads a filter position through this same
28
+ * answer, and an adapter's filter translation is expected to, so a position cannot
29
+ * be operators to one reader and a value to another.
31
30
  */
32
31
  export declare function isPlainRecord(value: unknown): value is Readonly<Record<string, unknown>>;
33
32
  /**
@@ -36,14 +35,13 @@ export declare function isPlainRecord(value: unknown): value is Readonly<Record<
36
35
  */
37
36
  export declare function normalizeScalarFieldValue(contract: Contract, field: ScalarField<Contract>, value: unknown, scope: RequestOrigin | "runtime", path: Path): ScalarValueNormalizationResult;
38
37
  /**
39
- * Copies one validated runtime value of a scalar field into a value the
40
- * framework owns, per scalar codec (DA-25): `datetime` → a new `Date`,
41
- * `bytes` → a sliced `Uint8Array`, `json` → a structured deep copy, and a list
42
- * → a new array of copied items. `decimal` keeps its identity, and strings,
43
- * numbers, booleans, bigints, enum members and `null` are immutable and
44
- * returned unchanged. {@link SCALAR_ATOM_COPIERS} is exhaustive over
45
- * {@link BuiltInScalar}, so a new codec cannot reach this function without
46
- * declaring its copy.
38
+ * Copies one validated runtime value of a scalar field into a value the framework
39
+ * owns, per scalar codec: `datetime` → a new `Date`, `bytes` → a sliced
40
+ * `Uint8Array`, `json` → a structured deep copy, and a list → a new array of
41
+ * copied items. `decimal` keeps its identity, and strings, numbers, booleans,
42
+ * bigints, enum members and `null` are immutable and returned unchanged. {@link
43
+ * SCALAR_ATOM_COPIERS} is exhaustive over {@link BuiltInScalar}, so a new codec
44
+ * cannot reach this function without declaring its copy.
47
45
  */
48
46
  export declare function copyScalarFieldValue(field: ScalarField<Contract>, value: unknown): unknown;
49
47
  /**