@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
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * The caller's say over an in-process request it does not write by hand: the
3
3
  * second argument of every `framework.application.*` / `framework.client.*` call
4
- * and of `framework.transaction` (Q10 = B, Q14; closes F-833).
4
+ * and of `framework.transaction`.
5
5
  *
6
- * Not "transport options": an in-process call has no transport. A deferred
7
- * handle (`framework.appTx.*` / `framework.clientTx.*`) takes none — a handle is
8
- * not a request, and the id belongs to the run that executes it.
6
+ * Not "transport options": an in-process call has no transport. A deferred handle
7
+ * (`framework.appTx.*` / `framework.clientTx.*`) takes none — a handle is not a
8
+ * request, and the id belongs to the run that executes it.
9
9
  */
10
10
  export type ExecutionOptions = {
11
11
  /**
@@ -8,18 +8,15 @@ import type { PipelineContext } from "./pipeline.js";
8
8
  * What a write-stage compute callback is told about the field it is computing.
9
9
  *
10
10
  * `R` is the Resource the behavior is bound to and `Cfg` the authored
11
- * configuration literal, so `resource` is the literal Resource name and
12
- * `field` — like every other field-key position in the framework — is
13
- * {@link EffectiveFieldKey}: what the adapter discovered plus what the
14
- * configuration added.
11
+ * configuration literal, so `resource` is the literal Resource name and `field` —
12
+ * like every other field-key position in the framework — is {@link
13
+ * EffectiveFieldKey}: what the adapter discovered plus what the configuration
14
+ * added.
15
15
  *
16
- * `data` is keyed on the same set and stays `unknown`-valued. Scalar-to-TS
17
- * *value* typing is `OperationResultFor`'s job in Phase 9 (F-701) and is out
18
- * of scope here by the plan's hard constraints; what this type buys a callback
19
- * author is the key set, not the value.
16
+ * `data` is keyed on the same set and stays `unknown`-valued.
20
17
  *
21
18
  * Under the default `Cfg` the key set degenerates to `string` and this is
22
- * byte-for-byte the shape it was before `Cfg` existed (B4).
19
+ * byte-for-byte the shape it was before `Cfg` existed.
23
20
  */
24
21
  type ComputedWriteContext<M extends AdapterModel, Cfg, R extends FrameworkResourceKey<M>> = {
25
22
  readonly scope: RequestOrigin;
@@ -1,7 +1,3 @@
1
- /**
2
- * Stable typed error used by server-side pipelines, behaviors and Adapters to
3
- * deliberately surface one framework error outcome.
4
- */
5
1
  export class FrameworkError extends Error {
6
2
  code;
7
3
  cause;
@@ -4,12 +4,11 @@ import type { Contract, CoreContract } from "../contracts/contract.js";
4
4
  import type { OperationExecutionRequest } from "./operation-request.js";
5
5
  import type { PipelineContext } from "./pipeline.js";
6
6
  /**
7
- * The two Contracts a write reads (F-829, the write-side twin of D2/F-824). The
8
- * **Core** Contract decides what the Adapter is asked to store; the **active**
9
- * Contract adds the fields its scope declared under `application.fields` /
10
- * `client.fields`, which Core does not carry. Such a field is VIRTUAL by
11
- * construction (`contract.compiler.ts`), so its behaviours run and its value is
12
- * stripped before the Adapter call — never sent.
7
+ * The two Contracts a write reads. The **Core** Contract decides what the Adapter
8
+ * is asked to store; the **active** Contract adds the fields its scope declared
9
+ * under `application.fields` / `client.fields`, which Core does not carry. Such a
10
+ * field is VIRTUAL by construction (`contract.compiler.ts`), so its behaviours run
11
+ * and its value is stripped before the Adapter call — never sent.
13
12
  */
14
13
  export type WriteContracts = {
15
14
  readonly core: CoreContract;
@@ -2,13 +2,6 @@ import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
2
2
  import { FrameworkError } from "./framework-error.js";
3
3
  import { INTERNAL_FAILURE_MESSAGE } from "./operation-response.js";
4
4
  import { normalizeScalarFieldValue } from "./scalar-runtime.js";
5
- /**
6
- * The scalar field `fieldName` names on `resourceName` for a write: Core's own
7
- * field when Core has one — a field the scope hides or narrows is still stored,
8
- * and its behaviours still run — else the scalar the active scope added. Its
9
- * values are normalized against Core's registries either way: a scope adds
10
- * fields, never a scalar or an enum.
11
- */
12
5
  function writeScalarField(contracts, resourceName, fieldName) {
13
6
  const field = lookupField(lookupResource(contracts.core, resourceName), fieldName) ??
14
7
  lookupField(lookupResource(contracts.active, resourceName), fieldName);
@@ -16,15 +9,6 @@ function writeScalarField(contracts, resourceName, fieldName) {
16
9
  ? field
17
10
  : undefined;
18
11
  }
19
- /**
20
- * The sole grammar for relation-mutation record operands. Every write pass
21
- * maps these sites with its own record leaf, leaving identifier-only
22
- * directives untouched. The identifier operands this table deliberately leaves
23
- * empty are described by `RELATION_MUTATION_SELECTOR_SITES` in
24
- * `transactions/operation-arguments-encoder.ts`, the only pass that has to
25
- * encode them; both tables are keyed by `RelationMutationDirective`, so a new
26
- * directive fails to compile in both places.
27
- */
28
12
  export const RELATION_MUTATION_RECORD_SITES = {
29
13
  $create: [{ key: undefined, stage: "create" }],
30
14
  $connect: [],
@@ -38,11 +22,6 @@ export const RELATION_MUTATION_RECORD_SITES = {
38
22
  { key: "update", stage: "update" },
39
23
  ],
40
24
  };
41
- /**
42
- * Narrows a caller-supplied own key to the shared directive vocabulary. Own
43
- * membership, not an assertion: `Object.prototype` members resolve through the
44
- * prototype chain and would otherwise reach a site loop with a non-iterable.
45
- */
46
25
  export function isRelationMutationDirective(key) {
47
26
  return Object.hasOwn(RELATION_MUTATION_RECORD_SITES, key);
48
27
  }
@@ -144,7 +123,6 @@ async function mapRelationMutationRecords(relation, value, leaf) {
144
123
  }
145
124
  return normalized;
146
125
  }
147
- /** Apply effective computed create/update field behavior to post-pipe logical args. */
148
126
  export async function applyWriteFieldBehaviors(contracts, behaviors, request, args, contextFactory) {
149
127
  if (behaviors === undefined || !isRecord(args)) {
150
128
  return args;
@@ -190,7 +168,6 @@ async function stripVirtualRecord(contracts, resourceName, data) {
190
168
  }
191
169
  return persisted;
192
170
  }
193
- /** Strip VIRTUAL scalar input only at the persistence boundary. */
194
171
  export async function stripVirtualMutationInput(contracts, request, args) {
195
172
  if (!isRecord(args)) {
196
173
  return args;
@@ -1,5 +1,5 @@
1
1
  import type { ValidationIssue } from "../operations/validation-code.js";
2
- export declare const OPERATION_CODES: readonly ["A1000", "A1001", "A1002", "A1003", "A1004", "A1005", "A1006", "A1007", "A1008", "A1009", "A2000", "A2001", "A2002", "A2003", "A2004", "A2005", "A2006", "A2007", "A2008", "A2009", "A2010", "A2011", "A2012", "A2013", "A2014", "A3000", "A3001", "A3002", "A3003", "A4000", "A4001", "A4002"];
2
+ export declare const OPERATION_CODES: readonly ["A1000", "A1001", "A1002", "A1003", "A1004", "A1005", "A1006", "A1007", "A1008", "A1009", "A2000", "A2001", "A2002", "A2003", "A2004", "A2005", "A2006", "A2007", "A2008", "A2009", "A2010", "A2011", "A2012", "A2013", "A2014", "A3000", "A3001", "A3002", "A3003", "A3004", "A4000", "A4001", "A4002"];
3
3
  export type OperationCode = (typeof OPERATION_CODES)[number];
4
4
  export type OperationSuccessCode = Extract<OperationCode, `A1${string}`>;
5
5
  export type OperationErrorCode = Exclude<OperationCode, OperationSuccessCode>;
@@ -28,37 +28,21 @@ export const OPERATION_CODES = [
28
28
  "A3001",
29
29
  "A3002",
30
30
  "A3003",
31
+ "A3004",
31
32
  "A4000",
32
33
  "A4001",
33
34
  "A4002",
34
35
  ];
35
- /**
36
- * Builds the success-with-data arm. The only sanctioned way to construct
37
- * one: a runtime-computed `code` (e.g. from a table-driven outcome mapper)
38
- * still yields a well-typed {@link OperationSuccessResponse}.
39
- */
40
36
  export function operationSuccess(data, code) {
41
37
  return { data, code, cause: null };
42
38
  }
43
- /** Builds the no-match success arm (A1001). */
44
39
  export function operationNoMatch() {
45
40
  return { data: null, code: "A1001", cause: null };
46
41
  }
47
- /**
48
- * Builds the failure arm. The only sanctioned way to construct one: a
49
- * runtime-computed error `code` still yields a well-typed
50
- * {@link OperationFailureResponse}.
51
- */
52
42
  export function operationFailure(code, cause) {
53
43
  return { data: null, code, cause };
54
44
  }
55
- /** The one generic internal-failure text; every A3000 carries it. */
56
45
  export const INTERNAL_FAILURE_MESSAGE = "Internal framework error.";
57
- /**
58
- * The generic internal-failure envelope. Nothing about an internal failure is
59
- * caller-actionable, so every A3000 the framework emits is this exact value -
60
- * one constant rather than one restatement per boundary.
61
- */
62
46
  export function internalFailure() {
63
47
  return operationFailure("A3000", { message: INTERNAL_FAILURE_MESSAGE });
64
48
  }
@@ -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
+ }