@aventara/core 0.1.0-pilot.3 → 1.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/CHANGELOG.md +338 -0
  2. package/README.md +100 -3
  3. package/dist/adapters/adapter-discovery-diagnostic.d.ts +2 -0
  4. package/dist/adapters/adapter-field.d.ts +3 -26
  5. package/dist/adapters/adapter-identifier.d.ts +1 -11
  6. package/dist/adapters/adapter-model.d.ts +12 -0
  7. package/dist/adapters/adapter-operation-executor.interface.d.ts +5 -1
  8. package/dist/adapters/adapter-resource.d.ts +7 -28
  9. package/dist/adapters/adapter.interface.d.ts +25 -26
  10. package/dist/compiler/contract-compiler-diagnostic.d.ts +8 -0
  11. package/dist/compiler/contract.compiler.d.ts +11 -22
  12. package/dist/compiler/contract.compiler.js +67 -0
  13. package/dist/config/any-framework-config.d.ts +5 -26
  14. package/dist/config/config-diagnostic.d.ts +2 -0
  15. package/dist/config/config-layer.d.ts +6 -19
  16. package/dist/config/config.resolver.d.ts +9 -14
  17. package/dist/config/config.resolver.js +1 -0
  18. package/dist/config/config.validator.d.ts +6 -3
  19. package/dist/config/config.validator.js +6 -0
  20. package/dist/config/define-fields.d.ts +15 -48
  21. package/dist/config/effective-fields.d.ts +6 -29
  22. package/dist/config/entrypoint.normalizer.d.ts +6 -18
  23. package/dist/config/framework-behaviors-config.d.ts +11 -10
  24. package/dist/config/framework-config.d.ts +19 -16
  25. package/dist/config/framework-fields-config.d.ts +20 -7
  26. package/dist/config/framework-restrictions-config.d.ts +41 -61
  27. package/dist/config/pipeline-config.d.ts +7 -17
  28. package/dist/contracts/capability.d.ts +13 -49
  29. package/dist/contracts/client-contract-structure.validator.d.ts +28 -54
  30. package/dist/contracts/client-contract-structure.validator.js +99 -2
  31. package/dist/contracts/contract-canonicalizer.d.ts +4 -6
  32. package/dist/contracts/contract-canonicalizer.js +32 -0
  33. package/dist/contracts/contract-hash.d.ts +3 -2
  34. package/dist/contracts/contract-limits.d.ts +7 -1
  35. package/dist/contracts/contract-protocol.d.ts +7 -8
  36. package/dist/contracts/contract.d.ts +20 -4
  37. package/dist/contracts/enum-definition.d.ts +2 -0
  38. package/dist/contracts/field.d.ts +40 -167
  39. package/dist/contracts/identifier.d.ts +1 -16
  40. package/dist/contracts/json-shape.d.ts +65 -0
  41. package/dist/contracts/json-shape.js +1 -0
  42. package/dist/contracts/lifecycle-trait.d.ts +4 -3
  43. package/dist/contracts/reference-write-limit.d.ts +7 -0
  44. package/dist/contracts/reference-write-limit.js +1 -0
  45. package/dist/contracts/resource-operations.d.ts +2 -62
  46. package/dist/contracts/resource.d.ts +2 -0
  47. package/dist/contracts/scalar-value-type.d.ts +28 -48
  48. package/dist/contracts/scalar.d.ts +4 -3
  49. package/dist/contracts/transaction-capability.d.ts +2 -0
  50. package/dist/contracts/wire-scalar-format.d.ts +6 -21
  51. package/dist/framework/contract-view.d.ts +152 -130
  52. package/dist/framework/framework-construction.error.d.ts +6 -1
  53. package/dist/framework/framework.d.ts +33 -46
  54. package/dist/framework/framework.factory.d.ts +3 -17
  55. package/dist/framework/framework.factory.js +56 -13
  56. package/dist/framework/operation-facade.d.ts +3 -7
  57. package/dist/index.d.ts +8 -40
  58. package/dist/index.js +0 -20
  59. package/dist/internal.d.ts +35 -0
  60. package/dist/internal.js +16 -0
  61. package/dist/json/avz.d.ts +96 -0
  62. package/dist/json/avz.js +212 -0
  63. package/dist/json/index.d.ts +6 -0
  64. package/dist/json/index.js +1 -0
  65. package/dist/json/json-shape.registry.d.ts +22 -0
  66. package/dist/json/json-shape.registry.js +16 -0
  67. package/dist/json/zod-issue.translator.d.ts +51 -0
  68. package/dist/json/zod-issue.translator.js +142 -0
  69. package/dist/operations/operation-arguments.d.ts +198 -303
  70. package/dist/operations/operation-call.d.ts +13 -15
  71. package/dist/operations/operation-identity.d.ts +6 -2
  72. package/dist/operations/operation-identity.js +3 -0
  73. package/dist/operations/operation-result.d.ts +15 -61
  74. package/dist/operations/operation-validator-core.d.ts +6 -5
  75. package/dist/operations/operation-validator-core.js +28 -10
  76. package/dist/operations/operation-validator.d.ts +22 -9
  77. package/dist/operations/operation-validator.js +26 -10
  78. package/dist/operations/projection-scope.resolver.d.ts +1 -11
  79. package/dist/operations/structural-limits.walker.d.ts +19 -0
  80. package/dist/operations/structural-limits.walker.js +344 -0
  81. package/dist/operations/validation-code.d.ts +5 -17
  82. package/dist/operations/write-scope.resolver.d.ts +3 -28
  83. package/dist/protocol/absent-route.responder.d.ts +25 -39
  84. package/dist/protocol/absent-route.responder.js +32 -6
  85. package/dist/protocol/av-protocol.d.ts +2 -8
  86. package/dist/protocol/contract-document.responder.d.ts +5 -18
  87. package/dist/protocol/http-request.decoder.d.ts +18 -59
  88. package/dist/protocol/http-request.decoder.js +11 -3
  89. package/dist/protocol/http-request.reader.d.ts +5 -5
  90. package/dist/protocol/http-response.encoder.d.ts +14 -20
  91. package/dist/protocol/http-status.table.d.ts +0 -8
  92. package/dist/protocol/index.d.ts +2 -7
  93. package/dist/protocol/operation-response.guard.d.ts +0 -15
  94. package/dist/protocol/protocol-failure.d.ts +8 -27
  95. package/dist/protocol/protocol-headers.d.ts +15 -42
  96. package/dist/protocol/protocol-surface.d.ts +0 -22
  97. package/dist/protocol/protocol.binder.d.ts +19 -27
  98. package/dist/protocol/protocol.binder.js +3 -1
  99. package/dist/protocol/protocol.handler.d.ts +19 -23
  100. package/dist/protocol/protocol.handler.js +14 -4
  101. package/dist/protocol/unmounted-route.error.d.ts +19 -0
  102. package/dist/protocol/unmounted-route.error.js +10 -0
  103. package/dist/runtime/caller-data.copier.d.ts +59 -0
  104. package/dist/runtime/caller-data.copier.js +470 -0
  105. package/dist/runtime/caller-data.door.d.ts +56 -0
  106. package/dist/runtime/caller-data.door.js +65 -0
  107. package/dist/runtime/computed-read-runtime.js +3 -10
  108. package/dist/runtime/computed-value.error.d.ts +13 -0
  109. package/dist/runtime/computed-value.error.js +9 -0
  110. package/dist/runtime/create-requirement.d.ts +43 -7
  111. package/dist/runtime/create-requirement.js +89 -9
  112. package/dist/runtime/decimal.d.ts +5 -3
  113. package/dist/runtime/diagnostics-reporter.d.ts +33 -37
  114. package/dist/runtime/diagnostics-reporter.js +11 -4
  115. package/dist/runtime/error-boundary.d.ts +14 -10
  116. package/dist/runtime/error-boundary.js +65 -35
  117. package/dist/runtime/execution-options.d.ts +3 -7
  118. package/dist/runtime/field-behavior.d.ts +14 -21
  119. package/dist/runtime/framework-error.d.ts +14 -2
  120. package/dist/runtime/json-value.d.ts +7 -15
  121. package/dist/runtime/misuse-reporter.d.ts +32 -0
  122. package/dist/runtime/misuse-reporter.js +19 -0
  123. package/dist/runtime/mutation-planner.d.ts +6 -12
  124. package/dist/runtime/mutation-planner.js +13 -11
  125. package/dist/runtime/operation-request.d.ts +11 -11
  126. package/dist/runtime/operation-response.d.ts +18 -23
  127. package/dist/runtime/operation-runtime.d.ts +25 -33
  128. package/dist/runtime/operation-runtime.js +37 -18
  129. package/dist/runtime/outcome-mapper.js +12 -2
  130. package/dist/runtime/output-validator.d.ts +10 -0
  131. package/dist/runtime/output-validator.js +11 -0
  132. package/dist/runtime/owned-execution.registry.d.ts +13 -0
  133. package/dist/runtime/owned-execution.registry.js +7 -0
  134. package/dist/runtime/pipeline.d.ts +33 -36
  135. package/dist/runtime/referential-action.d.ts +8 -26
  136. package/dist/runtime/request-envelope.validator.d.ts +5 -0
  137. package/dist/runtime/request-envelope.validator.js +118 -0
  138. package/dist/runtime/request-id.d.ts +3 -10
  139. package/dist/runtime/scalar-runtime.d.ts +42 -14
  140. package/dist/runtime/scalar-runtime.js +162 -79
  141. package/dist/transactions/deferred-operation.d.ts +10 -21
  142. package/dist/transactions/operation-arguments-encoder.d.ts +1 -0
  143. package/dist/transactions/operation-fingerprint.d.ts +3 -1
  144. package/dist/transactions/transaction-envelope.d.ts +2 -4
  145. package/dist/transactions/transaction-integrity-error.d.ts +4 -15
  146. package/dist/transactions/transaction-plan-assembler.d.ts +6 -1
  147. package/dist/transactions/transaction-plan-assembler.js +74 -75
  148. package/dist/transactions/transaction-plan-validator.d.ts +33 -24
  149. package/dist/transactions/transaction-plan-validator.js +204 -153
  150. package/dist/transactions/transaction-reference-collector.d.ts +30 -0
  151. package/dist/transactions/transaction-reference-collector.js +204 -0
  152. package/dist/transactions/transaction-reference-resolver.d.ts +9 -12
  153. package/dist/transactions/transaction-reference-resolver.js +10 -5
  154. package/dist/transactions/transaction-reference-validator.js +24 -17
  155. package/dist/transactions/transaction-reference.d.ts +31 -64
  156. package/dist/transactions/transaction-request.d.ts +6 -8
  157. package/dist/transactions/transaction-response.d.ts +10 -7
  158. package/dist/transactions/transaction-response.js +24 -1
  159. package/dist/transactions/transaction-runtime.d.ts +7 -5
  160. package/dist/transactions/transaction-runtime.js +4 -3
  161. package/dist/transactions/transaction-slot-path-error.d.ts +5 -15
  162. package/dist/transactions/transaction-slot-writer.d.ts +3 -13
  163. package/package.json +17 -3
  164. package/dist/operations/operation-boolean-nodes.d.ts +0 -11
  165. package/dist/operations/operation-boolean-nodes.js +0 -283
  166. package/dist/operations/operation-nesting.d.ts +0 -8
  167. package/dist/operations/operation-nesting.js +0 -307
@@ -6,43 +6,50 @@ 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
+ import { type ArgumentsSource } from "./caller-data.door.js";
10
+ import { type RecordWriteFacts } from "./create-requirement.js";
10
11
  import type { DiagnosticsReporter } from "./diagnostics-reporter.js";
11
12
  import type { ClientProtocolIdentity, FrameworkOperationRequest, OperationExecutionRequest } from "./operation-request.js";
12
13
  import type { OperationFailureResponse, OperationResponse } from "./operation-response.js";
14
+ import { type NormalizationWrite } from "./scalar-runtime.js";
13
15
  export type OperationRuntimeDependencies<M extends AdapterModel> = {
14
16
  readonly adapter: Adapter<M>;
15
17
  readonly contracts: FrameworkContracts;
16
18
  readonly config: ResolvedFrameworkConfig<M>;
17
19
  };
18
20
  /**
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
+ * What a validated operation's pipeline needs: everything except the root Adapter. Its
22
+ * single Adapter call goes through the executor passed beside it, so inside a
21
23
  * transaction plan the root Adapter is unreachable by type.
22
24
  */
23
25
  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
+ /**
27
+ * The Adapter's create requirements and referential edges, read once at
28
+ * construction.
29
+ */
30
+ readonly recordWrites: RecordWriteFacts;
26
31
  };
27
32
  /**
28
33
  * The executor core owns no diagnostics channel: the framework factory owns
29
34
  * it and hands in one reporter already bound to this request, so every
30
35
  * boundary — including the outermost totality guard, which runs outside this
31
- * function — reports through the same channel.
36
+ * function — reports through the same channel. `source` says whether the
37
+ * request's arguments are the caller's own objects, copied at the door, or data
38
+ * no caller holds (see {@link ArgumentsSource}).
32
39
  */
33
- export type FrameworkOperationExecutor = (request: FrameworkOperationRequest, report: DiagnosticsReporter) => Promise<OperationResponse>;
34
- /** Selects the Contract of the request's execution scope for both execution seams. */
40
+ export type FrameworkOperationExecutor = (request: FrameworkOperationRequest, report: DiagnosticsReporter, source: ArgumentsSource) => Promise<OperationResponse>;
41
+ /** Selects the Contract of the request's origin. */
35
42
  export declare function activeContract(contracts: FrameworkContracts, request: FrameworkOperationRequest | FrameworkTransactionRequest): Contract;
36
43
  /** Creates the transport-independent operation executor owned by core. */
37
44
  export declare function createOperationRuntime<M extends AdapterModel>(dependencies: OperationRuntimeDependencies<M>): FrameworkOperationExecutor;
38
- /** Validates the request-level protocol identity shared by both execution seams. */
45
+ /** Validates the request-level protocol identity. */
39
46
  export declare function validateProtocolIdentity(contracts: FrameworkContracts, request: FrameworkOperationRequest | FrameworkTransactionRequest): OperationFailureResponse | undefined;
40
47
  /**
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.
48
+ * The stale-client check on one client protocol identity: the protocol version first
49
+ * (`A2006`), then the ClientContract hash (`A2005`). Exact match only. The one
50
+ * implementation — the protocol's decoders and absence answers
51
+ * (`@aventara/core/protocol`) call it too, so a stale client hears the same envelope
52
+ * whichever layer answers.
46
53
  */
47
54
  export declare function protocolIdentityFailure(contracts: FrameworkContracts, protocol: ClientProtocolIdentity): OperationFailureResponse | undefined;
48
55
  export type ValidatedOperationArguments = {
@@ -52,26 +59,11 @@ export type ValidatedOperationArguments = {
52
59
  readonly ok: false;
53
60
  readonly response: OperationFailureResponse;
54
61
  };
55
- /** Reusable per-operation Framework Validation Layer argument seam. */
56
- export declare function validateOperationRequest(contract: Contract, request: FrameworkOperationRequest, options?: OperationValidationOptions): ValidatedOperationArguments;
62
+ /** Validates one operation's arguments through the Framework Validation Layer. */
63
+ export declare function validateOperationRequest(contract: Contract, request: FrameworkOperationRequest, options?: OperationValidationOptions, write?: NormalizationWrite): ValidatedOperationArguments;
57
64
  /**
58
65
  * Everything after the Framework Validation Layer: the developer/runtime path
59
- * wrapped in the post-validation error boundary. Validation failures never reach
60
- * this function, so developer filters never see them.
61
- *
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.
66
- *
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`.
71
- *
72
- * `argumentsPath` is the framework's own address for these arguments —
73
- * `["arguments"]` standalone, `["operations", i, "args"]` inside a plan. The
74
- * response boundary needs it to decide whether a thrower-supplied issue path
75
- * denotes the caller's own arguments or is unvouched text.
66
+ * wrapped in the post-validation error boundary. Validation failures never
67
+ * reach this function, so developer filters never see them.
76
68
  */
77
69
  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,32 +1,34 @@
1
1
  import { AVENTARA_PROTOCOL_VERSION } from "../contracts/contract-protocol.js";
2
- import { validateOperationArguments } from "../operations/operation-validator.js";
2
+ import { findOperationAvailabilityIssue, validateOperationArguments, } from "../operations/operation-validator.js";
3
+ import { copyAtDoor, misuseIdentityOf, refuseCallerData, } from "./caller-data.door.js";
3
4
  import { materializeReadOperation, planReadOperation, } from "./computed-read-runtime.js";
4
- import { missingCreateValues, } from "./create-requirement.js";
5
+ import { createRequirementIssues, recordWriteFactsOf, } from "./create-requirement.js";
5
6
  import { AdapterBoundary, OPERATION_ARGUMENTS_PATH, resolveOperationFailure, sanitizeOperationFailure, } from "./error-boundary.js";
7
+ import { createMisuseReporter, } from "./misuse-reporter.js";
6
8
  import { applyWriteFieldBehaviors, stripVirtualMutationInput, } from "./mutation-planner.js";
7
9
  import { internalFailure, operationFailure } from "./operation-response.js";
8
10
  import { mapOperationOutcome, mapValidationOutcome } from "./outcome-mapper.js";
9
- import { validateOperationOutput } from "./output-validator.js";
11
+ import { OutputValidationError, validateOperationOutput, } from "./output-validator.js";
10
12
  import { refusePipeOutput } from "./pipe-output.validator.js";
11
- import { normalizeOperationScalarValues } from "./scalar-runtime.js";
13
+ import { normalizeOperationScalarValues, } from "./scalar-runtime.js";
12
14
  export function activeContract(contracts, request) {
13
- return request.scope === "application"
15
+ return request.origin === "application"
14
16
  ? contracts.application
15
17
  : contracts.client;
16
18
  }
17
19
  function activeScopeConfig(config, request) {
18
- return request.scope === "application" ? config.application : config.client;
20
+ return request.origin === "application" ? config.application : config.client;
19
21
  }
20
22
  function pipelineContext(contract, request, args) {
21
23
  return {
22
24
  contract,
23
- scope: request.scope,
25
+ origin: request.origin,
24
26
  resource: request.resource,
25
27
  family: request.family,
26
28
  variant: request.variant,
27
29
  args,
28
30
  requestId: request.requestId,
29
- ...(request.scope === "client" && request.transport !== undefined
31
+ ...(request.origin === "client" && request.transport !== undefined
30
32
  ? { transport: request.transport }
31
33
  : {}),
32
34
  };
@@ -37,24 +39,30 @@ function forbidden() {
37
39
  });
38
40
  }
39
41
  function outputAccepted(contract, request, projection, data, report) {
42
+ let validation;
40
43
  try {
41
- return validateOperationOutput(contract, request, projection, data).ok;
44
+ validation = validateOperationOutput(contract, request, projection, data);
42
45
  }
43
46
  catch (error) {
44
47
  report(error, "A3000");
45
48
  return false;
46
49
  }
50
+ if (!validation.ok) {
51
+ report(new OutputValidationError(request, validation.issues), "A3000");
52
+ }
53
+ return validation.ok;
47
54
  }
48
55
  export function createOperationRuntime(dependencies) {
49
56
  const pipelineDependencies = {
50
57
  contracts: dependencies.contracts,
51
58
  config: dependencies.config,
52
- createRequirements: dependencies.adapter.createRequirements ?? {},
59
+ recordWrites: recordWriteFactsOf(dependencies.adapter),
53
60
  };
54
- return async (request, report) => {
61
+ const misuse = createMisuseReporter(dependencies.config.misuse);
62
+ return async (request, report, source) => {
55
63
  let validated;
56
64
  try {
57
- validated = validateOperation(dependencies, request);
65
+ validated = validateOperation(dependencies, request, source, misuse);
58
66
  }
59
67
  catch (error) {
60
68
  return sanitizeOperationFailure(error, "runtime", report, {
@@ -68,20 +76,31 @@ export function createOperationRuntime(dependencies) {
68
76
  return executeValidatedOperation(pipelineDependencies, request, validated.contract, validated.arguments, OPERATION_ARGUMENTS_PATH, dependencies.adapter, report);
69
77
  };
70
78
  }
71
- function validateOperation(dependencies, request) {
79
+ function validateOperation(dependencies, request, source, misuse) {
72
80
  const protocolFailure = validateProtocolIdentity(dependencies.contracts, request);
73
81
  if (protocolFailure !== undefined) {
74
82
  return rejected(protocolFailure);
75
83
  }
76
84
  const contract = activeContract(dependencies.contracts, request);
77
- const validation = validateOperationRequest(contract, request);
85
+ let owned = request;
86
+ if (source === "caller" &&
87
+ findOperationAvailabilityIssue(contract, request) === undefined) {
88
+ const copy = copyAtDoor(request.arguments);
89
+ if (!copy.ok) {
90
+ return rejected(mapValidationOutcome([
91
+ refuseCallerData(copy.error, OPERATION_ARGUMENTS_PATH, misuseIdentityOf(request), misuse),
92
+ ]));
93
+ }
94
+ owned = { ...request, arguments: copy.value };
95
+ }
96
+ const validation = validateOperationRequest(contract, owned, undefined, "in-place");
78
97
  if (!validation.ok) {
79
98
  return rejected(validation.response);
80
99
  }
81
100
  return { ok: true, contract, arguments: validation.arguments };
82
101
  }
83
102
  export function validateProtocolIdentity(contracts, request) {
84
- return request.scope === "client"
103
+ return request.origin === "client"
85
104
  ? protocolIdentityFailure(contracts, request.protocol)
86
105
  : undefined;
87
106
  }
@@ -98,12 +117,12 @@ export function protocolIdentityFailure(contracts, protocol) {
98
117
  }
99
118
  return undefined;
100
119
  }
101
- export function validateOperationRequest(contract, request, options) {
120
+ export function validateOperationRequest(contract, request, options, write = "copy") {
102
121
  const validation = validateOperationArguments(contract, request, options);
103
122
  if (!validation.ok) {
104
123
  return { ok: false, response: mapValidationOutcome(validation.issues) };
105
124
  }
106
- const scalarNormalization = normalizeOperationScalarValues(contract, { ...request, arguments: validation.arguments }, request.scope, options);
125
+ const scalarNormalization = normalizeOperationScalarValues(contract, { ...request, arguments: validation.arguments }, request.origin, options, write);
107
126
  return scalarNormalization.ok
108
127
  ? { ok: true, arguments: scalarNormalization.arguments }
109
128
  : {
@@ -144,7 +163,7 @@ export async function executeValidatedOperation(dependencies, request, contract,
144
163
  }
145
164
  }
146
165
  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);
166
+ const missing = createRequirementIssues(contract, dependencies.recordWrites, request, args, argumentsPath);
148
167
  if (missing.length > 0) {
149
168
  return mapValidationOutcome(missing);
150
169
  }
@@ -42,11 +42,21 @@ export function mapOperationOutcome(operation, data) {
42
42
  ? absenceOutcome(rule.absence)
43
43
  : operationSuccess(data, rule.success);
44
44
  }
45
+ function stepRelativeHead(issue) {
46
+ const path = issue.path;
47
+ return path?.[0] === "operations" && typeof path[1] === "number"
48
+ ? path[2]
49
+ : path?.[0];
50
+ }
51
+ function namesUnavailableOperation(issue) {
52
+ const head = stepRelativeHead(issue);
53
+ return issue.code === "V1006" && (head === "family" || head === "variant");
54
+ }
45
55
  function validationCode(issues) {
46
- if (issues.some((issue) => issue.path?.[0] === "resource")) {
56
+ if (issues.some((issue) => issue.code !== "V1019" && stepRelativeHead(issue) === "resource")) {
47
57
  return "A2001";
48
58
  }
49
- if (issues.some((issue) => issue.path?.[0] === "operation")) {
59
+ if (issues.some(namesUnavailableOperation)) {
50
60
  return "A2002";
51
61
  }
52
62
  if (issues.some((issue) => issue.code === "V1010" ||
@@ -11,6 +11,16 @@ export type OutputValidationResult = {
11
11
  type OutputOperation = OperationIdentity & {
12
12
  readonly resource: string;
13
13
  };
14
+ /**
15
+ * The error the diagnostics channel receives when a result fails output
16
+ * validation: whose result it was and what the validator found. The caller
17
+ * receives the generic `A3000` and never this.
18
+ */
19
+ export declare class OutputValidationError extends Error {
20
+ readonly issues: readonly ValidationIssue[];
21
+ readonly name = "OutputValidationError";
22
+ constructor(operation: OutputOperation, issues: readonly ValidationIssue[]);
23
+ }
14
24
  /** Validate final operation output against fixed cardinality and executed projection. */
15
25
  export declare function validateOperationOutput(contract: Contract, operation: OutputOperation, projection: ReadProjectionPlan | undefined, value: unknown): OutputValidationResult;
16
26
  export {};
@@ -168,6 +168,17 @@ function validateProjection(contract, operation, projection, value) {
168
168
  ? validateExactRecord(contract, value, projection, ["data"])
169
169
  : { ok: true };
170
170
  }
171
+ export class OutputValidationError extends Error {
172
+ issues;
173
+ name = "OutputValidationError";
174
+ constructor(operation, issues) {
175
+ const [first] = issues;
176
+ super(`The result of ${operation.resource}.${operation.family}.${operation.variant} does not satisfy its Contract${first === undefined
177
+ ? "."
178
+ : ` at "${(first.path ?? []).join(".")}" (${first.code}: ${first.message})`}`);
179
+ this.issues = issues;
180
+ }
181
+ }
171
182
  export function validateOperationOutput(contract, operation, projection, value) {
172
183
  const cardinality = validateOperationOutputCardinality(operation, value);
173
184
  return cardinality.ok
@@ -0,0 +1,13 @@
1
+ import type { ClientTransactionRequest } from "../transactions/transaction-request.js";
2
+ import type { TransactionResponse } from "../transactions/transaction-response.js";
3
+ import type { ClientOperationRequest } from "./operation-request.js";
4
+ import type { OperationResponse } from "./operation-response.js";
5
+ /** The two entries that run a request the protocol decoded itself, uncopied. */
6
+ export type OwnedExecution = {
7
+ readonly execute: (request: ClientOperationRequest) => Promise<OperationResponse>;
8
+ readonly executeTransaction: (request: ClientTransactionRequest) => Promise<TransactionResponse>;
9
+ };
10
+ /** Records `framework`'s owned entries; called once, at construction. */
11
+ export declare function recordOwnedExecution(framework: object, execution: OwnedExecution): void;
12
+ /** `framework`'s owned entries, or `undefined` for anything `createFramework` did not return. */
13
+ export declare function ownedExecutionOf(framework: object): OwnedExecution | undefined;
@@ -0,0 +1,7 @@
1
+ const OWNED_EXECUTIONS = new WeakMap();
2
+ export function recordOwnedExecution(framework, execution) {
3
+ OWNED_EXECUTIONS.set(framework, execution);
4
+ }
5
+ export function ownedExecutionOf(framework) {
6
+ return OWNED_EXECUTIONS.get(framework);
7
+ }
@@ -5,43 +5,16 @@ 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 arrived
9
- * on — a **deliberate exception** to core's transport-agnosticism.
10
- *
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).
8
+ * What a pipeline is told about the HTTP request a client-origin operation arrived on.
15
9
  */
16
10
  export type PipelineTransportContext = {
17
11
  readonly method: "POST";
18
12
  readonly headers: Readonly<Record<string, string>>;
19
13
  };
20
- /**
21
- * What a pipeline callback is told about the operation it is wrapping.
22
- *
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.
28
- *
29
- * A `Cfg` parameter here would be referenced nowhere — `biome` flags it
30
- * (`lint/correctness/noUnusedVariables`) and it would encode no semantic
31
- * dependency. `FrameworkScopeConfig` already has the precedent one line above
32
- * `pipelines`: `fields` takes `M` only.
33
- *
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.
38
- *
39
- * Under the default `M` the key set degenerates to `string`, so an unannotated
40
- * `PipelineContext` is what it was before.
41
- */
14
+ /** What a pipeline callback is told about the operation it is wrapping. */
42
15
  export type PipelineContext<M extends AdapterModel = AdapterModel> = {
43
16
  readonly contract: Contract;
44
- readonly scope: RequestOrigin;
17
+ readonly origin: RequestOrigin;
45
18
  readonly resource: FrameworkResourceKey<M>;
46
19
  readonly family: OperationFamily;
47
20
  readonly variant: string;
@@ -49,9 +22,33 @@ export type PipelineContext<M extends AdapterModel = AdapterModel> = {
49
22
  readonly requestId: string;
50
23
  readonly transport?: PipelineTransportContext;
51
24
  };
52
- export type Guard<M extends AdapterModel = AdapterModel> = (context: PipelineContext<M>) => boolean | undefined | Promise<boolean | undefined>;
53
- export type Pipe<M extends AdapterModel = AdapterModel> = (args: unknown, context: PipelineContext<M>) => unknown | Promise<unknown>;
54
- export type BeforeHook<M extends AdapterModel = AdapterModel> = (context: PipelineContext<M>) => void | Promise<void>;
55
- export type AfterHook<M extends AdapterModel = AdapterModel> = (result: unknown, context: PipelineContext<M>) => void | Promise<void>;
56
- export type Interceptor<M extends AdapterModel = AdapterModel> = (context: PipelineContext<M>, next: () => Promise<unknown>) => Promise<unknown>;
57
- export type Filter<M extends AdapterModel = AdapterModel> = (error: unknown, context: PipelineContext<M>) => FrameworkError | undefined | Promise<FrameworkError | undefined>;
25
+ /** Allows or denies an operation; returning `false` answers `A4001`. */
26
+ export type Guard<M extends AdapterModel = AdapterModel> = {
27
+ guard(context: PipelineContext<M>): boolean | undefined | Promise<boolean | undefined>;
28
+ }["guard"];
29
+ /**
30
+ * Transforms the arguments before the operation runs; returning `undefined`
31
+ * keeps them as they are.
32
+ */
33
+ export type Pipe<M extends AdapterModel = AdapterModel> = {
34
+ pipe(args: unknown, context: PipelineContext<M>): unknown | Promise<unknown>;
35
+ }["pipe"];
36
+ /** Runs just before the operation executes. */
37
+ export type BeforeHook<M extends AdapterModel = AdapterModel> = {
38
+ before(context: PipelineContext<M>): void | Promise<void>;
39
+ }["before"];
40
+ /** Runs after the operation succeeds, with its result. */
41
+ export type AfterHook<M extends AdapterModel = AdapterModel> = {
42
+ after(result: unknown, context: PipelineContext<M>): void | Promise<void>;
43
+ }["after"];
44
+ /** Wraps the operation's execution; call `next()` to run it and get its result. */
45
+ export type Interceptor<M extends AdapterModel = AdapterModel> = {
46
+ intercept(context: PipelineContext<M>, next: () => Promise<unknown>): Promise<unknown>;
47
+ }["intercept"];
48
+ /**
49
+ * Turns a thrown error into a {@link FrameworkError} to answer with; returning
50
+ * `undefined` passes the error on.
51
+ */
52
+ export type Filter<M extends AdapterModel = AdapterModel> = {
53
+ filter(error: unknown, context: PipelineContext<M>): FrameworkError | undefined | Promise<FrameworkError | undefined>;
54
+ }["filter"];
@@ -1,33 +1,8 @@
1
1
  /**
2
2
  * Core's referential-action vocabulary, and the transitive cascade closure the
3
3
  * transaction planner reads.
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. So the words here are
7
- * Aventara's, stated as the effect on the referencing rows, and an Adapter
8
- * translates its own provider's vocabulary into them.
9
- *
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.
13
- */
14
- /**
15
- * What deleting a referenced row does to the rows that reference it.
16
- *
17
- * - `cascade` — they are deleted with it, and their own referencing rows in turn
18
- * (SQL `ON DELETE CASCADE`).
19
- * - `restrict` — the delete is refused while they reference it (`RESTRICT`).
20
- * - `noAction` — the delete is refused while they reference it (`NO ACTION`).
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.
25
- * - `setNull` — they are kept, the reference set to null (`SET NULL`).
26
- * - `setDefault` — they are kept, the reference set to its column default (`SET
27
- * DEFAULT`).
28
- *
29
- * Only `cascade` takes rows away, so only `cascade` is followed by the closure.
30
4
  */
5
+ /** What deleting a referenced row does to the rows that reference it. */
31
6
  export type ReferentialDeleteEffect = "cascade" | "restrict" | "noAction" | "setNull" | "setDefault";
32
7
  /**
33
8
  * One relation edge whose referencing side holds the reference.
@@ -48,6 +23,13 @@ export type ReferentialEdge = {
48
23
  readonly field: string;
49
24
  /** The Resource the edge points AT — the one whose deletion fires `onDelete`. */
50
25
  readonly target: string;
26
+ /**
27
+ * The scalar fields of the ENCLOSING Resource that hold the reference, in the
28
+ * order the store pairs them with the `target`'s key. A record that writes the
29
+ * relation `field` and any of these names the reference twice and is refused;
30
+ * a required relation may be written through all of them instead.
31
+ */
32
+ readonly references: readonly string[];
51
33
  /** What deleting a `target` row does to rows of the ENCLOSING Resource. */
52
34
  readonly onDelete: ReferentialDeleteEffect;
53
35
  };
@@ -0,0 +1,5 @@
1
+ import type { ValidationIssue } from "../operations/validation-code.js";
2
+ /** The issues of one malformed operation request envelope; empty when well formed. */
3
+ export declare function validateOperationEnvelope(request: unknown): readonly ValidationIssue[];
4
+ /** The issues of one malformed transaction request envelope; empty when well formed. */
5
+ export declare function validateTransactionEnvelope(request: unknown): readonly ValidationIssue[];
@@ -0,0 +1,118 @@
1
+ const ORIGINS = ["application", "client"];
2
+ const OPERATION_IDENTITY_MEMBERS = ["resource", "family", "variant"];
3
+ export function validateOperationEnvelope(request) {
4
+ if (!isRecord(request)) {
5
+ return [issue([], "V1001", "Operation request must be an object.")];
6
+ }
7
+ const issues = [...originAndIdentityIssues(request)];
8
+ for (const member of OPERATION_IDENTITY_MEMBERS) {
9
+ const failure = requiredStringIssue(request, member);
10
+ if (failure !== undefined) {
11
+ issues.push(failure);
12
+ }
13
+ }
14
+ return issues;
15
+ }
16
+ export function validateTransactionEnvelope(request) {
17
+ if (!isRecord(request)) {
18
+ return [issue([], "V1001", "Transaction request must be an object.")];
19
+ }
20
+ return originAndIdentityIssues(request);
21
+ }
22
+ function originAndIdentityIssues(request) {
23
+ const issues = [];
24
+ const origin = request.origin;
25
+ if (origin === undefined) {
26
+ issues.push(issue(["origin"], "V1000", 'Required property "origin" is missing.'));
27
+ }
28
+ else if (typeof origin !== "string") {
29
+ issues.push(issue(["origin"], "V1001", "Request origin must be a string."));
30
+ }
31
+ else if (!ORIGINS.includes(origin)) {
32
+ issues.push(issue(["origin"], "V1002", 'Request origin must be "application" or "client".'));
33
+ }
34
+ const requestId = request.requestId;
35
+ if (requestId === undefined || requestId === "") {
36
+ issues.push(issue(["requestId"], "V1000", 'Required property "requestId" is missing or empty.'));
37
+ }
38
+ else if (typeof requestId !== "string") {
39
+ issues.push(issue(["requestId"], "V1001", "Request id must be a string."));
40
+ }
41
+ if (origin === "client") {
42
+ issues.push(...protocolIssues(request.protocol));
43
+ if (request.transport !== undefined) {
44
+ issues.push(...transportIssues(request.transport));
45
+ }
46
+ }
47
+ else if (origin === "application") {
48
+ for (const member of ["protocol", "transport"]) {
49
+ if (request[member] !== undefined) {
50
+ issues.push(issue([member], "V1005", `Property "${member}" belongs to a client request, not an application one.`));
51
+ }
52
+ }
53
+ }
54
+ return issues;
55
+ }
56
+ function protocolIssues(protocol) {
57
+ if (protocol === undefined) {
58
+ return [
59
+ issue(["protocol"], "V1000", 'Required property "protocol" is missing from a client request.'),
60
+ ];
61
+ }
62
+ if (!isRecord(protocol)) {
63
+ return [
64
+ issue(["protocol"], "V1001", "Protocol identity must be an object."),
65
+ ];
66
+ }
67
+ const issues = [];
68
+ if (protocol.version === undefined) {
69
+ issues.push(issue(["protocol", "version"], "V1000", 'Required property "version" is missing.'));
70
+ }
71
+ else if (!Number.isInteger(protocol.version)) {
72
+ issues.push(issue(["protocol", "version"], "V1001", "Protocol version must be an integer."));
73
+ }
74
+ if (protocol.hash === undefined) {
75
+ issues.push(issue(["protocol", "hash"], "V1000", 'Required property "hash" is missing.'));
76
+ }
77
+ else if (typeof protocol.hash !== "string") {
78
+ issues.push(issue(["protocol", "hash"], "V1001", "Protocol hash must be a string."));
79
+ }
80
+ return issues;
81
+ }
82
+ function transportIssues(transport) {
83
+ if (!isRecord(transport)) {
84
+ return [
85
+ issue(["transport"], "V1001", "Transport metadata must be an object."),
86
+ ];
87
+ }
88
+ const issues = [];
89
+ if (transport.method !== "POST") {
90
+ issues.push(issue(["transport", "method"], "V1001", 'Transport method must be "POST".'));
91
+ }
92
+ const headers = transport.headers;
93
+ if (!isRecord(headers)) {
94
+ issues.push(issue(["transport", "headers"], "V1001", "Transport headers must be an object."));
95
+ return issues;
96
+ }
97
+ for (const [name, value] of Object.entries(headers)) {
98
+ if (typeof value !== "string") {
99
+ issues.push(issue(["transport", "headers", name], "V1001", "A transport header value must be a string."));
100
+ }
101
+ }
102
+ return issues;
103
+ }
104
+ function requiredStringIssue(request, member) {
105
+ const value = request[member];
106
+ if (value === undefined) {
107
+ return issue([member], "V1000", `Required property "${member}" is missing.`);
108
+ }
109
+ return typeof value === "string"
110
+ ? undefined
111
+ : issue([member], "V1001", `Property "${member}" must be a string.`);
112
+ }
113
+ function isRecord(value) {
114
+ return typeof value === "object" && value !== null && !Array.isArray(value);
115
+ }
116
+ function issue(path, code, message) {
117
+ return { path, code, message };
118
+ }
@@ -1,13 +1,6 @@
1
1
  /**
2
- * The request id of one façade call or transaction run: the caller's
3
- * `ExecutionOptions.requestId` when it is a non-empty string, otherwise a minted
4
- * one. Nothing is refused (the call runs either way); the empty string is read
5
- * as absent, because it names nothing a log line or a diagnostic could be
6
- * correlated by.
7
- *
8
- * Read as `unknown` because the façades are total for untyped callers too: a
9
- * value the type refuses — a non-string id, a non-object options argument, a
10
- * getter that throws — is ignored rather than carried into a `string` slot or
11
- * thrown out of a call that must resolve.
2
+ * The request id of one call or transaction run: the caller's
3
+ * `ExecutionOptions.requestId` when it is a non-empty string, otherwise a new
4
+ * one. Any other value is ignored.
12
5
  */
13
6
  export declare function requestIdOf(options: unknown): string;