@aventara/core 1.0.0-rc.0 → 1.0.0-rc.1
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.
- package/CHANGELOG.md +1140 -0
- package/README.md +224 -57
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
- package/dist/adapters/adapter-field.d.ts +11 -11
- package/dist/adapters/adapter-identifier.d.ts +6 -6
- package/dist/adapters/adapter-model.d.ts +9 -9
- package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
- package/dist/adapters/adapter-operation.d.ts +3 -3
- package/dist/adapters/adapter-resource.d.ts +18 -9
- package/dist/adapters/adapter-sql-statement.d.ts +19 -0
- package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
- package/dist/adapters/adapter.interface.d.ts +49 -29
- package/dist/avz/avz-check.factory.d.ts +83 -0
- package/dist/avz/avz-check.factory.js +116 -0
- package/dist/avz/avz-check.registry.d.ts +35 -0
- package/dist/avz/avz-check.registry.js +9 -0
- package/dist/avz/avz.d.ts +203 -0
- package/dist/{json → avz}/avz.js +150 -8
- package/dist/{json → avz}/index.d.ts +2 -1
- package/dist/{json → avz}/json-shape.registry.d.ts +7 -7
- package/dist/{json → avz}/zod-issue.translator.d.ts +8 -2
- package/dist/{json → avz}/zod-issue.translator.js +7 -0
- package/dist/compiler/contract-compiler-diagnostic.d.ts +31 -5
- package/dist/compiler/contract.compiler.d.ts +18 -18
- package/dist/compiler/contract.compiler.js +548 -441
- package/dist/compiler/create-requirements.projector.d.ts +49 -0
- package/dist/compiler/create-requirements.projector.js +423 -0
- package/dist/compiler/field-validator.compiler.d.ts +47 -0
- package/dist/compiler/field-validator.compiler.js +238 -0
- package/dist/config/any-framework-config.d.ts +19 -12
- package/dist/config/config-diagnostic.d.ts +11 -4
- package/dist/config/config-layer.d.ts +15 -6
- package/dist/config/config.resolver.d.ts +74 -31
- package/dist/config/config.resolver.js +108 -44
- package/dist/config/config.validator.d.ts +7 -7
- package/dist/config/config.validator.js +581 -72
- package/dist/config/define-resource.d.ts +57 -0
- package/dist/config/define-resource.js +14 -0
- package/dist/config/effective-fields.d.ts +19 -23
- package/dist/config/entrypoint.normalizer.d.ts +1 -1
- package/dist/config/exact-framework-config.d.ts +280 -0
- package/dist/config/field-compute.resolver.d.ts +19 -0
- package/dist/config/field-compute.resolver.js +115 -0
- package/dist/config/field-pipeline-config.d.ts +47 -0
- package/dist/config/forced-operation.validator.d.ts +10 -0
- package/dist/config/forced-operation.validator.js +66 -0
- package/dist/config/framework-config.d.ts +110 -39
- package/dist/config/framework-exemptions-config.d.ts +91 -0
- package/dist/config/framework-exemptions-config.js +1 -0
- package/dist/config/framework-field-definition.d.ts +78 -0
- package/dist/config/framework-field-definition.js +28 -0
- package/dist/config/framework-resources-config.d.ts +116 -0
- package/dist/config/framework-resources-config.js +1 -0
- package/dist/config/pipeline-config.d.ts +56 -20
- package/dist/config/pipeline-tree.resolver.d.ts +79 -0
- package/dist/config/pipeline-tree.resolver.js +220 -0
- package/dist/config/stage-type.eraser.d.ts +65 -0
- package/dist/config/stage-type.eraser.js +1 -0
- package/dist/contracts/capability.d.ts +11 -11
- package/dist/contracts/client-contract-structure.validator.d.ts +323 -28
- package/dist/contracts/client-contract-structure.validator.js +152 -8
- package/dist/contracts/contract-canonicalizer.d.ts +19 -3
- package/dist/contracts/contract-canonicalizer.js +141 -28
- package/dist/contracts/contract-hash.d.ts +12 -7
- package/dist/contracts/contract-hash.js +3 -2
- package/dist/contracts/contract-limits.d.ts +4 -1
- package/dist/contracts/contract-limits.js +1 -0
- package/dist/contracts/contract-lookup.d.ts +5 -5
- package/dist/contracts/contract-protocol.d.ts +4 -4
- package/dist/contracts/contract.d.ts +24 -16
- package/dist/contracts/create-requirements.d.ts +35 -0
- package/dist/contracts/create-requirements.js +1 -0
- package/dist/contracts/enum-definition.d.ts +1 -1
- package/dist/contracts/field-check.d.ts +101 -0
- package/dist/contracts/field-check.js +1 -0
- package/dist/contracts/field.d.ts +52 -42
- package/dist/contracts/identifier.d.ts +4 -4
- package/dist/contracts/json-shape.d.ts +12 -1
- package/dist/contracts/lifecycle-trait.d.ts +2 -2
- package/dist/contracts/resource-operations.d.ts +28 -28
- package/dist/contracts/resource.d.ts +15 -9
- package/dist/contracts/scalar-value-type.d.ts +15 -15
- package/dist/contracts/scalar.d.ts +3 -3
- package/dist/contracts/transaction-capability.d.ts +1 -1
- package/dist/contracts/wire-scalar-format.d.ts +4 -4
- package/dist/framework/contract-view.d.ts +213 -147
- package/dist/framework/framework-construction.error.d.ts +10 -10
- package/dist/framework/framework-construction.error.js +2 -2
- package/dist/framework/framework.d.ts +47 -23
- package/dist/framework/framework.factory.d.ts +8 -6
- package/dist/framework/framework.factory.js +40 -20
- package/dist/framework/operation-facade.d.ts +123 -52
- package/dist/framework/operation-facade.js +9 -1
- package/dist/index.d.ts +55 -50
- package/dist/index.js +6 -6
- package/dist/internal.d.ts +15 -10
- package/dist/internal.js +3 -1
- package/dist/operations/operation-arguments.d.ts +267 -202
- package/dist/operations/operation-call.d.ts +27 -28
- package/dist/operations/operation-identity.d.ts +10 -10
- package/dist/operations/operation-result.d.ts +21 -21
- package/dist/operations/operation-validator-core.d.ts +6 -6
- package/dist/operations/operation-validator-core.js +13 -2
- package/dist/operations/operation-validator.d.ts +9 -9
- package/dist/operations/projection-scope.resolver.d.ts +6 -6
- package/dist/operations/structural-limits.walker.d.ts +6 -6
- package/dist/operations/structural-limits.walker.js +145 -0
- package/dist/operations/validation-code.d.ts +11 -6
- package/dist/operations/validation-code.js +4 -0
- package/dist/operations/write-scope.resolver.d.ts +6 -6
- package/dist/protocol/absent-route.responder.d.ts +13 -12
- package/dist/protocol/absent-route.responder.js +19 -19
- package/dist/protocol/contract-document.responder.d.ts +3 -3
- package/dist/protocol/contract-document.responder.js +2 -2
- package/dist/protocol/http-request.decoder.d.ts +17 -13
- package/dist/protocol/http-request.decoder.js +10 -10
- package/dist/protocol/http-request.reader.d.ts +3 -3
- package/dist/protocol/http-response.encoder.d.ts +4 -4
- package/dist/protocol/http-response.encoder.js +94 -55
- package/dist/protocol/http-status.table.d.ts +2 -2
- package/dist/protocol/operation-response.guard.d.ts +2 -2
- package/dist/protocol/protocol-surface.d.ts +4 -4
- package/dist/protocol/protocol.binder.d.ts +50 -27
- package/dist/protocol/protocol.binder.js +12 -9
- package/dist/protocol/protocol.handler.d.ts +22 -13
- package/dist/protocol/protocol.handler.js +44 -9
- package/dist/protocol/unmounted-route.error.d.ts +4 -4
- package/dist/protocol/unmounted-route.error.js +2 -2
- package/dist/runtime/caller-data.copier.d.ts +5 -5
- package/dist/runtime/caller-data.copier.js +13 -13
- package/dist/runtime/caller-data.door.d.ts +6 -6
- package/dist/runtime/caller-data.door.js +6 -4
- package/dist/runtime/computed-read-runtime.d.ts +7 -6
- package/dist/runtime/computed-read-runtime.js +61 -48
- package/dist/runtime/computed-value.error.d.ts +6 -6
- package/dist/runtime/computed-value.error.js +3 -3
- package/dist/runtime/context-executor.runtime.d.ts +59 -0
- package/dist/runtime/context-executor.runtime.js +275 -0
- package/dist/runtime/create-requirement.d.ts +10 -81
- package/dist/runtime/create-requirement.js +80 -57
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +1 -1
- package/dist/runtime/diagnostics-reporter.d.ts +53 -27
- package/dist/runtime/diagnostics-reporter.js +24 -0
- package/dist/runtime/error-boundary.d.ts +65 -8
- package/dist/runtime/error-boundary.js +45 -19
- package/dist/runtime/execution-options.d.ts +10 -2
- package/dist/runtime/field-check.evaluator.d.ts +29 -0
- package/dist/runtime/field-check.evaluator.js +521 -0
- package/dist/runtime/field-compute.d.ts +64 -0
- package/dist/runtime/field-compute.js +1 -0
- package/dist/runtime/field-stage.plan.d.ts +15 -0
- package/dist/runtime/field-stage.plan.js +27 -0
- package/dist/runtime/field-stage.runtime.d.ts +35 -0
- package/dist/runtime/field-stage.runtime.js +143 -0
- package/dist/runtime/field-validator.runtime.d.ts +11 -0
- package/dist/runtime/field-validator.runtime.js +87 -0
- package/dist/runtime/framework-error-class.table.d.ts +33 -0
- package/dist/runtime/framework-error-class.table.js +25 -0
- package/dist/runtime/framework-error.d.ts +68 -8
- package/dist/runtime/framework-error.factory.d.ts +4 -0
- package/dist/runtime/framework-error.factory.js +15 -0
- package/dist/runtime/framework-error.js +23 -2
- package/dist/runtime/interceptor-next.error.d.ts +5 -0
- package/dist/runtime/interceptor-next.error.js +6 -0
- package/dist/runtime/json-value.d.ts +3 -3
- package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
- package/dist/runtime/level-lifecycle.runtime.js +42 -0
- package/dist/runtime/misuse-reporter.d.ts +12 -12
- package/dist/runtime/mutation-planner.d.ts +19 -10
- package/dist/runtime/mutation-planner.js +54 -30
- package/dist/runtime/nested-write.walker.d.ts +32 -0
- package/dist/runtime/nested-write.walker.js +73 -0
- package/dist/runtime/operation-request.d.ts +33 -6
- package/dist/runtime/operation-response.d.ts +17 -17
- package/dist/runtime/operation-runtime.d.ts +113 -25
- package/dist/runtime/operation-runtime.js +395 -74
- package/dist/runtime/outcome-mapper.d.ts +16 -5
- package/dist/runtime/outcome-mapper.js +23 -6
- package/dist/runtime/output-validator.d.ts +10 -10
- package/dist/runtime/output-validator.js +3 -3
- package/dist/runtime/owned-execution.registry.d.ts +6 -6
- package/dist/runtime/pipe-output.validator.d.ts +33 -18
- package/dist/runtime/pipe-output.validator.js +12 -12
- package/dist/runtime/pipeline.d.ts +196 -34
- package/dist/runtime/readonly-value.d.ts +32 -0
- package/dist/runtime/readonly-value.js +141 -0
- package/dist/runtime/request-envelope.validator.d.ts +3 -3
- package/dist/runtime/request-envelope.validator.js +12 -7
- package/dist/runtime/request-id.d.ts +1 -1
- package/dist/runtime/request-state.d.ts +14 -0
- package/dist/runtime/request-state.js +1 -0
- package/dist/runtime/request-state.resolver.d.ts +12 -0
- package/dist/runtime/request-state.resolver.js +20 -0
- package/dist/runtime/scalar-runtime.d.ts +31 -16
- package/dist/runtime/scalar-runtime.js +16 -18
- package/dist/transactions/deferred-operation.d.ts +35 -20
- package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
- package/dist/transactions/operation-arguments-encoder.js +2 -2
- package/dist/transactions/sql-statement.d.ts +77 -0
- package/dist/transactions/sql-statement.js +47 -0
- package/dist/transactions/sql-statement.validator.d.ts +44 -0
- package/dist/transactions/sql-statement.validator.js +134 -0
- package/dist/transactions/transaction-envelope.d.ts +4 -4
- package/dist/transactions/transaction-integrity-error.d.ts +6 -6
- package/dist/transactions/transaction-integrity-error.js +5 -2
- package/dist/transactions/transaction-plan-assembler.d.ts +21 -11
- package/dist/transactions/transaction-plan-assembler.js +148 -34
- package/dist/transactions/transaction-plan-validator.d.ts +29 -20
- package/dist/transactions/transaction-plan-validator.js +70 -38
- package/dist/transactions/transaction-reference-collector.d.ts +5 -5
- package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
- package/dist/transactions/transaction-reference-resolver.js +26 -14
- package/dist/transactions/transaction-reference-validator.d.ts +34 -4
- package/dist/transactions/transaction-reference-validator.js +58 -26
- package/dist/transactions/transaction-reference.d.ts +13 -13
- package/dist/transactions/transaction-request.d.ts +18 -5
- package/dist/transactions/transaction-response.d.ts +6 -6
- package/dist/transactions/transaction-runtime.d.ts +8 -7
- package/dist/transactions/transaction-runtime.js +166 -50
- package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
- package/dist/transactions/transaction-settlement.runtime.js +120 -0
- package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
- package/dist/transactions/transaction-slot-path-error.js +2 -2
- package/dist/transactions/transaction-slot-writer.js +2 -2
- package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
- package/dist/transactions/transaction-stage.runtime.js +123 -0
- package/package.json +6 -5
- package/src/runtime/field-check.evaluator.ts +720 -0
- package/dist/config/define-fields.d.ts +0 -42
- package/dist/config/define-fields.js +0 -3
- package/dist/config/framework-behaviors-config.d.ts +0 -30
- package/dist/config/framework-fields-config.d.ts +0 -42
- package/dist/config/framework-restrictions-config.d.ts +0 -142
- package/dist/json/avz.d.ts +0 -96
- package/dist/runtime/field-behavior.d.ts +0 -48
- /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
- /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
- /package/dist/{json → avz}/index.js +0 -0
- /package/dist/{json → avz}/json-shape.registry.js +0 -0
- /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
- /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
|
@@ -1,38 +1,146 @@
|
|
|
1
|
+
import { hasFieldValidators } from "../compiler/field-validator.compiler.js";
|
|
2
|
+
import { fieldStagesOf, isForcedOperation, nestedTargetLevels, operationLevels, rawPipelineTree, transactionLevelsOf, } from "../config/pipeline-tree.resolver.js";
|
|
1
3
|
import { AVENTARA_PROTOCOL_VERSION } from "../contracts/contract-protocol.js";
|
|
2
4
|
import { findOperationAvailabilityIssue, validateOperationArguments, } from "../operations/operation-validator.js";
|
|
5
|
+
import { transactionInfrastructureFailure } from "../transactions/transaction-response.js";
|
|
6
|
+
import { settleTransaction } from "../transactions/transaction-settlement.runtime.js";
|
|
7
|
+
import { TRANSACTION_PIPES, transactionFacts, } from "../transactions/transaction-stage.runtime.js";
|
|
3
8
|
import { copyAtDoor, misuseIdentityOf, refuseCallerData, } from "./caller-data.door.js";
|
|
4
9
|
import { materializeReadOperation, planReadOperation, } from "./computed-read-runtime.js";
|
|
10
|
+
import { createContextExecutor, } from "./context-executor.runtime.js";
|
|
5
11
|
import { createRequirementIssues, recordWriteFactsOf, } from "./create-requirement.js";
|
|
12
|
+
import { createDiagnosticsReporter, } from "./diagnostics-reporter.js";
|
|
6
13
|
import { AdapterBoundary, OPERATION_ARGUMENTS_PATH, resolveOperationFailure, sanitizeOperationFailure, } from "./error-boundary.js";
|
|
14
|
+
import { planFieldStages } from "./field-stage.plan.js";
|
|
15
|
+
import { guardSentFields, pipeSentFields, valueAt, writeRecordPathsOf, } from "./field-stage.runtime.js";
|
|
16
|
+
import { fieldValidatorIssues } from "./field-validator.runtime.js";
|
|
17
|
+
import { thrownFailure } from "./framework-error.factory.js";
|
|
18
|
+
import { AvInterceptorNextError } from "./interceptor-next.error.js";
|
|
19
|
+
import { runLevelLifecycle, } from "./level-lifecycle.runtime.js";
|
|
7
20
|
import { createMisuseReporter, } from "./misuse-reporter.js";
|
|
8
|
-
import {
|
|
21
|
+
import { applyNestedWriteCompute, applyWriteCompute, stripVirtualMutationInput, } from "./mutation-planner.js";
|
|
22
|
+
import { nestedWriteTargetsOf, } from "./nested-write.walker.js";
|
|
9
23
|
import { internalFailure, operationFailure } from "./operation-response.js";
|
|
10
|
-
import { mapOperationOutcome, mapValidationOutcome } from "./outcome-mapper.js";
|
|
11
|
-
import {
|
|
12
|
-
import { refusePipeOutput } from "./pipe-output.validator.js";
|
|
24
|
+
import { mapOperationOutcome, mapValidationOutcome, uniqueMissFailure, } from "./outcome-mapper.js";
|
|
25
|
+
import { AvOutputValidationError, validateOperationOutput, } from "./output-validator.js";
|
|
26
|
+
import { refusePipeOutput, refuseStageOutput, } from "./pipe-output.validator.js";
|
|
27
|
+
import { deepFreeze, frozenCopy } from "./readonly-value.js";
|
|
28
|
+
import { requestStateOf } from "./request-state.resolver.js";
|
|
13
29
|
import { normalizeOperationScalarValues, } from "./scalar-runtime.js";
|
|
30
|
+
function scopeOf(request, binding) {
|
|
31
|
+
return {
|
|
32
|
+
requestId: request.requestId,
|
|
33
|
+
transaction: binding?.kind === "transaction" ? binding.executor : undefined,
|
|
34
|
+
state: request.state,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function withRequestState(request) {
|
|
38
|
+
return { ...request, state: requestStateOf(request.state) };
|
|
39
|
+
}
|
|
40
|
+
export function createPipelineDependencies(dependencies, model) {
|
|
41
|
+
const misuse = createMisuseReporter(dependencies.config.misuse);
|
|
42
|
+
const recordWrites = recordWriteFactsOf(dependencies.adapter);
|
|
43
|
+
const contexts = createContextExecutor({
|
|
44
|
+
model,
|
|
45
|
+
adapter: dependencies.adapter,
|
|
46
|
+
application: dependencies.contracts.application,
|
|
47
|
+
diagnostics: dependencies.config.diagnostics,
|
|
48
|
+
misuse,
|
|
49
|
+
run: (request, transaction) => executeContextCall(raw, dependencies, request, transaction, misuse),
|
|
50
|
+
});
|
|
51
|
+
const { contracts, config } = dependencies;
|
|
52
|
+
const pipeline = {
|
|
53
|
+
contracts,
|
|
54
|
+
config,
|
|
55
|
+
recordWrites,
|
|
56
|
+
contexts,
|
|
57
|
+
fieldStages: {
|
|
58
|
+
application: planFieldStages(config.application.pipelines, contracts.application),
|
|
59
|
+
client: planFieldStages(config.client.pipelines, contracts.client),
|
|
60
|
+
agent: planFieldStages(config.agent.pipelines, contracts.agent),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
const rawTree = rawPipelineTree(config.application.pipelines);
|
|
64
|
+
const raw = {
|
|
65
|
+
...pipeline,
|
|
66
|
+
config: {
|
|
67
|
+
...config,
|
|
68
|
+
application: { ...config.application, pipelines: rawTree },
|
|
69
|
+
},
|
|
70
|
+
fieldStages: {
|
|
71
|
+
...pipeline.fieldStages,
|
|
72
|
+
application: planFieldStages(rawTree, contracts.application),
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
return pipeline;
|
|
76
|
+
}
|
|
77
|
+
async function executeContextCall(raw, dependencies, request, transaction, misuse) {
|
|
78
|
+
const report = createDiagnosticsReporter(dependencies.config.diagnostics, request);
|
|
79
|
+
try {
|
|
80
|
+
let validated;
|
|
81
|
+
try {
|
|
82
|
+
validated = validateOperation(dependencies, request, "caller", misuse);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return sanitizeOperationFailure(error, "runtime", report, {
|
|
86
|
+
path: OPERATION_ARGUMENTS_PATH,
|
|
87
|
+
arguments: request.arguments,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (!validated.ok) {
|
|
91
|
+
return validated.response;
|
|
92
|
+
}
|
|
93
|
+
const stateful = withRequestState(request);
|
|
94
|
+
const admitted = await admitOperation(raw, stateful, validated.contract, validated.arguments, OPERATION_ARGUMENTS_PATH, report, {
|
|
95
|
+
requestId: request.requestId,
|
|
96
|
+
transaction,
|
|
97
|
+
state: stateful.state,
|
|
98
|
+
});
|
|
99
|
+
if (!admitted.ok) {
|
|
100
|
+
return admitted.response;
|
|
101
|
+
}
|
|
102
|
+
return await executeAdmittedOperation(raw, stateful, validated.contract, admitted.args, OPERATION_ARGUMENTS_PATH, transaction === undefined
|
|
103
|
+
? { kind: "standalone", executor: dependencies.adapter }
|
|
104
|
+
: { kind: "transaction", executor: transaction }, report);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
report(error, "A3000");
|
|
108
|
+
return internalFailure();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
14
111
|
export function activeContract(contracts, request) {
|
|
15
|
-
return request.origin
|
|
16
|
-
? contracts.application
|
|
17
|
-
: contracts.client;
|
|
112
|
+
return contracts[request.origin];
|
|
18
113
|
}
|
|
19
114
|
function activeScopeConfig(config, request) {
|
|
20
|
-
return request.origin
|
|
115
|
+
return config[request.origin];
|
|
21
116
|
}
|
|
22
|
-
function
|
|
117
|
+
function operationFacts(contract, request, args) {
|
|
23
118
|
return {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
args,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
119
|
+
$operation: {
|
|
120
|
+
resource: request.resource,
|
|
121
|
+
family: request.family,
|
|
122
|
+
variant: request.variant,
|
|
123
|
+
},
|
|
124
|
+
$args: args,
|
|
125
|
+
$origin: request.origin,
|
|
126
|
+
$requestId: request.requestId,
|
|
127
|
+
$contract: contract,
|
|
128
|
+
...(request.origin !== "application" && request.transport !== undefined
|
|
129
|
+
? { $transport: request.transport }
|
|
33
130
|
: {}),
|
|
34
131
|
};
|
|
35
132
|
}
|
|
133
|
+
function nestedFacts(contract, request, target, payload) {
|
|
134
|
+
return {
|
|
135
|
+
...operationFacts(contract, request, payload),
|
|
136
|
+
$operation: {
|
|
137
|
+
resource: target.resource,
|
|
138
|
+
family: target.family,
|
|
139
|
+
variant: "one",
|
|
140
|
+
nested: true,
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
36
144
|
function forbidden() {
|
|
37
145
|
return operationFailure("A4001", {
|
|
38
146
|
message: "Operation denied by a framework guard.",
|
|
@@ -48,16 +156,11 @@ function outputAccepted(contract, request, projection, data, report) {
|
|
|
48
156
|
return false;
|
|
49
157
|
}
|
|
50
158
|
if (!validation.ok) {
|
|
51
|
-
report(new
|
|
159
|
+
report(new AvOutputValidationError(request, validation.issues), "A3000");
|
|
52
160
|
}
|
|
53
161
|
return validation.ok;
|
|
54
162
|
}
|
|
55
|
-
export function createOperationRuntime(dependencies) {
|
|
56
|
-
const pipelineDependencies = {
|
|
57
|
-
contracts: dependencies.contracts,
|
|
58
|
-
config: dependencies.config,
|
|
59
|
-
recordWrites: recordWriteFactsOf(dependencies.adapter),
|
|
60
|
-
};
|
|
163
|
+
export function createOperationRuntime(dependencies, pipelineDependencies) {
|
|
61
164
|
const misuse = createMisuseReporter(dependencies.config.misuse);
|
|
62
165
|
return async (request, report, source) => {
|
|
63
166
|
let validated;
|
|
@@ -73,7 +176,19 @@ export function createOperationRuntime(dependencies) {
|
|
|
73
176
|
if (!validated.ok) {
|
|
74
177
|
return validated.response;
|
|
75
178
|
}
|
|
76
|
-
|
|
179
|
+
const stateful = withRequestState(request);
|
|
180
|
+
const admitted = await admitOperation(pipelineDependencies, stateful, validated.contract, validated.arguments, OPERATION_ARGUMENTS_PATH, report, {
|
|
181
|
+
requestId: request.requestId,
|
|
182
|
+
transaction: undefined,
|
|
183
|
+
state: stateful.state,
|
|
184
|
+
});
|
|
185
|
+
if (!admitted.ok) {
|
|
186
|
+
return admitted.response;
|
|
187
|
+
}
|
|
188
|
+
if (isForcedOperation(activeScopeConfig(dependencies.config, request).pipelines, request)) {
|
|
189
|
+
return executeForcedOperation(pipelineDependencies, dependencies.adapter, stateful, validated.contract, admitted.args, report);
|
|
190
|
+
}
|
|
191
|
+
return executeAdmittedOperation(pipelineDependencies, stateful, validated.contract, admitted.args, OPERATION_ARGUMENTS_PATH, { kind: "standalone", executor: dependencies.adapter }, report);
|
|
77
192
|
};
|
|
78
193
|
}
|
|
79
194
|
function validateOperation(dependencies, request, source, misuse) {
|
|
@@ -100,19 +215,21 @@ function validateOperation(dependencies, request, source, misuse) {
|
|
|
100
215
|
return { ok: true, contract, arguments: validation.arguments };
|
|
101
216
|
}
|
|
102
217
|
export function validateProtocolIdentity(contracts, request) {
|
|
103
|
-
return request.origin === "
|
|
104
|
-
?
|
|
105
|
-
:
|
|
218
|
+
return request.origin === "application"
|
|
219
|
+
? undefined
|
|
220
|
+
: protocolIdentityFailure(contracts, request.protocol, request.origin);
|
|
106
221
|
}
|
|
107
|
-
export function protocolIdentityFailure(contracts, protocol) {
|
|
222
|
+
export function protocolIdentityFailure(contracts, protocol, origin) {
|
|
108
223
|
if (protocol.version !== AVENTARA_PROTOCOL_VERSION) {
|
|
109
224
|
return operationFailure("A2006", {
|
|
110
225
|
message: "Aventara protocol version does not match the server.",
|
|
111
226
|
});
|
|
112
227
|
}
|
|
113
|
-
if (protocol.hash !== contracts.
|
|
228
|
+
if (protocol.hash !== contracts[origin].protocol.hash) {
|
|
114
229
|
return operationFailure("A2005", {
|
|
115
|
-
message:
|
|
230
|
+
message: origin === "client"
|
|
231
|
+
? "Generated client contract does not match the server. Regenerate the client."
|
|
232
|
+
: "Agent contract does not match the server. Rebuild the agent's tools from the current Agent Contract.",
|
|
116
233
|
});
|
|
117
234
|
}
|
|
118
235
|
return undefined;
|
|
@@ -133,76 +250,280 @@ export function validateOperationRequest(contract, request, options, write = "co
|
|
|
133
250
|
function rejected(response) {
|
|
134
251
|
return { ok: false, response };
|
|
135
252
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
253
|
+
const FIELD_PIPES = "Field pipes";
|
|
254
|
+
const STEP_PIPES = "Pipes";
|
|
255
|
+
export const EVERY_FIELD = () => true;
|
|
256
|
+
function filtersOf(levels) {
|
|
257
|
+
return levels.flatMap((level) => level.filters);
|
|
258
|
+
}
|
|
259
|
+
const SINGLE_CALL = { select: EVERY_FIELD };
|
|
260
|
+
export async function admitOperation(dependencies, request, contract, args, argumentsPath, report, scope, admission = SINGLE_CALL) {
|
|
261
|
+
const contexts = dependencies.contexts;
|
|
262
|
+
const tree = activeScopeConfig(dependencies.config, request).pipelines;
|
|
263
|
+
const levels = operationLevels(tree, request);
|
|
264
|
+
const planned = dependencies.fieldStages[request.origin];
|
|
265
|
+
const guarded = planned.guarded(request.resource);
|
|
266
|
+
const contracts = { core: dependencies.contracts.core, active: contract };
|
|
267
|
+
let current = deepFreeze(args);
|
|
268
|
+
const context = () => contexts.operation(scope, operationFacts(contract, request, current));
|
|
269
|
+
const admit = async () => {
|
|
270
|
+
for (const level of levels) {
|
|
271
|
+
for (const guard of level.guards) {
|
|
272
|
+
if ((await guard(context())) === false) {
|
|
273
|
+
return { ok: false, response: forbidden() };
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (guarded.length > 0 &&
|
|
278
|
+
!(await guardSentFields(fieldStagesOf(tree, request.resource), guarded, writeRecordPathsOf(request.family, current), current, context))) {
|
|
279
|
+
return { ok: false, response: forbidden() };
|
|
280
|
+
}
|
|
281
|
+
for (const target of planned.nestedGuards
|
|
282
|
+
? nestedWriteTargetsOf(contract, dependencies.recordWrites.references, request, current)
|
|
283
|
+
: []) {
|
|
284
|
+
const told = () => contexts.operation(scope, nestedFacts(contract, request, target, valueAt(current, target.path)));
|
|
285
|
+
for (const level of nestedTargetLevels(tree, target.resource, target.family)) {
|
|
286
|
+
for (const guard of level.guards) {
|
|
287
|
+
if ((await guard(told())) === false) {
|
|
288
|
+
return { ok: false, response: forbidden() };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const targetGuarded = planned.guarded(target.resource);
|
|
293
|
+
if (targetGuarded.length > 0 &&
|
|
294
|
+
!(await guardSentFields(fieldStagesOf(tree, target.resource), targetGuarded, target.records.map((site) => site.path), current, told))) {
|
|
295
|
+
return { ok: false, response: forbidden() };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
let piped = false;
|
|
299
|
+
let pipeIndex = 0;
|
|
300
|
+
for (const level of levels) {
|
|
301
|
+
for (const pipe of level.pipes) {
|
|
302
|
+
const returned = await pipe(current, context());
|
|
303
|
+
if (returned !== undefined && returned !== current) {
|
|
304
|
+
const refused = refusePipeOutput(contracts, request, returned, pipe, pipeIndex, admission.validation);
|
|
305
|
+
if (refused !== undefined) {
|
|
306
|
+
report(refused.error, "A3004");
|
|
307
|
+
return { ok: false, response: refused.response };
|
|
308
|
+
}
|
|
309
|
+
current = deepFreeze(returned);
|
|
310
|
+
piped = true;
|
|
311
|
+
}
|
|
312
|
+
pipeIndex += 1;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const fields = await runFieldInputStages(dependencies, request, contract, current, argumentsPath, report, admission);
|
|
316
|
+
if (!fields.ok) {
|
|
317
|
+
return fields;
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
ok: true,
|
|
321
|
+
args: deepFreeze(fields.args),
|
|
322
|
+
piped: piped || fields.piped,
|
|
323
|
+
};
|
|
324
|
+
};
|
|
141
325
|
try {
|
|
142
|
-
return await
|
|
326
|
+
return await admit();
|
|
143
327
|
}
|
|
144
328
|
catch (error) {
|
|
145
|
-
return
|
|
329
|
+
return {
|
|
330
|
+
ok: false,
|
|
331
|
+
response: await resolveOperationFailure(error, filtersOf(levels), context(), new AdapterBoundary(), report, { path: argumentsPath, arguments: current }),
|
|
332
|
+
};
|
|
146
333
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
334
|
+
}
|
|
335
|
+
async function runFieldInputStages(dependencies, request, contract, args, argumentsPath, report, admission) {
|
|
336
|
+
const tree = activeScopeConfig(dependencies.config, request).pipelines;
|
|
337
|
+
const planned = dependencies.fieldStages[request.origin];
|
|
338
|
+
const pipes = planned.anyPipes;
|
|
339
|
+
if (!pipes && !hasFieldValidators(contract)) {
|
|
340
|
+
return { ok: true, args, piped: false };
|
|
341
|
+
}
|
|
342
|
+
let current = args;
|
|
343
|
+
let changed = false;
|
|
344
|
+
if (pipes) {
|
|
345
|
+
const pipedFields = planned.piped(request.resource);
|
|
346
|
+
let piped = pipedFields.length === 0
|
|
347
|
+
? { args: current, changed: false }
|
|
348
|
+
: await pipeSentFields(fieldStagesOf(tree, request.resource), pipedFields, writeRecordPathsOf(request.family, current), current, admission.select);
|
|
349
|
+
changed = piped.changed;
|
|
350
|
+
for (const target of nestedWriteTargetsOf(contract, dependencies.recordWrites.references, request, piped.args)) {
|
|
351
|
+
const targetPiped = planned.piped(target.resource);
|
|
352
|
+
if (targetPiped.length === 0) {
|
|
353
|
+
continue;
|
|
152
354
|
}
|
|
355
|
+
piped = await pipeSentFields(fieldStagesOf(tree, target.resource), targetPiped, target.records.map((site) => site.path), piped.args, admission.select);
|
|
356
|
+
changed ||= piped.changed;
|
|
153
357
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
358
|
+
current = piped.args;
|
|
359
|
+
if (changed) {
|
|
360
|
+
const refused = refuseStageOutput({ core: dependencies.contracts.core, active: contract }, request, current, FIELD_PIPES, admission.validation);
|
|
361
|
+
if (refused !== undefined) {
|
|
362
|
+
report(refused.error, "A3004");
|
|
363
|
+
return { ok: false, response: refused.response };
|
|
158
364
|
}
|
|
159
|
-
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const broken = fieldValidatorIssues(contract, dependencies.recordWrites.references, request, current, argumentsPath, admission.select);
|
|
368
|
+
if (broken.length > 0) {
|
|
369
|
+
return { ok: false, response: mapValidationOutcome(broken) };
|
|
370
|
+
}
|
|
371
|
+
return { ok: true, args: current, piped: changed };
|
|
372
|
+
}
|
|
373
|
+
export async function prepareStep(dependencies, request, contract, args, argumentsPath, report, executor, changes, outer) {
|
|
374
|
+
let current = deepFreeze(args);
|
|
375
|
+
const scope = {
|
|
376
|
+
requestId: request.requestId,
|
|
377
|
+
transaction: executor,
|
|
378
|
+
state: request.state,
|
|
379
|
+
};
|
|
380
|
+
try {
|
|
381
|
+
const resolved = await runFieldInputStages(dependencies, request, contract, current, argumentsPath, report, { select: changes.written });
|
|
382
|
+
if (!resolved.ok) {
|
|
383
|
+
return resolved;
|
|
384
|
+
}
|
|
385
|
+
current = resolved.args;
|
|
386
|
+
const subject = changes.transactionPiped
|
|
387
|
+
? TRANSACTION_PIPES
|
|
388
|
+
: changes.pipedAroundReferences
|
|
389
|
+
? STEP_PIPES
|
|
390
|
+
: undefined;
|
|
391
|
+
if (subject !== undefined) {
|
|
392
|
+
const refused = refuseStageOutput({ core: dependencies.contracts.core, active: contract }, request, current, subject);
|
|
160
393
|
if (refused !== undefined) {
|
|
161
394
|
report(refused.error, "A3004");
|
|
162
|
-
return refused.response;
|
|
395
|
+
return { ok: false, response: refused.response };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (changes.transactionPiped) {
|
|
399
|
+
const broken = fieldValidatorIssues(contract, dependencies.recordWrites.references, request, current, argumentsPath, EVERY_FIELD);
|
|
400
|
+
if (broken.length > 0) {
|
|
401
|
+
return { ok: false, response: mapValidationOutcome(broken) };
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return { ok: true, args: deepFreeze(current), piped: resolved.piped };
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
return {
|
|
408
|
+
ok: false,
|
|
409
|
+
response: await resolveOperationFailure(error, filtersOf(operationLevels(activeScopeConfig(dependencies.config, request).pipelines, request)), dependencies.contexts.operation(scope, operationFacts(contract, request, current)), new AdapterBoundary(), report, { path: argumentsPath, arguments: current }, outer),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
async function executeForcedOperation(dependencies, adapter, request, contract, args, report) {
|
|
414
|
+
const plan = [
|
|
415
|
+
{
|
|
416
|
+
resource: request.resource,
|
|
417
|
+
family: request.family,
|
|
418
|
+
variant: request.variant,
|
|
419
|
+
args,
|
|
420
|
+
},
|
|
421
|
+
];
|
|
422
|
+
const settled = await settleTransaction({
|
|
423
|
+
adapter,
|
|
424
|
+
levels: transactionLevelsOf(activeScopeConfig(dependencies.config, request).pipelines, request.resource),
|
|
425
|
+
plan,
|
|
426
|
+
contextOf: (current, executor) => dependencies.contexts.transaction({
|
|
427
|
+
requestId: request.requestId,
|
|
428
|
+
transaction: executor,
|
|
429
|
+
state: request.state,
|
|
430
|
+
}, transactionFacts(contract, request, current)),
|
|
431
|
+
failureView: { path: OPERATION_ARGUMENTS_PATH, arguments: args },
|
|
432
|
+
report,
|
|
433
|
+
runStep: async (_index, step, piped, executor, _results, outer) => {
|
|
434
|
+
let stepArguments = step.args;
|
|
435
|
+
if (piped) {
|
|
436
|
+
const prepared = await prepareStep(dependencies, request, contract, stepArguments, OPERATION_ARGUMENTS_PATH, report, executor, {
|
|
437
|
+
written: () => false,
|
|
438
|
+
transactionPiped: true,
|
|
439
|
+
pipedAroundReferences: false,
|
|
440
|
+
}, outer);
|
|
441
|
+
if (!prepared.ok) {
|
|
442
|
+
return prepared.response;
|
|
443
|
+
}
|
|
444
|
+
stepArguments = prepared.args;
|
|
163
445
|
}
|
|
446
|
+
return executeAdmittedOperation(dependencies, request, contract, stepArguments, OPERATION_ARGUMENTS_PATH, { kind: "transaction", executor, outer }, report);
|
|
447
|
+
},
|
|
448
|
+
});
|
|
449
|
+
switch (settled.kind) {
|
|
450
|
+
case "committed":
|
|
451
|
+
return settled.responses[0] ?? internalFailure();
|
|
452
|
+
case "rolled-back":
|
|
453
|
+
return settled.response;
|
|
454
|
+
case "broken":
|
|
455
|
+
report(settled.error, "A3002");
|
|
456
|
+
return transactionInfrastructureFailure();
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
export async function executeAdmittedOperation(dependencies, request, contract, admittedArguments, argumentsPath, binding, report) {
|
|
460
|
+
const { executor } = binding;
|
|
461
|
+
const scope = scopeOf(request, binding);
|
|
462
|
+
const contexts = dependencies.contexts;
|
|
463
|
+
const scopeConfig = activeScopeConfig(dependencies.config, request);
|
|
464
|
+
const levels = operationLevels(scopeConfig.pipelines, request);
|
|
465
|
+
const adapterBoundary = new AdapterBoundary();
|
|
466
|
+
const contracts = { core: dependencies.contracts.core, active: contract };
|
|
467
|
+
let args = deepFreeze(admittedArguments);
|
|
468
|
+
let told;
|
|
469
|
+
const view = () => {
|
|
470
|
+
if (told === undefined || told.of !== args) {
|
|
471
|
+
told = { of: args, view: frozenCopy(args) };
|
|
164
472
|
}
|
|
165
|
-
|
|
166
|
-
|
|
473
|
+
return told.view;
|
|
474
|
+
};
|
|
475
|
+
const context = () => contexts.operation(scope, operationFacts(contract, request, view()));
|
|
476
|
+
const computeContext = () => contexts.compute(scope, operationFacts(contract, request, view()));
|
|
477
|
+
const reuse = { refusal: undefined };
|
|
478
|
+
const refusedReuse = (refusal) => {
|
|
479
|
+
report(refusal, "A3000");
|
|
480
|
+
return internalFailure();
|
|
481
|
+
};
|
|
482
|
+
let response;
|
|
483
|
+
try {
|
|
484
|
+
response = await runLifecycle();
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
if (reuse.refusal !== undefined) {
|
|
488
|
+
return refusedReuse(reuse.refusal);
|
|
489
|
+
}
|
|
490
|
+
return resolveOperationFailure(error, filtersOf(levels), context(), adapterBoundary, report, { path: argumentsPath, arguments: args }, binding.kind === "transaction" ? binding.outer : undefined);
|
|
491
|
+
}
|
|
492
|
+
return reuse.refusal === undefined ? response : refusedReuse(reuse.refusal);
|
|
493
|
+
async function runLifecycle() {
|
|
494
|
+
let computed = await applyWriteCompute(contracts, scopeConfig.compute, request, args, computeContext);
|
|
495
|
+
computed = await applyNestedWriteCompute(contracts, scopeConfig.compute, computed, nestedWriteTargetsOf(contract, dependencies.recordWrites.references, request, computed), (target, payload) => contexts.compute(scope, nestedFacts(contract, request, target, payload)));
|
|
496
|
+
const missing = createRequirementIssues(contract, dependencies.recordWrites, request, computed, argumentsPath);
|
|
167
497
|
if (missing.length > 0) {
|
|
168
498
|
return mapValidationOutcome(missing);
|
|
169
499
|
}
|
|
170
|
-
|
|
171
|
-
await hook(pipelineContext(contract, request, args));
|
|
172
|
-
}
|
|
500
|
+
args = deepFreeze(computed);
|
|
173
501
|
let readMaterialized = false;
|
|
174
502
|
let executedReadPlan;
|
|
175
503
|
const materialize = async (data, plan) => {
|
|
176
504
|
readMaterialized = true;
|
|
177
505
|
executedReadPlan = plan;
|
|
178
|
-
return materializeReadOperation(contract, scopeConfig.
|
|
506
|
+
return materializeReadOperation(contract, scopeConfig.compute, request, plan, data, computeContext);
|
|
179
507
|
};
|
|
180
|
-
|
|
181
|
-
const readPlan = planReadOperation(dependencies.contracts.core, contract, scopeConfig.
|
|
182
|
-
const adapterArguments = await stripVirtualMutationInput(
|
|
508
|
+
const invoke = async () => {
|
|
509
|
+
const readPlan = planReadOperation(dependencies.contracts.core, dependencies.config.adapter.model, contract, scopeConfig.compute, request, args);
|
|
510
|
+
const adapterArguments = await stripVirtualMutationInput(contracts, request, readPlan.arguments);
|
|
183
511
|
const identity = request;
|
|
184
512
|
const adapterData = await adapterBoundary.execute(() => executor.execute({
|
|
185
513
|
...identity,
|
|
186
514
|
resource: request.resource,
|
|
187
515
|
arguments: adapterArguments,
|
|
188
516
|
}));
|
|
517
|
+
const miss = uniqueMissFailure(request, adapterData);
|
|
518
|
+
if (miss !== undefined) {
|
|
519
|
+
throw thrownFailure(miss);
|
|
520
|
+
}
|
|
189
521
|
return materialize(adapterData, readPlan);
|
|
190
522
|
};
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
const next = invoke;
|
|
197
|
-
invoke = () => interceptor(pipelineContext(contract, request, args), next);
|
|
198
|
-
}
|
|
199
|
-
let data = await invoke();
|
|
200
|
-
if (!readMaterialized) {
|
|
201
|
-
data = await materialize(data, planReadOperation(dependencies.contracts.core, contract, scopeConfig.behaviors, request, args));
|
|
202
|
-
}
|
|
203
|
-
for (const hook of pipelines.hooks.after) {
|
|
204
|
-
await hook(data, pipelineContext(contract, request, args));
|
|
205
|
-
}
|
|
523
|
+
const materialized = async (data) => readMaterialized
|
|
524
|
+
? data
|
|
525
|
+
: materialize(data, planReadOperation(dependencies.contracts.core, dependencies.config.adapter.model, contract, scopeConfig.compute, request, args));
|
|
526
|
+
const data = await materialized(await runLevelLifecycle(levels, context, invoke, materialized, reuse, () => new AvInterceptorNextError()));
|
|
206
527
|
if (!outputAccepted(contract, request, executedReadPlan?.projection, data, report)) {
|
|
207
528
|
return internalFailure();
|
|
208
529
|
}
|
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
1
|
+
import type { AvOperationIdentity } from "../operations/operation-identity.js";
|
|
2
|
+
import type { AvValidationIssue } from "../operations/validation-code.js";
|
|
3
|
+
import type { AvOperationFailureResponse, AvOperationResponse } from "./operation-response.js";
|
|
4
|
+
/**
|
|
5
|
+
* The failure a strict unique operation's miss raises inside the pipeline, or
|
|
6
|
+
* `undefined` when `data` is a row, or the variant answers a miss with `null`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function uniqueMissFailure(operation: AvOperationIdentity, data: unknown): AvOperationFailureResponse | undefined;
|
|
4
9
|
/**
|
|
5
10
|
* Maps one validated operation result to its stable outcome envelope. The
|
|
6
11
|
* result must already have passed output validation, so `null` can only
|
|
7
12
|
* reach a variant whose cardinality admits it.
|
|
8
13
|
*/
|
|
9
|
-
export declare function mapOperationOutcome(operation:
|
|
14
|
+
export declare function mapOperationOutcome(operation: AvOperationIdentity, data: unknown): AvOperationResponse;
|
|
15
|
+
/** The A-codes a set of Framework Validation Layer issues can roll up to. */
|
|
16
|
+
export type ValidationOutcomeCode = "A2001" | "A2002" | "A2004" | "A2007" | "A2009";
|
|
17
|
+
/** The framework's top-level `cause.message` for each validation outcome code. */
|
|
18
|
+
export declare const VALIDATION_OUTCOME_MESSAGES: {
|
|
19
|
+
readonly [K in ValidationOutcomeCode]: string;
|
|
20
|
+
};
|
|
10
21
|
/** Maps framework validation issues to their stable failure envelope. */
|
|
11
|
-
export declare function mapValidationOutcome(issues: readonly
|
|
22
|
+
export declare function mapValidationOutcome(issues: readonly AvValidationIssue[]): AvOperationFailureResponse;
|
|
@@ -21,18 +21,26 @@ function outcomeRule(operation) {
|
|
|
21
21
|
const table = OUTCOME_TABLE;
|
|
22
22
|
return table[`${operation.family}.${operation.variant}`];
|
|
23
23
|
}
|
|
24
|
+
function notFound() {
|
|
25
|
+
return operationFailure("A2003", {
|
|
26
|
+
message: "No record matched the unique selector.",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
24
29
|
function absenceOutcome(absence) {
|
|
25
30
|
switch (absence) {
|
|
26
31
|
case "no-match":
|
|
27
32
|
return operationNoMatch();
|
|
28
33
|
case "not-found":
|
|
29
|
-
return
|
|
30
|
-
message: "No record matched the unique selector.",
|
|
31
|
-
});
|
|
34
|
+
return notFound();
|
|
32
35
|
case undefined:
|
|
33
36
|
return internalFailure();
|
|
34
37
|
}
|
|
35
38
|
}
|
|
39
|
+
export function uniqueMissFailure(operation, data) {
|
|
40
|
+
return data === null && outcomeRule(operation)?.absence === "not-found"
|
|
41
|
+
? notFound()
|
|
42
|
+
: undefined;
|
|
43
|
+
}
|
|
36
44
|
export function mapOperationOutcome(operation, data) {
|
|
37
45
|
const rule = outcomeRule(operation);
|
|
38
46
|
if (rule === undefined) {
|
|
@@ -52,6 +60,13 @@ function namesUnavailableOperation(issue) {
|
|
|
52
60
|
const head = stepRelativeHead(issue);
|
|
53
61
|
return issue.code === "V1006" && (head === "family" || head === "variant");
|
|
54
62
|
}
|
|
63
|
+
export const VALIDATION_OUTCOME_MESSAGES = {
|
|
64
|
+
A2001: "The requested Resource is not available.",
|
|
65
|
+
A2002: "The requested operation is not available on this Resource.",
|
|
66
|
+
A2004: "Operation arguments failed framework validation.",
|
|
67
|
+
A2007: "Operation arguments failed framework validation.",
|
|
68
|
+
A2009: "Operation arguments failed framework validation.",
|
|
69
|
+
};
|
|
55
70
|
function validationCode(issues) {
|
|
56
71
|
if (issues.some((issue) => issue.code !== "V1019" && stepRelativeHead(issue) === "resource")) {
|
|
57
72
|
return "A2001";
|
|
@@ -68,14 +83,16 @@ function validationCode(issues) {
|
|
|
68
83
|
}
|
|
69
84
|
if (issues.some((issue) => issue.code === "V1013" ||
|
|
70
85
|
issue.code === "V1014" ||
|
|
71
|
-
issue.code === "V1015"
|
|
86
|
+
issue.code === "V1015" ||
|
|
87
|
+
issue.code === "V1021")) {
|
|
72
88
|
return "A2009";
|
|
73
89
|
}
|
|
74
90
|
return "A2004";
|
|
75
91
|
}
|
|
76
92
|
export function mapValidationOutcome(issues) {
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
const code = validationCode(issues);
|
|
94
|
+
return operationFailure(code, {
|
|
95
|
+
message: VALIDATION_OUTCOME_MESSAGES[code],
|
|
79
96
|
issues,
|
|
80
97
|
});
|
|
81
98
|
}
|