@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
|
@@ -19,7 +19,7 @@ function serverContract(core, active) {
|
|
|
19
19
|
return { ...core, resources };
|
|
20
20
|
}
|
|
21
21
|
const SERVER_VIEWS = new WeakMap();
|
|
22
|
-
function serverContractFor(core, active) {
|
|
22
|
+
export function serverContractFor(core, active) {
|
|
23
23
|
let view = SERVER_VIEWS.get(active);
|
|
24
24
|
if (view === undefined) {
|
|
25
25
|
view = serverContract(core, active);
|
|
@@ -64,7 +64,9 @@ export function refusePipeOutput(contracts, request, args, pipe, index, options)
|
|
|
64
64
|
return refuseStageOutput(contracts, request, args, `Pipe ${pipeLabel(pipe, index)}`, options);
|
|
65
65
|
}
|
|
66
66
|
export function refuseStageOutput(contracts, request, args, subject, options) {
|
|
67
|
-
|
|
67
|
+
return refusedStageOutput(request, serverIssues(serverContractFor(contracts.core, contracts.active), request, args, options), subject);
|
|
68
|
+
}
|
|
69
|
+
export function refusedStageOutput(request, issues, subject) {
|
|
68
70
|
const [first] = issues;
|
|
69
71
|
if (first === undefined) {
|
|
70
72
|
return undefined;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { AvAdapterModel } from "../adapters/adapter-model.js";
|
|
2
2
|
import type { AvAdapterSqlValue } from "../adapters/adapter-sql-statement.js";
|
|
3
|
+
import type { AvPreComputeLift } from "../config/computed-fields.js";
|
|
3
4
|
import type { AvRequestOrigin } from "../config/config-layer.js";
|
|
4
5
|
import type { AvConfigResourceKey } from "../config/framework-field-definition.js";
|
|
5
6
|
import type { AvContract } from "../contracts/contract.js";
|
|
6
7
|
import type { AvApplicationScalarForms } from "../contracts/scalar-value-type.js";
|
|
7
|
-
import type { AvContractApplicationView } from "../framework/contract-view.js";
|
|
8
|
+
import type { AvContractApplicationView, RequirementsLessComputed } from "../framework/contract-view.js";
|
|
8
9
|
import type { AvContextOperations } from "../framework/operation-facade.js";
|
|
9
10
|
import type { AvOperationArgumentsFor, AvResourceKey } from "../operations/operation-arguments.js";
|
|
10
11
|
import type { AvOperationFamily, AvOperationIdentity } from "../operations/operation-identity.js";
|
|
11
12
|
import type { AvTransactionReference } from "../transactions/transaction-reference.js";
|
|
12
13
|
import type { AvError } from "./framework-error.js";
|
|
13
|
-
import type { AvDeepReadonly, AvDeepReadonlyLeaf } from "./readonly-value.js";
|
|
14
|
+
import type { ArrayForm, AvDeepReadonly, AvDeepReadonlyLeaf } from "./readonly-value.js";
|
|
14
15
|
import type { AvState } from "./request-state.js";
|
|
15
16
|
/**
|
|
16
17
|
* What a pipeline is told about the HTTP request a client-origin operation arrived on.
|
|
@@ -36,24 +37,38 @@ type AvContextFacts = {
|
|
|
36
37
|
*/
|
|
37
38
|
readonly $state: AvState;
|
|
38
39
|
};
|
|
39
|
-
/**
|
|
40
|
-
* `ctx.$sql`: a developer SQL statement, written only as a tagged template —
|
|
41
|
-
* ``await ctx.$sql<Row>`insert into audit (actor) values (${actorId})` `` — run at
|
|
42
|
-
* once. Every interpolated value is bound as a parameter, never spliced into the
|
|
43
|
-
* text. It runs in the current transaction; outside one, as a plain statement,
|
|
44
|
-
* with no transaction opened around it. Resolves the rows the statement returned.
|
|
45
|
-
*/
|
|
46
|
-
export type AvContextSql = <Row = unknown>(strings: TemplateStringsArray, ...values: readonly AvAdapterSqlValue[]) => Promise<Row[]>;
|
|
47
40
|
/**
|
|
48
41
|
* The arguments a variant-level stage is told: the operation's arguments under
|
|
49
42
|
* the model's Application Contract view, in the application forms, a reference
|
|
50
43
|
* position admitting an unresolved `$ref`. `unknown` for a model whose Resource
|
|
51
44
|
* names are not literal.
|
|
52
45
|
*/
|
|
53
|
-
export type AvStageArguments<M extends AvAdapterModel, R extends string, F extends AvOperationFamily, V extends string> =
|
|
46
|
+
export type AvStageArguments<M extends AvAdapterModel, R extends string, F extends AvOperationFamily, V extends string> = StageArgumentsOf<AvContractApplicationView<M>, R, F, V>;
|
|
47
|
+
/** The arguments of operation `F`.`V` on `R` under the Contract view `C`. */
|
|
48
|
+
type StageArgumentsOf<C extends AvContract, R extends string, F extends AvOperationFamily, V extends string> = string extends R ? unknown : R extends AvResourceKey<C> ? AvOperationArgumentsFor<C, R, Extract<AvOperationIdentity, {
|
|
54
49
|
readonly family: F;
|
|
55
50
|
readonly variant: V;
|
|
56
51
|
}>, AvApplicationScalarForms, AvApplicationScalarForms> : unknown;
|
|
52
|
+
/**
|
|
53
|
+
* The arguments a variant's guards, pipes and filters are told: the operation's
|
|
54
|
+
* arguments before write compute has run, so a create need not hold a field
|
|
55
|
+
* `compute.create` fills (`Lift`, as `"<Resource>.<field>"`; `string`: any).
|
|
56
|
+
*/
|
|
57
|
+
export type AvPreComputeStageArguments<M extends AvAdapterModel, R extends string, F extends AvOperationFamily, V extends string, Lift extends string = AvPreComputeLift<M, "root">> = F extends "find" | "delete" ? AvStageArguments<M, R, F, V> : [Lift] extends [never] ? AvStageArguments<M, R, F, V> : StageArgumentsOf<PreComputeView<AvContractApplicationView<M>, Lift>, R, F, V>;
|
|
58
|
+
/** A Contract view with the create requirements `Lift` names lifted. */
|
|
59
|
+
type PreComputeView<C, Lift extends string> = Extract<{
|
|
60
|
+
readonly [P in keyof C]: P extends "resources" ? string extends Lift ? {
|
|
61
|
+
readonly [R in keyof C[P]]: Omit<C[P][R], "createRequirements">;
|
|
62
|
+
} : {
|
|
63
|
+
readonly [R in keyof C[P]]: LiftedResource<C[P][R], LiftedFieldOf<Lift, R>>;
|
|
64
|
+
} : C[P];
|
|
65
|
+
}, AvContract>;
|
|
66
|
+
/** The fields of Resource `R` that `Lift` names. */
|
|
67
|
+
type LiftedFieldOf<Lift extends string, R> = Lift extends `${R & string}.${infer Field}` ? Field : never;
|
|
68
|
+
/** One Resource with the create requirements on `Fields` lifted; itself when none. */
|
|
69
|
+
type LiftedResource<Resource, Fields> = [Fields] extends [never] ? Resource : {
|
|
70
|
+
readonly [P in keyof Resource]: P extends "createRequirements" ? RequirementsLessComputed<Exclude<Resource[P], undefined>, Fields> : Resource[P];
|
|
71
|
+
};
|
|
57
72
|
/**
|
|
58
73
|
* What a pipeline callback is told about the operation it is wrapping, and the
|
|
59
74
|
* framework's executor: `ctx.<Resource>.<family>.<variant>(args)`, `ctx.$sql` and
|
|
@@ -83,11 +98,20 @@ export type AvContext<M extends AvAdapterModel = AvAdapterModel, R extends strin
|
|
|
83
98
|
* returned them in a pipe, and the validated arguments the operation executes in
|
|
84
99
|
* an interceptor, a hook or a filter. Only a pipe changes them, by returning new
|
|
85
100
|
* ones; writing to them throws a `TypeError`. At a variant, typed as that
|
|
86
|
-
* operation's arguments ({@link AvStageArguments}), deep read-only;
|
|
87
|
-
* `
|
|
101
|
+
* operation's arguments ({@link AvStageArguments}), deep read-only; in a guard or
|
|
102
|
+
* a filter a create may lack the fields `compute.create` fills ({@link
|
|
103
|
+
* AvPreComputeStageArguments}: write compute has not run); above it, `unknown`.
|
|
88
104
|
*/
|
|
89
105
|
readonly $args: AvDeepReadonly<A>;
|
|
90
|
-
|
|
106
|
+
/**
|
|
107
|
+
* A developer SQL statement, written only as a tagged template —
|
|
108
|
+
* ``await ctx.$sql<Row>`insert into audit (actor) values (${actorId})` ``
|
|
109
|
+
* — run at once. Every interpolated value is bound as a parameter, never
|
|
110
|
+
* spliced into the text. It runs in the current transaction; outside one,
|
|
111
|
+
* as a plain statement, with no transaction opened around it. Resolves
|
|
112
|
+
* the rows the statement returned.
|
|
113
|
+
*/
|
|
114
|
+
readonly $sql: <Row = unknown>(strings: TemplateStringsArray, ...values: readonly AvAdapterSqlValue[]) => Promise<Row[]>;
|
|
91
115
|
/**
|
|
92
116
|
* Runs `work` in a transaction, `tx` being this ctx bound to it, and resolves
|
|
93
117
|
* what `work` resolved; a throw rolls it back. Inside an open transaction it
|
|
@@ -103,22 +127,22 @@ export type AvGuard<M extends AvAdapterModel = AvAdapterModel, R extends string
|
|
|
103
127
|
* Transforms the arguments before the validators judge them: the only stage
|
|
104
128
|
* that changes them. `args` is deep-frozen, so a pipe returns new arguments;
|
|
105
129
|
* returning `undefined` keeps them as they are. At a variant, `args` and the
|
|
106
|
-
* return are typed as that operation's arguments
|
|
107
|
-
*
|
|
130
|
+
* return are typed as that operation's arguments before write compute runs
|
|
131
|
+
* ({@link AvPreComputeStageArguments}: a create may lack the fields
|
|
132
|
+
* `compute.create` fills), `args` deep read-only.
|
|
108
133
|
*/
|
|
109
134
|
export type AvPipe<M extends AvAdapterModel = AvAdapterModel, R extends string = AvConfigResourceKey<M>, F extends AvOperationFamily = AvOperationFamily, V extends string = string, A = unknown> = {
|
|
110
|
-
pipe(args: PipeArguments<A>, context: AvContext<M, R, F, V, A>):
|
|
135
|
+
pipe(args: PipeArguments<A>, context: AvContext<M, R, F, V, A>): unknown extends PipeArguments<A> ? any : PipeArguments<A> | undefined | void | Promise<PipeArguments<A> | undefined | void>;
|
|
111
136
|
}["pipe"];
|
|
112
137
|
/**
|
|
113
138
|
* A variant pipe's arguments and return: the operation's arguments, deep
|
|
114
139
|
* read-only, each member closed to inference.
|
|
115
140
|
*/
|
|
116
|
-
type PipeArguments<T> = T extends AvDeepReadonlyLeaf | AvTransactionReference ? T : T extends
|
|
141
|
+
type PipeArguments<T> = T extends AvDeepReadonlyLeaf | AvTransactionReference ? T : T extends readonly unknown[] ? ArrayForm<T> extends "array" ? readonly NoInfer<PipeArguments<Exclude<T[number], undefined>>>[] : {
|
|
142
|
+
readonly [K in keyof T]: NoInfer<PipeArguments<Exclude<T[K], undefined>>>;
|
|
143
|
+
} : T extends object ? {
|
|
117
144
|
readonly [K in keyof T]: NoInfer<PipeArguments<Exclude<T[K], undefined>>>;
|
|
118
145
|
} : T;
|
|
119
|
-
/** What a pipe returns: arguments of type `A`, or `undefined` to keep them. */
|
|
120
|
-
type PipeResult<A> = unknown extends A ? any : // biome-ignore lint/suspicious/noConfusingVoidType: a pipe written as `() => void sideEffect()` keeps the arguments, as `undefined` does
|
|
121
|
-
A | undefined | void | Promise<A | undefined | void>;
|
|
122
146
|
/** Runs just before the operation executes, inside every interceptor; `ctx.$args` is deep-frozen. */
|
|
123
147
|
export type AvBeforeHook<M extends AvAdapterModel = AvAdapterModel, R extends string = AvConfigResourceKey<M>, F extends AvOperationFamily = AvOperationFamily, V extends string = string, A = unknown> = {
|
|
124
148
|
before(context: AvContext<M, R, F, V, A>): void | Promise<void>;
|
|
@@ -180,7 +204,8 @@ export type AvTransactionStep<M extends AvAdapterModel = AvAdapterModel> = {
|
|
|
180
204
|
export type AvTransactionContext<M extends AvAdapterModel = AvAdapterModel> = AvContextOperations<M> & AvContextFacts & {
|
|
181
205
|
/** The plan, in step order, `$ref`s unresolved, deep-frozen. */
|
|
182
206
|
readonly $args: readonly AvTransactionStep<M>[];
|
|
183
|
-
|
|
207
|
+
/** As a stage's `ctx.$sql`: a developer SQL statement, run in this transaction. */
|
|
208
|
+
readonly $sql: <Row = unknown>(strings: TemplateStringsArray, ...values: readonly AvAdapterSqlValue[]) => Promise<Row[]>;
|
|
184
209
|
/** Runs `work` inside this transaction (it opens nothing): `tx` is this ctx. */
|
|
185
210
|
$transaction<T>(work: (tx: AvTransactionContext<M>) => Promise<T>): Promise<T>;
|
|
186
211
|
};
|
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
import { AvDecimal } from "./decimal.js";
|
|
1
|
+
import { type AvDecimal } from "./decimal.js";
|
|
2
2
|
/** A value `AvDeepReadonly` leaves as it is: a primitive, a function, or a scalar instance. */
|
|
3
3
|
export type AvDeepReadonlyLeaf = string | number | boolean | bigint | symbol | null | undefined | Date | Uint8Array | AvDecimal | ((...args: never[]) => unknown);
|
|
4
|
+
/**
|
|
5
|
+
* Whether array type `T` is an open array (every length: `"array"`) or a tuple
|
|
6
|
+
* (`"tuple"`). A tuple whose only fixed positions are optional ones before a rest
|
|
7
|
+
* element (`[a?, ...b[]]`) has every length too and reads as an array.
|
|
8
|
+
*/
|
|
9
|
+
export type ArrayForm<T extends readonly unknown[]> = T extends readonly [] | readonly [unknown, ...unknown[]] | readonly [...unknown[], unknown] ? "tuple" : T extends {
|
|
10
|
+
readonly length: infer Length;
|
|
11
|
+
} ? number extends Length ? "array" : "tuple" : "tuple";
|
|
4
12
|
/**
|
|
5
13
|
* `T` with every property, element and nested property read-only: what a stage
|
|
6
|
-
* that only reads is told. Arrays become read-only arrays
|
|
7
|
-
* `Uint8Array`, an `AvDecimal` and functions are left as
|
|
14
|
+
* that only reads is told. Arrays become read-only arrays, tuples read-only
|
|
15
|
+
* tuples; a `Date`, a `Uint8Array`, an `AvDecimal` and functions are left as
|
|
16
|
+
* they are.
|
|
8
17
|
*/
|
|
9
|
-
export type AvDeepReadonly<T> = T extends AvDeepReadonlyLeaf ? T : T extends
|
|
18
|
+
export type AvDeepReadonly<T> = T extends AvDeepReadonlyLeaf ? T : T extends readonly unknown[] ? ArrayForm<T> extends "array" ? readonly AvDeepReadonly<T[number]>[] : {
|
|
19
|
+
readonly [K in keyof T]: AvDeepReadonly<T[K]>;
|
|
20
|
+
} : T extends object ? {
|
|
10
21
|
readonly [K in keyof T]: AvDeepReadonly<T[K]>;
|
|
11
22
|
} : T;
|
|
12
23
|
/**
|
|
@@ -21,8 +32,12 @@ export declare function deepFreeze<T>(value: T): T;
|
|
|
21
32
|
* only reads is told — the validated arguments after validation, a result in an
|
|
22
33
|
* after hook — so neither what is written nor the result the caller receives can
|
|
23
34
|
* be changed through it.
|
|
35
|
+
*
|
|
36
|
+
* With `snapshot`, a copied container found to hold no `Date` or `Uint8Array`
|
|
37
|
+
* is remembered as such at once, so a later `frozenCopy` of the copy shares it
|
|
38
|
+
* without walking it: for a copy that is itself copied again for each stage.
|
|
24
39
|
*/
|
|
25
|
-
export declare function frozenCopy<T>(value: T): AvDeepReadonly<T>;
|
|
40
|
+
export declare function frozenCopy<T>(value: T, snapshot?: boolean): AvDeepReadonly<T>;
|
|
26
41
|
/**
|
|
27
42
|
* A read-only view of `value`: reads see the live value, nested plain objects
|
|
28
43
|
* and arrays as views too, and any write, definition or deletion throws a
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isAvDecimal } from "./decimal.js";
|
|
2
2
|
function isContainer(value) {
|
|
3
3
|
if (value === null || typeof value !== "object") {
|
|
4
4
|
return false;
|
|
@@ -6,7 +6,7 @@ function isContainer(value) {
|
|
|
6
6
|
if (Array.isArray(value)) {
|
|
7
7
|
return true;
|
|
8
8
|
}
|
|
9
|
-
if (value
|
|
9
|
+
if (isAvDecimal(value)) {
|
|
10
10
|
return false;
|
|
11
11
|
}
|
|
12
12
|
const prototype = Object.getPrototypeOf(value);
|
|
@@ -36,51 +36,99 @@ function scalarCopy(value) {
|
|
|
36
36
|
}
|
|
37
37
|
return undefined;
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
const SCALAR_FREE = new WeakSet();
|
|
40
|
+
function scalarFree(value) {
|
|
41
|
+
return Object.isFrozen(value) && SCALAR_FREE.has(value);
|
|
42
|
+
}
|
|
43
|
+
const WALKING = Symbol("walking");
|
|
44
|
+
function place(target, key, value, enumerable) {
|
|
45
|
+
if (enumerable && typeof key === "string" && !(key in target)) {
|
|
46
|
+
target[key] = value;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
Object.defineProperty(target, key, {
|
|
50
|
+
value,
|
|
51
|
+
enumerable,
|
|
52
|
+
writable: true,
|
|
53
|
+
configurable: true,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function frozenCopy(value, snapshot = false) {
|
|
57
|
+
const scalar = scalarCopy(value);
|
|
58
|
+
if (scalar !== undefined) {
|
|
59
|
+
return scalar;
|
|
60
|
+
}
|
|
61
|
+
if (!isContainer(value) || scalarFree(value)) {
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
40
64
|
const copies = new Map();
|
|
65
|
+
let cycled = false;
|
|
66
|
+
let holdsScalar = false;
|
|
41
67
|
const copy = (current) => {
|
|
68
|
+
if (current === null || typeof current !== "object") {
|
|
69
|
+
return current;
|
|
70
|
+
}
|
|
42
71
|
const scalar = scalarCopy(current);
|
|
43
72
|
if (scalar !== undefined) {
|
|
73
|
+
holdsScalar = true;
|
|
44
74
|
return scalar;
|
|
45
75
|
}
|
|
46
|
-
if (!isContainer(current)) {
|
|
76
|
+
if (!isContainer(current) || scalarFree(current)) {
|
|
47
77
|
return current;
|
|
48
78
|
}
|
|
49
|
-
|
|
50
|
-
|
|
79
|
+
const known = copies.get(current);
|
|
80
|
+
if (known === WALKING) {
|
|
81
|
+
cycled = true;
|
|
82
|
+
return current;
|
|
83
|
+
}
|
|
84
|
+
if (known !== undefined) {
|
|
85
|
+
holdsScalar ||= !scalarFree(known);
|
|
86
|
+
return known;
|
|
51
87
|
}
|
|
52
|
-
copies.set(current,
|
|
53
|
-
const
|
|
54
|
-
|
|
88
|
+
copies.set(current, WALKING);
|
|
89
|
+
const outer = holdsScalar;
|
|
90
|
+
holdsScalar = false;
|
|
91
|
+
let changed = !(Object.isFrozen(current) && DEEP_FROZEN.has(current));
|
|
92
|
+
const array = Array.isArray(current);
|
|
93
|
+
const prototype = Object.getPrototypeOf(current);
|
|
94
|
+
const target = array
|
|
95
|
+
? new Array(current.length)
|
|
96
|
+
: prototype === Object.prototype
|
|
97
|
+
? {}
|
|
98
|
+
: Object.create(prototype);
|
|
55
99
|
for (const key of Reflect.ownKeys(current)) {
|
|
100
|
+
if (array && key === "length") {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
56
103
|
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
57
|
-
if (descriptor === undefined ||
|
|
58
|
-
!("value" in descriptor) ||
|
|
59
|
-
(Array.isArray(current) && key === "length")) {
|
|
104
|
+
if (descriptor === undefined || !("value" in descriptor)) {
|
|
60
105
|
continue;
|
|
61
106
|
}
|
|
62
107
|
const next = copy(descriptor.value);
|
|
63
108
|
changed ||= next !== descriptor.value;
|
|
64
|
-
|
|
109
|
+
place(target, key, next, descriptor.enumerable === true);
|
|
65
110
|
}
|
|
111
|
+
const held = holdsScalar;
|
|
112
|
+
holdsScalar = outer || held;
|
|
66
113
|
if (!changed) {
|
|
67
114
|
copies.set(current, current);
|
|
115
|
+
if (!cycled) {
|
|
116
|
+
SCALAR_FREE.add(current);
|
|
117
|
+
}
|
|
68
118
|
return current;
|
|
69
119
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
});
|
|
120
|
+
Object.freeze(target);
|
|
121
|
+
DEEP_FROZEN.add(target);
|
|
122
|
+
if (cycled) {
|
|
123
|
+
for (const key of Reflect.ownKeys(target)) {
|
|
124
|
+
deepFreeze(target[key]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (snapshot && !held && !cycled) {
|
|
128
|
+
SCALAR_FREE.add(target);
|
|
80
129
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return frozen;
|
|
130
|
+
copies.set(current, target);
|
|
131
|
+
return target;
|
|
84
132
|
};
|
|
85
133
|
return copy(value);
|
|
86
134
|
}
|
|
@@ -29,7 +29,7 @@ export type ReferentialEdge = {
|
|
|
29
29
|
* relation `field` and any of these names the reference twice and is refused;
|
|
30
30
|
* a required relation may be written through all of them instead.
|
|
31
31
|
*/
|
|
32
|
-
readonly
|
|
32
|
+
readonly referenceFields: readonly string[];
|
|
33
33
|
/** What deleting a `target` row does to rows of the ENCLOSING Resource. */
|
|
34
34
|
readonly onDelete: ReferentialDeleteEffect;
|
|
35
35
|
};
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import type { AvRequestOrigin } from "../config/config-layer.js";
|
|
2
|
-
import type { AvScalarFilterOperator } from "../contracts/capability.js";
|
|
3
2
|
import type { AvContract } from "../contracts/contract.js";
|
|
4
3
|
import type { AvScalarField } from "../contracts/field.js";
|
|
5
4
|
import type { AvOperationIdentity } from "../operations/operation-identity.js";
|
|
6
5
|
import type { OperationValidationOptions, SemanticOperationArguments } from "../operations/operation-validator.js";
|
|
7
6
|
import type { AvValidationIssue } from "../operations/validation-code.js";
|
|
8
7
|
import { type TransactionSlotShape } from "../transactions/transaction-reference.js";
|
|
9
|
-
|
|
8
|
+
type ScalarNormalizationResult = {
|
|
10
9
|
readonly ok: true;
|
|
11
10
|
readonly arguments: SemanticOperationArguments;
|
|
12
11
|
} | {
|
|
13
12
|
readonly ok: false;
|
|
14
13
|
readonly issues: readonly AvValidationIssue[];
|
|
15
14
|
};
|
|
16
|
-
|
|
15
|
+
type ScalarValueNormalizationResult = {
|
|
17
16
|
readonly ok: true;
|
|
18
17
|
readonly value: unknown;
|
|
19
18
|
} | {
|
|
@@ -31,6 +30,8 @@ type Path = readonly (string | number)[];
|
|
|
31
30
|
* one reader and a value to another.
|
|
32
31
|
*/
|
|
33
32
|
export declare function isPlainRecord(value: unknown): value is Readonly<Record<string, unknown>>;
|
|
33
|
+
/** The `V1001` of an `undefined` the request reads with. */
|
|
34
|
+
export declare function unsentReadValueIssue(path: Path): AvValidationIssue;
|
|
34
35
|
/**
|
|
35
36
|
* An operation argument's JSON value is bounded by `maxJsonDepth`, which the
|
|
36
37
|
* structural walk enforces before this runs; a `$sql` step's parameter is not
|
|
@@ -71,14 +72,6 @@ export declare function jsonShapeIssues(field: AvScalarField<AvContract>, value:
|
|
|
71
72
|
* its copy.
|
|
72
73
|
*/
|
|
73
74
|
export declare function copyScalarFieldValue(field: AvScalarField<AvContract>, value: unknown): unknown;
|
|
74
|
-
/**
|
|
75
|
-
* The operators of a step's own filter whose operand is a reference position, and
|
|
76
|
-
* the shape each holds — the one table the normalization below and the reference
|
|
77
|
-
* collector (`transaction-reference-collector.ts`) both read. `not` also takes a
|
|
78
|
-
* nested operator object. Not here, so never a reference: `mode`, `isEmpty`, `path`
|
|
79
|
-
* and a `json` field's `string*`/`array*` operators, which take no field value.
|
|
80
|
-
*/
|
|
81
|
-
export declare const REFERENCE_OPERANDS: Readonly<Partial<Record<AvScalarFilterOperator, TransactionSlotShape>>>;
|
|
82
75
|
/** The shape of `operator`'s operand when it is a reference position. */
|
|
83
76
|
export declare function referenceOperandShape(operator: string): TransactionSlotShape | undefined;
|
|
84
77
|
/**
|
|
@@ -1,13 +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";
|
|
6
9
|
const INT_MIN = -(2 ** 31);
|
|
7
10
|
const INT_MAX = 2 ** 31 - 1;
|
|
8
11
|
const BIGINT_MIN = -(2n ** 63n);
|
|
9
12
|
const BIGINT_MAX = 2n ** 63n - 1n;
|
|
10
13
|
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;
|
|
14
|
+
const NAMED_FORMATS = new Set(AVZ_FORMATS);
|
|
15
|
+
function isNamedFormat(format) {
|
|
16
|
+
return NAMED_FORMATS.has(format);
|
|
17
|
+
}
|
|
11
18
|
function isRecord(value) {
|
|
12
19
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13
20
|
}
|
|
@@ -24,6 +31,10 @@ function issue(code, path, message) {
|
|
|
24
31
|
function typeIssue(path, expected) {
|
|
25
32
|
return issue("V1001", path, `Value must be ${expected}.`);
|
|
26
33
|
}
|
|
34
|
+
const UNSENT_READ_VALUE_MESSAGE = "Value must not be undefined: it would be left out, and the request would reach more rows.";
|
|
35
|
+
export function unsentReadValueIssue(path) {
|
|
36
|
+
return issue("V1001", path, UNSENT_READ_VALUE_MESSAGE);
|
|
37
|
+
}
|
|
27
38
|
function formatIssue(path, expected) {
|
|
28
39
|
return issue("V1003", path, `Value must use ${expected}.`);
|
|
29
40
|
}
|
|
@@ -83,18 +94,14 @@ export function normalizeJsonValue(value, path, seen) {
|
|
|
83
94
|
const normalized = [];
|
|
84
95
|
task.emit(normalized);
|
|
85
96
|
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];
|
|
97
|
+
for (let index = current.length - 1; index >= 0; index -= 1) {
|
|
98
|
+
const entry = current[index];
|
|
92
99
|
stack.push({
|
|
93
100
|
kind: "visit",
|
|
94
|
-
value: entry
|
|
95
|
-
link: { parent: task.link, segment:
|
|
101
|
+
value: entry === undefined ? null : entry,
|
|
102
|
+
link: { parent: task.link, segment: index },
|
|
96
103
|
emit: (child) => {
|
|
97
|
-
normalized
|
|
104
|
+
normalized[index] = child;
|
|
98
105
|
},
|
|
99
106
|
});
|
|
100
107
|
}
|
|
@@ -112,7 +119,7 @@ export function normalizeJsonValue(value, path, seen) {
|
|
|
112
119
|
const normalized = {};
|
|
113
120
|
task.emit(normalized);
|
|
114
121
|
stack.push({ kind: "leave", container: current });
|
|
115
|
-
const entries = Object.entries(current);
|
|
122
|
+
const entries = Object.entries(current).filter((entry) => entry[1] !== undefined);
|
|
116
123
|
for (let cursor = entries.length - 1; cursor >= 0; cursor -= 1) {
|
|
117
124
|
const entry = entries[cursor];
|
|
118
125
|
stack.push({
|
|
@@ -231,10 +238,9 @@ function normalizeScalarAtom(contract, field, value, origin, path, allowNull) {
|
|
|
231
238
|
break;
|
|
232
239
|
case "decimal":
|
|
233
240
|
if (origin === "application") {
|
|
234
|
-
normalized =
|
|
235
|
-
value
|
|
236
|
-
|
|
237
|
-
: { ok: false, issues: [typeIssue(path, "an Aventara Decimal")] };
|
|
241
|
+
normalized = isAvDecimal(value)
|
|
242
|
+
? { ok: true, value }
|
|
243
|
+
: { ok: false, issues: [typeIssue(path, "an Aventara Decimal")] };
|
|
238
244
|
}
|
|
239
245
|
else if (typeof value !== "string") {
|
|
240
246
|
normalized = {
|
|
@@ -359,9 +365,21 @@ export function normalizeScalarFieldValue(contract, field, value, origin, path)
|
|
|
359
365
|
});
|
|
360
366
|
return issues.length > 0
|
|
361
367
|
? { ok: false, issues }
|
|
362
|
-
: { ok: true, value: normalized };
|
|
368
|
+
: heldToFormat(field, { ok: true, value: normalized }, origin, path);
|
|
363
369
|
}
|
|
364
|
-
return normalizeScalarAtom(contract, field, value, origin === "runtime" ? "application" : origin, path, field.nullable);
|
|
370
|
+
return heldToFormat(field, normalizeScalarAtom(contract, field, value, origin === "runtime" ? "application" : origin, path, field.nullable), origin, path);
|
|
371
|
+
}
|
|
372
|
+
function heldToFormat(field, normalized, origin, path) {
|
|
373
|
+
const format = field.format;
|
|
374
|
+
if (origin !== "runtime" ||
|
|
375
|
+
!normalized.ok ||
|
|
376
|
+
format === undefined ||
|
|
377
|
+
format === "uuid" ||
|
|
378
|
+
!isNamedFormat(format)) {
|
|
379
|
+
return normalized;
|
|
380
|
+
}
|
|
381
|
+
const issues = fieldCheckIssues([{ check: "string_format", format }], normalized.value, "string", path, field.list);
|
|
382
|
+
return issues.length === 0 ? normalized : { ok: false, issues };
|
|
365
383
|
}
|
|
366
384
|
export function jsonShapeIssues(field, value, path, position = "value") {
|
|
367
385
|
if (field.json === undefined || value === null || value === undefined) {
|
|
@@ -427,6 +445,11 @@ export function copyScalarFieldValue(field, value) {
|
|
|
427
445
|
function normalizeListElement(contract, field, value, origin, path) {
|
|
428
446
|
return normalizeScalarAtom(contract, field, value, origin, path, false);
|
|
429
447
|
}
|
|
448
|
+
function normalizeFilterEntry(contract, field, value, origin, path) {
|
|
449
|
+
return value === undefined
|
|
450
|
+
? { ok: false, issues: [unsentReadValueIssue(path)] }
|
|
451
|
+
: normalizeListElement(contract, field, value, origin, path);
|
|
452
|
+
}
|
|
430
453
|
function mergeResult(result, issues) {
|
|
431
454
|
if (result.ok) {
|
|
432
455
|
return result.value;
|
|
@@ -434,7 +457,7 @@ function mergeResult(result, issues) {
|
|
|
434
457
|
issues.push(...result.issues);
|
|
435
458
|
return undefined;
|
|
436
459
|
}
|
|
437
|
-
|
|
460
|
+
const REFERENCE_OPERANDS = Object.freeze({
|
|
438
461
|
equals: "value",
|
|
439
462
|
not: "value",
|
|
440
463
|
lt: "nonNullValue",
|
|
@@ -463,10 +486,40 @@ const NULL_OPERAND_OPERATORS = new Set([
|
|
|
463
486
|
"arrayStartsWith",
|
|
464
487
|
"arrayEndsWith",
|
|
465
488
|
]);
|
|
489
|
+
const KEYWORD_OPERANDS = Object.freeze({
|
|
490
|
+
mode: (operand, path, issues) => {
|
|
491
|
+
if (!FILTER_MODES.includes(operand)) {
|
|
492
|
+
issues.push(typeIssue(path, '"default", "sensitive" or "insensitive"'));
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
isEmpty: (operand, path, issues) => {
|
|
496
|
+
if (typeof operand !== "boolean") {
|
|
497
|
+
issues.push(typeIssue(path, "a boolean"));
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
path: (operand, path, issues) => {
|
|
501
|
+
if (!Array.isArray(operand)) {
|
|
502
|
+
issues.push(typeIssue(path, "an array of strings"));
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
for (let index = 0; index < operand.length; index += 1) {
|
|
506
|
+
if (operand[index] === undefined) {
|
|
507
|
+
issues.push(unsentReadValueIssue([...path, index]));
|
|
508
|
+
}
|
|
509
|
+
else if (typeof operand[index] !== "string") {
|
|
510
|
+
issues.push(typeIssue([...path, index], "a string"));
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
});
|
|
466
515
|
function normalizeScalarFilter(contract, field, value, origin, path, issues, references = false) {
|
|
467
516
|
if (isKeptReference(references, value)) {
|
|
468
517
|
return value;
|
|
469
518
|
}
|
|
519
|
+
if (value === undefined) {
|
|
520
|
+
issues.push(unsentReadValueIssue(path));
|
|
521
|
+
return value;
|
|
522
|
+
}
|
|
470
523
|
if (!isPlainRecord(value)) {
|
|
471
524
|
return mergeResult(normalizeScalarFieldValue(contract, field, value, origin, path), issues);
|
|
472
525
|
}
|
|
@@ -477,11 +530,17 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
|
|
|
477
530
|
const normalized = cloneRecord(value);
|
|
478
531
|
for (const [operator, operand] of Object.entries(value)) {
|
|
479
532
|
const operandPath = [...path, operator];
|
|
533
|
+
if (operand === undefined) {
|
|
534
|
+
issues.push(unsentReadValueIssue(operandPath));
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
480
537
|
if (operand === null && !acceptsNullOperand(operator)) {
|
|
481
538
|
issues.push(typeIssue(operandPath, "non-null"));
|
|
482
539
|
continue;
|
|
483
540
|
}
|
|
484
|
-
|
|
541
|
+
const keyword = lookupOwn(KEYWORD_OPERANDS, operator);
|
|
542
|
+
if (keyword !== undefined) {
|
|
543
|
+
keyword(operand, operandPath, issues);
|
|
485
544
|
continue;
|
|
486
545
|
}
|
|
487
546
|
if (referenceOperandShape(operator) !== undefined &&
|
|
@@ -497,14 +556,14 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
|
|
|
497
556
|
issues.push(typeIssue(operandPath, "an array"));
|
|
498
557
|
continue;
|
|
499
558
|
}
|
|
500
|
-
normalized[operator] = operand.map((entry, index) => mergeResult(
|
|
559
|
+
normalized[operator] = operand.map((entry, index) => mergeResult(normalizeFilterEntry(contract, field, entry, origin, [
|
|
501
560
|
...operandPath,
|
|
502
561
|
index,
|
|
503
562
|
]), issues));
|
|
504
563
|
continue;
|
|
505
564
|
}
|
|
506
565
|
if (operator === "has") {
|
|
507
|
-
normalized[operator] = mergeResult(
|
|
566
|
+
normalized[operator] = mergeResult(normalizeFilterEntry(contract, field, operand, origin, operandPath), issues);
|
|
508
567
|
continue;
|
|
509
568
|
}
|
|
510
569
|
if (operator === "hasEvery" || operator === "hasSome") {
|
|
@@ -512,7 +571,7 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
|
|
|
512
571
|
issues.push(typeIssue(operandPath, "an array"));
|
|
513
572
|
continue;
|
|
514
573
|
}
|
|
515
|
-
normalized[operator] = operand.map((entry, index) => mergeResult(
|
|
574
|
+
normalized[operator] = operand.map((entry, index) => mergeResult(normalizeFilterEntry(contract, field, entry, origin, [
|
|
516
575
|
...operandPath,
|
|
517
576
|
index,
|
|
518
577
|
]), issues));
|
|
@@ -521,6 +580,12 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
|
|
|
521
580
|
if ("scalar" in field.type &&
|
|
522
581
|
field.type.scalar === "json" &&
|
|
523
582
|
(operator.startsWith("string") || operator.startsWith("array"))) {
|
|
583
|
+
if (operator.startsWith("string")) {
|
|
584
|
+
if (typeof operand !== "string") {
|
|
585
|
+
issues.push(typeIssue(operandPath, "a string"));
|
|
586
|
+
}
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
524
589
|
const jsonResult = normalizeJsonValue(operand, operandPath, new Set());
|
|
525
590
|
normalized[operator] = mergeResult(jsonResult, issues);
|
|
526
591
|
continue;
|
|
@@ -585,6 +650,10 @@ function normalizeUniqueSelector(contract, resourceName, selector, origin, path,
|
|
|
585
650
|
const normalized = cloneRecord(selector);
|
|
586
651
|
for (const [fieldName, value] of Object.entries(selector)) {
|
|
587
652
|
const field = lookupField(resource, fieldName);
|
|
653
|
+
if (field?.kind === "scalar" && value === undefined) {
|
|
654
|
+
issues.push(unsentReadValueIssue([...path, fieldName]));
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
588
657
|
if (field?.kind === "scalar" && !isKeptReference(references, value)) {
|
|
589
658
|
normalized[fieldName] = mergeResult(normalizeScalarFieldValue(contract, field, value, origin, [
|
|
590
659
|
...path,
|
|
@@ -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;
|