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

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 (261) hide show
  1. package/CHANGELOG.md +1022 -16
  2. package/ERRORS.md +142 -0
  3. package/README.md +330 -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 +281 -31
  19. package/dist/compiler/create-requirements.projector.js +1 -3
  20. package/dist/compiler/field-validator.compiler.js +1 -3
  21. package/dist/compiler/managed-elsewhere.projector.d.ts +52 -0
  22. package/dist/compiler/managed-elsewhere.projector.js +179 -0
  23. package/dist/compiler/managed-elsewhere.validator.d.ts +9 -0
  24. package/dist/compiler/managed-elsewhere.validator.js +74 -0
  25. package/dist/config/cache-stage.factory.d.ts +24 -0
  26. package/dist/config/cache-stage.factory.js +3 -0
  27. package/dist/config/computed-fields.d.ts +62 -0
  28. package/dist/config/computed-fields.js +73 -0
  29. package/dist/config/config-diagnostic.guidance.d.ts +6 -0
  30. package/dist/config/config-diagnostic.guidance.js +32 -0
  31. package/dist/config/config.resolver.d.ts +24 -0
  32. package/dist/config/config.resolver.js +66 -7
  33. package/dist/config/config.validator.js +283 -21
  34. package/dist/config/define-resource.d.ts +1 -5
  35. package/dist/config/entrypoint.normalizer.d.ts +3 -2
  36. package/dist/config/exact-framework-config.d.ts +73 -22
  37. package/dist/config/field-compute.resolver.d.ts +2 -0
  38. package/dist/config/field-compute.resolver.js +9 -3
  39. package/dist/config/framework-config.d.ts +53 -15
  40. package/dist/config/framework-resources-config.d.ts +25 -14
  41. package/dist/config/pipeline-config.d.ts +20 -4
  42. package/dist/config/pipeline-tree.resolver.d.ts +2 -2
  43. package/dist/config/pipeline-tree.resolver.js +2 -3
  44. package/dist/contracts/access-route.rules.d.ts +10 -0
  45. package/dist/contracts/access-route.rules.js +45 -0
  46. package/dist/contracts/canonical-json.writer.d.ts +7 -0
  47. package/dist/contracts/canonical-json.writer.js +55 -0
  48. package/dist/contracts/capability.d.ts +26 -1
  49. package/dist/contracts/capability.js +20 -0
  50. package/dist/contracts/client-contract-structure.validator.d.ts +365 -89
  51. package/dist/contracts/client-contract-structure.validator.js +49 -2
  52. package/dist/contracts/contract-access.d.ts +29 -0
  53. package/dist/contracts/contract-access.js +1 -0
  54. package/dist/contracts/contract-canonicalizer.d.ts +9 -1
  55. package/dist/contracts/contract-canonicalizer.js +35 -4
  56. package/dist/contracts/contract-hash.d.ts +0 -1
  57. package/dist/contracts/contract-hash.js +1 -1
  58. package/dist/contracts/contract-limits.d.ts +6 -0
  59. package/dist/contracts/contract-limits.js +2 -0
  60. package/dist/contracts/contract.d.ts +42 -1
  61. package/dist/contracts/field.d.ts +58 -1
  62. package/dist/contracts/json-schema.projector.d.ts +11 -0
  63. package/dist/contracts/json-schema.projector.js +76 -0
  64. package/dist/contracts/scalar-value-type.d.ts +3 -1
  65. package/dist/framework/access-facade.d.ts +116 -0
  66. package/dist/framework/access-facade.js +118 -0
  67. package/dist/framework/contract-view.d.ts +120 -16
  68. package/dist/framework/framework-construction.error.d.ts +14 -4
  69. package/dist/framework/framework-construction.error.js +18 -2
  70. package/dist/framework/framework.d.ts +26 -5
  71. package/dist/framework/framework.factory.d.ts +1 -1
  72. package/dist/framework/framework.factory.js +145 -24
  73. package/dist/framework/operation-facade.d.ts +141 -13
  74. package/dist/framework/operation-facade.js +9 -1
  75. package/dist/framework/operation-set.runner.d.ts +47 -0
  76. package/dist/framework/operation-set.runner.js +168 -0
  77. package/dist/index.d.ts +19 -5
  78. package/dist/index.js +3 -0
  79. package/dist/internal.d.ts +9 -2
  80. package/dist/internal.js +8 -0
  81. package/dist/operations/database-refusal.d.ts +51 -0
  82. package/dist/operations/database-refusal.js +45 -0
  83. package/dist/operations/operation-arguments.d.ts +148 -60
  84. package/dist/operations/operation-call.d.ts +9 -5
  85. package/dist/operations/operation-result.d.ts +6 -4
  86. package/dist/operations/operation-validator-core.js +160 -39
  87. package/dist/operations/operation-validator.d.ts +1 -1
  88. package/dist/operations/structural-limits.walker.d.ts +1 -1
  89. package/dist/operations/structural-limits.walker.js +1 -3
  90. package/dist/operations/validation-code.guidance.d.ts +6 -0
  91. package/dist/operations/validation-code.guidance.js +90 -0
  92. package/dist/operations/write-scope.resolver.d.ts +4 -0
  93. package/dist/operations/write-scope.resolver.js +2 -0
  94. package/dist/policy/access-provider.interface.d.ts +91 -0
  95. package/dist/policy/access-provider.interface.js +1 -0
  96. package/dist/policy/access-provider.registry.d.ts +17 -0
  97. package/dist/policy/access-provider.registry.js +15 -0
  98. package/dist/policy/access-question.d.ts +20 -0
  99. package/dist/policy/access-question.js +1 -0
  100. package/dist/policy/access-question.registry.d.ts +5 -0
  101. package/dist/policy/access-question.registry.js +19 -0
  102. package/dist/policy/access-question.runtime.d.ts +36 -0
  103. package/dist/policy/access-question.runtime.js +295 -0
  104. package/dist/policy/access-verb.table.d.ts +56 -0
  105. package/dist/policy/access-verb.table.js +21 -0
  106. package/dist/policy/access-verbs.d.ts +46 -0
  107. package/dist/policy/access-verbs.js +1 -0
  108. package/dist/policy/policy-admission.d.ts +20 -0
  109. package/dist/policy/policy-admission.js +407 -0
  110. package/dist/policy/policy-after-update.runtime.d.ts +79 -0
  111. package/dist/policy/policy-after-update.runtime.js +206 -0
  112. package/dist/policy/policy-condition.d.ts +15 -0
  113. package/dist/policy/policy-condition.js +36 -0
  114. package/dist/policy/policy-engine.d.ts +205 -0
  115. package/dist/policy/policy-engine.js +1 -0
  116. package/dist/policy/policy-named-rows.d.ts +18 -0
  117. package/dist/policy/policy-named-rows.js +51 -0
  118. package/dist/policy/policy-nested-write.runtime.d.ts +44 -0
  119. package/dist/policy/policy-nested-write.runtime.js +536 -0
  120. package/dist/policy/policy-read.runtime.d.ts +81 -0
  121. package/dist/policy/policy-read.runtime.js +501 -0
  122. package/dist/policy/policy-reference-write.runtime.d.ts +51 -0
  123. package/dist/policy/policy-reference-write.runtime.js +336 -0
  124. package/dist/policy/policy-request.d.ts +72 -0
  125. package/dist/policy/policy-request.js +115 -0
  126. package/dist/policy/policy-session.d.ts +106 -0
  127. package/dist/policy/policy-session.js +293 -0
  128. package/dist/policy/policy-startup.validator.d.ts +10 -0
  129. package/dist/policy/policy-startup.validator.js +112 -0
  130. package/dist/policy/policy-write.runtime.d.ts +39 -0
  131. package/dist/policy/policy-write.runtime.js +530 -0
  132. package/dist/prebuilt/core-version.generated.d.ts +2 -0
  133. package/dist/prebuilt/core-version.generated.js +1 -0
  134. package/dist/prebuilt/prebuilt-changes.d.ts +29 -0
  135. package/dist/prebuilt/prebuilt-changes.js +63 -0
  136. package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
  137. package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
  138. package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
  139. package/dist/prebuilt/prebuilt-contracts.js +1 -0
  140. package/dist/prebuilt/prebuilt-contracts.loader.d.ts +19 -0
  141. package/dist/prebuilt/prebuilt-contracts.loader.js +75 -0
  142. package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
  143. package/dist/prebuilt/prebuilt-inputs.fingerprint.js +227 -0
  144. package/dist/protocol/absent-route.responder.d.ts +49 -14
  145. package/dist/protocol/absent-route.responder.js +92 -15
  146. package/dist/protocol/contract-document.responder.d.ts +9 -1
  147. package/dist/protocol/contract-document.responder.js +3 -2
  148. package/dist/protocol/http-request.decoder.d.ts +52 -3
  149. package/dist/protocol/http-request.decoder.js +45 -6
  150. package/dist/protocol/http-request.reader.d.ts +10 -3
  151. package/dist/protocol/http-request.reader.js +12 -2
  152. package/dist/protocol/http-response.encoder.d.ts +12 -0
  153. package/dist/protocol/http-response.encoder.js +17 -3
  154. package/dist/protocol/operation-response.guard.js +1 -3
  155. package/dist/protocol/operation-set.decoder.d.ts +48 -0
  156. package/dist/protocol/operation-set.decoder.js +185 -0
  157. package/dist/protocol/operation-set.handler.d.ts +27 -0
  158. package/dist/protocol/operation-set.handler.js +179 -0
  159. package/dist/protocol/protocol-headers.d.ts +12 -3
  160. package/dist/protocol/protocol-headers.js +12 -5
  161. package/dist/protocol/protocol-surface.d.ts +16 -3
  162. package/dist/protocol/protocol-surface.js +41 -1
  163. package/dist/protocol/protocol.binder.d.ts +52 -1
  164. package/dist/protocol/protocol.binder.js +6 -1
  165. package/dist/protocol/protocol.handler.d.ts +45 -1
  166. package/dist/protocol/protocol.handler.js +164 -19
  167. package/dist/protocol/read-cache.responder.d.ts +26 -0
  168. package/dist/protocol/read-cache.responder.js +47 -0
  169. package/dist/protocol/read-query.codec.d.ts +34 -0
  170. package/dist/protocol/read-query.codec.js +125 -0
  171. package/dist/runtime/caller-data.copier.d.ts +18 -3
  172. package/dist/runtime/caller-data.copier.js +21 -8
  173. package/dist/runtime/caller-data.door.d.ts +13 -6
  174. package/dist/runtime/computed-read-runtime.d.ts +6 -1
  175. package/dist/runtime/computed-read-runtime.js +229 -48
  176. package/dist/runtime/computed-value.error.d.ts +3 -2
  177. package/dist/runtime/computed-value.error.js +3 -2
  178. package/dist/runtime/context-executor.runtime.d.ts +4 -3
  179. package/dist/runtime/context-executor.runtime.js +4 -2
  180. package/dist/runtime/core-class.brand.d.ts +10 -0
  181. package/dist/runtime/core-class.brand.js +21 -0
  182. package/dist/runtime/create-requirement.d.ts +31 -5
  183. package/dist/runtime/create-requirement.js +130 -24
  184. package/dist/runtime/decimal.d.ts +14 -0
  185. package/dist/runtime/decimal.js +21 -0
  186. package/dist/runtime/diagnostics-reporter.d.ts +49 -9
  187. package/dist/runtime/diagnostics-reporter.js +16 -9
  188. package/dist/runtime/error-boundary.d.ts +9 -2
  189. package/dist/runtime/error-boundary.js +12 -0
  190. package/dist/runtime/error-guidance.d.ts +12 -0
  191. package/dist/runtime/error-guidance.js +1 -0
  192. package/dist/runtime/error.catalog.d.ts +49 -0
  193. package/dist/runtime/error.catalog.js +49 -0
  194. package/dist/runtime/execution-options.d.ts +8 -0
  195. package/dist/runtime/field-check.evaluator.d.ts +0 -2
  196. package/dist/runtime/field-check.evaluator.js +1 -1
  197. package/dist/runtime/field-compute.d.ts +11 -10
  198. package/dist/runtime/field-stage.runtime.d.ts +3 -3
  199. package/dist/runtime/field-stage.runtime.js +9 -11
  200. package/dist/runtime/field-validator.runtime.d.ts +19 -1
  201. package/dist/runtime/field-validator.runtime.js +49 -9
  202. package/dist/runtime/framework-error.js +9 -0
  203. package/dist/runtime/framework-warning.emitter.d.ts +6 -0
  204. package/dist/runtime/framework-warning.emitter.js +8 -0
  205. package/dist/runtime/json-value.d.ts +10 -5
  206. package/dist/runtime/level-lifecycle.runtime.d.ts +4 -3
  207. package/dist/runtime/level-lifecycle.runtime.js +3 -5
  208. package/dist/runtime/mutation-planner.d.ts +1 -6
  209. package/dist/runtime/mutation-planner.js +4 -17
  210. package/dist/runtime/nested-write.walker.js +2 -1
  211. package/dist/runtime/operation-code.guidance.d.ts +6 -0
  212. package/dist/runtime/operation-code.guidance.js +167 -0
  213. package/dist/runtime/operation-request.d.ts +16 -9
  214. package/dist/runtime/operation-runtime.d.ts +35 -17
  215. package/dist/runtime/operation-runtime.js +220 -49
  216. package/dist/runtime/outcome-mapper.d.ts +2 -1
  217. package/dist/runtime/output-validator.d.ts +1 -1
  218. package/dist/runtime/output-validator.js +1 -3
  219. package/dist/runtime/owned-execution.registry.d.ts +34 -4
  220. package/dist/runtime/pipe-output.validator.d.ts +10 -0
  221. package/dist/runtime/pipe-output.validator.js +4 -2
  222. package/dist/runtime/pipeline.d.ts +52 -23
  223. package/dist/runtime/readonly-value.d.ts +20 -5
  224. package/dist/runtime/readonly-value.js +74 -26
  225. package/dist/runtime/record.guard.d.ts +14 -0
  226. package/dist/runtime/record.guard.js +10 -0
  227. package/dist/runtime/referential-action.d.ts +1 -1
  228. package/dist/runtime/request-envelope.validator.js +13 -10
  229. package/dist/runtime/scalar-runtime.d.ts +4 -21
  230. package/dist/runtime/scalar-runtime.js +91 -31
  231. package/dist/runtime/thenable.guard.d.ts +7 -0
  232. package/dist/runtime/thenable.guard.js +5 -0
  233. package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
  234. package/dist/runtime/unsent-value.normalizer.js +129 -0
  235. package/dist/transactions/operation-arguments-encoder.js +9 -10
  236. package/dist/transactions/operation-fingerprint.js +2 -2
  237. package/dist/transactions/sql-statement.d.ts +10 -2
  238. package/dist/transactions/sql-statement.validator.d.ts +2 -2
  239. package/dist/transactions/sql-statement.validator.js +40 -6
  240. package/dist/transactions/transaction-envelope.d.ts +5 -4
  241. package/dist/transactions/transaction-integrity-error.js +2 -0
  242. package/dist/transactions/transaction-plan-assembler.d.ts +12 -5
  243. package/dist/transactions/transaction-plan-assembler.js +49 -17
  244. package/dist/transactions/transaction-plan-validator.d.ts +23 -7
  245. package/dist/transactions/transaction-plan-validator.js +31 -7
  246. package/dist/transactions/transaction-reference-collector.js +8 -10
  247. package/dist/transactions/transaction-reference-resolver.d.ts +3 -2
  248. package/dist/transactions/transaction-reference-resolver.js +1 -3
  249. package/dist/transactions/transaction-reference-validator.d.ts +43 -3
  250. package/dist/transactions/transaction-reference-validator.js +83 -57
  251. package/dist/transactions/transaction-reference.d.ts +1 -1
  252. package/dist/transactions/transaction-reference.js +5 -0
  253. package/dist/transactions/transaction-request.d.ts +8 -8
  254. package/dist/transactions/transaction-runtime.d.ts +5 -2
  255. package/dist/transactions/transaction-runtime.js +108 -27
  256. package/dist/transactions/transaction-settlement.runtime.d.ts +4 -3
  257. package/dist/transactions/transaction-slot-path-error.js +2 -0
  258. package/dist/transactions/transaction-stage.runtime.d.ts +5 -3
  259. package/dist/transactions/transaction-stage.runtime.js +8 -7
  260. package/package.json +6 -5
  261. package/src/runtime/field-check.evaluator.ts +1 -1
@@ -0,0 +1,179 @@
1
+ import { isRecord } from "../runtime/record.guard.js";
2
+ const WRITE_FAMILIES = [
3
+ "create",
4
+ "update",
5
+ "delete",
6
+ "upsert",
7
+ ];
8
+ const SOURCE_ROW_DIRECTIVES = [
9
+ "$connect",
10
+ "$disconnect",
11
+ ];
12
+ export function managedElsewhereOf(rootTree) {
13
+ const managed = new Set();
14
+ if (!isRecord(rootTree)) {
15
+ return managed;
16
+ }
17
+ for (const [name, entry] of Object.entries(rootTree)) {
18
+ if (isRecord(entry) &&
19
+ entry.managedElsewhere === true) {
20
+ managed.add(name);
21
+ }
22
+ }
23
+ return managed;
24
+ }
25
+ function keptDirectives(capability, kept) {
26
+ if (Array.isArray(capability)) {
27
+ const left = capability.filter((directive) => kept.includes(directive));
28
+ return left.length > 0 ? left : undefined;
29
+ }
30
+ if (isRecord(capability) &&
31
+ Array.isArray(capability.directives)) {
32
+ const left = capability.directives.filter((directive) => kept.includes(directive));
33
+ return left.length > 0 ? { ...capability, directives: left } : undefined;
34
+ }
35
+ return capability;
36
+ }
37
+ function narrowWrites(field, kept) {
38
+ const capabilities = field.capabilities;
39
+ if (!isRecord(capabilities)) {
40
+ return;
41
+ }
42
+ for (const stage of ["create", "update"]) {
43
+ if (!Object.hasOwn(capabilities, stage)) {
44
+ continue;
45
+ }
46
+ const narrowed = keptDirectives(capabilities[stage], kept);
47
+ if (narrowed === undefined) {
48
+ delete capabilities[stage];
49
+ }
50
+ else {
51
+ capabilities[stage] = narrowed;
52
+ }
53
+ }
54
+ }
55
+ export function withholdManagedWrites(baseline, managed) {
56
+ if (managed.size === 0) {
57
+ return;
58
+ }
59
+ for (const [name, resource] of Object.entries(baseline.resources)) {
60
+ const isManaged = managed.has(name);
61
+ if (isManaged) {
62
+ for (const family of WRITE_FAMILIES) {
63
+ delete resource.operations[family];
64
+ }
65
+ }
66
+ for (const field of Object.values(resource.fields)) {
67
+ if (isManaged) {
68
+ narrowWrites(field, []);
69
+ continue;
70
+ }
71
+ if (field.kind !== "relation" || !managed.has(String(field.target))) {
72
+ continue;
73
+ }
74
+ narrowWrites(field, Array.isArray(field.referenceFields) && field.referenceFields.length > 0
75
+ ? SOURCE_ROW_DIRECTIVES
76
+ : []);
77
+ }
78
+ }
79
+ }
80
+ export function hideManagedElsewhere(view, managed) {
81
+ for (const name of managed) {
82
+ delete view.resources[name];
83
+ }
84
+ if (managed.size === 0) {
85
+ return;
86
+ }
87
+ for (const resource of Object.values(view.resources)) {
88
+ for (const [fieldName, field] of Object.entries(resource.fields)) {
89
+ if (field.kind === "relation" && managed.has(String(field.target))) {
90
+ delete resource.fields[fieldName];
91
+ }
92
+ }
93
+ }
94
+ }
95
+ function enumOf(field) {
96
+ const type = field.kind === "scalar" ? field.type : undefined;
97
+ return typeof type === "object" &&
98
+ type !== null &&
99
+ typeof type.enum === "string"
100
+ ? type.enum
101
+ : undefined;
102
+ }
103
+ function enumsUsed(resources) {
104
+ const used = new Set();
105
+ for (const resource of resources) {
106
+ for (const field of Object.values(resource.fields)) {
107
+ const name = enumOf(field);
108
+ if (name !== undefined) {
109
+ used.add(name);
110
+ }
111
+ }
112
+ }
113
+ return used;
114
+ }
115
+ export function pruneManagedEnums(view, unhidden, managed) {
116
+ const managedUse = enumsUsed([...managed].flatMap((name) => Object.hasOwn(unhidden.resources, name)
117
+ ? [unhidden.resources[name]]
118
+ : []));
119
+ if (managedUse.size === 0) {
120
+ return;
121
+ }
122
+ const served = enumsUsed(Object.values(view.resources));
123
+ for (const name of managedUse) {
124
+ if (!served.has(name)) {
125
+ delete view.enums[name];
126
+ }
127
+ }
128
+ }
129
+ export function markCoreResources(projection, model, managed) {
130
+ for (const [name, resource] of Object.entries(projection.resources)) {
131
+ const schema = Object.hasOwn(model.resources, name)
132
+ ? model.resources[name]?.schema
133
+ : undefined;
134
+ if (typeof schema === "string") {
135
+ resource.schema = schema;
136
+ }
137
+ if (managed.has(name)) {
138
+ resource.managedElsewhere = true;
139
+ }
140
+ }
141
+ }
142
+ export function managedWriteReason(managed, resource, family) {
143
+ return managed.has(resource) && WRITE_FAMILIES.includes(family)
144
+ ? `operation family "${family}" is not available for resource "${resource}": it is managed elsewhere (resources.${resource}.managedElsewhere), read and joined, never written`
145
+ : undefined;
146
+ }
147
+ export function explainManagedRefusals(diagnostics, from, layer, universe, managed) {
148
+ if (managed.size === 0) {
149
+ return;
150
+ }
151
+ const prefix = `${layer}.exemptions.resources.`;
152
+ for (let index = from; index < diagnostics.length; index += 1) {
153
+ const diagnostic = diagnostics[index];
154
+ if (diagnostic?.code !== "COMPILER_HIDDEN_REQUIRED_FIELD" ||
155
+ !diagnostic.path.startsWith(prefix)) {
156
+ continue;
157
+ }
158
+ const [resource, fieldsKey, field] = diagnostic.path
159
+ .slice(prefix.length)
160
+ .split(".");
161
+ const relation = resource !== undefined &&
162
+ fieldsKey === "fields" &&
163
+ field !== undefined &&
164
+ Object.hasOwn(universe.resources, resource)
165
+ ? universe.resources[resource]?.fields[field]
166
+ : undefined;
167
+ const target = String(relation?.target);
168
+ if (relation?.kind !== "relation" || !managed.has(target)) {
169
+ continue;
170
+ }
171
+ const references = Array.isArray(relation.referenceFields)
172
+ ? relation.referenceFields.map((name) => `"${String(name)}"`)
173
+ : [];
174
+ diagnostics[index] = {
175
+ ...diagnostic,
176
+ message: `${diagnostic.message}; "${field}" points at resource "${target}", which is managed elsewhere (resources.${target}.managedElsewhere): the ${layer} layer never writes it${references.length === 0 ? "" : `, so expose ${references.join(" and ")}`}`,
177
+ };
178
+ }
179
+ }
@@ -0,0 +1,9 @@
1
+ import type { AvConfigDiagnostic } from "../config/config-diagnostic.js";
2
+ import type { ReferentialActionTable } from "../runtime/referential-action.js";
3
+ import type { AvContracts } from "./contract.compiler.js";
4
+ /**
5
+ * One `CONFIG_INVALID_VALUE` per deletable Resource whose delete — directly, or
6
+ * through the cascades it sets off — does `cascade`, `setNull` or `setDefault`
7
+ * to the rows of a Resource managed elsewhere.
8
+ */
9
+ export declare function validateManagedDeletes(contracts: AvContracts, referentialActions: ReferentialActionTable | undefined): readonly AvConfigDiagnostic[];
@@ -0,0 +1,74 @@
1
+ import { advertisedDirectives } from "../contracts/field.js";
2
+ function deletable(contracts) {
3
+ const names = new Set();
4
+ for (const contract of contracts) {
5
+ for (const [name, resource] of Object.entries(contract.resources)) {
6
+ if (Object.hasOwn(resource.operations, "delete")) {
7
+ names.add(name);
8
+ }
9
+ for (const field of Object.values(resource.fields)) {
10
+ if (field.kind !== "relation") {
11
+ continue;
12
+ }
13
+ const { create, update } = field.capabilities;
14
+ if (advertisedDirectives(create)?.includes("$delete") === true ||
15
+ advertisedDirectives(update)?.includes("$delete") === true) {
16
+ names.add(field.target);
17
+ }
18
+ }
19
+ }
20
+ }
21
+ return names;
22
+ }
23
+ function hopLabel(resource, edge) {
24
+ return `${resource}.${edge.field} → ${edge.target}`;
25
+ }
26
+ export function validateManagedDeletes(contracts, referentialActions) {
27
+ const core = contracts.core;
28
+ const managed = new Set(Object.entries(core.resources)
29
+ .filter(([, resource]) => resource.managedElsewhere === true)
30
+ .map(([name]) => name));
31
+ if (managed.size === 0 || referentialActions === undefined) {
32
+ return [];
33
+ }
34
+ const firedBy = new Map();
35
+ for (const [resource, edges] of Object.entries(referentialActions)) {
36
+ for (const edge of edges) {
37
+ firedBy.set(edge.target, [
38
+ ...(firedBy.get(edge.target) ?? []),
39
+ { edge, from: resource },
40
+ ]);
41
+ }
42
+ }
43
+ const diagnostics = [];
44
+ const deleters = [
45
+ ...deletable([contracts.application, contracts.client, contracts.agent]),
46
+ ]
47
+ .filter((name) => !managed.has(name))
48
+ .sort();
49
+ for (const deleted of deleters) {
50
+ const reachedBy = new Map([[deleted, []]]);
51
+ const queue = [deleted];
52
+ for (let next = queue.shift(); next !== undefined; next = queue.shift()) {
53
+ const chain = reachedBy.get(next) ?? [];
54
+ for (const { edge, from } of firedBy.get(next) ?? []) {
55
+ const hop = [hopLabel(from, edge), ...chain];
56
+ if (managed.has(from)) {
57
+ if (edge.onDelete !== "restrict" && edge.onDelete !== "noAction") {
58
+ diagnostics.push({
59
+ code: "CONFIG_INVALID_VALUE",
60
+ path: `resources.${from}.managedElsewhere`,
61
+ message: `resources.${from}.managedElsewhere: deleting a "${deleted}" row does ${edge.onDelete} to "${from}" rows (${hop.join(", ")}), and another service owns "${from}"; take every delete of "${deleted}" away at the root (exemptions.resources.${deleted}.operations: ["delete"], and every $delete into it), or make the reference refuse the delete (onDelete: Restrict or NoAction)`,
62
+ });
63
+ }
64
+ continue;
65
+ }
66
+ if (edge.onDelete === "cascade" && !reachedBy.has(from)) {
67
+ reachedBy.set(from, hop);
68
+ queue.push(from);
69
+ }
70
+ }
71
+ }
72
+ }
73
+ return diagnostics;
74
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A read's cache stage, for `pipelines.cache` at the root or client layer, a
3
+ * Resource, the `find` family or one of its variants:
4
+ * `pipelines: { cache: cache({ for: "user", maxAge: 60 }) }`. The read is then
5
+ * also served as a GET a browser or a CDN may keep for `maxAge` seconds.
6
+ *
7
+ * Always write `for`, even for the default `"user"`: the audience is the
8
+ * promise this read makes, and it should be visible in the code.
9
+ */
10
+ export declare function cache(stage: {
11
+ /**
12
+ * Who may share one answer: `"user"` (the default; each caller's own
13
+ * browser keeps its own), `"anonymous"` (anonymous callers share one answer a
14
+ * CDN may keep; a signed-in caller's stays private) or `"everyone"` (one
15
+ * answer for every caller, which a CDN may hand to anyone). Always write it,
16
+ * even for `"user"`: the audience is the promise this read makes.
17
+ */
18
+ readonly for?: "user" | "anonymous" | "everyone";
19
+ /** How long an answer may be kept, in whole seconds (`max-age`). */
20
+ readonly maxAge: number;
21
+ }): {
22
+ readonly for: "user" | "anonymous" | "everyone";
23
+ readonly maxAge: number;
24
+ };
@@ -0,0 +1,3 @@
1
+ export function cache(stage) {
2
+ return Object.freeze({ for: stage.for ?? "user", maxAge: stage.maxAge });
3
+ }
@@ -0,0 +1,62 @@
1
+ import type { AvConfigAgentParent, AvConfigLayerName } from "./config-layer.js";
2
+ /** The fields one layer's write compute fills, per Resource and phase. */
3
+ type LayerComputedFields = Readonly<Record<string, {
4
+ /** Fields with a `compute.create` stage, sorted. */
5
+ readonly create?: readonly string[];
6
+ /** Fields with a `compute.update` stage, sorted. */
7
+ readonly update?: readonly string[];
8
+ }>>;
9
+ /**
10
+ * Which fields a configuration's write compute fills: per layer, the
11
+ * Resources, and per Resource the fields with a `compute.create` and a
12
+ * `compute.update` stage, each list sorted; and the layer the agent layer
13
+ * extends. Written by an adapter's generate step from the configuration's type,
14
+ * and compared with the configuration at startup.
15
+ */
16
+ export type AvConfigComputedFields = {
17
+ readonly agent: LayerComputedFields;
18
+ /** The agent layer's `extends`: `"client"` when the configuration leaves it out. */
19
+ readonly agentExtends: Exclude<AvConfigLayerName, "agent">;
20
+ readonly application: LayerComputedFields;
21
+ readonly client: LayerComputedFields;
22
+ readonly root: LayerComputedFields;
23
+ };
24
+ /** The layers whose compute may have filled a field when a stage of layer `L` runs. */
25
+ type ComputingLayers<Facts extends AvConfigComputedFields, L extends AvConfigLayerName> = L extends "root" ? AvConfigLayerName : L extends "agent" ? "root" | "agent" | Exclude<Facts["agentExtends"], "root"> : "root" | L | (Facts["agentExtends"] extends L ? "agent" : never);
26
+ /** `"<Resource>.<field>"` for every field a layer's `compute.create` fills. */
27
+ type CreateComputedOf<Layer> = Layer extends object ? {
28
+ [R in keyof Layer]: Layer[R] extends {
29
+ readonly create?: readonly (infer Field extends string)[];
30
+ } ? `${R & string}.${Field}` : never;
31
+ }[keyof Layer] : never;
32
+ /**
33
+ * The create fields a variant's guards, pipes and filters of layer `L` may be
34
+ * told without, as `"<Resource>.<field>"`: those a layer whose compute runs for
35
+ * the stage's origins fills on create. `string` (every field) when the model's
36
+ * type carries no statement of them.
37
+ */
38
+ export type AvPreComputeLift<M, L extends AvConfigLayerName> = M extends {
39
+ readonly "~computedFields"?: infer Facts;
40
+ } ? Exclude<Facts, undefined> extends infer Stated extends AvConfigComputedFields ? string extends keyof Stated["root"] ? string : CreateComputedOf<Stated[ComputingLayers<Stated, L>]> : string : string;
41
+ /** The configuration's layers' `resources` trees and the agent's parent. */
42
+ type ComputedFieldsSource = {
43
+ readonly trees: Readonly<Record<AvConfigLayerName, unknown>>;
44
+ readonly agentExtends: AvConfigAgentParent;
45
+ };
46
+ /** The computed fields of a configuration, in the form a generate step writes them. */
47
+ export declare function computedFieldsOf(source: ComputedFieldsSource): AvConfigComputedFields;
48
+ /**
49
+ * How the configuration's computed fields differ from the ones the generated
50
+ * types were written with, one sentence per difference; empty when they agree.
51
+ */
52
+ export declare function computedFieldsDrift(generated: AvConfigComputedFields, configured: AvConfigComputedFields): string[];
53
+ /** The warning's code, as `process.emitWarning` reports it. */
54
+ export declare const STALE_GENERATED_TYPES_WARNING = "AVENTARA_STALE_GENERATED_TYPES";
55
+ /**
56
+ * Warns, through `process.emitWarning` (code
57
+ * {@link STALE_GENERATED_TYPES_WARNING}), when the generated types' computed
58
+ * fields differ from the configuration's. Nothing to compare (an Adapter that
59
+ * carries none) warns nothing.
60
+ */
61
+ export declare function warnOnStaleComputedFields(generated: AvConfigComputedFields | undefined, configured: AvConfigComputedFields): void;
62
+ export {};
@@ -0,0 +1,73 @@
1
+ import { emitFrameworkWarning } from "../runtime/framework-warning.emitter.js";
2
+ import { computeOfLayer } from "./field-compute.resolver.js";
3
+ function layerComputedFields(resources) {
4
+ const result = {};
5
+ const compute = computeOfLayer(resources);
6
+ for (const resource of Object.keys(compute).sort()) {
7
+ const fields = compute[resource] ?? {};
8
+ const create = Object.keys(fields)
9
+ .filter((field) => fields[field]?.create !== undefined)
10
+ .sort();
11
+ const update = Object.keys(fields)
12
+ .filter((field) => fields[field]?.update !== undefined)
13
+ .sort();
14
+ if (create.length === 0 && update.length === 0) {
15
+ continue;
16
+ }
17
+ result[resource] = {
18
+ ...(create.length === 0 ? {} : { create }),
19
+ ...(update.length === 0 ? {} : { update }),
20
+ };
21
+ }
22
+ return result;
23
+ }
24
+ export function computedFieldsOf(source) {
25
+ return {
26
+ agent: layerComputedFields(source.trees.agent),
27
+ agentExtends: source.agentExtends,
28
+ application: layerComputedFields(source.trees.application),
29
+ client: layerComputedFields(source.trees.client),
30
+ root: layerComputedFields(source.trees.root),
31
+ };
32
+ }
33
+ const LAYERS = ["root", "application", "client", "agent"];
34
+ const PHASES = ["create", "update"];
35
+ function missingFrom(left, right) {
36
+ const missing = [];
37
+ for (const layer of LAYERS) {
38
+ for (const [resource, phases] of Object.entries(left[layer])) {
39
+ for (const phase of PHASES) {
40
+ for (const field of phases[phase] ?? []) {
41
+ if (!right[layer][resource]?.[phase]?.includes(field)) {
42
+ missing.push(`${layer} ${resource}.${field} (${phase})`);
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ return missing;
49
+ }
50
+ export function computedFieldsDrift(generated, configured) {
51
+ const drift = [
52
+ ...missingFrom(configured, generated).map((entry) => `computed in the configuration, not in the types: ${entry}`),
53
+ ...missingFrom(generated, configured).map((entry) => `computed in the types, not in the configuration: ${entry}`),
54
+ ];
55
+ if (generated.agentExtends !== configured.agentExtends) {
56
+ drift.push(`the agent layer extends "${configured.agentExtends}", the types say "${generated.agentExtends}"`);
57
+ }
58
+ return drift;
59
+ }
60
+ export const STALE_GENERATED_TYPES_WARNING = "AVENTARA_STALE_GENERATED_TYPES";
61
+ export function warnOnStaleComputedFields(generated, configured) {
62
+ if (generated === undefined) {
63
+ return;
64
+ }
65
+ const drift = computedFieldsDrift(generated, configured);
66
+ if (drift.length === 0) {
67
+ return;
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
70
+ .map((line) => ` - ${line}`)
71
+ .join("\n")}`;
72
+ emitFrameworkWarning(message, STALE_GENERATED_TYPES_WARNING);
73
+ }
@@ -0,0 +1,6 @@
1
+ import type { ErrorCodeDescription } from "../runtime/error-guidance.js";
2
+ import type { AvConfigDiagnosticCode } from "./config-diagnostic.js";
3
+ /** Meaning, cause and fix of every configuration diagnostic code. */
4
+ export declare const CONFIG_DIAGNOSTIC_GUIDANCE: {
5
+ readonly [C in AvConfigDiagnosticCode]: ErrorCodeDescription;
6
+ };
@@ -0,0 +1,32 @@
1
+ export const CONFIG_DIAGNOSTIC_GUIDANCE = {
2
+ CONFIG_REQUIRED: {
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, or policy.engine or policy.principal when policy is set.",
5
+ fix: "Add the key at the diagnostic's path.",
6
+ },
7
+ CONFIG_UNKNOWN_PROPERTY: {
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. `policy` and `prebuilt` are root-only keys; in an overlay they read as unknown.",
10
+ fix: 'Remove it, or move it where it belongs (the configuration reference\'s "Where a key can go").',
11
+ },
12
+ CONFIG_INVALID_TYPE: {
13
+ message: "A configuration key has the wrong type.",
14
+ cause: "A value of the wrong type, or a key set to an explicit undefined.",
15
+ fix: "Give the value the type the reference states; leave a key out rather than setting it to undefined.",
16
+ },
17
+ CONFIG_INVALID_VALUE: {
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, 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, or its unheld naming a Resource the data source's model does not have (the message names the Resource or the rule).",
20
+ fix: "Use a value the message allows.",
21
+ },
22
+ CONFIG_MOVED_PROPERTY: {
23
+ message: "A configuration key moved in rc.1.",
24
+ cause: "Configuration written for 1.0.0-rc.0: restrictions, entrypoint, fields, behaviors, transactions or pipelines.before/after.",
25
+ fix: "Move it where the message says: restrictions to exposed + exemptions, entrypoint to api, fields to `resources.<R>.fields.<f>.definition`, transactions to transactionsStyle, before/after to pipelines.hooks.",
26
+ },
27
+ CONFIG_FORBIDDEN_IN_OVERLAY: {
28
+ message: "A root-only key is written in an overlay.",
29
+ cause: "A field definition in application, client or agent.",
30
+ fix: "Define the field once at the root (`resources.<R>.fields.<f>.definition`); an overlay only chooses its visibility.",
31
+ },
32
+ };
@@ -1,6 +1,8 @@
1
1
  import type { AvAdapter } from "../adapters/adapter.interface.js";
2
2
  import type { AvAdapterModel } from "../adapters/adapter-model.js";
3
+ import type { AvContractClientAccess } from "../contracts/contract-access.js";
3
4
  import type { AvTransactionCapability } from "../contracts/transaction-capability.js";
5
+ import type { AvPolicyEngine } from "../policy/policy-engine.js";
4
6
  import type { AvDiagnosticsSink } from "../runtime/diagnostics-reporter.js";
5
7
  import type { AvMisuseSink } from "../runtime/misuse-reporter.js";
6
8
  import type { AvConfigAgentParent } from "./config-layer.js";
@@ -8,6 +10,21 @@ import { type ResolvedComputeConfig } from "./field-compute.resolver.js";
8
10
  import type { AvConfig, AvConfigLimits } from "./framework-config.js";
9
11
  import type { AvConfigFieldDefinition } from "./framework-field-definition.js";
10
12
  import { type ResolvedPipelineTree } from "./pipeline-tree.resolver.js";
13
+ /** The root's `policy`, its engine's passive lists read once. */
14
+ export type ResolvedPolicyConfig = {
15
+ /** Where the engine was configured, for the startup diagnostics' paths. */
16
+ readonly at: "policy.engine" | "access.policy";
17
+ readonly engine: AvPolicyEngine;
18
+ readonly principal: NonNullable<AvConfig["policy"]>["principal"];
19
+ /** The governed fields and relations, by Resource. */
20
+ readonly governed: Readonly<Record<string, readonly string[]>>;
21
+ /** The rule paths, by rule name, each a list of segments. */
22
+ readonly paths: Readonly<Record<string, readonly (readonly string[])[]>>;
23
+ /** The writes whose grants carry an `after` condition, by Resource. */
24
+ readonly afterChecks: Readonly<Record<string, readonly string[]>>;
25
+ /** The Resources the rules read in conditions and decide no read of. */
26
+ readonly unheld: readonly string[];
27
+ };
11
28
  /** The root's field definitions, by Resource and field name. */
12
29
  export type ResolvedFieldDefinitions = Readonly<Record<string, Readonly<Record<string, AvConfigFieldDefinition>>>>;
13
30
  /** One resolved layer of the configuration: root, application, client or agent. */
@@ -63,6 +80,13 @@ export type ResolvedFrameworkConfig<M extends AvAdapterModel = AvAdapterModel> =
63
80
  readonly diagnostics: AvDiagnosticsSink | undefined;
64
81
  /** The misuse sink, from the root configuration only. */
65
82
  readonly misuse: AvMisuseSink | undefined;
83
+ /** The policy, from the root configuration only; absent, nothing is checked. */
84
+ readonly policy: ResolvedPolicyConfig | undefined;
85
+ /**
86
+ * The ClientContract's `access` section: present exactly when `access` is
87
+ * configured, with `url` and `verbs` when its provider has `client`.
88
+ */
89
+ readonly access: AvContractClientAccess | undefined;
66
90
  /** Every field definition, from the root only; the same for all three layers. */
67
91
  readonly definitions: ResolvedFieldDefinitions;
68
92
  /** Each layer's `resources` tree as authored: the compiler checks its names. */
@@ -1,16 +1,11 @@
1
1
  import { AvConstructionError } from "../framework/framework-construction.error.js";
2
+ import { ACCESS_VERB_NAMES, providerVerb, } from "../policy/access-verb.table.js";
3
+ import { isPlainRecord } from "../runtime/record.guard.js";
2
4
  import { mountPathRefusalDiagnostic } from "./config.validator.js";
3
5
  import { normalizeEntrypoint } from "./entrypoint.normalizer.js";
4
6
  import { chainCompute, computeOfLayer, } from "./field-compute.resolver.js";
5
7
  import { resolvePipelineTree, } from "./pipeline-tree.resolver.js";
6
8
  const hasOwn = (value, key) => Object.hasOwn(value, key);
7
- function isPlainRecord(value) {
8
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
9
- return false;
10
- }
11
- const prototype = Object.getPrototypeOf(value);
12
- return prototype === Object.prototype || prototype === null;
13
- }
14
9
  function cloneConfigValue(value) {
15
10
  if (Array.isArray(value)) {
16
11
  return [...value];
@@ -140,6 +135,68 @@ function resolveMcp(mcp) {
140
135
  }),
141
136
  });
142
137
  }
138
+ function frozenLists(value) {
139
+ if (!isPlainRecord(value)) {
140
+ return Object.freeze({});
141
+ }
142
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, names]) => [
143
+ key,
144
+ Object.freeze([...names]),
145
+ ])));
146
+ }
147
+ function frozenPathLists(value) {
148
+ if (!isPlainRecord(value)) {
149
+ return Object.freeze({});
150
+ }
151
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, paths]) => [
152
+ key,
153
+ Object.freeze(paths.map((path) => Object.freeze([...path]))),
154
+ ])));
155
+ }
156
+ const accessPrincipal = ({ $state, }) => $state.me ?? null;
157
+ function resolveAccessSection(access) {
158
+ if (access === undefined) {
159
+ return undefined;
160
+ }
161
+ const client = access.client;
162
+ if (client === undefined) {
163
+ return {};
164
+ }
165
+ const verbs = {};
166
+ for (const name of ACCESS_VERB_NAMES) {
167
+ const verb = providerVerb(client.verbs, name);
168
+ if (verb !== undefined) {
169
+ const { method, path, args, result } = verb;
170
+ verbs[name] = {
171
+ method,
172
+ path,
173
+ ...(args === undefined ? {} : { args: args }),
174
+ result: result,
175
+ };
176
+ }
177
+ }
178
+ return {
179
+ url: client.url,
180
+ verbs: verbs,
181
+ };
182
+ }
183
+ function resolvePolicy(configured, access) {
184
+ const policy = access === undefined
185
+ ? configured
186
+ : { engine: access.policy, principal: accessPrincipal };
187
+ if (policy === undefined) {
188
+ return undefined;
189
+ }
190
+ return Object.freeze({
191
+ at: access === undefined ? "policy.engine" : "access.policy",
192
+ engine: policy.engine,
193
+ principal: policy.principal,
194
+ governed: frozenLists(policy.engine.governed),
195
+ paths: frozenPathLists(policy.engine.paths),
196
+ afterChecks: frozenLists(policy.engine.afterChecks),
197
+ unheld: Object.freeze(Array.isArray(policy.engine.unheld) ? [...policy.engine.unheld] : []),
198
+ });
199
+ }
143
200
  export function resolveFrameworkConfig(config) {
144
201
  const root = config;
145
202
  const application = config.application;
@@ -158,6 +215,8 @@ export function resolveFrameworkConfig(config) {
158
215
  adapter: config.adapter,
159
216
  diagnostics: config.diagnostics,
160
217
  misuse: config.misuse,
218
+ policy: resolvePolicy(config.policy, config.access),
219
+ access: resolveAccessSection(config.access),
161
220
  definitions: definitionsOf(root.resources),
162
221
  trees: {
163
222
  root: root.resources,