@aventara/core 0.1.0-pilot.3 → 1.0.0-rc.0
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 +338 -0
- package/README.md +100 -3
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +2 -0
- package/dist/adapters/adapter-field.d.ts +3 -26
- package/dist/adapters/adapter-identifier.d.ts +1 -11
- package/dist/adapters/adapter-model.d.ts +12 -0
- package/dist/adapters/adapter-operation-executor.interface.d.ts +5 -1
- package/dist/adapters/adapter-resource.d.ts +7 -28
- package/dist/adapters/adapter.interface.d.ts +25 -26
- package/dist/compiler/contract-compiler-diagnostic.d.ts +8 -0
- package/dist/compiler/contract.compiler.d.ts +11 -22
- package/dist/compiler/contract.compiler.js +67 -0
- package/dist/config/any-framework-config.d.ts +5 -26
- package/dist/config/config-diagnostic.d.ts +2 -0
- package/dist/config/config-layer.d.ts +6 -19
- package/dist/config/config.resolver.d.ts +9 -14
- package/dist/config/config.resolver.js +1 -0
- package/dist/config/config.validator.d.ts +6 -3
- package/dist/config/config.validator.js +6 -0
- package/dist/config/define-fields.d.ts +15 -48
- package/dist/config/effective-fields.d.ts +6 -29
- package/dist/config/entrypoint.normalizer.d.ts +6 -18
- package/dist/config/framework-behaviors-config.d.ts +11 -10
- package/dist/config/framework-config.d.ts +19 -16
- package/dist/config/framework-fields-config.d.ts +20 -7
- package/dist/config/framework-restrictions-config.d.ts +41 -61
- package/dist/config/pipeline-config.d.ts +7 -17
- package/dist/contracts/capability.d.ts +13 -49
- package/dist/contracts/client-contract-structure.validator.d.ts +28 -54
- package/dist/contracts/client-contract-structure.validator.js +99 -2
- package/dist/contracts/contract-canonicalizer.d.ts +4 -6
- package/dist/contracts/contract-canonicalizer.js +32 -0
- package/dist/contracts/contract-hash.d.ts +3 -2
- package/dist/contracts/contract-limits.d.ts +7 -1
- package/dist/contracts/contract-protocol.d.ts +7 -8
- package/dist/contracts/contract.d.ts +20 -4
- package/dist/contracts/enum-definition.d.ts +2 -0
- package/dist/contracts/field.d.ts +40 -167
- package/dist/contracts/identifier.d.ts +1 -16
- package/dist/contracts/json-shape.d.ts +65 -0
- package/dist/contracts/json-shape.js +1 -0
- package/dist/contracts/lifecycle-trait.d.ts +4 -3
- package/dist/contracts/reference-write-limit.d.ts +7 -0
- package/dist/contracts/reference-write-limit.js +1 -0
- package/dist/contracts/resource-operations.d.ts +2 -62
- package/dist/contracts/resource.d.ts +2 -0
- package/dist/contracts/scalar-value-type.d.ts +28 -48
- package/dist/contracts/scalar.d.ts +4 -3
- package/dist/contracts/transaction-capability.d.ts +2 -0
- package/dist/contracts/wire-scalar-format.d.ts +6 -21
- package/dist/framework/contract-view.d.ts +152 -130
- package/dist/framework/framework-construction.error.d.ts +6 -1
- package/dist/framework/framework.d.ts +33 -46
- package/dist/framework/framework.factory.d.ts +3 -17
- package/dist/framework/framework.factory.js +56 -13
- package/dist/framework/operation-facade.d.ts +3 -7
- package/dist/index.d.ts +8 -40
- package/dist/index.js +0 -20
- package/dist/internal.d.ts +35 -0
- package/dist/internal.js +16 -0
- package/dist/json/avz.d.ts +96 -0
- package/dist/json/avz.js +212 -0
- package/dist/json/index.d.ts +6 -0
- package/dist/json/index.js +1 -0
- package/dist/json/json-shape.registry.d.ts +22 -0
- package/dist/json/json-shape.registry.js +16 -0
- package/dist/json/zod-issue.translator.d.ts +51 -0
- package/dist/json/zod-issue.translator.js +142 -0
- package/dist/operations/operation-arguments.d.ts +198 -303
- package/dist/operations/operation-call.d.ts +13 -15
- package/dist/operations/operation-identity.d.ts +6 -2
- package/dist/operations/operation-identity.js +3 -0
- package/dist/operations/operation-result.d.ts +15 -61
- package/dist/operations/operation-validator-core.d.ts +6 -5
- package/dist/operations/operation-validator-core.js +28 -10
- package/dist/operations/operation-validator.d.ts +22 -9
- package/dist/operations/operation-validator.js +26 -10
- package/dist/operations/projection-scope.resolver.d.ts +1 -11
- package/dist/operations/structural-limits.walker.d.ts +19 -0
- package/dist/operations/structural-limits.walker.js +344 -0
- package/dist/operations/validation-code.d.ts +5 -17
- package/dist/operations/write-scope.resolver.d.ts +3 -28
- package/dist/protocol/absent-route.responder.d.ts +25 -39
- package/dist/protocol/absent-route.responder.js +32 -6
- package/dist/protocol/av-protocol.d.ts +2 -8
- package/dist/protocol/contract-document.responder.d.ts +5 -18
- package/dist/protocol/http-request.decoder.d.ts +18 -59
- package/dist/protocol/http-request.decoder.js +11 -3
- package/dist/protocol/http-request.reader.d.ts +5 -5
- package/dist/protocol/http-response.encoder.d.ts +14 -20
- package/dist/protocol/http-status.table.d.ts +0 -8
- package/dist/protocol/index.d.ts +2 -7
- package/dist/protocol/operation-response.guard.d.ts +0 -15
- package/dist/protocol/protocol-failure.d.ts +8 -27
- package/dist/protocol/protocol-headers.d.ts +15 -42
- package/dist/protocol/protocol-surface.d.ts +0 -22
- package/dist/protocol/protocol.binder.d.ts +19 -27
- package/dist/protocol/protocol.binder.js +3 -1
- package/dist/protocol/protocol.handler.d.ts +19 -23
- package/dist/protocol/protocol.handler.js +14 -4
- package/dist/protocol/unmounted-route.error.d.ts +19 -0
- package/dist/protocol/unmounted-route.error.js +10 -0
- package/dist/runtime/caller-data.copier.d.ts +59 -0
- package/dist/runtime/caller-data.copier.js +470 -0
- package/dist/runtime/caller-data.door.d.ts +56 -0
- package/dist/runtime/caller-data.door.js +65 -0
- package/dist/runtime/computed-read-runtime.js +3 -10
- package/dist/runtime/computed-value.error.d.ts +13 -0
- package/dist/runtime/computed-value.error.js +9 -0
- package/dist/runtime/create-requirement.d.ts +43 -7
- package/dist/runtime/create-requirement.js +89 -9
- package/dist/runtime/decimal.d.ts +5 -3
- package/dist/runtime/diagnostics-reporter.d.ts +33 -37
- package/dist/runtime/diagnostics-reporter.js +11 -4
- package/dist/runtime/error-boundary.d.ts +14 -10
- package/dist/runtime/error-boundary.js +65 -35
- package/dist/runtime/execution-options.d.ts +3 -7
- package/dist/runtime/field-behavior.d.ts +14 -21
- package/dist/runtime/framework-error.d.ts +14 -2
- package/dist/runtime/json-value.d.ts +7 -15
- package/dist/runtime/misuse-reporter.d.ts +32 -0
- package/dist/runtime/misuse-reporter.js +19 -0
- package/dist/runtime/mutation-planner.d.ts +6 -12
- package/dist/runtime/mutation-planner.js +13 -11
- package/dist/runtime/operation-request.d.ts +11 -11
- package/dist/runtime/operation-response.d.ts +18 -23
- package/dist/runtime/operation-runtime.d.ts +25 -33
- package/dist/runtime/operation-runtime.js +37 -18
- package/dist/runtime/outcome-mapper.js +12 -2
- package/dist/runtime/output-validator.d.ts +10 -0
- package/dist/runtime/output-validator.js +11 -0
- package/dist/runtime/owned-execution.registry.d.ts +13 -0
- package/dist/runtime/owned-execution.registry.js +7 -0
- package/dist/runtime/pipeline.d.ts +33 -36
- package/dist/runtime/referential-action.d.ts +8 -26
- package/dist/runtime/request-envelope.validator.d.ts +5 -0
- package/dist/runtime/request-envelope.validator.js +118 -0
- package/dist/runtime/request-id.d.ts +3 -10
- package/dist/runtime/scalar-runtime.d.ts +42 -14
- package/dist/runtime/scalar-runtime.js +162 -79
- package/dist/transactions/deferred-operation.d.ts +10 -21
- package/dist/transactions/operation-arguments-encoder.d.ts +1 -0
- package/dist/transactions/operation-fingerprint.d.ts +3 -1
- package/dist/transactions/transaction-envelope.d.ts +2 -4
- package/dist/transactions/transaction-integrity-error.d.ts +4 -15
- package/dist/transactions/transaction-plan-assembler.d.ts +6 -1
- package/dist/transactions/transaction-plan-assembler.js +74 -75
- package/dist/transactions/transaction-plan-validator.d.ts +33 -24
- package/dist/transactions/transaction-plan-validator.js +204 -153
- package/dist/transactions/transaction-reference-collector.d.ts +30 -0
- package/dist/transactions/transaction-reference-collector.js +204 -0
- package/dist/transactions/transaction-reference-resolver.d.ts +9 -12
- package/dist/transactions/transaction-reference-resolver.js +10 -5
- package/dist/transactions/transaction-reference-validator.js +24 -17
- package/dist/transactions/transaction-reference.d.ts +31 -64
- package/dist/transactions/transaction-request.d.ts +6 -8
- package/dist/transactions/transaction-response.d.ts +10 -7
- package/dist/transactions/transaction-response.js +24 -1
- package/dist/transactions/transaction-runtime.d.ts +7 -5
- package/dist/transactions/transaction-runtime.js +4 -3
- package/dist/transactions/transaction-slot-path-error.d.ts +5 -15
- package/dist/transactions/transaction-slot-writer.d.ts +3 -13
- package/package.json +17 -3
- package/dist/operations/operation-boolean-nodes.d.ts +0 -11
- package/dist/operations/operation-boolean-nodes.js +0 -283
- package/dist/operations/operation-nesting.d.ts +0 -8
- package/dist/operations/operation-nesting.js +0 -307
|
@@ -5,47 +5,46 @@ import type { AdapterOperationExecutor } from "./adapter-operation-executor.inte
|
|
|
5
5
|
/** Provider-specific implementation of Aventara discovery and execution. */
|
|
6
6
|
export interface Adapter<M extends AdapterModel = AdapterModel> extends AdapterOperationExecutor<M> {
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
* synchronously
|
|
10
|
-
* call: the model is a settled fact of the constructed adapter, so it
|
|
11
|
-
* cannot fail, change, or be observed twice during construction.
|
|
8
|
+
* What the adapter discovered about the data source, in Aventara's vocabulary.
|
|
9
|
+
* Read once, synchronously, when the framework is constructed.
|
|
12
10
|
*/
|
|
13
11
|
readonly model: M;
|
|
14
12
|
/**
|
|
15
13
|
* Optional referential actions: for each Resource that holds a reference,
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* every relation holding one — the fields that hold it (`references`) and
|
|
15
|
+
* what deleting a referenced row does to its rows. Read once when the
|
|
16
|
+
* framework is constructed; never part of a Contract or its hash.
|
|
19
17
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
18
|
+
* An edge listed under Resource `R` says **deleting a `target` row does
|
|
19
|
+
* `onDelete` to rows of `R`**, and that `R`'s `references` fields hold the
|
|
20
|
+
* relation `field`'s reference.
|
|
23
21
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
22
|
+
* When absent, no cascade is planned and no transaction plan is refused
|
|
23
|
+
* because of one (`V1019`); a write that sets a relation and a field holding
|
|
24
|
+
* its reference both is not refused before the store; and a required
|
|
25
|
+
* relation can be satisfied only by writing the relation.
|
|
26
26
|
*/
|
|
27
27
|
readonly referentialActions?: ReferentialActionTable;
|
|
28
28
|
/**
|
|
29
29
|
* Optional create requirements: for each Resource, the values its store
|
|
30
|
-
* cannot create a row without
|
|
31
|
-
*
|
|
32
|
-
* like `model`, and never part of a Contract or its hash.
|
|
30
|
+
* cannot create a row without. Read once when the framework is constructed;
|
|
31
|
+
* never part of a Contract or its hash.
|
|
33
32
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* When absent, creates are not checked for missing values before they reach
|
|
34
|
+
* the store.
|
|
36
35
|
*/
|
|
37
36
|
readonly createRequirements?: CreateRequirementTable;
|
|
38
37
|
/**
|
|
39
|
-
* Optional callback transaction capability, required exactly when the
|
|
40
|
-
* model declares `transactions: "interactive"`. The callback
|
|
41
|
-
* transaction-scoped operation executor, never the provider
|
|
42
|
-
* every operation of a plan executes through it.
|
|
38
|
+
* Optional callback transaction capability, required exactly when the
|
|
39
|
+
* discovered model declares `transactions: "interactive"`. The callback
|
|
40
|
+
* receives only a transaction-scoped operation executor, never the provider
|
|
41
|
+
* client itself, and every operation of a plan executes through it.
|
|
43
42
|
*
|
|
44
|
-
* The implementation
|
|
45
|
-
* never a wrapper — and
|
|
46
|
-
* implied by that rejection.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
43
|
+
* The implementation must reject with the exact value `work` threw — by identity,
|
|
44
|
+
* never a wrapper — and must not resolve after `work` rejected; rollback is
|
|
45
|
+
* implied by that rejection. Guards, pipes, hooks and interceptors of every plan
|
|
46
|
+
* operation run inside `work` while the provider transaction is open, so they must
|
|
47
|
+
* not perform network I/O or unrelated slow work.
|
|
49
48
|
*/
|
|
50
49
|
transaction?<T>(work: (executor: AdapterOperationExecutor<M>) => Promise<T>): Promise<T>;
|
|
51
50
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
/** Codes of {@link ContractCompilerDiagnostic}. */
|
|
1
2
|
export type ContractCompilerDiagnosticCode = "COMPILER_UNKNOWN_RESOURCE" | "COMPILER_UNKNOWN_FIELD" | "COMPILER_RELATION_FIELD_CONFIG_UNSUPPORTED" | "COMPILER_INCOMPLETE_FIELD" | "COMPILER_STRUCTURAL_FIELD_OVERRIDE" | "COMPILER_MISSING_SCALAR" | "COMPILER_MISSING_ENUM" | "COMPILER_DANGLING_RELATION" | "COMPILER_UNAVAILABLE_OPERATION" | "COMPILER_UNAVAILABLE_CAPABILITY" | "COMPILER_INVALID_LIFECYCLE" | "COMPILER_MISSING_READ_BEHAVIOR" | "COMPILER_INVALID_BEHAVIOR_DEPENDENCY"
|
|
3
|
+
/**
|
|
4
|
+
* A field's `json` shape cannot apply: the field is not a Json field the
|
|
5
|
+
* adapter discovered, the value is not a shape built with `AvZ`, or the shape
|
|
6
|
+
* admits `null` or absence at its top level.
|
|
7
|
+
*/
|
|
8
|
+
| "COMPILER_INVALID_JSON_SHAPE"
|
|
2
9
|
/**
|
|
3
10
|
* The finished, hashed client projection is not a structurally valid
|
|
4
11
|
* `ClientContract`. A framework-bug guard: the server never serves what its own
|
|
@@ -6,6 +13,7 @@ export type ContractCompilerDiagnosticCode = "COMPILER_UNKNOWN_RESOURCE" | "COMP
|
|
|
6
13
|
* (`$.enums.status.values[1]`).
|
|
7
14
|
*/
|
|
8
15
|
| "COMPILER_INVALID_CLIENT_CONTRACT";
|
|
16
|
+
/** One reason a configuration could not be compiled into Contracts. */
|
|
9
17
|
export type ContractCompilerDiagnostic = {
|
|
10
18
|
readonly code: ContractCompilerDiagnosticCode;
|
|
11
19
|
readonly path: string;
|
|
@@ -2,11 +2,16 @@ import type { AdapterModel } from "../adapters/adapter-model.js";
|
|
|
2
2
|
import type { ResolvedFrameworkConfig } from "../config/config.resolver.js";
|
|
3
3
|
import type { ApplicationContract, ClientContract, CoreContract } from "../contracts/contract.js";
|
|
4
4
|
import type { ContractCompilerDiagnostic } from "./contract-compiler-diagnostic.js";
|
|
5
|
+
/** The Core, Application and Client Contracts compiled from one configuration. */
|
|
5
6
|
export type FrameworkContracts = {
|
|
6
7
|
readonly core: CoreContract;
|
|
7
8
|
readonly application: ApplicationContract;
|
|
8
9
|
readonly client: ClientContract;
|
|
9
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* The result of {@link compileContracts}: the three Contracts, or the
|
|
13
|
+
* diagnostics that stopped compilation.
|
|
14
|
+
*/
|
|
10
15
|
export type ContractCompilationResult = {
|
|
11
16
|
readonly ok: true;
|
|
12
17
|
readonly contracts: FrameworkContracts;
|
|
@@ -16,28 +21,12 @@ export type ContractCompilationResult = {
|
|
|
16
21
|
};
|
|
17
22
|
/**
|
|
18
23
|
* Whether writing `configured` VERBATIM as capability member `key` would advertise
|
|
19
|
-
* something `baseline` does not — the question the `fields` walk asks before it
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* Answered on the member's AXIS, off the same `satisfies`-pinned table the
|
|
23
|
-
* restriction walk and the output gate read, and the baseline is read through the
|
|
24
|
-
* shape's own owners: {@link holdsAt} for a position list, the `…At` readers for a
|
|
25
|
-
* scope-qualified token arm. Never off the runtime shape of the value: an
|
|
26
|
-
* object-shaped arm used to match no branch and pass unexamined, and `true` was
|
|
27
|
-
* compared to the baseline with `!== true`, which reads a qualified baseline as
|
|
28
|
-
* *not advertised* even where it holds in every position.
|
|
29
|
-
*
|
|
30
|
-
* The unqualified forms keep their meaning: `true` and a bare token list claim
|
|
31
|
-
* every position on the axis, so either one over a qualified baseline is an
|
|
32
|
-
* addition — writing it verbatim would widen the member.
|
|
33
|
-
*
|
|
34
|
-
* A key no axis covers, and a value its axis does not admit, are not additions;
|
|
35
|
-
* they are unrepresentable, and the output gate ({@link
|
|
36
|
-
* validateCapabilityRepresentation}) is the one place that says so. So is a
|
|
37
|
-
* position off the axis.
|
|
38
|
-
*
|
|
39
|
-
* Exported for its unit test only; it is not part of the package surface.
|
|
24
|
+
* something `baseline` does not — the question the `fields` walk asks before it copies
|
|
25
|
+
* a value onto an adapter field.
|
|
40
26
|
*/
|
|
41
27
|
export declare function capabilityAddsBaseline(fieldKind: "scalar" | "relation", baseline: Readonly<Record<string, unknown>>, key: string, configured: unknown): boolean;
|
|
42
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Compiles the Core Contract from the adapter model and the root
|
|
30
|
+
* configuration, then the Application and Client Contracts from it.
|
|
31
|
+
*/
|
|
43
32
|
export declare function compileContracts<M extends AdapterModel>(model: M, config: ResolvedFrameworkConfig<M>): Promise<ContractCompilationResult>;
|
|
@@ -6,6 +6,7 @@ import { DEFAULT_CONTRACT_LIMITS, } from "../contracts/contract-limits.js";
|
|
|
6
6
|
import { AVENTARA_PROTOCOL_VERSION } from "../contracts/contract-protocol.js";
|
|
7
7
|
import { advertisedDirectives, advertisedDirectivesAt, advertisedReducers, advertisedReducersAt, holdsAt, holdsInAnyScope, } from "../contracts/field.js";
|
|
8
8
|
import { isOperationVariantAvailable, } from "../contracts/resource-operations.js";
|
|
9
|
+
import { bindShapedField, jsonShapeOf } from "../json/json-shape.registry.js";
|
|
9
10
|
const UNIQUE_VARIANTS = [
|
|
10
11
|
["find", "unique"],
|
|
11
12
|
["update", "unique"],
|
|
@@ -99,6 +100,9 @@ function createBaseline(model) {
|
|
|
99
100
|
])),
|
|
100
101
|
limits: { ...DEFAULT_CONTRACT_LIMITS },
|
|
101
102
|
transactions: model.transactions,
|
|
103
|
+
...(model.referenceWrites === undefined
|
|
104
|
+
? {}
|
|
105
|
+
: { referenceWrites: model.referenceWrites }),
|
|
102
106
|
};
|
|
103
107
|
}
|
|
104
108
|
function scalarFieldPath(scope, resource, field) {
|
|
@@ -111,6 +115,10 @@ function structuralValueMatches(field, config, key) {
|
|
|
111
115
|
return JSON.stringify(field[key]) === JSON.stringify(config[key]);
|
|
112
116
|
}
|
|
113
117
|
function addConfiguredScalarField(config, path, diagnostics) {
|
|
118
|
+
if (config.json !== undefined) {
|
|
119
|
+
refuseJsonShape(path, "is not a field of the adapter model", diagnostics);
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
114
122
|
const missing = ["type", "nullable", "list"].filter((key) => config[key] === undefined);
|
|
115
123
|
if (missing.length > 0) {
|
|
116
124
|
diagnostics.push(diagnostic("COMPILER_INCOMPLETE_FIELD", path, `framework-added scalar field is missing required properties: ${missing.join(", ")}`));
|
|
@@ -157,6 +165,8 @@ export function capabilityAddsBaseline(fieldKind, baseline, key, configured) {
|
|
|
157
165
|
}
|
|
158
166
|
const baselineValue = baseline[key];
|
|
159
167
|
switch (axis.kind) {
|
|
168
|
+
case "limit":
|
|
169
|
+
return baselineValue !== undefined && configured !== baselineValue;
|
|
160
170
|
case "flag":
|
|
161
171
|
return configured === true && baselineValue !== true;
|
|
162
172
|
case "tokens": {
|
|
@@ -213,6 +223,31 @@ function advertisedTokensAt(tokenKey, baselineValue, position) {
|
|
|
213
223
|
? advertisedDirectivesAt(baselineValue, position)
|
|
214
224
|
: advertisedReducersAt(baselineValue, position);
|
|
215
225
|
}
|
|
226
|
+
function fieldLabel(path) {
|
|
227
|
+
const [, resource, , field] = path.split(".").slice(1);
|
|
228
|
+
return `${resource}.${field}`;
|
|
229
|
+
}
|
|
230
|
+
function refuseJsonShape(path, reason, diagnostics) {
|
|
231
|
+
diagnostics.push(diagnostic("COMPILER_INVALID_JSON_SHAPE", `${path}.json`, `field "${fieldLabel(path)}" ${reason}; only a Json field the adapter discovered may declare a "json" shape`));
|
|
232
|
+
}
|
|
233
|
+
function applyJsonShape(field, baselineField, shape, path, diagnostics) {
|
|
234
|
+
const type = baselineField.type;
|
|
235
|
+
if (type.scalar !== "json") {
|
|
236
|
+
refuseJsonShape(path, `is ${type.scalar === undefined ? "an enum" : `${/^[aeiou]/.test(type.scalar) ? "an" : "a"} ${type.scalar}`} field`, diagnostics);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const runtime = jsonShapeOf(shape);
|
|
240
|
+
if (runtime === undefined) {
|
|
241
|
+
diagnostics.push(diagnostic("COMPILER_INVALID_JSON_SHAPE", `${path}.json`, `field "${fieldLabel(path)}": "json" must be a shape built with AvZ from "@aventara/core/json"; a Zod schema or any other value is not accepted`));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (runtime.validate(null).length === 0 ||
|
|
245
|
+
runtime.validate(undefined).length === 0) {
|
|
246
|
+
diagnostics.push(diagnostic("COMPILER_INVALID_JSON_SHAPE", `${path}.json`, `field "${fieldLabel(path)}": a field's shape cannot admit null or absence at its top level; whether the field may be null comes from the adapter model`));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
field.json = cloneValue(runtime.schema);
|
|
250
|
+
}
|
|
216
251
|
function applyExistingScalarConfig(field, baselineField, config, path, diagnostics) {
|
|
217
252
|
for (const key of ["type", "nullable", "list"]) {
|
|
218
253
|
if (!structuralValueMatches(field, config, key)) {
|
|
@@ -222,6 +257,9 @@ function applyExistingScalarConfig(field, baselineField, config, path, diagnosti
|
|
|
222
257
|
if (config.format !== undefined) {
|
|
223
258
|
field.format = config.format;
|
|
224
259
|
}
|
|
260
|
+
if (config.json !== undefined) {
|
|
261
|
+
applyJsonShape(field, baselineField, config.json, path, diagnostics);
|
|
262
|
+
}
|
|
225
263
|
if (config.lifecycle !== undefined) {
|
|
226
264
|
diagnostics.push(diagnostic("COMPILER_INVALID_LIFECYCLE", `${path}.lifecycle`, "existing adapter field lifecycle cannot be redefined by framework field configuration"));
|
|
227
265
|
}
|
|
@@ -243,6 +281,9 @@ function applyFrameworkOwnedScalarConfig(field, config, path, diagnostics) {
|
|
|
243
281
|
field[key] = cloneValue(config[key]);
|
|
244
282
|
}
|
|
245
283
|
}
|
|
284
|
+
if (config.json !== undefined) {
|
|
285
|
+
refuseJsonShape(path, "is not a field of the adapter model", diagnostics);
|
|
286
|
+
}
|
|
246
287
|
if (config.format !== undefined) {
|
|
247
288
|
field.format = config.format;
|
|
248
289
|
}
|
|
@@ -391,6 +432,7 @@ const RELATION_CAPABILITY_AXES = {
|
|
|
391
432
|
},
|
|
392
433
|
create: { kind: "scopedTokens", tokenKey: "directives", axis: WRITE_SCOPES },
|
|
393
434
|
update: { kind: "scopedTokens", tokenKey: "directives", axis: WRITE_SCOPES },
|
|
435
|
+
nestedWriteFilter: { kind: "limit", values: ["scalar"] },
|
|
394
436
|
};
|
|
395
437
|
function capabilityAxisFor(kind, key) {
|
|
396
438
|
const axes = kind === "scalar" ? SCALAR_CAPABILITY_AXES : RELATION_CAPABILITY_AXES;
|
|
@@ -478,6 +520,10 @@ function applyScopedTokenRestriction(tokenKey, axis, baselineValue, current, key
|
|
|
478
520
|
return true;
|
|
479
521
|
}
|
|
480
522
|
function applyCapabilityRestriction(fieldKind, inherited, restoration, current, key, value, path, diagnostics) {
|
|
523
|
+
if (capabilityAxisFor(fieldKind, key)?.kind === "limit") {
|
|
524
|
+
unavailableCapability(path, `capability "${key}" is an adapter limit and takes no restriction`, diagnostics);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
481
527
|
if (value === false) {
|
|
482
528
|
delete current[key];
|
|
483
529
|
return;
|
|
@@ -728,6 +774,10 @@ function positionListFault(axis, positions) {
|
|
|
728
774
|
}
|
|
729
775
|
function representationFault(axis, value) {
|
|
730
776
|
switch (axis.kind) {
|
|
777
|
+
case "limit":
|
|
778
|
+
return typeof value === "string" && axis.values.includes(value)
|
|
779
|
+
? undefined
|
|
780
|
+
: `one of ${axis.values.join(", ")} is expected`;
|
|
731
781
|
case "flag":
|
|
732
782
|
return typeof value === "boolean" ? undefined : "a boolean is expected";
|
|
733
783
|
case "tokens":
|
|
@@ -827,6 +877,20 @@ function projectDerivedScope(model, scopeName, core, scope, diagnostics) {
|
|
|
827
877
|
validateProjection(projection, baseline, scopeName, scope.restrictions, diagnostics);
|
|
828
878
|
return projection;
|
|
829
879
|
}
|
|
880
|
+
function bindShapedFields(contract, fields) {
|
|
881
|
+
for (const [resourceName, resource] of Object.entries(contract.resources)) {
|
|
882
|
+
for (const [fieldName, field] of Object.entries(resource.fields)) {
|
|
883
|
+
if (field.kind !== "scalar" || field.json === undefined) {
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
const shape = jsonShapeOf(fields?.[resourceName]?.[fieldName]?.json);
|
|
887
|
+
if (shape === undefined) {
|
|
888
|
+
throw new Error(`the Contract compiler found no shape for shaped field "${resourceName}.${fieldName}"`);
|
|
889
|
+
}
|
|
890
|
+
bindShapedField(field, shape);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
830
894
|
export async function compileContracts(model, config) {
|
|
831
895
|
const compilerConfig = config;
|
|
832
896
|
const diagnostics = [];
|
|
@@ -856,6 +920,9 @@ export async function compileContracts(model, config) {
|
|
|
856
920
|
],
|
|
857
921
|
};
|
|
858
922
|
}
|
|
923
|
+
bindShapedFields(core, compilerConfig.root.fields);
|
|
924
|
+
bindShapedFields(application, compilerConfig.application.fields);
|
|
925
|
+
bindShapedFields(client, compilerConfig.client.fields);
|
|
859
926
|
return {
|
|
860
927
|
ok: true,
|
|
861
928
|
contracts: { core, application, client },
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A `fields` block whose resource and field names are both unconstrained.
|
|
3
|
-
*
|
|
4
|
-
* The `undefined` in the value position is required, not incidental: under
|
|
5
|
-
* `exactOptionalPropertyTypes` a real `fields` block is a `Partial` record,
|
|
6
|
-
* so its values include `undefined` and it would not otherwise be assignable
|
|
7
|
-
* to the widest block.
|
|
8
|
-
*/
|
|
1
|
+
/** A `fields` block whose resource and field names are both unconstrained. */
|
|
9
2
|
export type AnyFieldsBlock = Readonly<Record<string, Readonly<Record<string, unknown>> | undefined>>;
|
|
10
3
|
/** The widest scope overlay: a `fields` block, and anything else. */
|
|
11
4
|
export type AnyScopeConfig = {
|
|
@@ -13,24 +6,10 @@ export type AnyScopeConfig = {
|
|
|
13
6
|
readonly fields?: AnyFieldsBlock;
|
|
14
7
|
};
|
|
15
8
|
/**
|
|
16
|
-
* The widest
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* `createFramework`. Because it constrains nothing, a `FrameworkConfig<M>`
|
|
21
|
-
* written without a `Cfg` argument behaves exactly as it did before `Cfg`
|
|
22
|
-
* existed: narrowing below the resource key is opt-in, never imposed.
|
|
23
|
-
*
|
|
24
|
-
* `Cfg` is the authored configuration literal itself, not a shape the
|
|
25
|
-
* framework validates. Its only job is to carry the field names the
|
|
26
|
-
* configuration adds — at the root layer and at each scope overlay — so that
|
|
27
|
-
* a resource's effective field-key set is computable from the adapter model
|
|
28
|
-
* plus the configuration alone, and never from a compiled Contract.
|
|
29
|
-
*
|
|
30
|
-
* The index signature is what makes "widest" true. Without it this is a weak
|
|
31
|
-
* type, and a configuration that happens to declare no `fields`,
|
|
32
|
-
* `application` or `client` would have no property in common with it and be
|
|
33
|
-
* rejected — the opposite of the intended default.
|
|
9
|
+
* The widest configuration type: every resource admits every field name. It
|
|
10
|
+
* is the default `Cfg` of `FrameworkConfig`, `Framework` and
|
|
11
|
+
* `createFramework`; pass your own configuration's type instead to get field
|
|
12
|
+
* names checked.
|
|
34
13
|
*/
|
|
35
14
|
export type AnyFrameworkConfig = {
|
|
36
15
|
readonly [key: string]: unknown;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
/** Codes of {@link ConfigDiagnostic}. */
|
|
1
2
|
export type ConfigDiagnosticCode = "CONFIG_REQUIRED" | "CONFIG_UNKNOWN_PROPERTY" | "CONFIG_INVALID_TYPE" | "CONFIG_INVALID_VALUE";
|
|
3
|
+
/** One problem found in a configuration, at its path. */
|
|
2
4
|
export type ConfigDiagnostic = {
|
|
3
5
|
readonly code: ConfigDiagnosticCode;
|
|
4
6
|
readonly path: string;
|
|
@@ -1,25 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One layer of the configuration
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Application and Client Contracts.
|
|
6
|
-
*
|
|
7
|
-
* The one source of truth for the layer set. {@link RequestOrigin} is derived
|
|
8
|
-
* from it rather than declared beside it, so the two cannot drift.
|
|
9
|
-
*
|
|
10
|
-
* Deliberately not named `…Scope`: that word already carries several unrelated
|
|
11
|
-
* meanings in this package (the framework scope configuration, the pipeline
|
|
12
|
-
* `scope`, and the capability positions `ProjectionScope`/`WriteScope`), and a
|
|
13
|
-
* layer of the configuration tree is none of them.
|
|
2
|
+
* One layer of the configuration: `root` is the configuration's own top level
|
|
3
|
+
* and compiles to the Core Contract; `application` and `client` are overlays
|
|
4
|
+
* on top of it and compile to the Application and Client Contracts.
|
|
14
5
|
*/
|
|
15
6
|
export type ConfigLayer = "root" | "application" | "client";
|
|
16
7
|
/**
|
|
17
|
-
* The layers a request can
|
|
18
|
-
* `
|
|
19
|
-
*
|
|
20
|
-
* not an origin.
|
|
21
|
-
*
|
|
22
|
-
* It is also the set of overlays a configuration may declare, because each
|
|
23
|
-
* origin sees exactly the root layer plus its own overlay.
|
|
8
|
+
* The layers a request can come from: `application` (in-process calls) or
|
|
9
|
+
* `client` (remote calls). Each sees the root configuration plus its own
|
|
10
|
+
* overlay.
|
|
24
11
|
*/
|
|
25
12
|
export type RequestOrigin = Exclude<ConfigLayer, "root">;
|
|
@@ -2,39 +2,34 @@ import type { Adapter } from "../adapters/adapter.interface.js";
|
|
|
2
2
|
import type { AdapterModel } from "../adapters/adapter-model.js";
|
|
3
3
|
import type { TransactionCapability } from "../contracts/transaction-capability.js";
|
|
4
4
|
import type { FrameworkDiagnosticsSink } from "../runtime/diagnostics-reporter.js";
|
|
5
|
+
import type { FrameworkMisuseSink } from "../runtime/misuse-reporter.js";
|
|
5
6
|
import type { FrameworkBehaviorsConfig } from "./framework-behaviors-config.js";
|
|
6
7
|
import type { FrameworkConfig, FrameworkLimitsConfig } from "./framework-config.js";
|
|
7
8
|
import type { FrameworkFieldsConfig } from "./framework-fields-config.js";
|
|
8
9
|
import type { FrameworkRestrictionsConfig } from "./framework-restrictions-config.js";
|
|
9
10
|
import type { ResolvedPipelineConfig } from "./pipeline-config.js";
|
|
11
|
+
/** One resolved layer of the configuration: root, application or client. */
|
|
10
12
|
export type ResolvedFrameworkScopeConfig<M extends AdapterModel = AdapterModel> = {
|
|
11
13
|
readonly fields: FrameworkFieldsConfig<M> | undefined;
|
|
12
|
-
/**
|
|
13
|
-
* The **erased** behaviors view, deliberately not `FrameworkBehaviorsConfig<M>`.
|
|
14
|
-
*
|
|
15
|
-
* A compute context carries `resource: R`, so a behavior bound to one
|
|
16
|
-
* Resource is a callback that accepts only that Resource's name. That makes
|
|
17
|
-
* `FrameworkBehaviorsConfig<M>` contravariant in the Resource key and *not*
|
|
18
|
-
* a subtype of the widest instantiation — correctly so. The runtime dispatches
|
|
19
|
-
* by string key and can never recover which literal it holds, so it consumes
|
|
20
|
-
* behaviors erased, exactly as it consumes the compiled Contract: narrowing is
|
|
21
|
-
* an authoring-time affordance, and resolution is the boundary where it ends.
|
|
22
|
-
*/
|
|
14
|
+
/** The behaviors configuration, with Resource and field keys as plain strings. */
|
|
23
15
|
readonly behaviors: FrameworkBehaviorsConfig | undefined;
|
|
24
16
|
readonly restrictions: FrameworkRestrictionsConfig<M> | undefined;
|
|
25
17
|
readonly limits: FrameworkLimitsConfig | undefined;
|
|
26
18
|
readonly pipelines: ResolvedPipelineConfig;
|
|
27
19
|
readonly transactions: TransactionCapability | undefined;
|
|
28
20
|
};
|
|
21
|
+
/** A configuration after defaults and overlays are resolved, before compilation. */
|
|
29
22
|
export type ResolvedFrameworkConfig<M extends AdapterModel = AdapterModel> = {
|
|
30
23
|
/**
|
|
31
|
-
* The mount path in its canonical form: `""` at the root, otherwise
|
|
32
|
-
*
|
|
24
|
+
* The mount path in its canonical form: `""` at the root, otherwise
|
|
25
|
+
* `/seg(/seg)*`.
|
|
33
26
|
*/
|
|
34
27
|
readonly entrypoint: string;
|
|
35
28
|
readonly adapter: Adapter<M>;
|
|
36
|
-
/**
|
|
29
|
+
/** The diagnostics sink, from the root configuration only. */
|
|
37
30
|
readonly diagnostics: FrameworkDiagnosticsSink | undefined;
|
|
31
|
+
/** The misuse sink, from the root configuration only. */
|
|
32
|
+
readonly misuse: FrameworkMisuseSink | undefined;
|
|
38
33
|
readonly root: ResolvedFrameworkScopeConfig<M>;
|
|
39
34
|
readonly application: ResolvedFrameworkScopeConfig<M>;
|
|
40
35
|
readonly client: ResolvedFrameworkScopeConfig<M>;
|
|
@@ -103,6 +103,7 @@ export function resolveFrameworkConfig(config) {
|
|
|
103
103
|
entrypoint: resolveEntrypoint(config.entrypoint),
|
|
104
104
|
adapter: config.adapter,
|
|
105
105
|
diagnostics: config.diagnostics,
|
|
106
|
+
misuse: config.misuse,
|
|
106
107
|
root: resolveScope(root),
|
|
107
108
|
application: resolveScope(root, config.application),
|
|
108
109
|
client: resolveScope(root, config.client),
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ConfigDiagnostic } from "./config-diagnostic.js";
|
|
2
2
|
/**
|
|
3
3
|
* The refusal of an entrypoint whose mount path is not canonical, from the
|
|
4
|
-
* normalizer's message: it names the defect and the rule — never the value, which
|
|
5
|
-
*
|
|
4
|
+
* normalizer's message: it names the defect and the rule — never the value, which may
|
|
5
|
+
* carry credentials.
|
|
6
6
|
*/
|
|
7
7
|
export declare function entrypointRefusalDiagnostic(message: string): ConfigDiagnostic;
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Checks the structure of a configuration object and reports each problem as
|
|
10
|
+
* a {@link ConfigDiagnostic}.
|
|
11
|
+
*/
|
|
9
12
|
export declare function validateFrameworkConfig(value: unknown): readonly ConfigDiagnostic[];
|
|
@@ -10,6 +10,7 @@ const ROOT_PROPERTIES = new Set([
|
|
|
10
10
|
"pipelines",
|
|
11
11
|
"transactions",
|
|
12
12
|
"diagnostics",
|
|
13
|
+
"misuse",
|
|
13
14
|
"application",
|
|
14
15
|
"client",
|
|
15
16
|
]);
|
|
@@ -206,6 +207,11 @@ export function validateFrameworkConfig(value) {
|
|
|
206
207
|
typeof value.diagnostics !== "function") {
|
|
207
208
|
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "diagnostics", "diagnostics must be a function when provided"));
|
|
208
209
|
}
|
|
210
|
+
if (Object.hasOwn(value, "misuse") &&
|
|
211
|
+
value.misuse !== undefined &&
|
|
212
|
+
typeof value.misuse !== "function") {
|
|
213
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "misuse", "misuse must be a function when provided"));
|
|
214
|
+
}
|
|
209
215
|
if (Object.hasOwn(value, "application")) {
|
|
210
216
|
validateScope(value.application, "application", diagnostics);
|
|
211
217
|
}
|
|
@@ -2,55 +2,33 @@ import type { AdapterModel } from "../adapters/adapter-model.js";
|
|
|
2
2
|
import type { AdapterFieldKey } from "../adapters/adapter-resource.js";
|
|
3
3
|
import type { FrameworkResourceKey, FrameworkScalarFieldConfig, FrameworkScalarTypeRef } from "./framework-fields-config.js";
|
|
4
4
|
/**
|
|
5
|
-
* What
|
|
6
|
-
*
|
|
7
|
-
* Everything in {@link FrameworkScalarFieldConfig} except `type`: the adapter
|
|
8
|
-
* owns a discovered field's scalar/enum identity, so restating it here would
|
|
9
|
-
* create a second source of truth for a fact discovery already settled.
|
|
10
|
-
* `type?: never` rather than an omission so the rejection is a pointed
|
|
11
|
-
* assignability error at the offending property, not a whole-object one.
|
|
5
|
+
* What a configuration may say about a field the adapter already discovered:
|
|
6
|
+
* everything in {@link FrameworkScalarFieldConfig} except `type`.
|
|
12
7
|
*/
|
|
13
8
|
export type KnownScalarFieldConfig<M extends AdapterModel = AdapterModel> = Omit<FrameworkScalarFieldConfig<M>, "type"> & {
|
|
14
9
|
readonly type?: never;
|
|
15
10
|
};
|
|
16
11
|
/**
|
|
17
|
-
* A scalar field the configuration adds, which the adapter
|
|
18
|
-
*
|
|
19
|
-
* Nothing upstream can supply the structural leaves for a name that exists
|
|
20
|
-
* nowhere in the provider, so `type`, `nullable` and `list` are required
|
|
21
|
-
* here while they stay optional on the augmentation shape.
|
|
12
|
+
* A scalar field the configuration adds, which the adapter did not discover.
|
|
13
|
+
* `type`, `nullable` and `list` are required; it takes no `json` shape.
|
|
22
14
|
*/
|
|
23
|
-
export type AddedScalarFieldConfig<M extends AdapterModel = AdapterModel> = Omit<FrameworkScalarFieldConfig<M>, "type" | "nullable" | "list"> & {
|
|
15
|
+
export type AddedScalarFieldConfig<M extends AdapterModel = AdapterModel> = Omit<FrameworkScalarFieldConfig<M>, "type" | "nullable" | "list" | "json"> & {
|
|
24
16
|
readonly type: FrameworkScalarTypeRef<M>;
|
|
25
17
|
readonly nullable: boolean;
|
|
26
18
|
readonly list: boolean;
|
|
19
|
+
readonly json?: never;
|
|
27
20
|
};
|
|
28
|
-
/**
|
|
29
|
-
* The shape one authored field name admits, by what the adapter knows of it.
|
|
30
|
-
*
|
|
31
|
-
* Where there is no discovered key set to discriminate on, the answer is today's
|
|
32
|
-
* undivided {@link FrameworkScalarFieldConfig}.
|
|
33
|
-
*/
|
|
21
|
+
/** The shape one authored field name admits, by what the adapter knows of it. */
|
|
34
22
|
type ScalarFieldConfigFor<M extends AdapterModel, R extends FrameworkResourceKey<M>, F> = string extends AdapterFieldKey<M, R> ? FrameworkScalarFieldConfig<M> : F extends AdapterFieldKey<M, R> ? M["resources"][R]["fields"][F] extends {
|
|
35
23
|
readonly kind: "relation";
|
|
36
|
-
} ? never :
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
* signature, and an index signature cannot tell a discovered name from an
|
|
45
|
-
* added one. The three buckets are only reachable from a mapped type keyed
|
|
46
|
-
* on `keyof Self`.
|
|
47
|
-
*
|
|
48
|
-
* The self-reference is legal here despite the workstream's stopping rule 3
|
|
49
|
-
* because the `fields` block carries no callbacks: nothing inside it needs
|
|
50
|
-
* contextual typing, so there is no inference to collapse. A callback
|
|
51
|
-
* appearing in this block is the signal that this encoding has stopped
|
|
52
|
-
* being safe.
|
|
53
|
-
*/
|
|
24
|
+
} ? never : M["resources"][R]["fields"][F] extends {
|
|
25
|
+
readonly type: {
|
|
26
|
+
readonly scalar: "json";
|
|
27
|
+
};
|
|
28
|
+
} ? KnownScalarFieldConfig<M> : Omit<KnownScalarFieldConfig<M>, "json"> & {
|
|
29
|
+
readonly json?: never;
|
|
30
|
+
} : AddedScalarFieldConfig<M>;
|
|
31
|
+
/** The shape an authored `fields` block is checked against, keyed on its own names. */
|
|
54
32
|
export type FrameworkFieldsShape<M extends AdapterModel, Self> = {
|
|
55
33
|
readonly [R in keyof Self]: R extends FrameworkResourceKey<M> ? {
|
|
56
34
|
readonly [F in keyof Self[R]]: ScalarFieldConfigFor<M, R, F>;
|
|
@@ -59,17 +37,6 @@ export type FrameworkFieldsShape<M extends AdapterModel, Self> = {
|
|
|
59
37
|
/**
|
|
60
38
|
* Authors stage 1 of a configuration: the `fields` block, checked against
|
|
61
39
|
* what the adapter discovered and returned unwidened.
|
|
62
|
-
*
|
|
63
|
-
* The returned literal is stage 2's `Cfg` fields block, so the names it adds
|
|
64
|
-
* are nameable from `restrictions`, `behaviors` and `dependsOn`.
|
|
65
|
-
*
|
|
66
|
-
* `M` is bound by the **model value**, not a type argument: TypeScript has
|
|
67
|
-
* no partial type-argument inference, so `defineFields<M>({ … })` would
|
|
68
|
-
* force `Self` to be written out by hand. The model is already a value at
|
|
69
|
-
* the authoring site, so binding it there costs nothing.
|
|
70
|
-
*
|
|
71
|
-
* The model argument is type-level only — the returned block is the authored
|
|
72
|
-
* one, unchanged.
|
|
73
40
|
*/
|
|
74
41
|
export declare function defineFields<M extends AdapterModel, const Self extends FrameworkFieldsShape<M, Self>>(_model: M, fields: Self): Self;
|
|
75
42
|
export {};
|
|
@@ -13,41 +13,18 @@ import type { FrameworkResourceKey } from "./framework-fields-config.js";
|
|
|
13
13
|
type FieldsBlockOf<Layer> = Layer extends {
|
|
14
14
|
readonly fields?: infer Block;
|
|
15
15
|
} ? Block extends AnyFieldsBlock ? Block : never : never;
|
|
16
|
-
/**
|
|
17
|
-
* The field names a single `fields` block declares for `R`.
|
|
18
|
-
*
|
|
19
|
-
* The `[Block] extends [never]` guard is load-bearing, not defensive: `keyof
|
|
20
|
-
* never` is `string | number | symbol`, so a layer with no `fields` block
|
|
21
|
-
* would otherwise widen the whole effective key set back to `string` and
|
|
22
|
-
* silently erase every narrowing downstream of it.
|
|
23
|
-
*/
|
|
16
|
+
/** The field names a single `fields` block declares for `R`. */
|
|
24
17
|
type FieldsBlockKey<Block, R extends string> = [Block] extends [never] ? never : R extends keyof Block ? Extract<keyof NonNullable<Block[R]>, string> : never;
|
|
25
|
-
/**
|
|
26
|
-
* The field names `Cfg`'s own root `fields` block adds for `R`.
|
|
27
|
-
*
|
|
28
|
-
* Under the default `Cfg` this degenerates to `string`: `AnyFieldsBlock` maps
|
|
29
|
-
* every resource name to an open record, so the effective key set widens back to
|
|
30
|
-
* an index-signature-compatible `string` and today's wide acceptance is preserved.
|
|
31
|
-
*/
|
|
18
|
+
/** The field names the configuration's root `fields` block adds to Resource `R`. */
|
|
32
19
|
export type ConfigFieldKey<M extends AdapterModel, Cfg, R extends FrameworkResourceKey<M>> = FieldsBlockKey<FieldsBlockOf<Cfg>, R>;
|
|
33
|
-
/**
|
|
34
|
-
* The single answer to "what are `R`'s field names under this configuration".
|
|
35
|
-
*
|
|
36
|
-
* Nothing else in the framework may compute a field-key set; `restrictions`,
|
|
37
|
-
* `behaviors` and `dependsOn` all key on this type.
|
|
38
|
-
*
|
|
39
|
-
* There is deliberately no subtraction here. A `HiddenFieldKey` subtraction would
|
|
40
|
-
* contradict the Accepted ADR and must not be reintroduced.
|
|
41
|
-
*/
|
|
20
|
+
/** The single answer to "what are `R`'s field names under this configuration". */
|
|
42
21
|
export type EffectiveFieldKey<M extends AdapterModel, Cfg, R extends FrameworkResourceKey<M>> = AdapterFieldKey<M, R> | ConfigFieldKey<M, Cfg, R>;
|
|
43
22
|
/** The overlay `Cfg` carries for `Origin`, or `never` when it declares none. */
|
|
44
23
|
type OriginOverlayOf<Cfg, Origin extends RequestOrigin> = Origin extends keyof Cfg ? NonNullable<Cfg[Origin]> : never;
|
|
45
24
|
/**
|
|
46
|
-
* `R`'s
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* Origins are siblings, never parents: an overlay contributes to its own origin
|
|
50
|
-
* only, so a name added under `client` is not visible under `application`.
|
|
25
|
+
* `R`'s field names as seen from one request origin: the root layer plus
|
|
26
|
+
* whatever the `application` or `client` overlay adds. A name added under
|
|
27
|
+
* `client` is not visible under `application`, and the other way round.
|
|
51
28
|
*/
|
|
52
29
|
export type EffectiveFieldKeyAt<M extends AdapterModel, Cfg, R extends FrameworkResourceKey<M>, Origin extends RequestOrigin> = EffectiveFieldKey<M, Cfg, R> | FieldsBlockKey<FieldsBlockOf<OriginOverlayOf<Cfg, Origin>>, R>;
|
|
53
30
|
export {};
|