@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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PROJECTION_SCOPES, WRITE_SCOPES } from "./capability.js";
|
|
2
2
|
import { AVENTARA_PROTOCOL_VERSION } from "./contract-protocol.js";
|
|
3
3
|
import { LIFECYCLE_TRAITS } from "./lifecycle-trait.js";
|
|
4
|
+
import { REFERENCE_WRITE_LIMITS } from "./reference-write-limit.js";
|
|
4
5
|
import { TRANSACTION_CAPABILITIES } from "./transaction-capability.js";
|
|
5
6
|
const STRING = { is: "string" };
|
|
6
7
|
const NUMBER = { is: "number" };
|
|
@@ -106,7 +107,7 @@ const SCALAR_FIELD = object({
|
|
|
106
107
|
create: list(literal(SCALAR_MUTATION_DIRECTIVES)),
|
|
107
108
|
update: list(literal(SCALAR_MUTATION_DIRECTIVES)),
|
|
108
109
|
}),
|
|
109
|
-
}, { format: STRING });
|
|
110
|
+
}, { format: STRING, json: { is: "jsonSchema" } });
|
|
110
111
|
const RELATION_FIELD = object({
|
|
111
112
|
kind: literal(["relation"]),
|
|
112
113
|
target: STRING,
|
|
@@ -124,6 +125,7 @@ const RELATION_FIELD = object({
|
|
|
124
125
|
})),
|
|
125
126
|
create: RELATION_MUTATION,
|
|
126
127
|
update: RELATION_MUTATION,
|
|
128
|
+
nestedWriteFilter: literal(["scalar"]),
|
|
127
129
|
}),
|
|
128
130
|
});
|
|
129
131
|
const OPERATION_AVAILABILITY = union(literal([true]), object({ projection: literal(PROJECTION_SCOPES) }, { write: literal(WRITE_SCOPES) }), object({ write: literal(WRITE_SCOPES) }, { projection: literal(PROJECTION_SCOPES) }));
|
|
@@ -177,7 +179,7 @@ export const CLIENT_CONTRACT_STRUCTURE = object({
|
|
|
177
179
|
version: literal([AVENTARA_PROTOCOL_VERSION]),
|
|
178
180
|
hash: { is: "prefixed", prefix: "sha256:" },
|
|
179
181
|
}),
|
|
180
|
-
});
|
|
182
|
+
}, { referenceWrites: literal(REFERENCE_WRITE_LIMITS) });
|
|
181
183
|
export function validateClientContractStructure(value) {
|
|
182
184
|
const defect = defectOf(CLIENT_CONTRACT_STRUCTURE, value, "$", 0);
|
|
183
185
|
if (defect !== undefined) {
|
|
@@ -233,6 +235,101 @@ function defectOf(node, value, path, depth) {
|
|
|
233
235
|
}
|
|
234
236
|
case "union":
|
|
235
237
|
return unionDefect(node.arms, value, path, depth);
|
|
238
|
+
case "jsonSchema":
|
|
239
|
+
return jsonSchemaDefect(value, path, depth);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const JSON_SCHEMA_SCALAR_TYPES = [
|
|
243
|
+
"string",
|
|
244
|
+
"number",
|
|
245
|
+
"integer",
|
|
246
|
+
"boolean",
|
|
247
|
+
"null",
|
|
248
|
+
];
|
|
249
|
+
const JSON_PRIMITIVE = union(STRING, NUMBER, BOOLEAN);
|
|
250
|
+
function jsonSchemaDefect(value, path, depth) {
|
|
251
|
+
const kind = kindDefect(value, "object", path, depth);
|
|
252
|
+
if (kind !== undefined)
|
|
253
|
+
return kind;
|
|
254
|
+
const schema = value;
|
|
255
|
+
const keys = Object.keys(schema).sort().join(",");
|
|
256
|
+
const child = (member, entry, at = `${path}.${member}`) => jsonSchemaDefect(entry, at, depth + 1);
|
|
257
|
+
const children = (member, minimum) => {
|
|
258
|
+
const listKind = kindDefect(schema[member], "array", `${path}.${member}`, depth + 1);
|
|
259
|
+
if (listKind !== undefined)
|
|
260
|
+
return listKind;
|
|
261
|
+
const entries = schema[member];
|
|
262
|
+
if (entries.length < minimum) {
|
|
263
|
+
return at(`${path}.${member}`, depth + 1, `needs at least ${minimum} entries`);
|
|
264
|
+
}
|
|
265
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
266
|
+
const defect = child(member, entries[index], `${path}.${member}[${index}]`);
|
|
267
|
+
if (defect !== undefined)
|
|
268
|
+
return defect;
|
|
269
|
+
}
|
|
270
|
+
return undefined;
|
|
271
|
+
};
|
|
272
|
+
switch (keys) {
|
|
273
|
+
case "type":
|
|
274
|
+
return literalDefect(JSON_SCHEMA_SCALAR_TYPES, schema.type, `${path}.type`, depth + 1);
|
|
275
|
+
case "const":
|
|
276
|
+
return Object.is(schema.const, null)
|
|
277
|
+
? undefined
|
|
278
|
+
: defectOf(JSON_PRIMITIVE, schema.const, `${path}.const`, depth + 1);
|
|
279
|
+
case "enum,type":
|
|
280
|
+
return (literalDefect(["string"], schema.type, `${path}.type`, depth + 1) ??
|
|
281
|
+
defectOf({ is: "list", item: STRING, nonEmpty: true, unique: true }, schema.enum, `${path}.enum`, depth + 1));
|
|
282
|
+
case "additionalProperties,properties,type":
|
|
283
|
+
case "additionalProperties,properties,required,type": {
|
|
284
|
+
const typeDefect = literalDefect(["object"], schema.type, `${path}.type`, depth + 1) ??
|
|
285
|
+
literalDefect([false], schema.additionalProperties, `${path}.additionalProperties`, depth + 1);
|
|
286
|
+
if (typeDefect !== undefined)
|
|
287
|
+
return typeDefect;
|
|
288
|
+
const propertiesKind = kindDefect(schema.properties, "object", `${path}.properties`, depth + 1);
|
|
289
|
+
if (propertiesKind !== undefined)
|
|
290
|
+
return propertiesKind;
|
|
291
|
+
const properties = schema.properties;
|
|
292
|
+
for (const name of Object.keys(properties)) {
|
|
293
|
+
const defect = child("properties", properties[name], `${path}.properties.${name}`);
|
|
294
|
+
if (defect !== undefined)
|
|
295
|
+
return defect;
|
|
296
|
+
}
|
|
297
|
+
if (!Object.hasOwn(schema, "required"))
|
|
298
|
+
return undefined;
|
|
299
|
+
const requiredDefect = defectOf({ is: "list", item: STRING, nonEmpty: false, unique: true }, schema.required, `${path}.required`, depth + 1);
|
|
300
|
+
if (requiredDefect !== undefined)
|
|
301
|
+
return requiredDefect;
|
|
302
|
+
const undeclared = schema.required.findIndex((name) => !Object.hasOwn(properties, name));
|
|
303
|
+
return undeclared === -1
|
|
304
|
+
? undefined
|
|
305
|
+
: at(`${path}.required[${undeclared}]`, depth + 1, "names a key that is not one of the object's properties");
|
|
306
|
+
}
|
|
307
|
+
case "additionalProperties,type":
|
|
308
|
+
return (literalDefect(["object"], schema.type, `${path}.type`, depth + 1) ??
|
|
309
|
+
child("additionalProperties", schema.additionalProperties));
|
|
310
|
+
case "items,type":
|
|
311
|
+
return (literalDefect(["array"], schema.type, `${path}.type`, depth + 1) ??
|
|
312
|
+
child("items", schema.items));
|
|
313
|
+
case "items,maxItems,minItems,prefixItems,type": {
|
|
314
|
+
const defect = literalDefect(["array"], schema.type, `${path}.type`, depth + 1) ??
|
|
315
|
+
literalDefect([false], schema.items, `${path}.items`, depth + 1) ??
|
|
316
|
+
children("prefixItems", 1);
|
|
317
|
+
if (defect !== undefined)
|
|
318
|
+
return defect;
|
|
319
|
+
const length = schema.prefixItems.length;
|
|
320
|
+
for (const bound of ["minItems", "maxItems"]) {
|
|
321
|
+
if (schema[bound] !== length) {
|
|
322
|
+
return at(`${path}.${bound}`, depth + 1, `is ${JSON.stringify(schema[bound])}, not the tuple's length ${length}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
case "anyOf":
|
|
328
|
+
return children("anyOf", 2);
|
|
329
|
+
case "oneOf":
|
|
330
|
+
return children("oneOf", 1);
|
|
331
|
+
default:
|
|
332
|
+
return at(path, depth, `is not a Json Shape schema: its keywords {${keys}} are not one of the forms Aventara emits`);
|
|
236
333
|
}
|
|
237
334
|
}
|
|
238
335
|
function literalDefect(values, value, path, depth) {
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import type { Contract } from "./contract.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* `TypeError
|
|
5
|
-
* consumers and error text are unchanged, while callers that must attribute
|
|
6
|
-
* a canonicalization failure to its input can recognize this class exactly.
|
|
3
|
+
* Thrown when a value cannot be written as canonical JSON (RFC 8785). It is a
|
|
4
|
+
* `TypeError`.
|
|
7
5
|
*/
|
|
8
6
|
export declare class CanonicalJsonError extends TypeError {
|
|
9
7
|
}
|
|
10
|
-
/** RFC 8785
|
|
8
|
+
/** Canonical JSON (RFC 8785) for a JSON-safe value. */
|
|
11
9
|
export declare function canonicalizeJson(value: unknown): string;
|
|
12
|
-
/**
|
|
10
|
+
/** The Contract as canonical JSON (RFC 8785): the same bytes for the same Contract. */
|
|
13
11
|
export declare function canonicalizeContract<C extends Contract>(contract: C): string;
|
|
@@ -83,10 +83,42 @@ function normalizeCapabilities(capabilities) {
|
|
|
83
83
|
}
|
|
84
84
|
return normalized;
|
|
85
85
|
}
|
|
86
|
+
function normalizeJsonSchema(schema) {
|
|
87
|
+
if (Array.isArray(schema)) {
|
|
88
|
+
return schema.map(normalizeJsonSchema);
|
|
89
|
+
}
|
|
90
|
+
if (typeof schema !== "object" || schema === null) {
|
|
91
|
+
return schema;
|
|
92
|
+
}
|
|
93
|
+
const normalized = {};
|
|
94
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
95
|
+
if (key === "required" && Array.isArray(value)) {
|
|
96
|
+
normalized[key] = [...value].sort((left, right) => compareCodeUnits(String(left), String(right)));
|
|
97
|
+
}
|
|
98
|
+
else if (key === "properties" &&
|
|
99
|
+
typeof value === "object" &&
|
|
100
|
+
value !== null) {
|
|
101
|
+
normalized[key] = Object.fromEntries(Object.entries(value).map(([name, child]) => [
|
|
102
|
+
name,
|
|
103
|
+
normalizeJsonSchema(child),
|
|
104
|
+
]));
|
|
105
|
+
}
|
|
106
|
+
else if (key === "const" || key === "enum") {
|
|
107
|
+
normalized[key] = value;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
normalized[key] = normalizeJsonSchema(value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return normalized;
|
|
114
|
+
}
|
|
86
115
|
function normalizeField(field) {
|
|
87
116
|
if (field.kind === "scalar") {
|
|
88
117
|
return {
|
|
89
118
|
...field,
|
|
119
|
+
...(field.json !== undefined
|
|
120
|
+
? { json: normalizeJsonSchema(field.json) }
|
|
121
|
+
: {}),
|
|
90
122
|
lifecycle: normalizeStringSet(field.lifecycle, LIFECYCLE_TRAITS),
|
|
91
123
|
capabilities: normalizeCapabilities(field.capabilities),
|
|
92
124
|
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { ClientContract } from "./contract.js";
|
|
2
2
|
import type { ContractHash, ContractProtocol } from "./contract-protocol.js";
|
|
3
|
+
/** A ClientContract whose `protocol` does not carry the hash yet. */
|
|
3
4
|
export type ClientContractHashInput = Omit<ClientContract, "protocol"> & {
|
|
4
5
|
readonly protocol: ContractProtocol;
|
|
5
6
|
};
|
|
6
7
|
export declare function createClientContractHashInput(contract: ClientContract | ClientContractHashInput): ClientContractHashInput;
|
|
7
|
-
/**
|
|
8
|
+
/** Computes a ClientContract's `sha256:` hash, ignoring `protocol.hash` itself. */
|
|
8
9
|
export declare function computeClientContractHash(contract: ClientContract | ClientContractHashInput): Promise<ContractHash>;
|
|
9
|
-
/**
|
|
10
|
+
/** Whether a ClientContract's `protocol.hash` matches its content. */
|
|
10
11
|
export declare function verifyClientContractHash(contract: ClientContract): Promise<boolean>;
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
/** The request limits a Contract enforces. */
|
|
1
2
|
export type ContractLimits = {
|
|
3
|
+
/** The largest request body accepted, in bytes. */
|
|
2
4
|
readonly maxRequestBytes: number;
|
|
5
|
+
/** How deeply arguments may nest. */
|
|
3
6
|
readonly maxNestingDepth: number;
|
|
7
|
+
/** The largest `limit` a list read may ask for. */
|
|
4
8
|
readonly maxListLimit: number;
|
|
9
|
+
/** The most operations one transaction may hold. */
|
|
5
10
|
readonly maxTransactionOperations: number;
|
|
11
|
+
/** The most boolean and filter nodes one request may hold. */
|
|
6
12
|
readonly maxBooleanNodes: number;
|
|
7
13
|
};
|
|
8
|
-
/**
|
|
14
|
+
/** The limits in force when the configuration sets none. */
|
|
9
15
|
export declare const DEFAULT_CONTRACT_LIMITS: {
|
|
10
16
|
readonly maxRequestBytes: 1048576;
|
|
11
17
|
readonly maxNestingDepth: 12;
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Aventara's
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* application-specific Contract changes. Resource/field/configuration/database
|
|
6
|
-
* changes are identified by the ClientContract hash and do not require a
|
|
7
|
-
* protocol-version bump by themselves. Increment this value only when a
|
|
8
|
-
* breaking change makes previous-protocol clients unable to correctly parse or
|
|
9
|
-
* interpret Aventara's external protocol semantics.
|
|
2
|
+
* The version of Aventara's wire protocol. It changes only when older
|
|
3
|
+
* clients can no longer read the protocol correctly; schema and
|
|
4
|
+
* configuration changes are tracked by the ClientContract hash instead.
|
|
10
5
|
*/
|
|
11
6
|
export declare const AVENTARA_PROTOCOL_VERSION: 1;
|
|
7
|
+
/** A Contract hash: `sha256:` followed by the hex digest. */
|
|
12
8
|
export type ContractHash = `sha256:${string}`;
|
|
9
|
+
/** The protocol a Contract is written for. */
|
|
13
10
|
export type ContractProtocol = {
|
|
11
|
+
/** The protocol version; see {@link AVENTARA_PROTOCOL_VERSION}. */
|
|
14
12
|
readonly version: typeof AVENTARA_PROTOCOL_VERSION;
|
|
15
13
|
};
|
|
14
|
+
/** A ClientContract's `protocol`: the protocol version and the Contract hash. */
|
|
16
15
|
export type ClientContractProtocol = ContractProtocol & {
|
|
17
16
|
readonly hash: ContractHash;
|
|
18
17
|
};
|
|
@@ -1,24 +1,40 @@
|
|
|
1
1
|
import type { ContractLimits } from "./contract-limits.js";
|
|
2
2
|
import type { ClientContractProtocol } from "./contract-protocol.js";
|
|
3
3
|
import type { EnumDefinition } from "./enum-definition.js";
|
|
4
|
+
import type { ReferenceWriteLimit } from "./reference-write-limit.js";
|
|
4
5
|
import type { Resource } from "./resource.js";
|
|
5
6
|
import type { ScalarRegistry } from "./scalar.js";
|
|
6
7
|
import type { TransactionCapability } from "./transaction-capability.js";
|
|
7
|
-
/**
|
|
8
|
+
/** What every Contract describes: scalars, enums, Resources, limits and transactions. */
|
|
8
9
|
export interface Contract {
|
|
10
|
+
/** The scalar types available. */
|
|
9
11
|
readonly scalars: ScalarRegistry;
|
|
12
|
+
/** The enums, by name. */
|
|
10
13
|
readonly enums: Readonly<Record<string, EnumDefinition>>;
|
|
14
|
+
/** The Resources, by name. */
|
|
11
15
|
readonly resources: Readonly<Record<string, Resource<this>>>;
|
|
16
|
+
/** The request limits in force. */
|
|
12
17
|
readonly limits: ContractLimits;
|
|
18
|
+
/** Whether interactive transactions are available. */
|
|
13
19
|
readonly transactions: TransactionCapability;
|
|
20
|
+
/**
|
|
21
|
+
* `"uniform"` when one record must write the relations that hold a reference
|
|
22
|
+
* either all as relations or all through their reference fields; a record
|
|
23
|
+
* that mixes them is refused with `A2004`. Absent: each relation either way.
|
|
24
|
+
*/
|
|
25
|
+
readonly referenceWrites?: ReferenceWriteLimit;
|
|
14
26
|
}
|
|
15
|
-
/**
|
|
27
|
+
/** The Contract compiled from the root configuration. */
|
|
16
28
|
export interface CoreContract extends Contract {
|
|
17
29
|
}
|
|
18
|
-
/**
|
|
30
|
+
/** The Contract in-process calls run under (`framework.application`). */
|
|
19
31
|
export interface ApplicationContract extends Contract {
|
|
20
32
|
}
|
|
21
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* The Contract remote clients run under, published at `GET /_contract` and
|
|
35
|
+
* identified by its hash.
|
|
36
|
+
*/
|
|
22
37
|
export interface ClientContract extends Contract {
|
|
38
|
+
/** The protocol version and this Contract's hash. */
|
|
23
39
|
readonly protocol: ClientContractProtocol;
|
|
24
40
|
}
|
|
@@ -1,49 +1,19 @@
|
|
|
1
1
|
import type { PaginationCapability, ProjectionScope, RelationFilterCapability, RelationMutationDirective, RelationReducer, ScalarFilterOperator, ScalarMutationDirective, WriteScope } from "./capability.js";
|
|
2
2
|
import type { Contract } from "./contract.js";
|
|
3
|
+
import type { JsonSchema } from "./json-shape.js";
|
|
3
4
|
import type { LifecycleTrait } from "./lifecycle-trait.js";
|
|
4
|
-
/**
|
|
5
|
+
/** A format name that refines a scalar type, carried in the Contract as data. */
|
|
5
6
|
export type ScalarFormat = string;
|
|
6
|
-
/**
|
|
7
|
-
* The projection positions a field is projectable in: at least one, because an
|
|
8
|
-
* EMPTY list is not a third answer.
|
|
9
|
-
*
|
|
10
|
-
* `absent ≠ empty` is this repository's established rule and the validation layer
|
|
11
|
-
* already acts on the difference, so the empty list has to be unrepresentable
|
|
12
|
-
* rather than merely discouraged. An absent capability member is "not available at
|
|
13
|
-
* all"; an empty list would say the same thing in a second encoding, which is one
|
|
14
|
-
* semantic fact with two hashes.
|
|
15
|
-
*
|
|
16
|
-
* **A scope list qualifies the TOP LEVEL of a projection only. It does not narrow
|
|
17
|
-
* recursively** — see {@link ProjectionScope}, where the runtime measurement and
|
|
18
|
-
* the reason are recorded.
|
|
19
|
-
*/
|
|
7
|
+
/** The projection positions a field can be selected in (at least one). */
|
|
20
8
|
export type ProjectionScopes = readonly [ProjectionScope, ...ProjectionScope[]];
|
|
21
9
|
/**
|
|
22
|
-
* The write positions a field
|
|
23
|
-
*
|
|
24
|
-
* the directive list beside it is separately allowed to be empty and already
|
|
25
|
-
* means something: on a relation mutation member, absent answers `V1006` *"is not
|
|
26
|
-
* writable during <stage>"* while `[]` answers *"writable with no directive"*. A
|
|
27
|
-
* scope list that could also be empty would put a third, unreachable answer next
|
|
28
|
-
* to those two.
|
|
29
|
-
*
|
|
30
|
-
* **A scope list qualifies the TOP LEVEL of a write argument only. It does not
|
|
31
|
-
* narrow recursively** — see {@link WriteScope}.
|
|
10
|
+
* The write positions a field can be written in (at least one). A scope list
|
|
11
|
+
* applies to the top level of a write argument only; see {@link WriteScope}.
|
|
32
12
|
*/
|
|
33
13
|
export type WriteScopes = readonly [WriteScope, ...WriteScope[]];
|
|
34
14
|
/**
|
|
35
|
-
* Whether a relation
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* `true`/`false` is the unqualified answer: available, or not, in every projection
|
|
39
|
-
* position. A {@link ProjectionScopes} list is the qualified answer, and it exists
|
|
40
|
-
* because both pilot engines measurably narrow the top level of a BULK write's
|
|
41
|
-
* returned-row projection to the relations the written row itself resolves, while
|
|
42
|
-
* advertising the full surface on every read.
|
|
43
|
-
*
|
|
44
|
-
* The unqualified arm is retained deliberately: an adapter that has not measured
|
|
45
|
-
* the axis must be able to say so, and a Contract that predates the measurement
|
|
46
|
-
* must keep its meaning.
|
|
15
|
+
* Whether a relation can be selected or included: `true`, or the projection
|
|
16
|
+
* positions it can be used in.
|
|
47
17
|
*/
|
|
48
18
|
export type RelationProjectionCapability = boolean | ProjectionScopes;
|
|
49
19
|
/**
|
|
@@ -57,36 +27,25 @@ export type ScopedRelationReducers = {
|
|
|
57
27
|
readonly scopes: ProjectionScopes;
|
|
58
28
|
};
|
|
59
29
|
/**
|
|
60
|
-
* A relation's
|
|
61
|
-
*
|
|
62
|
-
* unqualified arm is an array, the qualified arm an object.
|
|
30
|
+
* A relation's reducers: a reducer list, or an object with the reducers and
|
|
31
|
+
* the projection positions they apply in.
|
|
63
32
|
*/
|
|
64
33
|
export type RelationReducerCapability = readonly RelationReducer[] | ScopedRelationReducers;
|
|
65
34
|
/**
|
|
66
|
-
* The mutation directives a relation admits at one write stage,
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* `directives: []` stays meaningful and stays distinct from an absent member —
|
|
70
|
-
* "writable with no directive" against "not writable during this stage" — so the
|
|
71
|
-
* scoped form carries the directive list rather than replacing it.
|
|
35
|
+
* The mutation directives a relation admits at one write stage, and the write
|
|
36
|
+
* positions they are admitted in. `directives: []` means writable with no
|
|
37
|
+
* directive.
|
|
72
38
|
*/
|
|
73
39
|
export type ScopedRelationMutations = {
|
|
74
40
|
readonly directives: readonly RelationMutationDirective[];
|
|
75
41
|
readonly scopes: WriteScopes;
|
|
76
42
|
};
|
|
77
43
|
/**
|
|
78
|
-
* A relation's write capability
|
|
79
|
-
*
|
|
80
|
-
* — the unqualified arm is an array, the qualified arm an object.
|
|
44
|
+
* A relation's write capability for `create` or `update`: a directive list, or
|
|
45
|
+
* an object with the directives and the write positions they apply in.
|
|
81
46
|
*/
|
|
82
47
|
export type RelationMutationCapability = readonly RelationMutationDirective[] | ScopedRelationMutations;
|
|
83
|
-
/**
|
|
84
|
-
* Scalar capabilities carry no scope qualifier. Measured on both pilot engines,
|
|
85
|
-
* 2026-10-03: all six members are uniform across every operation variant on the
|
|
86
|
-
* axis — the same scalar members, the same operands, the same declared input
|
|
87
|
-
* symbols in the singular and the bulk forms. Qualifying them would encode a
|
|
88
|
-
* variation that does not exist.
|
|
89
|
-
*/
|
|
48
|
+
/** What can be done with a scalar field. Scalar capabilities apply in every position. */
|
|
90
49
|
export type ScalarFieldCapabilities = {
|
|
91
50
|
readonly select?: boolean;
|
|
92
51
|
readonly filter?: readonly ScalarFilterOperator[];
|
|
@@ -95,18 +54,7 @@ export type ScalarFieldCapabilities = {
|
|
|
95
54
|
readonly create?: readonly ScalarMutationDirective[];
|
|
96
55
|
readonly update?: readonly ScalarMutationDirective[];
|
|
97
56
|
};
|
|
98
|
-
/**
|
|
99
|
-
* Relation capabilities. Five of the eight members are scope-qualified, which is
|
|
100
|
-
* exactly the five the survey measured as varying by operation variant, by two
|
|
101
|
-
* independent mechanisms: the bulk-returning projection ceiling (`select`,
|
|
102
|
-
* `include`, `reducers`) and the flat bulk write stage (`create`, `update`).
|
|
103
|
-
*
|
|
104
|
-
* The other three are left alone on the same evidence. `filter` is uniform
|
|
105
|
-
* (`<M>WhereInput` and `<M>WhereUniqueInput` agree member-for-member on relation
|
|
106
|
-
* members), `order` is uniform (one orderBy input type across every orderBy-carrying
|
|
107
|
-
* variant), and `pagination` is read off the edge's own arguments, the same symbol
|
|
108
|
-
* under every projection that admits the edge at all.
|
|
109
|
-
*/
|
|
57
|
+
/** What can be done with a relation field. */
|
|
110
58
|
export type RelationFieldCapabilities = {
|
|
111
59
|
readonly select?: RelationProjectionCapability;
|
|
112
60
|
readonly include?: RelationProjectionCapability;
|
|
@@ -116,121 +64,43 @@ export type RelationFieldCapabilities = {
|
|
|
116
64
|
readonly reducers?: RelationReducerCapability;
|
|
117
65
|
readonly create?: RelationMutationCapability;
|
|
118
66
|
readonly update?: RelationMutationCapability;
|
|
67
|
+
/**
|
|
68
|
+
* What the `where` of a nested `$update` or `$delete` through this relation may
|
|
69
|
+
* filter: `"scalar"` — the target's scalar fields only, combined with
|
|
70
|
+
* `AND`/`OR`/`NOT`, and no relation filter at any depth. Absent: any filter the
|
|
71
|
+
* target takes.
|
|
72
|
+
*/
|
|
73
|
+
readonly nestedWriteFilter?: "scalar";
|
|
119
74
|
};
|
|
120
75
|
/**
|
|
121
|
-
* The reducers a relation advertises,
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* Scope-blind on purpose, and only for a reader on an axis the scope does not
|
|
125
|
-
* qualify. `orderBy: { <to-many>: { $count: … } }` is the one such reader: ordering
|
|
126
|
-
* was measured uniform across every variant that carries an `orderBy` argument, so
|
|
127
|
-
* there is no projection position to ask about and asking would invent one.
|
|
128
|
-
*
|
|
129
|
-
* Every reader on the projection axis wants {@link advertisedReducersAt} instead.
|
|
76
|
+
* The reducers a relation advertises, in any projection position. Use
|
|
77
|
+
* {@link advertisedReducersAt} for one position.
|
|
130
78
|
*/
|
|
131
79
|
export declare function advertisedReducers(capability: RelationReducerCapability | undefined): readonly RelationReducer[] | undefined;
|
|
132
|
-
/**
|
|
133
|
-
* Whether a projection capability holds at one projection scope — **the one home
|
|
134
|
-
* of the scope-aware capability read.**
|
|
135
|
-
*
|
|
136
|
-
* Every reader that has a scope asks this, and no reader compares a capability
|
|
137
|
-
* member to `true` any more. That is not tidiness: a qualified member is an
|
|
138
|
-
* object/array, so `member === true` is `false` for *every* scope, and the six
|
|
139
|
-
* `select`/`include` comparisons this function replaces read a measured
|
|
140
|
-
* `["record"]` as **unavailable everywhere** — a to-many projection refused on
|
|
141
|
-
* `update.first`, which is the single-record scope the adapter advertises it in.
|
|
142
|
-
*
|
|
143
|
-
* The parameter admits `boolean` as well as {@link ProjectionScopes} because the
|
|
144
|
-
* unqualified arm means *holds in every scope*: an adapter that has not measured
|
|
145
|
-
* the axis, and every Contract that predates the measurement, must keep answering
|
|
146
|
-
* what they answered before. So `true` holds at every scope and `false`/absent at
|
|
147
|
-
* none, and the qualified arm is consulted only where one exists.
|
|
148
|
-
*
|
|
149
|
-
* **A scope qualifies the TOP LEVEL of a projection only** — see {@link
|
|
150
|
-
* ProjectionScope}, where the runtime measurement is recorded. A caller that
|
|
151
|
-
* recurses must ask about the nested position with the unrestricted scope, not
|
|
152
|
-
* with its own.
|
|
153
|
-
*/
|
|
80
|
+
/** Whether a projection capability holds at one projection position. */
|
|
154
81
|
export declare function holdsAt(capability: RelationProjectionCapability | undefined, scope: ProjectionScope): boolean;
|
|
155
82
|
/**
|
|
156
|
-
* Whether a projection capability holds in **any**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* It exists so that reader does not hand-roll `=== true` either. The Contract
|
|
160
|
-
* compiler's VIRTUAL lifecycle check asks whether a field is selectable *at all*
|
|
161
|
-
* before requiring an effective read behavior; a field selectable in one scope
|
|
162
|
-
* needs that behavior exactly as much as one selectable in every scope.
|
|
163
|
-
*
|
|
164
|
-
* A reader with a scope must use {@link holdsAt}. This one answers a strictly
|
|
165
|
-
* weaker question and will over-advertise if used in its place.
|
|
83
|
+
* Whether a projection capability holds in **any** projection position. Use
|
|
84
|
+
* {@link holdsAt} when the position is known.
|
|
166
85
|
*/
|
|
167
86
|
export declare function holdsInAnyScope(capability: RelationProjectionCapability | undefined): boolean;
|
|
168
87
|
/**
|
|
169
|
-
* The reducers a relation advertises
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* One call rather than two, because the two-call form is the defect. A reader that
|
|
173
|
-
* asks {@link advertisedReducers} for the tokens and then has to remember a second
|
|
174
|
-
* {@link holdsAt} call will forget it, and a forgotten scope check is a silent
|
|
175
|
-
* over-advertisement. Here the scope cannot be forgotten: there is no way to get
|
|
176
|
-
* the tokens without naming the position they are wanted in.
|
|
177
|
-
*
|
|
178
|
-
* `undefined` is the same answer an absent member gives, which is correct — a
|
|
179
|
-
* reducer that does not hold at this scope is not advertised at this scope — and
|
|
180
|
-
* it keeps `[]` meaning what it means everywhere else: advertised, with no
|
|
181
|
-
* reducer.
|
|
88
|
+
* The reducers a relation advertises at one projection position, or
|
|
89
|
+
* `undefined` where it has none there.
|
|
182
90
|
*/
|
|
183
91
|
export declare function advertisedReducersAt(capability: RelationReducerCapability | undefined, scope: ProjectionScope): readonly RelationReducer[] | undefined;
|
|
184
92
|
/**
|
|
185
|
-
* The mutation directives a relation advertises at one write stage,
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* Scope-blind on purpose, and only for a reader that has no write position to ask
|
|
190
|
-
* about or whose answer may only be WIDER than the validator's. Two readers
|
|
191
|
-
* qualify. `operation-boolean-nodes.ts`'s `maxBooleanNodes` walk descends a
|
|
192
|
-
* relation mutation to count the filter nodes inside it; reading scope-blind makes
|
|
193
|
-
* that walk a superset of what the grammar walk admits, which is the harmless
|
|
194
|
-
* direction — it counts inside a write that is about to be refused, and it can
|
|
195
|
-
* never stop counting inside one that is admitted. An adapter's own write
|
|
196
|
-
* translator is the other: it is handed the write site's verdict by whoever
|
|
197
|
-
* dispatched it and holds no advertised variant to resolve a scope from.
|
|
198
|
-
*
|
|
199
|
-
* Every reader on the write axis that HAS a position wants {@link
|
|
200
|
-
* advertisedDirectivesAt} instead.
|
|
201
|
-
*
|
|
202
|
-
* See {@link advertisedReducers} for why the scopes are not returned beside them.
|
|
93
|
+
* The mutation directives a relation advertises at one write stage, in any
|
|
94
|
+
* write position. `undefined` means the stage is unavailable; `[]` means
|
|
95
|
+
* available with no directive.
|
|
203
96
|
*/
|
|
204
97
|
export declare function advertisedDirectives(capability: RelationMutationCapability | undefined): readonly RelationMutationDirective[] | undefined;
|
|
205
98
|
/**
|
|
206
|
-
* The mutation directives a relation advertises
|
|
207
|
-
*
|
|
208
|
-
* **the one home of the scope-aware write read**, and the write mirror of {@link
|
|
209
|
-
* advertisedReducersAt}.
|
|
210
|
-
*
|
|
211
|
-
* One call rather than two, for the reason {@link advertisedReducersAt} gives: a
|
|
212
|
-
* reader that fetches the directives and then has to remember a separate scope
|
|
213
|
-
* check will forget it, and a forgotten scope check is a silent
|
|
214
|
-
* over-advertisement. Here the position cannot be forgotten, because there is no
|
|
215
|
-
* way to reach the directives without naming it.
|
|
216
|
-
*
|
|
217
|
-
* `undefined` for a stage that does not hold at this scope is the same answer an
|
|
218
|
-
* absent member gives, which is what the caller must say: a relation that is not
|
|
219
|
-
* writable in this write position is *"not writable during <stage>"* there. That
|
|
220
|
-
* keeps `[]` meaning what it means everywhere else — advertised, with no directive
|
|
221
|
-
* — so the two live answers `V1006` already distinguishes stay two, and the scope
|
|
222
|
-
* does not invent a third.
|
|
223
|
-
*
|
|
224
|
-
* The unqualified arm is an array and means *holds in every write position*: an
|
|
225
|
-
* adapter that has not measured the axis, and every Contract that predates the
|
|
226
|
-
* measurement, keep the write surface they had.
|
|
227
|
-
*
|
|
228
|
-
* **A scope qualifies the TOP LEVEL of a write argument only** — see {@link
|
|
229
|
-
* WriteScope}. A caller that recurses into a nested write must ask about the
|
|
230
|
-
* nested position with the unrestricted scope, not with its own.
|
|
99
|
+
* The mutation directives a relation advertises at one write position, or
|
|
100
|
+
* `undefined` where it is not writable there.
|
|
231
101
|
*/
|
|
232
102
|
export declare function advertisedDirectivesAt(capability: RelationMutationCapability | undefined, scope: WriteScope): readonly RelationMutationDirective[] | undefined;
|
|
233
|
-
/**
|
|
103
|
+
/** A scalar field, including an enum-backed one. */
|
|
234
104
|
export type ScalarField<C extends Contract> = {
|
|
235
105
|
readonly kind: "scalar";
|
|
236
106
|
readonly type: {
|
|
@@ -243,8 +113,10 @@ export type ScalarField<C extends Contract> = {
|
|
|
243
113
|
readonly list: boolean;
|
|
244
114
|
readonly lifecycle: readonly LifecycleTrait[];
|
|
245
115
|
readonly capabilities: ScalarFieldCapabilities;
|
|
116
|
+
/** A `json` field's shape, as JSON Schema: what every value written to it is. */
|
|
117
|
+
readonly json?: JsonSchema;
|
|
246
118
|
};
|
|
247
|
-
/**
|
|
119
|
+
/** A relation field; the `target` Resource describes the related row. */
|
|
248
120
|
export type RelationField<C extends Contract> = {
|
|
249
121
|
readonly kind: "relation";
|
|
250
122
|
readonly target: Extract<keyof C["resources"], string>;
|
|
@@ -252,4 +124,5 @@ export type RelationField<C extends Contract> = {
|
|
|
252
124
|
readonly nullable: boolean;
|
|
253
125
|
readonly capabilities: RelationFieldCapabilities;
|
|
254
126
|
};
|
|
127
|
+
/** One field of a Resource: a scalar or a relation. */
|
|
255
128
|
export type Field<C extends Contract> = ScalarField<C> | RelationField<C>;
|
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
import type { Contract } from "./contract.js";
|
|
2
2
|
import type { Field } from "./field.js";
|
|
3
|
-
/**
|
|
4
|
-
* Keys of `TFields` whose Field can be scalar-shaped.
|
|
5
|
-
*
|
|
6
|
-
* `TFields[K]` is an indexed access, not a naked type parameter, so a bare
|
|
7
|
-
* conditional would not distribute over a union-valued field. With the
|
|
8
|
-
* abstract `ResourceFieldMap` default every key maps to the whole `Field`
|
|
9
|
-
* union, which is not a subtype of the scalar branch, so the bare form
|
|
10
|
-
* collapses to `never` and makes `Identifier` uninhabitable. `Extract` selects
|
|
11
|
-
* the scalar branch out of the union instead, so a key is kept unless it is
|
|
12
|
-
* provably relation-only.
|
|
13
|
-
*
|
|
14
|
-
* The branch is selected by its `kind` discriminant rather than by
|
|
15
|
-
* `ScalarField<Contract>`: `Field<C>` measures as invariant in `C`, so a
|
|
16
|
-
* `Field<ClientContract>` is not assignable to a `Field<Contract>` and the
|
|
17
|
-
* `Extract` would collapse again. This mirrors `AdapterScalarFieldKey`.
|
|
18
|
-
*/
|
|
3
|
+
/** Keys of `TFields` whose field can be scalar-shaped. */
|
|
19
4
|
export type ScalarFieldKey<TFields extends Readonly<Record<string, Field<Contract>>>> = Extract<{
|
|
20
5
|
[K in keyof TFields]: [
|
|
21
6
|
Extract<TFields[K], {
|