@aventara/core 0.1.0-pilot.4 → 1.0.0-rc.1
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 +1478 -0
- package/README.md +275 -11
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
- package/dist/adapters/adapter-field.d.ts +11 -11
- package/dist/adapters/adapter-identifier.d.ts +6 -6
- package/dist/adapters/adapter-model.d.ts +17 -9
- package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
- package/dist/adapters/adapter-operation.d.ts +3 -3
- package/dist/adapters/adapter-resource.d.ts +18 -9
- package/dist/adapters/adapter-sql-statement.d.ts +19 -0
- package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
- package/dist/adapters/adapter.interface.d.ts +49 -25
- package/dist/avz/avz-check.factory.d.ts +83 -0
- package/dist/avz/avz-check.factory.js +116 -0
- package/dist/avz/avz-check.registry.d.ts +35 -0
- package/dist/avz/avz-check.registry.js +9 -0
- package/dist/avz/avz.d.ts +203 -0
- package/dist/avz/avz.js +354 -0
- package/dist/avz/index.d.ts +7 -0
- package/dist/avz/index.js +1 -0
- package/dist/avz/json-shape.registry.d.ts +22 -0
- package/dist/avz/json-shape.registry.js +16 -0
- package/dist/avz/zod-issue.translator.d.ts +57 -0
- package/dist/avz/zod-issue.translator.js +149 -0
- package/dist/compiler/contract-compiler-diagnostic.d.ts +37 -5
- package/dist/compiler/contract.compiler.d.ts +18 -18
- package/dist/compiler/contract.compiler.js +595 -421
- package/dist/compiler/create-requirements.projector.d.ts +49 -0
- package/dist/compiler/create-requirements.projector.js +423 -0
- package/dist/compiler/field-validator.compiler.d.ts +47 -0
- package/dist/compiler/field-validator.compiler.js +238 -0
- package/dist/config/any-framework-config.d.ts +19 -12
- package/dist/config/config-diagnostic.d.ts +11 -4
- package/dist/config/config-layer.d.ts +15 -6
- package/dist/config/config.resolver.d.ts +75 -29
- package/dist/config/config.resolver.js +109 -44
- package/dist/config/config.validator.d.ts +7 -7
- package/dist/config/config.validator.js +586 -71
- package/dist/config/define-resource.d.ts +57 -0
- package/dist/config/define-resource.js +14 -0
- package/dist/config/effective-fields.d.ts +19 -23
- package/dist/config/entrypoint.normalizer.d.ts +1 -1
- package/dist/config/exact-framework-config.d.ts +280 -0
- package/dist/config/field-compute.resolver.d.ts +19 -0
- package/dist/config/field-compute.resolver.js +115 -0
- package/dist/config/field-pipeline-config.d.ts +47 -0
- package/dist/config/forced-operation.validator.d.ts +10 -0
- package/dist/config/forced-operation.validator.js +66 -0
- package/dist/config/framework-config.d.ts +111 -37
- package/dist/config/framework-exemptions-config.d.ts +91 -0
- package/dist/config/framework-exemptions-config.js +1 -0
- package/dist/config/framework-field-definition.d.ts +78 -0
- package/dist/config/framework-field-definition.js +28 -0
- package/dist/config/framework-resources-config.d.ts +116 -0
- package/dist/config/framework-resources-config.js +1 -0
- package/dist/config/pipeline-config.d.ts +56 -20
- package/dist/config/pipeline-tree.resolver.d.ts +79 -0
- package/dist/config/pipeline-tree.resolver.js +220 -0
- package/dist/config/stage-type.eraser.d.ts +65 -0
- package/dist/config/stage-type.eraser.js +1 -0
- package/dist/contracts/capability.d.ts +11 -11
- package/dist/contracts/client-contract-structure.validator.d.ts +344 -29
- package/dist/contracts/client-contract-structure.validator.js +248 -7
- package/dist/contracts/contract-canonicalizer.d.ts +19 -3
- package/dist/contracts/contract-canonicalizer.js +172 -27
- package/dist/contracts/contract-hash.d.ts +12 -7
- package/dist/contracts/contract-hash.js +3 -2
- package/dist/contracts/contract-limits.d.ts +4 -1
- package/dist/contracts/contract-limits.js +1 -0
- package/dist/contracts/contract-lookup.d.ts +5 -5
- package/dist/contracts/contract-protocol.d.ts +4 -4
- package/dist/contracts/contract.d.ts +31 -16
- package/dist/contracts/create-requirements.d.ts +35 -0
- package/dist/contracts/create-requirements.js +1 -0
- package/dist/contracts/enum-definition.d.ts +1 -1
- package/dist/contracts/field-check.d.ts +101 -0
- package/dist/contracts/field-check.js +1 -0
- package/dist/contracts/field.d.ts +61 -41
- package/dist/contracts/identifier.d.ts +4 -4
- package/dist/contracts/json-shape.d.ts +76 -0
- package/dist/contracts/json-shape.js +1 -0
- package/dist/contracts/lifecycle-trait.d.ts +2 -2
- 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 +28 -28
- package/dist/contracts/resource.d.ts +15 -9
- package/dist/contracts/scalar-value-type.d.ts +25 -14
- package/dist/contracts/scalar.d.ts +3 -3
- package/dist/contracts/transaction-capability.d.ts +1 -1
- package/dist/contracts/wire-scalar-format.d.ts +4 -4
- package/dist/framework/contract-view.d.ts +234 -63
- package/dist/framework/framework-construction.error.d.ts +10 -10
- package/dist/framework/framework-construction.error.js +2 -2
- package/dist/framework/framework.d.ts +50 -24
- package/dist/framework/framework.factory.d.ts +8 -6
- package/dist/framework/framework.factory.js +95 -32
- package/dist/framework/operation-facade.d.ts +123 -52
- package/dist/framework/operation-facade.js +9 -1
- package/dist/index.d.ts +55 -82
- package/dist/index.js +6 -26
- package/dist/internal.d.ts +40 -0
- package/dist/internal.js +18 -0
- package/dist/operations/operation-arguments.d.ts +316 -201
- package/dist/operations/operation-call.d.ts +27 -28
- package/dist/operations/operation-identity.d.ts +12 -10
- package/dist/operations/operation-identity.js +3 -0
- package/dist/operations/operation-result.d.ts +21 -21
- package/dist/operations/operation-validator-core.d.ts +6 -6
- package/dist/operations/operation-validator-core.js +41 -12
- package/dist/operations/operation-validator.d.ts +24 -9
- package/dist/operations/operation-validator.js +26 -10
- package/dist/operations/projection-scope.resolver.d.ts +7 -7
- package/dist/operations/structural-limits.walker.d.ts +19 -0
- package/dist/operations/structural-limits.walker.js +489 -0
- package/dist/operations/validation-code.d.ts +11 -6
- package/dist/operations/validation-code.js +4 -0
- package/dist/operations/write-scope.resolver.d.ts +6 -6
- package/dist/protocol/absent-route.responder.d.ts +13 -11
- package/dist/protocol/absent-route.responder.js +42 -16
- package/dist/protocol/contract-document.responder.d.ts +3 -3
- package/dist/protocol/contract-document.responder.js +2 -2
- package/dist/protocol/http-request.decoder.d.ts +23 -13
- package/dist/protocol/http-request.decoder.js +19 -11
- package/dist/protocol/http-request.reader.d.ts +3 -3
- package/dist/protocol/http-response.encoder.d.ts +4 -4
- package/dist/protocol/http-response.encoder.js +94 -55
- package/dist/protocol/http-status.table.d.ts +2 -2
- package/dist/protocol/operation-response.guard.d.ts +2 -2
- package/dist/protocol/protocol-surface.d.ts +4 -4
- package/dist/protocol/protocol.binder.d.ts +52 -27
- package/dist/protocol/protocol.binder.js +14 -9
- package/dist/protocol/protocol.handler.d.ts +28 -15
- package/dist/protocol/protocol.handler.js +55 -10
- 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 +67 -0
- package/dist/runtime/computed-read-runtime.d.ts +7 -6
- package/dist/runtime/computed-read-runtime.js +61 -55
- package/dist/runtime/computed-value.error.d.ts +13 -0
- package/dist/runtime/computed-value.error.js +9 -0
- package/dist/runtime/context-executor.runtime.d.ts +59 -0
- package/dist/runtime/context-executor.runtime.js +275 -0
- package/dist/runtime/create-requirement.d.ts +22 -57
- package/dist/runtime/create-requirement.js +140 -37
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +1 -1
- package/dist/runtime/diagnostics-reporter.d.ts +64 -22
- package/dist/runtime/diagnostics-reporter.js +35 -4
- package/dist/runtime/error-boundary.d.ts +73 -8
- package/dist/runtime/error-boundary.js +104 -48
- package/dist/runtime/execution-options.d.ts +10 -2
- package/dist/runtime/field-check.evaluator.d.ts +29 -0
- package/dist/runtime/field-check.evaluator.js +521 -0
- package/dist/runtime/field-compute.d.ts +64 -0
- package/dist/runtime/field-compute.js +1 -0
- package/dist/runtime/field-stage.plan.d.ts +15 -0
- package/dist/runtime/field-stage.plan.js +27 -0
- package/dist/runtime/field-stage.runtime.d.ts +35 -0
- package/dist/runtime/field-stage.runtime.js +143 -0
- package/dist/runtime/field-validator.runtime.d.ts +11 -0
- package/dist/runtime/field-validator.runtime.js +87 -0
- package/dist/runtime/framework-error-class.table.d.ts +33 -0
- package/dist/runtime/framework-error-class.table.js +25 -0
- package/dist/runtime/framework-error.d.ts +79 -7
- package/dist/runtime/framework-error.factory.d.ts +4 -0
- package/dist/runtime/framework-error.factory.js +15 -0
- package/dist/runtime/framework-error.js +23 -2
- package/dist/runtime/interceptor-next.error.d.ts +5 -0
- package/dist/runtime/interceptor-next.error.js +6 -0
- package/dist/runtime/json-value.d.ts +3 -3
- package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
- package/dist/runtime/level-lifecycle.runtime.js +42 -0
- package/dist/runtime/misuse-reporter.d.ts +32 -0
- package/dist/runtime/misuse-reporter.js +19 -0
- package/dist/runtime/mutation-planner.d.ts +19 -10
- package/dist/runtime/mutation-planner.js +62 -36
- package/dist/runtime/nested-write.walker.d.ts +32 -0
- package/dist/runtime/nested-write.walker.js +73 -0
- package/dist/runtime/operation-request.d.ts +35 -15
- package/dist/runtime/operation-response.d.ts +17 -17
- package/dist/runtime/operation-runtime.d.ts +125 -30
- package/dist/runtime/operation-runtime.js +423 -83
- package/dist/runtime/outcome-mapper.d.ts +16 -5
- package/dist/runtime/outcome-mapper.js +35 -8
- package/dist/runtime/output-validator.d.ts +16 -6
- package/dist/runtime/output-validator.js +12 -1
- package/dist/runtime/owned-execution.registry.d.ts +13 -0
- package/dist/runtime/owned-execution.registry.js +7 -0
- package/dist/runtime/pipe-output.validator.d.ts +33 -18
- package/dist/runtime/pipe-output.validator.js +12 -12
- package/dist/runtime/pipeline.d.ts +202 -28
- package/dist/runtime/readonly-value.d.ts +32 -0
- package/dist/runtime/readonly-value.js +141 -0
- package/dist/runtime/referential-action.d.ts +7 -0
- package/dist/runtime/request-envelope.validator.d.ts +5 -0
- package/dist/runtime/request-envelope.validator.js +123 -0
- package/dist/runtime/request-id.d.ts +1 -1
- package/dist/runtime/request-state.d.ts +14 -0
- package/dist/runtime/request-state.js +1 -0
- package/dist/runtime/request-state.resolver.d.ts +12 -0
- package/dist/runtime/request-state.resolver.js +20 -0
- package/dist/runtime/scalar-runtime.d.ts +56 -13
- package/dist/runtime/scalar-runtime.js +167 -86
- package/dist/transactions/deferred-operation.d.ts +35 -20
- package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
- package/dist/transactions/operation-arguments-encoder.js +2 -2
- package/dist/transactions/sql-statement.d.ts +77 -0
- package/dist/transactions/sql-statement.js +47 -0
- package/dist/transactions/sql-statement.validator.d.ts +44 -0
- package/dist/transactions/sql-statement.validator.js +134 -0
- package/dist/transactions/transaction-envelope.d.ts +4 -4
- package/dist/transactions/transaction-integrity-error.d.ts +6 -6
- package/dist/transactions/transaction-integrity-error.js +5 -2
- package/dist/transactions/transaction-plan-assembler.d.ts +23 -9
- package/dist/transactions/transaction-plan-assembler.js +195 -82
- package/dist/transactions/transaction-plan-validator.d.ts +53 -21
- package/dist/transactions/transaction-plan-validator.js +246 -163
- 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 +19 -2
- package/dist/transactions/transaction-reference-resolver.js +27 -10
- package/dist/transactions/transaction-reference-validator.d.ts +34 -4
- package/dist/transactions/transaction-reference-validator.js +82 -43
- package/dist/transactions/transaction-reference.d.ts +30 -58
- package/dist/transactions/transaction-request.d.ts +20 -7
- package/dist/transactions/transaction-response.d.ts +12 -7
- package/dist/transactions/transaction-response.js +24 -1
- package/dist/transactions/transaction-runtime.d.ts +11 -8
- package/dist/transactions/transaction-runtime.js +167 -50
- package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
- package/dist/transactions/transaction-settlement.runtime.js +120 -0
- package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
- package/dist/transactions/transaction-slot-path-error.js +2 -2
- package/dist/transactions/transaction-slot-writer.js +2 -2
- package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
- package/dist/transactions/transaction-stage.runtime.js +123 -0
- package/package.json +18 -3
- package/src/runtime/field-check.evaluator.ts +720 -0
- package/dist/config/define-fields.d.ts +0 -35
- package/dist/config/define-fields.js +0 -3
- package/dist/config/framework-behaviors-config.d.ts +0 -30
- package/dist/config/framework-fields-config.d.ts +0 -36
- package/dist/config/framework-restrictions-config.d.ts +0 -142
- 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
- package/dist/runtime/field-behavior.d.ts +0 -48
- /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
- /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
- /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
- /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
export function isPlainRecord(value) {
|
|
2
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
const prototype = Object.getPrototypeOf(value);
|
|
6
|
+
return prototype === Object.prototype || prototype === null;
|
|
7
|
+
}
|
|
8
|
+
const WRITE_KEYS = {
|
|
9
|
+
create: ["data"],
|
|
10
|
+
update: ["data"],
|
|
11
|
+
upsert: ["create", "update"],
|
|
12
|
+
};
|
|
13
|
+
function writeRecordsOf(family, args) {
|
|
14
|
+
const keys = WRITE_KEYS[family];
|
|
15
|
+
if (keys === undefined || !isPlainRecord(args)) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
const records = [];
|
|
19
|
+
for (const key of keys) {
|
|
20
|
+
if (!Object.hasOwn(args, key)) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const value = args[key];
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
for (const [index, entry] of value.entries()) {
|
|
26
|
+
if (isPlainRecord(entry)) {
|
|
27
|
+
records.push({ path: [key, index], record: entry });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else if (isPlainRecord(value)) {
|
|
32
|
+
records.push({ path: [key], record: value });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return records;
|
|
36
|
+
}
|
|
37
|
+
function sends(record, field) {
|
|
38
|
+
return Object.hasOwn(record, field) && record[field] !== undefined;
|
|
39
|
+
}
|
|
40
|
+
function isDirective(value) {
|
|
41
|
+
return (isPlainRecord(value) &&
|
|
42
|
+
Object.keys(value).some((key) => key.startsWith("$")));
|
|
43
|
+
}
|
|
44
|
+
export function stagedFieldsOf(contract, resource, stages, kind) {
|
|
45
|
+
const fields = Object.hasOwn(contract.resources, resource)
|
|
46
|
+
? contract.resources[resource]?.fields
|
|
47
|
+
: undefined;
|
|
48
|
+
if (fields === undefined) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
return Object.keys(fields).filter((field) => Object.hasOwn(stages, field) && (stages[field]?.[kind].length ?? 0) > 0);
|
|
52
|
+
}
|
|
53
|
+
export function valueAt(args, path) {
|
|
54
|
+
let current = args;
|
|
55
|
+
for (const key of path) {
|
|
56
|
+
if (Array.isArray(current) && typeof key === "number") {
|
|
57
|
+
current = current[key];
|
|
58
|
+
}
|
|
59
|
+
else if (isPlainRecord(current) && Object.hasOwn(current, key)) {
|
|
60
|
+
current = current[key];
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return current;
|
|
67
|
+
}
|
|
68
|
+
export function replaceAt(args, path, record) {
|
|
69
|
+
const [head, ...rest] = path;
|
|
70
|
+
if (head === undefined) {
|
|
71
|
+
return record;
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(args)) {
|
|
74
|
+
const copy = [...args];
|
|
75
|
+
copy[head] = replaceAt(copy[head], rest, record);
|
|
76
|
+
return copy;
|
|
77
|
+
}
|
|
78
|
+
const container = args;
|
|
79
|
+
return {
|
|
80
|
+
...container,
|
|
81
|
+
[head]: replaceAt(container[head], rest, record),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export function writeRecordPathsOf(family, args) {
|
|
85
|
+
return writeRecordsOf(family, args).map((entry) => entry.path);
|
|
86
|
+
}
|
|
87
|
+
export async function pipeSentFields(stages, fields, records, args, select) {
|
|
88
|
+
if (fields.length === 0) {
|
|
89
|
+
return { args, changed: false };
|
|
90
|
+
}
|
|
91
|
+
let current = args;
|
|
92
|
+
let changed = false;
|
|
93
|
+
for (const path of records) {
|
|
94
|
+
const record = valueAt(current, path);
|
|
95
|
+
if (!isPlainRecord(record)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
let piped;
|
|
99
|
+
for (const field of fields) {
|
|
100
|
+
if (!sends(record, field) ||
|
|
101
|
+
isDirective(record[field]) ||
|
|
102
|
+
!select([...path, field])) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
let value = record[field];
|
|
106
|
+
for (const pipe of stages[field]?.pipes ?? []) {
|
|
107
|
+
const next = await pipe(value);
|
|
108
|
+
if (next !== undefined) {
|
|
109
|
+
value = next;
|
|
110
|
+
changed = true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (value !== record[field]) {
|
|
114
|
+
piped ??= { ...record };
|
|
115
|
+
piped[field] = value;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (piped !== undefined) {
|
|
119
|
+
current = replaceAt(current, path, piped);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { args: current, changed };
|
|
123
|
+
}
|
|
124
|
+
export async function guardSentFields(stages, fields, records, args, context) {
|
|
125
|
+
if (fields.length === 0) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
const sent = records.map((path) => valueAt(args, path)).filter(isPlainRecord);
|
|
129
|
+
for (const field of fields) {
|
|
130
|
+
for (const record of sent) {
|
|
131
|
+
if (!sends(record, field)) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
for (const guard of stages[field]?.guards ?? []) {
|
|
135
|
+
const told = context();
|
|
136
|
+
if ((await guard(record[field], told)) === false) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AvContract } from "../contracts/contract.js";
|
|
2
|
+
import type { AvValidationIssue } from "../operations/validation-code.js";
|
|
3
|
+
import { type Path } from "./field-stage.runtime.js";
|
|
4
|
+
import type { OperationExecutionRequest } from "./operation-request.js";
|
|
5
|
+
import type { ReferentialActionTable } from "./referential-action.js";
|
|
6
|
+
/**
|
|
7
|
+
* Every way the sent fields `select` admits break their validators: the
|
|
8
|
+
* operation's own records, then each nested write target's. Paths are the
|
|
9
|
+
* request's: `argumentsPath` and then the value's place in the arguments.
|
|
10
|
+
*/
|
|
11
|
+
export declare function fieldValidatorIssues(contract: AvContract, references: ReferentialActionTable, request: OperationExecutionRequest, args: unknown, argumentsPath: Path, select: (path: Path) => boolean): readonly AvValidationIssue[];
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { validatedFieldsOf, } from "../compiler/field-validator.compiler.js";
|
|
2
|
+
import { entryCheckIssues, fieldCheckIssues } from "./field-check.evaluator.js";
|
|
3
|
+
import { isPlainRecord, valueAt, writeRecordPathsOf, } from "./field-stage.runtime.js";
|
|
4
|
+
import { nestedWriteTargetsOf } from "./nested-write.walker.js";
|
|
5
|
+
import { jsonShapeIssues } from "./scalar-runtime.js";
|
|
6
|
+
function isDirective(value) {
|
|
7
|
+
return (isPlainRecord(value) &&
|
|
8
|
+
Object.keys(value).some((key) => key.startsWith("$")));
|
|
9
|
+
}
|
|
10
|
+
function rulesOf(contract, compiled, resource, name) {
|
|
11
|
+
const fields = Object.hasOwn(contract.resources, resource)
|
|
12
|
+
? contract.resources[resource]?.fields
|
|
13
|
+
: undefined;
|
|
14
|
+
const field = fields !== undefined && Object.hasOwn(fields, name)
|
|
15
|
+
? fields[name]
|
|
16
|
+
: undefined;
|
|
17
|
+
if (field?.kind === "scalar" && field.json !== undefined) {
|
|
18
|
+
return { kind: "shape", field };
|
|
19
|
+
}
|
|
20
|
+
const checks = Object.hasOwn(compiled, name) ? compiled[name] : undefined;
|
|
21
|
+
return checks === undefined
|
|
22
|
+
? undefined
|
|
23
|
+
: { kind: "checks", compiled: checks };
|
|
24
|
+
}
|
|
25
|
+
function valueIssues(rules, value, at) {
|
|
26
|
+
if (rules.kind === "shape") {
|
|
27
|
+
return jsonShapeIssues(rules.field, value, at);
|
|
28
|
+
}
|
|
29
|
+
const { compiled } = rules;
|
|
30
|
+
return fieldCheckIssues(compiled.checks, value, compiled.subject ?? "string", at, compiled.list);
|
|
31
|
+
}
|
|
32
|
+
function entryIssues(rules, entry, at) {
|
|
33
|
+
if (rules.kind === "shape") {
|
|
34
|
+
return jsonShapeIssues(rules.field, entry, at, "entry");
|
|
35
|
+
}
|
|
36
|
+
const { compiled } = rules;
|
|
37
|
+
return entryCheckIssues(compiled.checks, entry, compiled.subject ?? "string", at);
|
|
38
|
+
}
|
|
39
|
+
function recordIssues(contract, resource, records, args, argumentsPath, select) {
|
|
40
|
+
const compiled = validatedFieldsOf(contract, resource);
|
|
41
|
+
const issues = [];
|
|
42
|
+
for (const path of records) {
|
|
43
|
+
const record = valueAt(args, path);
|
|
44
|
+
if (!isPlainRecord(record)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
for (const name of Object.keys(record)) {
|
|
48
|
+
const rules = rulesOf(contract, compiled, resource, name);
|
|
49
|
+
if (rules === undefined) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const fieldPath = [...path, name];
|
|
53
|
+
const value = record[name];
|
|
54
|
+
const at = [...argumentsPath, ...fieldPath];
|
|
55
|
+
if (!isDirective(value)) {
|
|
56
|
+
if (select(fieldPath)) {
|
|
57
|
+
issues.push(...valueIssues(rules, value, at));
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const list = rules.kind === "shape" ? rules.field.list : rules.compiled.list;
|
|
62
|
+
if (!list || !Object.hasOwn(value, "$push")) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const operand = value.$push;
|
|
66
|
+
const pushed = Array.isArray(operand)
|
|
67
|
+
? operand.map((entry, index) => ({
|
|
68
|
+
entry,
|
|
69
|
+
path: [...fieldPath, "$push", index],
|
|
70
|
+
}))
|
|
71
|
+
: [{ entry: operand, path: [...fieldPath, "$push"] }];
|
|
72
|
+
for (const { entry, path: entryPath } of pushed) {
|
|
73
|
+
if (!isDirective(entry) && (select(fieldPath) || select(entryPath))) {
|
|
74
|
+
issues.push(...entryIssues(rules, entry, [...argumentsPath, ...entryPath]));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return issues;
|
|
80
|
+
}
|
|
81
|
+
export function fieldValidatorIssues(contract, references, request, args, argumentsPath, select) {
|
|
82
|
+
const issues = recordIssues(contract, request.resource, writeRecordPathsOf(request.family, args), args, argumentsPath, select);
|
|
83
|
+
for (const target of nestedWriteTargetsOf(contract, references, request, args)) {
|
|
84
|
+
issues.push(...recordIssues(contract, target.resource, target.records.map((site) => site.path), args, argumentsPath, select));
|
|
85
|
+
}
|
|
86
|
+
return issues;
|
|
87
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AvOperationErrorCode } from "./operation-response.js";
|
|
2
|
+
/** The `AvError` subclass every error code is thrown as, by class name. */
|
|
3
|
+
export declare const FRAMEWORK_ERROR_CLASS_OF: {
|
|
4
|
+
readonly A2000: "AvProtocolError";
|
|
5
|
+
readonly A2001: "AvProtocolError";
|
|
6
|
+
readonly A2002: "AvProtocolError";
|
|
7
|
+
readonly A2003: "AvNotFoundError";
|
|
8
|
+
readonly A2004: "AvValidationError";
|
|
9
|
+
readonly A2005: "AvContractMismatchError";
|
|
10
|
+
readonly A2006: "AvProtocolError";
|
|
11
|
+
readonly A2007: "AvValidationError";
|
|
12
|
+
readonly A2008: "AvConflictError";
|
|
13
|
+
readonly A2009: "AvValidationError";
|
|
14
|
+
readonly A2010: "AvProtocolError";
|
|
15
|
+
readonly A2011: "AvProtocolError";
|
|
16
|
+
readonly A2012: "AvProtocolError";
|
|
17
|
+
readonly A2013: "AvConflictError";
|
|
18
|
+
readonly A2014: "AvConflictError";
|
|
19
|
+
readonly A3000: "AvInternalError";
|
|
20
|
+
readonly A3001: "AvInternalError";
|
|
21
|
+
readonly A3002: "AvInternalError";
|
|
22
|
+
readonly A3003: "AvInternalError";
|
|
23
|
+
readonly A3004: "AvInternalError";
|
|
24
|
+
readonly A4000: "AvAuthError";
|
|
25
|
+
readonly A4001: "AvAuthError";
|
|
26
|
+
readonly A4002: "AvAuthError";
|
|
27
|
+
};
|
|
28
|
+
/** The name of one `AvError` subclass. */
|
|
29
|
+
export type AvErrorClassName = (typeof FRAMEWORK_ERROR_CLASS_OF)[AvOperationErrorCode];
|
|
30
|
+
/** The error codes `AvError` subclass `N` is thrown for. */
|
|
31
|
+
export type AvErrorCodeOf<N extends AvErrorClassName> = {
|
|
32
|
+
readonly [C in AvOperationErrorCode]: (typeof FRAMEWORK_ERROR_CLASS_OF)[C] extends N ? C : never;
|
|
33
|
+
}[AvOperationErrorCode];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const FRAMEWORK_ERROR_CLASS_OF = {
|
|
2
|
+
A2000: "AvProtocolError",
|
|
3
|
+
A2001: "AvProtocolError",
|
|
4
|
+
A2002: "AvProtocolError",
|
|
5
|
+
A2003: "AvNotFoundError",
|
|
6
|
+
A2004: "AvValidationError",
|
|
7
|
+
A2005: "AvContractMismatchError",
|
|
8
|
+
A2006: "AvProtocolError",
|
|
9
|
+
A2007: "AvValidationError",
|
|
10
|
+
A2008: "AvConflictError",
|
|
11
|
+
A2009: "AvValidationError",
|
|
12
|
+
A2010: "AvProtocolError",
|
|
13
|
+
A2011: "AvProtocolError",
|
|
14
|
+
A2012: "AvProtocolError",
|
|
15
|
+
A2013: "AvConflictError",
|
|
16
|
+
A2014: "AvConflictError",
|
|
17
|
+
A3000: "AvInternalError",
|
|
18
|
+
A3001: "AvInternalError",
|
|
19
|
+
A3002: "AvInternalError",
|
|
20
|
+
A3003: "AvInternalError",
|
|
21
|
+
A3004: "AvInternalError",
|
|
22
|
+
A4000: "AvAuthError",
|
|
23
|
+
A4001: "AvAuthError",
|
|
24
|
+
A4002: "AvAuthError",
|
|
25
|
+
};
|
|
@@ -1,10 +1,82 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvErrorCodeOf } from "./framework-error-class.table.js";
|
|
2
|
+
import type { AvCause, AvOperationErrorCode } from "./operation-response.js";
|
|
2
3
|
/**
|
|
3
|
-
* Throw from a pipeline,
|
|
4
|
-
* framework error code and cause.
|
|
4
|
+
* Throw from a pipeline, compute stage or Adapter to answer the request with one
|
|
5
|
+
* framework error code and cause. Every in-process call that fails
|
|
6
|
+
* (`framework.application`, `framework.client`, `framework.transaction`, a ctx
|
|
7
|
+
* call) throws one too, as the subclass of its code's class:
|
|
8
|
+
* {@link AvValidationError}, {@link AvNotFoundError}, {@link AvConflictError},
|
|
9
|
+
* {@link AvAuthError}, {@link AvInternalError}, {@link AvProtocolError} or
|
|
10
|
+
* {@link AvContractMismatchError}. Branch on the class or on `code`, never on the
|
|
11
|
+
* message.
|
|
12
|
+
*
|
|
13
|
+
* For any code outside `A3xxx`, `cause.message` is sent to the caller exactly as
|
|
14
|
+
* written: put nothing in it you would not show the caller (no database text,
|
|
15
|
+
* SQL, stack, path or secret). So is each issue's `message`, when your own code
|
|
16
|
+
* (a pipe, guard, hook, interceptor, filter or compute stage) throws it and it is not
|
|
17
|
+
* blank and at most 1,000 characters; otherwise, and for an issue an Adapter
|
|
18
|
+
* throws, the caller receives the framework's own text for the issue's code. An
|
|
19
|
+
* issue's `code` must be a validation code (`V1000` and up) and its `path` must
|
|
20
|
+
* name a member of the request's own arguments, or the issue, or the path, is
|
|
21
|
+
* not sent. For an `A3xxx` code the caller receives the framework's generic
|
|
22
|
+
* message instead, and nothing of the cause; the error itself reaches the root
|
|
23
|
+
* `diagnostics` sink once, marked `deliberate: true`.
|
|
5
24
|
*/
|
|
6
|
-
export declare class
|
|
7
|
-
readonly
|
|
8
|
-
|
|
9
|
-
|
|
25
|
+
export declare class AvError<C extends AvOperationErrorCode = AvOperationErrorCode> extends Error {
|
|
26
|
+
readonly name: string;
|
|
27
|
+
/** The outcome code. Branch on this, never on the message. */
|
|
28
|
+
readonly code: C;
|
|
29
|
+
/** The cause: its message, and its issues or failing step when there are any. */
|
|
30
|
+
readonly cause: AvCause;
|
|
31
|
+
constructor(code: C, cause: AvCause | string);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The request broke a Contract rule: a field, type, capability, projection,
|
|
35
|
+
* reference or limit. The cause's issues say which, by code and path. Thrown for
|
|
36
|
+
* A2004, A2007 and A2009.
|
|
37
|
+
*/
|
|
38
|
+
export declare class AvValidationError extends AvError<AvErrorCodeOf<"AvValidationError">> {
|
|
39
|
+
readonly name: string;
|
|
40
|
+
}
|
|
41
|
+
/** A strict unique target does not exist. Thrown for A2003. */
|
|
42
|
+
export declare class AvNotFoundError extends AvError<AvErrorCodeOf<"AvNotFoundError">> {
|
|
43
|
+
readonly name: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The operation conflicted with stored state, and nothing was written. A2008
|
|
47
|
+
* fails the same way if sent again; A2013 and A2014 may succeed if sent again,
|
|
48
|
+
* and no retry is automatic. Thrown for A2008, A2013 and A2014.
|
|
49
|
+
*/
|
|
50
|
+
export declare class AvConflictError extends AvError<AvErrorCodeOf<"AvConflictError">> {
|
|
51
|
+
readonly name: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The caller is not authenticated, or is not permitted to run the operation.
|
|
55
|
+
* Thrown for A4000, A4001 and A4002.
|
|
56
|
+
*/
|
|
57
|
+
export declare class AvAuthError extends AvError<AvErrorCodeOf<"AvAuthError">> {
|
|
58
|
+
readonly name: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The server failed. Nothing in its cause is actionable; the diagnostics are in
|
|
62
|
+
* the `diagnostics` sink. Thrown for A3000 to A3004.
|
|
63
|
+
*/
|
|
64
|
+
export declare class AvInternalError extends AvError<AvErrorCodeOf<"AvInternalError">> {
|
|
65
|
+
readonly name: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The request was not one the server could route or interpret: an unknown
|
|
69
|
+
* Resource or operation, a protocol version it does not speak, or a body, media
|
|
70
|
+
* type, size or method it refuses. Thrown for A2000, A2001, A2002, A2006, A2010,
|
|
71
|
+
* A2011 and A2012.
|
|
72
|
+
*/
|
|
73
|
+
export declare class AvProtocolError extends AvError<AvErrorCodeOf<"AvProtocolError">> {
|
|
74
|
+
readonly name: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The caller's ClientContract hash is not the one the server serves (a stale
|
|
78
|
+
* generated client). Thrown for A2005.
|
|
79
|
+
*/
|
|
80
|
+
export declare class AvContractMismatchError extends AvError<AvErrorCodeOf<"AvContractMismatchError">> {
|
|
81
|
+
readonly name: string;
|
|
10
82
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type AvError } from "./framework-error.js";
|
|
2
|
+
import type { AvOperationFailureResponse } from "./operation-response.js";
|
|
3
|
+
/** The `AvError` subclass a failure response throws in process, with its code and cause. */
|
|
4
|
+
export declare function thrownFailure(response: AvOperationFailureResponse): AvError;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AvAuthError, AvConflictError, AvContractMismatchError, AvInternalError, AvNotFoundError, AvProtocolError, AvValidationError, } from "./framework-error.js";
|
|
2
|
+
import { FRAMEWORK_ERROR_CLASS_OF, } from "./framework-error-class.table.js";
|
|
3
|
+
const ERROR_CLASSES = {
|
|
4
|
+
AvAuthError,
|
|
5
|
+
AvConflictError,
|
|
6
|
+
AvContractMismatchError,
|
|
7
|
+
AvInternalError,
|
|
8
|
+
AvNotFoundError,
|
|
9
|
+
AvProtocolError,
|
|
10
|
+
AvValidationError,
|
|
11
|
+
};
|
|
12
|
+
export function thrownFailure(response) {
|
|
13
|
+
const ErrorClass = ERROR_CLASSES[FRAMEWORK_ERROR_CLASS_OF[response.code]];
|
|
14
|
+
return new ErrorClass(response.code, response.cause);
|
|
15
|
+
}
|
|
@@ -1,11 +1,32 @@
|
|
|
1
|
-
export class
|
|
1
|
+
export class AvError extends Error {
|
|
2
|
+
name = "AvError";
|
|
2
3
|
code;
|
|
3
4
|
cause;
|
|
4
5
|
constructor(code, cause) {
|
|
5
6
|
const normalizedCause = typeof cause === "string" ? { message: cause } : cause;
|
|
6
7
|
super(normalizedCause.message);
|
|
7
|
-
this.name = "FrameworkError";
|
|
8
8
|
this.code = code;
|
|
9
9
|
this.cause = normalizedCause;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
export class AvValidationError extends AvError {
|
|
13
|
+
name = "AvValidationError";
|
|
14
|
+
}
|
|
15
|
+
export class AvNotFoundError extends AvError {
|
|
16
|
+
name = "AvNotFoundError";
|
|
17
|
+
}
|
|
18
|
+
export class AvConflictError extends AvError {
|
|
19
|
+
name = "AvConflictError";
|
|
20
|
+
}
|
|
21
|
+
export class AvAuthError extends AvError {
|
|
22
|
+
name = "AvAuthError";
|
|
23
|
+
}
|
|
24
|
+
export class AvInternalError extends AvError {
|
|
25
|
+
name = "AvInternalError";
|
|
26
|
+
}
|
|
27
|
+
export class AvProtocolError extends AvError {
|
|
28
|
+
name = "AvProtocolError";
|
|
29
|
+
}
|
|
30
|
+
export class AvContractMismatchError extends AvError {
|
|
31
|
+
name = "AvContractMismatchError";
|
|
32
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A JSON value: the form of the `json` scalar, in arguments and results.
|
|
3
3
|
*
|
|
4
|
-
* A `Date`, a `bigint`, a `Uint8Array`, a {@link
|
|
4
|
+
* A `Date`, a `bigint`, a `Uint8Array`, a {@link AvDecimal}, any other class
|
|
5
5
|
* instance and an `undefined` property are refused (`V1001`), as are a
|
|
6
6
|
* non-finite `number` (`V1004`) and a cycle (`V1001`).
|
|
7
7
|
*
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
* A `json` field's value is this without its top-level `null`: a field-level
|
|
11
11
|
* `null` is the field's absence, which only `nullable: true` admits.
|
|
12
12
|
*/
|
|
13
|
-
export type
|
|
14
|
-
readonly [key: string]:
|
|
13
|
+
export type AvJsonValue = string | number | boolean | null | readonly AvJsonValue[] | {
|
|
14
|
+
readonly [key: string]: AvJsonValue;
|
|
15
15
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** One level's lifecycle registrations, as an operation or a transaction level holds them. */
|
|
2
|
+
export type LifecycleLevel<Context> = {
|
|
3
|
+
readonly hooks: {
|
|
4
|
+
readonly before: readonly ((context: Context) => unknown)[];
|
|
5
|
+
readonly after: readonly ((result: unknown, context: Context) => unknown)[];
|
|
6
|
+
};
|
|
7
|
+
readonly interceptors: readonly ((context: Context, next: () => Promise<unknown>) => Promise<unknown>)[];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* The record of a `next()` called more than once: the first refusal, kept so a
|
|
11
|
+
* caller can refuse the whole call even when the interceptor swallowed it.
|
|
12
|
+
*/
|
|
13
|
+
export type NextReuse<Refusal extends Error = Error> = {
|
|
14
|
+
refusal: Refusal | undefined;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Runs `levels`' lifecycle around `inner`: every interceptor, outer to inner,
|
|
18
|
+
* around every before hook, `inner`, and every after hook. `settle` turns what
|
|
19
|
+
* `inner` or an interceptor below resolved into what the stage above sees (the
|
|
20
|
+
* operation read-materializes it). An after hook is told a deep-frozen copy of
|
|
21
|
+
* the result; the result itself goes on to the interceptors, which may answer a
|
|
22
|
+
* different one. Every `next()` is single use: a second call rejects with
|
|
23
|
+
* `refuse()`'s error, recorded in `reuse`, and runs nothing.
|
|
24
|
+
*/
|
|
25
|
+
export declare function runLevelLifecycle<Context, Refusal extends Error>(levels: readonly LifecycleLevel<Context>[], context: () => Context, inner: () => Promise<unknown>, settle: (result: unknown) => Promise<unknown>, reuse: NextReuse<Refusal>, refuse: () => Refusal): Promise<unknown>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { frozenCopy } from "./readonly-value.js";
|
|
2
|
+
export async function runLevelLifecycle(levels, context, inner, settle, reuse, refuse) {
|
|
3
|
+
const once = (run) => {
|
|
4
|
+
let called = false;
|
|
5
|
+
return () => {
|
|
6
|
+
if (called) {
|
|
7
|
+
const refusal = refuse();
|
|
8
|
+
reuse.refusal ??= refusal;
|
|
9
|
+
return Promise.reject(refusal);
|
|
10
|
+
}
|
|
11
|
+
called = true;
|
|
12
|
+
return run();
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
const hooked = async () => {
|
|
16
|
+
for (const level of levels) {
|
|
17
|
+
for (const hook of level.hooks.before) {
|
|
18
|
+
await hook(context());
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const result = await settle(await inner());
|
|
22
|
+
const after = [...levels].reverse().flatMap((level) => level.hooks.after);
|
|
23
|
+
if (after.length > 0) {
|
|
24
|
+
const told = frozenCopy(result);
|
|
25
|
+
for (const hook of after) {
|
|
26
|
+
await hook(told, context());
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
};
|
|
31
|
+
const interceptors = levels.flatMap((level) => level.interceptors);
|
|
32
|
+
let next = hooked;
|
|
33
|
+
for (let index = interceptors.length - 1; index >= 0; index -= 1) {
|
|
34
|
+
const interceptor = interceptors[index];
|
|
35
|
+
if (interceptor === undefined) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const continuation = once(next);
|
|
39
|
+
next = () => interceptor(context(), async () => settle(await continuation()));
|
|
40
|
+
}
|
|
41
|
+
return next();
|
|
42
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { AvRequestOrigin } from "../config/config-layer.js";
|
|
2
|
+
import type { AvOperationFamily } 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`, `AvDecimal` 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 AvMisuseEvent = {
|
|
14
|
+
readonly kind: "unreadable-value" | "non-plain-value";
|
|
15
|
+
/** The issue path the caller received. */
|
|
16
|
+
readonly path: readonly (string | number)[];
|
|
17
|
+
readonly origin?: AvRequestOrigin;
|
|
18
|
+
readonly resource?: string;
|
|
19
|
+
readonly family?: AvOperationFamily;
|
|
20
|
+
readonly variant?: string;
|
|
21
|
+
readonly operation?: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Receives a {@link AvMisuseEvent} 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 AvMisuseSink = (event: AvMisuseEvent) => void | Promise<void>;
|
|
29
|
+
/** The channel as the runtime holds it: total, never awaited. */
|
|
30
|
+
export type MisuseReporter = (event: AvMisuseEvent) => void;
|
|
31
|
+
/** Binds the configured sink; without one, nothing is observed. */
|
|
32
|
+
export declare function createMisuseReporter(sink: AvMisuseSink | 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
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
1
|
+
import type { ResolvedComputeConfig } from "../config/field-compute.resolver.js";
|
|
2
|
+
import type { AvRelationMutationDirective } from "../contracts/capability.js";
|
|
3
|
+
import type { AvContract, AvContractCore } from "../contracts/contract.js";
|
|
4
|
+
import type { AvComputeContext } from "./field-compute.js";
|
|
5
|
+
import type { NestedWriteTarget } from "./nested-write.walker.js";
|
|
4
6
|
import type { OperationExecutionRequest } from "./operation-request.js";
|
|
5
|
-
import type { PipelineContext } from "./pipeline.js";
|
|
6
7
|
/**
|
|
7
8
|
* The two Contracts a write reads. The Core Contract decides what the Adapter is asked
|
|
8
9
|
* to store; the active Contract adds the fields its scope declared under
|
|
@@ -10,11 +11,11 @@ import type { PipelineContext } from "./pipeline.js";
|
|
|
10
11
|
* virtual: its behaviours run and its value is stripped before the Adapter call.
|
|
11
12
|
*/
|
|
12
13
|
export type WriteContracts = {
|
|
13
|
-
readonly core:
|
|
14
|
-
readonly active:
|
|
14
|
+
readonly core: AvContractCore;
|
|
15
|
+
readonly active: AvContract;
|
|
15
16
|
};
|
|
16
17
|
type WriteStage = "create" | "update";
|
|
17
|
-
type ContextFactory = (args: unknown) =>
|
|
18
|
+
type ContextFactory = (args: unknown) => AvComputeContext;
|
|
18
19
|
export type RelationRecordSite = {
|
|
19
20
|
readonly key: string | undefined;
|
|
20
21
|
readonly stage: WriteStage;
|
|
@@ -53,9 +54,17 @@ export declare const RELATION_MUTATION_RECORD_SITES: {
|
|
|
53
54
|
* membership, not an assertion: `Object.prototype` members resolve through the
|
|
54
55
|
* prototype chain and would otherwise reach a site loop with a non-iterable.
|
|
55
56
|
*/
|
|
56
|
-
export declare function isRelationMutationDirective(key: string): key is
|
|
57
|
-
/** Apply effective
|
|
58
|
-
export declare function
|
|
57
|
+
export declare function isRelationMutationDirective(key: string): key is AvRelationMutationDirective;
|
|
58
|
+
/** Apply the effective create/update compute stages to the post-pipe logical args. */
|
|
59
|
+
export declare function applyWriteCompute(contracts: WriteContracts, compute: ResolvedComputeConfig | undefined, request: OperationExecutionRequest, args: unknown, contextFactory: ContextFactory): Promise<unknown>;
|
|
60
|
+
/**
|
|
61
|
+
* Applies each nested target's write compute, in the targets' order, to its
|
|
62
|
+
* records: `compute.create` on a created row (an `$upsert`'s `create` branch
|
|
63
|
+
* too), `compute.update` on an updated one. Each target's stages are told
|
|
64
|
+
* `context(target, payload)`, the nested target's own context. The arguments
|
|
65
|
+
* are copied where a record changes, never written.
|
|
66
|
+
*/
|
|
67
|
+
export declare function applyNestedWriteCompute(contracts: WriteContracts, compute: ResolvedComputeConfig | undefined, args: unknown, targets: readonly NestedWriteTarget[], context: (target: NestedWriteTarget, payload: unknown) => AvComputeContext): Promise<unknown>;
|
|
59
68
|
/** Strip VIRTUAL scalar input only at the persistence boundary. */
|
|
60
69
|
export declare function stripVirtualMutationInput(contracts: WriteContracts, request: OperationExecutionRequest, args: unknown): Promise<unknown>;
|
|
61
70
|
export {};
|