@aventara/core 1.0.0-rc.1 → 1.0.0-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/CHANGELOG.md +943 -16
  2. package/ERRORS.md +142 -0
  3. package/README.md +274 -16
  4. package/dist/adapters/adapter-discovery-diagnostic.guidance.d.ts +6 -0
  5. package/dist/adapters/adapter-discovery-diagnostic.guidance.js +7 -0
  6. package/dist/adapters/adapter-field.d.ts +22 -0
  7. package/dist/adapters/adapter-identifier.d.ts +9 -0
  8. package/dist/adapters/adapter-identifier.js +6 -1
  9. package/dist/adapters/adapter-model.d.ts +9 -0
  10. package/dist/adapters/adapter-operation.d.ts +18 -3
  11. package/dist/adapters/adapter-resource.d.ts +5 -0
  12. package/dist/adapters/adapter.interface.d.ts +10 -0
  13. package/dist/avz/avz.d.ts +1 -1
  14. package/dist/avz/json-shape.registry.d.ts +2 -1
  15. package/dist/compiler/contract-compiler-diagnostic.guidance.d.ts +6 -0
  16. package/dist/compiler/contract-compiler-diagnostic.guidance.js +97 -0
  17. package/dist/compiler/contract.compiler.d.ts +6 -0
  18. package/dist/compiler/contract.compiler.js +227 -24
  19. package/dist/compiler/managed-elsewhere.projector.d.ts +52 -0
  20. package/dist/compiler/managed-elsewhere.projector.js +179 -0
  21. package/dist/compiler/managed-elsewhere.validator.d.ts +9 -0
  22. package/dist/compiler/managed-elsewhere.validator.js +74 -0
  23. package/dist/config/computed-fields.d.ts +62 -0
  24. package/dist/config/computed-fields.js +73 -0
  25. package/dist/config/config-diagnostic.guidance.d.ts +6 -0
  26. package/dist/config/config-diagnostic.guidance.js +32 -0
  27. package/dist/config/config.resolver.d.ts +24 -0
  28. package/dist/config/config.resolver.js +65 -0
  29. package/dist/config/config.validator.js +222 -8
  30. package/dist/config/define-resource.d.ts +1 -5
  31. package/dist/config/entrypoint.normalizer.d.ts +3 -2
  32. package/dist/config/exact-framework-config.d.ts +67 -22
  33. package/dist/config/field-compute.resolver.d.ts +2 -0
  34. package/dist/config/field-compute.resolver.js +8 -0
  35. package/dist/config/framework-config.d.ts +53 -15
  36. package/dist/config/framework-resources-config.d.ts +25 -14
  37. package/dist/config/pipeline-config.d.ts +4 -4
  38. package/dist/config/pipeline-tree.resolver.d.ts +2 -2
  39. package/dist/contracts/access-route.rules.d.ts +10 -0
  40. package/dist/contracts/access-route.rules.js +45 -0
  41. package/dist/contracts/capability.d.ts +26 -1
  42. package/dist/contracts/capability.js +20 -0
  43. package/dist/contracts/client-contract-structure.validator.d.ts +301 -89
  44. package/dist/contracts/client-contract-structure.validator.js +37 -2
  45. package/dist/contracts/contract-access.d.ts +29 -0
  46. package/dist/contracts/contract-access.js +1 -0
  47. package/dist/contracts/contract-canonicalizer.d.ts +9 -1
  48. package/dist/contracts/contract-canonicalizer.js +30 -4
  49. package/dist/contracts/contract-hash.d.ts +0 -1
  50. package/dist/contracts/contract-hash.js +1 -1
  51. package/dist/contracts/contract.d.ts +27 -1
  52. package/dist/contracts/field.d.ts +58 -1
  53. package/dist/contracts/json-schema.projector.d.ts +11 -0
  54. package/dist/contracts/json-schema.projector.js +78 -0
  55. package/dist/contracts/scalar-value-type.d.ts +3 -1
  56. package/dist/framework/access-facade.d.ts +116 -0
  57. package/dist/framework/access-facade.js +120 -0
  58. package/dist/framework/contract-view.d.ts +120 -16
  59. package/dist/framework/framework-construction.error.d.ts +14 -4
  60. package/dist/framework/framework-construction.error.js +18 -2
  61. package/dist/framework/framework.d.ts +26 -5
  62. package/dist/framework/framework.factory.d.ts +1 -1
  63. package/dist/framework/framework.factory.js +115 -23
  64. package/dist/framework/operation-facade.d.ts +80 -13
  65. package/dist/framework/operation-facade.js +2 -1
  66. package/dist/index.d.ts +17 -5
  67. package/dist/index.js +1 -0
  68. package/dist/internal.d.ts +7 -2
  69. package/dist/internal.js +6 -0
  70. package/dist/operations/database-refusal.d.ts +51 -0
  71. package/dist/operations/database-refusal.js +45 -0
  72. package/dist/operations/operation-arguments.d.ts +148 -60
  73. package/dist/operations/operation-call.d.ts +9 -5
  74. package/dist/operations/operation-result.d.ts +6 -4
  75. package/dist/operations/operation-validator-core.js +159 -36
  76. package/dist/operations/operation-validator.d.ts +1 -1
  77. package/dist/operations/structural-limits.walker.d.ts +1 -1
  78. package/dist/operations/validation-code.guidance.d.ts +6 -0
  79. package/dist/operations/validation-code.guidance.js +90 -0
  80. package/dist/operations/write-scope.resolver.d.ts +4 -0
  81. package/dist/operations/write-scope.resolver.js +2 -0
  82. package/dist/policy/access-provider.interface.d.ts +91 -0
  83. package/dist/policy/access-provider.interface.js +1 -0
  84. package/dist/policy/access-provider.registry.d.ts +17 -0
  85. package/dist/policy/access-provider.registry.js +15 -0
  86. package/dist/policy/access-question.d.ts +20 -0
  87. package/dist/policy/access-question.js +1 -0
  88. package/dist/policy/access-question.registry.d.ts +5 -0
  89. package/dist/policy/access-question.registry.js +19 -0
  90. package/dist/policy/access-question.runtime.d.ts +36 -0
  91. package/dist/policy/access-question.runtime.js +297 -0
  92. package/dist/policy/access-verb.table.d.ts +56 -0
  93. package/dist/policy/access-verb.table.js +21 -0
  94. package/dist/policy/access-verbs.d.ts +46 -0
  95. package/dist/policy/access-verbs.js +1 -0
  96. package/dist/policy/policy-admission.d.ts +20 -0
  97. package/dist/policy/policy-admission.js +409 -0
  98. package/dist/policy/policy-after-update.runtime.d.ts +79 -0
  99. package/dist/policy/policy-after-update.runtime.js +208 -0
  100. package/dist/policy/policy-condition.d.ts +15 -0
  101. package/dist/policy/policy-condition.js +36 -0
  102. package/dist/policy/policy-engine.d.ts +205 -0
  103. package/dist/policy/policy-engine.js +1 -0
  104. package/dist/policy/policy-named-rows.d.ts +18 -0
  105. package/dist/policy/policy-named-rows.js +51 -0
  106. package/dist/policy/policy-nested-write.runtime.d.ts +44 -0
  107. package/dist/policy/policy-nested-write.runtime.js +545 -0
  108. package/dist/policy/policy-read.runtime.d.ts +81 -0
  109. package/dist/policy/policy-read.runtime.js +503 -0
  110. package/dist/policy/policy-reference-write.runtime.d.ts +51 -0
  111. package/dist/policy/policy-reference-write.runtime.js +345 -0
  112. package/dist/policy/policy-request.d.ts +41 -0
  113. package/dist/policy/policy-request.js +63 -0
  114. package/dist/policy/policy-session.d.ts +106 -0
  115. package/dist/policy/policy-session.js +295 -0
  116. package/dist/policy/policy-startup.validator.d.ts +10 -0
  117. package/dist/policy/policy-startup.validator.js +112 -0
  118. package/dist/policy/policy-write.runtime.d.ts +39 -0
  119. package/dist/policy/policy-write.runtime.js +532 -0
  120. package/dist/prebuilt/core-version.generated.d.ts +2 -0
  121. package/dist/prebuilt/core-version.generated.js +1 -0
  122. package/dist/prebuilt/prebuilt-changes.d.ts +29 -0
  123. package/dist/prebuilt/prebuilt-changes.js +65 -0
  124. package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
  125. package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
  126. package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
  127. package/dist/prebuilt/prebuilt-contracts.js +1 -0
  128. package/dist/prebuilt/prebuilt-contracts.loader.d.ts +19 -0
  129. package/dist/prebuilt/prebuilt-contracts.loader.js +77 -0
  130. package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
  131. package/dist/prebuilt/prebuilt-inputs.fingerprint.js +229 -0
  132. package/dist/protocol/absent-route.responder.d.ts +9 -27
  133. package/dist/protocol/absent-route.responder.js +36 -3
  134. package/dist/protocol/contract-document.responder.d.ts +2 -1
  135. package/dist/protocol/http-request.decoder.d.ts +19 -3
  136. package/dist/protocol/http-request.decoder.js +24 -1
  137. package/dist/protocol/http-request.reader.d.ts +6 -3
  138. package/dist/protocol/http-request.reader.js +1 -0
  139. package/dist/protocol/http-response.encoder.js +2 -2
  140. package/dist/protocol/protocol-headers.d.ts +3 -3
  141. package/dist/protocol/protocol-surface.d.ts +6 -1
  142. package/dist/protocol/protocol-surface.js +7 -0
  143. package/dist/protocol/protocol.binder.d.ts +16 -1
  144. package/dist/protocol/protocol.binder.js +4 -1
  145. package/dist/protocol/protocol.handler.d.ts +12 -1
  146. package/dist/protocol/protocol.handler.js +86 -17
  147. package/dist/runtime/caller-data.copier.d.ts +18 -3
  148. package/dist/runtime/caller-data.copier.js +21 -8
  149. package/dist/runtime/caller-data.door.d.ts +13 -6
  150. package/dist/runtime/computed-read-runtime.d.ts +6 -1
  151. package/dist/runtime/computed-read-runtime.js +232 -45
  152. package/dist/runtime/computed-value.error.d.ts +3 -2
  153. package/dist/runtime/computed-value.error.js +3 -2
  154. package/dist/runtime/context-executor.runtime.d.ts +4 -3
  155. package/dist/runtime/context-executor.runtime.js +4 -2
  156. package/dist/runtime/core-class.brand.d.ts +10 -0
  157. package/dist/runtime/core-class.brand.js +21 -0
  158. package/dist/runtime/create-requirement.d.ts +31 -5
  159. package/dist/runtime/create-requirement.js +129 -21
  160. package/dist/runtime/decimal.d.ts +14 -0
  161. package/dist/runtime/decimal.js +21 -0
  162. package/dist/runtime/diagnostics-reporter.d.ts +44 -9
  163. package/dist/runtime/diagnostics-reporter.js +13 -1
  164. package/dist/runtime/error-boundary.d.ts +9 -2
  165. package/dist/runtime/error-boundary.js +12 -0
  166. package/dist/runtime/error-guidance.d.ts +12 -0
  167. package/dist/runtime/error-guidance.js +1 -0
  168. package/dist/runtime/error.catalog.d.ts +49 -0
  169. package/dist/runtime/error.catalog.js +49 -0
  170. package/dist/runtime/execution-options.d.ts +8 -0
  171. package/dist/runtime/field-check.evaluator.d.ts +0 -2
  172. package/dist/runtime/field-check.evaluator.js +1 -1
  173. package/dist/runtime/field-compute.d.ts +11 -10
  174. package/dist/runtime/field-stage.runtime.d.ts +3 -1
  175. package/dist/runtime/field-stage.runtime.js +8 -4
  176. package/dist/runtime/field-validator.runtime.d.ts +19 -1
  177. package/dist/runtime/field-validator.runtime.js +47 -8
  178. package/dist/runtime/framework-error.js +9 -0
  179. package/dist/runtime/framework-warning.emitter.d.ts +6 -0
  180. package/dist/runtime/framework-warning.emitter.js +8 -0
  181. package/dist/runtime/json-value.d.ts +10 -5
  182. package/dist/runtime/level-lifecycle.runtime.d.ts +4 -3
  183. package/dist/runtime/level-lifecycle.runtime.js +3 -5
  184. package/dist/runtime/mutation-planner.d.ts +1 -6
  185. package/dist/runtime/mutation-planner.js +3 -14
  186. package/dist/runtime/operation-code.guidance.d.ts +6 -0
  187. package/dist/runtime/operation-code.guidance.js +167 -0
  188. package/dist/runtime/operation-request.d.ts +16 -9
  189. package/dist/runtime/operation-runtime.d.ts +35 -17
  190. package/dist/runtime/operation-runtime.js +220 -49
  191. package/dist/runtime/outcome-mapper.d.ts +2 -1
  192. package/dist/runtime/output-validator.d.ts +1 -1
  193. package/dist/runtime/owned-execution.registry.d.ts +13 -3
  194. package/dist/runtime/pipe-output.validator.d.ts +10 -0
  195. package/dist/runtime/pipe-output.validator.js +4 -2
  196. package/dist/runtime/pipeline.d.ts +47 -22
  197. package/dist/runtime/readonly-value.d.ts +20 -5
  198. package/dist/runtime/readonly-value.js +74 -26
  199. package/dist/runtime/referential-action.d.ts +1 -1
  200. package/dist/runtime/scalar-runtime.d.ts +4 -11
  201. package/dist/runtime/scalar-runtime.js +91 -22
  202. package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
  203. package/dist/runtime/unsent-value.normalizer.js +128 -0
  204. package/dist/transactions/operation-arguments-encoder.js +8 -3
  205. package/dist/transactions/operation-fingerprint.js +2 -2
  206. package/dist/transactions/sql-statement.d.ts +10 -2
  207. package/dist/transactions/sql-statement.validator.d.ts +2 -2
  208. package/dist/transactions/sql-statement.validator.js +38 -5
  209. package/dist/transactions/transaction-envelope.d.ts +5 -4
  210. package/dist/transactions/transaction-integrity-error.js +2 -0
  211. package/dist/transactions/transaction-plan-assembler.d.ts +12 -5
  212. package/dist/transactions/transaction-plan-assembler.js +48 -10
  213. package/dist/transactions/transaction-plan-validator.d.ts +23 -7
  214. package/dist/transactions/transaction-plan-validator.js +27 -1
  215. package/dist/transactions/transaction-reference-resolver.d.ts +3 -2
  216. package/dist/transactions/transaction-reference-validator.d.ts +43 -3
  217. package/dist/transactions/transaction-reference-validator.js +83 -57
  218. package/dist/transactions/transaction-reference.d.ts +1 -1
  219. package/dist/transactions/transaction-reference.js +5 -0
  220. package/dist/transactions/transaction-request.d.ts +8 -8
  221. package/dist/transactions/transaction-runtime.d.ts +5 -2
  222. package/dist/transactions/transaction-runtime.js +79 -27
  223. package/dist/transactions/transaction-settlement.runtime.d.ts +4 -3
  224. package/dist/transactions/transaction-slot-path-error.js +2 -0
  225. package/dist/transactions/transaction-stage.runtime.d.ts +5 -3
  226. package/dist/transactions/transaction-stage.runtime.js +7 -4
  227. package/package.json +6 -5
  228. package/src/runtime/field-check.evaluator.ts +1 -1
@@ -1,4 +1,5 @@
1
- import { PROJECTION_SCOPES, WRITE_SCOPES } from "./capability.js";
1
+ import { isAccessPath, isAccessUrl } from "./access-route.rules.js";
2
+ import { FILTER_MODES, PROJECTION_SCOPES, WRITE_SCOPES } from "./capability.js";
2
3
  import { AVENTARA_PROTOCOL_VERSION } from "./contract-protocol.js";
3
4
  import { LIFECYCLE_TRAITS } from "./lifecycle-trait.js";
4
5
  import { REFERENCE_WRITE_LIMITS } from "./reference-write-limit.js";
@@ -226,6 +227,8 @@ const SCALAR_FIELD = object({
226
227
  capabilities: object({}, {
227
228
  select: BOOLEAN,
228
229
  filter: list(literal(SCALAR_FILTER_OPERATORS)),
230
+ filterModes: list(literal(FILTER_MODES)),
231
+ pathFilterModes: list(literal(FILTER_MODES)),
229
232
  order: BOOLEAN,
230
233
  orderNulls: BOOLEAN,
231
234
  create: list(literal(SCALAR_MUTATION_DIRECTIVES)),
@@ -235,6 +238,7 @@ const SCALAR_FIELD = object({
235
238
  format: STRING,
236
239
  json: { is: "jsonSchema" },
237
240
  validators: list(FIELD_CHECK),
241
+ governed: literal([true]),
238
242
  });
239
243
  const RELATION_FIELD = object({
240
244
  kind: literal(["relation"]),
@@ -255,6 +259,10 @@ const RELATION_FIELD = object({
255
259
  update: RELATION_MUTATION,
256
260
  nestedWriteFilter: literal(["scalar"]),
257
261
  }),
262
+ }, {
263
+ inverse: STRING,
264
+ referenceFields: nonEmptyList(STRING),
265
+ governed: literal([true]),
258
266
  });
259
267
  const OPERATION_AVAILABILITY = union(literal([true]), object({ projection: literal(PROJECTION_SCOPES) }, { write: literal(WRITE_SCOPES) }), object({ write: literal(WRITE_SCOPES) }, { projection: literal(PROJECTION_SCOPES) }));
260
268
  const RESOURCE = object({
@@ -297,6 +305,26 @@ const RESOURCE = object({
297
305
  parentSupplies: registry(registry(nonEmptyList(STRING))),
298
306
  }),
299
307
  });
308
+ const ACCESS_VERB = object({
309
+ method: literal(["GET", "POST"]),
310
+ path: { is: "route", form: "path" },
311
+ result: { is: "jsonSchema" },
312
+ }, { args: { is: "jsonSchema" } });
313
+ const ACCESS = object({}, {
314
+ url: { is: "route", form: "url" },
315
+ verbs: object({}, {
316
+ signIn: ACCESS_VERB,
317
+ signUp: ACCESS_VERB,
318
+ signOut: ACCESS_VERB,
319
+ resetPassword: ACCESS_VERB,
320
+ me: ACCESS_VERB,
321
+ switchUser: ACCESS_VERB,
322
+ lock: ACCESS_VERB,
323
+ setPin: ACCESS_VERB,
324
+ enrollDevice: ACCESS_VERB,
325
+ revokeDevice: ACCESS_VERB,
326
+ }),
327
+ });
300
328
  export const CLIENT_CONTRACT_STRUCTURE = object({
301
329
  scalars: registry(object({ builtin: literal([true]) })),
302
330
  enums: registry(object({ values: uniqueList(STRING) })),
@@ -314,7 +342,7 @@ export const CLIENT_CONTRACT_STRUCTURE = object({
314
342
  version: literal([AVENTARA_PROTOCOL_VERSION]),
315
343
  hash: { is: "prefixed", prefix: "sha256:" },
316
344
  }),
317
- }, { referenceWrites: literal(REFERENCE_WRITE_LIMITS) });
345
+ }, { referenceWrites: literal(REFERENCE_WRITE_LIMITS), access: ACCESS });
318
346
  export function validateClientContractStructure(value) {
319
347
  const defect = defectOf(CLIENT_CONTRACT_STRUCTURE, value, "$", 0);
320
348
  if (defect !== undefined) {
@@ -336,6 +364,13 @@ function defectOf(node, value, path, depth) {
336
364
  (value.startsWith(node.prefix)
337
365
  ? undefined
338
366
  : at(path, depth, `is ${JSON.stringify(value)}, which does not begin with ${JSON.stringify(node.prefix)}`)));
367
+ case "route":
368
+ return (kindDefect(value, "string", path, depth) ??
369
+ ((node.form === "url" ? isAccessUrl : isAccessPath)(value)
370
+ ? undefined
371
+ : at(path, depth, node.form === "url"
372
+ ? "is no absolute http(s) URL and no root-relative path"
373
+ : 'is no path under the url, starting with one "/"')));
339
374
  case "list":
340
375
  return listDefect(node, value, path, depth);
341
376
  case "registry": {
@@ -0,0 +1,29 @@
1
+ import type { JsonSchema } from "./json-shape.js";
2
+ /**
3
+ * The ClientContract's `access` section: present exactly when the framework's
4
+ * `access` option is configured, and then it marks the `POST /_access/can`
5
+ * route. `url` and `verbs` say where Aventara Access is mounted and each of its
6
+ * `$access` verbs, when the provider states them.
7
+ */
8
+ export type AvContractClientAccess = {
9
+ /** Where the app mounted Access: absolute, or root-relative (`"/api/auth"`). */
10
+ readonly url?: string;
11
+ /**
12
+ * Each `$access` verb the provider offers: its route under `url`, and the
13
+ * JSON Schema of its arguments and result. A verb left out is no verb of
14
+ * `$access` (a `service` profile has no `signIn` or `signUp`; only a
15
+ * `kiosk` profile has `switchUser`, `lock`, `setPin`, `enrollDevice` and
16
+ * `revokeDevice`).
17
+ */
18
+ readonly verbs?: {
19
+ readonly [Verb in "signIn" | "signUp" | "signOut" | "resetPassword" | "me" | "switchUser" | "lock" | "setPin" | "enrollDevice" | "revokeDevice"]?: {
20
+ readonly method: "GET" | "POST";
21
+ /** Under `url`: `"/sign-in/email"`. */
22
+ readonly path: string;
23
+ /** The arguments' JSON Schema; absent when the verb takes none. */
24
+ readonly args?: JsonSchema;
25
+ /** The result's JSON Schema (`me`: the value when someone is signed in). */
26
+ readonly result: JsonSchema;
27
+ };
28
+ };
29
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -7,8 +7,15 @@ export declare class AvCanonicalJsonError extends TypeError {
7
7
  }
8
8
  /** Canonical JSON (RFC 8785) for a JSON-safe value. */
9
9
  export declare function canonicalizeJson(value: unknown): string;
10
+ /**
11
+ * Canonical JSON (RFC 8785) for a value as JSON carries it: a key holding
12
+ * `undefined` is left out, and an array element holding it (or a hole) is
13
+ * `null`, as `JSON.stringify` writes them. Everything else as
14
+ * {@link canonicalizeJson}.
15
+ */
16
+ export declare function canonicalizeWireJson(value: unknown): string;
10
17
  /** One top-level member of a canonical Contract: its key and its value's canonical text. */
11
- export type CanonicalContractMember = {
18
+ type CanonicalContractMember = {
12
19
  readonly key: string;
13
20
  readonly text: string;
14
21
  };
@@ -25,3 +32,4 @@ export declare function recordCanonicalContractText(contract: AvContract, text:
25
32
  * recorded one, else written now. Both are the same bytes for the same Contract.
26
33
  */
27
34
  export declare function canonicalContractTextOf<C extends AvContract>(contract: C): string;
35
+ export {};
@@ -1,10 +1,12 @@
1
- import { PROJECTION_SCOPES, WRITE_SCOPES } from "./capability.js";
1
+ import { brandCoreClass } from "../runtime/core-class.brand.js";
2
+ import { FILTER_MODES, PROJECTION_SCOPES, WRITE_SCOPES } from "./capability.js";
2
3
  import { LIFECYCLE_TRAITS } from "./lifecycle-trait.js";
3
4
  import { projectionScopeOf, writeScopeOf } from "./resource-operations.js";
4
5
  const PAGINATION_ORDER = ["limit", "offset", "cursor"];
5
6
  const REDUCER_ORDER = ["$count"];
6
7
  export class AvCanonicalJsonError extends TypeError {
7
8
  }
9
+ brandCoreClass(AvCanonicalJsonError, "AvCanonicalJsonError");
8
10
  function compareCodeUnits(left, right) {
9
11
  if (left === right) {
10
12
  return 0;
@@ -61,6 +63,12 @@ function normalizeCapabilities(capabilities) {
61
63
  if (Array.isArray(filter)) {
62
64
  normalized.filter = normalizeStringSet(filter);
63
65
  }
66
+ for (const key of ["filterModes", "pathFilterModes"]) {
67
+ const modes = normalized[key];
68
+ if (Array.isArray(modes)) {
69
+ normalized[key] = normalizeStringSet(modes, FILTER_MODES);
70
+ }
71
+ }
64
72
  for (const key of ["create", "update"]) {
65
73
  const value = normalized[key];
66
74
  if (Array.isArray(value)) {
@@ -178,6 +186,16 @@ function normalizeResource(resource) {
178
186
  : {
179
187
  createRequirements: normalizeCreateRequirements(resource.createRequirements),
180
188
  }),
189
+ ...coreResourceFacts(resource),
190
+ };
191
+ }
192
+ function coreResourceFacts(resource) {
193
+ const facts = resource;
194
+ return {
195
+ ...(facts.schema === undefined ? {} : { schema: facts.schema }),
196
+ ...(facts.managedElsewhere === undefined
197
+ ? {}
198
+ : { managedElsewhere: facts.managedElsewhere }),
181
199
  };
182
200
  }
183
201
  function isJsonObject(value) {
@@ -286,7 +304,7 @@ function canonicalizePrimitive(value, link) {
286
304
  return undefined;
287
305
  }
288
306
  }
289
- function canonicalizeValue(value, link = undefined) {
307
+ function canonicalizeValue(value, link = undefined, undefinedAsJson = false) {
290
308
  const output = [];
291
309
  const stack = [{ kind: "value", value, link }];
292
310
  while (stack.length > 0) {
@@ -310,7 +328,10 @@ function canonicalizeValue(value, link = undefined) {
310
328
  output.push("[");
311
329
  stack.push({ kind: "text", text: "]" });
312
330
  for (let index = current.length - 1; index >= 0; index -= 1) {
313
- if (index in current) {
331
+ if (undefinedAsJson && current[index] === undefined) {
332
+ stack.push({ kind: "text", text: "null" });
333
+ }
334
+ else if (index in current) {
314
335
  stack.push({
315
336
  kind: "value",
316
337
  value: current[index],
@@ -328,7 +349,9 @@ function canonicalizeValue(value, link = undefined) {
328
349
  throw new AvCanonicalJsonError(`non-plain object at ${pathOf(link)} is not canonical JSON`);
329
350
  }
330
351
  const record = current;
331
- const keys = Object.keys(record).sort(compareCodeUnits);
352
+ const keys = Object.keys(record)
353
+ .filter((key) => !undefinedAsJson || record[key] !== undefined)
354
+ .sort(compareCodeUnits);
332
355
  output.push("{");
333
356
  stack.push({ kind: "text", text: "}" });
334
357
  for (let index = keys.length - 1; index >= 0; index -= 1) {
@@ -349,6 +372,9 @@ function canonicalizeValue(value, link = undefined) {
349
372
  export function canonicalizeJson(value) {
350
373
  return canonicalizeValue(value);
351
374
  }
375
+ export function canonicalizeWireJson(value) {
376
+ return canonicalizeValue(value, undefined, true);
377
+ }
352
378
  export function canonicalizeContractMembers(contract) {
353
379
  const record = normalizeContract(contract);
354
380
  return Object.keys(record)
@@ -4,7 +4,6 @@ import type { AvContractHash, AvContractProtocol } from "./contract-protocol.js"
4
4
  export type AvContractClientHashInput = Omit<AvContractClient, "protocol"> & {
5
5
  readonly protocol: AvContractProtocol;
6
6
  };
7
- export declare function createClientContractHashInput(contract: AvContractClient | AvContractClientHashInput): AvContractClientHashInput;
8
7
  /** Computes a ClientContract's `sha256:` hash, ignoring `protocol.hash` itself. */
9
8
  export declare function computeClientContractHash(contract: AvContractClient | AvContractClientHashInput): Promise<AvContractHash>;
10
9
  /**
@@ -1,5 +1,5 @@
1
1
  import { canonicalizeContract } from "./contract-canonicalizer.js";
2
- export function createClientContractHashInput(contract) {
2
+ function createClientContractHashInput(contract) {
3
3
  const { protocol, ...semantic } = contract;
4
4
  return {
5
5
  ...semantic,
@@ -1,6 +1,8 @@
1
+ import type { AvContractClientAccess } from "./contract-access.js";
1
2
  import type { AvContractLimits } from "./contract-limits.js";
2
3
  import type { AvContractClientProtocol } from "./contract-protocol.js";
3
4
  import type { AvEnumDefinition } from "./enum-definition.js";
5
+ import type { AvCoreField } from "./field.js";
4
6
  import type { ReferenceWriteLimit } from "./reference-write-limit.js";
5
7
  import type { AvResource } from "./resource.js";
6
8
  import type { AvScalarRegistry } from "./scalar.js";
@@ -24,8 +26,27 @@ export interface AvContract {
24
26
  */
25
27
  readonly referenceWrites?: ReferenceWriteLimit;
26
28
  }
27
- /** The Contract compiled from the root configuration. */
29
+ /**
30
+ * The Contract compiled from the root configuration. Its relation fields also
31
+ * state the target's fields their reference names (`references`), and its
32
+ * Resources the database schema the store keeps them in (`schema`) and whether
33
+ * another service owns them (`managedElsewhere`), which no other Contract
34
+ * carries.
35
+ */
28
36
  export interface AvContractCore extends AvContract {
37
+ /** The Resources, by name. */
38
+ readonly resources: Readonly<Record<string, AvCoreResource>>;
39
+ }
40
+ /**
41
+ * A Resource as the Core Contract states it: its fields are
42
+ * {@link AvCoreField}s, so its relation fields carry `references`.
43
+ */
44
+ export interface AvCoreResource extends AvResource<AvContractCore> {
45
+ readonly fields: Readonly<Record<string, AvCoreField<AvContractCore>>>;
46
+ /** The database schema the store keeps this Resource in, when the adapter states one. */
47
+ readonly schema?: string;
48
+ /** Present when another service owns this Resource: it is read, never written. */
49
+ readonly managedElsewhere?: true;
29
50
  }
30
51
  /** The Contract in-process calls run under (`framework.application`). */
31
52
  export interface AvContractApplication extends AvContract {
@@ -37,6 +58,11 @@ export interface AvContractApplication extends AvContract {
37
58
  export interface AvContractClient extends AvContract {
38
59
  /** The protocol version and this Contract's hash. */
39
60
  readonly protocol: AvContractClientProtocol;
61
+ /**
62
+ * Present exactly when the framework's `access` option is configured: the
63
+ * `POST /_access/can` route, and `$access`'s other verbs when it has `verbs`.
64
+ */
65
+ readonly access?: AvContractClientAccess;
40
66
  }
41
67
  /**
42
68
  * The Contract an AI agent's calls run under (the `agent` layer), identified by
@@ -1,4 +1,4 @@
1
- import type { AvPaginationCapability, AvProjectionScope, AvRelationFilterCapability, AvRelationMutationDirective, AvRelationReducer, AvScalarFilterOperator, AvScalarMutationDirective, AvWriteScope } from "./capability.js";
1
+ import type { AvPaginationCapability, AvProjectionScope, AvRelationFilterCapability, AvRelationMutationDirective, AvRelationReducer, AvScalarFilterOperator, AvScalarMutationDirective, AvWriteScope, ScalarFilterMode } from "./capability.js";
2
2
  import type { AvContract } from "./contract.js";
3
3
  import type { AvFieldCheck } from "./field-check.js";
4
4
  import type { JsonSchema } from "./json-shape.js";
@@ -53,6 +53,19 @@ export type AvRelationMutationCapability = readonly AvRelationMutationDirective[
53
53
  export type AvScalarFieldCapabilities = {
54
54
  readonly select?: boolean;
55
55
  readonly filter?: readonly AvScalarFilterOperator[];
56
+ /**
57
+ * The modes the `mode` operator takes on this field: `"default"` (the
58
+ * database's own comparison) always, and `"sensitive"` or `"insensitive"`
59
+ * where the adapter honours them. Present exactly when `filter` offers `mode`.
60
+ */
61
+ readonly filterModes?: readonly ScalarFilterMode[];
62
+ /**
63
+ * A `json` field's modes when the filter names a `path`: the text compared is
64
+ * the value at that path rather than the whole document, and the database may
65
+ * honour more modes there. Present exactly when `filter` offers `mode` and
66
+ * `path`; without a `path`, `filterModes` applies.
67
+ */
68
+ readonly pathFilterModes?: readonly ScalarFilterMode[];
56
69
  readonly order?: boolean;
57
70
  readonly orderNulls?: boolean;
58
71
  readonly create?: readonly AvScalarMutationDirective[];
@@ -125,6 +138,12 @@ export type AvScalarField<C extends AvContract> = {
125
138
  * `list: true` length check, which applies to the list.
126
139
  */
127
140
  readonly validators?: readonly AvFieldCheck[];
141
+ /**
142
+ * `true` when the policy governs the field: it is left out of every default
143
+ * projection, and a caller that wants it names it (`select: ["$defaults",
144
+ * "email"]`).
145
+ */
146
+ readonly governed?: true;
128
147
  };
129
148
  /** A relation field; the `target` Resource describes the related row. */
130
149
  export type AvRelationField<C extends AvContract> = {
@@ -133,6 +152,44 @@ export type AvRelationField<C extends AvContract> = {
133
152
  readonly cardinality: "one" | "many";
134
153
  readonly nullable: boolean;
135
154
  readonly capabilities: AvRelationFieldCapabilities;
155
+ /**
156
+ * The target's relation field that is this relation's other end
157
+ * (`User.posts` ↔ `Post.author`): a record written through this relation sits
158
+ * at that field's `"loopback"` write position. Absent when the adapter states
159
+ * no pairing or the layer hides the other end.
160
+ */
161
+ readonly inverse?: string;
162
+ /**
163
+ * The scalar fields of this Resource that hold the relation's reference, in the
164
+ * order the data source pairs them with the target's key (`Post.category` has
165
+ * `["categoryId"]`). Writing the relation and one of them in one record names
166
+ * the reference twice; inside a record written through the relation's other
167
+ * end they sit at the `"loopback"` position with it. Absent on the end that
168
+ * holds no reference, and where the layer shows none of them.
169
+ */
170
+ readonly referenceFields?: readonly [string, ...string[]];
171
+ /**
172
+ * `true` when the policy engine's `governed` set names the relation: including
173
+ * it on rows the caller may not read it on refuses the request.
174
+ */
175
+ readonly governed?: true;
136
176
  };
137
177
  /** One field of a Resource: a scalar or a relation. */
138
178
  export type AvField<C extends AvContract> = AvScalarField<C> | AvRelationField<C>;
179
+ /**
180
+ * A relation field as the Core Contract states it: an {@link AvRelationField}
181
+ * and, on the end that holds the reference, the target's fields it names.
182
+ */
183
+ export type AvCoreRelationField<C extends AvContract> = AvRelationField<C> & {
184
+ /**
185
+ * The target's scalar fields that `referenceFields` name, pair by pair in the
186
+ * same order (`Post.author`: `referenceFields: ["authorId"]`,
187
+ * `references: ["id"]`). Present only beside a `referenceFields` the
188
+ * Contract states whole, and only when every one of them is a scalar field
189
+ * the Core Contract shows on the target; absent on the end that holds no
190
+ * reference, where the other end (`inverse`) states the pairs.
191
+ */
192
+ readonly references?: readonly [string, ...string[]];
193
+ };
194
+ /** One field of a Resource as the Core Contract states it. */
195
+ export type AvCoreField<C extends AvContract> = AvScalarField<C> | AvCoreRelationField<C>;
@@ -0,0 +1,11 @@
1
+ type Schema = {
2
+ readonly [key: string]: unknown;
3
+ };
4
+ /**
5
+ * `value` projected onto `schema` (the `json` field subset): every object down
6
+ * to the members the schema declares, a list member by member, frozen.
7
+ *
8
+ * @throws TypeError when the value is not one the schema describes.
9
+ */
10
+ export declare function projectJsonValue(value: unknown, schema: Schema): unknown;
11
+ export {};
@@ -0,0 +1,78 @@
1
+ function isRecord(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
4
+ function memberSchema(schema) {
5
+ if (!isRecord(schema)) {
6
+ throw new TypeError("the schema is not one of the forms a ClientContract states");
7
+ }
8
+ return schema;
9
+ }
10
+ function fits(value, schema) {
11
+ if (Object.hasOwn(schema, "const")) {
12
+ return value === schema.const;
13
+ }
14
+ if (Array.isArray(schema.enum)) {
15
+ return schema.enum.includes(value);
16
+ }
17
+ for (const keyword of ["anyOf", "oneOf"]) {
18
+ const options = schema[keyword];
19
+ if (Array.isArray(options)) {
20
+ return options.some((option) => fits(value, option));
21
+ }
22
+ }
23
+ switch (schema.type) {
24
+ case "null":
25
+ return value === null;
26
+ case "boolean":
27
+ return typeof value === "boolean";
28
+ case "string":
29
+ return typeof value === "string";
30
+ case "number":
31
+ return typeof value === "number";
32
+ case "integer":
33
+ return Number.isInteger(value);
34
+ case "array":
35
+ return Array.isArray(value);
36
+ case "object":
37
+ return isRecord(value);
38
+ default:
39
+ return false;
40
+ }
41
+ }
42
+ export function projectJsonValue(value, schema) {
43
+ if (!fits(value, schema)) {
44
+ throw new TypeError("the value is not the one the ClientContract describes");
45
+ }
46
+ for (const keyword of ["anyOf", "oneOf"]) {
47
+ const options = schema[keyword];
48
+ if (Array.isArray(options)) {
49
+ const option = options.find((candidate) => fits(value, candidate));
50
+ return projectJsonValue(value, option);
51
+ }
52
+ }
53
+ if (Array.isArray(value)) {
54
+ const prefix = schema.prefixItems;
55
+ const items = Array.isArray(prefix)
56
+ ? undefined
57
+ : memberSchema(schema.items);
58
+ return Object.freeze(value.map((member, index) => projectJsonValue(member, items ?? memberSchema(prefix[index]))));
59
+ }
60
+ if (isRecord(value)) {
61
+ const properties = schema.properties;
62
+ if (isRecord(properties)) {
63
+ const copy = {};
64
+ for (const [key, member] of Object.entries(properties)) {
65
+ if (Object.hasOwn(value, key) && value[key] !== undefined) {
66
+ copy[key] = projectJsonValue(value[key], member);
67
+ }
68
+ }
69
+ return Object.freeze(copy);
70
+ }
71
+ const values = memberSchema(schema.additionalProperties);
72
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, member]) => [
73
+ key,
74
+ projectJsonValue(member, values),
75
+ ])));
76
+ }
77
+ return value;
78
+ }
@@ -78,5 +78,7 @@ type ListValue<List, Atom> = [List] extends [false] ? Atom : [List] extends [tru
78
78
  * - `list: true` → `readonly T[]`, whose entries are never `null`;
79
79
  * - `nullable: true` → `| null`, on the whole value, list or not.
80
80
  */
81
- export type AvScalarValueType<C extends AvContract, Field extends AvScalarField<C>, Forms extends ScalarForms = AvApplicationScalarForms> = ListValue<Field["list"], FieldAtom<C, Field, Forms>> | (true extends Field["nullable"] ? null : never);
81
+ export type AvScalarValueType<C extends AvContract, Field extends AvScalarField<C>, Forms extends ScalarForms = AvApplicationScalarForms> = [
82
+ ListValue<Field["list"], FieldAtom<C, Field, Forms>> | (true extends Field["nullable"] ? null : never)
83
+ ] extends [infer Value] ? Value : never;
82
84
  export {};
@@ -0,0 +1,116 @@
1
+ import type { AvContract } from "../contracts/contract.js";
2
+ import type { AvApplicationScalarForms } from "../contracts/scalar-value-type.js";
3
+ import type { AvResourceKey, AvUniqueWhereInput } from "../operations/operation-arguments.js";
4
+ import type { AvAccessProvider } from "../policy/access-provider.interface.js";
5
+ import type { AvAccessQuestion } from "../policy/access-question.js";
6
+ import type { AccessQuestionsAnswerer } from "../policy/access-question.runtime.js";
7
+ import type { AvAccessVerbs } from "../policy/access-verbs.js";
8
+ import type { AvState } from "../runtime/request-state.js";
9
+ /** The questions a client of `C` may ask: one per Resource, with its own unique `where` in the application forms. */
10
+ export type AccessQuestionOf<C extends AvContract> = C extends AvContract ? {
11
+ readonly [R in AvResourceKey<C>]: AvAccessQuestion<R, AvUniqueWhereInput<C, R, AvApplicationScalarForms>>;
12
+ }[AvResourceKey<C>] : never;
13
+ /** The headers a server-side `$access` call hands Access: a Web `Headers`, or a Node request's `headers`. */
14
+ type AccessHeadersInput = {
15
+ get(name: string): string | null;
16
+ has(name: string): boolean;
17
+ forEach(callback: (value: string, name: string) => void): void;
18
+ } | {
19
+ readonly [name: string]: string | readonly string[] | undefined;
20
+ };
21
+ /** A server-side `$access` call's options. */
22
+ type ServerAccessOptions = {
23
+ /** As `AvExecutionOptions.requestId`: `can`'s request id. */
24
+ readonly requestId?: string;
25
+ /**
26
+ * As `AvExecutionOptions.state`: the state `can` is answered under. Left out,
27
+ * `can` is answered under the state the provider seeds from `headers`, or
28
+ * anonymously without them.
29
+ */
30
+ readonly state?: AvState & object;
31
+ /**
32
+ * The request's headers, which Access reads the session from: a Web
33
+ * `Headers`, or a Node request's `headers` (`req.headers`). Omitted, none.
34
+ */
35
+ readonly headers?: AccessHeadersInput;
36
+ /** The host's own request, handed to Access as `state` is. */
37
+ readonly request?: unknown;
38
+ /**
39
+ * The host's own response (Express's `res`, Fastify's `reply`): Access sets
40
+ * a sign-in's or sign-out's cookie on it. Under Next, a Server Action needs
41
+ * none: Access sets it through Next's `cookies()`.
42
+ */
43
+ readonly response?: unknown;
44
+ };
45
+ /** What a verb's `call` resolves, `null` and `undefined` taken out. */
46
+ type CallResult<Verb> = Verb extends {
47
+ call(input: never): Promise<infer Result>;
48
+ } ? Exclude<Result, null | undefined> : unknown;
49
+ /** The `args` a verb's `call` reads. */
50
+ type CallArgs<Verb> = Verb extends {
51
+ call(input: infer Input): Promise<unknown>;
52
+ } ? Input extends {
53
+ readonly args: infer Args;
54
+ } ? Args : unknown : unknown;
55
+ /** The provider's `client`, when its type states one. */
56
+ type ClientOf<A> = A extends {
57
+ readonly client: infer Client;
58
+ } ? Exclude<Client, undefined> : never;
59
+ /** The verbs a provider's `verbs` type states as present (an optional one is no verb). */
60
+ type PresentVerbs<Verbs> = {
61
+ [V in keyof Verbs]-?: undefined extends Verbs[V] ? never : V;
62
+ }[keyof Verbs] & ProviderVerbName;
63
+ /** The verbs a provider's `client` may state. */
64
+ type ProviderVerbName = keyof NonNullable<AvAccessProvider["client"]>["verbs"];
65
+ /** `$access` of a client of `C` whose provider is `A`: `can`, and the verbs its `client` offers. */
66
+ type ServerAccess<C extends AvContract, A> = [ClientOf<A>] extends [never] ? Pick<AvAccessVerbs<AccessQuestionOf<C>, never, {
67
+ signIn: never;
68
+ signUp: never;
69
+ resetPassword: never;
70
+ switchUser: never;
71
+ setPin: never;
72
+ enrollDevice: never;
73
+ revokeDevice: never;
74
+ }, ServerAccessOptions>, "can"> : ClientOf<A> extends {
75
+ readonly verbs: infer Verbs;
76
+ } ? Pick<AvAccessVerbs<AccessQuestionOf<C>, CallResult<VerbOf<Verbs, "me">>, {
77
+ signIn: CallArgs<VerbOf<Verbs, "signIn">>;
78
+ signUp: CallArgs<VerbOf<Verbs, "signUp">>;
79
+ resetPassword: CallArgs<VerbOf<Verbs, "resetPassword">>;
80
+ switchUser: CallArgs<VerbOf<Verbs, "switchUser">>;
81
+ setPin: CallArgs<VerbOf<Verbs, "setPin">>;
82
+ enrollDevice: CallArgs<VerbOf<Verbs, "enrollDevice">>;
83
+ revokeDevice: CallArgs<VerbOf<Verbs, "revokeDevice">>;
84
+ }, ServerAccessOptions>, "can" | PresentVerbs<Verbs>> : never;
85
+ /** One verb of a provider's `verbs` type, or `never` when it states none. */
86
+ type VerbOf<Verbs, V extends string> = V extends keyof Verbs ? Exclude<Verbs[V], undefined> : never;
87
+ /** The configuration's `access` provider; `undefined` without one. */
88
+ export type AccessProviderOf<Cfg> = Cfg extends {
89
+ readonly access: infer A extends AvAccessProvider;
90
+ } ? A : undefined;
91
+ /**
92
+ * `framework.serverClient`'s `$access` member over the Client Contract's view
93
+ * `C`: `{ $access }` with `access`, nothing (`unknown`) without it.
94
+ */
95
+ export type ServerClientAccess<C, A> = [A] extends [undefined] ? unknown : C extends AvContract ? {
96
+ /**
97
+ * The policy's answers for the UI (`can`) and, when the provider
98
+ * states its `client`, the Aventara Access verbs, each run in
99
+ * process with the headers the call names.
100
+ */
101
+ readonly $access: ServerAccess<C, A>;
102
+ } : unknown;
103
+ /** What `$access` reads off the Framework. */
104
+ type ServerClientAccessDependencies = {
105
+ /** The Client Contract, whose unique `where`s a question's is encoded by. */
106
+ readonly contract: AvContract;
107
+ /** The `can` route's answerer. */
108
+ readonly answer: AccessQuestionsAnswerer;
109
+ /** The provider's `client`, when it has one. */
110
+ readonly client: AvAccessProvider["client"];
111
+ /** The provider's `state`, called on the provider: `can`'s state from a call's `headers`. */
112
+ readonly state: AvAccessProvider["state"];
113
+ };
114
+ /** A server-side `$access`, as `serverClient` carries it. */
115
+ export declare function createServerClientAccess(dependencies: ServerClientAccessDependencies): object;
116
+ export {};