@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
|
@@ -79,8 +79,11 @@ function projectPath(value) {
|
|
|
79
79
|
export const OPERATION_ARGUMENTS_PATH = [
|
|
80
80
|
"arguments",
|
|
81
81
|
];
|
|
82
|
+
export function planStepPath(index) {
|
|
83
|
+
return ["operations", index];
|
|
84
|
+
}
|
|
82
85
|
export function planOperationArgumentsPath(index) {
|
|
83
|
-
return [
|
|
86
|
+
return [...planStepPath(index), "args"];
|
|
84
87
|
}
|
|
85
88
|
const UNRESOLVED = Symbol("unresolved-path-segment");
|
|
86
89
|
function readOwnMember(container, key) {
|
|
@@ -100,38 +103,43 @@ function readSegment(container, segment) {
|
|
|
100
103
|
}
|
|
101
104
|
return readOwnMember(container, segment);
|
|
102
105
|
}
|
|
103
|
-
function
|
|
106
|
+
function vouchPath(path, view) {
|
|
104
107
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
for (let index = 0; index < view.path.length; index += 1) {
|
|
109
|
-
if (path[index] !== view.path[index]) {
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
108
|
+
const root = [view.path, OPERATION_ARGUMENTS_PATH].find((prefix) => startsWith(path, prefix));
|
|
109
|
+
if (root === undefined) {
|
|
110
|
+
return undefined;
|
|
112
111
|
}
|
|
113
112
|
let current = view.arguments;
|
|
114
|
-
for (let index =
|
|
113
|
+
for (let index = root.length; index < path.length; index += 1) {
|
|
115
114
|
const segment = path[index];
|
|
116
115
|
if (segment === undefined) {
|
|
117
|
-
return
|
|
116
|
+
return undefined;
|
|
118
117
|
}
|
|
119
118
|
current = readSegment(current, segment);
|
|
120
119
|
if (current === UNRESOLVED) {
|
|
121
|
-
return
|
|
120
|
+
return undefined;
|
|
122
121
|
}
|
|
123
122
|
}
|
|
124
|
-
return
|
|
123
|
+
return [...view.path, ...path.slice(root.length)];
|
|
125
124
|
}
|
|
126
125
|
catch {
|
|
127
|
-
return
|
|
126
|
+
return undefined;
|
|
128
127
|
}
|
|
129
128
|
}
|
|
129
|
+
function startsWith(path, prefix) {
|
|
130
|
+
return (path.length >= prefix.length &&
|
|
131
|
+
prefix.every((segment, index) => path[index] === segment));
|
|
132
|
+
}
|
|
130
133
|
function isValidationCode(value) {
|
|
131
134
|
return (typeof value === "string" &&
|
|
132
135
|
VALIDATION_CODES.includes(value));
|
|
133
136
|
}
|
|
134
|
-
|
|
137
|
+
export const MAX_DEVELOPER_ISSUE_MESSAGE_LENGTH = 1_000;
|
|
138
|
+
function isDeveloperMessage(message) {
|
|
139
|
+
return (message.length <= MAX_DEVELOPER_ISSUE_MESSAGE_LENGTH &&
|
|
140
|
+
message.trim().length > 0);
|
|
141
|
+
}
|
|
142
|
+
function projectIssue(value, unvouched, view, author) {
|
|
135
143
|
if (typeof value !== "object" || value === null) {
|
|
136
144
|
return undefined;
|
|
137
145
|
}
|
|
@@ -142,23 +150,32 @@ function projectIssue(value, unvouched, view) {
|
|
|
142
150
|
}
|
|
143
151
|
return undefined;
|
|
144
152
|
}
|
|
145
|
-
const
|
|
153
|
+
const supplied = readProperty(value, "message");
|
|
146
154
|
const rawPath = readProperty(value, "path");
|
|
147
|
-
if (typeof
|
|
155
|
+
if (typeof supplied !== "string") {
|
|
148
156
|
return undefined;
|
|
149
157
|
}
|
|
150
|
-
|
|
158
|
+
let message = VALIDATION_CODE_MESSAGES[code];
|
|
159
|
+
if (author === "developer") {
|
|
160
|
+
if (isDeveloperMessage(supplied)) {
|
|
161
|
+
message = supplied;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
unvouched.push(supplied);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
151
167
|
if (rawPath === undefined) {
|
|
152
|
-
return { code, message
|
|
168
|
+
return { code, message };
|
|
153
169
|
}
|
|
154
|
-
const
|
|
155
|
-
|
|
170
|
+
const projected = projectPath(rawPath);
|
|
171
|
+
const path = projected === undefined ? undefined : vouchPath(projected, view);
|
|
172
|
+
if (path === undefined) {
|
|
156
173
|
unvouched.push(rawPath);
|
|
157
|
-
return { code, message
|
|
174
|
+
return { code, message };
|
|
158
175
|
}
|
|
159
|
-
return { path, code, message
|
|
176
|
+
return { path, code, message };
|
|
160
177
|
}
|
|
161
|
-
function projectCause(value, unvouched, view) {
|
|
178
|
+
function projectCause(value, unvouched, view, author) {
|
|
162
179
|
const message = readProperty(value, "message");
|
|
163
180
|
if (typeof message !== "string") {
|
|
164
181
|
return undefined;
|
|
@@ -166,7 +183,7 @@ function projectCause(value, unvouched, view) {
|
|
|
166
183
|
const rawIssues = readList(readProperty(value, "issues"));
|
|
167
184
|
const rawOperation = readProperty(value, "operation");
|
|
168
185
|
const issues = rawIssues
|
|
169
|
-
?.map((issue) => projectIssue(issue, unvouched, view))
|
|
186
|
+
?.map((issue) => projectIssue(issue, unvouched, view, author))
|
|
170
187
|
.filter((issue) => issue !== undefined);
|
|
171
188
|
return {
|
|
172
189
|
message,
|
|
@@ -206,7 +223,7 @@ function isOperationErrorCode(value) {
|
|
|
206
223
|
OPERATION_CODES.includes(value) &&
|
|
207
224
|
!value.startsWith("A1"));
|
|
208
225
|
}
|
|
209
|
-
function vetDeliberateOutcome(error, view) {
|
|
226
|
+
function vetDeliberateOutcome(error, view, author) {
|
|
210
227
|
if (!isFrameworkErrorInstance(error)) {
|
|
211
228
|
return undefined;
|
|
212
229
|
}
|
|
@@ -218,7 +235,7 @@ function vetDeliberateOutcome(error, view) {
|
|
|
218
235
|
return MALFORMED_OUTCOME;
|
|
219
236
|
}
|
|
220
237
|
const unvouched = [];
|
|
221
|
-
const cause = projectCause(rawCause, unvouched, view);
|
|
238
|
+
const cause = projectCause(rawCause, unvouched, view, author);
|
|
222
239
|
return cause === undefined ? MALFORMED_OUTCOME : { code, cause, unvouched };
|
|
223
240
|
}
|
|
224
241
|
function reportUnvouched(outcome, report) {
|
|
@@ -226,13 +243,20 @@ function reportUnvouched(outcome, report) {
|
|
|
226
243
|
report(value, outcome.code);
|
|
227
244
|
}
|
|
228
245
|
}
|
|
246
|
+
function reportDeliberate(thrown, outcome, report) {
|
|
247
|
+
if (isInternalCode(outcome.code)) {
|
|
248
|
+
report(thrown, outcome.code, { deliberate: true });
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
reportUnvouched(outcome, report);
|
|
252
|
+
}
|
|
229
253
|
function deliberateFailure(outcome) {
|
|
230
254
|
return operationFailure(outcome.code, isInternalCode(outcome.code)
|
|
231
255
|
? internalCause(outcome.code, outcome.cause)
|
|
232
256
|
: outcome.cause);
|
|
233
257
|
}
|
|
234
258
|
export function sanitizeOperationFailure(error, origin, report, view) {
|
|
235
|
-
const outcome = vetDeliberateOutcome(error, view);
|
|
259
|
+
const outcome = vetDeliberateOutcome(error, view, origin === "adapter" ? "adapter" : "developer");
|
|
236
260
|
if (outcome === undefined) {
|
|
237
261
|
report(error, unexpectedCode(origin));
|
|
238
262
|
return unexpectedFailure(origin);
|
|
@@ -240,7 +264,9 @@ export function sanitizeOperationFailure(error, origin, report, view) {
|
|
|
240
264
|
if (outcome === MALFORMED_OUTCOME) {
|
|
241
265
|
report(error, "A3000");
|
|
242
266
|
}
|
|
243
|
-
|
|
267
|
+
else {
|
|
268
|
+
reportDeliberate(error, outcome, report);
|
|
269
|
+
}
|
|
244
270
|
return deliberateFailure(outcome);
|
|
245
271
|
}
|
|
246
272
|
export async function resolveOperationFailure(error, filters, context, boundary, report, view) {
|
|
@@ -249,20 +275,24 @@ export async function resolveOperationFailure(error, filters, context, boundary,
|
|
|
249
275
|
if (filter === undefined) {
|
|
250
276
|
continue;
|
|
251
277
|
}
|
|
252
|
-
const handled = await attemptFilter(filter, error, context, view);
|
|
278
|
+
const handled = await attemptFilter(filter, error, context, view, boundary);
|
|
253
279
|
if (handled !== undefined) {
|
|
254
|
-
if (handled === MALFORMED_OUTCOME) {
|
|
280
|
+
if (handled.outcome === MALFORMED_OUTCOME) {
|
|
255
281
|
report(error, "A3000");
|
|
256
282
|
}
|
|
257
|
-
|
|
258
|
-
|
|
283
|
+
else {
|
|
284
|
+
reportDeliberate(handled.returned, handled.outcome, report);
|
|
285
|
+
}
|
|
286
|
+
return deliberateFailure(handled.outcome);
|
|
259
287
|
}
|
|
260
288
|
}
|
|
261
289
|
return sanitizeOperationFailure(error, boundary.originOf(error), report, view);
|
|
262
290
|
}
|
|
263
|
-
async function attemptFilter(filter, error, context, view) {
|
|
291
|
+
async function attemptFilter(filter, error, context, view, boundary) {
|
|
264
292
|
try {
|
|
265
|
-
|
|
293
|
+
const returned = await filter(error, context);
|
|
294
|
+
const outcome = vetDeliberateOutcome(returned, view, boundary.originOf(returned) === "adapter" ? "adapter" : "developer");
|
|
295
|
+
return outcome === undefined ? undefined : { returned, outcome };
|
|
266
296
|
}
|
|
267
297
|
catch {
|
|
268
298
|
return undefined;
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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.
|
|
2
|
+
* Options for one in-process call: the optional second argument of every
|
|
3
|
+
* `framework.application.*` / `framework.client.*` call and of
|
|
4
|
+
* `framework.transaction`.
|
|
9
5
|
*/
|
|
10
6
|
export type ExecutionOptions = {
|
|
11
7
|
/**
|
|
@@ -6,34 +6,31 @@ import type { FrameworkResourceKey } from "../config/framework-fields-config.js"
|
|
|
6
6
|
import type { PipelineContext } from "./pipeline.js";
|
|
7
7
|
/**
|
|
8
8
|
* What a write-stage compute callback is told about the field it is computing.
|
|
9
|
-
*
|
|
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 `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
|
-
*
|
|
16
|
-
* `data` is keyed on the same set and stays `unknown`-valued.
|
|
17
|
-
*
|
|
18
|
-
* Under the default `Cfg` the key set degenerates to `string` and this is
|
|
19
|
-
* byte-for-byte the shape it was before `Cfg` existed.
|
|
20
9
|
*/
|
|
21
10
|
type ComputedWriteContext<M extends AdapterModel, Cfg, R extends FrameworkResourceKey<M>> = {
|
|
22
|
-
readonly
|
|
11
|
+
readonly origin: RequestOrigin;
|
|
23
12
|
readonly resource: R;
|
|
24
13
|
readonly field: EffectiveFieldKey<M, Cfg, R>;
|
|
25
14
|
readonly value: unknown;
|
|
26
15
|
readonly data: Readonly<Record<EffectiveFieldKey<M, Cfg, R>, unknown>>;
|
|
27
16
|
readonly operation: PipelineContext;
|
|
28
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Computes a field's value when a row is created. It receives the incoming
|
|
20
|
+
* `data` and returns the value to store.
|
|
21
|
+
*/
|
|
29
22
|
export type ComputeOnCreate<M extends AdapterModel = AdapterModel, Cfg = AnyFrameworkConfig, R extends FrameworkResourceKey<M> = FrameworkResourceKey<M>> = (context: ComputedWriteContext<M, Cfg, R>) => unknown | Promise<unknown>;
|
|
23
|
+
/**
|
|
24
|
+
* Computes a field's value when a row is updated. It receives the incoming
|
|
25
|
+
* `data` and returns the value to store.
|
|
26
|
+
*/
|
|
30
27
|
export type ComputeOnUpdate<M extends AdapterModel = AdapterModel, Cfg = AnyFrameworkConfig, R extends FrameworkResourceKey<M> = FrameworkResourceKey<M>> = (context: ComputedWriteContext<M, Cfg, R>) => unknown | Promise<unknown>;
|
|
31
28
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
29
|
+
* Computes a field's value when a row is read. It receives the stored `record`
|
|
30
|
+
* and returns the value to return.
|
|
34
31
|
*/
|
|
35
32
|
export type ComputeOnRead<M extends AdapterModel = AdapterModel, Cfg = AnyFrameworkConfig, R extends FrameworkResourceKey<M> = FrameworkResourceKey<M>> = (context: {
|
|
36
|
-
readonly
|
|
33
|
+
readonly origin: RequestOrigin;
|
|
37
34
|
readonly resource: R;
|
|
38
35
|
readonly field: EffectiveFieldKey<M, Cfg, R>;
|
|
39
36
|
readonly value: unknown;
|
|
@@ -41,12 +38,8 @@ export type ComputeOnRead<M extends AdapterModel = AdapterModel, Cfg = AnyFramew
|
|
|
41
38
|
readonly operation: PipelineContext;
|
|
42
39
|
}) => unknown | Promise<unknown>;
|
|
43
40
|
/**
|
|
44
|
-
* A computed read,
|
|
45
|
-
*
|
|
46
|
-
* `dependsOn` is a field-key position and therefore keys on
|
|
47
|
-
* {@link EffectiveFieldKey} like every other one. A name outside that set is
|
|
48
|
-
* an array-element assignability failure (`TS2322`), not an excess property:
|
|
49
|
-
* the author wrote a `string` where a member of the key union was required.
|
|
41
|
+
* A computed read: a compute function, or `{ compute, dependsOn }` naming the
|
|
42
|
+
* fields it reads.
|
|
50
43
|
*/
|
|
51
44
|
export type ComputedReadBehavior<M extends AdapterModel = AdapterModel, Cfg = AnyFrameworkConfig, R extends FrameworkResourceKey<M> = FrameworkResourceKey<M>> = ComputeOnRead<M, Cfg, R> | {
|
|
52
45
|
readonly dependsOn?: readonly EffectiveFieldKey<M, Cfg, R>[];
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import type { Cause, OperationErrorCode } from "./operation-response.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Throw from a pipeline, behavior or Adapter to answer the request with one
|
|
4
|
+
* framework error code and cause.
|
|
5
|
+
*
|
|
6
|
+
* For any code outside `A3xxx`, `cause.message` is sent to the caller exactly as
|
|
7
|
+
* written: put nothing in it you would not show the caller (no database text,
|
|
8
|
+
* SQL, stack, path or secret). So is each issue's `message`, when your own code
|
|
9
|
+
* (a pipe, guard, hook, interceptor, filter or behavior) throws it and it is not
|
|
10
|
+
* blank and at most 1,000 characters; otherwise, and for an issue an Adapter
|
|
11
|
+
* throws, the caller receives the framework's own text for the issue's code. An
|
|
12
|
+
* issue's `code` must be a validation code (`V1000` and up) and its `path` must
|
|
13
|
+
* name a member of the request's own arguments, or the issue, or the path, is
|
|
14
|
+
* not sent. For an `A3xxx` code the caller receives the framework's generic
|
|
15
|
+
* message instead, and nothing of the cause; the error itself reaches the root
|
|
16
|
+
* `diagnostics` sink once, marked `deliberate: true`.
|
|
5
17
|
*/
|
|
6
18
|
export declare class FrameworkError extends Error {
|
|
7
19
|
readonly code: OperationErrorCode;
|
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A JSON value: the
|
|
2
|
+
* A JSON value: the form of the `json` scalar, in arguments and results.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* and
|
|
6
|
-
*
|
|
7
|
-
* a `bigint`, a `Uint8Array`, core's `Decimal`, any other class instance and an
|
|
8
|
-
* `undefined` property are refused (`V1001`), and none of them is a member here.
|
|
4
|
+
* A `Date`, a `bigint`, a `Uint8Array`, a {@link Decimal}, any other class
|
|
5
|
+
* instance and an `undefined` property are refused (`V1001`), as are a
|
|
6
|
+
* non-finite `number` (`V1004`) and a cycle (`V1001`).
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
* a cycle (`V1001`).
|
|
8
|
+
* Results are fresh plain copies, so mutating one is safe.
|
|
12
9
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* one is safe, but nothing is promised about it.
|
|
16
|
-
*
|
|
17
|
-
* A `json` FIELD's form is this without its top-level `null`
|
|
18
|
-
* (`ApplicationScalarForms["json"]`): a field-level `null` is the field's
|
|
19
|
-
* absence, which only `nullable: true` admits.
|
|
10
|
+
* A `json` field's value is this without its top-level `null`: a field-level
|
|
11
|
+
* `null` is the field's absence, which only `nullable: true` admits.
|
|
20
12
|
*/
|
|
21
13
|
export type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
|
|
22
14
|
readonly [key: string]: JsonValue;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { RequestOrigin } from "../config/config-layer.js";
|
|
2
|
+
import type { OperationFamily } from "../operations/operation-identity.js";
|
|
3
|
+
/**
|
|
4
|
+
* One caller value the framework refused because it could not be read
|
|
5
|
+
* (`"unreadable-value"`: a revoked Proxy, a throwing getter or trap) or is not plain
|
|
6
|
+
* data (`"non-plain-value"`: a class instance other than `Date`, `Decimal` or
|
|
7
|
+
* `Uint8Array`). The caller received `A2004` with one `V1001` at `path`.
|
|
8
|
+
*
|
|
9
|
+
* The operation's identity is present when the framework could read it — absent for
|
|
10
|
+
* a request whose own envelope could not be read — and `operation` is the step's
|
|
11
|
+
* index in a transaction plan.
|
|
12
|
+
*/
|
|
13
|
+
export type FrameworkMisuseEvent = {
|
|
14
|
+
readonly kind: "unreadable-value" | "non-plain-value";
|
|
15
|
+
/** The issue path the caller received. */
|
|
16
|
+
readonly path: readonly (string | number)[];
|
|
17
|
+
readonly origin?: RequestOrigin;
|
|
18
|
+
readonly resource?: string;
|
|
19
|
+
readonly family?: OperationFamily;
|
|
20
|
+
readonly variant?: string;
|
|
21
|
+
readonly operation?: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Receives a {@link FrameworkMisuseEvent} for every caller value the framework
|
|
25
|
+
* refused as unreadable or not plain data. Set once, as the root configuration's
|
|
26
|
+
* `misuse`. What it returns, throws or rejects with never reaches the response.
|
|
27
|
+
*/
|
|
28
|
+
export type FrameworkMisuseSink = (event: FrameworkMisuseEvent) => void | Promise<void>;
|
|
29
|
+
/** The channel as the runtime holds it: total, never awaited. */
|
|
30
|
+
export type MisuseReporter = (event: FrameworkMisuseEvent) => void;
|
|
31
|
+
/** Binds the configured sink; without one, nothing is observed. */
|
|
32
|
+
export declare function createMisuseReporter(sink: FrameworkMisuseSink | undefined): MisuseReporter;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const IGNORE_MISUSE = () => undefined;
|
|
2
|
+
export function createMisuseReporter(sink) {
|
|
3
|
+
if (sink === undefined) {
|
|
4
|
+
return IGNORE_MISUSE;
|
|
5
|
+
}
|
|
6
|
+
return (event) => {
|
|
7
|
+
try {
|
|
8
|
+
const settled = sink(event);
|
|
9
|
+
if (typeof settled === "object" &&
|
|
10
|
+
settled !== null &&
|
|
11
|
+
typeof settled.then === "function") {
|
|
12
|
+
settled.then(undefined, () => undefined);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -4,11 +4,10 @@ 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. The
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* and its value is stripped before the Adapter call — never sent.
|
|
7
|
+
* The two Contracts a write reads. The Core Contract decides what the Adapter is asked
|
|
8
|
+
* to store; the active Contract adds the fields its scope declared under
|
|
9
|
+
* `application.fields` / `client.fields`, which Core does not carry. Such a field is
|
|
10
|
+
* virtual: its behaviours run and its value is stripped before the Adapter call.
|
|
12
11
|
*/
|
|
13
12
|
export type WriteContracts = {
|
|
14
13
|
readonly core: CoreContract;
|
|
@@ -21,13 +20,8 @@ export type RelationRecordSite = {
|
|
|
21
20
|
readonly stage: WriteStage;
|
|
22
21
|
};
|
|
23
22
|
/**
|
|
24
|
-
* The sole grammar for relation-mutation record operands. Every write pass
|
|
25
|
-
*
|
|
26
|
-
* directives untouched. The identifier operands this table deliberately leaves
|
|
27
|
-
* empty are described by `RELATION_MUTATION_SELECTOR_SITES` in
|
|
28
|
-
* `transactions/operation-arguments-encoder.ts`, the only pass that has to
|
|
29
|
-
* encode them; both tables are keyed by `RelationMutationDirective`, so a new
|
|
30
|
-
* directive fails to compile in both places.
|
|
23
|
+
* The sole grammar for relation-mutation record operands. Every write pass maps these
|
|
24
|
+
* sites with its own record leaf, leaving identifier-only directives untouched.
|
|
31
25
|
*/
|
|
32
26
|
export declare const RELATION_MUTATION_RECORD_SITES: {
|
|
33
27
|
readonly $create: readonly [{
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { normalizeScalarFieldValue } from "./scalar-runtime.js";
|
|
2
|
+
import { ComputedValueError } from "./computed-value.error.js";
|
|
3
|
+
import { jsonShapeIssues, normalizeScalarFieldValue, } from "./scalar-runtime.js";
|
|
5
4
|
function writeScalarField(contracts, resourceName, fieldName) {
|
|
6
5
|
const field = lookupField(lookupResource(contracts.core, resourceName), fieldName) ??
|
|
7
6
|
lookupField(lookupResource(contracts.active, resourceName), fieldName);
|
|
@@ -34,12 +33,6 @@ function cloneRecord(value) {
|
|
|
34
33
|
function relationEntries(value) {
|
|
35
34
|
return Array.isArray(value) ? value : [value];
|
|
36
35
|
}
|
|
37
|
-
function behaviorFailure(issues) {
|
|
38
|
-
throw new FrameworkError("A3000", {
|
|
39
|
-
message: INTERNAL_FAILURE_MESSAGE,
|
|
40
|
-
issues,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
36
|
async function computeRecord(contracts, behaviors, request, resourceName, stage, data, contextFactory) {
|
|
44
37
|
if (!isRecord(data)) {
|
|
45
38
|
return data;
|
|
@@ -64,7 +57,7 @@ async function computeRecord(contracts, behaviors, request, resourceName, stage,
|
|
|
64
57
|
continue;
|
|
65
58
|
}
|
|
66
59
|
const computed = await callback({
|
|
67
|
-
|
|
60
|
+
origin: request.origin,
|
|
68
61
|
resource: resourceName,
|
|
69
62
|
field: fieldName,
|
|
70
63
|
value: logicalData[fieldName],
|
|
@@ -76,7 +69,16 @@ async function computeRecord(contracts, behaviors, request, resourceName, stage,
|
|
|
76
69
|
}
|
|
77
70
|
const validation = normalizeScalarFieldValue(contracts.core, field, computed, "runtime", ["behaviors", resourceName, fieldName, stage]);
|
|
78
71
|
if (!validation.ok) {
|
|
79
|
-
|
|
72
|
+
throw new ComputedValueError(resourceName, fieldName, stage, validation.issues);
|
|
73
|
+
}
|
|
74
|
+
const shapeIssues = jsonShapeIssues(field, validation.value, [
|
|
75
|
+
"behaviors",
|
|
76
|
+
resourceName,
|
|
77
|
+
fieldName,
|
|
78
|
+
stage,
|
|
79
|
+
]);
|
|
80
|
+
if (shapeIssues.length > 0) {
|
|
81
|
+
throw new ComputedValueError(resourceName, fieldName, stage, shapeIssues);
|
|
80
82
|
}
|
|
81
83
|
logicalData[fieldName] = validation.value;
|
|
82
84
|
}
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import type { RequestOrigin } from "../config/config-layer.js";
|
|
2
1
|
import type { OperationIdentity } from "../operations/operation-identity.js";
|
|
3
2
|
import type { PipelineTransportContext } from "./pipeline.js";
|
|
4
|
-
/**
|
|
5
|
-
* @deprecated Use {@link RequestOrigin}. The set it names is not a property of
|
|
6
|
-
* an operation but the origin layer a request belongs to, derived from
|
|
7
|
-
* `ConfigLayer`; kept as an alias so existing consumers keep compiling.
|
|
8
|
-
*/
|
|
9
|
-
export type OperationScope = RequestOrigin;
|
|
3
|
+
/** The protocol version and ClientContract hash a client says it was built for. */
|
|
10
4
|
export type ClientProtocolIdentity = {
|
|
11
5
|
readonly version: number;
|
|
12
6
|
readonly hash: string;
|
|
@@ -16,24 +10,30 @@ type RuntimeOperationBase = OperationIdentity & {
|
|
|
16
10
|
readonly arguments: unknown;
|
|
17
11
|
readonly requestId: string;
|
|
18
12
|
};
|
|
13
|
+
/** An in-process request, run under the Application Contract. */
|
|
19
14
|
export type ApplicationOperationRequest = RuntimeOperationBase & {
|
|
20
|
-
readonly
|
|
15
|
+
readonly origin: "application";
|
|
21
16
|
readonly protocol?: never;
|
|
22
17
|
readonly transport?: never;
|
|
23
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* A remote request, run under the Client Contract; `protocol` is the identity
|
|
21
|
+
* the client sent.
|
|
22
|
+
*/
|
|
24
23
|
export type ClientOperationRequest = RuntimeOperationBase & {
|
|
25
|
-
readonly
|
|
24
|
+
readonly origin: "client";
|
|
26
25
|
readonly protocol: ClientProtocolIdentity;
|
|
27
26
|
readonly transport?: PipelineTransportContext;
|
|
28
27
|
};
|
|
28
|
+
/** One operation request, as `framework.execute` takes it. */
|
|
29
29
|
export type FrameworkOperationRequest = ApplicationOperationRequest | ClientOperationRequest;
|
|
30
30
|
type WithoutArguments<Request> = Request extends unknown ? Omit<Request, "arguments"> : never;
|
|
31
31
|
/**
|
|
32
32
|
* A request as every stage after the Framework Validation Layer sees it:
|
|
33
|
-
* identity,
|
|
33
|
+
* identity, origin, request id and transport, but never `arguments`. Those
|
|
34
34
|
* stages act on the validated arguments passed beside it; the unvalidated
|
|
35
35
|
* view — inside a transaction plan still carrying `$ref` placeholders — is
|
|
36
|
-
* unreachable by type. Distributive, so the
|
|
36
|
+
* unreachable by type. Distributive, so the origin and family/variant
|
|
37
37
|
* correlations survive the omission.
|
|
38
38
|
*/
|
|
39
39
|
export type OperationExecutionRequest = WithoutArguments<FrameworkOperationRequest>;
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import type { ValidationIssue } from "../operations/validation-code.js";
|
|
2
|
+
/** Every outcome code, as a runtime list. */
|
|
2
3
|
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"];
|
|
4
|
+
/**
|
|
5
|
+
* Every outcome code: `A1xxx` success, `A2xxx` caller error, `A3xxx` internal
|
|
6
|
+
* failure, `A4xxx` authentication or permission denial.
|
|
7
|
+
*/
|
|
3
8
|
export type OperationCode = (typeof OPERATION_CODES)[number];
|
|
9
|
+
/** The success codes, `A1xxx`. */
|
|
4
10
|
export type OperationSuccessCode = Extract<OperationCode, `A1${string}`>;
|
|
11
|
+
/** Every outcome code that is not a success. */
|
|
5
12
|
export type OperationErrorCode = Exclude<OperationCode, OperationSuccessCode>;
|
|
6
13
|
/** Internal-failure family: nothing in the cause is caller-actionable. */
|
|
7
14
|
export type OperationInternalErrorCode = Extract<OperationErrorCode, `A3${string}`>;
|
|
15
|
+
/** Why a request failed: a message, and the validation issues when there are any. */
|
|
8
16
|
export type Cause = {
|
|
9
17
|
readonly message: string;
|
|
10
18
|
readonly issues?: readonly ValidationIssue[];
|
|
11
19
|
readonly operation?: number;
|
|
12
20
|
};
|
|
13
|
-
/**
|
|
14
|
-
* The canonical wire envelope (spec section 13): a flat `{ data, code,
|
|
15
|
-
* cause }` shape. `OperationResponse<T>` below is a discriminated union
|
|
16
|
-
* derived from this one shape — every arm is a differently-constrained
|
|
17
|
-
* projection of the same three fields, never an independent structure.
|
|
18
|
-
*/
|
|
21
|
+
/** The response envelope: `{ data, code, cause }`. */
|
|
19
22
|
type OperationResponseEnvelope<TData, TCode extends OperationCode, TCause extends Cause | null> = {
|
|
20
23
|
readonly data: TData;
|
|
21
24
|
readonly code: TCode;
|
|
@@ -23,33 +26,25 @@ type OperationResponseEnvelope<TData, TCode extends OperationCode, TCause extend
|
|
|
23
26
|
};
|
|
24
27
|
/** Success codes that carry response data (every success code but A1001). */
|
|
25
28
|
export type OperationSuccessDataCode = Exclude<OperationSuccessCode, "A1001">;
|
|
26
|
-
/**
|
|
29
|
+
/** A success with data: `cause` is `null`. */
|
|
27
30
|
export type OperationSuccessResponse<T> = OperationResponseEnvelope<T, OperationSuccessDataCode, null>;
|
|
28
|
-
/**
|
|
31
|
+
/** A success with no matching row (`A1001`): `data` and `cause` are `null`. */
|
|
29
32
|
export type OperationNoMatchResponse = OperationResponseEnvelope<null, "A1001", null>;
|
|
30
|
-
/**
|
|
33
|
+
/** A failure: `data` is `null` and `cause` says why. */
|
|
31
34
|
export type OperationFailureResponse = OperationResponseEnvelope<null, OperationErrorCode, Cause>;
|
|
32
|
-
export type OperationResponse<T = unknown> = OperationSuccessResponse<T> | OperationNoMatchResponse | OperationFailureResponse;
|
|
33
35
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* still yields a well-typed {@link OperationSuccessResponse}.
|
|
36
|
+
* What an operation answers: success with data, a no-match success (`A1001`),
|
|
37
|
+
* or a failure with a cause.
|
|
37
38
|
*/
|
|
39
|
+
export type OperationResponse<T = unknown> = OperationSuccessResponse<T> | OperationNoMatchResponse | OperationFailureResponse;
|
|
40
|
+
/** Builds a success response with data. */
|
|
38
41
|
export declare function operationSuccess<T>(data: T, code: OperationSuccessDataCode): OperationSuccessResponse<T>;
|
|
39
42
|
/** Builds the no-match success arm (A1001). */
|
|
40
43
|
export declare function operationNoMatch(): OperationNoMatchResponse;
|
|
41
|
-
/**
|
|
42
|
-
* Builds the failure arm. The only sanctioned way to construct one: a
|
|
43
|
-
* runtime-computed error `code` still yields a well-typed
|
|
44
|
-
* {@link OperationFailureResponse}.
|
|
45
|
-
*/
|
|
44
|
+
/** Builds a failure response. */
|
|
46
45
|
export declare function operationFailure(code: OperationErrorCode, cause: Cause): OperationFailureResponse;
|
|
47
46
|
/** The one generic internal-failure text; every A3000 carries it. */
|
|
48
47
|
export declare const INTERNAL_FAILURE_MESSAGE = "Internal framework error.";
|
|
49
|
-
/**
|
|
50
|
-
* The generic internal-failure envelope. Nothing about an internal failure is
|
|
51
|
-
* caller-actionable, so every A3000 the framework emits is this exact value -
|
|
52
|
-
* one constant rather than one restatement per boundary.
|
|
53
|
-
*/
|
|
48
|
+
/** The response every internal failure (`A3000`) answers with. */
|
|
54
49
|
export declare function internalFailure(): OperationFailureResponse;
|
|
55
50
|
export {};
|