@aventara/core 1.0.0-rc.1 → 1.0.0-rc.11
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 +1022 -16
- package/ERRORS.md +142 -0
- package/README.md +330 -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 +281 -31
- package/dist/compiler/create-requirements.projector.js +1 -3
- package/dist/compiler/field-validator.compiler.js +1 -3
- 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/cache-stage.factory.d.ts +24 -0
- package/dist/config/cache-stage.factory.js +3 -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 +66 -7
- package/dist/config/config.validator.js +283 -21
- 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 +73 -22
- package/dist/config/field-compute.resolver.d.ts +2 -0
- package/dist/config/field-compute.resolver.js +9 -3
- 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 +20 -4
- package/dist/config/pipeline-tree.resolver.d.ts +2 -2
- package/dist/config/pipeline-tree.resolver.js +2 -3
- package/dist/contracts/access-route.rules.d.ts +10 -0
- package/dist/contracts/access-route.rules.js +45 -0
- package/dist/contracts/canonical-json.writer.d.ts +7 -0
- package/dist/contracts/canonical-json.writer.js +55 -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 +365 -89
- package/dist/contracts/client-contract-structure.validator.js +49 -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 +35 -4
- package/dist/contracts/contract-hash.d.ts +0 -1
- package/dist/contracts/contract-hash.js +1 -1
- package/dist/contracts/contract-limits.d.ts +6 -0
- package/dist/contracts/contract-limits.js +2 -0
- package/dist/contracts/contract.d.ts +42 -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 +76 -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 +118 -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 +145 -24
- package/dist/framework/operation-facade.d.ts +141 -13
- package/dist/framework/operation-facade.js +9 -1
- package/dist/framework/operation-set.runner.d.ts +47 -0
- package/dist/framework/operation-set.runner.js +168 -0
- package/dist/index.d.ts +19 -5
- package/dist/index.js +3 -0
- package/dist/internal.d.ts +9 -2
- package/dist/internal.js +8 -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 +160 -39
- package/dist/operations/operation-validator.d.ts +1 -1
- package/dist/operations/structural-limits.walker.d.ts +1 -1
- package/dist/operations/structural-limits.walker.js +1 -3
- 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 +295 -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 +407 -0
- package/dist/policy/policy-after-update.runtime.d.ts +79 -0
- package/dist/policy/policy-after-update.runtime.js +206 -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 +536 -0
- package/dist/policy/policy-read.runtime.d.ts +81 -0
- package/dist/policy/policy-read.runtime.js +501 -0
- package/dist/policy/policy-reference-write.runtime.d.ts +51 -0
- package/dist/policy/policy-reference-write.runtime.js +336 -0
- package/dist/policy/policy-request.d.ts +72 -0
- package/dist/policy/policy-request.js +115 -0
- package/dist/policy/policy-session.d.ts +106 -0
- package/dist/policy/policy-session.js +293 -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 +530 -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 +63 -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 +75 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.js +227 -0
- package/dist/protocol/absent-route.responder.d.ts +49 -14
- package/dist/protocol/absent-route.responder.js +92 -15
- package/dist/protocol/contract-document.responder.d.ts +9 -1
- package/dist/protocol/contract-document.responder.js +3 -2
- package/dist/protocol/http-request.decoder.d.ts +52 -3
- package/dist/protocol/http-request.decoder.js +45 -6
- package/dist/protocol/http-request.reader.d.ts +10 -3
- package/dist/protocol/http-request.reader.js +12 -2
- package/dist/protocol/http-response.encoder.d.ts +12 -0
- package/dist/protocol/http-response.encoder.js +17 -3
- package/dist/protocol/operation-response.guard.js +1 -3
- package/dist/protocol/operation-set.decoder.d.ts +48 -0
- package/dist/protocol/operation-set.decoder.js +185 -0
- package/dist/protocol/operation-set.handler.d.ts +27 -0
- package/dist/protocol/operation-set.handler.js +179 -0
- package/dist/protocol/protocol-headers.d.ts +12 -3
- package/dist/protocol/protocol-headers.js +12 -5
- package/dist/protocol/protocol-surface.d.ts +16 -3
- package/dist/protocol/protocol-surface.js +41 -1
- package/dist/protocol/protocol.binder.d.ts +52 -1
- package/dist/protocol/protocol.binder.js +6 -1
- package/dist/protocol/protocol.handler.d.ts +45 -1
- package/dist/protocol/protocol.handler.js +164 -19
- package/dist/protocol/read-cache.responder.d.ts +26 -0
- package/dist/protocol/read-cache.responder.js +47 -0
- package/dist/protocol/read-query.codec.d.ts +34 -0
- package/dist/protocol/read-query.codec.js +125 -0
- 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 +229 -48
- 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 +130 -24
- package/dist/runtime/decimal.d.ts +14 -0
- package/dist/runtime/decimal.js +21 -0
- package/dist/runtime/diagnostics-reporter.d.ts +49 -9
- package/dist/runtime/diagnostics-reporter.js +16 -9
- 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 -3
- package/dist/runtime/field-stage.runtime.js +9 -11
- package/dist/runtime/field-validator.runtime.d.ts +19 -1
- package/dist/runtime/field-validator.runtime.js +49 -9
- 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 +4 -17
- package/dist/runtime/nested-write.walker.js +2 -1
- 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/output-validator.js +1 -3
- package/dist/runtime/owned-execution.registry.d.ts +34 -4
- 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 +52 -23
- package/dist/runtime/readonly-value.d.ts +20 -5
- package/dist/runtime/readonly-value.js +74 -26
- package/dist/runtime/record.guard.d.ts +14 -0
- package/dist/runtime/record.guard.js +10 -0
- package/dist/runtime/referential-action.d.ts +1 -1
- package/dist/runtime/request-envelope.validator.js +13 -10
- package/dist/runtime/scalar-runtime.d.ts +4 -21
- package/dist/runtime/scalar-runtime.js +91 -31
- package/dist/runtime/thenable.guard.d.ts +7 -0
- package/dist/runtime/thenable.guard.js +5 -0
- package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
- package/dist/runtime/unsent-value.normalizer.js +129 -0
- package/dist/transactions/operation-arguments-encoder.js +9 -10
- 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 +40 -6
- 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 +49 -17
- package/dist/transactions/transaction-plan-validator.d.ts +23 -7
- package/dist/transactions/transaction-plan-validator.js +31 -7
- package/dist/transactions/transaction-reference-collector.js +8 -10
- package/dist/transactions/transaction-reference-resolver.d.ts +3 -2
- package/dist/transactions/transaction-reference-resolver.js +1 -3
- 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 +108 -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 +8 -7
- package/package.json +6 -5
- package/src/runtime/field-check.evaluator.ts +1 -1
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import { shapeOfField } from "../avz/json-shape.registry.js";
|
|
2
|
+
import { AVZ_FORMATS } from "../config/framework-field-definition.js";
|
|
3
|
+
import { FILTER_MODES, } from "../contracts/capability.js";
|
|
2
4
|
import { lookupField, lookupOwn, lookupResource, } from "../contracts/contract-lookup.js";
|
|
3
5
|
import { decodeBase64, WIRE_SCALAR_PATTERNS, } from "../contracts/wire-scalar-format.js";
|
|
4
6
|
import { routeTransactionReferenceCandidate, } from "../transactions/transaction-reference.js";
|
|
5
|
-
import { AvDecimal } from "./decimal.js";
|
|
7
|
+
import { AvDecimal, isAvDecimal } from "./decimal.js";
|
|
8
|
+
import { fieldCheckIssues } from "./field-check.evaluator.js";
|
|
9
|
+
import { isPlainRecord } from "./record.guard.js";
|
|
6
10
|
const INT_MIN = -(2 ** 31);
|
|
7
11
|
const INT_MAX = 2 ** 31 - 1;
|
|
8
12
|
const BIGINT_MIN = -(2n ** 63n);
|
|
9
13
|
const BIGINT_MAX = 2n ** 63n - 1n;
|
|
10
14
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export function isPlainRecord(value) {
|
|
15
|
-
if (!isRecord(value)) {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
const prototype = Object.getPrototypeOf(value);
|
|
19
|
-
return prototype === Object.prototype || prototype === null;
|
|
15
|
+
const NAMED_FORMATS = new Set(AVZ_FORMATS);
|
|
16
|
+
function isNamedFormat(format) {
|
|
17
|
+
return NAMED_FORMATS.has(format);
|
|
20
18
|
}
|
|
21
19
|
function issue(code, path, message) {
|
|
22
20
|
return { code, path, message };
|
|
@@ -24,6 +22,10 @@ function issue(code, path, message) {
|
|
|
24
22
|
function typeIssue(path, expected) {
|
|
25
23
|
return issue("V1001", path, `Value must be ${expected}.`);
|
|
26
24
|
}
|
|
25
|
+
const UNSENT_READ_VALUE_MESSAGE = "Value must not be undefined: it would be left out, and the request would reach more rows.";
|
|
26
|
+
export function unsentReadValueIssue(path) {
|
|
27
|
+
return issue("V1001", path, UNSENT_READ_VALUE_MESSAGE);
|
|
28
|
+
}
|
|
27
29
|
function formatIssue(path, expected) {
|
|
28
30
|
return issue("V1003", path, `Value must use ${expected}.`);
|
|
29
31
|
}
|
|
@@ -83,18 +85,14 @@ export function normalizeJsonValue(value, path, seen) {
|
|
|
83
85
|
const normalized = [];
|
|
84
86
|
task.emit(normalized);
|
|
85
87
|
stack.push({ kind: "leave", container: current });
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
entries.push([index, entry]);
|
|
89
|
-
});
|
|
90
|
-
for (let cursor = entries.length - 1; cursor >= 0; cursor -= 1) {
|
|
91
|
-
const entry = entries[cursor];
|
|
88
|
+
for (let index = current.length - 1; index >= 0; index -= 1) {
|
|
89
|
+
const entry = current[index];
|
|
92
90
|
stack.push({
|
|
93
91
|
kind: "visit",
|
|
94
|
-
value: entry
|
|
95
|
-
link: { parent: task.link, segment:
|
|
92
|
+
value: entry === undefined ? null : entry,
|
|
93
|
+
link: { parent: task.link, segment: index },
|
|
96
94
|
emit: (child) => {
|
|
97
|
-
normalized
|
|
95
|
+
normalized[index] = child;
|
|
98
96
|
},
|
|
99
97
|
});
|
|
100
98
|
}
|
|
@@ -112,7 +110,7 @@ export function normalizeJsonValue(value, path, seen) {
|
|
|
112
110
|
const normalized = {};
|
|
113
111
|
task.emit(normalized);
|
|
114
112
|
stack.push({ kind: "leave", container: current });
|
|
115
|
-
const entries = Object.entries(current);
|
|
113
|
+
const entries = Object.entries(current).filter((entry) => entry[1] !== undefined);
|
|
116
114
|
for (let cursor = entries.length - 1; cursor >= 0; cursor -= 1) {
|
|
117
115
|
const entry = entries[cursor];
|
|
118
116
|
stack.push({
|
|
@@ -231,10 +229,9 @@ function normalizeScalarAtom(contract, field, value, origin, path, allowNull) {
|
|
|
231
229
|
break;
|
|
232
230
|
case "decimal":
|
|
233
231
|
if (origin === "application") {
|
|
234
|
-
normalized =
|
|
235
|
-
value
|
|
236
|
-
|
|
237
|
-
: { ok: false, issues: [typeIssue(path, "an Aventara Decimal")] };
|
|
232
|
+
normalized = isAvDecimal(value)
|
|
233
|
+
? { ok: true, value }
|
|
234
|
+
: { ok: false, issues: [typeIssue(path, "an Aventara Decimal")] };
|
|
238
235
|
}
|
|
239
236
|
else if (typeof value !== "string") {
|
|
240
237
|
normalized = {
|
|
@@ -359,9 +356,21 @@ export function normalizeScalarFieldValue(contract, field, value, origin, path)
|
|
|
359
356
|
});
|
|
360
357
|
return issues.length > 0
|
|
361
358
|
? { ok: false, issues }
|
|
362
|
-
: { ok: true, value: normalized };
|
|
359
|
+
: heldToFormat(field, { ok: true, value: normalized }, origin, path);
|
|
363
360
|
}
|
|
364
|
-
return normalizeScalarAtom(contract, field, value, origin === "runtime" ? "application" : origin, path, field.nullable);
|
|
361
|
+
return heldToFormat(field, normalizeScalarAtom(contract, field, value, origin === "runtime" ? "application" : origin, path, field.nullable), origin, path);
|
|
362
|
+
}
|
|
363
|
+
function heldToFormat(field, normalized, origin, path) {
|
|
364
|
+
const format = field.format;
|
|
365
|
+
if (origin !== "runtime" ||
|
|
366
|
+
!normalized.ok ||
|
|
367
|
+
format === undefined ||
|
|
368
|
+
format === "uuid" ||
|
|
369
|
+
!isNamedFormat(format)) {
|
|
370
|
+
return normalized;
|
|
371
|
+
}
|
|
372
|
+
const issues = fieldCheckIssues([{ check: "string_format", format }], normalized.value, "string", path, field.list);
|
|
373
|
+
return issues.length === 0 ? normalized : { ok: false, issues };
|
|
365
374
|
}
|
|
366
375
|
export function jsonShapeIssues(field, value, path, position = "value") {
|
|
367
376
|
if (field.json === undefined || value === null || value === undefined) {
|
|
@@ -427,6 +436,11 @@ export function copyScalarFieldValue(field, value) {
|
|
|
427
436
|
function normalizeListElement(contract, field, value, origin, path) {
|
|
428
437
|
return normalizeScalarAtom(contract, field, value, origin, path, false);
|
|
429
438
|
}
|
|
439
|
+
function normalizeFilterEntry(contract, field, value, origin, path) {
|
|
440
|
+
return value === undefined
|
|
441
|
+
? { ok: false, issues: [unsentReadValueIssue(path)] }
|
|
442
|
+
: normalizeListElement(contract, field, value, origin, path);
|
|
443
|
+
}
|
|
430
444
|
function mergeResult(result, issues) {
|
|
431
445
|
if (result.ok) {
|
|
432
446
|
return result.value;
|
|
@@ -434,7 +448,7 @@ function mergeResult(result, issues) {
|
|
|
434
448
|
issues.push(...result.issues);
|
|
435
449
|
return undefined;
|
|
436
450
|
}
|
|
437
|
-
|
|
451
|
+
const REFERENCE_OPERANDS = Object.freeze({
|
|
438
452
|
equals: "value",
|
|
439
453
|
not: "value",
|
|
440
454
|
lt: "nonNullValue",
|
|
@@ -463,10 +477,40 @@ const NULL_OPERAND_OPERATORS = new Set([
|
|
|
463
477
|
"arrayStartsWith",
|
|
464
478
|
"arrayEndsWith",
|
|
465
479
|
]);
|
|
480
|
+
const KEYWORD_OPERANDS = Object.freeze({
|
|
481
|
+
mode: (operand, path, issues) => {
|
|
482
|
+
if (!FILTER_MODES.includes(operand)) {
|
|
483
|
+
issues.push(typeIssue(path, '"default", "sensitive" or "insensitive"'));
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
isEmpty: (operand, path, issues) => {
|
|
487
|
+
if (typeof operand !== "boolean") {
|
|
488
|
+
issues.push(typeIssue(path, "a boolean"));
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
path: (operand, path, issues) => {
|
|
492
|
+
if (!Array.isArray(operand)) {
|
|
493
|
+
issues.push(typeIssue(path, "an array of strings"));
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
for (let index = 0; index < operand.length; index += 1) {
|
|
497
|
+
if (operand[index] === undefined) {
|
|
498
|
+
issues.push(unsentReadValueIssue([...path, index]));
|
|
499
|
+
}
|
|
500
|
+
else if (typeof operand[index] !== "string") {
|
|
501
|
+
issues.push(typeIssue([...path, index], "a string"));
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
});
|
|
466
506
|
function normalizeScalarFilter(contract, field, value, origin, path, issues, references = false) {
|
|
467
507
|
if (isKeptReference(references, value)) {
|
|
468
508
|
return value;
|
|
469
509
|
}
|
|
510
|
+
if (value === undefined) {
|
|
511
|
+
issues.push(unsentReadValueIssue(path));
|
|
512
|
+
return value;
|
|
513
|
+
}
|
|
470
514
|
if (!isPlainRecord(value)) {
|
|
471
515
|
return mergeResult(normalizeScalarFieldValue(contract, field, value, origin, path), issues);
|
|
472
516
|
}
|
|
@@ -477,11 +521,17 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
|
|
|
477
521
|
const normalized = cloneRecord(value);
|
|
478
522
|
for (const [operator, operand] of Object.entries(value)) {
|
|
479
523
|
const operandPath = [...path, operator];
|
|
524
|
+
if (operand === undefined) {
|
|
525
|
+
issues.push(unsentReadValueIssue(operandPath));
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
480
528
|
if (operand === null && !acceptsNullOperand(operator)) {
|
|
481
529
|
issues.push(typeIssue(operandPath, "non-null"));
|
|
482
530
|
continue;
|
|
483
531
|
}
|
|
484
|
-
|
|
532
|
+
const keyword = lookupOwn(KEYWORD_OPERANDS, operator);
|
|
533
|
+
if (keyword !== undefined) {
|
|
534
|
+
keyword(operand, operandPath, issues);
|
|
485
535
|
continue;
|
|
486
536
|
}
|
|
487
537
|
if (referenceOperandShape(operator) !== undefined &&
|
|
@@ -497,14 +547,14 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
|
|
|
497
547
|
issues.push(typeIssue(operandPath, "an array"));
|
|
498
548
|
continue;
|
|
499
549
|
}
|
|
500
|
-
normalized[operator] = operand.map((entry, index) => mergeResult(
|
|
550
|
+
normalized[operator] = operand.map((entry, index) => mergeResult(normalizeFilterEntry(contract, field, entry, origin, [
|
|
501
551
|
...operandPath,
|
|
502
552
|
index,
|
|
503
553
|
]), issues));
|
|
504
554
|
continue;
|
|
505
555
|
}
|
|
506
556
|
if (operator === "has") {
|
|
507
|
-
normalized[operator] = mergeResult(
|
|
557
|
+
normalized[operator] = mergeResult(normalizeFilterEntry(contract, field, operand, origin, operandPath), issues);
|
|
508
558
|
continue;
|
|
509
559
|
}
|
|
510
560
|
if (operator === "hasEvery" || operator === "hasSome") {
|
|
@@ -512,7 +562,7 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
|
|
|
512
562
|
issues.push(typeIssue(operandPath, "an array"));
|
|
513
563
|
continue;
|
|
514
564
|
}
|
|
515
|
-
normalized[operator] = operand.map((entry, index) => mergeResult(
|
|
565
|
+
normalized[operator] = operand.map((entry, index) => mergeResult(normalizeFilterEntry(contract, field, entry, origin, [
|
|
516
566
|
...operandPath,
|
|
517
567
|
index,
|
|
518
568
|
]), issues));
|
|
@@ -521,6 +571,12 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
|
|
|
521
571
|
if ("scalar" in field.type &&
|
|
522
572
|
field.type.scalar === "json" &&
|
|
523
573
|
(operator.startsWith("string") || operator.startsWith("array"))) {
|
|
574
|
+
if (operator.startsWith("string")) {
|
|
575
|
+
if (typeof operand !== "string") {
|
|
576
|
+
issues.push(typeIssue(operandPath, "a string"));
|
|
577
|
+
}
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
524
580
|
const jsonResult = normalizeJsonValue(operand, operandPath, new Set());
|
|
525
581
|
normalized[operator] = mergeResult(jsonResult, issues);
|
|
526
582
|
continue;
|
|
@@ -585,6 +641,10 @@ function normalizeUniqueSelector(contract, resourceName, selector, origin, path,
|
|
|
585
641
|
const normalized = cloneRecord(selector);
|
|
586
642
|
for (const [fieldName, value] of Object.entries(selector)) {
|
|
587
643
|
const field = lookupField(resource, fieldName);
|
|
644
|
+
if (field?.kind === "scalar" && value === undefined) {
|
|
645
|
+
issues.push(unsentReadValueIssue([...path, fieldName]));
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
588
648
|
if (field?.kind === "scalar" && !isKeptReference(references, value)) {
|
|
589
649
|
normalized[fieldName] = mergeResult(normalizeScalarFieldValue(contract, field, value, origin, [
|
|
590
650
|
...path,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether `value` is what `await` waits for rather than answers at once: an
|
|
3
|
+
* object or function whose `then` is a function. Reads `then` once; a `then`
|
|
4
|
+
* getter that throws throws here, so a caller reading an untrusted value guards
|
|
5
|
+
* the call.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isThenable(value: unknown): value is PromiseLike<unknown>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AvContract } from "../contracts/contract.js";
|
|
2
|
+
import type { AvOperationFamily } from "../operations/operation-identity.js";
|
|
3
|
+
/**
|
|
4
|
+
* One operation's arguments with every write record's unsent fields removed — a
|
|
5
|
+
* field whose value is `undefined`, at any depth of nested write data the
|
|
6
|
+
* Contract's `maxNestingDepth` admits — and
|
|
7
|
+
* nothing else changed (see the module note): `where`, selectors and every other
|
|
8
|
+
* member are left as they are. The same object when nothing was unsent; otherwise
|
|
9
|
+
* a copy of each container on the way, never a write into `args`. Total over
|
|
10
|
+
* malformed input.
|
|
11
|
+
*/
|
|
12
|
+
export declare function withoutUnsentWriteValues(contract: AvContract, request: {
|
|
13
|
+
readonly resource: string;
|
|
14
|
+
readonly family: AvOperationFamily;
|
|
15
|
+
}, args: unknown): unknown;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
2
|
+
import { WRITE_KEYS } from "./field-stage.runtime.js";
|
|
3
|
+
import { isRelationMutationDirective, RELATION_MUTATION_RECORD_SITES, } from "./mutation-planner.js";
|
|
4
|
+
import { isPlainRecord } from "./record.guard.js";
|
|
5
|
+
const REMOVE = Symbol("remove");
|
|
6
|
+
const DEPTH_CEILING = 256;
|
|
7
|
+
function rebuilt(record, changes) {
|
|
8
|
+
const copy = Object.getPrototypeOf(record) === null ? Object.create(null) : {};
|
|
9
|
+
for (const key of Object.keys(record)) {
|
|
10
|
+
const value = changes.has(key) ? changes.get(key) : record[key];
|
|
11
|
+
if (value === REMOVE) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(copy, key, {
|
|
15
|
+
value,
|
|
16
|
+
writable: true,
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return copy;
|
|
22
|
+
}
|
|
23
|
+
function eachEntry(value, strip) {
|
|
24
|
+
if (!Array.isArray(value)) {
|
|
25
|
+
return strip(value);
|
|
26
|
+
}
|
|
27
|
+
let copy;
|
|
28
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
29
|
+
if (!(index in value)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const entry = value[index];
|
|
33
|
+
const stripped = strip(entry);
|
|
34
|
+
if (stripped !== entry) {
|
|
35
|
+
copy ??= value.slice();
|
|
36
|
+
copy[index] = stripped;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return copy ?? value;
|
|
40
|
+
}
|
|
41
|
+
function stripRecord(walk, resourceName, record, depth) {
|
|
42
|
+
if (depth > walk.depth || !isPlainRecord(record)) {
|
|
43
|
+
return record;
|
|
44
|
+
}
|
|
45
|
+
const resource = lookupResource(walk.contract, resourceName);
|
|
46
|
+
let changes;
|
|
47
|
+
for (const key of Object.keys(record)) {
|
|
48
|
+
const value = record[key];
|
|
49
|
+
if (value === undefined) {
|
|
50
|
+
changes ??= new Map();
|
|
51
|
+
changes.set(key, REMOVE);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const field = lookupField(resource, key);
|
|
55
|
+
if (field?.kind !== "relation") {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const stripped = stripRelationWrite(walk, field.target, value, depth + 1);
|
|
59
|
+
if (stripped !== value) {
|
|
60
|
+
changes ??= new Map();
|
|
61
|
+
changes.set(key, stripped);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return changes === undefined ? record : rebuilt(record, changes);
|
|
65
|
+
}
|
|
66
|
+
function stripRelationWrite(walk, target, value, depth) {
|
|
67
|
+
if (!isPlainRecord(value)) {
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
let changes;
|
|
71
|
+
for (const directive of Object.keys(value)) {
|
|
72
|
+
if (!isRelationMutationDirective(directive)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const sites = RELATION_MUTATION_RECORD_SITES[directive];
|
|
76
|
+
if (sites.length === 0) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const operand = value[directive];
|
|
80
|
+
const next = eachEntry(operand, (entry) => {
|
|
81
|
+
let stripped = entry;
|
|
82
|
+
for (const site of sites) {
|
|
83
|
+
if (site.key === undefined) {
|
|
84
|
+
stripped = stripRecord(walk, target, stripped, depth);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (!isPlainRecord(stripped) || !Object.hasOwn(stripped, site.key)) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const inner = stripped[site.key];
|
|
91
|
+
const record = stripRecord(walk, target, inner, depth);
|
|
92
|
+
if (record !== inner) {
|
|
93
|
+
stripped = rebuilt(stripped, new Map([[site.key, record]]));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return stripped;
|
|
97
|
+
});
|
|
98
|
+
if (next !== operand) {
|
|
99
|
+
changes ??= new Map();
|
|
100
|
+
changes.set(directive, next);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return changes === undefined ? value : rebuilt(value, changes);
|
|
104
|
+
}
|
|
105
|
+
export function withoutUnsentWriteValues(contract, request, args) {
|
|
106
|
+
const keys = Object.hasOwn(WRITE_KEYS, request.family)
|
|
107
|
+
? WRITE_KEYS[request.family]
|
|
108
|
+
: undefined;
|
|
109
|
+
if (keys === undefined || !isPlainRecord(args)) {
|
|
110
|
+
return args;
|
|
111
|
+
}
|
|
112
|
+
const walk = {
|
|
113
|
+
contract,
|
|
114
|
+
depth: Math.min(contract.limits.maxNestingDepth, DEPTH_CEILING),
|
|
115
|
+
};
|
|
116
|
+
let changes;
|
|
117
|
+
for (const key of keys) {
|
|
118
|
+
if (!Object.hasOwn(args, key)) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const value = args[key];
|
|
122
|
+
const next = eachEntry(value, (entry) => stripRecord(walk, request.resource, entry, 0));
|
|
123
|
+
if (next !== value) {
|
|
124
|
+
changes ??= new Map();
|
|
125
|
+
changes.set(key, next);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return changes === undefined ? args : rebuilt(args, changes);
|
|
129
|
+
}
|
|
@@ -1,15 +1,9 @@
|
|
|
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
|
+
import { isPlainRecord } from "../runtime/record.guard.js";
|
|
5
6
|
import { isTransactionReferenceCandidate } from "./transaction-reference.js";
|
|
6
|
-
function isPlainRecord(value) {
|
|
7
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
const prototype = Object.getPrototypeOf(value);
|
|
11
|
-
return prototype === Object.prototype || prototype === null;
|
|
12
|
-
}
|
|
13
7
|
function cloneRecord(value) {
|
|
14
8
|
return { ...value };
|
|
15
9
|
}
|
|
@@ -27,9 +21,11 @@ function encodeScalarAtom(field, value) {
|
|
|
27
21
|
case "bigint":
|
|
28
22
|
return typeof value === "bigint" ? value.toString(10) : value;
|
|
29
23
|
case "decimal":
|
|
30
|
-
return value
|
|
24
|
+
return isAvDecimal(value) ? value.toString() : value;
|
|
31
25
|
case "datetime":
|
|
32
|
-
return value instanceof Date
|
|
26
|
+
return value instanceof Date && !Number.isNaN(value.getTime())
|
|
27
|
+
? value.toISOString()
|
|
28
|
+
: value;
|
|
33
29
|
case "bytes":
|
|
34
30
|
return value instanceof Uint8Array ? encodeBase64(value) : value;
|
|
35
31
|
default:
|
|
@@ -282,6 +278,9 @@ function isUniqueOperation(operation) {
|
|
|
282
278
|
operation.family === "upsert"));
|
|
283
279
|
}
|
|
284
280
|
export function encodeOperationArgumentsForWire(contract, operation, args) {
|
|
281
|
+
if (!isPlainRecord(args)) {
|
|
282
|
+
return args;
|
|
283
|
+
}
|
|
285
284
|
const encoded = cloneRecord(args);
|
|
286
285
|
if (hasOwnWireProperty(args, "where")) {
|
|
287
286
|
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,13 @@
|
|
|
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
|
-
import { isPlainRecord
|
|
8
|
-
import {
|
|
8
|
+
import { isPlainRecord } from "../runtime/record.guard.js";
|
|
9
|
+
import { normalizeJsonValue } from "../runtime/scalar-runtime.js";
|
|
10
|
+
import { inspectReferenceCandidate, invalidSourceIndexIssue, referenceSourceAt, referenceSourceField, } from "./transaction-reference-validator.js";
|
|
9
11
|
import { transactionOperationFailure } from "./transaction-response.js";
|
|
10
12
|
function refused(issue, index) {
|
|
11
13
|
return {
|
|
@@ -18,7 +20,7 @@ export function normalizeSqlValue(value, path) {
|
|
|
18
20
|
typeof value === "string" ||
|
|
19
21
|
typeof value === "boolean" ||
|
|
20
22
|
typeof value === "bigint" ||
|
|
21
|
-
value
|
|
23
|
+
isAvDecimal(value) ||
|
|
22
24
|
value instanceof Uint8Array) {
|
|
23
25
|
return { ok: true, value };
|
|
24
26
|
}
|
|
@@ -56,7 +58,7 @@ export function normalizeSqlValue(value, path) {
|
|
|
56
58
|
};
|
|
57
59
|
}
|
|
58
60
|
export function validateSqlStatement(statement, context) {
|
|
59
|
-
const { index
|
|
61
|
+
const { index } = context;
|
|
60
62
|
if (!context.available) {
|
|
61
63
|
const message = "SQL statements are not available on this Adapter.";
|
|
62
64
|
return {
|
|
@@ -73,6 +75,22 @@ export function validateSqlStatement(statement, context) {
|
|
|
73
75
|
if (statement.refused !== undefined) {
|
|
74
76
|
return refused(refuseCallerData(statement.refused, [...planStepPath(index), "values"], { origin: "application", operation: index }, context.misuse), index);
|
|
75
77
|
}
|
|
78
|
+
try {
|
|
79
|
+
return validateSqlValues(statement, statement.strings, context);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (!(error instanceof AvCallerDataError)) {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
return refused(refuseCallerData(locateCallerDataError(error, statement.values.map((planned) => planned.kind === "value"
|
|
86
|
+
? planned.value
|
|
87
|
+
: planned.kind === "candidate"
|
|
88
|
+
? planned.candidate
|
|
89
|
+
: undefined)), [...planStepPath(index), "values"], { origin: "application", operation: index }, context.misuse), index);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function validateSqlValues(statement, strings, context) {
|
|
93
|
+
const { index, earlier } = context;
|
|
76
94
|
const values = [];
|
|
77
95
|
const issues = [];
|
|
78
96
|
for (const [position, planned] of statement.values.entries()) {
|
|
@@ -80,6 +98,22 @@ export function validateSqlStatement(statement, context) {
|
|
|
80
98
|
switch (planned.kind) {
|
|
81
99
|
case "misplaced-reference":
|
|
82
100
|
return refused(validationIssue("V1010", [...path, ...planned.path, "$ref"], "A transaction reference in a SQL step must be a whole value."), index);
|
|
101
|
+
case "candidate": {
|
|
102
|
+
const inspection = inspectReferenceCandidate(planned.candidate, path, earlier, index);
|
|
103
|
+
if (!inspection.ok) {
|
|
104
|
+
return refused(inspection.issue, index);
|
|
105
|
+
}
|
|
106
|
+
if (inspection.field.kind !== "scalar" || inspection.field.list) {
|
|
107
|
+
return refused(validationIssue("V1011", inspection.pathPath, "Transaction reference source is not a single scalar field."), index);
|
|
108
|
+
}
|
|
109
|
+
values.push({
|
|
110
|
+
kind: "reference",
|
|
111
|
+
operation: inspection.source.index,
|
|
112
|
+
field: inspection.segment,
|
|
113
|
+
source: inspection.field,
|
|
114
|
+
});
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
83
117
|
case "reference": {
|
|
84
118
|
const source = referenceSourceAt(earlier, planned.operation, index);
|
|
85
119
|
if (source === undefined) {
|
|
@@ -127,7 +161,7 @@ export function validateSqlStatement(statement, context) {
|
|
|
127
161
|
statement: {
|
|
128
162
|
kind: "sql",
|
|
129
163
|
index,
|
|
130
|
-
strings
|
|
164
|
+
strings,
|
|
131
165
|
values,
|
|
132
166
|
},
|
|
133
167
|
};
|
|
@@ -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");
|