@aventara/core 1.0.0-rc.1 → 1.0.0-rc.10
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 +943 -16
- package/ERRORS.md +142 -0
- package/README.md +274 -16
- package/dist/adapters/adapter-discovery-diagnostic.guidance.d.ts +6 -0
- package/dist/adapters/adapter-discovery-diagnostic.guidance.js +7 -0
- package/dist/adapters/adapter-field.d.ts +22 -0
- package/dist/adapters/adapter-identifier.d.ts +9 -0
- package/dist/adapters/adapter-identifier.js +6 -1
- package/dist/adapters/adapter-model.d.ts +9 -0
- package/dist/adapters/adapter-operation.d.ts +18 -3
- package/dist/adapters/adapter-resource.d.ts +5 -0
- package/dist/adapters/adapter.interface.d.ts +10 -0
- package/dist/avz/avz.d.ts +1 -1
- package/dist/avz/json-shape.registry.d.ts +2 -1
- package/dist/compiler/contract-compiler-diagnostic.guidance.d.ts +6 -0
- package/dist/compiler/contract-compiler-diagnostic.guidance.js +97 -0
- package/dist/compiler/contract.compiler.d.ts +6 -0
- package/dist/compiler/contract.compiler.js +227 -24
- package/dist/compiler/managed-elsewhere.projector.d.ts +52 -0
- package/dist/compiler/managed-elsewhere.projector.js +179 -0
- package/dist/compiler/managed-elsewhere.validator.d.ts +9 -0
- package/dist/compiler/managed-elsewhere.validator.js +74 -0
- package/dist/config/computed-fields.d.ts +62 -0
- package/dist/config/computed-fields.js +73 -0
- package/dist/config/config-diagnostic.guidance.d.ts +6 -0
- package/dist/config/config-diagnostic.guidance.js +32 -0
- package/dist/config/config.resolver.d.ts +24 -0
- package/dist/config/config.resolver.js +65 -0
- package/dist/config/config.validator.js +222 -8
- package/dist/config/define-resource.d.ts +1 -5
- package/dist/config/entrypoint.normalizer.d.ts +3 -2
- package/dist/config/exact-framework-config.d.ts +67 -22
- package/dist/config/field-compute.resolver.d.ts +2 -0
- package/dist/config/field-compute.resolver.js +8 -0
- package/dist/config/framework-config.d.ts +53 -15
- package/dist/config/framework-resources-config.d.ts +25 -14
- package/dist/config/pipeline-config.d.ts +4 -4
- package/dist/config/pipeline-tree.resolver.d.ts +2 -2
- package/dist/contracts/access-route.rules.d.ts +10 -0
- package/dist/contracts/access-route.rules.js +45 -0
- package/dist/contracts/capability.d.ts +26 -1
- package/dist/contracts/capability.js +20 -0
- package/dist/contracts/client-contract-structure.validator.d.ts +301 -89
- package/dist/contracts/client-contract-structure.validator.js +37 -2
- package/dist/contracts/contract-access.d.ts +29 -0
- package/dist/contracts/contract-access.js +1 -0
- package/dist/contracts/contract-canonicalizer.d.ts +9 -1
- package/dist/contracts/contract-canonicalizer.js +30 -4
- package/dist/contracts/contract-hash.d.ts +0 -1
- package/dist/contracts/contract-hash.js +1 -1
- package/dist/contracts/contract.d.ts +27 -1
- package/dist/contracts/field.d.ts +58 -1
- package/dist/contracts/json-schema.projector.d.ts +11 -0
- package/dist/contracts/json-schema.projector.js +78 -0
- package/dist/contracts/scalar-value-type.d.ts +3 -1
- package/dist/framework/access-facade.d.ts +116 -0
- package/dist/framework/access-facade.js +120 -0
- package/dist/framework/contract-view.d.ts +120 -16
- package/dist/framework/framework-construction.error.d.ts +14 -4
- package/dist/framework/framework-construction.error.js +18 -2
- package/dist/framework/framework.d.ts +26 -5
- package/dist/framework/framework.factory.d.ts +1 -1
- package/dist/framework/framework.factory.js +115 -23
- package/dist/framework/operation-facade.d.ts +80 -13
- package/dist/framework/operation-facade.js +2 -1
- package/dist/index.d.ts +17 -5
- package/dist/index.js +1 -0
- package/dist/internal.d.ts +7 -2
- package/dist/internal.js +6 -0
- package/dist/operations/database-refusal.d.ts +51 -0
- package/dist/operations/database-refusal.js +45 -0
- package/dist/operations/operation-arguments.d.ts +148 -60
- package/dist/operations/operation-call.d.ts +9 -5
- package/dist/operations/operation-result.d.ts +6 -4
- package/dist/operations/operation-validator-core.js +159 -36
- package/dist/operations/operation-validator.d.ts +1 -1
- package/dist/operations/structural-limits.walker.d.ts +1 -1
- package/dist/operations/validation-code.guidance.d.ts +6 -0
- package/dist/operations/validation-code.guidance.js +90 -0
- package/dist/operations/write-scope.resolver.d.ts +4 -0
- package/dist/operations/write-scope.resolver.js +2 -0
- package/dist/policy/access-provider.interface.d.ts +91 -0
- package/dist/policy/access-provider.interface.js +1 -0
- package/dist/policy/access-provider.registry.d.ts +17 -0
- package/dist/policy/access-provider.registry.js +15 -0
- package/dist/policy/access-question.d.ts +20 -0
- package/dist/policy/access-question.js +1 -0
- package/dist/policy/access-question.registry.d.ts +5 -0
- package/dist/policy/access-question.registry.js +19 -0
- package/dist/policy/access-question.runtime.d.ts +36 -0
- package/dist/policy/access-question.runtime.js +297 -0
- package/dist/policy/access-verb.table.d.ts +56 -0
- package/dist/policy/access-verb.table.js +21 -0
- package/dist/policy/access-verbs.d.ts +46 -0
- package/dist/policy/access-verbs.js +1 -0
- package/dist/policy/policy-admission.d.ts +20 -0
- package/dist/policy/policy-admission.js +409 -0
- package/dist/policy/policy-after-update.runtime.d.ts +79 -0
- package/dist/policy/policy-after-update.runtime.js +208 -0
- package/dist/policy/policy-condition.d.ts +15 -0
- package/dist/policy/policy-condition.js +36 -0
- package/dist/policy/policy-engine.d.ts +205 -0
- package/dist/policy/policy-engine.js +1 -0
- package/dist/policy/policy-named-rows.d.ts +18 -0
- package/dist/policy/policy-named-rows.js +51 -0
- package/dist/policy/policy-nested-write.runtime.d.ts +44 -0
- package/dist/policy/policy-nested-write.runtime.js +545 -0
- package/dist/policy/policy-read.runtime.d.ts +81 -0
- package/dist/policy/policy-read.runtime.js +503 -0
- package/dist/policy/policy-reference-write.runtime.d.ts +51 -0
- package/dist/policy/policy-reference-write.runtime.js +345 -0
- package/dist/policy/policy-request.d.ts +41 -0
- package/dist/policy/policy-request.js +63 -0
- package/dist/policy/policy-session.d.ts +106 -0
- package/dist/policy/policy-session.js +295 -0
- package/dist/policy/policy-startup.validator.d.ts +10 -0
- package/dist/policy/policy-startup.validator.js +112 -0
- package/dist/policy/policy-write.runtime.d.ts +39 -0
- package/dist/policy/policy-write.runtime.js +532 -0
- package/dist/prebuilt/core-version.generated.d.ts +2 -0
- package/dist/prebuilt/core-version.generated.js +1 -0
- package/dist/prebuilt/prebuilt-changes.d.ts +29 -0
- package/dist/prebuilt/prebuilt-changes.js +65 -0
- package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
- package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
- package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
- package/dist/prebuilt/prebuilt-contracts.js +1 -0
- package/dist/prebuilt/prebuilt-contracts.loader.d.ts +19 -0
- package/dist/prebuilt/prebuilt-contracts.loader.js +77 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.js +229 -0
- package/dist/protocol/absent-route.responder.d.ts +9 -27
- package/dist/protocol/absent-route.responder.js +36 -3
- package/dist/protocol/contract-document.responder.d.ts +2 -1
- package/dist/protocol/http-request.decoder.d.ts +19 -3
- package/dist/protocol/http-request.decoder.js +24 -1
- package/dist/protocol/http-request.reader.d.ts +6 -3
- package/dist/protocol/http-request.reader.js +1 -0
- package/dist/protocol/http-response.encoder.js +2 -2
- package/dist/protocol/protocol-headers.d.ts +3 -3
- package/dist/protocol/protocol-surface.d.ts +6 -1
- package/dist/protocol/protocol-surface.js +7 -0
- package/dist/protocol/protocol.binder.d.ts +16 -1
- package/dist/protocol/protocol.binder.js +4 -1
- package/dist/protocol/protocol.handler.d.ts +12 -1
- package/dist/protocol/protocol.handler.js +86 -17
- package/dist/runtime/caller-data.copier.d.ts +18 -3
- package/dist/runtime/caller-data.copier.js +21 -8
- package/dist/runtime/caller-data.door.d.ts +13 -6
- package/dist/runtime/computed-read-runtime.d.ts +6 -1
- package/dist/runtime/computed-read-runtime.js +232 -45
- package/dist/runtime/computed-value.error.d.ts +3 -2
- package/dist/runtime/computed-value.error.js +3 -2
- package/dist/runtime/context-executor.runtime.d.ts +4 -3
- package/dist/runtime/context-executor.runtime.js +4 -2
- package/dist/runtime/core-class.brand.d.ts +10 -0
- package/dist/runtime/core-class.brand.js +21 -0
- package/dist/runtime/create-requirement.d.ts +31 -5
- package/dist/runtime/create-requirement.js +129 -21
- package/dist/runtime/decimal.d.ts +14 -0
- package/dist/runtime/decimal.js +21 -0
- package/dist/runtime/diagnostics-reporter.d.ts +44 -9
- package/dist/runtime/diagnostics-reporter.js +13 -1
- package/dist/runtime/error-boundary.d.ts +9 -2
- package/dist/runtime/error-boundary.js +12 -0
- package/dist/runtime/error-guidance.d.ts +12 -0
- package/dist/runtime/error-guidance.js +1 -0
- package/dist/runtime/error.catalog.d.ts +49 -0
- package/dist/runtime/error.catalog.js +49 -0
- package/dist/runtime/execution-options.d.ts +8 -0
- package/dist/runtime/field-check.evaluator.d.ts +0 -2
- package/dist/runtime/field-check.evaluator.js +1 -1
- package/dist/runtime/field-compute.d.ts +11 -10
- package/dist/runtime/field-stage.runtime.d.ts +3 -1
- package/dist/runtime/field-stage.runtime.js +8 -4
- package/dist/runtime/field-validator.runtime.d.ts +19 -1
- package/dist/runtime/field-validator.runtime.js +47 -8
- package/dist/runtime/framework-error.js +9 -0
- package/dist/runtime/framework-warning.emitter.d.ts +6 -0
- package/dist/runtime/framework-warning.emitter.js +8 -0
- package/dist/runtime/json-value.d.ts +10 -5
- package/dist/runtime/level-lifecycle.runtime.d.ts +4 -3
- package/dist/runtime/level-lifecycle.runtime.js +3 -5
- package/dist/runtime/mutation-planner.d.ts +1 -6
- package/dist/runtime/mutation-planner.js +3 -14
- package/dist/runtime/operation-code.guidance.d.ts +6 -0
- package/dist/runtime/operation-code.guidance.js +167 -0
- package/dist/runtime/operation-request.d.ts +16 -9
- package/dist/runtime/operation-runtime.d.ts +35 -17
- package/dist/runtime/operation-runtime.js +220 -49
- package/dist/runtime/outcome-mapper.d.ts +2 -1
- package/dist/runtime/output-validator.d.ts +1 -1
- package/dist/runtime/owned-execution.registry.d.ts +13 -3
- package/dist/runtime/pipe-output.validator.d.ts +10 -0
- package/dist/runtime/pipe-output.validator.js +4 -2
- package/dist/runtime/pipeline.d.ts +47 -22
- package/dist/runtime/readonly-value.d.ts +20 -5
- package/dist/runtime/readonly-value.js +74 -26
- package/dist/runtime/referential-action.d.ts +1 -1
- package/dist/runtime/scalar-runtime.d.ts +4 -11
- package/dist/runtime/scalar-runtime.js +91 -22
- package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
- package/dist/runtime/unsent-value.normalizer.js +128 -0
- package/dist/transactions/operation-arguments-encoder.js +8 -3
- package/dist/transactions/operation-fingerprint.js +2 -2
- package/dist/transactions/sql-statement.d.ts +10 -2
- package/dist/transactions/sql-statement.validator.d.ts +2 -2
- package/dist/transactions/sql-statement.validator.js +38 -5
- package/dist/transactions/transaction-envelope.d.ts +5 -4
- package/dist/transactions/transaction-integrity-error.js +2 -0
- package/dist/transactions/transaction-plan-assembler.d.ts +12 -5
- package/dist/transactions/transaction-plan-assembler.js +48 -10
- package/dist/transactions/transaction-plan-validator.d.ts +23 -7
- package/dist/transactions/transaction-plan-validator.js +27 -1
- package/dist/transactions/transaction-reference-resolver.d.ts +3 -2
- package/dist/transactions/transaction-reference-validator.d.ts +43 -3
- package/dist/transactions/transaction-reference-validator.js +83 -57
- package/dist/transactions/transaction-reference.d.ts +1 -1
- package/dist/transactions/transaction-reference.js +5 -0
- package/dist/transactions/transaction-request.d.ts +8 -8
- package/dist/transactions/transaction-runtime.d.ts +5 -2
- package/dist/transactions/transaction-runtime.js +79 -27
- package/dist/transactions/transaction-settlement.runtime.d.ts +4 -3
- package/dist/transactions/transaction-slot-path-error.js +2 -0
- package/dist/transactions/transaction-stage.runtime.d.ts +5 -3
- package/dist/transactions/transaction-stage.runtime.js +7 -4
- package/package.json +6 -5
- package/src/runtime/field-check.evaluator.ts +1 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
2
|
+
import { isPlainRecord, WRITE_KEYS } from "./field-stage.runtime.js";
|
|
3
|
+
import { isRelationMutationDirective, RELATION_MUTATION_RECORD_SITES, } from "./mutation-planner.js";
|
|
4
|
+
const REMOVE = Symbol("remove");
|
|
5
|
+
const DEPTH_CEILING = 256;
|
|
6
|
+
function rebuilt(record, changes) {
|
|
7
|
+
const copy = Object.getPrototypeOf(record) === null ? Object.create(null) : {};
|
|
8
|
+
for (const key of Object.keys(record)) {
|
|
9
|
+
const value = changes.has(key) ? changes.get(key) : record[key];
|
|
10
|
+
if (value === REMOVE) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(copy, key, {
|
|
14
|
+
value,
|
|
15
|
+
writable: true,
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return copy;
|
|
21
|
+
}
|
|
22
|
+
function eachEntry(value, strip) {
|
|
23
|
+
if (!Array.isArray(value)) {
|
|
24
|
+
return strip(value);
|
|
25
|
+
}
|
|
26
|
+
let copy;
|
|
27
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
28
|
+
if (!(index in value)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const entry = value[index];
|
|
32
|
+
const stripped = strip(entry);
|
|
33
|
+
if (stripped !== entry) {
|
|
34
|
+
copy ??= value.slice();
|
|
35
|
+
copy[index] = stripped;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return copy ?? value;
|
|
39
|
+
}
|
|
40
|
+
function stripRecord(walk, resourceName, record, depth) {
|
|
41
|
+
if (depth > walk.depth || !isPlainRecord(record)) {
|
|
42
|
+
return record;
|
|
43
|
+
}
|
|
44
|
+
const resource = lookupResource(walk.contract, resourceName);
|
|
45
|
+
let changes;
|
|
46
|
+
for (const key of Object.keys(record)) {
|
|
47
|
+
const value = record[key];
|
|
48
|
+
if (value === undefined) {
|
|
49
|
+
changes ??= new Map();
|
|
50
|
+
changes.set(key, REMOVE);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const field = lookupField(resource, key);
|
|
54
|
+
if (field?.kind !== "relation") {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const stripped = stripRelationWrite(walk, field.target, value, depth + 1);
|
|
58
|
+
if (stripped !== value) {
|
|
59
|
+
changes ??= new Map();
|
|
60
|
+
changes.set(key, stripped);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return changes === undefined ? record : rebuilt(record, changes);
|
|
64
|
+
}
|
|
65
|
+
function stripRelationWrite(walk, target, value, depth) {
|
|
66
|
+
if (!isPlainRecord(value)) {
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
let changes;
|
|
70
|
+
for (const directive of Object.keys(value)) {
|
|
71
|
+
if (!isRelationMutationDirective(directive)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const sites = RELATION_MUTATION_RECORD_SITES[directive];
|
|
75
|
+
if (sites.length === 0) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const operand = value[directive];
|
|
79
|
+
const next = eachEntry(operand, (entry) => {
|
|
80
|
+
let stripped = entry;
|
|
81
|
+
for (const site of sites) {
|
|
82
|
+
if (site.key === undefined) {
|
|
83
|
+
stripped = stripRecord(walk, target, stripped, depth);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (!isPlainRecord(stripped) || !Object.hasOwn(stripped, site.key)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const inner = stripped[site.key];
|
|
90
|
+
const record = stripRecord(walk, target, inner, depth);
|
|
91
|
+
if (record !== inner) {
|
|
92
|
+
stripped = rebuilt(stripped, new Map([[site.key, record]]));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return stripped;
|
|
96
|
+
});
|
|
97
|
+
if (next !== operand) {
|
|
98
|
+
changes ??= new Map();
|
|
99
|
+
changes.set(directive, next);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return changes === undefined ? value : rebuilt(value, changes);
|
|
103
|
+
}
|
|
104
|
+
export function withoutUnsentWriteValues(contract, request, args) {
|
|
105
|
+
const keys = Object.hasOwn(WRITE_KEYS, request.family)
|
|
106
|
+
? WRITE_KEYS[request.family]
|
|
107
|
+
: undefined;
|
|
108
|
+
if (keys === undefined || !isPlainRecord(args)) {
|
|
109
|
+
return args;
|
|
110
|
+
}
|
|
111
|
+
const walk = {
|
|
112
|
+
contract,
|
|
113
|
+
depth: Math.min(contract.limits.maxNestingDepth, DEPTH_CEILING),
|
|
114
|
+
};
|
|
115
|
+
let changes;
|
|
116
|
+
for (const key of keys) {
|
|
117
|
+
if (!Object.hasOwn(args, key)) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const value = args[key];
|
|
121
|
+
const next = eachEntry(value, (entry) => stripRecord(walk, request.resource, entry, 0));
|
|
122
|
+
if (next !== value) {
|
|
123
|
+
changes ??= new Map();
|
|
124
|
+
changes.set(key, next);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return changes === undefined ? args : rebuilt(args, changes);
|
|
128
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
2
2
|
import { encodeBase64 } from "../contracts/wire-scalar-format.js";
|
|
3
|
-
import {
|
|
3
|
+
import { isAvDecimal } from "../runtime/decimal.js";
|
|
4
4
|
import { isRelationMutationDirective, RELATION_MUTATION_RECORD_SITES, } from "../runtime/mutation-planner.js";
|
|
5
5
|
import { isTransactionReferenceCandidate } from "./transaction-reference.js";
|
|
6
6
|
function isPlainRecord(value) {
|
|
@@ -27,9 +27,11 @@ function encodeScalarAtom(field, value) {
|
|
|
27
27
|
case "bigint":
|
|
28
28
|
return typeof value === "bigint" ? value.toString(10) : value;
|
|
29
29
|
case "decimal":
|
|
30
|
-
return value
|
|
30
|
+
return isAvDecimal(value) ? value.toString() : value;
|
|
31
31
|
case "datetime":
|
|
32
|
-
return value instanceof Date
|
|
32
|
+
return value instanceof Date && !Number.isNaN(value.getTime())
|
|
33
|
+
? value.toISOString()
|
|
34
|
+
: value;
|
|
33
35
|
case "bytes":
|
|
34
36
|
return value instanceof Uint8Array ? encodeBase64(value) : value;
|
|
35
37
|
default:
|
|
@@ -282,6 +284,9 @@ function isUniqueOperation(operation) {
|
|
|
282
284
|
operation.family === "upsert"));
|
|
283
285
|
}
|
|
284
286
|
export function encodeOperationArgumentsForWire(contract, operation, args) {
|
|
287
|
+
if (!isPlainRecord(args)) {
|
|
288
|
+
return args;
|
|
289
|
+
}
|
|
285
290
|
const encoded = cloneRecord(args);
|
|
286
291
|
if (hasOwnWireProperty(args, "where")) {
|
|
287
292
|
encoded.where = isUniqueOperation(operation)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { canonicalizeWireJson } from "../contracts/contract-canonicalizer.js";
|
|
2
2
|
const OPERATION_FINGERPRINT_PATTERN = /^fp1:[A-Za-z0-9_-]{22}$/;
|
|
3
3
|
function encodeBase64Url(bytes) {
|
|
4
4
|
let binary = "";
|
|
@@ -14,7 +14,7 @@ export function isOperationFingerprint(value) {
|
|
|
14
14
|
return typeof value === "string" && OPERATION_FINGERPRINT_PATTERN.test(value);
|
|
15
15
|
}
|
|
16
16
|
export async function computeOperationFingerprint(node) {
|
|
17
|
-
const canonical =
|
|
17
|
+
const canonical = canonicalizeWireJson({
|
|
18
18
|
resource: node.resource,
|
|
19
19
|
family: node.family,
|
|
20
20
|
variant: node.variant,
|
|
@@ -10,7 +10,7 @@ import type { AvTransactionReference } from "./transaction-reference.js";
|
|
|
10
10
|
* `${value}::jsonb`), or a `$ref` to an earlier step's field, resolved when the
|
|
11
11
|
* earlier steps have run and then bound like any other value.
|
|
12
12
|
*/
|
|
13
|
-
export type
|
|
13
|
+
export type AvSqlStatementValue = AvAdapterSqlValue | AvTransactionReference;
|
|
14
14
|
/**
|
|
15
15
|
* `framework.appTx.$sql`: a developer SQL step, written only as a tagged template —
|
|
16
16
|
* ``framework.appTx.$sql`select pgmq.send('jobs', jsonb_build_object('id', ${ref}))` ``.
|
|
@@ -23,7 +23,7 @@ export type SqlStatementValue = AvAdapterSqlValue | AvTransactionReference;
|
|
|
23
23
|
* SQL step's rows. No guard, pipe, hook, interceptor or filter runs around it: it
|
|
24
24
|
* has no Resource or operation for one to wrap.
|
|
25
25
|
*/
|
|
26
|
-
export type
|
|
26
|
+
export type AvSqlStatementBuilder = <Row = unknown>(strings: TemplateStringsArray, ...values: readonly AvSqlStatementValue[]) => AvOperation<Row[], "application">;
|
|
27
27
|
/** One value of a planned SQL step, as the typed runner bound it. */
|
|
28
28
|
export type PlannedSqlValue = {
|
|
29
29
|
readonly kind: "value";
|
|
@@ -35,6 +35,14 @@ export type PlannedSqlValue = {
|
|
|
35
35
|
readonly operation: number;
|
|
36
36
|
readonly field: string;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* A whole value that is a `$ref` control object the caller wrote, mirrored at
|
|
40
|
+
* the builder: validated as an operation step's reference is.
|
|
41
|
+
*/
|
|
42
|
+
| {
|
|
43
|
+
readonly kind: "candidate";
|
|
44
|
+
readonly candidate: unknown;
|
|
45
|
+
}
|
|
38
46
|
/** A `$ref` placeholder found inside a value, at `path` within it: refused. */
|
|
39
47
|
| {
|
|
40
48
|
readonly kind: "misplaced-reference";
|
|
@@ -5,7 +5,7 @@ import type { AvOperationFailureResponse } from "../runtime/operation-response.j
|
|
|
5
5
|
import type { PlannedSqlStatement, ValidatedSqlStatement } from "./sql-statement.js";
|
|
6
6
|
import type { ValidatedTransactionStep } from "./transaction-plan-validator.js";
|
|
7
7
|
type Path = readonly (string | number)[];
|
|
8
|
-
|
|
8
|
+
type SqlStatementValidation = {
|
|
9
9
|
readonly ok: true;
|
|
10
10
|
readonly statement: ValidatedSqlStatement;
|
|
11
11
|
} | {
|
|
@@ -13,7 +13,7 @@ export type SqlStatementValidation = {
|
|
|
13
13
|
readonly response: AvOperationFailureResponse;
|
|
14
14
|
};
|
|
15
15
|
/** What validating a SQL step needs from the plan around it. */
|
|
16
|
-
|
|
16
|
+
type SqlStatementValidationContext = {
|
|
17
17
|
/** The step's position in the plan. */
|
|
18
18
|
readonly index: number;
|
|
19
19
|
/** The steps validated before it, by position. */
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { validationIssue } from "../operations/validation-code.js";
|
|
2
|
+
import { AvCallerDataError, locateCallerDataError, } from "../runtime/caller-data.copier.js";
|
|
2
3
|
import { refuseCallerData } from "../runtime/caller-data.door.js";
|
|
3
|
-
import {
|
|
4
|
+
import { isAvDecimal } from "../runtime/decimal.js";
|
|
4
5
|
import { planStepPath } from "../runtime/error-boundary.js";
|
|
5
6
|
import { operationFailure } from "../runtime/operation-response.js";
|
|
6
7
|
import { mapValidationOutcome } from "../runtime/outcome-mapper.js";
|
|
7
8
|
import { isPlainRecord, normalizeJsonValue, } from "../runtime/scalar-runtime.js";
|
|
8
|
-
import { invalidSourceIndexIssue, referenceSourceAt, referenceSourceField, } from "./transaction-reference-validator.js";
|
|
9
|
+
import { inspectReferenceCandidate, invalidSourceIndexIssue, referenceSourceAt, referenceSourceField, } from "./transaction-reference-validator.js";
|
|
9
10
|
import { transactionOperationFailure } from "./transaction-response.js";
|
|
10
11
|
function refused(issue, index) {
|
|
11
12
|
return {
|
|
@@ -18,7 +19,7 @@ export function normalizeSqlValue(value, path) {
|
|
|
18
19
|
typeof value === "string" ||
|
|
19
20
|
typeof value === "boolean" ||
|
|
20
21
|
typeof value === "bigint" ||
|
|
21
|
-
value
|
|
22
|
+
isAvDecimal(value) ||
|
|
22
23
|
value instanceof Uint8Array) {
|
|
23
24
|
return { ok: true, value };
|
|
24
25
|
}
|
|
@@ -56,7 +57,7 @@ export function normalizeSqlValue(value, path) {
|
|
|
56
57
|
};
|
|
57
58
|
}
|
|
58
59
|
export function validateSqlStatement(statement, context) {
|
|
59
|
-
const { index
|
|
60
|
+
const { index } = context;
|
|
60
61
|
if (!context.available) {
|
|
61
62
|
const message = "SQL statements are not available on this Adapter.";
|
|
62
63
|
return {
|
|
@@ -73,6 +74,22 @@ export function validateSqlStatement(statement, context) {
|
|
|
73
74
|
if (statement.refused !== undefined) {
|
|
74
75
|
return refused(refuseCallerData(statement.refused, [...planStepPath(index), "values"], { origin: "application", operation: index }, context.misuse), index);
|
|
75
76
|
}
|
|
77
|
+
try {
|
|
78
|
+
return validateSqlValues(statement, statement.strings, context);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (!(error instanceof AvCallerDataError)) {
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
return refused(refuseCallerData(locateCallerDataError(error, statement.values.map((planned) => planned.kind === "value"
|
|
85
|
+
? planned.value
|
|
86
|
+
: planned.kind === "candidate"
|
|
87
|
+
? planned.candidate
|
|
88
|
+
: undefined)), [...planStepPath(index), "values"], { origin: "application", operation: index }, context.misuse), index);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function validateSqlValues(statement, strings, context) {
|
|
92
|
+
const { index, earlier } = context;
|
|
76
93
|
const values = [];
|
|
77
94
|
const issues = [];
|
|
78
95
|
for (const [position, planned] of statement.values.entries()) {
|
|
@@ -80,6 +97,22 @@ export function validateSqlStatement(statement, context) {
|
|
|
80
97
|
switch (planned.kind) {
|
|
81
98
|
case "misplaced-reference":
|
|
82
99
|
return refused(validationIssue("V1010", [...path, ...planned.path, "$ref"], "A transaction reference in a SQL step must be a whole value."), index);
|
|
100
|
+
case "candidate": {
|
|
101
|
+
const inspection = inspectReferenceCandidate(planned.candidate, path, earlier, index);
|
|
102
|
+
if (!inspection.ok) {
|
|
103
|
+
return refused(inspection.issue, index);
|
|
104
|
+
}
|
|
105
|
+
if (inspection.field.kind !== "scalar" || inspection.field.list) {
|
|
106
|
+
return refused(validationIssue("V1011", inspection.pathPath, "Transaction reference source is not a single scalar field."), index);
|
|
107
|
+
}
|
|
108
|
+
values.push({
|
|
109
|
+
kind: "reference",
|
|
110
|
+
operation: inspection.source.index,
|
|
111
|
+
field: inspection.segment,
|
|
112
|
+
source: inspection.field,
|
|
113
|
+
});
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
83
116
|
case "reference": {
|
|
84
117
|
const source = referenceSourceAt(earlier, planned.operation, index);
|
|
85
118
|
if (source === undefined) {
|
|
@@ -127,7 +160,7 @@ export function validateSqlStatement(statement, context) {
|
|
|
127
160
|
statement: {
|
|
128
161
|
kind: "sql",
|
|
129
162
|
index,
|
|
130
|
-
strings
|
|
163
|
+
strings,
|
|
131
164
|
values,
|
|
132
165
|
},
|
|
133
166
|
};
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import type { AvValidationIssue } from "../operations/validation-code.js";
|
|
2
|
-
|
|
2
|
+
type TransactionEnvelopeSchema = {
|
|
3
3
|
readonly required: readonly string[];
|
|
4
4
|
readonly optional?: readonly string[];
|
|
5
5
|
readonly invalidContainerPath: readonly (string | number)[];
|
|
6
6
|
readonly invalidContainerMessage: string;
|
|
7
7
|
};
|
|
8
|
-
|
|
8
|
+
type TransactionEnvelopeInspection = {
|
|
9
9
|
readonly ok: true;
|
|
10
10
|
readonly values: ReadonlyMap<string, unknown>;
|
|
11
11
|
} | {
|
|
12
12
|
readonly ok: false;
|
|
13
13
|
readonly issue: AvValidationIssue;
|
|
14
14
|
};
|
|
15
|
-
|
|
15
|
+
type TransactionOperationsInspection = {
|
|
16
16
|
readonly ok: true;
|
|
17
17
|
readonly value: object;
|
|
18
18
|
readonly length: number;
|
|
@@ -20,7 +20,7 @@ export type TransactionOperationsInspection = {
|
|
|
20
20
|
readonly ok: false;
|
|
21
21
|
readonly issue: AvValidationIssue;
|
|
22
22
|
};
|
|
23
|
-
|
|
23
|
+
type TransactionOperationEntryInspection = {
|
|
24
24
|
readonly ok: true;
|
|
25
25
|
readonly value: unknown;
|
|
26
26
|
} | {
|
|
@@ -39,3 +39,4 @@ export declare function inspectTransactionOperationEntry(operations: object, ind
|
|
|
39
39
|
* Proxies are unsupported; reflection failures are contained as envelope issues.
|
|
40
40
|
*/
|
|
41
41
|
export declare function inspectTransactionEnvelope(value: unknown, schema: TransactionEnvelopeSchema): TransactionEnvelopeInspection;
|
|
42
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandCoreClass } from "../runtime/core-class.brand.js";
|
|
1
2
|
const TRANSACTION_INTEGRITY_MESSAGES = {
|
|
2
3
|
"transaction-unavailable": "Adapter transaction function is unavailable.",
|
|
3
4
|
"resolved-after-abort": "Adapter transaction resolved after the framework requested rollback.",
|
|
@@ -15,3 +16,4 @@ export class AvTransactionIntegrityError extends Error {
|
|
|
15
16
|
this.violation = violation;
|
|
16
17
|
}
|
|
17
18
|
}
|
|
19
|
+
brandCoreClass(AvTransactionIntegrityError, "AvTransactionIntegrityError");
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { AvContracts } from "../compiler/contract.compiler.js";
|
|
2
2
|
import type { AvOperationIdentity } from "../operations/operation-identity.js";
|
|
3
|
-
import
|
|
3
|
+
import { AvCallerDataError } from "../runtime/caller-data.copier.js";
|
|
4
4
|
import type { MisuseReporter } from "../runtime/misuse-reporter.js";
|
|
5
|
-
import type {
|
|
5
|
+
import type { AvRequestProtocol } from "../runtime/operation-request.js";
|
|
6
6
|
import type { AvOperationFailureResponse } from "../runtime/operation-response.js";
|
|
7
7
|
import type { AvOperation, OperationFingerprint } from "./deferred-operation.js";
|
|
8
8
|
import type { PlannedSqlStatement } from "./sql-statement.js";
|
|
9
9
|
import type { TransactionOperationOrigin } from "./transaction-request.js";
|
|
10
10
|
/** The origin a handle's builder gives it. */
|
|
11
11
|
type HandleOrigin = "application" | "client";
|
|
12
|
+
/** The scalar forms a builder takes its arguments in. */
|
|
13
|
+
type ArgumentForms = "wire" | "application";
|
|
12
14
|
/** One wire node, exactly as the transaction runtime receives it. */
|
|
13
15
|
type WireOperationNode = {
|
|
14
16
|
readonly resource: string;
|
|
@@ -37,8 +39,13 @@ export type AssembledTransactionPlan = {
|
|
|
37
39
|
readonly response: AvOperationFailureResponse;
|
|
38
40
|
};
|
|
39
41
|
export type TransactionPlanAssembler = {
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Defers one operation into a handle; nothing runs. `forms` says which scalar
|
|
44
|
+
* forms a client-origin handle's arguments are in: `"wire"` (`clientTx`, the
|
|
45
|
+
* default) or `"application"` (`serverClientTx`), encoded into the wire forms
|
|
46
|
+
* from the builder's copy.
|
|
47
|
+
*/
|
|
48
|
+
defer(origin: HandleOrigin, resource: string, identity: AvOperationIdentity, args: unknown, forms?: ArgumentForms): AvOperation<unknown>;
|
|
42
49
|
/** Defers one `appTx.$sql` statement into a handle; nothing runs. */
|
|
43
50
|
deferSql(strings: unknown, values: readonly unknown[]): AvOperation<unknown>;
|
|
44
51
|
/** Binds a list of handles into the passive plan, or refuses it. */
|
|
@@ -49,5 +56,5 @@ export type TransactionPlanAssembler = {
|
|
|
49
56
|
* handle, so a handle another instance built — typed against another model,
|
|
50
57
|
* bound to other Contracts — is refused rather than run.
|
|
51
58
|
*/
|
|
52
|
-
export declare function createTransactionPlanAssembler(contracts: AvContracts, protocol:
|
|
59
|
+
export declare function createTransactionPlanAssembler(contracts: AvContracts, protocol: AvRequestProtocol, misuse: MisuseReporter): TransactionPlanAssembler;
|
|
53
60
|
export {};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { VALIDATION_CODE_MESSAGES, validationIssue, } from "../operations/validation-code.js";
|
|
2
|
+
import { AvCallerDataError, } from "../runtime/caller-data.copier.js";
|
|
2
3
|
import { copyAtDoor, refuseCallerData } from "../runtime/caller-data.door.js";
|
|
3
4
|
import { operationFailure } from "../runtime/operation-response.js";
|
|
4
5
|
import { mapValidationOutcome } from "../runtime/outcome-mapper.js";
|
|
6
|
+
import { withoutUnsentWriteValues } from "../runtime/unsent-value.normalizer.js";
|
|
5
7
|
import { encodeOperationArgumentsForWire } from "./operation-arguments-encoder.js";
|
|
6
8
|
import { computeOperationFingerprint } from "./operation-fingerprint.js";
|
|
7
9
|
import { readSqlTemplate } from "./sql-statement.js";
|
|
10
|
+
import { routeTransactionReferenceCandidate } from "./transaction-reference.js";
|
|
8
11
|
import { transactionOperationFailure } from "./transaction-response.js";
|
|
9
12
|
const SQL_STEP_NODE = Object.freeze({ fingerprint: null });
|
|
10
13
|
function isPlainRecord(value) {
|
|
@@ -46,6 +49,9 @@ function rebuildArguments(root, leaf) {
|
|
|
46
49
|
if (!Array.isArray(value) && !isPlainRecord(value)) {
|
|
47
50
|
return { value };
|
|
48
51
|
}
|
|
52
|
+
if (Object.hasOwn(value, "$ref")) {
|
|
53
|
+
return { value };
|
|
54
|
+
}
|
|
49
55
|
if (ancestors.has(value)) {
|
|
50
56
|
return { value };
|
|
51
57
|
}
|
|
@@ -92,15 +98,17 @@ function operationEntryFailure(operation, message) {
|
|
|
92
98
|
function referenceFailure(issue, operation) {
|
|
93
99
|
return transactionOperationFailure(mapValidationOutcome([issue]), operation);
|
|
94
100
|
}
|
|
101
|
+
const STEP_REFERENCE_MIRROR = (value) => Object.hasOwn(value, "$ref");
|
|
95
102
|
export function createTransactionPlanAssembler(contracts, protocol, misuse) {
|
|
96
103
|
const handles = new WeakMap();
|
|
97
104
|
const placeholders = new WeakMap();
|
|
98
105
|
const originOf = (origin) => origin === "application"
|
|
99
106
|
? { origin: "application" }
|
|
100
107
|
: { origin: "client", protocol };
|
|
101
|
-
const snapshot = (args) => {
|
|
108
|
+
const snapshot = (args, mirror) => {
|
|
102
109
|
const copy = copyAtDoor(args, {
|
|
103
110
|
keep: (value) => placeholders.has(value),
|
|
111
|
+
...(mirror === undefined ? {} : { mirror }),
|
|
104
112
|
});
|
|
105
113
|
return copy.ok ? { args: copy.value } : { args: {}, refused: copy.error };
|
|
106
114
|
};
|
|
@@ -131,15 +139,43 @@ export function createTransactionPlanAssembler(contracts, protocol, misuse) {
|
|
|
131
139
|
handles.set(handle, record);
|
|
132
140
|
return handle;
|
|
133
141
|
};
|
|
134
|
-
const defer = (origin, resource, identity, args) =>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
const defer = (origin, resource, identity, args, forms = "wire") => {
|
|
143
|
+
const snapped = snapshot(args, STEP_REFERENCE_MIRROR);
|
|
144
|
+
let copy = snapped;
|
|
145
|
+
if (origin === "client" &&
|
|
146
|
+
forms === "application" &&
|
|
147
|
+
snapped.refused === undefined) {
|
|
148
|
+
try {
|
|
149
|
+
copy = {
|
|
150
|
+
args: encodeOperationArgumentsForWire(contracts.client, { resource, ...identity }, snapped.args),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (!(error instanceof AvCallerDataError)) {
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
let sent = copy.args;
|
|
160
|
+
try {
|
|
161
|
+
sent = withoutUnsentWriteValues(origin === "application" ? contracts.application : contracts.client, { resource, family: identity.family }, copy.args);
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (!(error instanceof AvCallerDataError)) {
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return createHandle({
|
|
169
|
+
kind: "operation",
|
|
170
|
+
origin,
|
|
171
|
+
resource,
|
|
172
|
+
identity,
|
|
173
|
+
...copy,
|
|
174
|
+
args: sent,
|
|
175
|
+
});
|
|
176
|
+
};
|
|
141
177
|
const deferSql = (strings, values) => {
|
|
142
|
-
const copy = snapshot(values);
|
|
178
|
+
const copy = snapshot(values, STEP_REFERENCE_MIRROR);
|
|
143
179
|
return createHandle({
|
|
144
180
|
kind: "sql",
|
|
145
181
|
strings: readSqlTemplate(strings, values.length),
|
|
@@ -153,7 +189,9 @@ export function createTransactionPlanAssembler(contracts, protocol, misuse) {
|
|
|
153
189
|
const sites = placeholderSites(value);
|
|
154
190
|
const [site] = sites;
|
|
155
191
|
if (site === undefined) {
|
|
156
|
-
return
|
|
192
|
+
return routeTransactionReferenceCandidate(value).kind === "ordinary"
|
|
193
|
+
? { kind: "value", value }
|
|
194
|
+
: { kind: "candidate", candidate: value };
|
|
157
195
|
}
|
|
158
196
|
if (site.path.length > 0) {
|
|
159
197
|
return { kind: "misplaced-reference", path: site.path };
|
|
@@ -4,7 +4,7 @@ import type { AvScalarField } from "../contracts/field.js";
|
|
|
4
4
|
import type { SemanticOperationArguments } from "../operations/operation-validator.js";
|
|
5
5
|
import { AvCallerDataError } from "../runtime/caller-data.copier.js";
|
|
6
6
|
import { type ReadProjectionPlan } from "../runtime/computed-read-runtime.js";
|
|
7
|
-
import type {
|
|
7
|
+
import type { AvOperationRequest } from "../runtime/operation-request.js";
|
|
8
8
|
import type { AvOperationFailureResponse } from "../runtime/operation-response.js";
|
|
9
9
|
import type { OperationPipelineDependencies } from "../runtime/operation-runtime.js";
|
|
10
10
|
import type { CascadeClosure } from "../runtime/referential-action.js";
|
|
@@ -13,7 +13,7 @@ import type { PlannedSqlStatement, ValidatedSqlStatement } from "./sql-statement
|
|
|
13
13
|
import type { AvTransactionReference } from "./transaction-reference.js";
|
|
14
14
|
import { type TransactionSlotShape } from "./transaction-reference.js";
|
|
15
15
|
import { type TransactionReferenceCandidateSlot } from "./transaction-reference-collector.js";
|
|
16
|
-
import type {
|
|
16
|
+
import type { AvTransactionRequest, TransactionOperationOrigin } from "./transaction-request.js";
|
|
17
17
|
/**
|
|
18
18
|
* Where a plan's step arguments came from, so the validator knows whether to copy
|
|
19
19
|
* them at the door (see `ArgumentsSource`):
|
|
@@ -34,6 +34,22 @@ export type PlanArguments = {
|
|
|
34
34
|
readonly refused?: ReadonlyMap<number, AvCallerDataError>;
|
|
35
35
|
readonly statements?: ReadonlyMap<number, PlannedSqlStatement>;
|
|
36
36
|
};
|
|
37
|
+
/** A plan taken at a call, and the steps whose arguments could not be copied. */
|
|
38
|
+
type CapturedPlan = {
|
|
39
|
+
readonly plan: unknown;
|
|
40
|
+
readonly refused: ReadonlyMap<number, AvCallerDataError>;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* A host's parsed plan taken at `protocol.handleTransaction`'s call, before its first
|
|
44
|
+
* await (rc.2, e2e batch 2): its structure mirrored by descriptors, which is how this
|
|
45
|
+
* validator inspects it — so no getter it refuses runs, and every accessor stays one
|
|
46
|
+
* for it to refuse — and each step's `args` copied as {@link captureSteps} copies a
|
|
47
|
+
* caller's, its `$ref` control objects mirrored. Run as an `"owned"` plan with
|
|
48
|
+
* `refused`, it is answered exactly as the plan the host held at the call. A plan
|
|
49
|
+
* that is no object, or cannot be reflected, is returned as it is: nothing in it can
|
|
50
|
+
* change.
|
|
51
|
+
*/
|
|
52
|
+
export declare function capturePlanAtCall(plan: unknown): CapturedPlan;
|
|
37
53
|
export type { TransactionReferenceCandidateSlot } from "./transaction-reference-collector.js";
|
|
38
54
|
/** One validated Transaction Slot: where it is, what it holds, and its reference. */
|
|
39
55
|
export type TransactionReferenceSlot = {
|
|
@@ -49,7 +65,7 @@ export type TransactionReferenceSlot = {
|
|
|
49
65
|
};
|
|
50
66
|
export type CandidateTransactionOperation = {
|
|
51
67
|
readonly index: number;
|
|
52
|
-
readonly request:
|
|
68
|
+
readonly request: AvOperationRequest;
|
|
53
69
|
readonly arguments: SemanticOperationArguments;
|
|
54
70
|
readonly projection: ReadProjectionPlan | undefined;
|
|
55
71
|
readonly declaredFingerprint: unknown;
|
|
@@ -58,7 +74,7 @@ export type CandidateTransactionOperation = {
|
|
|
58
74
|
export type ValidatedTransactionOperation = {
|
|
59
75
|
readonly kind: "operation";
|
|
60
76
|
readonly index: number;
|
|
61
|
-
readonly request:
|
|
77
|
+
readonly request: AvOperationRequest;
|
|
62
78
|
/**
|
|
63
79
|
* The Contract of this operation's origin, which is the plan's: the one it was
|
|
64
80
|
* validated against, the one it runs under, and the one a later `$ref` reads its
|
|
@@ -81,12 +97,12 @@ export type ValidatedTransactionPlan = {
|
|
|
81
97
|
* Framework is constructed. The record-write facts are not among them: they
|
|
82
98
|
* are checked when each operation runs, after its pipes.
|
|
83
99
|
*/
|
|
84
|
-
|
|
100
|
+
type TransactionPlanDependencies<M extends AvAdapterModel> = Omit<OperationPipelineDependencies<M>, "recordWrites" | "contexts" | "fieldStages"> & {
|
|
85
101
|
readonly cascades: CascadeClosure;
|
|
86
102
|
/** Whether the Adapter declares `sqlStatements`: read once, at construction. */
|
|
87
103
|
readonly sqlStatements: boolean;
|
|
88
104
|
};
|
|
89
|
-
|
|
105
|
+
type TransactionPlanValidationResult = {
|
|
90
106
|
readonly ok: true;
|
|
91
107
|
readonly plan: ValidatedTransactionPlan;
|
|
92
108
|
} | {
|
|
@@ -102,4 +118,4 @@ export declare function transactionsUnavailableFailure(): AvOperationFailureResp
|
|
|
102
118
|
/**
|
|
103
119
|
* Whole-plan Framework Validation Layer for transaction structure and identity.
|
|
104
120
|
*/
|
|
105
|
-
export declare function validateTransactionPlan<M extends AvAdapterModel>(dependencies: TransactionPlanDependencies<M>, request:
|
|
121
|
+
export declare function validateTransactionPlan<M extends AvAdapterModel>(dependencies: TransactionPlanDependencies<M>, request: AvTransactionRequest, origins?: readonly TransactionOperationOrigin[], planArguments?: PlanArguments): Promise<TransactionPlanValidationResult>;
|
|
@@ -4,7 +4,7 @@ import { parseOperationIdentity } from "../operations/operation-identity.js";
|
|
|
4
4
|
import { findOperationAvailabilityIssue, operationUnavailableIssue, unavailableOperationKey, } from "../operations/operation-validator.js";
|
|
5
5
|
import { resourceUnavailableIssue } from "../operations/operation-validator-core.js";
|
|
6
6
|
import { VALIDATION_CODE_MESSAGES, validationIssue, } from "../operations/validation-code.js";
|
|
7
|
-
import { AvCallerDataError, locateCallerDataError, } from "../runtime/caller-data.copier.js";
|
|
7
|
+
import { AvCallerDataError, locateCallerDataError, mirrorCallerData, } from "../runtime/caller-data.copier.js";
|
|
8
8
|
import { copyAtDoor, misuseIdentityOf, refuseCallerData, } from "../runtime/caller-data.door.js";
|
|
9
9
|
import { planReadOperation, } from "../runtime/computed-read-runtime.js";
|
|
10
10
|
import { planOperationArgumentsPath, planStepPath, } from "../runtime/error-boundary.js";
|
|
@@ -24,6 +24,32 @@ const CALLER_PLAN = { source: "caller" };
|
|
|
24
24
|
const STEP_ARGUMENTS_POLICY = {
|
|
25
25
|
mirror: (value) => Object.hasOwn(value, "$ref"),
|
|
26
26
|
};
|
|
27
|
+
export function capturePlanAtCall(plan) {
|
|
28
|
+
const refused = new Map();
|
|
29
|
+
if (typeof plan !== "object" || plan === null) {
|
|
30
|
+
return { plan, refused };
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const mirrored = mirrorCallerData(plan, {
|
|
34
|
+
copyAt: (path) => path.length === 3 &&
|
|
35
|
+
path[0] === "operations" &&
|
|
36
|
+
typeof path[1] === "number" &&
|
|
37
|
+
path[2] === "args"
|
|
38
|
+
? STEP_ARGUMENTS_POLICY
|
|
39
|
+
: undefined,
|
|
40
|
+
refused: (path, error) => {
|
|
41
|
+
refused.set(path[1], error);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
return { plan: mirrored, refused };
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error instanceof AvCallerDataError) {
|
|
48
|
+
return { plan, refused };
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
27
53
|
function captureSteps(operations, operationCount, planArguments) {
|
|
28
54
|
const steps = [];
|
|
29
55
|
const statements = planArguments.source === "owned" ? planArguments.statements : undefined;
|
|
@@ -3,14 +3,14 @@ import type { SemanticOperationArguments } from "../operations/operation-validat
|
|
|
3
3
|
import type { AvValidationIssue } from "../operations/validation-code.js";
|
|
4
4
|
import type { ValidatedSqlStatement } from "./sql-statement.js";
|
|
5
5
|
import type { ValidatedTransactionOperation } from "./transaction-plan-validator.js";
|
|
6
|
-
|
|
6
|
+
type ResolvedTransactionArguments = {
|
|
7
7
|
readonly ok: true;
|
|
8
8
|
readonly arguments: SemanticOperationArguments;
|
|
9
9
|
} | {
|
|
10
10
|
readonly ok: false;
|
|
11
11
|
readonly issues: readonly AvValidationIssue[];
|
|
12
12
|
};
|
|
13
|
-
|
|
13
|
+
type ResolvedSqlStatement = {
|
|
14
14
|
readonly ok: true;
|
|
15
15
|
readonly statement: AvAdapterSqlStatement;
|
|
16
16
|
} | {
|
|
@@ -38,3 +38,4 @@ export declare function resolveSqlStatement(statement: ValidatedSqlStatement, re
|
|
|
38
38
|
* alter the source's committed result.
|
|
39
39
|
*/
|
|
40
40
|
export declare function resolveTransactionReferences(operation: ValidatedTransactionOperation, results: readonly unknown[]): ResolvedTransactionArguments;
|
|
41
|
+
export {};
|