@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.
Files changed (73) hide show
  1. package/CHANGELOG.md +130 -1
  2. package/ERRORS.md +6 -6
  3. package/README.md +72 -12
  4. package/dist/adapters/adapter-operation.d.ts +7 -0
  5. package/dist/compiler/contract.compiler.d.ts +6 -0
  6. package/dist/compiler/contract.compiler.js +73 -12
  7. package/dist/config/computed-fields.js +2 -9
  8. package/dist/config/config-diagnostic.guidance.js +3 -3
  9. package/dist/config/config.resolver.d.ts +14 -0
  10. package/dist/config/config.resolver.js +31 -0
  11. package/dist/config/config.validator.js +82 -0
  12. package/dist/config/exact-framework-config.d.ts +5 -0
  13. package/dist/config/framework-config.d.ts +26 -1
  14. package/dist/contracts/client-contract-structure.validator.d.ts +8 -0
  15. package/dist/contracts/client-contract-structure.validator.js +6 -1
  16. package/dist/contracts/field.d.ts +11 -0
  17. package/dist/framework/contract-view.d.ts +41 -10
  18. package/dist/framework/framework.factory.js +39 -11
  19. package/dist/framework/operation-facade.d.ts +24 -5
  20. package/dist/framework/operation-facade.js +2 -1
  21. package/dist/index.d.ts +4 -1
  22. package/dist/internal.d.ts +1 -0
  23. package/dist/internal.js +1 -0
  24. package/dist/operations/operation-arguments.d.ts +1 -1
  25. package/dist/operations/operation-call.d.ts +9 -5
  26. package/dist/operations/operation-result.d.ts +5 -3
  27. package/dist/operations/operation-validator-core.js +6 -0
  28. package/dist/policy/policy-admission.d.ts +22 -0
  29. package/dist/policy/policy-admission.js +400 -0
  30. package/dist/policy/policy-after-update.runtime.d.ts +29 -0
  31. package/dist/policy/policy-after-update.runtime.js +131 -0
  32. package/dist/policy/policy-condition.d.ts +15 -0
  33. package/dist/policy/policy-condition.js +36 -0
  34. package/dist/policy/policy-engine.d.ts +111 -0
  35. package/dist/policy/policy-engine.js +1 -0
  36. package/dist/policy/policy-nested-write.runtime.d.ts +38 -0
  37. package/dist/policy/policy-nested-write.runtime.js +449 -0
  38. package/dist/policy/policy-read.runtime.d.ts +83 -0
  39. package/dist/policy/policy-read.runtime.js +494 -0
  40. package/dist/policy/policy-request.d.ts +37 -0
  41. package/dist/policy/policy-request.js +52 -0
  42. package/dist/policy/policy-session.d.ts +83 -0
  43. package/dist/policy/policy-session.js +234 -0
  44. package/dist/policy/policy-startup.validator.d.ts +8 -0
  45. package/dist/policy/policy-startup.validator.js +106 -0
  46. package/dist/policy/policy-write.runtime.d.ts +30 -0
  47. package/dist/policy/policy-write.runtime.js +394 -0
  48. package/dist/prebuilt/core-version.generated.d.ts +2 -0
  49. package/dist/prebuilt/core-version.generated.js +1 -0
  50. package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
  51. package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
  52. package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
  53. package/dist/prebuilt/prebuilt-contracts.js +1 -0
  54. package/dist/prebuilt/prebuilt-contracts.loader.d.ts +21 -0
  55. package/dist/prebuilt/prebuilt-contracts.loader.js +122 -0
  56. package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
  57. package/dist/prebuilt/prebuilt-inputs.fingerprint.js +192 -0
  58. package/dist/runtime/computed-read-runtime.d.ts +6 -1
  59. package/dist/runtime/computed-read-runtime.js +224 -45
  60. package/dist/runtime/context-executor.runtime.js +4 -2
  61. package/dist/runtime/execution-options.d.ts +8 -0
  62. package/dist/runtime/framework-warning.emitter.d.ts +6 -0
  63. package/dist/runtime/framework-warning.emitter.js +8 -0
  64. package/dist/runtime/level-lifecycle.runtime.js +3 -2
  65. package/dist/runtime/operation-code.guidance.js +3 -3
  66. package/dist/runtime/operation-request.d.ts +7 -0
  67. package/dist/runtime/operation-runtime.d.ts +9 -1
  68. package/dist/runtime/operation-runtime.js +114 -10
  69. package/dist/runtime/readonly-value.d.ts +5 -1
  70. package/dist/runtime/readonly-value.js +67 -28
  71. package/dist/transactions/transaction-runtime.d.ts +4 -1
  72. package/dist/transactions/transaction-runtime.js +27 -5
  73. 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
- if (forced) {
198
- return executeForcedOperation(pipelineDependencies, dependencies.adapter, stateful, validated.contract, admitted.args, report);
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: transactionLevelsOf(activeScopeConfig(dependencies.config, request).pipelines, request.resource),
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
- const readPlan = planReadOperation(dependencies.contracts.core, dependencies.config.adapter.model, contract, scopeConfig.compute, request, args);
556
- const adapterArguments = await stripVirtualMutationInput(contracts, request, readPlan.arguments);
557
- const identity = request;
558
- const adapterData = await adapterBoundary.execute(() => executor.execute({
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
- export function frozenCopy(value) {
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) || SCALAR_FREE.has(current)) {
76
+ if (!isContainer(current) || scalarFree(current)) {
50
77
  return current;
51
78
  }
52
- if (copies.has(current)) {
53
- cycled ||= walking.has(current);
54
- return copies.get(current);
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, current);
57
- walking.add(current);
58
- const entries = [];
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
- entries.push([key, descriptor, next]);
109
+ place(target, key, next, descriptor.enumerable === true);
70
110
  }
71
- walking.delete(current);
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
- const target = Array.isArray(current)
80
- ? new Array(current.length)
81
- : Object.create(Object.getPrototypeOf(current));
82
- for (const [key, descriptor, next] of entries) {
83
- Object.defineProperty(target, key, {
84
- value: next,
85
- enumerable: descriptor.enumerable ?? false,
86
- writable: true,
87
- configurable: true,
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
- const frozen = deepFreeze(target);
91
- copies.set(current, frozen);
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) => Promise<AvTransactionResponse>;
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(adapter, pipelineDependencies, request, validation.plan, request.state, diagnostics);
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(adapter, dependencies, request, plan, state, diagnostics) {
83
- const stateful = (operation) => ({ ...operation.request, state });
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
- const admission = await admitOperation(dependencies, stateful(step), step.contract, step.arguments, planOperationArgumentsPath(step.index), report, { requestId: request.requestId, transaction: undefined, state }, {
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",
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"