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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +338 -0
  2. package/README.md +100 -3
  3. package/dist/adapters/adapter-model.d.ts +8 -0
  4. package/dist/adapters/adapter.interface.d.ts +6 -2
  5. package/dist/compiler/contract-compiler-diagnostic.d.ts +6 -0
  6. package/dist/compiler/contract.compiler.js +67 -0
  7. package/dist/config/config.resolver.d.ts +3 -0
  8. package/dist/config/config.resolver.js +1 -0
  9. package/dist/config/config.validator.js +6 -0
  10. package/dist/config/define-fields.d.ts +10 -3
  11. package/dist/config/framework-config.d.ts +4 -1
  12. package/dist/config/framework-fields-config.d.ts +6 -0
  13. package/dist/contracts/client-contract-structure.validator.d.ts +21 -1
  14. package/dist/contracts/client-contract-structure.validator.js +99 -2
  15. package/dist/contracts/contract-canonicalizer.js +32 -0
  16. package/dist/contracts/contract.d.ts +7 -0
  17. package/dist/contracts/field.d.ts +10 -0
  18. package/dist/contracts/json-shape.d.ts +65 -0
  19. package/dist/contracts/json-shape.js +1 -0
  20. package/dist/contracts/reference-write-limit.d.ts +7 -0
  21. package/dist/contracts/reference-write-limit.js +1 -0
  22. package/dist/contracts/scalar-value-type.d.ts +12 -1
  23. package/dist/framework/contract-view.d.ts +126 -21
  24. package/dist/framework/framework.d.ts +5 -3
  25. package/dist/framework/framework.factory.js +56 -13
  26. package/dist/index.d.ts +8 -40
  27. package/dist/index.js +0 -20
  28. package/dist/internal.d.ts +35 -0
  29. package/dist/internal.js +16 -0
  30. package/dist/json/avz.d.ts +96 -0
  31. package/dist/json/avz.js +212 -0
  32. package/dist/json/index.d.ts +6 -0
  33. package/dist/json/index.js +1 -0
  34. package/dist/json/json-shape.registry.d.ts +22 -0
  35. package/dist/json/json-shape.registry.js +16 -0
  36. package/dist/json/zod-issue.translator.d.ts +51 -0
  37. package/dist/json/zod-issue.translator.js +142 -0
  38. package/dist/operations/operation-arguments.d.ts +151 -101
  39. package/dist/operations/operation-identity.d.ts +2 -0
  40. package/dist/operations/operation-identity.js +3 -0
  41. package/dist/operations/operation-validator-core.d.ts +1 -1
  42. package/dist/operations/operation-validator-core.js +28 -10
  43. package/dist/operations/operation-validator.d.ts +20 -5
  44. package/dist/operations/operation-validator.js +26 -10
  45. package/dist/operations/projection-scope.resolver.d.ts +1 -1
  46. package/dist/operations/structural-limits.walker.d.ts +19 -0
  47. package/dist/operations/structural-limits.walker.js +344 -0
  48. package/dist/protocol/absent-route.responder.d.ts +2 -1
  49. package/dist/protocol/absent-route.responder.js +32 -6
  50. package/dist/protocol/http-request.decoder.d.ts +6 -0
  51. package/dist/protocol/http-request.decoder.js +11 -3
  52. package/dist/protocol/protocol.binder.d.ts +4 -2
  53. package/dist/protocol/protocol.binder.js +3 -1
  54. package/dist/protocol/protocol.handler.d.ts +6 -2
  55. package/dist/protocol/protocol.handler.js +14 -4
  56. package/dist/protocol/unmounted-route.error.d.ts +19 -0
  57. package/dist/protocol/unmounted-route.error.js +10 -0
  58. package/dist/runtime/caller-data.copier.d.ts +59 -0
  59. package/dist/runtime/caller-data.copier.js +470 -0
  60. package/dist/runtime/caller-data.door.d.ts +56 -0
  61. package/dist/runtime/caller-data.door.js +65 -0
  62. package/dist/runtime/computed-read-runtime.js +3 -10
  63. package/dist/runtime/computed-value.error.d.ts +13 -0
  64. package/dist/runtime/computed-value.error.js +9 -0
  65. package/dist/runtime/create-requirement.d.ts +43 -7
  66. package/dist/runtime/create-requirement.js +89 -9
  67. package/dist/runtime/diagnostics-reporter.d.ts +20 -4
  68. package/dist/runtime/diagnostics-reporter.js +11 -4
  69. package/dist/runtime/error-boundary.d.ts +8 -0
  70. package/dist/runtime/error-boundary.js +65 -35
  71. package/dist/runtime/field-behavior.d.ts +2 -2
  72. package/dist/runtime/framework-error.d.ts +12 -0
  73. package/dist/runtime/misuse-reporter.d.ts +32 -0
  74. package/dist/runtime/misuse-reporter.js +19 -0
  75. package/dist/runtime/mutation-planner.js +13 -11
  76. package/dist/runtime/operation-request.d.ts +4 -11
  77. package/dist/runtime/operation-runtime.d.ts +14 -7
  78. package/dist/runtime/operation-runtime.js +37 -18
  79. package/dist/runtime/outcome-mapper.js +12 -2
  80. package/dist/runtime/output-validator.d.ts +10 -0
  81. package/dist/runtime/output-validator.js +11 -0
  82. package/dist/runtime/owned-execution.registry.d.ts +13 -0
  83. package/dist/runtime/owned-execution.registry.js +7 -0
  84. package/dist/runtime/pipeline.d.ts +19 -7
  85. package/dist/runtime/referential-action.d.ts +7 -0
  86. package/dist/runtime/request-envelope.validator.d.ts +5 -0
  87. package/dist/runtime/request-envelope.validator.js +118 -0
  88. package/dist/runtime/scalar-runtime.d.ts +30 -2
  89. package/dist/runtime/scalar-runtime.js +162 -79
  90. package/dist/transactions/transaction-plan-assembler.d.ts +5 -1
  91. package/dist/transactions/transaction-plan-assembler.js +74 -75
  92. package/dist/transactions/transaction-plan-validator.d.ts +30 -7
  93. package/dist/transactions/transaction-plan-validator.js +204 -153
  94. package/dist/transactions/transaction-reference-collector.d.ts +30 -0
  95. package/dist/transactions/transaction-reference-collector.js +204 -0
  96. package/dist/transactions/transaction-reference-resolver.js +10 -5
  97. package/dist/transactions/transaction-reference-validator.js +24 -17
  98. package/dist/transactions/transaction-reference.d.ts +23 -51
  99. package/dist/transactions/transaction-request.d.ts +3 -3
  100. package/dist/transactions/transaction-response.d.ts +6 -1
  101. package/dist/transactions/transaction-response.js +24 -1
  102. package/dist/transactions/transaction-runtime.d.ts +4 -2
  103. package/dist/transactions/transaction-runtime.js +4 -3
  104. package/package.json +17 -3
  105. package/dist/operations/operation-boolean-nodes.d.ts +0 -11
  106. package/dist/operations/operation-boolean-nodes.js +0 -283
  107. package/dist/operations/operation-nesting.d.ts +0 -8
  108. package/dist/operations/operation-nesting.js +0 -307
@@ -1,4 +1,5 @@
1
1
  import { validationIssue } from "../operations/validation-code.js";
2
+ import { copyAtDoor, refuseCallerData } from "../runtime/caller-data.door.js";
2
3
  import { operationFailure } from "../runtime/operation-response.js";
3
4
  import { mapValidationOutcome } from "../runtime/outcome-mapper.js";
4
5
  import { encodeOperationArgumentsForWire } from "./operation-arguments-encoder.js";
@@ -11,63 +12,63 @@ function isPlainRecord(value) {
11
12
  const prototype = Object.getPrototypeOf(value);
12
13
  return prototype === Object.prototype || prototype === null;
13
14
  }
14
- function referenceKeys(identity) {
15
- switch (identity.family) {
16
- case "create":
17
- case "update":
18
- return ["data"];
19
- case "upsert":
20
- return ["create", "update"];
21
- default:
22
- return [];
15
+ const KEEP = Symbol("keep");
16
+ function rebuildArguments(value, path, leaf, ancestors = new Set()) {
17
+ const replaced = leaf(value, path);
18
+ if (replaced !== KEEP) {
19
+ return replaced;
23
20
  }
24
- }
25
- function referenceContainers(identity, args) {
26
- if (!isPlainRecord(args)) {
27
- return [];
28
- }
29
- return referenceKeys(identity).flatMap((key) => {
30
- const value = args[key];
31
- if (Array.isArray(value)) {
32
- return value.flatMap((entry, index) => isPlainRecord(entry) ? [{ path: [key, index], record: entry }] : []);
33
- }
34
- return isPlainRecord(value) ? [{ path: [key], record: value }] : [];
35
- });
36
- }
37
- function replaceContainers(identity, args, rebuild) {
38
- if (!isPlainRecord(args)) {
39
- return args;
21
+ if (!Array.isArray(value) && !isPlainRecord(value)) {
22
+ return value;
40
23
  }
41
- const result = { ...args };
42
- for (const key of referenceKeys(identity)) {
43
- const value = args[key];
44
- if (Array.isArray(value)) {
45
- result[key] = value.map((entry, index) => isPlainRecord(entry)
46
- ? rebuild({ path: [key, index], record: entry })
47
- : entry);
48
- }
49
- else if (isPlainRecord(value)) {
50
- result[key] = rebuild({ path: [key], record: value });
51
- }
24
+ if (ancestors.has(value)) {
25
+ return value;
52
26
  }
53
- return result;
27
+ ancestors.add(value);
28
+ const rebuilt = Array.isArray(value)
29
+ ? value.map((entry, index) => rebuildArguments(entry, [...path, index], leaf, ancestors))
30
+ : Object.fromEntries(Object.entries(value).map(([key, entry]) => [
31
+ key,
32
+ rebuildArguments(entry, [...path, key], leaf, ancestors),
33
+ ]));
34
+ ancestors.delete(value);
35
+ return rebuilt;
54
36
  }
55
37
  function operationEntryFailure(operation, message) {
56
38
  return transactionOperationFailure(operationFailure("A2004", {
57
39
  message: "Transaction plan failed framework validation.",
58
- issues: [validationIssue("V1001", ["operation"], message)],
40
+ issues: [validationIssue("V1001", [], message)],
59
41
  }), operation);
60
42
  }
61
43
  function referenceFailure(issue, operation) {
62
44
  return transactionOperationFailure(mapValidationOutcome([issue]), operation);
63
45
  }
64
- export function createTransactionPlanAssembler(contracts, protocol) {
46
+ export function createTransactionPlanAssembler(contracts, protocol, misuse) {
65
47
  const handles = new WeakMap();
66
48
  const placeholders = new WeakMap();
67
49
  const originOf = (origin) => origin === "application"
68
- ? { scope: "application" }
69
- : { scope: "client", protocol };
70
- const snapshot = (identity, args) => replaceContainers(identity, args, ({ record }) => ({ ...record }));
50
+ ? { origin: "application" }
51
+ : { origin: "client", protocol };
52
+ const snapshot = (args) => {
53
+ const copy = copyAtDoor(args, {
54
+ keep: (value) => placeholders.has(value),
55
+ });
56
+ return copy.ok ? { args: copy.value } : { args: {}, refused: copy.error };
57
+ };
58
+ const placeholderSites = (args) => {
59
+ const sites = [];
60
+ rebuildArguments(args, [], (value, path) => {
61
+ const placeholder = typeof value === "object" && value !== null
62
+ ? placeholders.get(value)
63
+ : undefined;
64
+ if (placeholder === undefined) {
65
+ return KEEP;
66
+ }
67
+ sites.push({ path, object: value, placeholder });
68
+ return value;
69
+ });
70
+ return sites;
71
+ };
71
72
  const defer = (origin, resource, identity, args) => {
72
73
  const handle = Object.freeze({
73
74
  $ref: (field) => {
@@ -82,11 +83,22 @@ export function createTransactionPlanAssembler(contracts, protocol) {
82
83
  origin,
83
84
  resource,
84
85
  identity,
85
- args: snapshot(identity, args),
86
+ ...snapshot(args),
86
87
  });
87
88
  return handle;
88
89
  };
89
- const assemble = async (operations) => {
90
+ const assemble = async (input) => {
91
+ const list = copyAtDoor(input, { keep: (value) => value !== input });
92
+ if (!list.ok) {
93
+ return {
94
+ ok: false,
95
+ response: operationFailure("A2004", {
96
+ message: "Transaction plan failed framework validation.",
97
+ issues: [refuseCallerData(list.error, ["operations"], {}, misuse)],
98
+ }),
99
+ };
100
+ }
101
+ const operations = list.value;
90
102
  if (!Array.isArray(operations)) {
91
103
  return {
92
104
  ok: false,
@@ -120,17 +132,12 @@ export function createTransactionPlanAssembler(contracts, protocol) {
120
132
  records.push(record);
121
133
  }
122
134
  for (const [index, record] of records.entries()) {
123
- for (const { path, record: container } of referenceContainers(record.identity, record.args)) {
124
- for (const [field, value] of Object.entries(container)) {
125
- const placeholder = typeof value === "object" && value !== null
126
- ? placeholders.get(value)
127
- : undefined;
128
- if (placeholder !== undefined && !positions.has(placeholder.source)) {
129
- return {
130
- ok: false,
131
- response: referenceFailure(validationIssue("V1010", ["operations", index, "args", ...path, field, "$ref"], "Transaction reference names an operation that is not in this transaction."), index),
132
- };
133
- }
135
+ for (const { path, placeholder } of placeholderSites(record.args)) {
136
+ if (!positions.has(placeholder.source)) {
137
+ return {
138
+ ok: false,
139
+ response: referenceFailure(validationIssue("V1010", ["operations", index, "args", ...path, "$ref"], "Transaction reference names an operation that is not in this transaction."), index),
140
+ };
134
141
  }
135
142
  }
136
143
  }
@@ -145,26 +152,17 @@ export function createTransactionPlanAssembler(contracts, protocol) {
145
152
  };
146
153
  const materialize = async (index) => {
147
154
  const record = records[index];
148
- const references = [];
149
- const args = replaceContainers(record.identity, record.args, ({ record: container }) => {
150
- const bound = { ...container };
151
- for (const [field, value] of Object.entries(container)) {
152
- const placeholder = typeof value === "object" && value !== null
153
- ? placeholders.get(value)
154
- : undefined;
155
- if (placeholder === undefined) {
156
- continue;
157
- }
158
- const operation = positions.get(placeholder.source);
159
- references.push(nodeAt(operation).then(({ fingerprint }) => {
160
- bound[field] = {
161
- $ref: { operation, fingerprint, path: [placeholder.field] },
162
- };
163
- }));
164
- }
165
- return bound;
166
- });
167
- await Promise.all(references);
155
+ const bound = new Map();
156
+ await Promise.all(placeholderSites(record.args).map(async ({ object, placeholder }) => {
157
+ const operation = positions.get(placeholder.source);
158
+ const { fingerprint } = await nodeAt(operation);
159
+ bound.set(object, {
160
+ $ref: { operation, fingerprint, path: [placeholder.field] },
161
+ });
162
+ }));
163
+ const args = rebuildArguments(record.args, [], (value) => typeof value === "object" && value !== null && bound.has(value)
164
+ ? bound.get(value)
165
+ : KEEP);
168
166
  return {
169
167
  resource: record.resource,
170
168
  family: record.identity.family,
@@ -195,6 +193,7 @@ export function createTransactionPlanAssembler(contracts, protocol) {
195
193
  operations: await Promise.all(records.map((_, index) => nodeAt(index))),
196
194
  },
197
195
  origins: records.map((record) => originOf(record.origin)),
196
+ refused: new Map(records.flatMap((record, index) => record.refused === undefined ? [] : [[index, record.refused]])),
198
197
  };
199
198
  };
200
199
  return { defer, assemble };
@@ -2,6 +2,7 @@ import type { AdapterModel } from "../adapters/adapter-model.js";
2
2
  import type { Contract } from "../contracts/contract.js";
3
3
  import type { ScalarField } from "../contracts/field.js";
4
4
  import type { SemanticOperationArguments } from "../operations/operation-validator.js";
5
+ import { CallerDataError } from "../runtime/caller-data.copier.js";
5
6
  import { type ReadProjectionPlan } from "../runtime/computed-read-runtime.js";
6
7
  import type { FrameworkOperationRequest } from "../runtime/operation-request.js";
7
8
  import type { OperationFailureResponse } from "../runtime/operation-response.js";
@@ -9,15 +10,37 @@ import type { OperationPipelineDependencies } from "../runtime/operation-runtime
9
10
  import type { CascadeClosure } from "../runtime/referential-action.js";
10
11
  import type { OperationFingerprint } from "./deferred-operation.js";
11
12
  import type { TransactionReference } from "./transaction-reference.js";
13
+ import { type TransactionSlotShape } from "./transaction-reference.js";
14
+ import { type TransactionReferenceCandidateSlot } from "./transaction-reference-collector.js";
12
15
  import type { FrameworkTransactionRequest, TransactionOperationOrigin } from "./transaction-request.js";
13
- export type TransactionReferenceCandidateSlot = {
14
- readonly path: readonly (string | number)[];
15
- readonly field: ScalarField<Contract>;
16
- readonly candidate: unknown;
16
+ /**
17
+ * Where a plan's step arguments came from, so the validator knows whether to copy
18
+ * them at the door (see `ArgumentsSource`):
19
+ *
20
+ * - `"caller"` — `framework.executeTransaction`'s caller's own objects: every step's
21
+ * arguments are copied when the plan is handed in, before the validator's first
22
+ * await, so a step revoked or changed after the call is never read again;
23
+ * - `"owned"` — a plan the protocol decoder parsed, or the typed runner assembled
24
+ * from its builders' copies; `refused` names the steps whose arguments a builder
25
+ * could not copy, answered at their turn.
26
+ */
27
+ export type PlanArguments = {
28
+ readonly source: "caller";
29
+ } | {
30
+ readonly source: "owned";
31
+ readonly refused?: ReadonlyMap<number, CallerDataError>;
17
32
  };
33
+ export type { TransactionReferenceCandidateSlot } from "./transaction-reference-collector.js";
34
+ /** One validated Transaction Slot: where it is, what it holds, and its reference. */
18
35
  export type TransactionReferenceSlot = {
19
36
  readonly path: readonly (string | number)[];
37
+ /** The destination field. */
20
38
  readonly field: ScalarField<Contract>;
39
+ readonly shape: TransactionSlotShape;
40
+ /** The slot is a scalar value of mutation data: what it resolves to is written. */
41
+ readonly written: boolean;
42
+ /** The source field, under its own step's Contract: what the slot receives. */
43
+ readonly source: ScalarField<Contract>;
21
44
  readonly reference: TransactionReference;
22
45
  };
23
46
  export type CandidateTransactionOperation = {
@@ -48,10 +71,10 @@ export type ValidatedTransactionPlan = {
48
71
  /**
49
72
  * What validating a plan needs beyond one operation's pipeline: the cascade
50
73
  * closure over the Adapter's referential actions, resolved once when the
51
- * Framework is constructed. The create requirements are not among them: they
74
+ * Framework is constructed. The record-write facts are not among them: they
52
75
  * are checked when each operation runs, after its pipes.
53
76
  */
54
- export type TransactionPlanDependencies<M extends AdapterModel> = Omit<OperationPipelineDependencies<M>, "createRequirements"> & {
77
+ export type TransactionPlanDependencies<M extends AdapterModel> = Omit<OperationPipelineDependencies<M>, "recordWrites"> & {
55
78
  readonly cascades: CascadeClosure;
56
79
  };
57
80
  export type TransactionPlanValidationResult = {
@@ -70,4 +93,4 @@ export declare function transactionsUnavailableFailure(): OperationFailureRespon
70
93
  /**
71
94
  * Whole-plan Framework Validation Layer for transaction structure and identity.
72
95
  */
73
- export declare function validateTransactionPlan<M extends AdapterModel>(dependencies: TransactionPlanDependencies<M>, request: FrameworkTransactionRequest, origins?: readonly TransactionOperationOrigin[]): Promise<TransactionPlanValidationResult>;
96
+ export declare function validateTransactionPlan<M extends AdapterModel>(dependencies: TransactionPlanDependencies<M>, request: FrameworkTransactionRequest, origins?: readonly TransactionOperationOrigin[], planArguments?: PlanArguments): Promise<TransactionPlanValidationResult>;