@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
package/dist/runtime/decimal.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { WIRE_SCALAR_PATTERNS } from "../contracts/wire-scalar-format.js";
|
|
2
|
-
/** Provider-neutral exact decimal runtime value owned by Aventara. */
|
|
3
2
|
export class Decimal {
|
|
4
3
|
#value;
|
|
5
4
|
constructor(value) {
|
|
6
|
-
// `RegExp#test` coerces its argument, so without this guard a number from
|
|
7
|
-
// untyped code passed as `"5"` and `toString()` then returned the number.
|
|
8
5
|
if (typeof value !== "string") {
|
|
9
6
|
throw new TypeError("Decimal value must be a string.");
|
|
10
7
|
}
|
|
@@ -16,11 +13,6 @@ export class Decimal {
|
|
|
16
13
|
toString() {
|
|
17
14
|
return this.#value;
|
|
18
15
|
}
|
|
19
|
-
/**
|
|
20
|
-
* The decimal string, exactly as given: what `JSON.stringify` writes for a
|
|
21
|
-
* Decimal (Q16), so a result a developer serializes by hand carries its
|
|
22
|
-
* digits rather than `{}`. The protocol's encoders do not rely on it.
|
|
23
|
-
*/
|
|
24
16
|
toJSON() {
|
|
25
17
|
return this.#value;
|
|
26
18
|
}
|
|
@@ -5,12 +5,12 @@ import type { FrameworkOperationRequest } from "./operation-request.js";
|
|
|
5
5
|
import type { OperationErrorCode } from "./operation-response.js";
|
|
6
6
|
/**
|
|
7
7
|
* One unplanned failure as the framework saw it before sanitization: the raw
|
|
8
|
-
* value, the outcome code actually emitted, and enough identity to act on
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* value, the outcome code actually emitted, and enough identity to act on it. The
|
|
9
|
+
* code is usually an internal one, because the channel fires for unplanned
|
|
10
|
+
* failures; it is the thrower's own code when the framework dropped something it
|
|
11
|
+
* could not vouch for from an otherwise honoured deliberate outcome, which changes
|
|
12
|
+
* no outcome. Server-side only — a diagnostic is never projected onto the wire,
|
|
13
|
+
* which is precisely why it may carry the raw error.
|
|
14
14
|
*/
|
|
15
15
|
export type OperationDiagnostic = {
|
|
16
16
|
readonly kind: "operation";
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/** The default channel: no sink configured, so nothing is observed. */
|
|
2
1
|
const IGNORE_DIAGNOSTIC = () => undefined;
|
|
3
|
-
/**
|
|
4
|
-
* A sink may return a promise. Reading `then` off an arbitrary return value
|
|
5
|
-
* can itself throw, so this runs inside the reporter's own guard.
|
|
6
|
-
*/
|
|
7
2
|
function isThenable(value) {
|
|
8
3
|
if ((typeof value !== "object" || value === null) &&
|
|
9
4
|
typeof value !== "function") {
|
|
@@ -11,12 +6,6 @@ function isThenable(value) {
|
|
|
11
6
|
}
|
|
12
7
|
return typeof value.then === "function";
|
|
13
8
|
}
|
|
14
|
-
/**
|
|
15
|
-
* The channel bound to one diagnostic identity, as the reporters below bind
|
|
16
|
-
* it. Exported for `@aventara/core/protocol`, whose binding reports a failure
|
|
17
|
-
* only it can meet — a result with no wire form (`A3003`) — under the
|
|
18
|
-
* identity it holds; never exported from the main entry.
|
|
19
|
-
*/
|
|
20
9
|
export function createReporter(sink, identity) {
|
|
21
10
|
if (sink === undefined) {
|
|
22
11
|
return IGNORE_DIAGNOSTIC;
|
|
@@ -33,38 +22,15 @@ export function createReporter(sink, identity) {
|
|
|
33
22
|
}
|
|
34
23
|
};
|
|
35
24
|
}
|
|
36
|
-
/**
|
|
37
|
-
* The sink each constructed Framework was configured with, keyed by the
|
|
38
|
-
* Framework object `createFramework` returned. The protocol's binding
|
|
39
|
-
* (`AvProtocol.bind(framework)`, `@aventara/core/protocol`) reads it here, so
|
|
40
|
-
* the direction stays one-way — no execution module imports `protocol/` — and
|
|
41
|
-
* the public `Framework` type gains no member. A WeakMap: it keeps no
|
|
42
|
-
* Framework alive. A Framework built without a sink has no entry.
|
|
43
|
-
*/
|
|
44
25
|
const CONFIGURED_SINKS = new WeakMap();
|
|
45
|
-
/** Records the sink `framework` was configured with; called once, at construction. */
|
|
46
26
|
export function recordDiagnosticsSink(framework, sink) {
|
|
47
27
|
if (sink !== undefined) {
|
|
48
28
|
CONFIGURED_SINKS.set(framework, sink);
|
|
49
29
|
}
|
|
50
30
|
}
|
|
51
|
-
/** The sink `framework` was configured with, or `undefined` for none. */
|
|
52
31
|
export function diagnosticsSinkOf(framework) {
|
|
53
32
|
return CONFIGURED_SINKS.get(framework);
|
|
54
33
|
}
|
|
55
|
-
/**
|
|
56
|
-
* Binds the configured sink to one operation request.
|
|
57
|
-
*
|
|
58
|
-
* Identity is captured on entry rather than at report time: by the moment a
|
|
59
|
-
* failure is reported the request may be exactly what broke, and a diagnostic
|
|
60
|
-
* naming no operation is not actionable. A request whose own identity cannot
|
|
61
|
-
* be read is past observing, so it silently yields the no-op channel — the
|
|
62
|
-
* outermost totality guard still answers with the generic envelope.
|
|
63
|
-
*
|
|
64
|
-
* The response must be unaffected by observation, so the reporter neither
|
|
65
|
-
* awaits the sink nor lets anything the sink does escape: a throw, a rejected
|
|
66
|
-
* promise and a booby-trapped thenable all end here.
|
|
67
|
-
*/
|
|
68
34
|
export function createDiagnosticsReporter(sink, request, operation) {
|
|
69
35
|
let identity;
|
|
70
36
|
try {
|
|
@@ -83,7 +49,6 @@ export function createDiagnosticsReporter(sink, request, operation) {
|
|
|
83
49
|
}
|
|
84
50
|
return createReporter(sink, identity);
|
|
85
51
|
}
|
|
86
|
-
/** Binds the configured sink to one transaction request and its plan operations. */
|
|
87
52
|
export function createTransactionDiagnosticsChannel(sink, request) {
|
|
88
53
|
let identity;
|
|
89
54
|
try {
|
|
@@ -17,9 +17,9 @@ export declare class AdapterBoundary {
|
|
|
17
17
|
originOf(error: unknown): FailureOrigin;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* The one result-serialization text; every A3003 carries it. Its producer is
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* The one result-serialization text; every A3003 carries it. Its producer is the
|
|
21
|
+
* protocol's response encoder, which imports it from here so the code has one text
|
|
22
|
+
* wherever it is emitted.
|
|
23
23
|
*/
|
|
24
24
|
export declare const SERIALIZATION_FAILURE_MESSAGE = "Result serialization failed.";
|
|
25
25
|
/** Where a standalone operation's arguments sit in a framework issue path. */
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import { VALIDATION_CODE_MESSAGES, VALIDATION_CODES, } from "../operations/validation-code.js";
|
|
2
2
|
import { FrameworkError } from "./framework-error.js";
|
|
3
3
|
import { INTERNAL_FAILURE_MESSAGE, internalFailure, OPERATION_CODES, operationFailure, } from "./operation-response.js";
|
|
4
|
-
|
|
4
|
+
import { PIPE_OUTPUT_FAILURE_MESSAGE } from "./pipe-output.validator.js";
|
|
5
5
|
export const ADAPTER_FAILURE_MESSAGE = "Adapter execution failed.";
|
|
6
|
-
/**
|
|
7
|
-
* Structurally records every value thrown across the Adapter seam so the
|
|
8
|
-
* final error boundary can classify origin by identity, never by inspecting
|
|
9
|
-
* error text. One boundary instance belongs to one operation execution.
|
|
10
|
-
*/
|
|
11
6
|
export class AdapterBoundary {
|
|
12
7
|
#thrown = new Set();
|
|
13
|
-
/** Runs Adapter work, remembering anything it throws before re-throwing it. */
|
|
14
8
|
async execute(work) {
|
|
15
9
|
try {
|
|
16
10
|
return await work();
|
|
@@ -24,29 +18,17 @@ export class AdapterBoundary {
|
|
|
24
18
|
return this.#thrown.has(error) ? "adapter" : "runtime";
|
|
25
19
|
}
|
|
26
20
|
}
|
|
27
|
-
/**
|
|
28
|
-
* The one result-serialization text; every A3003 carries it. Its producer is
|
|
29
|
-
* the protocol's response encoder (`@aventara/core/protocol`, P1), which
|
|
30
|
-
* imports it from here so the code has one text wherever it is emitted.
|
|
31
|
-
*/
|
|
32
21
|
export const SERIALIZATION_FAILURE_MESSAGE = "Result serialization failed.";
|
|
33
|
-
/**
|
|
34
|
-
* The framework's own text for each internal-failure code. An A3xxx cause is
|
|
35
|
-
* never caller-actionable, so whatever text its thrower supplied is replaced
|
|
36
|
-
* by this table — the realistic thrower is a careless Adapter forwarding a
|
|
37
|
-
* provider message, and spec 13.5 forbids that text on the wire.
|
|
38
|
-
*/
|
|
39
22
|
const INTERNAL_FAILURE_MESSAGES = {
|
|
40
23
|
A3000: INTERNAL_FAILURE_MESSAGE,
|
|
41
24
|
A3001: ADAPTER_FAILURE_MESSAGE,
|
|
42
25
|
A3002: "Transaction failed.",
|
|
43
26
|
A3003: SERIALIZATION_FAILURE_MESSAGE,
|
|
27
|
+
A3004: PIPE_OUTPUT_FAILURE_MESSAGE,
|
|
44
28
|
};
|
|
45
|
-
/** The internal code an unexpected failure of one origin is emitted under. */
|
|
46
29
|
function unexpectedCode(origin) {
|
|
47
30
|
return origin === "adapter" ? "A3001" : "A3000";
|
|
48
31
|
}
|
|
49
|
-
/** The generic, text-free envelope for an unexpected failure of one origin. */
|
|
50
32
|
function unexpectedFailure(origin) {
|
|
51
33
|
return origin === "adapter"
|
|
52
34
|
? operationFailure("A3001", { message: ADAPTER_FAILURE_MESSAGE })
|
|
@@ -58,10 +40,6 @@ function isInternalCode(code) {
|
|
|
58
40
|
function isPathSegment(value) {
|
|
59
41
|
return typeof value === "string" || typeof value === "number";
|
|
60
42
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Whether an untrusted value is a list. `Array.isArray` is not itself total:
|
|
63
|
-
* it throws on a revoked Proxy, and a thrown value may be one.
|
|
64
|
-
*/
|
|
65
43
|
function isList(value) {
|
|
66
44
|
try {
|
|
67
45
|
return Array.isArray(value);
|
|
@@ -70,15 +48,6 @@ function isList(value) {
|
|
|
70
48
|
return false;
|
|
71
49
|
}
|
|
72
50
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Copies an untrusted list into a plain array the framework owns. Traversal,
|
|
75
|
-
* not just property reads, is untrusted here: an index accessor, a Proxy
|
|
76
|
-
* trap, an overridden `map` and a hijacked `Symbol.species` all run
|
|
77
|
-
* thrower-supplied code, and none of it may become the boundary's own
|
|
78
|
-
* failure. Elements are read one at a time through {@link readProperty}, so
|
|
79
|
-
* an unreadable element is `undefined` — a value every projection below
|
|
80
|
-
* already rejects. `undefined` means the value is no list at all.
|
|
81
|
-
*/
|
|
82
51
|
function readList(value) {
|
|
83
52
|
if (!isList(value)) {
|
|
84
53
|
return undefined;
|
|
@@ -93,12 +62,6 @@ function readList(value) {
|
|
|
93
62
|
}
|
|
94
63
|
return items;
|
|
95
64
|
}
|
|
96
|
-
/**
|
|
97
|
-
* Projects an issue path onto a plain array of segments the framework owns.
|
|
98
|
-
* Copying rather than forwarding is the point: a planted path arrives as a
|
|
99
|
-
* live handle to a caller-held array, and its non-index properties would
|
|
100
|
-
* otherwise ride into the envelope for every in-memory consumer of it.
|
|
101
|
-
*/
|
|
102
65
|
function projectPath(value) {
|
|
103
66
|
const items = readList(value);
|
|
104
67
|
if (items === undefined) {
|
|
@@ -113,31 +76,13 @@ function projectPath(value) {
|
|
|
113
76
|
}
|
|
114
77
|
return path;
|
|
115
78
|
}
|
|
116
|
-
/** Where a standalone operation's arguments sit in a framework issue path. */
|
|
117
79
|
export const OPERATION_ARGUMENTS_PATH = [
|
|
118
80
|
"arguments",
|
|
119
81
|
];
|
|
120
|
-
/** Where one plan operation's arguments sit in a framework issue path. */
|
|
121
82
|
export function planOperationArgumentsPath(index) {
|
|
122
83
|
return ["operations", index, "args"];
|
|
123
84
|
}
|
|
124
|
-
/** A segment that named no own, data-backed member of its container. */
|
|
125
85
|
const UNRESOLVED = Symbol("unresolved-path-segment");
|
|
126
|
-
/**
|
|
127
|
-
* Reads one own, data-backed member of an untrusted container.
|
|
128
|
-
*
|
|
129
|
-
* Own only: satisfying a segment from the prototype chain would let `toString`
|
|
130
|
-
* or `constructor` pose as caller data. Data-backed only: the descriptor is
|
|
131
|
-
* consulted and only its `value` used, so a getter never runs — running a
|
|
132
|
-
* caller's accessor while sanitizing an error is a side effect in the worst
|
|
133
|
-
* possible place.
|
|
134
|
-
*
|
|
135
|
-
* The reflection itself can throw: a revoked Proxy rejects
|
|
136
|
-
* `getOwnPropertyDescriptor`. That is not caught here. The whole walk has one
|
|
137
|
-
* containment point, in {@link resolvesAgainstArguments}, so there is exactly
|
|
138
|
-
* one place where an unwalkable path and an unresolvable one become the same
|
|
139
|
-
* answer.
|
|
140
|
-
*/
|
|
141
86
|
function readOwnMember(container, key) {
|
|
142
87
|
if (typeof container !== "object" || container === null) {
|
|
143
88
|
return UNRESOLVED;
|
|
@@ -147,7 +92,6 @@ function readOwnMember(container, key) {
|
|
|
147
92
|
? descriptor.value
|
|
148
93
|
: UNRESOLVED;
|
|
149
94
|
}
|
|
150
|
-
/** Resolves one path segment; a numeric segment indexes an array and nothing else. */
|
|
151
95
|
function readSegment(container, segment) {
|
|
152
96
|
if (typeof segment === "number") {
|
|
153
97
|
return Number.isInteger(segment) && segment >= 0 && isList(container)
|
|
@@ -156,22 +100,6 @@ function readSegment(container, segment) {
|
|
|
156
100
|
}
|
|
157
101
|
return readOwnMember(container, segment);
|
|
158
102
|
}
|
|
159
|
-
/**
|
|
160
|
-
* Whether a thrower-supplied path denotes a member of the caller's own
|
|
161
|
-
* arguments — the one thing that makes it safe to emit.
|
|
162
|
-
*
|
|
163
|
-
* A path that resolves echoes back a key sequence the caller already holds;
|
|
164
|
-
* anything else is unvouched content on the same channel spec 13.5 closes for
|
|
165
|
-
* the rest of `cause` (ADR 0008 decision 4, applied to the third axis). The
|
|
166
|
-
* prefix must be the framework's own address for these arguments, and every
|
|
167
|
-
* remaining segment must resolve: a resolving prefix does not license the
|
|
168
|
-
* rest, so a partial match is no match.
|
|
169
|
-
*
|
|
170
|
-
* Total, like every step the boundary takes on a thrown value. The walk runs
|
|
171
|
-
* over data the framework does not own — a revoked Proxy on it throws from
|
|
172
|
-
* reflection alone — so the whole walk sits inside one guard: an unresolvable
|
|
173
|
-
* path and an unwalkable one are the same answer.
|
|
174
|
-
*/
|
|
175
103
|
function resolvesAgainstArguments(path, view) {
|
|
176
104
|
try {
|
|
177
105
|
if (path.length < view.path.length) {
|
|
@@ -199,41 +127,16 @@ function resolvesAgainstArguments(path, view) {
|
|
|
199
127
|
return false;
|
|
200
128
|
}
|
|
201
129
|
}
|
|
202
|
-
/**
|
|
203
|
-
* Only an allocated Validation code (spec 13.3, amended by ADR 0006 DA-4) may
|
|
204
|
-
* be emitted. This is ADR 0005's rule on the axis it did not cover: the union
|
|
205
|
-
* is a compile-time promise a separately compiled Adapter or a developer
|
|
206
|
-
* filter never made, so the set is tested by identity before emission.
|
|
207
|
-
*/
|
|
208
130
|
function isValidationCode(value) {
|
|
209
131
|
return (typeof value === "string" &&
|
|
210
132
|
VALIDATION_CODES.includes(value));
|
|
211
133
|
}
|
|
212
|
-
/**
|
|
213
|
-
* Projects one issue onto the spec ValidationIssue shape; unknown keys drop.
|
|
214
|
-
*
|
|
215
|
-
* All three fields are framework-owned by the time they are emitted.
|
|
216
|
-
* An unrecognised `code` drops the whole issue rather than riding onto the
|
|
217
|
-
* wire in a field the spec defines as a closed set — and rather than being
|
|
218
|
-
* rewritten to a plausible neighbour, which would misreport what was wrong.
|
|
219
|
-
* The `message` is replaced by the framework's own published text for that
|
|
220
|
-
* code, because a thrower-supplied message is the same provider-text channel
|
|
221
|
-
* spec 13.5 closes for the rest of `cause`. The `path` is emitted only when
|
|
222
|
-
* every segment of it resolves against the caller's own arguments; it is an
|
|
223
|
-
* open-ended string channel otherwise, and spec 13.1 makes the field
|
|
224
|
-
* optional, so a path that cannot be vouched for is simply absent — the
|
|
225
|
-
* issue, its code and the outcome are unchanged. Each dropped value is
|
|
226
|
-
* appended to `unvouched`, so the boundary can hand it to the ADR 0004
|
|
227
|
-
* diagnostics channel — the only place it still exists.
|
|
228
|
-
*/
|
|
229
134
|
function projectIssue(value, unvouched, view) {
|
|
230
135
|
if (typeof value !== "object" || value === null) {
|
|
231
136
|
return undefined;
|
|
232
137
|
}
|
|
233
138
|
const code = readProperty(value, "code");
|
|
234
139
|
if (!isValidationCode(code)) {
|
|
235
|
-
// `undefined` is an unreadable or absent code, not a supplied one:
|
|
236
|
-
// reporting it would be noise, and the issue is malformed either way.
|
|
237
140
|
if (code !== undefined) {
|
|
238
141
|
unvouched.push(code);
|
|
239
142
|
}
|
|
@@ -255,18 +158,6 @@ function projectIssue(value, unvouched, view) {
|
|
|
255
158
|
}
|
|
256
159
|
return { path, code, message: owned };
|
|
257
160
|
}
|
|
258
|
-
/**
|
|
259
|
-
* Projects a thrower-supplied cause onto the spec 13 `Cause` shape:
|
|
260
|
-
* `{ message, issues?, operation? }`. Unknown keys are dropped rather than
|
|
261
|
-
* forwarded, so a provider error assigned wholesale as a cause loses its
|
|
262
|
-
* `sql`, `stack`, DSN and whatever else it carried. A cause without a string
|
|
263
|
-
* message is no cause at all.
|
|
264
|
-
*
|
|
265
|
-
* Total, like every step the boundary takes on a thrown value: a cause it
|
|
266
|
-
* cannot traverse yields the parts it could read, never an exception. A
|
|
267
|
-
* throw here would escape the boundary that was sanitizing it and cost the
|
|
268
|
-
* thrower the very outcome it deliberately named.
|
|
269
|
-
*/
|
|
270
161
|
function projectCause(value, unvouched, view) {
|
|
271
162
|
const message = readProperty(value, "message");
|
|
272
163
|
if (typeof message !== "string") {
|
|
@@ -279,29 +170,16 @@ function projectCause(value, unvouched, view) {
|
|
|
279
170
|
.filter((issue) => issue !== undefined);
|
|
280
171
|
return {
|
|
281
172
|
message,
|
|
282
|
-
// An empty list is no list: dropping every issue leaves the cause
|
|
283
|
-
// without issues rather than fabricating one to fill the field.
|
|
284
173
|
...(issues !== undefined && issues.length > 0 ? { issues } : {}),
|
|
285
174
|
...(Number.isInteger(rawOperation) && rawOperation >= 0
|
|
286
175
|
? { operation: rawOperation }
|
|
287
176
|
: {}),
|
|
288
177
|
};
|
|
289
178
|
}
|
|
290
|
-
/**
|
|
291
|
-
* Internal failures carry no caller-actionable diagnostics: the message is
|
|
292
|
-
* the framework's own and `issues` raised while validating computed/Adapter
|
|
293
|
-
* output describe server internals and stay behind the boundary (available
|
|
294
|
-
* to filters, never emitted).
|
|
295
|
-
*/
|
|
296
179
|
function internalCause(code, cause) {
|
|
297
180
|
const { issues: _issues, message: _message, ...emitted } = cause;
|
|
298
181
|
return { ...emitted, message: INTERNAL_FAILURE_MESSAGES[code] };
|
|
299
182
|
}
|
|
300
|
-
/**
|
|
301
|
-
* Reads one property of a thrown value. Thrown values are untrusted: a
|
|
302
|
-
* getter or Proxy trap may itself throw, and that must never become the
|
|
303
|
-
* boundary's own failure.
|
|
304
|
-
*/
|
|
305
183
|
function readProperty(value, key) {
|
|
306
184
|
try {
|
|
307
185
|
return value[key];
|
|
@@ -318,34 +196,16 @@ function isFrameworkErrorInstance(error) {
|
|
|
318
196
|
return false;
|
|
319
197
|
}
|
|
320
198
|
}
|
|
321
|
-
/**
|
|
322
|
-
* A {@link FrameworkError} that fails vetting. Its thrower deliberately named
|
|
323
|
-
* an outcome the framework cannot honour, so the failure is the broken
|
|
324
|
-
* outcome contract itself — an internal error, whatever the origin — and the
|
|
325
|
-
* offending value is replaced, never echoed.
|
|
326
|
-
*/
|
|
327
199
|
const MALFORMED_OUTCOME = {
|
|
328
200
|
code: "A3000",
|
|
329
201
|
cause: { message: INTERNAL_FAILURE_MESSAGE },
|
|
330
202
|
unvouched: [],
|
|
331
203
|
};
|
|
332
|
-
/**
|
|
333
|
-
* Only a recognised error code (spec 13.2) is a deliberate outcome. An
|
|
334
|
-
* unknown code, or a success code arriving on the failure path, would break
|
|
335
|
-
* the ADR 0003 union on the wire.
|
|
336
|
-
*/
|
|
337
204
|
function isOperationErrorCode(value) {
|
|
338
205
|
return (typeof value === "string" &&
|
|
339
206
|
OPERATION_CODES.includes(value) &&
|
|
340
207
|
!value.startsWith("A1"));
|
|
341
208
|
}
|
|
342
|
-
/**
|
|
343
|
-
* Vets a value as a deliberate {@link FrameworkError}. `undefined` means it
|
|
344
|
-
* is not one at all. Passing the `instanceof` check is not enough:
|
|
345
|
-
* `Object.create(FrameworkError.prototype)`, a booby-trapped accessor, an
|
|
346
|
-
* unrecognised code or a cause without a message all reach here, and each
|
|
347
|
-
* is {@link MALFORMED_OUTCOME} rather than something to dereference or emit.
|
|
348
|
-
*/
|
|
349
209
|
function vetDeliberateOutcome(error, view) {
|
|
350
210
|
if (!isFrameworkErrorInstance(error)) {
|
|
351
211
|
return undefined;
|
|
@@ -361,31 +221,16 @@ function vetDeliberateOutcome(error, view) {
|
|
|
361
221
|
const cause = projectCause(rawCause, unvouched, view);
|
|
362
222
|
return cause === undefined ? MALFORMED_OUTCOME : { code, cause, unvouched };
|
|
363
223
|
}
|
|
364
|
-
/**
|
|
365
|
-
* Hands every dropped issue code and issue path to the ADR 0004 channel under
|
|
366
|
-
* the outcome actually emitted. Dropping something the framework cannot vouch
|
|
367
|
-
* for is no grounds to escalate the thrower's chosen outcome, so the code
|
|
368
|
-
* stands and the channel carries the whole consequence. Reporting cannot
|
|
369
|
-
* affect the response: the reporter is total and swallows whatever the sink
|
|
370
|
-
* does.
|
|
371
|
-
*/
|
|
372
224
|
function reportUnvouched(outcome, report) {
|
|
373
225
|
for (const value of outcome.unvouched) {
|
|
374
226
|
report(value, outcome.code);
|
|
375
227
|
}
|
|
376
228
|
}
|
|
377
|
-
/** A deliberate outcome: the stable code and cause its thrower chose. */
|
|
378
229
|
function deliberateFailure(outcome) {
|
|
379
230
|
return operationFailure(outcome.code, isInternalCode(outcome.code)
|
|
380
231
|
? internalCause(outcome.code, outcome.cause)
|
|
381
232
|
: outcome.cause);
|
|
382
233
|
}
|
|
383
|
-
/**
|
|
384
|
-
* Final framework sanitization, shared by every boundary. A deliberate
|
|
385
|
-
* {@link FrameworkError} carries its own stable outcome; anything else is
|
|
386
|
-
* replaced by a generic cause so no provider text, stack, path, or secret
|
|
387
|
-
* can cross the response boundary. Total: it never throws.
|
|
388
|
-
*/
|
|
389
234
|
export function sanitizeOperationFailure(error, origin, report, view) {
|
|
390
235
|
const outcome = vetDeliberateOutcome(error, view);
|
|
391
236
|
if (outcome === undefined) {
|
|
@@ -398,13 +243,6 @@ export function sanitizeOperationFailure(error, origin, report, view) {
|
|
|
398
243
|
reportUnvouched(outcome, report);
|
|
399
244
|
return deliberateFailure(outcome);
|
|
400
245
|
}
|
|
401
|
-
/**
|
|
402
|
-
* Post-validation exception flow (spec 17.3): effective filters are stored as
|
|
403
|
-
* `[root..., scoped...]`, so reverse iteration tries scoped filters in reverse
|
|
404
|
-
* registration order, then root filters in reverse, then final sanitization.
|
|
405
|
-
* A filter handles an error only by returning a {@link FrameworkError};
|
|
406
|
-
* returning `undefined` or throwing leaves the original error unhandled.
|
|
407
|
-
*/
|
|
408
246
|
export async function resolveOperationFailure(error, filters, context, boundary, report, view) {
|
|
409
247
|
for (let index = filters.length - 1; index >= 0; index -= 1) {
|
|
410
248
|
const filter = filters[index];
|
|
@@ -413,9 +251,6 @@ export async function resolveOperationFailure(error, filters, context, boundary,
|
|
|
413
251
|
}
|
|
414
252
|
const handled = await attemptFilter(filter, error, context, view);
|
|
415
253
|
if (handled !== undefined) {
|
|
416
|
-
// A filter that names an outcome the framework cannot honour has
|
|
417
|
-
// swallowed the original error on the developer's behalf; the
|
|
418
|
-
// diagnostics channel is the only place it still exists.
|
|
419
254
|
if (handled === MALFORMED_OUTCOME) {
|
|
420
255
|
report(error, "A3000");
|
|
421
256
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The caller's say over an in-process request it does not write by hand: the
|
|
3
3
|
* second argument of every `framework.application.*` / `framework.client.*` call
|
|
4
|
-
* and of `framework.transaction
|
|
4
|
+
* and of `framework.transaction`.
|
|
5
5
|
*
|
|
6
|
-
* Not "transport options": an in-process call has no transport. A deferred
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Not "transport options": an in-process call has no transport. A deferred handle
|
|
7
|
+
* (`framework.appTx.*` / `framework.clientTx.*`) takes none — a handle is not a
|
|
8
|
+
* request, and the id belongs to the run that executes it.
|
|
9
9
|
*/
|
|
10
10
|
export type ExecutionOptions = {
|
|
11
11
|
/**
|
|
@@ -8,18 +8,15 @@ import type { PipelineContext } from "./pipeline.js";
|
|
|
8
8
|
* What a write-stage compute callback is told about the field it is computing.
|
|
9
9
|
*
|
|
10
10
|
* `R` is the Resource the behavior is bound to and `Cfg` the authored
|
|
11
|
-
* configuration literal, so `resource` is the literal Resource name and
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* configuration literal, so `resource` is the literal Resource name and `field` —
|
|
12
|
+
* like every other field-key position in the framework — is {@link
|
|
13
|
+
* EffectiveFieldKey}: what the adapter discovered plus what the configuration
|
|
14
|
+
* added.
|
|
15
15
|
*
|
|
16
|
-
* `data` is keyed on the same set and stays `unknown`-valued.
|
|
17
|
-
* *value* typing is `OperationResultFor`'s job in Phase 9 (F-701) and is out
|
|
18
|
-
* of scope here by the plan's hard constraints; what this type buys a callback
|
|
19
|
-
* author is the key set, not the value.
|
|
16
|
+
* `data` is keyed on the same set and stays `unknown`-valued.
|
|
20
17
|
*
|
|
21
18
|
* Under the default `Cfg` the key set degenerates to `string` and this is
|
|
22
|
-
* byte-for-byte the shape it was before `Cfg` existed
|
|
19
|
+
* byte-for-byte the shape it was before `Cfg` existed.
|
|
23
20
|
*/
|
|
24
21
|
type ComputedWriteContext<M extends AdapterModel, Cfg, R extends FrameworkResourceKey<M>> = {
|
|
25
22
|
readonly scope: RequestOrigin;
|
|
@@ -4,12 +4,11 @@ import type { Contract, CoreContract } from "../contracts/contract.js";
|
|
|
4
4
|
import type { OperationExecutionRequest } from "./operation-request.js";
|
|
5
5
|
import type { PipelineContext } from "./pipeline.js";
|
|
6
6
|
/**
|
|
7
|
-
* The two Contracts a write reads
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* stripped before the Adapter call — never sent.
|
|
7
|
+
* The two Contracts a write reads. The **Core** Contract decides what the Adapter
|
|
8
|
+
* is asked to store; the **active** Contract adds the fields its scope declared
|
|
9
|
+
* under `application.fields` / `client.fields`, which Core does not carry. Such a
|
|
10
|
+
* field is VIRTUAL by construction (`contract.compiler.ts`), so its behaviours run
|
|
11
|
+
* and its value is stripped before the Adapter call — never sent.
|
|
13
12
|
*/
|
|
14
13
|
export type WriteContracts = {
|
|
15
14
|
readonly core: CoreContract;
|
|
@@ -2,13 +2,6 @@ import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
|
2
2
|
import { FrameworkError } from "./framework-error.js";
|
|
3
3
|
import { INTERNAL_FAILURE_MESSAGE } from "./operation-response.js";
|
|
4
4
|
import { normalizeScalarFieldValue } from "./scalar-runtime.js";
|
|
5
|
-
/**
|
|
6
|
-
* The scalar field `fieldName` names on `resourceName` for a write: Core's own
|
|
7
|
-
* field when Core has one — a field the scope hides or narrows is still stored,
|
|
8
|
-
* and its behaviours still run — else the scalar the active scope added. Its
|
|
9
|
-
* values are normalized against Core's registries either way: a scope adds
|
|
10
|
-
* fields, never a scalar or an enum.
|
|
11
|
-
*/
|
|
12
5
|
function writeScalarField(contracts, resourceName, fieldName) {
|
|
13
6
|
const field = lookupField(lookupResource(contracts.core, resourceName), fieldName) ??
|
|
14
7
|
lookupField(lookupResource(contracts.active, resourceName), fieldName);
|
|
@@ -16,15 +9,6 @@ function writeScalarField(contracts, resourceName, fieldName) {
|
|
|
16
9
|
? field
|
|
17
10
|
: undefined;
|
|
18
11
|
}
|
|
19
|
-
/**
|
|
20
|
-
* The sole grammar for relation-mutation record operands. Every write pass
|
|
21
|
-
* maps these sites with its own record leaf, leaving identifier-only
|
|
22
|
-
* directives untouched. The identifier operands this table deliberately leaves
|
|
23
|
-
* empty are described by `RELATION_MUTATION_SELECTOR_SITES` in
|
|
24
|
-
* `transactions/operation-arguments-encoder.ts`, the only pass that has to
|
|
25
|
-
* encode them; both tables are keyed by `RelationMutationDirective`, so a new
|
|
26
|
-
* directive fails to compile in both places.
|
|
27
|
-
*/
|
|
28
12
|
export const RELATION_MUTATION_RECORD_SITES = {
|
|
29
13
|
$create: [{ key: undefined, stage: "create" }],
|
|
30
14
|
$connect: [],
|
|
@@ -38,11 +22,6 @@ export const RELATION_MUTATION_RECORD_SITES = {
|
|
|
38
22
|
{ key: "update", stage: "update" },
|
|
39
23
|
],
|
|
40
24
|
};
|
|
41
|
-
/**
|
|
42
|
-
* Narrows a caller-supplied own key to the shared directive vocabulary. Own
|
|
43
|
-
* membership, not an assertion: `Object.prototype` members resolve through the
|
|
44
|
-
* prototype chain and would otherwise reach a site loop with a non-iterable.
|
|
45
|
-
*/
|
|
46
25
|
export function isRelationMutationDirective(key) {
|
|
47
26
|
return Object.hasOwn(RELATION_MUTATION_RECORD_SITES, key);
|
|
48
27
|
}
|
|
@@ -144,7 +123,6 @@ async function mapRelationMutationRecords(relation, value, leaf) {
|
|
|
144
123
|
}
|
|
145
124
|
return normalized;
|
|
146
125
|
}
|
|
147
|
-
/** Apply effective computed create/update field behavior to post-pipe logical args. */
|
|
148
126
|
export async function applyWriteFieldBehaviors(contracts, behaviors, request, args, contextFactory) {
|
|
149
127
|
if (behaviors === undefined || !isRecord(args)) {
|
|
150
128
|
return args;
|
|
@@ -190,7 +168,6 @@ async function stripVirtualRecord(contracts, resourceName, data) {
|
|
|
190
168
|
}
|
|
191
169
|
return persisted;
|
|
192
170
|
}
|
|
193
|
-
/** Strip VIRTUAL scalar input only at the persistence boundary. */
|
|
194
171
|
export async function stripVirtualMutationInput(contracts, request, args) {
|
|
195
172
|
if (!isRecord(args)) {
|
|
196
173
|
return args;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ValidationIssue } from "../operations/validation-code.js";
|
|
2
|
-
export declare const OPERATION_CODES: readonly ["A1000", "A1001", "A1002", "A1003", "A1004", "A1005", "A1006", "A1007", "A1008", "A1009", "A2000", "A2001", "A2002", "A2003", "A2004", "A2005", "A2006", "A2007", "A2008", "A2009", "A2010", "A2011", "A2012", "A2013", "A2014", "A3000", "A3001", "A3002", "A3003", "A4000", "A4001", "A4002"];
|
|
2
|
+
export declare const OPERATION_CODES: readonly ["A1000", "A1001", "A1002", "A1003", "A1004", "A1005", "A1006", "A1007", "A1008", "A1009", "A2000", "A2001", "A2002", "A2003", "A2004", "A2005", "A2006", "A2007", "A2008", "A2009", "A2010", "A2011", "A2012", "A2013", "A2014", "A3000", "A3001", "A3002", "A3003", "A3004", "A4000", "A4001", "A4002"];
|
|
3
3
|
export type OperationCode = (typeof OPERATION_CODES)[number];
|
|
4
4
|
export type OperationSuccessCode = Extract<OperationCode, `A1${string}`>;
|
|
5
5
|
export type OperationErrorCode = Exclude<OperationCode, OperationSuccessCode>;
|
|
@@ -28,37 +28,21 @@ export const OPERATION_CODES = [
|
|
|
28
28
|
"A3001",
|
|
29
29
|
"A3002",
|
|
30
30
|
"A3003",
|
|
31
|
+
"A3004",
|
|
31
32
|
"A4000",
|
|
32
33
|
"A4001",
|
|
33
34
|
"A4002",
|
|
34
35
|
];
|
|
35
|
-
/**
|
|
36
|
-
* Builds the success-with-data arm. The only sanctioned way to construct
|
|
37
|
-
* one: a runtime-computed `code` (e.g. from a table-driven outcome mapper)
|
|
38
|
-
* still yields a well-typed {@link OperationSuccessResponse}.
|
|
39
|
-
*/
|
|
40
36
|
export function operationSuccess(data, code) {
|
|
41
37
|
return { data, code, cause: null };
|
|
42
38
|
}
|
|
43
|
-
/** Builds the no-match success arm (A1001). */
|
|
44
39
|
export function operationNoMatch() {
|
|
45
40
|
return { data: null, code: "A1001", cause: null };
|
|
46
41
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Builds the failure arm. The only sanctioned way to construct one: a
|
|
49
|
-
* runtime-computed error `code` still yields a well-typed
|
|
50
|
-
* {@link OperationFailureResponse}.
|
|
51
|
-
*/
|
|
52
42
|
export function operationFailure(code, cause) {
|
|
53
43
|
return { data: null, code, cause };
|
|
54
44
|
}
|
|
55
|
-
/** The one generic internal-failure text; every A3000 carries it. */
|
|
56
45
|
export const INTERNAL_FAILURE_MESSAGE = "Internal framework error.";
|
|
57
|
-
/**
|
|
58
|
-
* The generic internal-failure envelope. Nothing about an internal failure is
|
|
59
|
-
* caller-actionable, so every A3000 the framework emits is this exact value -
|
|
60
|
-
* one constant rather than one restatement per boundary.
|
|
61
|
-
*/
|
|
62
46
|
export function internalFailure() {
|
|
63
47
|
return operationFailure("A3000", { message: INTERNAL_FAILURE_MESSAGE });
|
|
64
48
|
}
|