@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
@@ -2,21 +2,25 @@ import type { FrameworkContracts } from "../compiler/contract.compiler.js";
2
2
  import type { Framework } from "../framework/framework.js";
3
3
  import { type FrameworkDiagnosticsSink } from "../runtime/diagnostics-reporter.js";
4
4
  import type { ClientOperationRequest } from "../runtime/operation-request.js";
5
+ import type { OwnedExecution } from "../runtime/owned-execution.registry.js";
5
6
  import { type AvProtocolRequest } from "./http-request.decoder.js";
6
7
  import { type AvProtocolResponse, type UnencodableReport } from "./http-response.encoder.js";
7
8
  /**
8
9
  * What the shortcuts read off the bound Framework: its compiled Contracts, its
9
- * execution entry points and its configured diagnostics sink.
10
+ * execution entry points — the public ones, and the owned ones that run a body this
11
+ * decoder parsed without copying it again (absent for anything but a
12
+ * constructed Framework) — and its configured diagnostics sink.
10
13
  */
11
14
  export type HandlerContext = {
12
15
  readonly contracts: FrameworkContracts;
13
16
  readonly execute: Framework["execute"];
14
17
  readonly executeTransaction: Framework["executeTransaction"];
18
+ readonly owned: OwnedExecution | undefined;
15
19
  readonly sink: FrameworkDiagnosticsSink | undefined;
16
20
  };
17
21
  /**
18
22
  * Where a plan's `A3003` goes: the Framework's diagnostics sink, as a `"transaction"`
19
- * diagnostic — client scope, the request id the plan ran under, and the step the
23
+ * diagnostic — client origin, the request id the plan ran under, and the step the
20
24
  * encoder blames as `operation` when it blames one. One spelling for both
21
25
  * `protocol.encodeTransaction` and `protocol.handleTransaction`.
22
26
  */
@@ -1,10 +1,20 @@
1
1
  import { createDiagnosticsReporter, createReporter, } from "../runtime/diagnostics-reporter.js";
2
- import { decodeOperationRequest, decodeTransactionRequest, } from "./http-request.decoder.js";
2
+ import { decodeOperationRequest, decodeTransactionRequest, isParsedByDecoder, } from "./http-request.decoder.js";
3
3
  import { encodeOperationResponse, encodeTransactionResponse, } from "./http-response.encoder.js";
4
+ function operationEntry(context, request) {
5
+ return context.owned !== undefined && isParsedByDecoder(request.arguments)
6
+ ? context.owned.execute
7
+ : context.execute;
8
+ }
9
+ function transactionEntry(context, request) {
10
+ return context.owned !== undefined && isParsedByDecoder(request.plan)
11
+ ? context.owned.executeTransaction
12
+ : context.executeTransaction;
13
+ }
4
14
  export function planUnencodableReport(sink, requestId) {
5
15
  return (error, operation) => createReporter(sink, {
6
16
  kind: "transaction",
7
- scope: "client",
17
+ origin: "client",
8
18
  requestId,
9
19
  ...(operation === undefined ? {} : { operation }),
10
20
  })(error, "A3003");
@@ -17,7 +27,7 @@ export async function handleOperationRequest(context, request) {
17
27
  if (!decoded.ok) {
18
28
  return decoded.response;
19
29
  }
20
- const response = await context.execute(decoded.request);
30
+ const response = await operationEntry(context, decoded.request)(decoded.request);
21
31
  return encodeOperationResponse(response, decoded.requestId, operationUnencodableReport(context.sink, decoded.request));
22
32
  }
23
33
  export async function handleTransactionRequest(context, request) {
@@ -25,6 +35,6 @@ export async function handleTransactionRequest(context, request) {
25
35
  if (!decoded.ok) {
26
36
  return decoded.response;
27
37
  }
28
- const response = await context.executeTransaction(decoded.request);
38
+ const response = await transactionEntry(context, decoded.request)(decoded.request);
29
39
  return encodeTransactionResponse(response, decoded.requestId, planUnencodableReport(context.sink, decoded.requestId));
30
40
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * What the diagnostics sink receives when `protocol.answerAbsent` is handed a
3
+ * request for a route the ClientContract advertises, with that route's own
4
+ * method: the host mounted only part of `protocol.surface()`. The caller's
5
+ * `500 A3000` is unchanged; this error names the route the host left out, so the
6
+ * server's diagnostics show it too. It is never sent to the caller.
7
+ */
8
+ export declare class UnmountedRouteError extends Error {
9
+ /** What the ClientContract advertises: `resource.family.variant`, or `transactions`. */
10
+ readonly advertised: string;
11
+ /** The route as the request reached it: method and entrypoint-relative path. */
12
+ readonly route: string;
13
+ readonly name = "UnmountedRouteError";
14
+ constructor(
15
+ /** What the ClientContract advertises: `resource.family.variant`, or `transactions`. */
16
+ advertised: string,
17
+ /** The route as the request reached it: method and entrypoint-relative path. */
18
+ route: string);
19
+ }
@@ -0,0 +1,10 @@
1
+ export class UnmountedRouteError extends Error {
2
+ advertised;
3
+ route;
4
+ name = "UnmountedRouteError";
5
+ constructor(advertised, route) {
6
+ super(`The ClientContract advertises ${advertised}, but the host mounted no route for ${route}, so protocol.answerAbsent answered it 500 A3000. Mount every protocol.surface() entry.`);
7
+ this.advertised = advertised;
8
+ this.route = route;
9
+ }
10
+ }
@@ -0,0 +1,59 @@
1
+ /** Why a caller value could not be copied. */
2
+ export type CallerDataFailureKind = "unreadable-value" | "non-plain-value";
3
+ /** A data location inside the copied value, from its root. */
4
+ export type CallerDataPath = readonly (string | number)[];
5
+ /**
6
+ * The curated failure of a door copy: what failed and where, relative to the copied
7
+ * root. Thrown by {@link copyCallerData} and caught by every door, which answers it as
8
+ * the caller's `A2004`/`V1001` at its own address. It never carries the value.
9
+ */
10
+ export declare class CallerDataError extends Error {
11
+ readonly kind: CallerDataFailureKind;
12
+ readonly path: CallerDataPath;
13
+ /**
14
+ * The control mirror `path` starts from, when the failure is a mirror's
15
+ * tripwire read after the door; {@link locateCallerDataError} resolves it.
16
+ */
17
+ readonly within?: object | undefined;
18
+ readonly name = "CallerDataError";
19
+ constructor(kind: CallerDataFailureKind, path: CallerDataPath,
20
+ /**
21
+ * The control mirror `path` starts from, when the failure is a mirror's
22
+ * tripwire read after the door; {@link locateCallerDataError} resolves it.
23
+ */
24
+ within?: object | undefined);
25
+ }
26
+ /**
27
+ * Objects a door keeps by identity rather than copying: values the framework built
28
+ * itself (a builder's `$ref` placeholder), or a request envelope's members, each
29
+ * copied by its own door. Never a caller's own object that a later reader reads.
30
+ * Asked about every object, arrays included, before anything else of it is read.
31
+ */
32
+ export type CallerDataKeep = (value: object) => boolean;
33
+ /**
34
+ * Objects a door mirrors by their descriptors rather than copying by value: a plan's
35
+ * `$ref` control objects (see {@link mirrorControlData}). Asked about every object
36
+ * the door does not keep, before anything else of it is read.
37
+ */
38
+ export type CallerDataMirror = (value: object) => boolean;
39
+ /** How a door treats the objects it meets; copying by value unless stated. */
40
+ export type CallerDataPolicy = {
41
+ readonly keep?: CallerDataKeep;
42
+ readonly mirror?: CallerDataMirror;
43
+ };
44
+ /**
45
+ * Places a mirror tripwire's failure in the value the door copied: its path from the
46
+ * copy's root, where `error.path` started from the mirror. A failure that is not a
47
+ * tripwire's, or a mirror the copy does not hold, is returned as it is.
48
+ */
49
+ export declare function locateCallerDataError(error: CallerDataError, copy: unknown): CallerDataError;
50
+ /**
51
+ * Copies a caller's value into fresh plain data (see the module note), reading the
52
+ * caller's objects exactly once.
53
+ *
54
+ * @throws {@link CallerDataError} when a value cannot be read or is not plain data,
55
+ * naming its path from `value`.
56
+ */
57
+ export declare function copyCallerData(value: unknown, policy?: CallerDataPolicy): unknown;
58
+ /** The `V1001` message a door answers a {@link CallerDataError} with. */
59
+ export declare function callerDataFailureMessage(kind: CallerDataFailureKind): string;
@@ -0,0 +1,470 @@
1
+ import { Decimal } from "./decimal.js";
2
+ const FAILURE_MESSAGES = {
3
+ "unreadable-value": "Value could not be read.",
4
+ "non-plain-value": "Value must be plain data.",
5
+ };
6
+ export class CallerDataError extends Error {
7
+ kind;
8
+ path;
9
+ within;
10
+ name = "CallerDataError";
11
+ constructor(kind, path, within) {
12
+ super(FAILURE_MESSAGES[kind]);
13
+ this.kind = kind;
14
+ this.path = path;
15
+ this.within = within;
16
+ }
17
+ }
18
+ const NO_POLICY = {};
19
+ const OBJECT_PROTOTYPE = Object.prototype;
20
+ const DATE_PROTOTYPE = Date.prototype;
21
+ const dateValueOf = Date.prototype.getTime;
22
+ const decimalValueOf = Decimal.prototype.toString;
23
+ const FAST_WALK_DEPTH = 256;
24
+ const SLOW_WALK = Symbol("slow walk");
25
+ class FastWalkFailure {
26
+ at;
27
+ constructor(at) {
28
+ this.at = at;
29
+ }
30
+ }
31
+ const HOLE = Symbol("hole");
32
+ function copyValueType(value, prototype) {
33
+ if (prototype === DATE_PROTOTYPE || value instanceof Date) {
34
+ return new Date(dateValueOf.call(value));
35
+ }
36
+ if (value instanceof Decimal) {
37
+ return new Decimal(decimalValueOf.call(value));
38
+ }
39
+ if (value instanceof Uint8Array) {
40
+ return new Uint8Array(value);
41
+ }
42
+ return undefined;
43
+ }
44
+ function isPlainPrototype(prototype) {
45
+ return prototype === OBJECT_PROTOTYPE || prototype === null;
46
+ }
47
+ function copyRecordShallow(value, prototype) {
48
+ const copy = prototype === null
49
+ ? Object.assign(Object.create(null), value)
50
+ : { ...value };
51
+ const symbols = Object.getOwnPropertySymbols(copy);
52
+ for (let index = 0; index < symbols.length; index += 1) {
53
+ delete copy[symbols[index]];
54
+ }
55
+ return copy;
56
+ }
57
+ function defineKey(record, key, value) {
58
+ if (key === "__proto__") {
59
+ Object.defineProperty(record, key, {
60
+ value,
61
+ writable: true,
62
+ enumerable: true,
63
+ configurable: true,
64
+ });
65
+ }
66
+ else {
67
+ record[key] = value;
68
+ }
69
+ }
70
+ function copyFast(value, depth, policy) {
71
+ try {
72
+ return copyFastNode(value, depth, policy);
73
+ }
74
+ catch (error) {
75
+ if (error === SLOW_WALK || error instanceof FastWalkFailure) {
76
+ throw error;
77
+ }
78
+ throw new FastWalkFailure(value);
79
+ }
80
+ }
81
+ function copyFastNode(value, depth, policy) {
82
+ if (depth > FAST_WALK_DEPTH) {
83
+ throw SLOW_WALK;
84
+ }
85
+ if (policy.keep?.(value) === true) {
86
+ return value;
87
+ }
88
+ if (policy.mirror?.(value) === true) {
89
+ return mirrorControlData(value);
90
+ }
91
+ if (Array.isArray(value)) {
92
+ const length = value.length;
93
+ const copy = [];
94
+ for (let index = 0; index < length; index += 1) {
95
+ const entry = value[index];
96
+ if (entry === undefined && !(index in value)) {
97
+ throw SLOW_WALK;
98
+ }
99
+ copy.push(typeof entry === "object" && entry !== null
100
+ ? copyFast(entry, depth + 1, policy)
101
+ : entry);
102
+ }
103
+ return copy;
104
+ }
105
+ const prototype = Object.getPrototypeOf(value);
106
+ if (isPlainPrototype(prototype)) {
107
+ const copy = copyRecordShallow(value, prototype);
108
+ for (const key in copy) {
109
+ const entry = copy[key];
110
+ if (typeof entry === "object" && entry !== null) {
111
+ copy[key] = copyFast(entry, depth + 1, policy);
112
+ }
113
+ }
114
+ return copy;
115
+ }
116
+ const copied = copyValueType(value, prototype);
117
+ if (copied === undefined) {
118
+ throw SLOW_WALK;
119
+ }
120
+ return copied;
121
+ }
122
+ function pathOf(link) {
123
+ const segments = [];
124
+ for (let current = link; current !== undefined; current = current.parent) {
125
+ segments.push(current.segment);
126
+ }
127
+ return segments.reverse();
128
+ }
129
+ function childLink(parent, segment) {
130
+ return { parent, segment };
131
+ }
132
+ function readAt(link, read) {
133
+ try {
134
+ return read();
135
+ }
136
+ catch {
137
+ throw new CallerDataError("unreadable-value", pathOf(link));
138
+ }
139
+ }
140
+ function copySlow(value, policy, failedAt) {
141
+ let root;
142
+ const ancestors = new Map();
143
+ const stack = [
144
+ {
145
+ kind: "visit",
146
+ read: () => value,
147
+ link: undefined,
148
+ assign: (copy) => {
149
+ root = copy;
150
+ },
151
+ },
152
+ ];
153
+ while (stack.length > 0) {
154
+ const task = stack.pop();
155
+ if (task.kind === "leave") {
156
+ if (task.refuse !== false) {
157
+ throw new CallerDataError("unreadable-value", pathOf(task.refuse));
158
+ }
159
+ ancestors.delete(task.container);
160
+ continue;
161
+ }
162
+ const { link } = task;
163
+ const current = readAt(link, task.read);
164
+ if (typeof current !== "object" || current === null) {
165
+ task.assign(current);
166
+ continue;
167
+ }
168
+ if (ancestors.has(current)) {
169
+ task.assign(ancestors.get(current));
170
+ continue;
171
+ }
172
+ const refuse = current === failedAt ? link : false;
173
+ const settle = (copy) => {
174
+ if (refuse !== false) {
175
+ throw new CallerDataError("unreadable-value", pathOf(refuse));
176
+ }
177
+ task.assign(copy);
178
+ };
179
+ if (readAt(link, () => policy.keep?.(current)) === true) {
180
+ settle(current);
181
+ continue;
182
+ }
183
+ if (readAt(link, () => policy.mirror?.(current)) === true) {
184
+ settle(readAt(link, () => mirrorControlData(current)));
185
+ continue;
186
+ }
187
+ if (readAt(link, () => Array.isArray(current))) {
188
+ const source = current;
189
+ const length = readAt(link, () => source.length);
190
+ const copy = new Array(length);
191
+ task.assign(copy);
192
+ ancestors.set(current, copy);
193
+ stack.push({ kind: "leave", container: current, refuse });
194
+ for (let index = length - 1; index >= 0; index -= 1) {
195
+ stack.push({
196
+ kind: "visit",
197
+ read: () => (index in source ? source[index] : HOLE),
198
+ link: childLink(link, index),
199
+ assign: (entry) => {
200
+ if (entry !== HOLE) {
201
+ copy[index] = entry;
202
+ }
203
+ },
204
+ });
205
+ }
206
+ continue;
207
+ }
208
+ const prototype = readAt(link, () => Object.getPrototypeOf(current));
209
+ if (!isPlainPrototype(prototype)) {
210
+ const copied = readAt(link, () => copyValueType(current, prototype));
211
+ if (copied === undefined) {
212
+ throw new CallerDataError("non-plain-value", pathOf(link));
213
+ }
214
+ settle(copied);
215
+ continue;
216
+ }
217
+ const source = current;
218
+ const copy = prototype === null ? Object.create(null) : {};
219
+ task.assign(copy);
220
+ ancestors.set(current, copy);
221
+ stack.push({ kind: "leave", container: current, refuse });
222
+ const keys = readAt(link, () => Object.keys(source));
223
+ for (const key of keys) {
224
+ defineKey(copy, key, undefined);
225
+ }
226
+ for (let index = keys.length - 1; index >= 0; index -= 1) {
227
+ const key = keys[index];
228
+ stack.push({
229
+ kind: "visit",
230
+ read: () => source[key],
231
+ link: childLink(link, key),
232
+ assign: (entry) => {
233
+ defineKey(copy, key, entry);
234
+ },
235
+ });
236
+ }
237
+ }
238
+ return root;
239
+ }
240
+ const UNREADABLE = (() => {
241
+ const { proxy, revoke } = Proxy.revocable({}, {});
242
+ revoke();
243
+ return proxy;
244
+ })();
245
+ const NON_PLAIN_PROTOTYPE = Object.freeze(Object.create(null));
246
+ function reflectNode(source) {
247
+ try {
248
+ const array = Array.isArray(source);
249
+ const prototype = Object.getPrototypeOf(source);
250
+ const keys = Reflect.ownKeys(source);
251
+ const descriptors = [];
252
+ for (let index = 0; index < keys.length; index += 1) {
253
+ descriptors.push(Object.getOwnPropertyDescriptor(source, keys[index]) ??
254
+ {});
255
+ }
256
+ return { array, prototype, keys, descriptors };
257
+ }
258
+ catch {
259
+ return undefined;
260
+ }
261
+ }
262
+ function pathSegment(array, key) {
263
+ if (array) {
264
+ const index = Number(key);
265
+ if (Number.isInteger(index) && index >= 0 && String(index) === key) {
266
+ return index;
267
+ }
268
+ }
269
+ return key;
270
+ }
271
+ function mirrorControlData(value) {
272
+ const shape = reflectNode(value);
273
+ if (shape === undefined) {
274
+ throw new CallerDataError("unreadable-value", []);
275
+ }
276
+ let root;
277
+ const refuse = (kind, path) => {
278
+ throw new CallerDataError(kind, path, root);
279
+ };
280
+ const mirrors = new Map();
281
+ const stack = [
282
+ {
283
+ source: value,
284
+ shape,
285
+ link: undefined,
286
+ assign: (mirror) => {
287
+ root = mirror;
288
+ },
289
+ },
290
+ ];
291
+ while (stack.length > 0) {
292
+ const { source, shape, link, assign } = stack.pop();
293
+ const known = mirrors.get(source);
294
+ if (known !== undefined) {
295
+ assign(known);
296
+ continue;
297
+ }
298
+ if (!shape.array && !isPlainPrototype(shape.prototype)) {
299
+ let copied;
300
+ try {
301
+ copied = copyValueType(source, shape.prototype);
302
+ }
303
+ catch {
304
+ copied = undefined;
305
+ }
306
+ if (copied !== undefined) {
307
+ mirrors.set(source, copied);
308
+ assign(copied);
309
+ continue;
310
+ }
311
+ }
312
+ const plain = shape.array
313
+ ? shape.prototype === Array.prototype || shape.prototype === null
314
+ : isPlainPrototype(shape.prototype);
315
+ const target = shape.array
316
+ ? []
317
+ : shape.prototype === null
318
+ ? Object.create(null)
319
+ : {};
320
+ if (!plain) {
321
+ Object.setPrototypeOf(target, NON_PLAIN_PROTOTYPE);
322
+ }
323
+ else if (shape.array && shape.prototype === null) {
324
+ Object.setPrototypeOf(target, null);
325
+ }
326
+ let unreadable;
327
+ for (let index = 0; index < shape.keys.length; index += 1) {
328
+ const key = shape.keys[index];
329
+ const descriptor = shape.descriptors[index];
330
+ const enumerable = descriptor.enumerable === true;
331
+ if (shape.array && key === "length") {
332
+ if (typeof descriptor.value === "number") {
333
+ target.length = descriptor.value;
334
+ }
335
+ continue;
336
+ }
337
+ if (typeof key === "symbol") {
338
+ Object.defineProperty(target, key, {
339
+ value: undefined,
340
+ enumerable,
341
+ configurable: true,
342
+ });
343
+ continue;
344
+ }
345
+ if (!("value" in descriptor)) {
346
+ const at = childLink(link, pathSegment(shape.array, key));
347
+ Object.defineProperty(target, key, {
348
+ get: () => refuse("unreadable-value", pathOf(at)),
349
+ enumerable,
350
+ configurable: true,
351
+ });
352
+ continue;
353
+ }
354
+ const store = enumerable &&
355
+ descriptor.writable === true &&
356
+ descriptor.configurable === true
357
+ ? (member) => {
358
+ defineKey(target, key, member);
359
+ }
360
+ : (member) => {
361
+ Object.defineProperty(target, key, {
362
+ value: member,
363
+ writable: descriptor.writable === true,
364
+ enumerable,
365
+ configurable: true,
366
+ });
367
+ };
368
+ const member = descriptor.value;
369
+ if (typeof member !== "object" || member === null) {
370
+ store(member);
371
+ continue;
372
+ }
373
+ if (mirrors.has(member)) {
374
+ store(mirrors.get(member));
375
+ continue;
376
+ }
377
+ const memberLink = childLink(link, pathSegment(shape.array, key));
378
+ const memberShape = reflectNode(member);
379
+ if (memberShape === undefined) {
380
+ store(UNREADABLE);
381
+ unreadable ??= new Map();
382
+ unreadable.set(key, memberLink);
383
+ continue;
384
+ }
385
+ store(undefined);
386
+ stack.push({
387
+ source: member,
388
+ shape: memberShape,
389
+ link: memberLink,
390
+ assign: store,
391
+ });
392
+ }
393
+ const node = plain && unreadable === undefined
394
+ ? target
395
+ : new Proxy(target, {
396
+ get: (into, key, receiver) => {
397
+ if (!plain) {
398
+ return refuse("non-plain-value", pathOf(link));
399
+ }
400
+ const at = typeof key === "string" ? unreadable?.get(key) : undefined;
401
+ return at === undefined
402
+ ? Reflect.get(into, key, receiver)
403
+ : refuse("unreadable-value", pathOf(at));
404
+ },
405
+ });
406
+ mirrors.set(source, node);
407
+ assign(node);
408
+ }
409
+ return root;
410
+ }
411
+ export function locateCallerDataError(error, copy) {
412
+ const within = error.within;
413
+ if (within === undefined) {
414
+ return error;
415
+ }
416
+ const at = pathTo(copy, within);
417
+ return at === undefined
418
+ ? error
419
+ : new CallerDataError(error.kind, [...at, ...error.path]);
420
+ }
421
+ function pathTo(copy, target) {
422
+ const seen = new Set();
423
+ const stack = [
424
+ { value: copy, path: [] },
425
+ ];
426
+ while (stack.length > 0) {
427
+ const { value, path } = stack.pop();
428
+ if (value === target) {
429
+ return path;
430
+ }
431
+ if (typeof value !== "object" ||
432
+ value === null ||
433
+ value === UNREADABLE ||
434
+ seen.has(value)) {
435
+ continue;
436
+ }
437
+ seen.add(value);
438
+ const array = Array.isArray(value);
439
+ for (const key of Object.getOwnPropertyNames(value)) {
440
+ if (array && key === "length") {
441
+ continue;
442
+ }
443
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
444
+ if (descriptor !== undefined && "value" in descriptor) {
445
+ stack.push({
446
+ value: descriptor.value,
447
+ path: [...path, pathSegment(array, key)],
448
+ });
449
+ }
450
+ }
451
+ }
452
+ return undefined;
453
+ }
454
+ function guardNative(operation, onFailure) {
455
+ try {
456
+ return operation();
457
+ }
458
+ catch (failure) {
459
+ return onFailure(failure);
460
+ }
461
+ }
462
+ export function copyCallerData(value, policy = NO_POLICY) {
463
+ if (typeof value !== "object" || value === null) {
464
+ return value;
465
+ }
466
+ return guardNative(() => copyFast(value, 0, policy), (failure) => copySlow(value, policy, failure instanceof FastWalkFailure ? failure.at : undefined));
467
+ }
468
+ export function callerDataFailureMessage(kind) {
469
+ return FAILURE_MESSAGES[kind];
470
+ }
@@ -0,0 +1,56 @@
1
+ import type { ValidationIssue } from "../operations/validation-code.js";
2
+ import { CallerDataError, type CallerDataPath, type CallerDataPolicy } from "./caller-data.copier.js";
3
+ import type { FrameworkMisuseEvent, MisuseReporter } from "./misuse-reporter.js";
4
+ /**
5
+ * Where the arguments an execution entry receives came from.
6
+ *
7
+ * - `"caller"`: a server-side caller's own objects — copied at the door.
8
+ * - `"owned"`: data no caller holds a reference to — a body the protocol decoder
9
+ * parsed with `JSON.parse` for this request alone, or a plan the typed runner
10
+ * assembled from copies its builders took. It is never copied again.
11
+ *
12
+ * Only core's own modules choose `"owned"`: the public `framework.execute` and
13
+ * `executeTransaction` always pass `"caller"`.
14
+ */
15
+ export type ArgumentsSource = "caller" | "owned";
16
+ /** The operation identity a misuse event may name, as far as it could be read. */
17
+ export type MisuseIdentity = Omit<FrameworkMisuseEvent, "kind" | "path">;
18
+ /**
19
+ * The identity of a request the framework holds a copy of, keeping only what is
20
+ * identity: an origin, a Resource name and a standard operation, each present only
21
+ * when it is one. Nothing the caller wrote anywhere else is carried.
22
+ */
23
+ export declare function misuseIdentityOf(request: {
24
+ readonly origin?: unknown;
25
+ readonly resource?: unknown;
26
+ readonly family?: unknown;
27
+ readonly variant?: unknown;
28
+ }): MisuseIdentity;
29
+ /**
30
+ * The caller's `V1001` for a refused value at `address` (the door's own spelling of
31
+ * where the copied value stands), reported once to the misuse hook.
32
+ */
33
+ export declare function refuseCallerData(error: CallerDataError, address: CallerDataPath, identity: MisuseIdentity, misuse: MisuseReporter): ValidationIssue;
34
+ /**
35
+ * A door's copy of one caller value, or the failure it met. The door holds it until
36
+ * its answer is due — a plan answers its steps in order — so the failure is a value
37
+ * here, not yet a response.
38
+ */
39
+ export type CallerDataCopy = {
40
+ readonly ok: true;
41
+ readonly value: unknown;
42
+ } | {
43
+ readonly ok: false;
44
+ readonly error: CallerDataError;
45
+ };
46
+ /** Copies `value` at a door, keeping {@link copyCallerData}'s refusal as a value. */
47
+ export declare function copyAtDoor(value: unknown, policy?: CallerDataPolicy): CallerDataCopy;
48
+ /**
49
+ * A request envelope handed to `framework.execute` or `executeTransaction`, copied at
50
+ * the door: its own members read once (a revoked envelope or a throwing member getter
51
+ * is refused at `[]` or `[member]`), the `members` named here — the protocol identity,
52
+ * the transport context — copied whole, and every other member kept as it is for its
53
+ * own door: the arguments, copied after the availability check, and a plan, which the
54
+ * plan validator inspects and copies step by step.
55
+ */
56
+ export declare function copyEnvelopeAtDoor(request: unknown, members: readonly string[]): CallerDataCopy;