@aventara/core 0.1.0-pilot.3 → 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.
- package/CHANGELOG.md +338 -0
- package/README.md +100 -3
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +2 -0
- package/dist/adapters/adapter-field.d.ts +3 -26
- package/dist/adapters/adapter-identifier.d.ts +1 -11
- package/dist/adapters/adapter-model.d.ts +12 -0
- package/dist/adapters/adapter-operation-executor.interface.d.ts +5 -1
- package/dist/adapters/adapter-resource.d.ts +7 -28
- package/dist/adapters/adapter.interface.d.ts +25 -26
- package/dist/compiler/contract-compiler-diagnostic.d.ts +8 -0
- package/dist/compiler/contract.compiler.d.ts +11 -22
- package/dist/compiler/contract.compiler.js +67 -0
- package/dist/config/any-framework-config.d.ts +5 -26
- package/dist/config/config-diagnostic.d.ts +2 -0
- package/dist/config/config-layer.d.ts +6 -19
- package/dist/config/config.resolver.d.ts +9 -14
- package/dist/config/config.resolver.js +1 -0
- package/dist/config/config.validator.d.ts +6 -3
- package/dist/config/config.validator.js +6 -0
- package/dist/config/define-fields.d.ts +15 -48
- package/dist/config/effective-fields.d.ts +6 -29
- package/dist/config/entrypoint.normalizer.d.ts +6 -18
- package/dist/config/framework-behaviors-config.d.ts +11 -10
- package/dist/config/framework-config.d.ts +19 -16
- package/dist/config/framework-fields-config.d.ts +20 -7
- package/dist/config/framework-restrictions-config.d.ts +41 -61
- package/dist/config/pipeline-config.d.ts +7 -17
- package/dist/contracts/capability.d.ts +13 -49
- package/dist/contracts/client-contract-structure.validator.d.ts +28 -54
- package/dist/contracts/client-contract-structure.validator.js +99 -2
- package/dist/contracts/contract-canonicalizer.d.ts +4 -6
- package/dist/contracts/contract-canonicalizer.js +32 -0
- package/dist/contracts/contract-hash.d.ts +3 -2
- package/dist/contracts/contract-limits.d.ts +7 -1
- package/dist/contracts/contract-protocol.d.ts +7 -8
- package/dist/contracts/contract.d.ts +20 -4
- package/dist/contracts/enum-definition.d.ts +2 -0
- package/dist/contracts/field.d.ts +40 -167
- package/dist/contracts/identifier.d.ts +1 -16
- package/dist/contracts/json-shape.d.ts +65 -0
- package/dist/contracts/json-shape.js +1 -0
- package/dist/contracts/lifecycle-trait.d.ts +4 -3
- package/dist/contracts/reference-write-limit.d.ts +7 -0
- package/dist/contracts/reference-write-limit.js +1 -0
- package/dist/contracts/resource-operations.d.ts +2 -62
- package/dist/contracts/resource.d.ts +2 -0
- package/dist/contracts/scalar-value-type.d.ts +28 -48
- package/dist/contracts/scalar.d.ts +4 -3
- package/dist/contracts/transaction-capability.d.ts +2 -0
- package/dist/contracts/wire-scalar-format.d.ts +6 -21
- package/dist/framework/contract-view.d.ts +152 -130
- package/dist/framework/framework-construction.error.d.ts +6 -1
- package/dist/framework/framework.d.ts +33 -46
- package/dist/framework/framework.factory.d.ts +3 -17
- package/dist/framework/framework.factory.js +56 -13
- package/dist/framework/operation-facade.d.ts +3 -7
- package/dist/index.d.ts +8 -40
- package/dist/index.js +0 -20
- package/dist/internal.d.ts +35 -0
- package/dist/internal.js +16 -0
- package/dist/json/avz.d.ts +96 -0
- package/dist/json/avz.js +212 -0
- package/dist/json/index.d.ts +6 -0
- package/dist/json/index.js +1 -0
- package/dist/json/json-shape.registry.d.ts +22 -0
- package/dist/json/json-shape.registry.js +16 -0
- package/dist/json/zod-issue.translator.d.ts +51 -0
- package/dist/json/zod-issue.translator.js +142 -0
- package/dist/operations/operation-arguments.d.ts +198 -303
- package/dist/operations/operation-call.d.ts +13 -15
- package/dist/operations/operation-identity.d.ts +6 -2
- package/dist/operations/operation-identity.js +3 -0
- package/dist/operations/operation-result.d.ts +15 -61
- package/dist/operations/operation-validator-core.d.ts +6 -5
- package/dist/operations/operation-validator-core.js +28 -10
- package/dist/operations/operation-validator.d.ts +22 -9
- package/dist/operations/operation-validator.js +26 -10
- package/dist/operations/projection-scope.resolver.d.ts +1 -11
- package/dist/operations/structural-limits.walker.d.ts +19 -0
- package/dist/operations/structural-limits.walker.js +344 -0
- package/dist/operations/validation-code.d.ts +5 -17
- package/dist/operations/write-scope.resolver.d.ts +3 -28
- package/dist/protocol/absent-route.responder.d.ts +25 -39
- package/dist/protocol/absent-route.responder.js +32 -6
- package/dist/protocol/av-protocol.d.ts +2 -8
- package/dist/protocol/contract-document.responder.d.ts +5 -18
- package/dist/protocol/http-request.decoder.d.ts +18 -59
- package/dist/protocol/http-request.decoder.js +11 -3
- package/dist/protocol/http-request.reader.d.ts +5 -5
- package/dist/protocol/http-response.encoder.d.ts +14 -20
- package/dist/protocol/http-status.table.d.ts +0 -8
- package/dist/protocol/index.d.ts +2 -7
- package/dist/protocol/operation-response.guard.d.ts +0 -15
- package/dist/protocol/protocol-failure.d.ts +8 -27
- package/dist/protocol/protocol-headers.d.ts +15 -42
- package/dist/protocol/protocol-surface.d.ts +0 -22
- package/dist/protocol/protocol.binder.d.ts +19 -27
- package/dist/protocol/protocol.binder.js +3 -1
- package/dist/protocol/protocol.handler.d.ts +19 -23
- package/dist/protocol/protocol.handler.js +14 -4
- package/dist/protocol/unmounted-route.error.d.ts +19 -0
- package/dist/protocol/unmounted-route.error.js +10 -0
- package/dist/runtime/caller-data.copier.d.ts +59 -0
- package/dist/runtime/caller-data.copier.js +470 -0
- package/dist/runtime/caller-data.door.d.ts +56 -0
- package/dist/runtime/caller-data.door.js +65 -0
- package/dist/runtime/computed-read-runtime.js +3 -10
- package/dist/runtime/computed-value.error.d.ts +13 -0
- package/dist/runtime/computed-value.error.js +9 -0
- package/dist/runtime/create-requirement.d.ts +43 -7
- package/dist/runtime/create-requirement.js +89 -9
- package/dist/runtime/decimal.d.ts +5 -3
- package/dist/runtime/diagnostics-reporter.d.ts +33 -37
- package/dist/runtime/diagnostics-reporter.js +11 -4
- package/dist/runtime/error-boundary.d.ts +14 -10
- package/dist/runtime/error-boundary.js +65 -35
- package/dist/runtime/execution-options.d.ts +3 -7
- package/dist/runtime/field-behavior.d.ts +14 -21
- package/dist/runtime/framework-error.d.ts +14 -2
- package/dist/runtime/json-value.d.ts +7 -15
- package/dist/runtime/misuse-reporter.d.ts +32 -0
- package/dist/runtime/misuse-reporter.js +19 -0
- package/dist/runtime/mutation-planner.d.ts +6 -12
- package/dist/runtime/mutation-planner.js +13 -11
- package/dist/runtime/operation-request.d.ts +11 -11
- package/dist/runtime/operation-response.d.ts +18 -23
- package/dist/runtime/operation-runtime.d.ts +25 -33
- package/dist/runtime/operation-runtime.js +37 -18
- package/dist/runtime/outcome-mapper.js +12 -2
- package/dist/runtime/output-validator.d.ts +10 -0
- package/dist/runtime/output-validator.js +11 -0
- package/dist/runtime/owned-execution.registry.d.ts +13 -0
- package/dist/runtime/owned-execution.registry.js +7 -0
- package/dist/runtime/pipeline.d.ts +33 -36
- package/dist/runtime/referential-action.d.ts +8 -26
- package/dist/runtime/request-envelope.validator.d.ts +5 -0
- package/dist/runtime/request-envelope.validator.js +118 -0
- package/dist/runtime/request-id.d.ts +3 -10
- package/dist/runtime/scalar-runtime.d.ts +42 -14
- package/dist/runtime/scalar-runtime.js +162 -79
- package/dist/transactions/deferred-operation.d.ts +10 -21
- package/dist/transactions/operation-arguments-encoder.d.ts +1 -0
- package/dist/transactions/operation-fingerprint.d.ts +3 -1
- package/dist/transactions/transaction-envelope.d.ts +2 -4
- package/dist/transactions/transaction-integrity-error.d.ts +4 -15
- package/dist/transactions/transaction-plan-assembler.d.ts +6 -1
- package/dist/transactions/transaction-plan-assembler.js +74 -75
- package/dist/transactions/transaction-plan-validator.d.ts +33 -24
- package/dist/transactions/transaction-plan-validator.js +204 -153
- package/dist/transactions/transaction-reference-collector.d.ts +30 -0
- package/dist/transactions/transaction-reference-collector.js +204 -0
- package/dist/transactions/transaction-reference-resolver.d.ts +9 -12
- package/dist/transactions/transaction-reference-resolver.js +10 -5
- package/dist/transactions/transaction-reference-validator.js +24 -17
- package/dist/transactions/transaction-reference.d.ts +31 -64
- package/dist/transactions/transaction-request.d.ts +6 -8
- package/dist/transactions/transaction-response.d.ts +10 -7
- package/dist/transactions/transaction-response.js +24 -1
- package/dist/transactions/transaction-runtime.d.ts +7 -5
- package/dist/transactions/transaction-runtime.js +4 -3
- package/dist/transactions/transaction-slot-path-error.d.ts +5 -15
- package/dist/transactions/transaction-slot-writer.d.ts +3 -13
- package/package.json +17 -3
- package/dist/operations/operation-boolean-nodes.d.ts +0 -11
- package/dist/operations/operation-boolean-nodes.js +0 -283
- package/dist/operations/operation-nesting.d.ts +0 -8
- package/dist/operations/operation-nesting.js +0 -307
|
@@ -3,6 +3,7 @@ import type { ExecutionOptions } from "../runtime/execution-options.js";
|
|
|
3
3
|
import type { OperationFailureResponse } from "../runtime/operation-response.js";
|
|
4
4
|
import type { TransactionReference } from "./transaction-reference.js";
|
|
5
5
|
import type { TransactionCommittedResponse } from "./transaction-response.js";
|
|
6
|
+
/** An operation fingerprint: `fp1:` and a hash of the operation and its arguments. */
|
|
6
7
|
export type OperationFingerprint = `fp1:${string}`;
|
|
7
8
|
/** One serializable transaction-plan operation node. */
|
|
8
9
|
export type DeferredOperation = OperationIdentity & {
|
|
@@ -10,36 +11,28 @@ export type DeferredOperation = OperationIdentity & {
|
|
|
10
11
|
readonly args: unknown;
|
|
11
12
|
readonly fingerprint: OperationFingerprint;
|
|
12
13
|
};
|
|
14
|
+
/** A transaction plan: its operations, in the order they run. */
|
|
13
15
|
export type TransactionPlan = {
|
|
14
16
|
readonly operations: readonly DeferredOperation[];
|
|
15
17
|
};
|
|
16
18
|
declare const OPERATION_RESULT: unique symbol;
|
|
17
19
|
declare const OPERATION_HANDLE: unique symbol;
|
|
18
|
-
/**
|
|
19
|
-
* Standard-library shapes that are objects but never a projected result row.
|
|
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
|
-
*/
|
|
20
|
+
/** Standard-library shapes that are objects but never a projected result row. */
|
|
24
21
|
export type BuiltinNonRow = Date | RegExp | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> | PromiseLike<unknown> | ArrayBufferView;
|
|
25
22
|
/**
|
|
26
|
-
* The single row behind a
|
|
27
|
-
*
|
|
28
|
-
* `BuiltinNonRow` shapes, non-objects, and objects without a string key are
|
|
29
|
-
* excluded without any `instanceof` test or `Record<string, unknown>` gate,
|
|
30
|
-
* which ordinary interfaces would fail.
|
|
23
|
+
* The single row behind a result type, or `never` when the result is not one
|
|
24
|
+
* referenceable row.
|
|
31
25
|
*/
|
|
32
26
|
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
27
|
/** The field names a result row can be referenced by. */
|
|
34
28
|
export type ReferenceableField<T> = Extract<keyof SingleRowResult<T>, string>;
|
|
29
|
+
/** Builds a `$ref` to one field of this handle's row, typed as that field. */
|
|
35
30
|
export type ReferenceBuilder<T> = <F extends ReferenceableField<T>>(field: F) => TransactionReference<SingleRowResult<T>[F]>;
|
|
36
31
|
/** Contributes `$ref` only where the result is a referenceable single row. */
|
|
37
32
|
export type OperationReferenceSurface<T> = [SingleRowResult<T>] extends [never] ? {} : {
|
|
38
33
|
readonly $ref: ReferenceBuilder<T>;
|
|
39
34
|
};
|
|
40
|
-
/**
|
|
41
|
-
* An opaque deferred-operation handle.
|
|
42
|
-
*/
|
|
35
|
+
/** An opaque deferred-operation handle. */
|
|
43
36
|
export type Operation<T> = {
|
|
44
37
|
readonly [OPERATION_HANDLE]: true;
|
|
45
38
|
readonly [OPERATION_RESULT]?: T;
|
|
@@ -49,13 +42,9 @@ export type TransactionResults<Ops extends readonly Operation<unknown>[]> = {
|
|
|
49
42
|
readonly [I in keyof Ops]: Ops[I] extends Operation<infer R> ? R : never;
|
|
50
43
|
};
|
|
51
44
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* plan order, or the failure response. Never the bare result tuple.
|
|
56
|
-
*
|
|
57
|
-
* The second argument is the run's `ExecutionOptions`: its `requestId` is every
|
|
58
|
-
* step's, under either origin. A handle takes none.
|
|
45
|
+
* `framework.transaction`'s type. It infers the handle tuple without `as const`
|
|
46
|
+
* and resolves the committed response (`A1009`) with each handle's result in
|
|
47
|
+
* plan order, or the failure response.
|
|
59
48
|
*/
|
|
60
49
|
export type TransactionRunner = <const Ops extends readonly Operation<unknown>[]>(operations: Ops, options?: ExecutionOptions) => Promise<TransactionCommittedResponse<TransactionResults<Ops>> | OperationFailureResponse>;
|
|
61
50
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
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 wire scalar form. */
|
|
4
5
|
export declare function encodeOperationArgumentsForWire(contract: Contract, operation: OperationIdentity & {
|
|
5
6
|
readonly resource: string;
|
|
6
7
|
}, args: SemanticOperationArguments): unknown;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { OperationFingerprint } from "./deferred-operation.js";
|
|
2
|
+
/** What an operation fingerprint is computed from. */
|
|
2
3
|
export type OperationFingerprintNode = {
|
|
3
4
|
readonly resource: string;
|
|
4
5
|
readonly family: string;
|
|
5
6
|
readonly variant: string;
|
|
6
7
|
readonly args: unknown;
|
|
7
8
|
};
|
|
9
|
+
/** Whether `value` is an operation fingerprint (`fp1:…`). */
|
|
8
10
|
export declare function isOperationFingerprint(value: unknown): value is OperationFingerprint;
|
|
9
|
-
/** Computes
|
|
11
|
+
/** Computes an operation's fingerprint from its wire-form arguments. */
|
|
10
12
|
export declare function computeOperationFingerprint(node: OperationFingerprintNode): Promise<OperationFingerprint>;
|
|
@@ -28,10 +28,8 @@ export type TransactionOperationEntryInspection = {
|
|
|
28
28
|
readonly issue: ValidationIssue;
|
|
29
29
|
};
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* length and per-index inspector rather than `operations.length` or
|
|
34
|
-
* `operations[index]` directly.
|
|
31
|
+
* The caller uses the returned length and per-index inspector rather than
|
|
32
|
+
* `operations.length` or `operations[index]` directly.
|
|
35
33
|
*/
|
|
36
34
|
export declare function inspectTransactionOperations(value: unknown): TransactionOperationsInspection;
|
|
37
35
|
/** Reads one operations entry through its own data descriptor. */
|
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The ways an Adapter transaction can break the
|
|
3
|
-
*
|
|
4
|
-
* that tells them apart, so it is structured data rather than message text.
|
|
5
|
-
*
|
|
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.
|
|
2
|
+
* The ways an Adapter transaction can break the framework's transaction
|
|
3
|
+
* contract. Each is answered `A3002`.
|
|
14
4
|
*/
|
|
15
5
|
export type TransactionIntegrityViolation = "transaction-unavailable" | "resolved-after-abort" | "resolved-incomplete" | "foreign-abort";
|
|
16
6
|
/**
|
|
17
|
-
*
|
|
18
|
-
* transaction
|
|
19
|
-
* is always the constant `A3002` envelope.
|
|
7
|
+
* Reported to diagnostics when an Adapter transaction breaks the framework's
|
|
8
|
+
* transaction contract. The caller receives `A3002`.
|
|
20
9
|
*/
|
|
21
10
|
export declare class TransactionIntegrityError extends Error {
|
|
22
11
|
readonly violation: TransactionIntegrityViolation;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { FrameworkContracts } from "../compiler/contract.compiler.js";
|
|
2
2
|
import type { RequestOrigin } from "../config/config-layer.js";
|
|
3
3
|
import type { OperationIdentity } from "../operations/operation-identity.js";
|
|
4
|
+
import type { CallerDataError } from "../runtime/caller-data.copier.js";
|
|
5
|
+
import type { MisuseReporter } from "../runtime/misuse-reporter.js";
|
|
4
6
|
import type { ClientProtocolIdentity } from "../runtime/operation-request.js";
|
|
5
7
|
import type { OperationFailureResponse } from "../runtime/operation-response.js";
|
|
6
8
|
import type { Operation, OperationFingerprint } from "./deferred-operation.js";
|
|
7
9
|
import type { TransactionOperationOrigin } from "./transaction-request.js";
|
|
10
|
+
/** One wire node, exactly as the transaction runtime receives it. */
|
|
8
11
|
type WireOperationNode = {
|
|
9
12
|
readonly resource: string;
|
|
10
13
|
readonly family: string;
|
|
@@ -19,6 +22,8 @@ export type AssembledTransactionPlan = {
|
|
|
19
22
|
};
|
|
20
23
|
/** Each operation's own origin, by index (mixed plans). */
|
|
21
24
|
readonly origins: readonly TransactionOperationOrigin[];
|
|
25
|
+
/** The steps whose arguments their builder could not copy, by index. */
|
|
26
|
+
readonly refused: ReadonlyMap<number, CallerDataError>;
|
|
22
27
|
} | {
|
|
23
28
|
readonly ok: false;
|
|
24
29
|
readonly response: OperationFailureResponse;
|
|
@@ -34,5 +39,5 @@ export type TransactionPlanAssembler = {
|
|
|
34
39
|
* handle, so a handle another instance built — typed against another model,
|
|
35
40
|
* bound to other Contracts — is refused rather than run.
|
|
36
41
|
*/
|
|
37
|
-
export declare function createTransactionPlanAssembler(contracts: FrameworkContracts, protocol: ClientProtocolIdentity): TransactionPlanAssembler;
|
|
42
|
+
export declare function createTransactionPlanAssembler(contracts: FrameworkContracts, protocol: ClientProtocolIdentity, misuse: MisuseReporter): TransactionPlanAssembler;
|
|
38
43
|
export {};
|
|
@@ -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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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", [
|
|
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
|
-
? {
|
|
69
|
-
: {
|
|
70
|
-
const snapshot = (
|
|
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
|
-
|
|
86
|
+
...snapshot(args),
|
|
86
87
|
});
|
|
87
88
|
return handle;
|
|
88
89
|
};
|
|
89
|
-
const assemble = async (
|
|
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,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
:
|
|
128
|
-
|
|
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
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
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>, "
|
|
77
|
+
export type TransactionPlanDependencies<M extends AdapterModel> = Omit<OperationPipelineDependencies<M>, "recordWrites"> & {
|
|
55
78
|
readonly cascades: CascadeClosure;
|
|
56
79
|
};
|
|
57
80
|
export type TransactionPlanValidationResult = {
|
|
@@ -62,26 +85,12 @@ export type TransactionPlanValidationResult = {
|
|
|
62
85
|
readonly response: OperationFailureResponse;
|
|
63
86
|
};
|
|
64
87
|
/**
|
|
65
|
-
* The refusal of a plan on a Contract without transaction plans (`A2002`, one
|
|
66
|
-
*
|
|
67
|
-
*
|
|
88
|
+
* The refusal of a plan on a Contract without transaction plans (`A2002`, one `V1006`
|
|
89
|
+
* at `["operations"]`). The one spelling — the protocol's absence answer for
|
|
90
|
+
* `/_transactions` is this envelope too.
|
|
68
91
|
*/
|
|
69
92
|
export declare function transactionsUnavailableFailure(): OperationFailureResponse;
|
|
70
93
|
/**
|
|
71
94
|
* Whole-plan Framework Validation Layer for transaction structure and identity.
|
|
72
|
-
*
|
|
73
|
-
* `origins` states each operation's origin by index, for a plan whose steps do
|
|
74
|
-
* not all share the request's (the typed runner's mixed plans). Absent — the
|
|
75
|
-
* untyped seam — every operation takes the request's own origin. Each operation
|
|
76
|
-
* is validated against its own origin's Contract.
|
|
77
|
-
*
|
|
78
|
-
* A plan is also refused when a step depends on rows an earlier step's cascade
|
|
79
|
-
* removed (`V1019`, plan-scoped: a lone operation that cascades is not a
|
|
80
|
-
* defect, since nothing follows it). The analysis runs over every step whatever
|
|
81
|
-
* its origin — one plan runs in one Adapter transaction, so a client step's
|
|
82
|
-
* read sees an application step's cascade — and reads each step against its own
|
|
83
|
-
* origin's Contract. It is a planning answer, given before any provider statement
|
|
84
|
-
* is issued, and as conservative as the facts it has: a plan cannot know which
|
|
85
|
-
* rows a delete will match, so it refuses at the granularity of a Resource.
|
|
86
95
|
*/
|
|
87
|
-
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>;
|