@aventara/core 0.1.0-pilot.1 → 0.1.0-pilot.2
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/dist/adapters/adapter-field.d.ts +4 -4
- package/dist/adapters/adapter-resource.d.ts +10 -12
- package/dist/adapters/adapter.interface.d.ts +20 -10
- package/dist/compiler/contract-compiler-diagnostic.d.ts +3 -4
- package/dist/compiler/contract.compiler.d.ts +5 -4
- package/dist/compiler/contract.compiler.js +0 -132
- package/dist/config/config.resolver.d.ts +2 -2
- package/dist/config/config.resolver.js +0 -5
- package/dist/config/config.validator.d.ts +3 -3
- package/dist/config/config.validator.js +0 -8
- package/dist/config/define-fields.d.ts +2 -7
- package/dist/config/define-fields.js +0 -15
- package/dist/config/effective-fields.d.ts +4 -8
- package/dist/config/entrypoint.normalizer.d.ts +7 -7
- package/dist/config/entrypoint.normalizer.js +0 -22
- package/dist/config/framework-behaviors-config.d.ts +6 -6
- package/dist/config/framework-config.d.ts +3 -5
- package/dist/config/framework-restrictions-config.d.ts +19 -29
- package/dist/config/pipeline-config.d.ts +8 -18
- package/dist/contracts/capability.d.ts +18 -21
- package/dist/contracts/capability.js +0 -75
- package/dist/contracts/client-contract-structure.validator.d.ts +15 -22
- package/dist/contracts/client-contract-structure.validator.js +0 -27
- package/dist/contracts/contract-canonicalizer.js +0 -96
- package/dist/contracts/contract-hash.js +0 -2
- package/dist/contracts/contract-limits.js +0 -1
- package/dist/contracts/contract-lookup.js +0 -10
- package/dist/contracts/contract-protocol.js +0 -10
- package/dist/contracts/field.d.ts +23 -26
- package/dist/contracts/field.js +0 -107
- package/dist/contracts/lifecycle-trait.js +0 -5
- package/dist/contracts/resource-operations.d.ts +17 -18
- package/dist/contracts/resource-operations.js +0 -63
- package/dist/contracts/scalar-value-type.d.ts +12 -22
- package/dist/contracts/scalar.js +0 -1
- package/dist/contracts/wire-scalar-format.d.ts +20 -20
- package/dist/contracts/wire-scalar-format.js +0 -26
- package/dist/framework/contract-view.d.ts +7 -8
- package/dist/framework/framework-construction.error.js +0 -1
- package/dist/framework/framework.d.ts +26 -27
- package/dist/framework/framework.factory.js +0 -49
- package/dist/framework/operation-facade.js +0 -17
- package/dist/operations/operation-arguments.d.ts +36 -46
- package/dist/operations/operation-boolean-nodes.js +0 -16
- package/dist/operations/operation-call.d.ts +4 -9
- package/dist/operations/operation-identity.js +0 -1
- package/dist/operations/operation-nesting.js +0 -5
- package/dist/operations/operation-result.d.ts +4 -4
- package/dist/operations/operation-validator-core.d.ts +2 -2
- package/dist/operations/operation-validator-core.js +6 -71
- package/dist/operations/operation-validator.d.ts +4 -4
- package/dist/operations/operation-validator.js +0 -12
- package/dist/operations/projection-scope.resolver.d.ts +8 -10
- package/dist/operations/projection-scope.resolver.js +0 -35
- package/dist/operations/validation-code.d.ts +11 -18
- package/dist/operations/validation-code.js +0 -24
- package/dist/operations/write-scope.resolver.d.ts +4 -7
- package/dist/operations/write-scope.resolver.js +0 -37
- package/dist/protocol/absent-route.responder.d.ts +33 -48
- package/dist/protocol/absent-route.responder.js +0 -55
- package/dist/protocol/av-protocol.d.ts +6 -6
- package/dist/protocol/av-protocol.js +0 -10
- package/dist/protocol/contract-document.responder.d.ts +15 -18
- package/dist/protocol/contract-document.responder.js +0 -33
- package/dist/protocol/http-request.decoder.d.ts +55 -61
- package/dist/protocol/http-request.decoder.js +0 -62
- package/dist/protocol/http-request.reader.d.ts +12 -13
- package/dist/protocol/http-request.reader.js +0 -45
- package/dist/protocol/http-response.encoder.d.ts +17 -18
- package/dist/protocol/http-response.encoder.js +0 -59
- package/dist/protocol/http-status.table.d.ts +7 -11
- package/dist/protocol/http-status.table.js +1 -15
- package/dist/protocol/index.d.ts +5 -5
- package/dist/protocol/index.js +0 -9
- package/dist/protocol/operation-response.guard.d.ts +12 -19
- package/dist/protocol/operation-response.guard.js +0 -29
- package/dist/protocol/protocol-failure.d.ts +21 -26
- package/dist/protocol/protocol-failure.js +0 -12
- package/dist/protocol/protocol-headers.d.ts +25 -30
- package/dist/protocol/protocol-headers.js +0 -66
- package/dist/protocol/protocol-surface.d.ts +25 -29
- package/dist/protocol/protocol-surface.js +0 -19
- package/dist/protocol/protocol.binder.d.ts +58 -71
- package/dist/protocol/protocol.binder.js +0 -8
- package/dist/protocol/protocol.handler.d.ts +28 -32
- package/dist/protocol/protocol.handler.js +0 -43
- package/dist/runtime/computed-read-runtime.js +0 -29
- package/dist/runtime/create-requirement.d.ts +65 -0
- package/dist/runtime/create-requirement.js +141 -0
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +0 -8
- package/dist/runtime/diagnostics-reporter.d.ts +6 -6
- package/dist/runtime/diagnostics-reporter.js +0 -35
- package/dist/runtime/error-boundary.d.ts +3 -3
- package/dist/runtime/error-boundary.js +2 -167
- package/dist/runtime/execution-options.d.ts +4 -4
- package/dist/runtime/field-behavior.d.ts +6 -9
- package/dist/runtime/framework-error.js +0 -4
- package/dist/runtime/mutation-planner.d.ts +5 -6
- package/dist/runtime/mutation-planner.js +0 -23
- package/dist/runtime/operation-response.d.ts +1 -1
- package/dist/runtime/operation-response.js +1 -17
- package/dist/runtime/operation-runtime.d.ts +24 -22
- package/dist/runtime/operation-runtime.js +18 -52
- package/dist/runtime/outcome-mapper.js +0 -22
- package/dist/runtime/output-validator.js +0 -1
- package/dist/runtime/pipe-output.validator.d.ts +48 -0
- package/dist/runtime/pipe-output.validator.js +79 -0
- package/dist/runtime/pipeline.d.ts +20 -26
- package/dist/runtime/referential-action.d.ts +14 -19
- package/dist/runtime/referential-action.js +0 -23
- package/dist/runtime/request-id.js +0 -21
- package/dist/runtime/scalar-runtime.d.ts +14 -16
- package/dist/runtime/scalar-runtime.js +12 -57
- package/dist/transactions/deferred-operation.d.ts +9 -14
- package/dist/transactions/operation-arguments-encoder.d.ts +0 -1
- package/dist/transactions/operation-arguments-encoder.js +0 -14
- package/dist/transactions/operation-fingerprint.js +0 -1
- package/dist/transactions/transaction-envelope.js +0 -14
- package/dist/transactions/transaction-integrity-error.d.ts +10 -11
- package/dist/transactions/transaction-integrity-error.js +0 -5
- package/dist/transactions/transaction-plan-assembler.d.ts +0 -1
- package/dist/transactions/transaction-plan-assembler.js +0 -35
- package/dist/transactions/transaction-plan-validator.d.ts +5 -5
- package/dist/transactions/transaction-plan-validator.js +0 -63
- package/dist/transactions/transaction-reference-resolver.d.ts +12 -13
- package/dist/transactions/transaction-reference-resolver.js +0 -16
- package/dist/transactions/transaction-reference-validator.js +0 -8
- package/dist/transactions/transaction-reference.d.ts +19 -43
- package/dist/transactions/transaction-reference.js +0 -11
- package/dist/transactions/transaction-request.d.ts +4 -5
- package/dist/transactions/transaction-response.d.ts +1 -1
- package/dist/transactions/transaction-runtime.d.ts +2 -3
- package/dist/transactions/transaction-runtime.js +5 -44
- package/dist/transactions/transaction-slot-path-error.d.ts +10 -11
- package/dist/transactions/transaction-slot-path-error.js +0 -7
- package/dist/transactions/transaction-slot-writer.d.ts +12 -12
- package/dist/transactions/transaction-slot-writer.js +0 -34
- package/package.json +2 -2
|
@@ -11,19 +11,19 @@ export type Framework<M extends AdapterModel = AdapterModel, Cfg = AnyFrameworkC
|
|
|
11
11
|
readonly entrypoint: string;
|
|
12
12
|
readonly contracts: FrameworkContracts;
|
|
13
13
|
/**
|
|
14
|
-
* The typed in-process façade:
|
|
14
|
+
* The typed in-process façade:
|
|
15
|
+
* `framework.application.<resource>.<family>.<variant>(args)`.
|
|
15
16
|
*
|
|
16
|
-
* Arguments and results are derived from the Application Contract's view of
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* types (ADR 0009).
|
|
17
|
+
* Arguments and results are derived from the Application Contract's view of `M`
|
|
18
|
+
* and `Cfg` (`ApplicationContractView`), so this is the member that reads the
|
|
19
|
+
* pair: two configurations that differ in one `fields` entry give two different
|
|
20
|
+
* `Framework` types. Each call is an application-scope request to {@link execute}
|
|
21
|
+
* — the same totality guard and validation layer — which still refuses what a
|
|
22
|
+
* restriction removed, since restrictions stay invisible to the types.
|
|
23
23
|
*
|
|
24
24
|
* A call's optional second argument is its `ExecutionOptions`: a supplied
|
|
25
|
-
* `requestId` is the request's, in every pipeline context and diagnostic;
|
|
26
|
-
*
|
|
25
|
+
* `requestId` is the request's, in every pipeline context and diagnostic; omitted
|
|
26
|
+
* or empty, a fresh id is minted per call.
|
|
27
27
|
*/
|
|
28
28
|
readonly application: ApplicationOperations<M, Cfg>;
|
|
29
29
|
/**
|
|
@@ -58,33 +58,32 @@ export type Framework<M extends AdapterModel = AdapterModel, Cfg = AnyFrameworkC
|
|
|
58
58
|
/**
|
|
59
59
|
* Validates a whole transaction plan, then runs its operations strictly
|
|
60
60
|
* sequentially inside one Adapter transaction. Answers `A1009` with each
|
|
61
|
-
* operation's final data in plan order, the failing operation's own code
|
|
62
|
-
*
|
|
63
|
-
*
|
|
61
|
+
* operation's final data in plan order, the failing operation's own code with
|
|
62
|
+
* `cause.operation` after rollback, or `A3002` when the transaction infrastructure
|
|
63
|
+
* fails. Total: it resolves for every input.
|
|
64
64
|
*
|
|
65
|
-
* Every operation's guards, pipes, hooks and interceptors run while the
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
65
|
+
* Every operation's guards, pipes, hooks and interceptors run while the provider
|
|
66
|
+
* transaction is open and must not perform network I/O or unrelated slow work. The
|
|
67
|
+
* Adapter must reject with the exact value its transaction callback threw, by
|
|
68
|
+
* identity.
|
|
69
69
|
*/
|
|
70
70
|
executeTransaction(request: FrameworkTransactionRequest): Promise<TransactionResponse>;
|
|
71
71
|
/**
|
|
72
72
|
* The one typed transaction runner: `framework.transaction([h1, h2, …])`.
|
|
73
73
|
*
|
|
74
74
|
* Binds each `$ref` to its source's position in this list, fingerprints every
|
|
75
|
-
* step, and runs the resulting passive plan through the same validation layer
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
75
|
+
* step, and runs the resulting passive plan through the same validation layer and
|
|
76
|
+
* transaction runtime as {@link executeTransaction} — each step under its own
|
|
77
|
+
* handle's origin, its own layer's Contract and pipelines, inside one Adapter
|
|
78
|
+
* transaction. The committed response is typed per position.
|
|
79
79
|
*
|
|
80
80
|
* Refused before anything runs: an entry that is not one of this Framework's
|
|
81
|
-
* handles, or the same handle twice (`A2004`/`V1001`); a `$ref` to a handle not
|
|
82
|
-
*
|
|
81
|
+
* handles, or the same handle twice (`A2004`/`V1001`); a `$ref` to a handle not in
|
|
82
|
+
* the list (`A2007`/`V1010`). Total: it resolves for every input.
|
|
83
83
|
*
|
|
84
|
-
* The optional second argument is the run's `ExecutionOptions`: its
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* (Q14).
|
|
84
|
+
* The optional second argument is the run's `ExecutionOptions`: its `requestId` is
|
|
85
|
+
* every step's, under either origin; omitted or empty, one id is minted for the
|
|
86
|
+
* run. Handles take no options — a handle is not a request.
|
|
88
87
|
*/
|
|
89
88
|
readonly transaction: TransactionRunner;
|
|
90
89
|
};
|
|
@@ -10,26 +10,6 @@ import { createTransactionPlanAssembler } from "../transactions/transaction-plan
|
|
|
10
10
|
import { createTransactionRuntime } from "../transactions/transaction-runtime.js";
|
|
11
11
|
import { FrameworkConstructionError } from "./framework-construction.error.js";
|
|
12
12
|
import { createOperationFacade, createOperationTree, } from "./operation-facade.js";
|
|
13
|
-
/**
|
|
14
|
-
* Validate, read the model, resolve, compile, and bind one Aventara Framework
|
|
15
|
-
* instance.
|
|
16
|
-
*
|
|
17
|
-
* The parameter is deliberately `Cfg & FrameworkConfig<M, Cfg>` rather than
|
|
18
|
-
* the obvious `FrameworkConfig<M, Cfg>`. `Cfg` has to appear somewhere the
|
|
19
|
-
* compiler actually infers from; inside `FrameworkConfig`'s body it only ever
|
|
20
|
-
* reaches non-inference positions, so it would fall back to its
|
|
21
|
-
* `AnyFrameworkConfig` default with no error reported anywhere, and every
|
|
22
|
-
* narrowing keyed on the authored configuration would quietly widen. The
|
|
23
|
-
* intersection puts `Cfg` directly against the argument type.
|
|
24
|
-
*
|
|
25
|
-
* `Cfg` is a `const` type parameter for the same reason: it stands for the
|
|
26
|
-
* configuration *as authored*. Without `const`, inferring `Cfg` from an
|
|
27
|
-
* inline configuration literal widens its scalar references away from their
|
|
28
|
-
* literal types, and the widened `Cfg` then loses to the intersection's own
|
|
29
|
-
* `FrameworkConfig` half — a call site that compiles today stops compiling.
|
|
30
|
-
* A configuration passed as an already-annotated variable is unaffected
|
|
31
|
-
* either way.
|
|
32
|
-
*/
|
|
33
13
|
export async function createFramework(config) {
|
|
34
14
|
const configDiagnostics = validateFrameworkConfig(config);
|
|
35
15
|
if (configDiagnostics.length > 0) {
|
|
@@ -62,8 +42,6 @@ export async function createFramework(config) {
|
|
|
62
42
|
config: resolvedConfig,
|
|
63
43
|
});
|
|
64
44
|
const executeOperation = (request) => executeTotally(execute, request, resolvedConfig.diagnostics);
|
|
65
|
-
// A client-origin call in process carries the server's own protocol
|
|
66
|
-
// identity: there is no remote generated client whose hash could be stale.
|
|
67
45
|
const protocol = {
|
|
68
46
|
version: AVENTARA_PROTOCOL_VERSION,
|
|
69
47
|
hash: compilation.contracts.client.protocol.hash,
|
|
@@ -91,23 +69,11 @@ export async function createFramework(config) {
|
|
|
91
69
|
clientTx: createOperationTree(model, (resource, identity) => (args) => assembler.defer("client", resource, identity, args === undefined ? {} : args)),
|
|
92
70
|
execute: executeOperation,
|
|
93
71
|
executeTransaction: (request) => executeTransactionTotally(executeTransaction, request, resolvedConfig.diagnostics),
|
|
94
|
-
// The result type is the handles' derivation, an authoring affordance
|
|
95
|
-
// (ADR 0009): every slot was held to its own Contract by output validation.
|
|
96
72
|
transaction: ((operations, options) => runTransactionTotally(executeTransaction, assembler, operations, requestIdOf(options), resolvedConfig.diagnostics)),
|
|
97
73
|
};
|
|
98
74
|
recordDiagnosticsSink(framework, resolvedConfig.diagnostics);
|
|
99
75
|
return framework;
|
|
100
76
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Outermost totality guard on the public execution seam. The validation
|
|
103
|
-
* boundary and the pipeline boundary do the real work; this covers only what
|
|
104
|
-
* they cannot — a failure inside those boundaries themselves. It reads
|
|
105
|
-
* nothing off the caught value and calls no sanitizer: the envelope is a
|
|
106
|
-
* constant, so this guard is incapable of failing. Reporting the raw error
|
|
107
|
-
* to the diagnostics channel preserves that property, because the reporter
|
|
108
|
-
* is total by construction. `Framework.execute()` therefore resolves to a
|
|
109
|
-
* valid OperationResponse for every input.
|
|
110
|
-
*/
|
|
111
77
|
async function executeTotally(execute, request, diagnostics) {
|
|
112
78
|
const report = createDiagnosticsReporter(diagnostics, request);
|
|
113
79
|
try {
|
|
@@ -118,12 +84,6 @@ async function executeTotally(execute, request, diagnostics) {
|
|
|
118
84
|
return internalFailure();
|
|
119
85
|
}
|
|
120
86
|
}
|
|
121
|
-
/**
|
|
122
|
-
* Outermost totality guard for the transport-independent transaction seam.
|
|
123
|
-
* The channel it binds is the only diagnostics route of the plan: the guard
|
|
124
|
-
* reports through its plan-level reporter, and the executor receives the
|
|
125
|
-
* same channel.
|
|
126
|
-
*/
|
|
127
87
|
async function executeTransactionTotally(execute, request, diagnostics, origins) {
|
|
128
88
|
const channel = createTransactionDiagnosticsChannel(diagnostics, request);
|
|
129
89
|
const report = channel.transaction();
|
|
@@ -135,15 +95,6 @@ async function executeTransactionTotally(execute, request, diagnostics, origins)
|
|
|
135
95
|
return internalFailure();
|
|
136
96
|
}
|
|
137
97
|
}
|
|
138
|
-
/**
|
|
139
|
-
* The typed runner's seam: assemble the handles into the passive plan, then run
|
|
140
|
-
* it through the same guard as `executeTransaction`, each step under its own
|
|
141
|
-
* origin. The plan is server code's, so its request is application-origin — no
|
|
142
|
-
* remote client identity to check — and every origin a step carries is gated by
|
|
143
|
-
* the validation layer. Assembly sits inside the same totality boundary: it
|
|
144
|
-
* resolves for every input. `requestId` is the run's — the caller's, or minted
|
|
145
|
-
* once for the whole run — so every step and every diagnostic carries one id.
|
|
146
|
-
*/
|
|
147
98
|
async function runTransactionTotally(execute, assembler, operations, requestId, diagnostics) {
|
|
148
99
|
let assembled;
|
|
149
100
|
try {
|
|
@@ -1,25 +1,9 @@
|
|
|
1
1
|
import { isOperationVariantAvailable } from "../contracts/resource-operations.js";
|
|
2
2
|
import { parseOperationIdentity } from "../operations/operation-identity.js";
|
|
3
3
|
import { requestIdOf } from "../runtime/request-id.js";
|
|
4
|
-
/**
|
|
5
|
-
* Builds a façade over `model`, the same object its types are read from, so a
|
|
6
|
-
* call that type-checks always reaches `execute`: a Resource, variant or field
|
|
7
|
-
* the active Contract does not carry is still a function here, and the
|
|
8
|
-
* validation layer answers it.
|
|
9
|
-
*
|
|
10
|
-
* Each call's request is `request`'s — its origin — under the caller's
|
|
11
|
-
* `ExecutionOptions.requestId`, or a freshly minted id when there is none; an
|
|
12
|
-
* omitted argument is the empty argument object, which the type admits only
|
|
13
|
-
* when every member is optional.
|
|
14
|
-
*/
|
|
15
4
|
export function createOperationFacade(model, request, execute) {
|
|
16
5
|
return createOperationTree(model, (resource, identity) => (args, options) => execute(request(resource, identity, args === undefined ? {} : args, requestIdOf(options))));
|
|
17
6
|
}
|
|
18
|
-
/**
|
|
19
|
-
* The runtime shape every façade and builder shares: one frozen property per
|
|
20
|
-
* Resource of `model`, one per family under it, one leaf per variant the model
|
|
21
|
-
* advertises in the standard grammar.
|
|
22
|
-
*/
|
|
23
7
|
export function createOperationTree(model, leaf) {
|
|
24
8
|
const resources = Object.entries(model.resources).map(([resource, { operations }]) => {
|
|
25
9
|
const families = Object.entries(operations).map(([family, variants]) => [family, variantLeaves(leaf, resource, family, variants)]);
|
|
@@ -27,7 +11,6 @@ export function createOperationTree(model, leaf) {
|
|
|
27
11
|
});
|
|
28
12
|
return Object.freeze(Object.fromEntries(resources));
|
|
29
13
|
}
|
|
30
|
-
/** One family's leaves: each advertised variant of the standard grammar. */
|
|
31
14
|
function variantLeaves(leaf, resource, family, variants) {
|
|
32
15
|
const leaves = Object.entries(variants ?? {}).flatMap(([variant, availability]) => {
|
|
33
16
|
const identity = parseOperationIdentity(family, variant);
|
|
@@ -59,11 +59,11 @@ type HoldsAtScope<Value, Scope extends ProjectionScope | WriteScope> = Value ext
|
|
|
59
59
|
readonly scopes: readonly (infer Stated)[];
|
|
60
60
|
} ? Scope extends Stated ? true : false : Value extends ProjectionScopes ? Scope extends Value[number] ? true : false : true;
|
|
61
61
|
/**
|
|
62
|
-
* The tokens a capability member advertises, whichever arm it is written in —
|
|
63
|
-
*
|
|
62
|
+
* The tokens a capability member advertises, whichever arm it is written in — the
|
|
63
|
+
* type-level mirror of `advertisedDirectives` and `advertisedReducers`, and
|
|
64
64
|
* scope-blind for the same reason they are: reading the tokens and deciding where
|
|
65
65
|
* they hold are two questions, and a reader handed both in one answer answers the
|
|
66
|
-
* second by forgetting it
|
|
66
|
+
* second by forgetting it. {@link CapabilityTokens} asks both.
|
|
67
67
|
*/
|
|
68
68
|
type AdvertisedTokens<Value> = Value extends {
|
|
69
69
|
readonly scopes: readonly WriteScope[];
|
|
@@ -135,13 +135,12 @@ export type ScalarFilterInput = unknown | Readonly<Partial<Record<ScalarFilterOp
|
|
|
135
135
|
* A scalar filter's shorthand equality value: the field's value, minus a JSON
|
|
136
136
|
* object.
|
|
137
137
|
*
|
|
138
|
-
* Every reader of a filter position — the validation layer, the scalar runtime
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* exclusion as `not`'s value arm below.
|
|
138
|
+
* Every reader of a filter position — the validation layer, the scalar runtime and
|
|
139
|
+
* the adapter — reads it through one discriminant (`runtime/scalar-runtime.ts`
|
|
140
|
+
* `isPlainRecord`): a plain record is an operator object, anything else is a
|
|
141
|
+
* value. So a `Date`, a `Decimal` and a `Uint8Array` are shorthand for their
|
|
142
|
+
* field, and a JSON object is an operator object by construction — `{ equals:
|
|
143
|
+
* document }` carries it. The same exclusion as `not`'s value arm below.
|
|
145
144
|
*/
|
|
146
145
|
type ScalarFilterShorthand<Value> = Exclude<Value, JsonObject>;
|
|
147
146
|
/**
|
|
@@ -241,25 +240,22 @@ type OrderValue<C extends Contract, K extends ResourceKey<C>, F extends Orderabl
|
|
|
241
240
|
/**
|
|
242
241
|
* One `orderBy` precedence entry: exactly one orderable field and its value.
|
|
243
242
|
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* `OrderValue<…, "mentor">`, which IS `OrderByEntry<C, "Author">`: TS2615, after
|
|
251
|
-
* which the entry degraded to `any` and refused nothing. A mapped property is
|
|
252
|
-
* resolved only when read, by which time the entry it recurses into exists. The
|
|
243
|
+
* A to-one relation's value is the target's entry, so a self-relation
|
|
244
|
+
* (`Author.mentor → Author`) or a one-to-one pair makes this type recursive. A
|
|
245
|
+
* type argument is instantiated eagerly, so resolving `OrderByEntry<C, "Author">`
|
|
246
|
+
* needed `OrderValue<…, "mentor">`, which IS `OrderByEntry<C, "Author">`: TS2615,
|
|
247
|
+
* after which the entry degraded to `any` and refused nothing. A mapped property
|
|
248
|
+
* is resolved only when read, by which time the entry it recurses into exists. The
|
|
253
249
|
* projection walk never hit this because its recursion already sits behind an
|
|
254
250
|
* object property (`{ select?: … }`).
|
|
255
251
|
*
|
|
256
252
|
* **No type-level depth cap, deliberately.** The runtime bound is the Contract's
|
|
257
|
-
* `limits.maxNestingDepth` — each to-one hop counts one level, and one past it
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
253
|
+
* `limits.maxNestingDepth` — each to-one hop counts one level, and one past it is
|
|
254
|
+
* V1013 (`operation-nesting.ts` `walkOrderBy`). That limit is a `number` in the
|
|
255
|
+
* Contract, resolved per scope at construction, so no literal exists for a type to
|
|
256
|
+
* count against; a fixed cap would refuse what a Contract with a larger limit
|
|
257
|
+
* accepts. `where` and `select` recurse uncapped for the same reason, and the
|
|
258
|
+
* validation layer refuses past the limit for all three.
|
|
263
259
|
*/
|
|
264
260
|
export type OrderByEntry<C extends Contract, K extends ResourceKey<C>> = {
|
|
265
261
|
[F in OrderableFieldKey<C, K>]: {
|
|
@@ -325,16 +321,14 @@ export type RelationProjectionInput<C extends Contract, K extends ResourceKey<C>
|
|
|
325
321
|
* One relation entry of a projection: exactly relation `F` of `Keys`, with its
|
|
326
322
|
* argument object, and every other key of `Keys` absent.
|
|
327
323
|
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
* for the relations it projects. Measured on the six-resource artifact shape:
|
|
337
|
-
* ≈4,500–6,700 instantiations per call whose projection names no relation.
|
|
324
|
+
* A projection's entry union is formed whenever its `select`/`include` constraint
|
|
325
|
+
* is — at every call, including one that passes no projection — and a type
|
|
326
|
+
* argument is instantiated when the union is: every relation's {@link
|
|
327
|
+
* RelationProjectionInput} (its nested `where`, `orderBy`, pagination and
|
|
328
|
+
* projection choice) for every relation the Resource has. A mapped property is
|
|
329
|
+
* resolved only when an entry names it, so a call pays for the relations it
|
|
330
|
+
* projects. Measured on the six-resource artifact shape: ≈4,500–6,700
|
|
331
|
+
* instantiations per call whose projection names no relation.
|
|
338
332
|
*/
|
|
339
333
|
type RelationProjectionEntry<C extends Contract, K extends ResourceKey<C>, Keys extends RelationFieldKeyOf<C, K>, F extends Keys, Scope extends ProjectionScope, Forms extends ScalarForms> = {
|
|
340
334
|
readonly [Only in F]: RelationProjectionInput<C, K, Only & RelationFieldKeyOf<C, K>, Scope, Forms>;
|
|
@@ -408,13 +402,13 @@ declare const NO_ADVERTISED_MEMBER: unique symbol;
|
|
|
408
402
|
*
|
|
409
403
|
* TypeScript runs no excess-property check against an object type with no known
|
|
410
404
|
* properties, so an empty mapped member would type as `{}` and accept any key
|
|
411
|
-
* silently, leaving it to `V1006` at runtime
|
|
405
|
+
* silently, leaving it to `V1006` at runtime. One optional member on a
|
|
412
406
|
* module-private symbol keeps the type weak — `{}` still assigns — while giving
|
|
413
|
-
* the excess-property check something to measure against, so the diagnostic
|
|
414
|
-
*
|
|
407
|
+
* the excess-property check something to measure against, so the diagnostic names
|
|
408
|
+
* the offending key.
|
|
415
409
|
*
|
|
416
|
-
* The empty case is reachable by design: `directives: []` means "writable, with
|
|
417
|
-
*
|
|
410
|
+
* The empty case is reachable by design: `directives: []` means "writable, with no
|
|
411
|
+
* directive", distinct from an absent member (see `contracts/field.ts`).
|
|
418
412
|
*/
|
|
419
413
|
type NoAdvertisedMember = {
|
|
420
414
|
readonly [NO_ADVERTISED_MEMBER]?: never;
|
|
@@ -489,11 +483,7 @@ type QueryProjectionArgs<C extends Contract, K extends ResourceKey<C>, Scope ext
|
|
|
489
483
|
* **This binding is core's grammar, not the adapter's routing.** A variant's real
|
|
490
484
|
* scope is an adapter fact and the adapter states it on the advertised variant
|
|
491
485
|
* (`OperationVariantScopes`); a host that routed, say, `update.first` through a
|
|
492
|
-
* bulk-returning call would make this map's `"record"` too generous.
|
|
493
|
-
* consequence is that the type can over-advertise where an adapter is narrower,
|
|
494
|
-
* never the reverse, and the runtime guard refuses what it advertised — which is
|
|
495
|
-
* why this surface is not a substitute for the validation layer (ADR 0009: the
|
|
496
|
-
* compiled Contract is untyped, so no type here is load-bearing at runtime).
|
|
486
|
+
* bulk-returning call would make this map's `"record"` too generous.
|
|
497
487
|
*
|
|
498
488
|
* An unqualified capability holds in every scope, so a Contract whose adapter has
|
|
499
489
|
* not measured the axis is bound to the same surface it had before the axis
|
|
@@ -580,7 +570,7 @@ export type OperationArgumentMap<C extends Contract, K extends ResourceKey<C>, F
|
|
|
580
570
|
* value is `unknown` ({@link UnstatedForms}); the application façade passes
|
|
581
571
|
* `ApplicationScalarForms`, so a `Date` handed to a `json` field or a number to a
|
|
582
572
|
* `string` one is refused where it is written rather than by `V1001` at runtime.
|
|
583
|
-
* The validation layer stays the guard either way
|
|
573
|
+
* The validation layer stays the guard either way.
|
|
584
574
|
*/
|
|
585
575
|
export type OperationArgumentsFor<C extends Contract, K extends ResourceKey<C>, I extends OperationIdentity, Forms extends ScalarForms = UnstatedForms> = I extends {
|
|
586
576
|
family: infer F;
|
|
@@ -95,10 +95,6 @@ function projectionRelation(contract, resourceName, entry, mode, scope) {
|
|
|
95
95
|
if (field?.kind !== "relation") {
|
|
96
96
|
return undefined;
|
|
97
97
|
}
|
|
98
|
-
// The SAME scope-aware read the grammar walk uses. This walk skips a relation it
|
|
99
|
-
// cannot descend, so reading the member scope-blind would stop counting beneath
|
|
100
|
-
// every measured relation — `maxBooleanNodes` silently unenforced inside a
|
|
101
|
-
// projection the grammar walk then admits.
|
|
102
98
|
if (mode === "select" && !holdsAt(field.capabilities.select, scope)) {
|
|
103
99
|
return undefined;
|
|
104
100
|
}
|
|
@@ -132,7 +128,6 @@ function walkProjection(state, resourceName, value, mode, scope, path) {
|
|
|
132
128
|
if (!(nestedMode in relation.value)) {
|
|
133
129
|
continue;
|
|
134
130
|
}
|
|
135
|
-
// Nested is unrestricted at every depth — {@link NESTED_PROJECTION_SCOPE}.
|
|
136
131
|
const issue = walkProjection(state, relation.edge.target, relation.value[nestedMode], nestedMode, NESTED_PROJECTION_SCOPE, [...relationPath, nestedMode]);
|
|
137
132
|
if (issue !== undefined) {
|
|
138
133
|
return issue;
|
|
@@ -157,12 +152,6 @@ function walkMutationData(state, resourceName, value, stage, path) {
|
|
|
157
152
|
if (edge?.kind !== "relation" || !isRecord(fieldValue)) {
|
|
158
153
|
continue;
|
|
159
154
|
}
|
|
160
|
-
// Scope-blind, unlike the projection read above, and deliberately: this walk
|
|
161
|
-
// may only ever be a SUPERSET of what the grammar walk admits. The write scope
|
|
162
|
-
// narrows the top level of a write, so a scope-aware read here could only
|
|
163
|
-
// descend less — and a walk that stops counting inside a write the grammar walk
|
|
164
|
-
// admits is `maxBooleanNodes` silently unenforced. Counting inside one it is
|
|
165
|
-
// about to refuse costs a node tally nobody reads.
|
|
166
155
|
const supported = advertisedDirectives(edge.capabilities[stage]);
|
|
167
156
|
for (const [directive, directiveValue] of Object.entries(fieldValue)) {
|
|
168
157
|
if (supported?.includes(directive) !== true) {
|
|
@@ -241,11 +230,6 @@ function isUniqueOperation(request) {
|
|
|
241
230
|
request.family === "delete" ||
|
|
242
231
|
request.family === "upsert"));
|
|
243
232
|
}
|
|
244
|
-
/**
|
|
245
|
-
* Enforces maxBooleanNodes across every general where tree in one semantic
|
|
246
|
-
* operation, including root filters, returned-relation filters, and nested
|
|
247
|
-
* relation mutation filters. Unique selectors are intentionally excluded.
|
|
248
|
-
*/
|
|
249
233
|
export function findOperationBooleanNodeIssue(contract, request, args) {
|
|
250
234
|
const state = { contract, count: 0 };
|
|
251
235
|
const projectionScope = resolveProjectionScope(lookupResource(contract, request.resource), request);
|
|
@@ -54,8 +54,8 @@ export type OperationGrammar<C extends Contract> = {
|
|
|
54
54
|
};
|
|
55
55
|
/**
|
|
56
56
|
* An operation's slot: its result, except that a first-style miss contributes
|
|
57
|
-
* `null`
|
|
58
|
-
*
|
|
57
|
+
* `null` — where the miss is not the `A1001` arm of a response of its own: a
|
|
58
|
+
* committed plan's slot, and a remote call that resolves to the data.
|
|
59
59
|
*/
|
|
60
60
|
export type OperationSlot<I extends OperationIdentity, Result> = I["variant"] extends "first" ? Result | null : Result;
|
|
61
61
|
/**
|
|
@@ -72,18 +72,13 @@ type PlainCall<C extends Contract, R extends ResourceKey<C>, I extends Operation
|
|
|
72
72
|
* projection when it carries one.
|
|
73
73
|
*/
|
|
74
74
|
export type OperationCall<C extends Contract, R extends ResourceKey<C>, I extends OperationIdentity, Forms extends ScalarForms, ResultForms extends ScalarForms, Settlement extends "response" | "data", Options> = "select" extends keyof OperationArgumentsFor<C, R, I, Forms> ? ProjectingCall<C, R, I, OperationArgumentsFor<C, R, I, Forms>, ResultForms, Settlement, Options> : PlainCall<C, R, I, OperationArgumentsFor<C, R, I, Forms>, ResultForms, Settlement, Options>;
|
|
75
|
-
/**
|
|
76
|
-
* A deferred operation's arguments: `Forms`, with DA-3 reference positions opened
|
|
77
|
-
* to a `$ref` typed in `ResultForms` — the forms the source's final data is read
|
|
78
|
-
* in, so a reference carries what its source resolves.
|
|
79
|
-
*/
|
|
80
75
|
type DeferredArguments<C extends Contract, R extends ResourceKey<C>, I extends OperationIdentity, Forms extends ScalarForms, ResultForms extends ScalarForms> = ReferenceableArgumentsFor<C, R, I, Forms, ResultForms>;
|
|
81
76
|
type DeferredProjectingCall<C extends Contract, R extends ResourceKey<C>, I extends OperationIdentity, Args, ResultForms extends ScalarForms> = <const Select extends SelectOf<Args> = never, const Include extends IncludeOf<Args> = never>(...args: ArgumentList<Omit<Args, ProjectionKey>, Omit<Args, ProjectionKey> & ProjectionArgument<Select, Include>>) => Operation<OperationSlot<I, AdmittedOperationResult<C, R, I, InferredProjection<Select, Include>, ResultForms>>>;
|
|
82
77
|
type DeferredPlainCall<C extends Contract, R extends ResourceKey<C>, I extends OperationIdentity, Args, ResultForms extends ScalarForms> = (...args: ArgumentList<Args, Args>) => Operation<OperationSlot<I, AdmittedOperationResult<C, R, I, unknown, ResultForms>>>;
|
|
83
78
|
/**
|
|
84
79
|
* One deferred operation: a handle typed `Operation<OperationSlot<…>>` — `$ref`
|
|
85
|
-
* present exactly where the result is one row. A handle is not a request and
|
|
86
|
-
*
|
|
80
|
+
* present exactly where the result is one row. A handle is not a request and takes
|
|
81
|
+
* no options.
|
|
87
82
|
*/
|
|
88
83
|
export type DeferredOperationCall<C extends Contract, R extends ResourceKey<C>, I extends OperationIdentity, Forms extends ScalarForms, ResultForms extends ScalarForms> = "select" extends keyof DeferredArguments<C, R, I, Forms, ResultForms> ? DeferredProjectingCall<C, R, I, DeferredArguments<C, R, I, Forms, ResultForms>, ResultForms> : DeferredPlainCall<C, R, I, DeferredArguments<C, R, I, Forms, ResultForms>, ResultForms>;
|
|
89
84
|
export {};
|
|
@@ -16,7 +16,6 @@ const STANDARD_OPERATIONS = {
|
|
|
16
16
|
"delete.count": true,
|
|
17
17
|
"upsert.unique": true,
|
|
18
18
|
};
|
|
19
|
-
/** Parses one exact standard operation family/variant pair. */
|
|
20
19
|
export function parseOperationIdentity(family, variant) {
|
|
21
20
|
const key = `${family}.${variant}`;
|
|
22
21
|
if (!Object.hasOwn(STANDARD_OPERATIONS, key)) {
|
|
@@ -257,11 +257,6 @@ function walkMutationData(context, resourceName, value, depth, path) {
|
|
|
257
257
|
}
|
|
258
258
|
return undefined;
|
|
259
259
|
}
|
|
260
|
-
/**
|
|
261
|
-
* Enforces the Contract's semantic nesting limit across where, projection,
|
|
262
|
-
* orderBy, mutation data, and relation traversal before deeper argument
|
|
263
|
-
* validation.
|
|
264
|
-
*/
|
|
265
260
|
export function findOperationNestingIssue(contract, resourceName, args) {
|
|
266
261
|
if ("where" in args) {
|
|
267
262
|
const rootPath = ["arguments", "where"];
|
|
@@ -90,7 +90,7 @@ type RelationResult<C extends Contract, Target extends ResourceKey<C>, Relation
|
|
|
90
90
|
/**
|
|
91
91
|
* The value a standard operation returns in `OperationResponse.data` on success,
|
|
92
92
|
* derived from the Contract, the Resource, the operation identity and the
|
|
93
|
-
* projection argument
|
|
93
|
+
* projection argument.
|
|
94
94
|
*
|
|
95
95
|
* - `count` → `number`;
|
|
96
96
|
* - `many` → `readonly Record[]`;
|
|
@@ -104,9 +104,9 @@ type RelationResult<C extends Contract, Target extends ResourceKey<C>, Relation
|
|
|
104
104
|
* unadvertised `$count`, a relation outside a bulk write's ceiling — is refused
|
|
105
105
|
* here instead of typed as present.
|
|
106
106
|
*
|
|
107
|
-
* `Forms`
|
|
108
|
-
*
|
|
109
|
-
*
|
|
107
|
+
* `Forms` is the scalar-form map. It defaults to the application forms, which are
|
|
108
|
+
* what a framework-produced value is in either scope; a transport or a generated
|
|
109
|
+
* client passes the wire forms and gets the same derivation.
|
|
110
110
|
*/
|
|
111
111
|
export type OperationResultFor<C extends Contract, K extends ResourceKey<C>, I extends OperationIdentity, Args extends OperationArgumentsFor<C, K, I>, Forms extends ScalarForms = ApplicationScalarForms> = AdmittedOperationResult<C, K, I, Args, Forms>;
|
|
112
112
|
/**
|
|
@@ -17,8 +17,8 @@ export type OperationValidationOptions = {
|
|
|
17
17
|
/**
|
|
18
18
|
* The Framework Validation Layer's issue for a request naming a Resource the
|
|
19
19
|
* active Contract does not have (`V1005` at `["resource"]`, rolling up to
|
|
20
|
-
* `A2001`). The one spelling — the protocol's absence answers
|
|
21
|
-
*
|
|
20
|
+
* `A2001`). The one spelling — the protocol's absence answers build theirs with
|
|
21
|
+
* it.
|
|
22
22
|
*/
|
|
23
23
|
export declare function resourceUnavailableIssue(resource: string): ValidationIssue;
|
|
24
24
|
export declare function validateOperationArguments(contract: Contract, request: OperationIdentity & {
|
|
@@ -168,11 +168,6 @@ function validateScalarFilter(fieldName, field, value, path, state) {
|
|
|
168
168
|
}
|
|
169
169
|
return;
|
|
170
170
|
}
|
|
171
|
-
// Equality shorthand: anything that is not a plain record is a value — a
|
|
172
|
-
// `Date`, a `Decimal` and a `Uint8Array` included — read through the scalar
|
|
173
|
-
// runtime's own discriminant, which holds it to the field's scalar after
|
|
174
|
-
// this layer (`normalizeScalarFilter`). Reading every object as operators
|
|
175
|
-
// refused the shorthand the ORM accepts (C-855, sign flipped).
|
|
176
171
|
if (!isPlainRecord(value)) {
|
|
177
172
|
if (!includes(supported, "equals")) {
|
|
178
173
|
addIssue(state.issues, "V1006", path, `Equality filtering is not available on field "${fieldName}".`);
|
|
@@ -409,14 +404,7 @@ function validateCursor(contract, resourceName, cursor, orderBy, path, issues) {
|
|
|
409
404
|
addIssue(issues, "V1009", path, "orderBy must include a complete Resource identifier as an explicit tie-breaker.");
|
|
410
405
|
}
|
|
411
406
|
}
|
|
412
|
-
function validateProjection(contract, resourceName, mode, value,
|
|
413
|
-
/**
|
|
414
|
-
* The projection position THIS projection's own field and relation entries sit
|
|
415
|
-
* in. The caller resolves it once from the advertised variant; a nested call
|
|
416
|
-
* passes {@link NESTED_PROJECTION_SCOPE}, because a scope qualifies the top level
|
|
417
|
-
* only. A `$count` entry is the exception and reads {@link ReducerOwner.scope}.
|
|
418
|
-
*/
|
|
419
|
-
scope, path, issues, reducerOwner) {
|
|
407
|
+
function validateProjection(contract, resourceName, mode, value, scope, path, issues, reducerOwner) {
|
|
420
408
|
if (!Array.isArray(value)) {
|
|
421
409
|
addIssue(issues, "V1008", path, `${mode} must be an array.`);
|
|
422
410
|
return;
|
|
@@ -431,10 +419,7 @@ scope, path, issues, reducerOwner) {
|
|
|
431
419
|
if (entry === "$count") {
|
|
432
420
|
if (mode !== "select" ||
|
|
433
421
|
reducerOwner?.edge.cardinality !== "many" ||
|
|
434
|
-
!includes(advertisedReducersAt(reducerOwner.edge.capabilities.reducers,
|
|
435
|
-
// The OWNER's position, not this projection's: see
|
|
436
|
-
// {@link ReducerOwner}.
|
|
437
|
-
reducerOwner.scope), "$count")) {
|
|
422
|
+
!includes(advertisedReducersAt(reducerOwner.edge.capabilities.reducers, reducerOwner.scope), "$count")) {
|
|
438
423
|
addIssue(issues, "V1008", entryPath, "$count is available only as an advertised to-many relation reducer.");
|
|
439
424
|
}
|
|
440
425
|
return;
|
|
@@ -477,16 +462,10 @@ scope, path, issues, reducerOwner) {
|
|
|
477
462
|
addIssue(issues, "V1008", relationPath, `Relation "${relationName}" is not available for ${mode}.`);
|
|
478
463
|
return;
|
|
479
464
|
}
|
|
480
|
-
validateRelationProjection(contract, field,
|
|
481
|
-
// The position the edge itself was admitted in. The edge's nested
|
|
482
|
-
// projection is unrestricted, but a `$count` of this edge is carried at
|
|
483
|
-
// THIS level, so the scope has to travel with the edge.
|
|
484
|
-
scope, entry[relationName], relationPath, issues);
|
|
465
|
+
validateRelationProjection(contract, field, scope, entry[relationName], relationPath, issues);
|
|
485
466
|
});
|
|
486
467
|
}
|
|
487
|
-
function validateRelationProjection(contract, edge,
|
|
488
|
-
/** The projection position `edge` itself was admitted in — see {@link ReducerOwner}. */
|
|
489
|
-
scope, value, path, issues) {
|
|
468
|
+
function validateRelationProjection(contract, edge, scope, value, path, issues) {
|
|
490
469
|
if (!isRecord(value)) {
|
|
491
470
|
addIssue(issues, "V1008", path, "Customized relation projection must be an object.");
|
|
492
471
|
return;
|
|
@@ -551,10 +530,6 @@ scope, value, path, issues) {
|
|
|
551
530
|
validateCursor(contract, edge.target, value.cursor, value.orderBy, [...path, "cursor"], issues);
|
|
552
531
|
}
|
|
553
532
|
}
|
|
554
|
-
// Past an admitted relation the unrestricted surface returns, measured on both
|
|
555
|
-
// pilot engines — see {@link NESTED_PROJECTION_SCOPE}. The edge's own scope is
|
|
556
|
-
// deliberately NOT passed down: a recursive narrowing would refuse what the ORM
|
|
557
|
-
// accepts.
|
|
558
533
|
if ("select" in value) {
|
|
559
534
|
validateProjection(contract, edge.target, "select", value.select, NESTED_PROJECTION_SCOPE, [...path, "select"], issues, { edge, scope });
|
|
560
535
|
}
|
|
@@ -595,16 +570,6 @@ function validateScalarMutation(fieldName, field, value, stage, path, issues, tr
|
|
|
595
570
|
}
|
|
596
571
|
}
|
|
597
572
|
}
|
|
598
|
-
/**
|
|
599
|
-
* One relation mutation directive's operand, past the point where the directive
|
|
600
|
-
* itself was admitted.
|
|
601
|
-
*
|
|
602
|
-
* It takes **no write scope**, and that is the rule rather than an omission: every
|
|
603
|
-
* data object it reaches is nested, and {@link NESTED_WRITE_SCOPE} is the answer at
|
|
604
|
-
* every depth. A parameter here would be a parameter this function could only
|
|
605
|
-
* ignore, and the next reader would thread the caller's scope through it and refuse
|
|
606
|
-
* what the ORM accepts.
|
|
607
|
-
*/
|
|
608
573
|
function validateRelationMutationEntry(contract, edge, directive, value, path, issues) {
|
|
609
574
|
const target = edge.target;
|
|
610
575
|
if (edge.cardinality === "one" && Array.isArray(value)) {
|
|
@@ -747,13 +712,7 @@ function validateRelationMutationEntry(contract, edge, directive, value, path, i
|
|
|
747
712
|
}
|
|
748
713
|
}
|
|
749
714
|
}
|
|
750
|
-
function validateRelationMutation(contract, fieldName, field, value, stage,
|
|
751
|
-
/** The write position this relation member itself sits in; see {@link WriteScope}. */
|
|
752
|
-
scope, path, issues) {
|
|
753
|
-
// Scope-aware, through the shape owner's one reader. A stage that does not hold
|
|
754
|
-
// at this write position is not writable at this write position, and says so with
|
|
755
|
-
// the message an absent member already has — which is why the two answers
|
|
756
|
-
// `advertisedDirectivesAt` can give are the two this branch already handled.
|
|
715
|
+
function validateRelationMutation(contract, fieldName, field, value, stage, scope, path, issues) {
|
|
757
716
|
const directives = advertisedDirectivesAt(field.capabilities[stage], scope);
|
|
758
717
|
if (directives === undefined) {
|
|
759
718
|
addIssue(issues, "V1006", path, `Relation "${fieldName}" is not writable during ${stage}.`);
|
|
@@ -772,15 +731,7 @@ scope, path, issues) {
|
|
|
772
731
|
validateRelationMutationEntry(contract, field, directive, directiveValue, directivePath, issues);
|
|
773
732
|
}
|
|
774
733
|
}
|
|
775
|
-
function validateMutationData(contract, resourceName, data, stage,
|
|
776
|
-
/**
|
|
777
|
-
* The write position THIS data object's own relation members sit in. The caller
|
|
778
|
-
* resolves it once from the advertised variant; a nested call passes
|
|
779
|
-
* {@link NESTED_WRITE_SCOPE}, because a scope qualifies the top level only.
|
|
780
|
-
* Scalar members never read it — all six scalar capability members were measured
|
|
781
|
-
* uniform across every variant on the axis.
|
|
782
|
-
*/
|
|
783
|
-
scope, path, issues, transactionReferences = false) {
|
|
734
|
+
function validateMutationData(contract, resourceName, data, stage, scope, path, issues, transactionReferences = false) {
|
|
784
735
|
if (!isRecord(data)) {
|
|
785
736
|
addIssue(issues, "V1001", path, `${stage} data must be an object.`);
|
|
786
737
|
return;
|
|
@@ -806,14 +757,6 @@ scope, path, issues, transactionReferences = false) {
|
|
|
806
757
|
}
|
|
807
758
|
}
|
|
808
759
|
function validateMutationArguments(contract, resourceName, request, args, issues, options) {
|
|
809
|
-
// The one resolution, from the variant the Resource advertises, and the write
|
|
810
|
-
// mirror of the projection scope resolved at the top level. It qualifies the top
|
|
811
|
-
// level of the write argument only; everything beneath an admitted relation
|
|
812
|
-
// directive is nested and asks about the unrestricted position instead.
|
|
813
|
-
//
|
|
814
|
-
// Resolved here rather than passed in because `request` is what carries the
|
|
815
|
-
// variant, and an argument walk handed a scope could be handed one resolved for a
|
|
816
|
-
// different request.
|
|
817
760
|
const scope = resolveWriteScope(getResource(contract, resourceName), request);
|
|
818
761
|
if (request.family === "create") {
|
|
819
762
|
if (!("data" in args)) {
|
|
@@ -853,12 +796,6 @@ function isUniqueOperation(request) {
|
|
|
853
796
|
request.family === "delete" ||
|
|
854
797
|
request.family === "upsert"));
|
|
855
798
|
}
|
|
856
|
-
/**
|
|
857
|
-
* The Framework Validation Layer's issue for a request naming a Resource the
|
|
858
|
-
* active Contract does not have (`V1005` at `["resource"]`, rolling up to
|
|
859
|
-
* `A2001`). The one spelling — the protocol's absence answers
|
|
860
|
-
* (`@aventara/core/protocol`, Q2's O3) build theirs with it.
|
|
861
|
-
*/
|
|
862
799
|
export function resourceUnavailableIssue(resource) {
|
|
863
800
|
return {
|
|
864
801
|
code: "V1005",
|
|
@@ -911,8 +848,6 @@ export function validateOperationArguments(contract, request, options) {
|
|
|
911
848
|
if ("cursor" in request.arguments) {
|
|
912
849
|
validateCursor(contract, request.resource, request.arguments.cursor, request.arguments.orderBy, ["arguments", "cursor"], issues);
|
|
913
850
|
}
|
|
914
|
-
// The one resolution, from the variant the Resource advertises. Everything below
|
|
915
|
-
// the top level is nested and asks about the unrestricted position instead.
|
|
916
851
|
const projectionScope = resolveProjectionScope(resource, request);
|
|
917
852
|
if ("select" in request.arguments) {
|
|
918
853
|
validateProjection(contract, request.resource, "select", request.arguments.select, projectionScope, ["arguments", "select"], issues);
|