@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.
Files changed (167) hide show
  1. package/CHANGELOG.md +338 -0
  2. package/README.md +100 -3
  3. package/dist/adapters/adapter-discovery-diagnostic.d.ts +2 -0
  4. package/dist/adapters/adapter-field.d.ts +3 -26
  5. package/dist/adapters/adapter-identifier.d.ts +1 -11
  6. package/dist/adapters/adapter-model.d.ts +12 -0
  7. package/dist/adapters/adapter-operation-executor.interface.d.ts +5 -1
  8. package/dist/adapters/adapter-resource.d.ts +7 -28
  9. package/dist/adapters/adapter.interface.d.ts +25 -26
  10. package/dist/compiler/contract-compiler-diagnostic.d.ts +8 -0
  11. package/dist/compiler/contract.compiler.d.ts +11 -22
  12. package/dist/compiler/contract.compiler.js +67 -0
  13. package/dist/config/any-framework-config.d.ts +5 -26
  14. package/dist/config/config-diagnostic.d.ts +2 -0
  15. package/dist/config/config-layer.d.ts +6 -19
  16. package/dist/config/config.resolver.d.ts +9 -14
  17. package/dist/config/config.resolver.js +1 -0
  18. package/dist/config/config.validator.d.ts +6 -3
  19. package/dist/config/config.validator.js +6 -0
  20. package/dist/config/define-fields.d.ts +15 -48
  21. package/dist/config/effective-fields.d.ts +6 -29
  22. package/dist/config/entrypoint.normalizer.d.ts +6 -18
  23. package/dist/config/framework-behaviors-config.d.ts +11 -10
  24. package/dist/config/framework-config.d.ts +19 -16
  25. package/dist/config/framework-fields-config.d.ts +20 -7
  26. package/dist/config/framework-restrictions-config.d.ts +41 -61
  27. package/dist/config/pipeline-config.d.ts +7 -17
  28. package/dist/contracts/capability.d.ts +13 -49
  29. package/dist/contracts/client-contract-structure.validator.d.ts +28 -54
  30. package/dist/contracts/client-contract-structure.validator.js +99 -2
  31. package/dist/contracts/contract-canonicalizer.d.ts +4 -6
  32. package/dist/contracts/contract-canonicalizer.js +32 -0
  33. package/dist/contracts/contract-hash.d.ts +3 -2
  34. package/dist/contracts/contract-limits.d.ts +7 -1
  35. package/dist/contracts/contract-protocol.d.ts +7 -8
  36. package/dist/contracts/contract.d.ts +20 -4
  37. package/dist/contracts/enum-definition.d.ts +2 -0
  38. package/dist/contracts/field.d.ts +40 -167
  39. package/dist/contracts/identifier.d.ts +1 -16
  40. package/dist/contracts/json-shape.d.ts +65 -0
  41. package/dist/contracts/json-shape.js +1 -0
  42. package/dist/contracts/lifecycle-trait.d.ts +4 -3
  43. package/dist/contracts/reference-write-limit.d.ts +7 -0
  44. package/dist/contracts/reference-write-limit.js +1 -0
  45. package/dist/contracts/resource-operations.d.ts +2 -62
  46. package/dist/contracts/resource.d.ts +2 -0
  47. package/dist/contracts/scalar-value-type.d.ts +28 -48
  48. package/dist/contracts/scalar.d.ts +4 -3
  49. package/dist/contracts/transaction-capability.d.ts +2 -0
  50. package/dist/contracts/wire-scalar-format.d.ts +6 -21
  51. package/dist/framework/contract-view.d.ts +152 -130
  52. package/dist/framework/framework-construction.error.d.ts +6 -1
  53. package/dist/framework/framework.d.ts +33 -46
  54. package/dist/framework/framework.factory.d.ts +3 -17
  55. package/dist/framework/framework.factory.js +56 -13
  56. package/dist/framework/operation-facade.d.ts +3 -7
  57. package/dist/index.d.ts +8 -40
  58. package/dist/index.js +0 -20
  59. package/dist/internal.d.ts +35 -0
  60. package/dist/internal.js +16 -0
  61. package/dist/json/avz.d.ts +96 -0
  62. package/dist/json/avz.js +212 -0
  63. package/dist/json/index.d.ts +6 -0
  64. package/dist/json/index.js +1 -0
  65. package/dist/json/json-shape.registry.d.ts +22 -0
  66. package/dist/json/json-shape.registry.js +16 -0
  67. package/dist/json/zod-issue.translator.d.ts +51 -0
  68. package/dist/json/zod-issue.translator.js +142 -0
  69. package/dist/operations/operation-arguments.d.ts +198 -303
  70. package/dist/operations/operation-call.d.ts +13 -15
  71. package/dist/operations/operation-identity.d.ts +6 -2
  72. package/dist/operations/operation-identity.js +3 -0
  73. package/dist/operations/operation-result.d.ts +15 -61
  74. package/dist/operations/operation-validator-core.d.ts +6 -5
  75. package/dist/operations/operation-validator-core.js +28 -10
  76. package/dist/operations/operation-validator.d.ts +22 -9
  77. package/dist/operations/operation-validator.js +26 -10
  78. package/dist/operations/projection-scope.resolver.d.ts +1 -11
  79. package/dist/operations/structural-limits.walker.d.ts +19 -0
  80. package/dist/operations/structural-limits.walker.js +344 -0
  81. package/dist/operations/validation-code.d.ts +5 -17
  82. package/dist/operations/write-scope.resolver.d.ts +3 -28
  83. package/dist/protocol/absent-route.responder.d.ts +25 -39
  84. package/dist/protocol/absent-route.responder.js +32 -6
  85. package/dist/protocol/av-protocol.d.ts +2 -8
  86. package/dist/protocol/contract-document.responder.d.ts +5 -18
  87. package/dist/protocol/http-request.decoder.d.ts +18 -59
  88. package/dist/protocol/http-request.decoder.js +11 -3
  89. package/dist/protocol/http-request.reader.d.ts +5 -5
  90. package/dist/protocol/http-response.encoder.d.ts +14 -20
  91. package/dist/protocol/http-status.table.d.ts +0 -8
  92. package/dist/protocol/index.d.ts +2 -7
  93. package/dist/protocol/operation-response.guard.d.ts +0 -15
  94. package/dist/protocol/protocol-failure.d.ts +8 -27
  95. package/dist/protocol/protocol-headers.d.ts +15 -42
  96. package/dist/protocol/protocol-surface.d.ts +0 -22
  97. package/dist/protocol/protocol.binder.d.ts +19 -27
  98. package/dist/protocol/protocol.binder.js +3 -1
  99. package/dist/protocol/protocol.handler.d.ts +19 -23
  100. package/dist/protocol/protocol.handler.js +14 -4
  101. package/dist/protocol/unmounted-route.error.d.ts +19 -0
  102. package/dist/protocol/unmounted-route.error.js +10 -0
  103. package/dist/runtime/caller-data.copier.d.ts +59 -0
  104. package/dist/runtime/caller-data.copier.js +470 -0
  105. package/dist/runtime/caller-data.door.d.ts +56 -0
  106. package/dist/runtime/caller-data.door.js +65 -0
  107. package/dist/runtime/computed-read-runtime.js +3 -10
  108. package/dist/runtime/computed-value.error.d.ts +13 -0
  109. package/dist/runtime/computed-value.error.js +9 -0
  110. package/dist/runtime/create-requirement.d.ts +43 -7
  111. package/dist/runtime/create-requirement.js +89 -9
  112. package/dist/runtime/decimal.d.ts +5 -3
  113. package/dist/runtime/diagnostics-reporter.d.ts +33 -37
  114. package/dist/runtime/diagnostics-reporter.js +11 -4
  115. package/dist/runtime/error-boundary.d.ts +14 -10
  116. package/dist/runtime/error-boundary.js +65 -35
  117. package/dist/runtime/execution-options.d.ts +3 -7
  118. package/dist/runtime/field-behavior.d.ts +14 -21
  119. package/dist/runtime/framework-error.d.ts +14 -2
  120. package/dist/runtime/json-value.d.ts +7 -15
  121. package/dist/runtime/misuse-reporter.d.ts +32 -0
  122. package/dist/runtime/misuse-reporter.js +19 -0
  123. package/dist/runtime/mutation-planner.d.ts +6 -12
  124. package/dist/runtime/mutation-planner.js +13 -11
  125. package/dist/runtime/operation-request.d.ts +11 -11
  126. package/dist/runtime/operation-response.d.ts +18 -23
  127. package/dist/runtime/operation-runtime.d.ts +25 -33
  128. package/dist/runtime/operation-runtime.js +37 -18
  129. package/dist/runtime/outcome-mapper.js +12 -2
  130. package/dist/runtime/output-validator.d.ts +10 -0
  131. package/dist/runtime/output-validator.js +11 -0
  132. package/dist/runtime/owned-execution.registry.d.ts +13 -0
  133. package/dist/runtime/owned-execution.registry.js +7 -0
  134. package/dist/runtime/pipeline.d.ts +33 -36
  135. package/dist/runtime/referential-action.d.ts +8 -26
  136. package/dist/runtime/request-envelope.validator.d.ts +5 -0
  137. package/dist/runtime/request-envelope.validator.js +118 -0
  138. package/dist/runtime/request-id.d.ts +3 -10
  139. package/dist/runtime/scalar-runtime.d.ts +42 -14
  140. package/dist/runtime/scalar-runtime.js +162 -79
  141. package/dist/transactions/deferred-operation.d.ts +10 -21
  142. package/dist/transactions/operation-arguments-encoder.d.ts +1 -0
  143. package/dist/transactions/operation-fingerprint.d.ts +3 -1
  144. package/dist/transactions/transaction-envelope.d.ts +2 -4
  145. package/dist/transactions/transaction-integrity-error.d.ts +4 -15
  146. package/dist/transactions/transaction-plan-assembler.d.ts +6 -1
  147. package/dist/transactions/transaction-plan-assembler.js +74 -75
  148. package/dist/transactions/transaction-plan-validator.d.ts +33 -24
  149. package/dist/transactions/transaction-plan-validator.js +204 -153
  150. package/dist/transactions/transaction-reference-collector.d.ts +30 -0
  151. package/dist/transactions/transaction-reference-collector.js +204 -0
  152. package/dist/transactions/transaction-reference-resolver.d.ts +9 -12
  153. package/dist/transactions/transaction-reference-resolver.js +10 -5
  154. package/dist/transactions/transaction-reference-validator.js +24 -17
  155. package/dist/transactions/transaction-reference.d.ts +31 -64
  156. package/dist/transactions/transaction-request.d.ts +6 -8
  157. package/dist/transactions/transaction-response.d.ts +10 -7
  158. package/dist/transactions/transaction-response.js +24 -1
  159. package/dist/transactions/transaction-runtime.d.ts +7 -5
  160. package/dist/transactions/transaction-runtime.js +4 -3
  161. package/dist/transactions/transaction-slot-path-error.d.ts +5 -15
  162. package/dist/transactions/transaction-slot-writer.d.ts +3 -13
  163. package/package.json +17 -3
  164. package/dist/operations/operation-boolean-nodes.d.ts +0 -11
  165. package/dist/operations/operation-boolean-nodes.js +0 -283
  166. package/dist/operations/operation-nesting.d.ts +0 -8
  167. 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
- * Framework-owned rejection of a value JCS cannot represent. It extends
4
- * `TypeError` and keeps its inherited `name`, so existing `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/JCS-compatible canonical encoding for JSON-safe Aventara data. */
8
+ /** Canonical JSON (RFC 8785) for a JSON-safe value. */
11
9
  export declare function canonicalizeJson(value: unknown): string;
12
- /** Canonical semantic representation used for deterministic Contract output. */
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
- /** Compute exact ClientContract identity with protocol.hash excluded. */
8
+ /** Computes a ClientContract's `sha256:` hash, ignoring `protocol.hash` itself. */
8
9
  export declare function computeClientContractHash(contract: ClientContract | ClientContractHashInput): Promise<ContractHash>;
9
- /** Recompute and compare the advertised ClientContract identity. */
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
- /** Conservative v1 framework defaults resolved before scoped limit overrides. */
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 external communication protocol version.
3
- *
4
- * This is intentionally independent from framework/package versions and from
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
- /** Passive semantic description shared by every Aventara Contract form. */
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
- /** Root-scope Contract form. Distinction becomes behavioral during compilation. */
27
+ /** The Contract compiled from the root configuration. */
16
28
  export interface CoreContract extends Contract {
17
29
  }
18
- /** Same-process application-operation Contract form. */
30
+ /** The Contract in-process calls run under (`framework.application`). */
19
31
  export interface ApplicationContract extends Contract {
20
32
  }
21
- /** Remotely introspectable external-consumer Contract form with exact identity. */
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,3 +1,5 @@
1
+ /** An enum: its allowed values. */
1
2
  export type EnumDefinition = {
3
+ /** The allowed values, in declaration order. */
2
4
  readonly values: readonly string[];
3
5
  };
@@ -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
- /** Passive scalar format identity; concrete codec semantics remain protocol-owned. */
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 is writable in: at least one, for the reason
23
- * {@link ProjectionScopes} gives. Here the distinction is sharper still, because
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 is projectable, and — when the adapter has measured it — the
36
- * projection positions it is projectable in.
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 reducer capability: the bare reducer list is the unqualified
61
- * answer, the scoped form the measured one. Discriminated structurally — the
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, qualified by the
67
- * write positions they are admitted in.
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 at one stage: the bare directive list is the
79
- * unqualified answer, the scoped form the measured one. Discriminated structurally
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, whichever arm the member is written in,
122
- * **ignoring the scope qualifier**.
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** scope — the scope-blind
157
- * question, for the one reader that legitimately has no scope to ask about.
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 **at one projection scope**: the tokens where
170
- * the member holds there, and `undefined` where it does not.
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, whichever arm
186
- * the member is written in, **ignoring the scope qualifier**. `undefined` and `[]`
187
- * stay distinct: the stage is unavailable, or available with no directive.
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 **at one write scope**: the
207
- * directives where the member holds there, and `undefined` where it does not —
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
- /** Atomic field from the API/query perspective, including enum-backed fields. */
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
- /** Relation edge only; the target Resource owns the returned object shape. */
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], {