@aventara/core 1.0.0-rc.3 → 1.0.0-rc.8

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 (96) hide show
  1. package/CHANGELOG.md +231 -1
  2. package/ERRORS.md +6 -6
  3. package/README.md +76 -12
  4. package/dist/adapters/adapter-field.d.ts +8 -0
  5. package/dist/adapters/adapter-identifier.d.ts +9 -0
  6. package/dist/adapters/adapter-identifier.js +6 -1
  7. package/dist/adapters/adapter-operation.d.ts +13 -0
  8. package/dist/compiler/contract.compiler.d.ts +6 -0
  9. package/dist/compiler/contract.compiler.js +99 -15
  10. package/dist/config/computed-fields.js +2 -9
  11. package/dist/config/config-diagnostic.guidance.js +3 -3
  12. package/dist/config/config.resolver.d.ts +14 -0
  13. package/dist/config/config.resolver.js +31 -0
  14. package/dist/config/config.validator.js +82 -0
  15. package/dist/config/exact-framework-config.d.ts +5 -0
  16. package/dist/config/framework-config.d.ts +30 -1
  17. package/dist/contracts/client-contract-structure.validator.d.ts +8 -0
  18. package/dist/contracts/client-contract-structure.validator.js +6 -1
  19. package/dist/contracts/contract.d.ts +15 -1
  20. package/dist/contracts/field.d.ts +28 -0
  21. package/dist/contracts/scalar-value-type.d.ts +3 -1
  22. package/dist/framework/contract-view.d.ts +41 -10
  23. package/dist/framework/framework-construction.error.d.ts +2 -1
  24. package/dist/framework/framework-construction.error.js +11 -1
  25. package/dist/framework/framework.d.ts +4 -4
  26. package/dist/framework/framework.factory.js +39 -11
  27. package/dist/framework/operation-facade.d.ts +29 -10
  28. package/dist/framework/operation-facade.js +2 -1
  29. package/dist/index.d.ts +10 -4
  30. package/dist/internal.d.ts +2 -2
  31. package/dist/internal.js +2 -0
  32. package/dist/operations/operation-arguments.d.ts +44 -28
  33. package/dist/operations/operation-call.d.ts +9 -5
  34. package/dist/operations/operation-result.d.ts +6 -4
  35. package/dist/operations/operation-validator-core.js +6 -0
  36. package/dist/policy/policy-admission.d.ts +22 -0
  37. package/dist/policy/policy-admission.js +409 -0
  38. package/dist/policy/policy-after-update.runtime.d.ts +29 -0
  39. package/dist/policy/policy-after-update.runtime.js +132 -0
  40. package/dist/policy/policy-condition.d.ts +15 -0
  41. package/dist/policy/policy-condition.js +36 -0
  42. package/dist/policy/policy-engine.d.ts +118 -0
  43. package/dist/policy/policy-engine.js +1 -0
  44. package/dist/policy/policy-nested-write.runtime.d.ts +38 -0
  45. package/dist/policy/policy-nested-write.runtime.js +453 -0
  46. package/dist/policy/policy-read.runtime.d.ts +81 -0
  47. package/dist/policy/policy-read.runtime.js +503 -0
  48. package/dist/policy/policy-request.d.ts +41 -0
  49. package/dist/policy/policy-request.js +63 -0
  50. package/dist/policy/policy-session.d.ts +93 -0
  51. package/dist/policy/policy-session.js +235 -0
  52. package/dist/policy/policy-startup.validator.d.ts +8 -0
  53. package/dist/policy/policy-startup.validator.js +104 -0
  54. package/dist/policy/policy-write.runtime.d.ts +37 -0
  55. package/dist/policy/policy-write.runtime.js +409 -0
  56. package/dist/prebuilt/core-version.generated.d.ts +2 -0
  57. package/dist/prebuilt/core-version.generated.js +1 -0
  58. package/dist/prebuilt/prebuilt-changes.d.ts +28 -0
  59. package/dist/prebuilt/prebuilt-changes.js +65 -0
  60. package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
  61. package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
  62. package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
  63. package/dist/prebuilt/prebuilt-contracts.js +1 -0
  64. package/dist/prebuilt/prebuilt-contracts.loader.d.ts +21 -0
  65. package/dist/prebuilt/prebuilt-contracts.loader.js +77 -0
  66. package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
  67. package/dist/prebuilt/prebuilt-inputs.fingerprint.js +192 -0
  68. package/dist/protocol/absent-route.responder.d.ts +4 -4
  69. package/dist/protocol/http-request.decoder.d.ts +2 -2
  70. package/dist/protocol/http-request.reader.d.ts +2 -2
  71. package/dist/protocol/protocol-headers.d.ts +3 -3
  72. package/dist/runtime/computed-read-runtime.d.ts +6 -1
  73. package/dist/runtime/computed-read-runtime.js +226 -52
  74. package/dist/runtime/context-executor.runtime.d.ts +2 -2
  75. package/dist/runtime/context-executor.runtime.js +4 -2
  76. package/dist/runtime/create-requirement.js +1 -1
  77. package/dist/runtime/diagnostics-reporter.d.ts +5 -5
  78. package/dist/runtime/execution-options.d.ts +8 -0
  79. package/dist/runtime/framework-warning.emitter.d.ts +6 -0
  80. package/dist/runtime/framework-warning.emitter.js +8 -0
  81. package/dist/runtime/level-lifecycle.runtime.js +3 -2
  82. package/dist/runtime/operation-code.guidance.js +3 -3
  83. package/dist/runtime/operation-request.d.ts +16 -9
  84. package/dist/runtime/operation-runtime.d.ts +15 -7
  85. package/dist/runtime/operation-runtime.js +139 -14
  86. package/dist/runtime/owned-execution.registry.d.ts +2 -2
  87. package/dist/runtime/readonly-value.d.ts +5 -1
  88. package/dist/runtime/readonly-value.js +67 -28
  89. package/dist/runtime/referential-action.d.ts +1 -1
  90. package/dist/transactions/sql-statement.d.ts +2 -2
  91. package/dist/transactions/transaction-plan-assembler.d.ts +2 -2
  92. package/dist/transactions/transaction-plan-validator.d.ts +5 -5
  93. package/dist/transactions/transaction-request.d.ts +8 -8
  94. package/dist/transactions/transaction-runtime.d.ts +5 -2
  95. package/dist/transactions/transaction-runtime.js +33 -6
  96. 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,22 @@ 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
+ const failed = (error) => {
48
+ report(error, "A3000");
49
+ return internalFailure();
50
+ };
51
+ const withPolicy = (policy) => policy === undefined ? request : { ...request, policy };
52
+ try {
53
+ const policy = openRequestPolicy(dependencies.config.policy, dependencies.contracts.core, request, caller, dependencies.adapter);
54
+ return policy instanceof Promise
55
+ ? policy.then(withPolicy, failed)
56
+ : withPolicy(policy);
57
+ }
58
+ catch (error) {
59
+ return failed(error);
60
+ }
61
+ }
43
62
  export function createPipelineDependencies(dependencies, model) {
44
63
  const misuse = createMisuseReporter(dependencies.config.misuse);
45
64
  const recordWrites = recordWriteFactsOf({
@@ -170,7 +189,7 @@ function outputAccepted(contract, request, projection, data, report) {
170
189
  }
171
190
  export function createOperationRuntime(dependencies, pipelineDependencies) {
172
191
  const misuse = createMisuseReporter(dependencies.config.misuse);
173
- return async (request, report, source) => {
192
+ return async (request, report, source, caller) => {
174
193
  let validated;
175
194
  try {
176
195
  validated = validateOperation(dependencies, request, source, misuse);
@@ -184,7 +203,11 @@ export function createOperationRuntime(dependencies, pipelineDependencies) {
184
203
  if (!validated.ok) {
185
204
  return validated.response;
186
205
  }
187
- const stateful = withRequestState(request);
206
+ const opened = withRequestPolicy(dependencies, withRequestState(request), caller, report);
207
+ const stateful = opened instanceof Promise ? await opened : opened;
208
+ if (!("state" in stateful)) {
209
+ return stateful;
210
+ }
188
211
  const forced = isForcedOperation(activeScopeConfig(dependencies.config, request).pipelines, request);
189
212
  const admitted = await admitOperation(pipelineDependencies, stateful, validated.contract, validated.arguments, OPERATION_ARGUMENTS_PATH, report, {
190
213
  requestId: request.requestId,
@@ -194,8 +217,20 @@ export function createOperationRuntime(dependencies, pipelineDependencies) {
194
217
  if (!admitted.ok) {
195
218
  return admitted.response;
196
219
  }
197
- if (forced) {
198
- return executeForcedOperation(pipelineDependencies, dependencies.adapter, stateful, validated.contract, admitted.args, report);
220
+ let checkedWrite = false;
221
+ if (stateful.policy !== undefined &&
222
+ request.family !== "find" &&
223
+ validated.contract.transactions === "interactive" &&
224
+ !forced) {
225
+ try {
226
+ 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);
227
+ }
228
+ catch (error) {
229
+ return sanitizeOperationFailure(error, stateful.policy.execution.boundary.originOf(error), report, { path: OPERATION_ARGUMENTS_PATH, arguments: admitted.args });
230
+ }
231
+ }
232
+ if (forced || checkedWrite) {
233
+ return executeForcedOperation(pipelineDependencies, dependencies.adapter, stateful, validated.contract, admitted.args, report, forced);
199
234
  }
200
235
  return executeAdmittedOperation(pipelineDependencies, stateful, validated.contract, admitted.args, OPERATION_ARGUMENTS_PATH, { kind: "standalone", executor: dependencies.adapter }, report);
201
236
  };
@@ -346,6 +381,12 @@ export async function admitOperation(dependencies, request, contract, args, argu
346
381
  if (absent.length > 0) {
347
382
  return { ok: false, response: mapValidationOutcome(absent) };
348
383
  }
384
+ if (request.policy !== undefined) {
385
+ const refused = await admitRequestPolicy(request.policy, dependencies.contracts.core, contract, request, current, report, { path: argumentsPath, arguments: current });
386
+ if (refused !== undefined) {
387
+ return { ok: false, response: refused };
388
+ }
389
+ }
349
390
  try {
350
391
  return await admit();
351
392
  }
@@ -441,7 +482,7 @@ export async function prepareStep(dependencies, request, contract, args, argumen
441
482
  };
442
483
  }
443
484
  }
444
- async function executeForcedOperation(dependencies, adapter, request, contract, args, report) {
485
+ async function executeForcedOperation(dependencies, adapter, request, contract, args, report, forced) {
445
486
  const plan = [
446
487
  {
447
488
  resource: request.resource,
@@ -452,7 +493,9 @@ async function executeForcedOperation(dependencies, adapter, request, contract,
452
493
  ];
453
494
  const settled = await settleTransaction({
454
495
  adapter,
455
- levels: transactionLevelsOf(activeScopeConfig(dependencies.config, request).pipelines, request.resource),
496
+ levels: forced
497
+ ? transactionLevelsOf(activeScopeConfig(dependencies.config, request).pipelines, request.resource)
498
+ : [],
456
499
  plan,
457
500
  contextOf: (current, executor) => dependencies.contexts.transaction({
458
501
  requestId: request.requestId,
@@ -544,6 +587,25 @@ export async function executeAdmittedOperation(dependencies, request, contract,
544
587
  throw hiddenRefusal;
545
588
  }
546
589
  args = deepFreeze(computed);
590
+ let policyWrite;
591
+ if (request.policy !== undefined && request.family !== "find") {
592
+ const planned = planReadOperation(dependencies.contracts.core, dependencies.config.adapter.model, contract, scopeConfig.compute, request, args);
593
+ const plannedArguments = await stripVirtualMutationInput(contracts, request, planned.arguments);
594
+ try {
595
+ policyWrite = await enforceWritePolicy(request.policy.on({ executor, boundary: adapterBoundary }), dependencies.contracts.core, dependencies.config.adapter.model, request, planned, plannedArguments, binding.kind === "transaction");
596
+ }
597
+ catch (error) {
598
+ return sanitizeOperationFailure(error, adapterBoundary.originOf(error), report, { path: argumentsPath, arguments: args });
599
+ }
600
+ if (policyWrite.kind === "refused") {
601
+ return policyWrite.response;
602
+ }
603
+ }
604
+ let policyFailure;
605
+ const refuseWrite = (response) => {
606
+ policyFailure = response;
607
+ throw thrownFailure(response);
608
+ };
547
609
  let readMaterialized = false;
548
610
  let executedReadPlan;
549
611
  const materialize = async (data, plan) => {
@@ -552,24 +614,87 @@ export async function executeAdmittedOperation(dependencies, request, contract,
552
614
  return materializeReadOperation(contract, scopeConfig.compute, request, plan, data, computeContext);
553
615
  };
554
616
  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({
559
- ...identity,
560
- resource: request.resource,
561
- arguments: adapterArguments,
562
- }));
617
+ let readPlan = planReadOperation(dependencies.contracts.core, dependencies.config.adapter.model, contract, scopeConfig.compute, request, args);
618
+ let adapterArguments = await stripVirtualMutationInput(contracts, request, readPlan.arguments);
619
+ let operation = request;
620
+ let settle;
621
+ let writeMiss;
622
+ let failedWriteMiss;
623
+ if (policyWrite !== undefined && policyWrite.kind !== "refused") {
624
+ writeMiss = policyWrite.miss;
625
+ failedWriteMiss = policyWrite.failedMiss;
626
+ if (policyWrite.kind === "empty") {
627
+ if (writeMiss !== undefined && policyWrite.data === null) {
628
+ refuseWrite(await writeMiss());
629
+ }
630
+ return materialize(policyWrite.data, readPlan);
631
+ }
632
+ operation = policyWrite.identity;
633
+ adapterArguments = policyWrite.arguments;
634
+ readPlan = policyWrite.plan;
635
+ settle = policyWrite.settle;
636
+ }
637
+ else if (request.policy !== undefined && request.family === "find") {
638
+ const read = await enforceReadPolicy(request.policy.on({ executor, boundary: adapterBoundary }), dependencies.contracts.core, dependencies.config.adapter.model, request, readPlan, adapterArguments);
639
+ if (read.kind === "refused") {
640
+ throw thrownFailure(read.response);
641
+ }
642
+ if (read.kind === "empty") {
643
+ const miss = uniqueMissFailure(request, read.data);
644
+ if (miss !== undefined) {
645
+ throw thrownFailure(miss);
646
+ }
647
+ return materialize(read.data, readPlan);
648
+ }
649
+ operation = read.identity;
650
+ adapterArguments = read.arguments;
651
+ readPlan = read.plan;
652
+ settle = read.settle;
653
+ }
654
+ const { policy: _session, ...identity } = operation;
655
+ let adapterData;
656
+ try {
657
+ adapterData = await adapterBoundary.execute(() => executor.execute({
658
+ ...identity,
659
+ resource: request.resource,
660
+ arguments: adapterArguments,
661
+ }));
662
+ }
663
+ catch (error) {
664
+ const missed = failedWriteMiss === undefined
665
+ ? undefined
666
+ : await failedWriteMiss().catch(() => undefined);
667
+ if (missed !== undefined) {
668
+ refuseWrite(missed);
669
+ }
670
+ throw error;
671
+ }
672
+ if (writeMiss !== undefined && adapterData === null) {
673
+ refuseWrite(await writeMiss());
674
+ }
563
675
  const miss = uniqueMissFailure(request, adapterData);
564
676
  if (miss !== undefined) {
565
677
  throw thrownFailure(miss);
566
678
  }
679
+ if (settle !== undefined) {
680
+ const settled = await settle(adapterData);
681
+ if (!settled.ok) {
682
+ if (policyWrite !== undefined) {
683
+ refuseWrite(settled.response);
684
+ }
685
+ throw thrownFailure(settled.response);
686
+ }
687
+ adapterData = settled.data;
688
+ }
567
689
  return materialize(adapterData, readPlan);
568
690
  };
569
691
  const materialized = async (data) => readMaterialized
570
692
  ? data
571
693
  : materialize(data, planReadOperation(dependencies.contracts.core, dependencies.config.adapter.model, contract, scopeConfig.compute, request, args));
572
694
  const data = await materialized(await runLevelLifecycle(levels, context, invoke, materialized, reuse, () => new AvInterceptorNextError()));
695
+ if (policyFailure !== undefined) {
696
+ return policyFailure;
697
+ }
573
698
  if (!outputAccepted(contract, request, executedReadPlan?.projection, data, report)) {
574
699
  return internalFailure();
575
700
  }
@@ -1,4 +1,4 @@
1
- import type { AgentTransactionRequest, ClientTransactionRequest } from "../transactions/transaction-request.js";
1
+ import type { AvAgentTransactionRequest, AvClientTransactionRequest } from "../transactions/transaction-request.js";
2
2
  import type { AvTransactionResponse } from "../transactions/transaction-response.js";
3
3
  import type { AvCallerDataError } from "./caller-data.copier.js";
4
4
  import type { RemoteOperationRequest } from "./operation-request.js";
@@ -15,7 +15,7 @@ export type OwnedExecution = {
15
15
  * at the call, and these steps' arguments could not be copied there: each is
16
16
  * answered at its turn as the door answers that failure.
17
17
  */
18
- readonly executeTransaction: (request: ClientTransactionRequest | AgentTransactionRequest, refused?: ReadonlyMap<number, AvCallerDataError>) => Promise<AvTransactionResponse>;
18
+ readonly executeTransaction: (request: AvClientTransactionRequest | AvAgentTransactionRequest, refused?: ReadonlyMap<number, AvCallerDataError>) => Promise<AvTransactionResponse>;
19
19
  };
20
20
  /** Records `framework`'s owned entries; called once, at construction. */
21
21
  export declare function recordOwnedExecution(framework: object, execution: OwnedExecution): void;
@@ -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
  }
@@ -29,7 +29,7 @@ export type ReferentialEdge = {
29
29
  * relation `field` and any of these names the reference twice and is refused;
30
30
  * a required relation may be written through all of them instead.
31
31
  */
32
- readonly references: readonly string[];
32
+ readonly referenceFields: readonly string[];
33
33
  /** What deleting a `target` row does to rows of the ENCLOSING Resource. */
34
34
  readonly onDelete: ReferentialDeleteEffect;
35
35
  };
@@ -10,7 +10,7 @@ import type { AvTransactionReference } from "./transaction-reference.js";
10
10
  * `${value}::jsonb`), or a `$ref` to an earlier step's field, resolved when the
11
11
  * earlier steps have run and then bound like any other value.
12
12
  */
13
- export type SqlStatementValue = AvAdapterSqlValue | AvTransactionReference;
13
+ export type AvSqlStatementValue = AvAdapterSqlValue | AvTransactionReference;
14
14
  /**
15
15
  * `framework.appTx.$sql`: a developer SQL step, written only as a tagged template —
16
16
  * ``framework.appTx.$sql`select pgmq.send('jobs', jsonb_build_object('id', ${ref}))` ``.
@@ -23,7 +23,7 @@ export type SqlStatementValue = AvAdapterSqlValue | AvTransactionReference;
23
23
  * SQL step's rows. No guard, pipe, hook, interceptor or filter runs around it: it
24
24
  * has no Resource or operation for one to wrap.
25
25
  */
26
- export type SqlStatementBuilder = <Row = unknown>(strings: TemplateStringsArray, ...values: readonly SqlStatementValue[]) => AvOperation<Row[], "application">;
26
+ export type AvSqlStatementBuilder = <Row = unknown>(strings: TemplateStringsArray, ...values: readonly AvSqlStatementValue[]) => AvOperation<Row[], "application">;
27
27
  /** One value of a planned SQL step, as the typed runner bound it. */
28
28
  export type PlannedSqlValue = {
29
29
  readonly kind: "value";
@@ -2,7 +2,7 @@ import type { AvContracts } from "../compiler/contract.compiler.js";
2
2
  import type { AvOperationIdentity } from "../operations/operation-identity.js";
3
3
  import { AvCallerDataError } from "../runtime/caller-data.copier.js";
4
4
  import type { MisuseReporter } from "../runtime/misuse-reporter.js";
5
- import type { ClientProtocolIdentity } from "../runtime/operation-request.js";
5
+ import type { AvRequestProtocol } from "../runtime/operation-request.js";
6
6
  import type { AvOperationFailureResponse } from "../runtime/operation-response.js";
7
7
  import type { AvOperation, OperationFingerprint } from "./deferred-operation.js";
8
8
  import type { PlannedSqlStatement } from "./sql-statement.js";
@@ -49,5 +49,5 @@ export type TransactionPlanAssembler = {
49
49
  * handle, so a handle another instance built — typed against another model,
50
50
  * bound to other Contracts — is refused rather than run.
51
51
  */
52
- export declare function createTransactionPlanAssembler(contracts: AvContracts, protocol: ClientProtocolIdentity, misuse: MisuseReporter): TransactionPlanAssembler;
52
+ export declare function createTransactionPlanAssembler(contracts: AvContracts, protocol: AvRequestProtocol, misuse: MisuseReporter): TransactionPlanAssembler;
53
53
  export {};
@@ -4,7 +4,7 @@ import type { AvScalarField } from "../contracts/field.js";
4
4
  import type { SemanticOperationArguments } from "../operations/operation-validator.js";
5
5
  import { AvCallerDataError } from "../runtime/caller-data.copier.js";
6
6
  import { type ReadProjectionPlan } from "../runtime/computed-read-runtime.js";
7
- import type { FrameworkOperationRequest } from "../runtime/operation-request.js";
7
+ import type { AvOperationRequest } from "../runtime/operation-request.js";
8
8
  import type { AvOperationFailureResponse } from "../runtime/operation-response.js";
9
9
  import type { OperationPipelineDependencies } from "../runtime/operation-runtime.js";
10
10
  import type { CascadeClosure } from "../runtime/referential-action.js";
@@ -13,7 +13,7 @@ import type { PlannedSqlStatement, ValidatedSqlStatement } from "./sql-statement
13
13
  import type { AvTransactionReference } from "./transaction-reference.js";
14
14
  import { type TransactionSlotShape } from "./transaction-reference.js";
15
15
  import { type TransactionReferenceCandidateSlot } from "./transaction-reference-collector.js";
16
- import type { FrameworkTransactionRequest, TransactionOperationOrigin } from "./transaction-request.js";
16
+ import type { AvTransactionRequest, TransactionOperationOrigin } from "./transaction-request.js";
17
17
  /**
18
18
  * Where a plan's step arguments came from, so the validator knows whether to copy
19
19
  * them at the door (see `ArgumentsSource`):
@@ -65,7 +65,7 @@ export type TransactionReferenceSlot = {
65
65
  };
66
66
  export type CandidateTransactionOperation = {
67
67
  readonly index: number;
68
- readonly request: FrameworkOperationRequest;
68
+ readonly request: AvOperationRequest;
69
69
  readonly arguments: SemanticOperationArguments;
70
70
  readonly projection: ReadProjectionPlan | undefined;
71
71
  readonly declaredFingerprint: unknown;
@@ -74,7 +74,7 @@ export type CandidateTransactionOperation = {
74
74
  export type ValidatedTransactionOperation = {
75
75
  readonly kind: "operation";
76
76
  readonly index: number;
77
- readonly request: FrameworkOperationRequest;
77
+ readonly request: AvOperationRequest;
78
78
  /**
79
79
  * The Contract of this operation's origin, which is the plan's: the one it was
80
80
  * validated against, the one it runs under, and the one a later `$ref` reads its
@@ -118,4 +118,4 @@ export declare function transactionsUnavailableFailure(): AvOperationFailureResp
118
118
  /**
119
119
  * Whole-plan Framework Validation Layer for transaction structure and identity.
120
120
  */
121
- export declare function validateTransactionPlan<M extends AvAdapterModel>(dependencies: TransactionPlanDependencies<M>, request: FrameworkTransactionRequest, origins?: readonly TransactionOperationOrigin[], planArguments?: PlanArguments): Promise<TransactionPlanValidationResult>;
121
+ export declare function validateTransactionPlan<M extends AvAdapterModel>(dependencies: TransactionPlanDependencies<M>, request: AvTransactionRequest, origins?: readonly TransactionOperationOrigin[], planArguments?: PlanArguments): Promise<TransactionPlanValidationResult>;
@@ -1,4 +1,4 @@
1
- import type { ClientProtocolIdentity } from "../runtime/operation-request.js";
1
+ import type { AvRequestProtocol } from "../runtime/operation-request.js";
2
2
  import type { AvTransportContext } from "../runtime/pipeline.js";
3
3
  import type { AvState } from "../runtime/request-state.js";
4
4
  type TransactionRequestBase = {
@@ -11,29 +11,29 @@ type TransactionRequestBase = {
11
11
  readonly state?: AvState;
12
12
  };
13
13
  /** An in-process transaction request, run under the Application Contract. */
14
- export type ApplicationTransactionRequest = TransactionRequestBase & {
14
+ export type AvApplicationTransactionRequest = TransactionRequestBase & {
15
15
  readonly origin: "application";
16
16
  readonly protocol?: never;
17
17
  readonly transport?: never;
18
18
  };
19
19
  /** A remote transaction request, run under the Client Contract. */
20
- export type ClientTransactionRequest = TransactionRequestBase & {
20
+ export type AvClientTransactionRequest = TransactionRequestBase & {
21
21
  readonly origin: "client";
22
- readonly protocol: ClientProtocolIdentity;
22
+ readonly protocol: AvRequestProtocol;
23
23
  readonly transport?: AvTransportContext;
24
24
  };
25
25
  /** An AI agent's transaction request, run under the Agent Contract. */
26
- export type AgentTransactionRequest = TransactionRequestBase & {
26
+ export type AvAgentTransactionRequest = TransactionRequestBase & {
27
27
  readonly origin: "agent";
28
- readonly protocol: ClientProtocolIdentity;
28
+ readonly protocol: AvRequestProtocol;
29
29
  readonly transport?: AvTransportContext;
30
30
  };
31
31
  /** One transaction request, as `framework.executeTransaction` takes it. */
32
- export type FrameworkTransactionRequest = ApplicationTransactionRequest | ClientTransactionRequest | AgentTransactionRequest;
32
+ export type AvTransactionRequest = AvApplicationTransactionRequest | AvClientTransactionRequest | AvAgentTransactionRequest;
33
33
  /**
34
34
  * The origin one plan operation runs under: its own layer's Contract, its own
35
35
  * pipelines, and — for a client or agent step — the protocol identity it is
36
36
  * accepted with.
37
37
  */
38
- export type TransactionOperationOrigin = Pick<ApplicationTransactionRequest, "origin"> | Pick<ClientTransactionRequest, "origin" | "protocol" | "transport"> | Pick<AgentTransactionRequest, "origin" | "protocol" | "transport">;
38
+ export type TransactionOperationOrigin = Pick<AvApplicationTransactionRequest, "origin"> | Pick<AvClientTransactionRequest, "origin" | "protocol" | "transport"> | Pick<AvAgentTransactionRequest, "origin" | "protocol" | "transport">;
39
39
  export {};
@@ -1,8 +1,9 @@
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";
5
- import type { FrameworkTransactionRequest, TransactionOperationOrigin } from "./transaction-request.js";
6
+ import type { AvTransactionRequest, TransactionOperationOrigin } from "./transaction-request.js";
6
7
  import type { AvTransactionResponse } from "./transaction-response.js";
7
8
  /**
8
9
  * The transaction executor core. Like the operation executor it owns no
@@ -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: AvTransactionRequest, 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,24 @@ 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 settle = (policy) => {
92
+ const step = policy === undefined
93
+ ? { ...operation.request, state }
94
+ : { ...operation.request, state, policy };
95
+ steps.set(operation, step);
96
+ return step;
97
+ };
98
+ const policy = openRequestPolicy(runtime.config.policy, runtime.contracts.core, { origin: operation.request.origin, state }, caller, adapter);
99
+ return policy instanceof Promise ? policy.then(settle) : settle(policy);
100
+ };
84
101
  const admitted = [];
85
102
  const standing = [];
86
103
  for (const step of plan.operations) {
@@ -90,7 +107,16 @@ async function executeTransactionPlan(adapter, dependencies, request, plan, stat
90
107
  continue;
91
108
  }
92
109
  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 }, {
110
+ let stepRequest;
111
+ try {
112
+ const opened = stateful(step);
113
+ stepRequest = opened instanceof Promise ? await opened : opened;
114
+ }
115
+ catch (error) {
116
+ report(error, "A3000");
117
+ return transactionOperationFailure(internalFailure(), step.index);
118
+ }
119
+ const admission = await admitOperation(dependencies, stepRequest, step.contract, step.arguments, planOperationArgumentsPath(step.index), report, { requestId: request.requestId, transaction: undefined, state }, {
94
120
  select: EVERY_FIELD,
95
121
  validation: { transactionReferences: true },
96
122
  beforeBegin: true,
@@ -118,7 +144,8 @@ async function executeTransactionPlan(adapter, dependencies, request, plan, stat
118
144
  const runPlanOperation = async (operation, args, transactionPiped, executor, results, outer) => {
119
145
  const report = diagnostics.operation(operation.request, operation.index);
120
146
  const argumentsPath = planOperationArgumentsPath(operation.index);
121
- const stepRequest = stateful(operation);
147
+ const opened = stateful(operation);
148
+ const stepRequest = opened instanceof Promise ? await opened : opened;
122
149
  try {
123
150
  let live = standing[operation.index] ?? operation.references;
124
151
  if (transactionPiped) {
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.8",
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"