@aventara/core 1.0.0-rc.3 → 1.0.0-rc.4
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 +130 -1
- package/ERRORS.md +6 -6
- package/README.md +72 -12
- package/dist/adapters/adapter-operation.d.ts +7 -0
- package/dist/compiler/contract.compiler.d.ts +6 -0
- package/dist/compiler/contract.compiler.js +73 -12
- package/dist/config/computed-fields.js +2 -9
- package/dist/config/config-diagnostic.guidance.js +3 -3
- package/dist/config/config.resolver.d.ts +14 -0
- package/dist/config/config.resolver.js +31 -0
- package/dist/config/config.validator.js +82 -0
- package/dist/config/exact-framework-config.d.ts +5 -0
- package/dist/config/framework-config.d.ts +26 -1
- package/dist/contracts/client-contract-structure.validator.d.ts +8 -0
- package/dist/contracts/client-contract-structure.validator.js +6 -1
- package/dist/contracts/field.d.ts +11 -0
- package/dist/framework/contract-view.d.ts +41 -10
- package/dist/framework/framework.factory.js +39 -11
- package/dist/framework/operation-facade.d.ts +24 -5
- package/dist/framework/operation-facade.js +2 -1
- package/dist/index.d.ts +4 -1
- package/dist/internal.d.ts +1 -0
- package/dist/internal.js +1 -0
- package/dist/operations/operation-arguments.d.ts +1 -1
- package/dist/operations/operation-call.d.ts +9 -5
- package/dist/operations/operation-result.d.ts +5 -3
- package/dist/operations/operation-validator-core.js +6 -0
- package/dist/policy/policy-admission.d.ts +22 -0
- package/dist/policy/policy-admission.js +400 -0
- package/dist/policy/policy-after-update.runtime.d.ts +29 -0
- package/dist/policy/policy-after-update.runtime.js +131 -0
- package/dist/policy/policy-condition.d.ts +15 -0
- package/dist/policy/policy-condition.js +36 -0
- package/dist/policy/policy-engine.d.ts +111 -0
- package/dist/policy/policy-engine.js +1 -0
- package/dist/policy/policy-nested-write.runtime.d.ts +38 -0
- package/dist/policy/policy-nested-write.runtime.js +449 -0
- package/dist/policy/policy-read.runtime.d.ts +83 -0
- package/dist/policy/policy-read.runtime.js +494 -0
- package/dist/policy/policy-request.d.ts +37 -0
- package/dist/policy/policy-request.js +52 -0
- package/dist/policy/policy-session.d.ts +83 -0
- package/dist/policy/policy-session.js +234 -0
- package/dist/policy/policy-startup.validator.d.ts +8 -0
- package/dist/policy/policy-startup.validator.js +106 -0
- package/dist/policy/policy-write.runtime.d.ts +30 -0
- package/dist/policy/policy-write.runtime.js +394 -0
- package/dist/prebuilt/core-version.generated.d.ts +2 -0
- package/dist/prebuilt/core-version.generated.js +1 -0
- package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
- package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
- package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
- package/dist/prebuilt/prebuilt-contracts.js +1 -0
- package/dist/prebuilt/prebuilt-contracts.loader.d.ts +21 -0
- package/dist/prebuilt/prebuilt-contracts.loader.js +122 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.js +192 -0
- package/dist/runtime/computed-read-runtime.d.ts +6 -1
- package/dist/runtime/computed-read-runtime.js +224 -45
- package/dist/runtime/context-executor.runtime.js +4 -2
- package/dist/runtime/execution-options.d.ts +8 -0
- package/dist/runtime/framework-warning.emitter.d.ts +6 -0
- package/dist/runtime/framework-warning.emitter.js +8 -0
- package/dist/runtime/level-lifecycle.runtime.js +3 -2
- package/dist/runtime/operation-code.guidance.js +3 -3
- package/dist/runtime/operation-request.d.ts +7 -0
- package/dist/runtime/operation-runtime.d.ts +9 -1
- package/dist/runtime/operation-runtime.js +114 -10
- package/dist/runtime/readonly-value.d.ts +5 -1
- package/dist/runtime/readonly-value.js +67 -28
- package/dist/transactions/transaction-runtime.d.ts +4 -1
- package/dist/transactions/transaction-runtime.js +27 -5
- package/package.json +2 -2
|
@@ -3,6 +3,9 @@ import { createComputedFields } from "../config/field-compute.resolver.js";
|
|
|
3
3
|
import { fieldStagesOf, isForcedOperation, nestedTargetLevels, operationLevels, rawPipelineTree, transactionLevelsOf, } from "../config/pipeline-tree.resolver.js";
|
|
4
4
|
import { AVENTARA_PROTOCOL_VERSION } from "../contracts/contract-protocol.js";
|
|
5
5
|
import { findOperationAvailabilityIssue, validateOperationArguments, } from "../operations/operation-validator.js";
|
|
6
|
+
import { enforceReadPolicy, } from "../policy/policy-read.runtime.js";
|
|
7
|
+
import { admitRequestPolicy, openRequestPolicy, } from "../policy/policy-request.js";
|
|
8
|
+
import { enforceWritePolicy, writeNeedsTransaction, } from "../policy/policy-write.runtime.js";
|
|
6
9
|
import { transactionInfrastructureFailure } from "../transactions/transaction-response.js";
|
|
7
10
|
import { settleTransaction } from "../transactions/transaction-settlement.runtime.js";
|
|
8
11
|
import { TRANSACTION_PIPES, transactionFacts, } from "../transactions/transaction-stage.runtime.js";
|
|
@@ -40,6 +43,16 @@ function scopeOf(request, binding) {
|
|
|
40
43
|
export function withRequestState(request) {
|
|
41
44
|
return { ...request, state: requestStateOf(request.state) };
|
|
42
45
|
}
|
|
46
|
+
export function withRequestPolicy(dependencies, request, caller, report) {
|
|
47
|
+
try {
|
|
48
|
+
const policy = openRequestPolicy(dependencies.config.policy, dependencies.contracts.core, request, caller, dependencies.adapter);
|
|
49
|
+
return policy === undefined ? request : { ...request, policy };
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
report(error, "A3000");
|
|
53
|
+
return internalFailure();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
43
56
|
export function createPipelineDependencies(dependencies, model) {
|
|
44
57
|
const misuse = createMisuseReporter(dependencies.config.misuse);
|
|
45
58
|
const recordWrites = recordWriteFactsOf({
|
|
@@ -170,7 +183,7 @@ function outputAccepted(contract, request, projection, data, report) {
|
|
|
170
183
|
}
|
|
171
184
|
export function createOperationRuntime(dependencies, pipelineDependencies) {
|
|
172
185
|
const misuse = createMisuseReporter(dependencies.config.misuse);
|
|
173
|
-
return async (request, report, source) => {
|
|
186
|
+
return async (request, report, source, caller) => {
|
|
174
187
|
let validated;
|
|
175
188
|
try {
|
|
176
189
|
validated = validateOperation(dependencies, request, source, misuse);
|
|
@@ -184,7 +197,10 @@ export function createOperationRuntime(dependencies, pipelineDependencies) {
|
|
|
184
197
|
if (!validated.ok) {
|
|
185
198
|
return validated.response;
|
|
186
199
|
}
|
|
187
|
-
const stateful = withRequestState(request);
|
|
200
|
+
const stateful = withRequestPolicy(dependencies, withRequestState(request), caller, report);
|
|
201
|
+
if (!("state" in stateful)) {
|
|
202
|
+
return stateful;
|
|
203
|
+
}
|
|
188
204
|
const forced = isForcedOperation(activeScopeConfig(dependencies.config, request).pipelines, request);
|
|
189
205
|
const admitted = await admitOperation(pipelineDependencies, stateful, validated.contract, validated.arguments, OPERATION_ARGUMENTS_PATH, report, {
|
|
190
206
|
requestId: request.requestId,
|
|
@@ -194,8 +210,20 @@ export function createOperationRuntime(dependencies, pipelineDependencies) {
|
|
|
194
210
|
if (!admitted.ok) {
|
|
195
211
|
return admitted.response;
|
|
196
212
|
}
|
|
197
|
-
|
|
198
|
-
|
|
213
|
+
let checkedWrite = false;
|
|
214
|
+
if (stateful.policy !== undefined &&
|
|
215
|
+
request.family !== "find" &&
|
|
216
|
+
validated.contract.transactions === "interactive" &&
|
|
217
|
+
!forced) {
|
|
218
|
+
try {
|
|
219
|
+
checkedWrite = await writeNeedsTransaction(stateful.policy, dependencies.contracts.core, stateful, () => planReadOperation(dependencies.contracts.core, dependencies.config.adapter.model, validated.contract, activeScopeConfig(dependencies.config, request).compute, stateful, admitted.args), admitted.args);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
return sanitizeOperationFailure(error, stateful.policy.execution.boundary.originOf(error), report, { path: OPERATION_ARGUMENTS_PATH, arguments: admitted.args });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (forced || checkedWrite) {
|
|
226
|
+
return executeForcedOperation(pipelineDependencies, dependencies.adapter, stateful, validated.contract, admitted.args, report, forced);
|
|
199
227
|
}
|
|
200
228
|
return executeAdmittedOperation(pipelineDependencies, stateful, validated.contract, admitted.args, OPERATION_ARGUMENTS_PATH, { kind: "standalone", executor: dependencies.adapter }, report);
|
|
201
229
|
};
|
|
@@ -346,6 +374,12 @@ export async function admitOperation(dependencies, request, contract, args, argu
|
|
|
346
374
|
if (absent.length > 0) {
|
|
347
375
|
return { ok: false, response: mapValidationOutcome(absent) };
|
|
348
376
|
}
|
|
377
|
+
if (request.policy !== undefined) {
|
|
378
|
+
const refused = await admitRequestPolicy(request.policy, dependencies.contracts.core, contract, request, current, report, { path: argumentsPath, arguments: current });
|
|
379
|
+
if (refused !== undefined) {
|
|
380
|
+
return { ok: false, response: refused };
|
|
381
|
+
}
|
|
382
|
+
}
|
|
349
383
|
try {
|
|
350
384
|
return await admit();
|
|
351
385
|
}
|
|
@@ -441,7 +475,7 @@ export async function prepareStep(dependencies, request, contract, args, argumen
|
|
|
441
475
|
};
|
|
442
476
|
}
|
|
443
477
|
}
|
|
444
|
-
async function executeForcedOperation(dependencies, adapter, request, contract, args, report) {
|
|
478
|
+
async function executeForcedOperation(dependencies, adapter, request, contract, args, report, forced) {
|
|
445
479
|
const plan = [
|
|
446
480
|
{
|
|
447
481
|
resource: request.resource,
|
|
@@ -452,7 +486,9 @@ async function executeForcedOperation(dependencies, adapter, request, contract,
|
|
|
452
486
|
];
|
|
453
487
|
const settled = await settleTransaction({
|
|
454
488
|
adapter,
|
|
455
|
-
levels:
|
|
489
|
+
levels: forced
|
|
490
|
+
? transactionLevelsOf(activeScopeConfig(dependencies.config, request).pipelines, request.resource)
|
|
491
|
+
: [],
|
|
456
492
|
plan,
|
|
457
493
|
contextOf: (current, executor) => dependencies.contexts.transaction({
|
|
458
494
|
requestId: request.requestId,
|
|
@@ -544,6 +580,25 @@ export async function executeAdmittedOperation(dependencies, request, contract,
|
|
|
544
580
|
throw hiddenRefusal;
|
|
545
581
|
}
|
|
546
582
|
args = deepFreeze(computed);
|
|
583
|
+
let policyWrite;
|
|
584
|
+
if (request.policy !== undefined && request.family !== "find") {
|
|
585
|
+
const planned = planReadOperation(dependencies.contracts.core, dependencies.config.adapter.model, contract, scopeConfig.compute, request, args);
|
|
586
|
+
const plannedArguments = await stripVirtualMutationInput(contracts, request, planned.arguments);
|
|
587
|
+
try {
|
|
588
|
+
policyWrite = await enforceWritePolicy(request.policy.on({ executor, boundary: adapterBoundary }), dependencies.contracts.core, dependencies.config.adapter.model, request, planned, plannedArguments, binding.kind === "transaction");
|
|
589
|
+
}
|
|
590
|
+
catch (error) {
|
|
591
|
+
return sanitizeOperationFailure(error, adapterBoundary.originOf(error), report, { path: argumentsPath, arguments: args });
|
|
592
|
+
}
|
|
593
|
+
if (policyWrite.kind === "refused") {
|
|
594
|
+
return policyWrite.response;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
let policyFailure;
|
|
598
|
+
const refuseWrite = (response) => {
|
|
599
|
+
policyFailure = response;
|
|
600
|
+
throw thrownFailure(response);
|
|
601
|
+
};
|
|
547
602
|
let readMaterialized = false;
|
|
548
603
|
let executedReadPlan;
|
|
549
604
|
const materialize = async (data, plan) => {
|
|
@@ -552,24 +607,73 @@ export async function executeAdmittedOperation(dependencies, request, contract,
|
|
|
552
607
|
return materializeReadOperation(contract, scopeConfig.compute, request, plan, data, computeContext);
|
|
553
608
|
};
|
|
554
609
|
const invoke = async () => {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
610
|
+
let readPlan = planReadOperation(dependencies.contracts.core, dependencies.config.adapter.model, contract, scopeConfig.compute, request, args);
|
|
611
|
+
let adapterArguments = await stripVirtualMutationInput(contracts, request, readPlan.arguments);
|
|
612
|
+
let operation = request;
|
|
613
|
+
let settle;
|
|
614
|
+
let writeMiss;
|
|
615
|
+
if (policyWrite !== undefined && policyWrite.kind !== "refused") {
|
|
616
|
+
writeMiss = policyWrite.miss;
|
|
617
|
+
if (policyWrite.kind === "empty") {
|
|
618
|
+
if (writeMiss !== undefined && policyWrite.data === null) {
|
|
619
|
+
refuseWrite(await writeMiss());
|
|
620
|
+
}
|
|
621
|
+
return materialize(policyWrite.data, readPlan);
|
|
622
|
+
}
|
|
623
|
+
operation = policyWrite.identity;
|
|
624
|
+
adapterArguments = policyWrite.arguments;
|
|
625
|
+
readPlan = policyWrite.plan;
|
|
626
|
+
settle = policyWrite.settle;
|
|
627
|
+
}
|
|
628
|
+
else if (request.policy !== undefined && request.family === "find") {
|
|
629
|
+
const read = await enforceReadPolicy(request.policy.on({ executor, boundary: adapterBoundary }), dependencies.contracts.core, dependencies.config.adapter.model, request, readPlan, adapterArguments);
|
|
630
|
+
if (read.kind === "refused") {
|
|
631
|
+
throw thrownFailure(read.response);
|
|
632
|
+
}
|
|
633
|
+
if (read.kind === "empty") {
|
|
634
|
+
const miss = uniqueMissFailure(request, read.data);
|
|
635
|
+
if (miss !== undefined) {
|
|
636
|
+
throw thrownFailure(miss);
|
|
637
|
+
}
|
|
638
|
+
return materialize(read.data, readPlan);
|
|
639
|
+
}
|
|
640
|
+
operation = read.identity;
|
|
641
|
+
adapterArguments = read.arguments;
|
|
642
|
+
readPlan = read.plan;
|
|
643
|
+
settle = read.settle;
|
|
644
|
+
}
|
|
645
|
+
const { policy: _session, ...identity } = operation;
|
|
646
|
+
let adapterData = await adapterBoundary.execute(() => executor.execute({
|
|
559
647
|
...identity,
|
|
560
648
|
resource: request.resource,
|
|
561
649
|
arguments: adapterArguments,
|
|
562
650
|
}));
|
|
651
|
+
if (writeMiss !== undefined && adapterData === null) {
|
|
652
|
+
refuseWrite(await writeMiss());
|
|
653
|
+
}
|
|
563
654
|
const miss = uniqueMissFailure(request, adapterData);
|
|
564
655
|
if (miss !== undefined) {
|
|
565
656
|
throw thrownFailure(miss);
|
|
566
657
|
}
|
|
658
|
+
if (settle !== undefined) {
|
|
659
|
+
const settled = await settle(adapterData);
|
|
660
|
+
if (!settled.ok) {
|
|
661
|
+
if (policyWrite !== undefined) {
|
|
662
|
+
refuseWrite(settled.response);
|
|
663
|
+
}
|
|
664
|
+
throw thrownFailure(settled.response);
|
|
665
|
+
}
|
|
666
|
+
adapterData = settled.data;
|
|
667
|
+
}
|
|
567
668
|
return materialize(adapterData, readPlan);
|
|
568
669
|
};
|
|
569
670
|
const materialized = async (data) => readMaterialized
|
|
570
671
|
? data
|
|
571
672
|
: materialize(data, planReadOperation(dependencies.contracts.core, dependencies.config.adapter.model, contract, scopeConfig.compute, request, args));
|
|
572
673
|
const data = await materialized(await runLevelLifecycle(levels, context, invoke, materialized, reuse, () => new AvInterceptorNextError()));
|
|
674
|
+
if (policyFailure !== undefined) {
|
|
675
|
+
return policyFailure;
|
|
676
|
+
}
|
|
573
677
|
if (!outputAccepted(contract, request, executedReadPlan?.projection, data, report)) {
|
|
574
678
|
return internalFailure();
|
|
575
679
|
}
|
|
@@ -32,8 +32,12 @@ export declare function deepFreeze<T>(value: T): T;
|
|
|
32
32
|
* only reads is told — the validated arguments after validation, a result in an
|
|
33
33
|
* after hook — so neither what is written nor the result the caller receives can
|
|
34
34
|
* be changed through it.
|
|
35
|
+
*
|
|
36
|
+
* With `snapshot`, a copied container found to hold no `Date` or `Uint8Array`
|
|
37
|
+
* is remembered as such at once, so a later `frozenCopy` of the copy shares it
|
|
38
|
+
* without walking it: for a copy that is itself copied again for each stage.
|
|
35
39
|
*/
|
|
36
|
-
export declare function frozenCopy<T>(value: T): AvDeepReadonly<T>;
|
|
40
|
+
export declare function frozenCopy<T>(value: T, snapshot?: boolean): AvDeepReadonly<T>;
|
|
37
41
|
/**
|
|
38
42
|
* A read-only view of `value`: reads see the live value, nested plain objects
|
|
39
43
|
* and arrays as views too, and any write, definition or deletion throws a
|
|
@@ -37,38 +37,79 @@ function scalarCopy(value) {
|
|
|
37
37
|
return undefined;
|
|
38
38
|
}
|
|
39
39
|
const SCALAR_FREE = new WeakSet();
|
|
40
|
-
|
|
40
|
+
function scalarFree(value) {
|
|
41
|
+
return Object.isFrozen(value) && SCALAR_FREE.has(value);
|
|
42
|
+
}
|
|
43
|
+
const WALKING = Symbol("walking");
|
|
44
|
+
function place(target, key, value, enumerable) {
|
|
45
|
+
if (enumerable && typeof key === "string" && !(key in target)) {
|
|
46
|
+
target[key] = value;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
Object.defineProperty(target, key, {
|
|
50
|
+
value,
|
|
51
|
+
enumerable,
|
|
52
|
+
writable: true,
|
|
53
|
+
configurable: true,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function frozenCopy(value, snapshot = false) {
|
|
57
|
+
const scalar = scalarCopy(value);
|
|
58
|
+
if (scalar !== undefined) {
|
|
59
|
+
return scalar;
|
|
60
|
+
}
|
|
61
|
+
if (!isContainer(value) || scalarFree(value)) {
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
41
64
|
const copies = new Map();
|
|
42
|
-
const walking = new Set();
|
|
43
65
|
let cycled = false;
|
|
66
|
+
let holdsScalar = false;
|
|
44
67
|
const copy = (current) => {
|
|
68
|
+
if (current === null || typeof current !== "object") {
|
|
69
|
+
return current;
|
|
70
|
+
}
|
|
45
71
|
const scalar = scalarCopy(current);
|
|
46
72
|
if (scalar !== undefined) {
|
|
73
|
+
holdsScalar = true;
|
|
47
74
|
return scalar;
|
|
48
75
|
}
|
|
49
|
-
if (!isContainer(current) ||
|
|
76
|
+
if (!isContainer(current) || scalarFree(current)) {
|
|
50
77
|
return current;
|
|
51
78
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
79
|
+
const known = copies.get(current);
|
|
80
|
+
if (known === WALKING) {
|
|
81
|
+
cycled = true;
|
|
82
|
+
return current;
|
|
83
|
+
}
|
|
84
|
+
if (known !== undefined) {
|
|
85
|
+
holdsScalar ||= !scalarFree(known);
|
|
86
|
+
return known;
|
|
55
87
|
}
|
|
56
|
-
copies.set(current,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
let changed = !DEEP_FROZEN.has(current);
|
|
88
|
+
copies.set(current, WALKING);
|
|
89
|
+
const outer = holdsScalar;
|
|
90
|
+
holdsScalar = false;
|
|
91
|
+
let changed = !(Object.isFrozen(current) && DEEP_FROZEN.has(current));
|
|
92
|
+
const array = Array.isArray(current);
|
|
93
|
+
const prototype = Object.getPrototypeOf(current);
|
|
94
|
+
const target = array
|
|
95
|
+
? new Array(current.length)
|
|
96
|
+
: prototype === Object.prototype
|
|
97
|
+
? {}
|
|
98
|
+
: Object.create(prototype);
|
|
60
99
|
for (const key of Reflect.ownKeys(current)) {
|
|
100
|
+
if (array && key === "length") {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
61
103
|
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
62
|
-
if (descriptor === undefined ||
|
|
63
|
-
!("value" in descriptor) ||
|
|
64
|
-
(Array.isArray(current) && key === "length")) {
|
|
104
|
+
if (descriptor === undefined || !("value" in descriptor)) {
|
|
65
105
|
continue;
|
|
66
106
|
}
|
|
67
107
|
const next = copy(descriptor.value);
|
|
68
108
|
changed ||= next !== descriptor.value;
|
|
69
|
-
|
|
109
|
+
place(target, key, next, descriptor.enumerable === true);
|
|
70
110
|
}
|
|
71
|
-
|
|
111
|
+
const held = holdsScalar;
|
|
112
|
+
holdsScalar = outer || held;
|
|
72
113
|
if (!changed) {
|
|
73
114
|
copies.set(current, current);
|
|
74
115
|
if (!cycled) {
|
|
@@ -76,20 +117,18 @@ export function frozenCopy(value) {
|
|
|
76
117
|
}
|
|
77
118
|
return current;
|
|
78
119
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
});
|
|
120
|
+
Object.freeze(target);
|
|
121
|
+
DEEP_FROZEN.add(target);
|
|
122
|
+
if (cycled) {
|
|
123
|
+
for (const key of Reflect.ownKeys(target)) {
|
|
124
|
+
deepFreeze(target[key]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (snapshot && !held && !cycled) {
|
|
128
|
+
SCALAR_FREE.add(target);
|
|
89
129
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return frozen;
|
|
130
|
+
copies.set(current, target);
|
|
131
|
+
return target;
|
|
93
132
|
};
|
|
94
133
|
return copy(value);
|
|
95
134
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AvAdapterModel } from "../adapters/adapter-model.js";
|
|
2
|
+
import { type PolicyCaller } from "../policy/policy-request.js";
|
|
2
3
|
import type { TransactionDiagnosticsChannel } from "../runtime/diagnostics-reporter.js";
|
|
3
4
|
import type { OperationPipelineDependencies, OperationRuntimeDependencies } from "../runtime/operation-runtime.js";
|
|
4
5
|
import type { PlanArguments } from "./transaction-plan-validator.js";
|
|
@@ -13,7 +14,9 @@ import type { AvTransactionResponse } from "./transaction-response.js";
|
|
|
13
14
|
* runner's mixed plans), and `planArguments` whether the steps' arguments are
|
|
14
15
|
* copied at the door; see `validateTransactionPlan`.
|
|
15
16
|
*/
|
|
16
|
-
export type FrameworkTransactionExecutor = (request: FrameworkTransactionRequest, diagnostics: TransactionDiagnosticsChannel, origins: readonly TransactionOperationOrigin[] | undefined, planArguments: PlanArguments
|
|
17
|
+
export type FrameworkTransactionExecutor = (request: FrameworkTransactionRequest, diagnostics: TransactionDiagnosticsChannel, origins: readonly TransactionOperationOrigin[] | undefined, planArguments: PlanArguments,
|
|
18
|
+
/** The principal an application run's steps run as (`{ as }`), when it names one. */
|
|
19
|
+
caller?: PolicyCaller) => Promise<AvTransactionResponse>;
|
|
17
20
|
/**
|
|
18
21
|
* Validates a whole plan through the Framework Validation Layer, then runs it inside
|
|
19
22
|
* exactly one Adapter transaction. The touched Resources' transaction stages and
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { transactionLevelsOf } from "../config/pipeline-tree.resolver.js";
|
|
2
|
+
import { openRequestPolicy, } from "../policy/policy-request.js";
|
|
2
3
|
import { AdapterBoundary, planOperationArgumentsPath, planStepPath, sanitizeOperationFailure, } from "../runtime/error-boundary.js";
|
|
3
4
|
import { internalFailure, operationSuccess, } from "../runtime/operation-response.js";
|
|
4
5
|
import { admitOperation, EVERY_FIELD, executeAdmittedOperation, prepareStep, STEP_PIPES, withRequestState, } from "../runtime/operation-runtime.js";
|
|
@@ -20,13 +21,13 @@ export function createTransactionRuntime(dependencies, pipelineDependencies) {
|
|
|
20
21
|
cascades: resolveCascadeClosure(adapter.referentialActions),
|
|
21
22
|
sqlStatements: adapter.sqlStatements === true,
|
|
22
23
|
};
|
|
23
|
-
return async (seeded, diagnostics, origins, planArguments) => {
|
|
24
|
+
return async (seeded, diagnostics, origins, planArguments, caller) => {
|
|
24
25
|
const request = withRequestState(seeded);
|
|
25
26
|
const validation = await validateTransactionPlan(planDependencies, request, origins, planArguments);
|
|
26
27
|
if (!validation.ok) {
|
|
27
28
|
return validation.response;
|
|
28
29
|
}
|
|
29
|
-
return executeTransactionPlan(
|
|
30
|
+
return executeTransactionPlan(dependencies, pipelineDependencies, request, validation.plan, request.state, diagnostics, caller);
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
function planOrigin(request, plan) {
|
|
@@ -79,8 +80,21 @@ function standingReferences(core, operation, args, steps, subject) {
|
|
|
79
80
|
}
|
|
80
81
|
return { ok: false, ...refused };
|
|
81
82
|
}
|
|
82
|
-
async function executeTransactionPlan(
|
|
83
|
-
const
|
|
83
|
+
async function executeTransactionPlan(runtime, dependencies, request, plan, state, diagnostics, caller) {
|
|
84
|
+
const { adapter } = runtime;
|
|
85
|
+
const steps = new Map();
|
|
86
|
+
const stateful = (operation) => {
|
|
87
|
+
const known = steps.get(operation);
|
|
88
|
+
if (known !== undefined) {
|
|
89
|
+
return known;
|
|
90
|
+
}
|
|
91
|
+
const policy = openRequestPolicy(runtime.config.policy, runtime.contracts.core, { origin: operation.request.origin, state }, caller, adapter);
|
|
92
|
+
const step = policy === undefined
|
|
93
|
+
? { ...operation.request, state }
|
|
94
|
+
: { ...operation.request, state, policy };
|
|
95
|
+
steps.set(operation, step);
|
|
96
|
+
return step;
|
|
97
|
+
};
|
|
84
98
|
const admitted = [];
|
|
85
99
|
const standing = [];
|
|
86
100
|
for (const step of plan.operations) {
|
|
@@ -90,7 +104,15 @@ async function executeTransactionPlan(adapter, dependencies, request, plan, stat
|
|
|
90
104
|
continue;
|
|
91
105
|
}
|
|
92
106
|
const report = diagnostics.operation(step.request, step.index);
|
|
93
|
-
|
|
107
|
+
let stepRequest;
|
|
108
|
+
try {
|
|
109
|
+
stepRequest = stateful(step);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
report(error, "A3000");
|
|
113
|
+
return transactionOperationFailure(internalFailure(), step.index);
|
|
114
|
+
}
|
|
115
|
+
const admission = await admitOperation(dependencies, stepRequest, step.contract, step.arguments, planOperationArgumentsPath(step.index), report, { requestId: request.requestId, transaction: undefined, state }, {
|
|
94
116
|
select: EVERY_FIELD,
|
|
95
117
|
validation: { transactionReferences: true },
|
|
96
118
|
beforeBegin: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aventara/core",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.4",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "Core runtime, contract compiler, and extension APIs for Aventara.",
|
|
6
6
|
"type": "module",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"access": "public"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
|
-
"build": "node ../../scripts/build/build-output.cleaner.ts dist && tsc -p tsconfig.json --removeComments --declaration false && tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist && node scripts/errors-markdown.generator.ts",
|
|
48
|
+
"build": "node ../../scripts/build/build-output.cleaner.ts dist && node scripts/core-version.generator.ts && tsc -p tsconfig.json --removeComments --declaration false && tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist && node scripts/errors-markdown.generator.ts",
|
|
49
49
|
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
|
|
50
50
|
"test": "vitest run --typecheck --config ../../vitest.config.mts --root .",
|
|
51
51
|
"lint": "biome lint src scripts"
|