@aventara/core 0.1.0-pilot.1 → 0.1.0-pilot.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/adapter-field.d.ts +4 -4
- package/dist/adapters/adapter-resource.d.ts +10 -12
- package/dist/adapters/adapter.interface.d.ts +20 -10
- package/dist/compiler/contract-compiler-diagnostic.d.ts +3 -4
- package/dist/compiler/contract.compiler.d.ts +5 -4
- package/dist/compiler/contract.compiler.js +0 -132
- package/dist/config/config.resolver.d.ts +2 -2
- package/dist/config/config.resolver.js +0 -5
- package/dist/config/config.validator.d.ts +3 -3
- package/dist/config/config.validator.js +0 -8
- package/dist/config/define-fields.d.ts +2 -7
- package/dist/config/define-fields.js +0 -15
- package/dist/config/effective-fields.d.ts +4 -8
- package/dist/config/entrypoint.normalizer.d.ts +7 -7
- package/dist/config/entrypoint.normalizer.js +0 -22
- package/dist/config/framework-behaviors-config.d.ts +6 -6
- package/dist/config/framework-config.d.ts +3 -5
- package/dist/config/framework-restrictions-config.d.ts +19 -29
- package/dist/config/pipeline-config.d.ts +8 -18
- package/dist/contracts/capability.d.ts +18 -21
- package/dist/contracts/capability.js +0 -75
- package/dist/contracts/client-contract-structure.validator.d.ts +15 -22
- package/dist/contracts/client-contract-structure.validator.js +0 -27
- package/dist/contracts/contract-canonicalizer.js +0 -96
- package/dist/contracts/contract-hash.js +0 -2
- package/dist/contracts/contract-limits.js +0 -1
- package/dist/contracts/contract-lookup.js +0 -10
- package/dist/contracts/contract-protocol.js +0 -10
- package/dist/contracts/field.d.ts +23 -26
- package/dist/contracts/field.js +0 -107
- package/dist/contracts/lifecycle-trait.js +0 -5
- package/dist/contracts/resource-operations.d.ts +17 -18
- package/dist/contracts/resource-operations.js +0 -63
- package/dist/contracts/scalar-value-type.d.ts +12 -22
- package/dist/contracts/scalar.js +0 -1
- package/dist/contracts/wire-scalar-format.d.ts +20 -20
- package/dist/contracts/wire-scalar-format.js +0 -26
- package/dist/framework/contract-view.d.ts +7 -8
- package/dist/framework/framework-construction.error.js +0 -1
- package/dist/framework/framework.d.ts +26 -27
- package/dist/framework/framework.factory.js +0 -49
- package/dist/framework/operation-facade.js +0 -17
- package/dist/operations/operation-arguments.d.ts +36 -46
- package/dist/operations/operation-boolean-nodes.js +0 -16
- package/dist/operations/operation-call.d.ts +4 -9
- package/dist/operations/operation-identity.js +0 -1
- package/dist/operations/operation-nesting.js +0 -5
- package/dist/operations/operation-result.d.ts +4 -4
- package/dist/operations/operation-validator-core.d.ts +2 -2
- package/dist/operations/operation-validator-core.js +6 -71
- package/dist/operations/operation-validator.d.ts +4 -4
- package/dist/operations/operation-validator.js +0 -12
- package/dist/operations/projection-scope.resolver.d.ts +8 -10
- package/dist/operations/projection-scope.resolver.js +0 -35
- package/dist/operations/validation-code.d.ts +11 -18
- package/dist/operations/validation-code.js +0 -24
- package/dist/operations/write-scope.resolver.d.ts +4 -7
- package/dist/operations/write-scope.resolver.js +0 -37
- package/dist/protocol/absent-route.responder.d.ts +33 -48
- package/dist/protocol/absent-route.responder.js +0 -55
- package/dist/protocol/av-protocol.d.ts +6 -6
- package/dist/protocol/av-protocol.js +0 -10
- package/dist/protocol/contract-document.responder.d.ts +15 -18
- package/dist/protocol/contract-document.responder.js +0 -33
- package/dist/protocol/http-request.decoder.d.ts +55 -61
- package/dist/protocol/http-request.decoder.js +0 -62
- package/dist/protocol/http-request.reader.d.ts +12 -13
- package/dist/protocol/http-request.reader.js +0 -45
- package/dist/protocol/http-response.encoder.d.ts +17 -18
- package/dist/protocol/http-response.encoder.js +0 -59
- package/dist/protocol/http-status.table.d.ts +7 -11
- package/dist/protocol/http-status.table.js +1 -15
- package/dist/protocol/index.d.ts +5 -5
- package/dist/protocol/index.js +0 -9
- package/dist/protocol/operation-response.guard.d.ts +12 -19
- package/dist/protocol/operation-response.guard.js +0 -29
- package/dist/protocol/protocol-failure.d.ts +21 -26
- package/dist/protocol/protocol-failure.js +0 -12
- package/dist/protocol/protocol-headers.d.ts +25 -30
- package/dist/protocol/protocol-headers.js +0 -66
- package/dist/protocol/protocol-surface.d.ts +25 -29
- package/dist/protocol/protocol-surface.js +0 -19
- package/dist/protocol/protocol.binder.d.ts +58 -71
- package/dist/protocol/protocol.binder.js +0 -8
- package/dist/protocol/protocol.handler.d.ts +28 -32
- package/dist/protocol/protocol.handler.js +0 -43
- package/dist/runtime/computed-read-runtime.js +0 -29
- package/dist/runtime/create-requirement.d.ts +65 -0
- package/dist/runtime/create-requirement.js +141 -0
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +0 -8
- package/dist/runtime/diagnostics-reporter.d.ts +6 -6
- package/dist/runtime/diagnostics-reporter.js +0 -35
- package/dist/runtime/error-boundary.d.ts +3 -3
- package/dist/runtime/error-boundary.js +2 -167
- package/dist/runtime/execution-options.d.ts +4 -4
- package/dist/runtime/field-behavior.d.ts +6 -9
- package/dist/runtime/framework-error.js +0 -4
- package/dist/runtime/mutation-planner.d.ts +5 -6
- package/dist/runtime/mutation-planner.js +0 -23
- package/dist/runtime/operation-response.d.ts +1 -1
- package/dist/runtime/operation-response.js +1 -17
- package/dist/runtime/operation-runtime.d.ts +24 -22
- package/dist/runtime/operation-runtime.js +18 -52
- package/dist/runtime/outcome-mapper.js +0 -22
- package/dist/runtime/output-validator.js +0 -1
- package/dist/runtime/pipe-output.validator.d.ts +48 -0
- package/dist/runtime/pipe-output.validator.js +79 -0
- package/dist/runtime/pipeline.d.ts +20 -26
- package/dist/runtime/referential-action.d.ts +14 -19
- package/dist/runtime/referential-action.js +0 -23
- package/dist/runtime/request-id.js +0 -21
- package/dist/runtime/scalar-runtime.d.ts +14 -16
- package/dist/runtime/scalar-runtime.js +12 -57
- package/dist/transactions/deferred-operation.d.ts +9 -14
- package/dist/transactions/operation-arguments-encoder.d.ts +0 -1
- package/dist/transactions/operation-arguments-encoder.js +0 -14
- package/dist/transactions/operation-fingerprint.js +0 -1
- package/dist/transactions/transaction-envelope.js +0 -14
- package/dist/transactions/transaction-integrity-error.d.ts +10 -11
- package/dist/transactions/transaction-integrity-error.js +0 -5
- package/dist/transactions/transaction-plan-assembler.d.ts +0 -1
- package/dist/transactions/transaction-plan-assembler.js +0 -35
- package/dist/transactions/transaction-plan-validator.d.ts +5 -5
- package/dist/transactions/transaction-plan-validator.js +0 -63
- package/dist/transactions/transaction-reference-resolver.d.ts +12 -13
- package/dist/transactions/transaction-reference-resolver.js +0 -16
- package/dist/transactions/transaction-reference-validator.js +0 -8
- package/dist/transactions/transaction-reference.d.ts +19 -43
- package/dist/transactions/transaction-reference.js +0 -11
- package/dist/transactions/transaction-request.d.ts +4 -5
- package/dist/transactions/transaction-response.d.ts +1 -1
- package/dist/transactions/transaction-runtime.d.ts +2 -3
- package/dist/transactions/transaction-runtime.js +5 -44
- package/dist/transactions/transaction-slot-path-error.d.ts +10 -11
- package/dist/transactions/transaction-slot-path-error.js +0 -7
- package/dist/transactions/transaction-slot-writer.d.ts +12 -12
- package/dist/transactions/transaction-slot-writer.js +0 -34
- package/package.json +2 -2
|
@@ -4,20 +4,12 @@ import { routeTransactionReferenceCandidate } from "../transactions/transaction-
|
|
|
4
4
|
import { Decimal } from "./decimal.js";
|
|
5
5
|
const INT_MIN = -(2 ** 31);
|
|
6
6
|
const INT_MAX = 2 ** 31 - 1;
|
|
7
|
+
const BIGINT_MIN = -(2n ** 63n);
|
|
8
|
+
const BIGINT_MAX = 2n ** 63n - 1n;
|
|
7
9
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
8
10
|
function isRecord(value) {
|
|
9
11
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10
12
|
}
|
|
11
|
-
/**
|
|
12
|
-
* The one discriminant between an argument OBJECT and a VALUE wherever a
|
|
13
|
-
* position admits both — a filter position above all: a **plain** record is an
|
|
14
|
-
* operator object, and anything else — a `Date`, a core `Decimal`, a
|
|
15
|
-
* `Uint8Array` — is a value, which equality shorthand then holds to the field's
|
|
16
|
-
* scalar (spec §9.1). The validator (`operation-validator-core.ts`) reads a
|
|
17
|
-
* filter position through this same answer, and an adapter's filter translation
|
|
18
|
-
* is expected to, so a position cannot be operators to one reader and a value to
|
|
19
|
-
* another.
|
|
20
|
-
*/
|
|
21
13
|
export function isPlainRecord(value) {
|
|
22
14
|
if (!isRecord(value)) {
|
|
23
15
|
return false;
|
|
@@ -45,20 +37,6 @@ function materializeJsonPath(root, link) {
|
|
|
45
37
|
segments.reverse();
|
|
46
38
|
return [...root, ...segments];
|
|
47
39
|
}
|
|
48
|
-
/**
|
|
49
|
-
* A JSON scalar's interior is opaque to the Contract, so no semantic limit
|
|
50
|
-
* bounds its depth - `maxNestingDepth` counts Contract nesting and
|
|
51
|
-
* `maxRequestBytes` is a transport concern. The traversal therefore carries
|
|
52
|
-
* its own stack on the heap: a payload deep enough to exhaust the call stack
|
|
53
|
-
* must still normalize rather than become an internal failure.
|
|
54
|
-
*
|
|
55
|
-
* `seen` holds the ancestors of the value being visited, so a repeated
|
|
56
|
-
* sibling reference is legal and only a genuine cycle is rejected.
|
|
57
|
-
*
|
|
58
|
-
* Every node it emits is typed {@link JsonValue}, so the public type and what
|
|
59
|
-
* this traversal produces are one fact: a branch that emitted anything else
|
|
60
|
-
* would not compile.
|
|
61
|
-
*/
|
|
62
40
|
function normalizeJsonValue(value, path, seen) {
|
|
63
41
|
const issues = [];
|
|
64
42
|
let root;
|
|
@@ -238,6 +216,16 @@ function normalizeScalarAtom(contract, field, value, scope, path, allowNull) {
|
|
|
238
216
|
else {
|
|
239
217
|
normalized = { ok: true, value: BigInt(value) };
|
|
240
218
|
}
|
|
219
|
+
if (normalized.ok &&
|
|
220
|
+
typeof normalized.value === "bigint" &&
|
|
221
|
+
(normalized.value < BIGINT_MIN || normalized.value > BIGINT_MAX)) {
|
|
222
|
+
normalized = {
|
|
223
|
+
ok: false,
|
|
224
|
+
issues: [
|
|
225
|
+
issue("V1004", path, "BigInt value is outside signed 64-bit range."),
|
|
226
|
+
],
|
|
227
|
+
};
|
|
228
|
+
}
|
|
241
229
|
break;
|
|
242
230
|
case "decimal":
|
|
243
231
|
if (scope === "application") {
|
|
@@ -346,10 +334,6 @@ function normalizeScalarAtom(contract, field, value, scope, path, allowNull) {
|
|
|
346
334
|
}
|
|
347
335
|
return normalized;
|
|
348
336
|
}
|
|
349
|
-
/**
|
|
350
|
-
* Normalizes one field value to Aventara's provider-neutral runtime form.
|
|
351
|
-
* `runtime` is used for framework-produced values regardless of request origin.
|
|
352
|
-
*/
|
|
353
337
|
export function normalizeScalarFieldValue(contract, field, value, scope, path) {
|
|
354
338
|
if (value === null) {
|
|
355
339
|
return field.nullable
|
|
@@ -377,32 +361,17 @@ export function normalizeScalarFieldValue(contract, field, value, scope, path) {
|
|
|
377
361
|
}
|
|
378
362
|
return normalizeScalarAtom(contract, field, value, scope === "runtime" ? "application" : scope, path, field.nullable);
|
|
379
363
|
}
|
|
380
|
-
/**
|
|
381
|
-
* One copier per built-in scalar codec, keyed by a mapped type over
|
|
382
|
-
* {@link BuiltInScalar}: a codec added to the protocol is a compile error
|
|
383
|
-
* here until it declares how a framework-owned copy of its runtime form is
|
|
384
|
-
* made, so DA-25 cannot silently degrade to identity sharing for it. The
|
|
385
|
-
* immutable codecs name themselves rather than sharing a `default` branch.
|
|
386
|
-
*/
|
|
387
364
|
const SCALAR_ATOM_COPIERS = {
|
|
388
365
|
string: (value) => value,
|
|
389
366
|
boolean: (value) => value,
|
|
390
367
|
int: (value) => value,
|
|
391
368
|
float: (value) => value,
|
|
392
369
|
bigint: (value) => value,
|
|
393
|
-
// `Decimal` holds its digits in a `readonly` ES private field assigned once
|
|
394
|
-
// by its constructor and exposes no mutator, so one instance cannot connect
|
|
395
|
-
// two views of it.
|
|
396
370
|
decimal: (value) => value,
|
|
397
371
|
datetime: (value) => value instanceof Date ? new Date(value.getTime()) : value,
|
|
398
372
|
bytes: (value) => (value instanceof Uint8Array ? value.slice() : value),
|
|
399
373
|
json: (value) => structuredClone(value),
|
|
400
374
|
};
|
|
401
|
-
/**
|
|
402
|
-
* The same table for a runtime lookup: a Contract's scalar name is a `string`
|
|
403
|
-
* in the type system, so an unknown name yields no copier and the value is
|
|
404
|
-
* returned as it is.
|
|
405
|
-
*/
|
|
406
375
|
const SCALAR_ATOM_COPIER_LOOKUP = SCALAR_ATOM_COPIERS;
|
|
407
376
|
function copyScalarAtom(field, value) {
|
|
408
377
|
if (value === null || "enum" in field.type) {
|
|
@@ -411,16 +380,6 @@ function copyScalarAtom(field, value) {
|
|
|
411
380
|
const copier = SCALAR_ATOM_COPIER_LOOKUP[field.type.scalar];
|
|
412
381
|
return copier === undefined ? value : copier(value);
|
|
413
382
|
}
|
|
414
|
-
/**
|
|
415
|
-
* Copies one validated runtime value of a scalar field into a value the
|
|
416
|
-
* framework owns, per scalar codec (DA-25): `datetime` → a new `Date`,
|
|
417
|
-
* `bytes` → a sliced `Uint8Array`, `json` → a structured deep copy, and a list
|
|
418
|
-
* → a new array of copied items. `decimal` keeps its identity, and strings,
|
|
419
|
-
* numbers, booleans, bigints, enum members and `null` are immutable and
|
|
420
|
-
* returned unchanged. {@link SCALAR_ATOM_COPIERS} is exhaustive over
|
|
421
|
-
* {@link BuiltInScalar}, so a new codec cannot reach this function without
|
|
422
|
-
* declaring its copy.
|
|
423
|
-
*/
|
|
424
383
|
export function copyScalarFieldValue(field, value) {
|
|
425
384
|
if (!field.list || !Array.isArray(value)) {
|
|
426
385
|
return copyScalarAtom(field, value);
|
|
@@ -780,10 +739,6 @@ function isUniqueOperation(request) {
|
|
|
780
739
|
request.family === "delete" ||
|
|
781
740
|
request.family === "upsert"));
|
|
782
741
|
}
|
|
783
|
-
/**
|
|
784
|
-
* Normalizes every scalar leaf in one structurally valid standard operation.
|
|
785
|
-
* Client wire values are decoded; application values are validated in runtime form.
|
|
786
|
-
*/
|
|
787
742
|
export function normalizeOperationScalarValues(contract, request, scope, options) {
|
|
788
743
|
const issues = [];
|
|
789
744
|
const args = cloneRecord(request.arguments);
|
|
@@ -17,9 +17,9 @@ declare const OPERATION_RESULT: unique symbol;
|
|
|
17
17
|
declare const OPERATION_HANDLE: unique symbol;
|
|
18
18
|
/**
|
|
19
19
|
* Standard-library shapes that are objects but never a projected result row.
|
|
20
|
-
* `ArrayBufferView` is TypeScript's shared structural base for every typed
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* `ArrayBufferView` is TypeScript's shared structural base for every typed array
|
|
21
|
+
* and `DataView`, so no constructor is enumerated and the declaration stays
|
|
22
|
+
* portable ES2022 with no runtime dependency.
|
|
23
23
|
*/
|
|
24
24
|
export type BuiltinNonRow = Date | RegExp | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> | PromiseLike<unknown> | ArrayBufferView;
|
|
25
25
|
/**
|
|
@@ -32,18 +32,13 @@ export type BuiltinNonRow = Date | RegExp | ReadonlyMap<unknown, unknown> | Read
|
|
|
32
32
|
export type SingleRowResult<T> = NonNullable<T> extends readonly unknown[] ? never : NonNullable<T> extends (...args: never[]) => unknown ? never : NonNullable<T> extends BuiltinNonRow ? never : NonNullable<T> extends object ? Extract<keyof NonNullable<T>, string> extends never ? never : NonNullable<T> : never;
|
|
33
33
|
/** The field names a result row can be referenced by. */
|
|
34
34
|
export type ReferenceableField<T> = Extract<keyof SingleRowResult<T>, string>;
|
|
35
|
-
/** Builds a §14.2 `$ref` carrying the source field's own value type. */
|
|
36
35
|
export type ReferenceBuilder<T> = <F extends ReferenceableField<T>>(field: F) => TransactionReference<SingleRowResult<T>[F]>;
|
|
37
36
|
/** Contributes `$ref` only where the result is a referenceable single row. */
|
|
38
37
|
export type OperationReferenceSurface<T> = [SingleRowResult<T>] extends [never] ? {} : {
|
|
39
38
|
readonly $ref: ReferenceBuilder<T>;
|
|
40
39
|
};
|
|
41
40
|
/**
|
|
42
|
-
* An opaque deferred-operation handle.
|
|
43
|
-
* it exposes no `resource`, `family`, `variant`, `args` or `fingerprint`, and
|
|
44
|
-
* is never intersected with `DeferredOperation`. Phase 9 and the generated
|
|
45
|
-
* Phase 12 builders construct handles; `T` is supplied by the caller until
|
|
46
|
-
* Phase 9 derives it from a Contract as `OperationResultFor` (DA-11).
|
|
41
|
+
* An opaque deferred-operation handle.
|
|
47
42
|
*/
|
|
48
43
|
export type Operation<T> = {
|
|
49
44
|
readonly [OPERATION_HANDLE]: true;
|
|
@@ -54,13 +49,13 @@ export type TransactionResults<Ops extends readonly Operation<unknown>[]> = {
|
|
|
54
49
|
readonly [I in keyof Ops]: Ops[I] extends Operation<infer R> ? R : never;
|
|
55
50
|
};
|
|
56
51
|
/**
|
|
57
|
-
* The one typed transaction runner — `framework.transaction`'s own type. It
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
52
|
+
* The one typed transaction runner — `framework.transaction`'s own type. It infers
|
|
53
|
+
* the handle tuple without `as const` at the call site, and resolves what the
|
|
54
|
+
* runtime answers: the committed envelope (`A1009`) with each handle's result in
|
|
55
|
+
* plan order, or the failure response. Never the bare result tuple.
|
|
61
56
|
*
|
|
62
57
|
* The second argument is the run's `ExecutionOptions`: its `requestId` is every
|
|
63
|
-
* step's, under either origin
|
|
58
|
+
* step's, under either origin. A handle takes none.
|
|
64
59
|
*/
|
|
65
60
|
export type TransactionRunner = <const Ops extends readonly Operation<unknown>[]>(operations: Ops, options?: ExecutionOptions) => Promise<TransactionCommittedResponse<TransactionResults<Ops>> | OperationFailureResponse>;
|
|
66
61
|
export {};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Contract } from "../contracts/contract.js";
|
|
2
2
|
import type { OperationIdentity } from "../operations/operation-identity.js";
|
|
3
3
|
import type { SemanticOperationArguments } from "../operations/operation-validator.js";
|
|
4
|
-
/** Encodes validated application arguments into the §6.2 wire scalar form. */
|
|
5
4
|
export declare function encodeOperationArgumentsForWire(contract: Contract, operation: OperationIdentity & {
|
|
6
5
|
readonly resource: string;
|
|
7
6
|
}, args: SemanticOperationArguments): unknown;
|
|
@@ -204,14 +204,6 @@ function encodeScalarMutation(field, value) {
|
|
|
204
204
|
}
|
|
205
205
|
return encoded;
|
|
206
206
|
}
|
|
207
|
-
/**
|
|
208
|
-
* The identifier half of the relation-mutation grammar, and the companion of
|
|
209
|
-
* `RELATION_MUTATION_RECORD_SITES`, which owns the record operands and leaves
|
|
210
|
-
* identifier operands empty because the write planner never encodes them. This
|
|
211
|
-
* pass does, so it describes them here rather than hand-walking the grammar a
|
|
212
|
-
* second time. Both tables are keyed by `RelationMutationDirective`, so a new
|
|
213
|
-
* directive cannot be added without this file failing to compile.
|
|
214
|
-
*/
|
|
215
207
|
const RELATION_MUTATION_SELECTOR_SITES = {
|
|
216
208
|
$create: [],
|
|
217
209
|
$connect: [{ key: undefined, form: "unique" }],
|
|
@@ -222,11 +214,6 @@ const RELATION_MUTATION_SELECTOR_SITES = {
|
|
|
222
214
|
$delete: [{ key: "where", form: "filter" }],
|
|
223
215
|
$upsert: [{ key: "where", form: "unique" }],
|
|
224
216
|
};
|
|
225
|
-
/**
|
|
226
|
-
* Encodes one site of a directive operand. A site with no key names the whole
|
|
227
|
-
* operand; a keyed site names one own member of it and leaves an operand that
|
|
228
|
-
* is not a plain record, or that does not carry the member, alone.
|
|
229
|
-
*/
|
|
230
217
|
function encodeRelationSite(entry, key, encode) {
|
|
231
218
|
if (key === undefined) {
|
|
232
219
|
return encode(entry);
|
|
@@ -294,7 +281,6 @@ function isUniqueOperation(operation) {
|
|
|
294
281
|
operation.family === "delete" ||
|
|
295
282
|
operation.family === "upsert"));
|
|
296
283
|
}
|
|
297
|
-
/** Encodes validated application arguments into the §6.2 wire scalar form. */
|
|
298
284
|
export function encodeOperationArgumentsForWire(contract, operation, args) {
|
|
299
285
|
const encoded = cloneRecord(args);
|
|
300
286
|
if (hasOwnWireProperty(args, "where")) {
|
|
@@ -13,7 +13,6 @@ function encodeBase64Url(bytes) {
|
|
|
13
13
|
export function isOperationFingerprint(value) {
|
|
14
14
|
return typeof value === "string" && OPERATION_FINGERPRINT_PATTERN.test(value);
|
|
15
15
|
}
|
|
16
|
-
/** Computes the v1 operation fingerprint from an already wire-form node. */
|
|
17
16
|
export async function computeOperationFingerprint(node) {
|
|
18
17
|
const canonical = canonicalizeJson({
|
|
19
18
|
resource: node.resource,
|
|
@@ -13,9 +13,6 @@ function inspectArrayDataProperty(value, key) {
|
|
|
13
13
|
if (!("value" in descriptor)) {
|
|
14
14
|
return { ok: false };
|
|
15
15
|
}
|
|
16
|
-
// This can invoke a transparent proxy trap, but never an accessor: the
|
|
17
|
-
// descriptor above proved that the property is data-backed. The value
|
|
18
|
-
// consumed below remains the descriptor value.
|
|
19
16
|
Reflect.get(value, key);
|
|
20
17
|
return { ok: true, present: true, value: descriptor.value };
|
|
21
18
|
}
|
|
@@ -23,12 +20,6 @@ function inspectArrayDataProperty(value, key) {
|
|
|
23
20
|
return { ok: false };
|
|
24
21
|
}
|
|
25
22
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Inspects the plan's operations tuple without dereferencing untrusted array
|
|
28
|
-
* properties outside a reflection boundary. The caller uses the returned
|
|
29
|
-
* length and per-index inspector rather than `operations.length` or
|
|
30
|
-
* `operations[index]` directly.
|
|
31
|
-
*/
|
|
32
23
|
export function inspectTransactionOperations(value) {
|
|
33
24
|
try {
|
|
34
25
|
if (!Array.isArray(value)) {
|
|
@@ -55,7 +46,6 @@ export function inspectTransactionOperations(value) {
|
|
|
55
46
|
return operationsReflectionFailure("Transaction operations array reflection failed.");
|
|
56
47
|
}
|
|
57
48
|
}
|
|
58
|
-
/** Reads one operations entry through its own data descriptor. */
|
|
59
49
|
export function inspectTransactionOperationEntry(operations, index) {
|
|
60
50
|
const entry = inspectArrayDataProperty(operations, String(index));
|
|
61
51
|
if (!entry.ok) {
|
|
@@ -70,10 +60,6 @@ export function inspectTransactionOperationEntry(operations, index) {
|
|
|
70
60
|
}
|
|
71
61
|
return { ok: true, value: entry.value };
|
|
72
62
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Inspects an untrusted transaction envelope without reading any property value.
|
|
75
|
-
* Proxies are unsupported; reflection failures are contained as envelope issues.
|
|
76
|
-
*/
|
|
77
63
|
export function inspectTransactionEnvelope(value, schema) {
|
|
78
64
|
if (typeof value !== "object" || value === null) {
|
|
79
65
|
return issue(schema.invalidContainerPath, schema.invalidContainerMessage);
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The ways an Adapter transaction can break the contract the framework's plan
|
|
3
|
-
* executor depends on
|
|
4
|
-
*
|
|
5
|
-
* than message text (ADR 0006: never infer an outcome from error text).
|
|
3
|
+
* executor depends on. Each is answered `A3002`; the violation is the only thing
|
|
4
|
+
* that tells them apart, so it is structured data rather than message text.
|
|
6
5
|
*
|
|
7
|
-
* - `transaction-unavailable`: the Adapter's `transaction` function is gone
|
|
8
|
-
*
|
|
9
|
-
* - `resolved-after-abort`: the Adapter resolved although the framework threw
|
|
10
|
-
*
|
|
11
|
-
* - `resolved-incomplete`: the Adapter resolved before every plan operation
|
|
12
|
-
*
|
|
13
|
-
* - `foreign-abort`: the Adapter rejected with the framework's rollback
|
|
14
|
-
*
|
|
6
|
+
* - `transaction-unavailable`: the Adapter's `transaction` function is gone at
|
|
7
|
+
* execution time.
|
|
8
|
+
* - `resolved-after-abort`: the Adapter resolved although the framework threw its
|
|
9
|
+
* rollback request.
|
|
10
|
+
* - `resolved-incomplete`: the Adapter resolved before every plan operation ran,
|
|
11
|
+
* so no committed `A1009` array exists.
|
|
12
|
+
* - `foreign-abort`: the Adapter rejected with the framework's rollback request
|
|
13
|
+
* although this plan never made one.
|
|
15
14
|
*/
|
|
16
15
|
export type TransactionIntegrityViolation = "transaction-unavailable" | "resolved-after-abort" | "resolved-incomplete" | "foreign-abort";
|
|
17
16
|
/**
|
|
@@ -4,11 +4,6 @@ const TRANSACTION_INTEGRITY_MESSAGES = {
|
|
|
4
4
|
"resolved-incomplete": "Adapter transaction resolved before the plan completed.",
|
|
5
5
|
"foreign-abort": "Adapter transaction rejected with a rollback request this plan never made.",
|
|
6
6
|
};
|
|
7
|
-
/**
|
|
8
|
-
* The framework-owned failure reported to diagnostics when an Adapter
|
|
9
|
-
* transaction violates its contract. It never reaches the wire: the response
|
|
10
|
-
* is always the constant `A3002` envelope.
|
|
11
|
-
*/
|
|
12
7
|
export class TransactionIntegrityError extends Error {
|
|
13
8
|
violation;
|
|
14
9
|
constructor(violation) {
|
|
@@ -5,7 +5,6 @@ import type { ClientProtocolIdentity } from "../runtime/operation-request.js";
|
|
|
5
5
|
import type { OperationFailureResponse } from "../runtime/operation-response.js";
|
|
6
6
|
import type { Operation, OperationFingerprint } from "./deferred-operation.js";
|
|
7
7
|
import type { TransactionOperationOrigin } from "./transaction-request.js";
|
|
8
|
-
/** One §14.2 wire node, exactly as the transaction runtime receives it. */
|
|
9
8
|
type WireOperationNode = {
|
|
10
9
|
readonly resource: string;
|
|
11
10
|
readonly family: string;
|
|
@@ -11,11 +11,6 @@ function isPlainRecord(value) {
|
|
|
11
11
|
const prototype = Object.getPrototypeOf(value);
|
|
12
12
|
return prototype === Object.prototype || prototype === null;
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
* The argument keys whose value holds DA-3 positions for `identity`: the
|
|
16
|
-
* top-level mutation data. `create.many`/`create.count` hold a list of records
|
|
17
|
-
* there; every other one holds one record.
|
|
18
|
-
*/
|
|
19
14
|
function referenceKeys(identity) {
|
|
20
15
|
switch (identity.family) {
|
|
21
16
|
case "create":
|
|
@@ -27,11 +22,6 @@ function referenceKeys(identity) {
|
|
|
27
22
|
return [];
|
|
28
23
|
}
|
|
29
24
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Every DA-3 container `args` holds. A value that is not a plain record (or, for
|
|
32
|
-
* a bulk create, a list of them) holds none: it reaches the validation layer as
|
|
33
|
-
* it is, which answers it.
|
|
34
|
-
*/
|
|
35
25
|
function referenceContainers(identity, args) {
|
|
36
26
|
if (!isPlainRecord(args)) {
|
|
37
27
|
return [];
|
|
@@ -44,10 +34,6 @@ function referenceContainers(identity, args) {
|
|
|
44
34
|
return isPlainRecord(value) ? [{ path: [key], record: value }] : [];
|
|
45
35
|
});
|
|
46
36
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Rebuilds `args` with each DA-3 container replaced by `rebuild(container)`,
|
|
49
|
-
* cloning only the containing path; every other value is the caller's own.
|
|
50
|
-
*/
|
|
51
37
|
function replaceContainers(identity, args, rebuild) {
|
|
52
38
|
if (!isPlainRecord(args)) {
|
|
53
39
|
return args;
|
|
@@ -66,34 +52,21 @@ function replaceContainers(identity, args, rebuild) {
|
|
|
66
52
|
}
|
|
67
53
|
return result;
|
|
68
54
|
}
|
|
69
|
-
/** A per-operation framework-validation refusal (`A2004`/`V1001`), at `["operation"]`. */
|
|
70
55
|
function operationEntryFailure(operation, message) {
|
|
71
56
|
return transactionOperationFailure(operationFailure("A2004", {
|
|
72
57
|
message: "Transaction plan failed framework validation.",
|
|
73
58
|
issues: [validationIssue("V1001", ["operation"], message)],
|
|
74
59
|
}), operation);
|
|
75
60
|
}
|
|
76
|
-
/** A reference refusal, routed like every other reference issue (DA-4). */
|
|
77
61
|
function referenceFailure(issue, operation) {
|
|
78
62
|
return transactionOperationFailure(mapValidationOutcome([issue]), operation);
|
|
79
63
|
}
|
|
80
|
-
/**
|
|
81
|
-
* One assembler per Framework: a handle is this Framework's or it is not a
|
|
82
|
-
* handle, so a handle another instance built — typed against another model,
|
|
83
|
-
* bound to other Contracts — is refused rather than run.
|
|
84
|
-
*/
|
|
85
64
|
export function createTransactionPlanAssembler(contracts, protocol) {
|
|
86
65
|
const handles = new WeakMap();
|
|
87
66
|
const placeholders = new WeakMap();
|
|
88
67
|
const originOf = (origin) => origin === "application"
|
|
89
68
|
? { scope: "application" }
|
|
90
69
|
: { scope: "client", protocol };
|
|
91
|
-
/**
|
|
92
|
-
* Fixes the handle's DA-3 positions when it is built: each container is
|
|
93
|
-
* copied, so a later mutation of the caller's data object cannot move a
|
|
94
|
-
* reference — and, since a handle can only reference handles built before it,
|
|
95
|
-
* the references between handles are acyclic.
|
|
96
|
-
*/
|
|
97
70
|
const snapshot = (identity, args) => replaceContainers(identity, args, ({ record }) => ({ ...record }));
|
|
98
71
|
const defer = (origin, resource, identity, args) => {
|
|
99
72
|
const handle = Object.freeze({
|
|
@@ -146,7 +119,6 @@ export function createTransactionPlanAssembler(contracts, protocol) {
|
|
|
146
119
|
positions.set(entry, index);
|
|
147
120
|
records.push(record);
|
|
148
121
|
}
|
|
149
|
-
// Every reference must name a handle in this list; the earliest refusal wins.
|
|
150
122
|
for (const [index, record] of records.entries()) {
|
|
151
123
|
for (const { path, record: container } of referenceContainers(record.identity, record.args)) {
|
|
152
124
|
for (const [field, value] of Object.entries(container)) {
|
|
@@ -201,13 +173,6 @@ export function createTransactionPlanAssembler(contracts, protocol) {
|
|
|
201
173
|
fingerprint: await fingerprintOf(record, args),
|
|
202
174
|
};
|
|
203
175
|
};
|
|
204
|
-
/**
|
|
205
|
-
* The DA-17 fingerprint the validation layer recomputes. When it cannot be
|
|
206
|
-
* computed here, the validation layer cannot compute it either and answers
|
|
207
|
-
* the operation itself first — an invalid argument before the fingerprint
|
|
208
|
-
* step, a value JCS cannot encode as `V1001` at it — so the declared value
|
|
209
|
-
* is never compared and is left `null`.
|
|
210
|
-
*/
|
|
211
176
|
const fingerprintOf = async (record, args) => {
|
|
212
177
|
const operation = { resource: record.resource, ...record.identity };
|
|
213
178
|
try {
|
|
@@ -48,9 +48,10 @@ export type ValidatedTransactionPlan = {
|
|
|
48
48
|
/**
|
|
49
49
|
* What validating a plan needs beyond one operation's pipeline: the cascade
|
|
50
50
|
* closure over the Adapter's referential actions, resolved once when the
|
|
51
|
-
* Framework is constructed.
|
|
51
|
+
* Framework is constructed. The create requirements are not among them: they
|
|
52
|
+
* are checked when each operation runs, after its pipes.
|
|
52
53
|
*/
|
|
53
|
-
export type TransactionPlanDependencies<M extends AdapterModel> = OperationPipelineDependencies<M> & {
|
|
54
|
+
export type TransactionPlanDependencies<M extends AdapterModel> = Omit<OperationPipelineDependencies<M>, "createRequirements"> & {
|
|
54
55
|
readonly cascades: CascadeClosure;
|
|
55
56
|
};
|
|
56
57
|
export type TransactionPlanValidationResult = {
|
|
@@ -62,9 +63,8 @@ export type TransactionPlanValidationResult = {
|
|
|
62
63
|
};
|
|
63
64
|
/**
|
|
64
65
|
* The refusal of a plan on a Contract without transaction plans (`A2002`, one
|
|
65
|
-
* `V1006` at `["operations"]`). The one spelling — the protocol's absence
|
|
66
|
-
*
|
|
67
|
-
* envelope too.
|
|
66
|
+
* `V1006` at `["operations"]`). The one spelling — the protocol's absence answer
|
|
67
|
+
* for `/_transactions` is this envelope too.
|
|
68
68
|
*/
|
|
69
69
|
export declare function transactionsUnavailableFailure(): OperationFailureResponse;
|
|
70
70
|
/**
|
|
@@ -48,7 +48,6 @@ function syntheticRequest(origin, requestId, resource, identity, args) {
|
|
|
48
48
|
: { transport: origin.transport }),
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
-
/** The origin the untyped seam gives every operation: the request's own. */
|
|
52
51
|
function requestOrigin(request) {
|
|
53
52
|
return request.scope === "application"
|
|
54
53
|
? { scope: "application" }
|
|
@@ -60,12 +59,6 @@ function requestOrigin(request) {
|
|
|
60
59
|
: { transport: request.transport }),
|
|
61
60
|
};
|
|
62
61
|
}
|
|
63
|
-
/**
|
|
64
|
-
* The Contracts a plan's operations run under: one per distinct origin. A
|
|
65
|
-
* plan-level gate — the transaction capability, the operation-count limit — holds
|
|
66
|
-
* only when it holds in every one of them, since every step runs inside the same
|
|
67
|
-
* Adapter transaction.
|
|
68
|
-
*/
|
|
69
62
|
function originContracts(contracts, origins) {
|
|
70
63
|
const layers = new Set(origins.map((origin) => origin.scope));
|
|
71
64
|
return [...layers].map((scope) => scope === "application" ? contracts.application : contracts.client);
|
|
@@ -89,9 +82,6 @@ function collectDataCandidates(contract, resourceName, data, path, candidates) {
|
|
|
89
82
|
}
|
|
90
83
|
function collectCandidates(contract, request, index, args) {
|
|
91
84
|
const candidates = [];
|
|
92
|
-
// The same address the response boundary resolves a thrower-supplied issue
|
|
93
|
-
// path against: one source of truth for where a plan operation's arguments
|
|
94
|
-
// live in a path.
|
|
95
85
|
const root = planOperationArgumentsPath(index);
|
|
96
86
|
if (request.family === "create") {
|
|
97
87
|
const data = lookupOwn(args, "data");
|
|
@@ -116,14 +106,6 @@ function collectCandidates(contract, request, index, args) {
|
|
|
116
106
|
}
|
|
117
107
|
return candidates;
|
|
118
108
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Every relation field an operation's arguments traverse — in projections,
|
|
121
|
-
* filters, orderings and nested writes alike — with the Resource each one
|
|
122
|
-
* reaches. A key that names a relation of the Resource in hand switches to its
|
|
123
|
-
* target; a key that names a scalar is a value and is not entered; any other key
|
|
124
|
-
* (an argument keyword, a logical or relation operator, a directive) keeps the
|
|
125
|
-
* Resource in hand. A string entry of an array is a bare projection entry.
|
|
126
|
-
*/
|
|
127
109
|
function* relationTraversals(contract, resourceName, value, path) {
|
|
128
110
|
const resource = lookupResource(contract, resourceName);
|
|
129
111
|
if (resource === undefined) {
|
|
@@ -157,7 +139,6 @@ function* relationTraversals(contract, resourceName, value, path) {
|
|
|
157
139
|
}
|
|
158
140
|
}
|
|
159
141
|
}
|
|
160
|
-
/** Every relation the operation's arguments traverse, from each argument's root. */
|
|
161
142
|
function* operationTraversals(contract, request, index) {
|
|
162
143
|
if (!isObject(request.arguments)) {
|
|
163
144
|
return;
|
|
@@ -170,10 +151,6 @@ function* operationTraversals(contract, request, index) {
|
|
|
170
151
|
]);
|
|
171
152
|
}
|
|
172
153
|
}
|
|
173
|
-
/**
|
|
174
|
-
* The Resources whose rows the operation deletes directly: its own Resource for
|
|
175
|
-
* the `delete` family, and the target of every nested `$delete`.
|
|
176
|
-
*/
|
|
177
154
|
function deletedResources(contract, request, index) {
|
|
178
155
|
const deleted = request.family === "delete" ? [request.resource] : [];
|
|
179
156
|
for (const traversal of operationTraversals(contract, request, index)) {
|
|
@@ -184,14 +161,6 @@ function deletedResources(contract, request, index) {
|
|
|
184
161
|
}
|
|
185
162
|
return deleted;
|
|
186
163
|
}
|
|
187
|
-
/**
|
|
188
|
-
* Where an operation depends on rows of a Resource an earlier step's cascade
|
|
189
|
-
* removed, or `undefined`. Every operation but a `create` addresses existing rows
|
|
190
|
-
* of its own Resource, so its own Resource is checked first, at the wire
|
|
191
|
-
* location that names it; then every relation its arguments traverse, at that
|
|
192
|
-
* relation's own wire location. A `create` adds rows and depends on none of its
|
|
193
|
-
* own Resource's, so only its arguments' relations are checked.
|
|
194
|
-
*/
|
|
195
164
|
function cascadeDependency(contract, request, index, cascaded) {
|
|
196
165
|
if (cascaded.size === 0) {
|
|
197
166
|
return undefined;
|
|
@@ -206,12 +175,6 @@ function cascadeDependency(contract, request, index, cascaded) {
|
|
|
206
175
|
}
|
|
207
176
|
return undefined;
|
|
208
177
|
}
|
|
209
|
-
/**
|
|
210
|
-
* The refusal of a plan on a Contract without transaction plans (`A2002`, one
|
|
211
|
-
* `V1006` at `["operations"]`). The one spelling — the protocol's absence
|
|
212
|
-
* answer for `/_transactions` (`@aventara/core/protocol`, Q2's O3) is this
|
|
213
|
-
* envelope too.
|
|
214
|
-
*/
|
|
215
178
|
export function transactionsUnavailableFailure() {
|
|
216
179
|
const message = "Transaction plans are not available on the active Contract.";
|
|
217
180
|
return operationFailure("A2002", {
|
|
@@ -219,23 +182,6 @@ export function transactionsUnavailableFailure() {
|
|
|
219
182
|
issues: [validationIssue("V1006", ["operations"], message)],
|
|
220
183
|
});
|
|
221
184
|
}
|
|
222
|
-
/**
|
|
223
|
-
* Whole-plan Framework Validation Layer for transaction structure and identity.
|
|
224
|
-
*
|
|
225
|
-
* `origins` states each operation's origin by index, for a plan whose steps do
|
|
226
|
-
* not all share the request's (the typed runner's mixed plans). Absent — the
|
|
227
|
-
* untyped seam — every operation takes the request's own origin. Each operation
|
|
228
|
-
* is validated against its own origin's Contract.
|
|
229
|
-
*
|
|
230
|
-
* A plan is also refused when a step depends on rows an earlier step's cascade
|
|
231
|
-
* removed (`V1019`, plan-scoped: a lone operation that cascades is not a
|
|
232
|
-
* defect, since nothing follows it). The analysis runs over every step whatever
|
|
233
|
-
* its origin — one plan runs in one Adapter transaction, so a client step's
|
|
234
|
-
* read sees an application step's cascade — and reads each step against its own
|
|
235
|
-
* origin's Contract. It is a planning answer, given before any provider statement
|
|
236
|
-
* is issued, and as conservative as the facts it has: a plan cannot know which
|
|
237
|
-
* rows a delete will match, so it refuses at the granularity of a Resource.
|
|
238
|
-
*/
|
|
239
185
|
export async function validateTransactionPlan(dependencies, request, origins) {
|
|
240
186
|
const protocolFailure = validateProtocolIdentity(dependencies.contracts, request);
|
|
241
187
|
if (protocolFailure !== undefined) {
|
|
@@ -284,7 +230,6 @@ export async function validateTransactionPlan(dependencies, request, origins) {
|
|
|
284
230
|
};
|
|
285
231
|
}
|
|
286
232
|
const validated = [];
|
|
287
|
-
// The Resources whose rows the cascades of steps validated so far may remove.
|
|
288
233
|
const cascaded = new Set();
|
|
289
234
|
for (let index = 0; index < operationCount; index += 1) {
|
|
290
235
|
const operationEntry = inspectTransactionOperationEntry(operations, index);
|
|
@@ -389,17 +334,9 @@ export async function validateTransactionPlan(dependencies, request, origins) {
|
|
|
389
334
|
});
|
|
390
335
|
}
|
|
391
336
|
catch (error) {
|
|
392
|
-
// D-722: only the canonicalizer's rejection of a caller-supplied value
|
|
393
|
-
// is caller-attributable. Encoder, digest, and every other failure is
|
|
394
|
-
// framework-owned and reaches the totality guard (A3000 + diagnostics).
|
|
395
337
|
if (!(error instanceof CanonicalJsonError)) {
|
|
396
338
|
throw error;
|
|
397
339
|
}
|
|
398
|
-
// The standalone argument spelling, not planOperationArgumentsPath(index):
|
|
399
|
-
// the issue is already attributed by cause.operation, and the shorter
|
|
400
|
-
// path is pinned by "maps remaining non-canonical fingerprint input to
|
|
401
|
-
// D-722". Register row F-722 owns whether it should become the in-plan
|
|
402
|
-
// spelling; changing it here would change a caller-visible path.
|
|
403
340
|
return {
|
|
404
341
|
ok: false,
|
|
405
342
|
response: structuralFailure(validationIssue("V1001", OPERATION_ARGUMENTS_PATH, "Invalid runtime/wire type."), index),
|
|
@@ -9,19 +9,18 @@ export type ResolvedTransactionArguments = {
|
|
|
9
9
|
readonly issues: readonly ValidationIssue[];
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
|
-
* Substitutes every validated `$ref` placeholder of one operation from the
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* operation mutating its arguments cannot alter the source's committed result.
|
|
12
|
+
* Substitutes every validated `$ref` placeholder of one operation from the results
|
|
13
|
+
* ledger: each earlier operation's final `data`, exactly the value at its index in
|
|
14
|
+
* the `A1009` results array. Validation already proved index, source fingerprint,
|
|
15
|
+
* projected path and type, and none of that is re-run here; only what can change
|
|
16
|
+
* at runtime is checked. A `null` or non-record source, or a source lacking the
|
|
17
|
+
* referenced own key, resolves to no value at the full reference path. The result
|
|
18
|
+
* is a fresh argument graph: only the ancestors of substituted slots are cloned,
|
|
19
|
+
* so the validated plan is never mutated, and every substituted value is a
|
|
20
|
+
* framework-owned copy of the source's value, so a dependent operation mutating
|
|
21
|
+
* its arguments cannot alter the source's committed result.
|
|
23
22
|
*
|
|
24
|
-
* Reading final data runs developer-produced accessors; a throw from here is
|
|
25
|
-
*
|
|
23
|
+
* Reading final data runs developer-produced accessors; a throw from here is the
|
|
24
|
+
* framework's own per-operation failure, classified by the caller.
|
|
26
25
|
*/
|
|
27
26
|
export declare function resolveTransactionReferences(operation: ValidatedTransactionOperation, results: readonly unknown[]): ResolvedTransactionArguments;
|
|
@@ -4,22 +4,6 @@ import { replaceTransactionSlotValues } from "./transaction-slot-writer.js";
|
|
|
4
4
|
function isRecord(value) {
|
|
5
5
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
8
|
-
* Substitutes every validated `$ref` placeholder of one operation from the
|
|
9
|
-
* results ledger: each earlier operation's final `data`, exactly the value at
|
|
10
|
-
* its index in the `A1009` results array (DA-8, D-708). Validation already proved index, source
|
|
11
|
-
* fingerprint, projected path and type, and none of that is re-run here
|
|
12
|
-
* (C-756); only what can change at runtime is checked. A `null` or non-record
|
|
13
|
-
* source, or a source lacking the referenced own key, resolves to no value
|
|
14
|
-
* (`V1018`, DA-2, DA-4) at the full reference path. The result is a fresh
|
|
15
|
-
* argument graph: only the ancestors of substituted slots are cloned, so the
|
|
16
|
-
* validated plan is never mutated, and every substituted value is a
|
|
17
|
-
* framework-owned copy of the source's value (DA-25), so a dependent
|
|
18
|
-
* operation mutating its arguments cannot alter the source's committed result.
|
|
19
|
-
*
|
|
20
|
-
* Reading final data runs developer-produced accessors; a throw from here is
|
|
21
|
-
* the framework's own per-operation failure (DA-23), classified by the caller.
|
|
22
|
-
*/
|
|
23
7
|
export function resolveTransactionReferences(operation, results) {
|
|
24
8
|
const substitutions = [];
|
|
25
9
|
for (const slot of operation.references) {
|