@aventara/core 1.0.0-rc.3 → 1.0.0-rc.8

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 (96) hide show
  1. package/CHANGELOG.md +231 -1
  2. package/ERRORS.md +6 -6
  3. package/README.md +76 -12
  4. package/dist/adapters/adapter-field.d.ts +8 -0
  5. package/dist/adapters/adapter-identifier.d.ts +9 -0
  6. package/dist/adapters/adapter-identifier.js +6 -1
  7. package/dist/adapters/adapter-operation.d.ts +13 -0
  8. package/dist/compiler/contract.compiler.d.ts +6 -0
  9. package/dist/compiler/contract.compiler.js +99 -15
  10. package/dist/config/computed-fields.js +2 -9
  11. package/dist/config/config-diagnostic.guidance.js +3 -3
  12. package/dist/config/config.resolver.d.ts +14 -0
  13. package/dist/config/config.resolver.js +31 -0
  14. package/dist/config/config.validator.js +82 -0
  15. package/dist/config/exact-framework-config.d.ts +5 -0
  16. package/dist/config/framework-config.d.ts +30 -1
  17. package/dist/contracts/client-contract-structure.validator.d.ts +8 -0
  18. package/dist/contracts/client-contract-structure.validator.js +6 -1
  19. package/dist/contracts/contract.d.ts +15 -1
  20. package/dist/contracts/field.d.ts +28 -0
  21. package/dist/contracts/scalar-value-type.d.ts +3 -1
  22. package/dist/framework/contract-view.d.ts +41 -10
  23. package/dist/framework/framework-construction.error.d.ts +2 -1
  24. package/dist/framework/framework-construction.error.js +11 -1
  25. package/dist/framework/framework.d.ts +4 -4
  26. package/dist/framework/framework.factory.js +39 -11
  27. package/dist/framework/operation-facade.d.ts +29 -10
  28. package/dist/framework/operation-facade.js +2 -1
  29. package/dist/index.d.ts +10 -4
  30. package/dist/internal.d.ts +2 -2
  31. package/dist/internal.js +2 -0
  32. package/dist/operations/operation-arguments.d.ts +44 -28
  33. package/dist/operations/operation-call.d.ts +9 -5
  34. package/dist/operations/operation-result.d.ts +6 -4
  35. package/dist/operations/operation-validator-core.js +6 -0
  36. package/dist/policy/policy-admission.d.ts +22 -0
  37. package/dist/policy/policy-admission.js +409 -0
  38. package/dist/policy/policy-after-update.runtime.d.ts +29 -0
  39. package/dist/policy/policy-after-update.runtime.js +132 -0
  40. package/dist/policy/policy-condition.d.ts +15 -0
  41. package/dist/policy/policy-condition.js +36 -0
  42. package/dist/policy/policy-engine.d.ts +118 -0
  43. package/dist/policy/policy-engine.js +1 -0
  44. package/dist/policy/policy-nested-write.runtime.d.ts +38 -0
  45. package/dist/policy/policy-nested-write.runtime.js +453 -0
  46. package/dist/policy/policy-read.runtime.d.ts +81 -0
  47. package/dist/policy/policy-read.runtime.js +503 -0
  48. package/dist/policy/policy-request.d.ts +41 -0
  49. package/dist/policy/policy-request.js +63 -0
  50. package/dist/policy/policy-session.d.ts +93 -0
  51. package/dist/policy/policy-session.js +235 -0
  52. package/dist/policy/policy-startup.validator.d.ts +8 -0
  53. package/dist/policy/policy-startup.validator.js +104 -0
  54. package/dist/policy/policy-write.runtime.d.ts +37 -0
  55. package/dist/policy/policy-write.runtime.js +409 -0
  56. package/dist/prebuilt/core-version.generated.d.ts +2 -0
  57. package/dist/prebuilt/core-version.generated.js +1 -0
  58. package/dist/prebuilt/prebuilt-changes.d.ts +28 -0
  59. package/dist/prebuilt/prebuilt-changes.js +65 -0
  60. package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
  61. package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
  62. package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
  63. package/dist/prebuilt/prebuilt-contracts.js +1 -0
  64. package/dist/prebuilt/prebuilt-contracts.loader.d.ts +21 -0
  65. package/dist/prebuilt/prebuilt-contracts.loader.js +77 -0
  66. package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
  67. package/dist/prebuilt/prebuilt-inputs.fingerprint.js +192 -0
  68. package/dist/protocol/absent-route.responder.d.ts +4 -4
  69. package/dist/protocol/http-request.decoder.d.ts +2 -2
  70. package/dist/protocol/http-request.reader.d.ts +2 -2
  71. package/dist/protocol/protocol-headers.d.ts +3 -3
  72. package/dist/runtime/computed-read-runtime.d.ts +6 -1
  73. package/dist/runtime/computed-read-runtime.js +226 -52
  74. package/dist/runtime/context-executor.runtime.d.ts +2 -2
  75. package/dist/runtime/context-executor.runtime.js +4 -2
  76. package/dist/runtime/create-requirement.js +1 -1
  77. package/dist/runtime/diagnostics-reporter.d.ts +5 -5
  78. package/dist/runtime/execution-options.d.ts +8 -0
  79. package/dist/runtime/framework-warning.emitter.d.ts +6 -0
  80. package/dist/runtime/framework-warning.emitter.js +8 -0
  81. package/dist/runtime/level-lifecycle.runtime.js +3 -2
  82. package/dist/runtime/operation-code.guidance.js +3 -3
  83. package/dist/runtime/operation-request.d.ts +16 -9
  84. package/dist/runtime/operation-runtime.d.ts +15 -7
  85. package/dist/runtime/operation-runtime.js +139 -14
  86. package/dist/runtime/owned-execution.registry.d.ts +2 -2
  87. package/dist/runtime/readonly-value.d.ts +5 -1
  88. package/dist/runtime/readonly-value.js +67 -28
  89. package/dist/runtime/referential-action.d.ts +1 -1
  90. package/dist/transactions/sql-statement.d.ts +2 -2
  91. package/dist/transactions/transaction-plan-assembler.d.ts +2 -2
  92. package/dist/transactions/transaction-plan-validator.d.ts +5 -5
  93. package/dist/transactions/transaction-request.d.ts +8 -8
  94. package/dist/transactions/transaction-runtime.d.ts +5 -2
  95. package/dist/transactions/transaction-runtime.js +33 -6
  96. package/package.json +2 -2
@@ -81,15 +81,28 @@ function cloneAdapterField(field) {
81
81
  ...(Array.isArray(field.referenceFields) && field.referenceFields.length > 0
82
82
  ? { referenceFields: [...field.referenceFields] }
83
83
  : {}),
84
+ ...(Array.isArray(field.referenceFields) &&
85
+ Array.isArray(field.references) &&
86
+ field.references.length > 0 &&
87
+ field.references.length === field.referenceFields.length
88
+ ? { references: [...field.references] }
89
+ : {}),
84
90
  };
85
91
  }
86
- function pruneHiddenPairings(projection) {
92
+ function pruneHiddenPairings(projection, references) {
87
93
  const resources = projection.resources;
88
94
  for (const [name, resource] of Object.entries(resources)) {
89
95
  for (const field of Object.values(resource.fields)) {
90
96
  if (field.kind !== "relation") {
91
97
  continue;
92
98
  }
99
+ if (Array.isArray(field.references) &&
100
+ (references === "none" ||
101
+ !wholePairing(resource.fields, Object.hasOwn(resources, String(field.target))
102
+ ? resources[String(field.target)]?.fields
103
+ : undefined, field.referenceFields, field.references))) {
104
+ delete field.references;
105
+ }
93
106
  if (Array.isArray(field.referenceFields)) {
94
107
  const shown = field.referenceFields.filter((reference) => typeof reference === "string" &&
95
108
  Object.hasOwn(resource.fields, reference) &&
@@ -113,6 +126,16 @@ function pruneHiddenPairings(projection) {
113
126
  }
114
127
  }
115
128
  }
129
+ function wholePairing(fields, targetFields, referenceFields, references) {
130
+ const shownIn = (within, name) => typeof name === "string" &&
131
+ within !== undefined &&
132
+ Object.hasOwn(within, name) &&
133
+ within[name]?.kind === "scalar";
134
+ return (Array.isArray(referenceFields) &&
135
+ referenceFields.length === references.length &&
136
+ referenceFields.every((name) => shownIn(fields, name)) &&
137
+ references.every((name) => shownIn(targetFields, name)));
138
+ }
116
139
  function cloneOperations(operations) {
117
140
  return cloneValue(operations);
118
141
  }
@@ -1002,18 +1025,40 @@ function projectCore(model, config, diagnostics) {
1002
1025
  baseline.transactions = resolveTransactions(model, "root", scope, diagnostics);
1003
1026
  checkResourceTree(model, baseline, "root", config.trees.root, config.definitions, diagnostics);
1004
1027
  applyFieldDefinitions(model, baseline, config.definitions, diagnostics);
1028
+ markGovernedFields(baseline, config.policy?.governed);
1005
1029
  applyCompute(baseline, scope.compute);
1006
1030
  checkLayerValidators(baseline.resources, "root", config.trees.root, diagnostics);
1007
1031
  const validators = compileValidatorTable(baseline.resources, scope.pipelines);
1008
1032
  const projection = projectExposure({ layer: "root", style: exposureStyle(scope), diagnostics }, baseline, baseline, scope.exemptions);
1009
1033
  qualifyFilterModes(projection);
1010
- pruneHiddenPairings(projection);
1034
+ pruneHiddenPairings(projection, "shown");
1011
1035
  projectCreateRequirements(model, projection.resources);
1012
1036
  checkHiddenRequirements(model, "root", baseline.resources, baseline.resources, projection.resources, diagnostics);
1013
1037
  applyValidators(projection.resources, validators);
1014
1038
  validateProjection(projection, "root", diagnostics);
1015
1039
  return { baseline, projection, validators };
1016
1040
  }
1041
+ function markGovernedFields(baseline, governed) {
1042
+ if (governed === undefined) {
1043
+ return;
1044
+ }
1045
+ for (const [resourceName, names] of Object.entries(governed)) {
1046
+ const resource = Object.hasOwn(baseline.resources, resourceName)
1047
+ ? baseline.resources[resourceName]
1048
+ : undefined;
1049
+ if (resource === undefined) {
1050
+ continue;
1051
+ }
1052
+ for (const name of names) {
1053
+ const field = Object.hasOwn(resource.fields, name)
1054
+ ? resource.fields[name]
1055
+ : undefined;
1056
+ if (field !== undefined) {
1057
+ field.governed = true;
1058
+ }
1059
+ }
1060
+ }
1061
+ }
1017
1062
  function projectDerivedScope(model, scopeName, core, parentView, config, diagnostics) {
1018
1063
  const scope = config[scopeName];
1019
1064
  const baseline = cloneValue(core.baseline);
@@ -1037,7 +1082,7 @@ function projectDerivedScope(model, scopeName, core, parentView, config, diagnos
1037
1082
  ...(scopeName === "agent" ? { parent: config.agent.parent } : {}),
1038
1083
  }, baseline, parent, scope.exemptions);
1039
1084
  qualifyFilterModes(projection);
1040
- pruneHiddenPairings(projection);
1085
+ pruneHiddenPairings(projection, "none");
1041
1086
  projectCreateRequirements(model, projection.resources);
1042
1087
  checkHiddenRequirements(model, scopeName, baseline.resources, parent.resources, projection.resources, diagnostics);
1043
1088
  applyValidators(projection.resources, validators);
@@ -1095,18 +1140,57 @@ export async function compileContracts(model, config) {
1095
1140
  };
1096
1141
  }
1097
1142
  }
1098
- bindShapedFields(core, compilerConfig.definitions);
1099
- bindShapedFields(application, compilerConfig.definitions);
1100
- bindShapedFields(client, compilerConfig.definitions);
1101
- bindShapedFields(agent, compilerConfig.definitions);
1102
- bindFieldValidators(core, coreCompilation.validators);
1103
- bindFieldValidators(application, applicationProjection.validators);
1104
- bindFieldValidators(client, clientProjection.validators);
1105
- bindFieldValidators(agent, agentProjection.validators);
1106
- return {
1107
- ok: true,
1108
- contracts: { core, application, client, agent },
1109
- };
1143
+ const contracts = { core, application, client, agent };
1144
+ bindRuntimePieces(contracts, compilerConfig.definitions, {
1145
+ core: coreCompilation.validators,
1146
+ application: applicationProjection.validators,
1147
+ client: clientProjection.validators,
1148
+ agent: agentProjection.validators,
1149
+ });
1150
+ return { ok: true, contracts };
1151
+ }
1152
+ function bindRuntimePieces(contracts, definitions, validators) {
1153
+ for (const origin of ["core", "application", "client", "agent"]) {
1154
+ bindShapedFields(contracts[origin], definitions);
1155
+ bindFieldValidators(contracts[origin], validators[origin]);
1156
+ }
1157
+ }
1158
+ export function bindPrebuiltContracts(model, config, contracts) {
1159
+ const compilerConfig = config;
1160
+ const fields = validatedFieldsView(model, compilerConfig.definitions);
1161
+ bindRuntimePieces(contracts, compilerConfig.definitions, {
1162
+ core: compileValidatorTable(fields, compilerConfig.root.pipelines),
1163
+ application: compileValidatorTable(fields, compilerConfig.application.pipelines),
1164
+ client: compileValidatorTable(fields, compilerConfig.client.pipelines),
1165
+ agent: compileValidatorTable(fields, compilerConfig.agent.pipelines),
1166
+ });
1167
+ }
1168
+ function validatedFieldsView(model, definitions) {
1169
+ const resources = { ...model.resources };
1170
+ for (const [resourceName, resourceDefinitions] of Object.entries(definitions)) {
1171
+ const resource = Object.hasOwn(model.resources, resourceName)
1172
+ ? model.resources[resourceName]
1173
+ : undefined;
1174
+ if (resource === undefined) {
1175
+ continue;
1176
+ }
1177
+ let added;
1178
+ for (const [fieldName, definition] of Object.entries(resourceDefinitions)) {
1179
+ if (Object.hasOwn(resource.fields, fieldName)) {
1180
+ continue;
1181
+ }
1182
+ added ??= {};
1183
+ added[fieldName] = {
1184
+ kind: "scalar",
1185
+ type: definition.type,
1186
+ list: definition.list,
1187
+ };
1188
+ }
1189
+ if (added !== undefined) {
1190
+ resources[resourceName] = { fields: { ...resource.fields, ...added } };
1191
+ }
1192
+ }
1193
+ return resources;
1110
1194
  }
1111
1195
  async function identifiedContract(projection) {
1112
1196
  const members = canonicalizeContractMembers({
@@ -1,3 +1,4 @@
1
+ import { emitFrameworkWarning } from "../runtime/framework-warning.emitter.js";
1
2
  import { computeOfLayer } from "./field-compute.resolver.js";
2
3
  function layerComputedFields(resources) {
3
4
  const result = {};
@@ -68,13 +69,5 @@ export function warnOnStaleComputedFields(generated, configured) {
68
69
  const message = `generated types are stale: run aventara:prepare. The computed fields the pre-compute stages are typed with differ from the configuration's:\n${drift
69
70
  .map((line) => ` - ${line}`)
70
71
  .join("\n")}`;
71
- const emitWarning = globalThis.process?.emitWarning;
72
- if (typeof emitWarning === "function") {
73
- emitWarning(message, {
74
- type: "AventaraWarning",
75
- code: STALE_GENERATED_TYPES_WARNING,
76
- });
77
- return;
78
- }
79
- globalThis.console?.warn(`AventaraWarning: ${message}`);
72
+ emitFrameworkWarning(message, STALE_GENERATED_TYPES_WARNING);
80
73
  }
@@ -1,12 +1,12 @@
1
1
  export const CONFIG_DIAGNOSTIC_GUIDANCE = {
2
2
  CONFIG_REQUIRED: {
3
3
  message: "A required configuration key is missing.",
4
- cause: "A key that must be present is absent: api, adapter, or an overlay's exposed when the overlay sets exemptions.",
4
+ cause: "A key that must be present is absent: api, adapter, or an overlay's exposed when the overlay sets exemptions, or policy.engine or policy.principal when policy is set.",
5
5
  fix: "Add the key at the diagnostic's path.",
6
6
  },
7
7
  CONFIG_UNKNOWN_PROPERTY: {
8
8
  message: "Unknown configuration key.",
9
- cause: "A key the configuration does not define at that place: a typo, or a key written at the wrong level of the tree.",
9
+ cause: "A key the configuration does not define at that place: a typo, or a key written at the wrong level of the tree. `policy` and `prebuilt` are root-only keys; in an overlay they read as unknown.",
10
10
  fix: 'Remove it, or move it where it belongs (the configuration reference\'s "Where a key can go").',
11
11
  },
12
12
  CONFIG_INVALID_TYPE: {
@@ -16,7 +16,7 @@ export const CONFIG_DIAGNOSTIC_GUIDANCE = {
16
16
  },
17
17
  CONFIG_INVALID_VALUE: {
18
18
  message: "A configuration value is not allowed.",
19
- cause: 'A malformed api or mcp path, a negative limit, an unknown transactionsStyle, a format that is not a named zod format, or forceTransaction under an effective transactionsStyle "none".',
19
+ cause: 'A malformed api or mcp path, a negative limit, an unknown transactionsStyle, a format that is not a named zod format, forceTransaction under an effective transactionsStyle "none", an after check on any action but update, or a policy engine\'s governed, paths or afterChecks naming a Resource, field or path the Core Contract does not have (the message names the Resource or the rule).',
20
20
  fix: "Use a value the message allows.",
21
21
  },
22
22
  CONFIG_MOVED_PROPERTY: {
@@ -1,6 +1,7 @@
1
1
  import type { AvAdapter } from "../adapters/adapter.interface.js";
2
2
  import type { AvAdapterModel } from "../adapters/adapter-model.js";
3
3
  import type { AvTransactionCapability } from "../contracts/transaction-capability.js";
4
+ import type { AvPolicyEngine } from "../policy/policy-engine.js";
4
5
  import type { AvDiagnosticsSink } from "../runtime/diagnostics-reporter.js";
5
6
  import type { AvMisuseSink } from "../runtime/misuse-reporter.js";
6
7
  import type { AvConfigAgentParent } from "./config-layer.js";
@@ -8,6 +9,17 @@ import { type ResolvedComputeConfig } from "./field-compute.resolver.js";
8
9
  import type { AvConfig, AvConfigLimits } from "./framework-config.js";
9
10
  import type { AvConfigFieldDefinition } from "./framework-field-definition.js";
10
11
  import { type ResolvedPipelineTree } from "./pipeline-tree.resolver.js";
12
+ /** The root's `policy`, its engine's passive lists read once. */
13
+ export type ResolvedPolicyConfig = {
14
+ readonly engine: AvPolicyEngine;
15
+ readonly principal: NonNullable<AvConfig["policy"]>["principal"];
16
+ /** The governed fields and relations, by Resource. */
17
+ readonly governed: Readonly<Record<string, readonly string[]>>;
18
+ /** The rule paths, by rule name, each a list of segments. */
19
+ readonly paths: Readonly<Record<string, readonly (readonly string[])[]>>;
20
+ /** The writes whose grants carry an `after` condition, by Resource. */
21
+ readonly afterChecks: Readonly<Record<string, readonly string[]>>;
22
+ };
11
23
  /** The root's field definitions, by Resource and field name. */
12
24
  export type ResolvedFieldDefinitions = Readonly<Record<string, Readonly<Record<string, AvConfigFieldDefinition>>>>;
13
25
  /** One resolved layer of the configuration: root, application, client or agent. */
@@ -63,6 +75,8 @@ export type ResolvedFrameworkConfig<M extends AvAdapterModel = AvAdapterModel> =
63
75
  readonly diagnostics: AvDiagnosticsSink | undefined;
64
76
  /** The misuse sink, from the root configuration only. */
65
77
  readonly misuse: AvMisuseSink | undefined;
78
+ /** The policy, from the root configuration only; absent, nothing is checked. */
79
+ readonly policy: ResolvedPolicyConfig | undefined;
66
80
  /** Every field definition, from the root only; the same for all three layers. */
67
81
  readonly definitions: ResolvedFieldDefinitions;
68
82
  /** Each layer's `resources` tree as authored: the compiler checks its names. */
@@ -140,6 +140,36 @@ function resolveMcp(mcp) {
140
140
  }),
141
141
  });
142
142
  }
143
+ function frozenLists(value) {
144
+ if (!isPlainRecord(value)) {
145
+ return Object.freeze({});
146
+ }
147
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, names]) => [
148
+ key,
149
+ Object.freeze([...names]),
150
+ ])));
151
+ }
152
+ function frozenPathLists(value) {
153
+ if (!isPlainRecord(value)) {
154
+ return Object.freeze({});
155
+ }
156
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, paths]) => [
157
+ key,
158
+ Object.freeze(paths.map((path) => Object.freeze([...path]))),
159
+ ])));
160
+ }
161
+ function resolvePolicy(policy) {
162
+ if (policy === undefined) {
163
+ return undefined;
164
+ }
165
+ return Object.freeze({
166
+ engine: policy.engine,
167
+ principal: policy.principal,
168
+ governed: frozenLists(policy.engine.governed),
169
+ paths: frozenPathLists(policy.engine.paths),
170
+ afterChecks: frozenLists(policy.engine.afterChecks),
171
+ });
172
+ }
143
173
  export function resolveFrameworkConfig(config) {
144
174
  const root = config;
145
175
  const application = config.application;
@@ -158,6 +188,7 @@ export function resolveFrameworkConfig(config) {
158
188
  adapter: config.adapter,
159
189
  diagnostics: config.diagnostics,
160
190
  misuse: config.misuse,
191
+ policy: resolvePolicy(config.policy),
161
192
  definitions: definitionsOf(root.resources),
162
193
  trees: {
163
194
  root: root.resources,
@@ -5,6 +5,8 @@ const ROOT_ONLY_PROPERTIES = new Set([
5
5
  "api",
6
6
  "mcp",
7
7
  "adapter",
8
+ "prebuilt",
9
+ "policy",
8
10
  "diagnostics",
9
11
  "misuse",
10
12
  "application",
@@ -618,6 +620,14 @@ export function validateFrameworkConfig(value) {
618
620
  else {
619
621
  validateAdapter(value.adapter, diagnostics);
620
622
  }
623
+ if (Object.hasOwn(value, "prebuilt") &&
624
+ value.prebuilt !== undefined &&
625
+ (value.prebuilt === null || typeof value.prebuilt !== "object")) {
626
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "prebuilt", "prebuilt must be the generated prebuilt Contracts object when provided"));
627
+ }
628
+ if (Object.hasOwn(value, "policy") && value.policy !== undefined) {
629
+ validatePolicy(value.policy, diagnostics);
630
+ }
621
631
  if (Object.hasOwn(value, "diagnostics") &&
622
632
  value.diagnostics !== undefined &&
623
633
  typeof value.diagnostics !== "function") {
@@ -639,6 +649,78 @@ export function validateFrameworkConfig(value) {
639
649
  }
640
650
  return diagnostics;
641
651
  }
652
+ const POLICY_PROPERTIES = new Set(["engine", "principal"]);
653
+ function validatePolicy(value, diagnostics) {
654
+ if (!isPlainRecord(value)) {
655
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "policy", "policy must be { engine, principal }"));
656
+ return;
657
+ }
658
+ for (const key of Object.keys(value)) {
659
+ if (!POLICY_PROPERTIES.has(key)) {
660
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", `policy.${key}`, `unknown policy property: ${key}`));
661
+ }
662
+ }
663
+ if (!Object.hasOwn(value, "principal")) {
664
+ diagnostics.push(diagnostic("CONFIG_REQUIRED", "policy.principal", "policy.principal is required"));
665
+ }
666
+ else if (typeof value.principal !== "function") {
667
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "policy.principal", "policy.principal must be a function"));
668
+ }
669
+ if (!Object.hasOwn(value, "engine")) {
670
+ diagnostics.push(diagnostic("CONFIG_REQUIRED", "policy.engine", "policy.engine is required"));
671
+ return;
672
+ }
673
+ const engine = value.engine;
674
+ if (engine === null || typeof engine !== "object") {
675
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "policy.engine", "policy.engine must be an object"));
676
+ return;
677
+ }
678
+ const members = engine;
679
+ for (const question of ["can", "scopeOf", "fieldsOf"]) {
680
+ if (typeof members[question] !== "function") {
681
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", `policy.engine.${question}`, `policy.engine.${question} must be a function`));
682
+ }
683
+ }
684
+ validatePolicyLists(members.governed, "governed", { all: "string lists", one: "a list of strings" }, isStringList, diagnostics);
685
+ validatePolicyLists(members.paths, "paths", {
686
+ all: "path lists",
687
+ one: "a list of paths, each a list of strings",
688
+ }, (paths) => Array.isArray(paths) && paths.every(isStringList), diagnostics);
689
+ validatePolicyLists(members.afterChecks, "afterChecks", { all: "action lists", one: "a list of actions" }, isStringList, diagnostics);
690
+ validateAfterCheckActions(members.afterChecks, diagnostics);
691
+ }
692
+ function validateAfterCheckActions(lists, diagnostics) {
693
+ if (!isPlainRecord(lists)) {
694
+ return;
695
+ }
696
+ for (const [resource, actions] of Object.entries(lists)) {
697
+ if (!isStringList(actions)) {
698
+ continue;
699
+ }
700
+ for (const action of actions) {
701
+ if (action !== "update") {
702
+ diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", `policy.engine.afterChecks.${resource}`, `policy.engine.afterChecks.${resource}: "${action}" is no action an after check runs on; only "update" is`));
703
+ }
704
+ }
705
+ }
706
+ }
707
+ function isStringList(value) {
708
+ return (Array.isArray(value) && value.every((name) => typeof name === "string"));
709
+ }
710
+ function validatePolicyLists(lists, list, entry, isEntry, diagnostics) {
711
+ if (lists === undefined) {
712
+ return;
713
+ }
714
+ if (!isPlainRecord(lists)) {
715
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", `policy.engine.${list}`, `policy.engine.${list} must be an object of ${entry.all} when provided`));
716
+ return;
717
+ }
718
+ for (const [key, value] of Object.entries(lists)) {
719
+ if (!isEntry(value)) {
720
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", `policy.engine.${list}.${key}`, `policy.engine.${list}.${key} must be ${entry.one}`));
721
+ }
722
+ }
723
+ }
642
724
  function validateMcp(value, api, diagnostics) {
643
725
  if (typeof value === "string") {
644
726
  validateMcpPath(value, "mcp", api, diagnostics);
@@ -238,6 +238,11 @@ type RootSchema<M extends AvAdapterModel, Cfg> = LayerKeysSchema & {
238
238
  readonly api: Leaf;
239
239
  readonly mcp: McpSchema;
240
240
  readonly adapter: Leaf;
241
+ readonly prebuilt: Leaf;
242
+ readonly policy: {
243
+ readonly engine: Leaf;
244
+ readonly principal: Leaf;
245
+ };
241
246
  readonly diagnostics: Leaf;
242
247
  readonly misuse: Leaf;
243
248
  readonly resources: RootResourcesSchema<M>;
@@ -2,10 +2,13 @@ import type { AvAdapter } from "../adapters/adapter.interface.js";
2
2
  import type { AvAdapterModel } from "../adapters/adapter-model.js";
3
3
  import type { AvContractLimits } from "../contracts/contract-limits.js";
4
4
  import type { AvTransactionCapability } from "../contracts/transaction-capability.js";
5
+ import type { AvPolicyEngine } from "../policy/policy-engine.js";
6
+ import type { AvPrebuiltContracts } from "../prebuilt/prebuilt-contracts.js";
5
7
  import type { AvDiagnosticsSink } from "../runtime/diagnostics-reporter.js";
6
8
  import type { AvMisuseSink } from "../runtime/misuse-reporter.js";
9
+ import type { AvState } from "../runtime/request-state.js";
7
10
  import type { AvConfigAny } from "./any-framework-config.js";
8
- import type { AvConfigLayerName } from "./config-layer.js";
11
+ import type { AvConfigLayerName, AvRequestOrigin } from "./config-layer.js";
9
12
  import type { AvConfigExemptions } from "./framework-exemptions-config.js";
10
13
  import type { AvConfigLayerKind, AvConfigResources, LayerOfKind } from "./framework-resources-config.js";
11
14
  import type { AvPipeline } from "./pipeline-config.js";
@@ -71,6 +74,32 @@ export type AvConfig<M extends AvAdapterModel = AvAdapterModel, Cfg = AvConfigAn
71
74
  */
72
75
  readonly mcp?: AvConfigMcp;
73
76
  readonly adapter: AvAdapter<M>;
77
+ /**
78
+ * Contracts compiled ahead of time (the generated `aventaraContracts`), for a
79
+ * fast cold start on serverless and edge platforms. Used only when they were
80
+ * compiled from this configuration and the adapter's model by this core;
81
+ * otherwise the Contracts are compiled at startup, as without it, and a
82
+ * `AVENTARA_PREBUILT_STALE` warning says why.
83
+ */
84
+ readonly prebuilt?: AvPrebuiltContracts;
85
+ /**
86
+ * The policy engine every client and agent request is checked against, and
87
+ * how a request's principal is derived from its `ctx.$state`. Application
88
+ * calls bypass it unless they name a principal (`{ as }`); calls a stage
89
+ * makes through `ctx` always do.
90
+ */
91
+ readonly policy?: {
92
+ readonly engine: AvPolicyEngine;
93
+ /**
94
+ * The request's principal, from its `ctx.$state` and its layer; `null` for
95
+ * an anonymous caller. It may answer with a promise (rc.5), which is
96
+ * awaited; a throw or a rejection answers `A3000`.
97
+ */
98
+ readonly principal: (facts: {
99
+ readonly $state: AvState;
100
+ readonly $origin: AvRequestOrigin;
101
+ }) => unknown;
102
+ };
74
103
  readonly diagnostics?: AvDiagnosticsSink;
75
104
  /** Told about every caller value refused as unreadable or not plain data. */
76
105
  readonly misuse?: AvMisuseSink;
@@ -498,6 +498,10 @@ export declare const CLIENT_CONTRACT_STRUCTURE: {
498
498
  readonly minimum: 0;
499
499
  readonly unique: false;
500
500
  };
501
+ readonly governed: {
502
+ readonly is: "literal";
503
+ readonly values: readonly [true];
504
+ };
501
505
  };
502
506
  };
503
507
  readonly relation: {
@@ -694,6 +698,10 @@ export declare const CLIENT_CONTRACT_STRUCTURE: {
694
698
  readonly minimum: 1;
695
699
  readonly unique: false;
696
700
  };
701
+ readonly governed: {
702
+ readonly is: "literal";
703
+ readonly values: readonly [true];
704
+ };
697
705
  };
698
706
  };
699
707
  };
@@ -237,6 +237,7 @@ const SCALAR_FIELD = object({
237
237
  format: STRING,
238
238
  json: { is: "jsonSchema" },
239
239
  validators: list(FIELD_CHECK),
240
+ governed: literal([true]),
240
241
  });
241
242
  const RELATION_FIELD = object({
242
243
  kind: literal(["relation"]),
@@ -257,7 +258,11 @@ const RELATION_FIELD = object({
257
258
  update: RELATION_MUTATION,
258
259
  nestedWriteFilter: literal(["scalar"]),
259
260
  }),
260
- }, { inverse: STRING, referenceFields: nonEmptyList(STRING) });
261
+ }, {
262
+ inverse: STRING,
263
+ referenceFields: nonEmptyList(STRING),
264
+ governed: literal([true]),
265
+ });
261
266
  const OPERATION_AVAILABILITY = union(literal([true]), object({ projection: literal(PROJECTION_SCOPES) }, { write: literal(WRITE_SCOPES) }), object({ write: literal(WRITE_SCOPES) }, { projection: literal(PROJECTION_SCOPES) }));
262
267
  const RESOURCE = object({
263
268
  fields: registry({
@@ -1,6 +1,7 @@
1
1
  import type { AvContractLimits } from "./contract-limits.js";
2
2
  import type { AvContractClientProtocol } from "./contract-protocol.js";
3
3
  import type { AvEnumDefinition } from "./enum-definition.js";
4
+ import type { AvCoreField } from "./field.js";
4
5
  import type { ReferenceWriteLimit } from "./reference-write-limit.js";
5
6
  import type { AvResource } from "./resource.js";
6
7
  import type { AvScalarRegistry } from "./scalar.js";
@@ -24,8 +25,21 @@ export interface AvContract {
24
25
  */
25
26
  readonly referenceWrites?: ReferenceWriteLimit;
26
27
  }
27
- /** The Contract compiled from the root configuration. */
28
+ /**
29
+ * The Contract compiled from the root configuration. Its relation fields also
30
+ * state the target's fields their reference names (`references`), which no
31
+ * other Contract carries.
32
+ */
28
33
  export interface AvContractCore extends AvContract {
34
+ /** The Resources, by name. */
35
+ readonly resources: Readonly<Record<string, AvCoreResource>>;
36
+ }
37
+ /**
38
+ * A Resource as the Core Contract states it: its fields are
39
+ * {@link AvCoreField}s, so its relation fields carry `references`.
40
+ */
41
+ export interface AvCoreResource extends AvResource<AvContractCore> {
42
+ readonly fields: Readonly<Record<string, AvCoreField<AvContractCore>>>;
29
43
  }
30
44
  /** The Contract in-process calls run under (`framework.application`). */
31
45
  export interface AvContractApplication extends AvContract {
@@ -138,6 +138,12 @@ export type AvScalarField<C extends AvContract> = {
138
138
  * `list: true` length check, which applies to the list.
139
139
  */
140
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;
141
147
  };
142
148
  /** A relation field; the `target` Resource describes the related row. */
143
149
  export type AvRelationField<C extends AvContract> = {
@@ -162,6 +168,28 @@ export type AvRelationField<C extends AvContract> = {
162
168
  * holds no reference, and where the layer shows none of them.
163
169
  */
164
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;
165
176
  };
166
177
  /** One field of a Resource: a scalar or a relation. */
167
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>;
@@ -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 {};