@astrale-os/kernel-dsl 1.0.0-beta.2 → 1.0.0-beta.20

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 (307) hide show
  1. package/dist/v1/addressing/class-key.js +3 -3
  2. package/dist/v1/addressing/definition-ref.js +21 -37
  3. package/dist/v1/addressing/domain-dependency.d.ts +0 -6
  4. package/dist/v1/addressing/domain-dependency.js +1 -6
  5. package/dist/v1/addressing/key.d.ts +4 -1
  6. package/dist/v1/addressing/key.js +23 -3
  7. package/dist/v1/addressing/ordering.d.ts +2 -0
  8. package/dist/v1/addressing/ordering.js +4 -0
  9. package/dist/v1/addressing/patterns.d.ts +1 -1
  10. package/dist/v1/addressing/patterns.js +2 -6
  11. package/dist/v1/builder/callable/authoring.d.ts +11 -5
  12. package/dist/v1/builder/callable/authoring.js +2 -0
  13. package/dist/v1/builder/class/authoring.d.ts +94 -224
  14. package/dist/v1/builder/class/authoring.js +31 -33
  15. package/dist/v1/builder/class/handle.d.ts +27 -0
  16. package/dist/v1/builder/class/handle.js +1 -0
  17. package/dist/v1/builder/class/input.d.ts +159 -0
  18. package/dist/v1/builder/class/projection.d.ts +129 -0
  19. package/dist/v1/builder/class/projection.js +1 -0
  20. package/dist/v1/builder/class/property.d.ts +3 -9
  21. package/dist/v1/builder/class/property.js +1 -0
  22. package/dist/v1/builder/core/authoring.d.ts +1 -1
  23. package/dist/v1/builder/index.d.ts +5 -2
  24. package/dist/v1/builder/lowering/callable.js +1 -0
  25. package/dist/v1/builder/lowering/class.d.ts +9 -1
  26. package/dist/v1/builder/lowering/class.js +62 -17
  27. package/dist/v1/builder/lowering/policy.js +2 -0
  28. package/dist/v1/builder/lowering/property-keys.d.ts +27 -0
  29. package/dist/v1/builder/lowering/property-keys.js +108 -0
  30. package/dist/v1/builder/lowering/schema/compile.js +11 -6
  31. package/dist/v1/builder/lowering/schema/context.d.ts +9 -1
  32. package/dist/v1/builder/lowering/schema/context.js +36 -4
  33. package/dist/v1/builder/lowering/schema/lifetime.d.ts +11 -0
  34. package/dist/v1/builder/lowering/schema/lifetime.js +55 -0
  35. package/dist/v1/builder/lowering/value/path.d.ts +1 -1
  36. package/dist/v1/builder/lowering/value/path.js +2 -4
  37. package/dist/v1/builder/lowering/value/zod.js +1 -3
  38. package/dist/v1/builder/policy/authoring.d.ts +85 -5
  39. package/dist/v1/builder/policy/authoring.js +107 -68
  40. package/dist/v1/builder/schema/define.d.ts +0 -1
  41. package/dist/v1/builder/schema/diagnostic.d.ts +3 -2
  42. package/dist/v1/builder/schema/diagnostic.js +5 -10
  43. package/dist/v1/builder/schema/input.d.ts +1 -1
  44. package/dist/v1/builder/schema/projection/callable.d.ts +7 -5
  45. package/dist/v1/builder/schema/projection/class.d.ts +118 -15
  46. package/dist/v1/builder/schema/projection/core-input.d.ts +2 -1
  47. package/dist/v1/builder/schema/projection/identity.d.ts +2 -1
  48. package/dist/v1/builder/schema/projection/model.d.ts +4 -2
  49. package/dist/v1/builder/schema/projection/property.d.ts +1 -1
  50. package/dist/v1/builder/schema/projection/resolved-class.d.ts +2 -1
  51. package/dist/v1/builder/schema/projection.d.ts +1 -1
  52. package/dist/v1/builder/value/full-match.d.ts +0 -1
  53. package/dist/v1/builder/value/full-match.js +0 -1
  54. package/dist/v1/builder/value/path-metadata.d.ts +1 -1
  55. package/dist/v1/builder/value/path.d.ts +1 -1
  56. package/dist/v1/bundle/codec.js +11 -13
  57. package/dist/v1/bundle/diagnostic.d.ts +2 -0
  58. package/dist/v1/bundle/diagnostic.js +9 -7
  59. package/dist/v1/bundle/model.js +1 -1
  60. package/dist/v1/bundle/structure.js +4 -6
  61. package/dist/v1/compiled/admission/accept.js +4 -10
  62. package/dist/v1/compiled/admission/closure.js +2 -2
  63. package/dist/v1/compiled/admission/diagnostic.d.ts +1 -1
  64. package/dist/v1/compiled/admission/diagnostic.js +1 -3
  65. package/dist/v1/compiled/admission/revision.d.ts +9 -1
  66. package/dist/v1/compiled/admission/revision.js +6 -3
  67. package/dist/v1/compiled/admission/semantics/class.d.ts +4 -1
  68. package/dist/v1/compiled/admission/semantics/class.js +37 -12
  69. package/dist/v1/compiled/admission/semantics/core.d.ts +1 -24
  70. package/dist/v1/compiled/admission/semantics/core.js +56 -24
  71. package/dist/v1/compiled/admission/semantics/edge.d.ts +2 -4
  72. package/dist/v1/compiled/admission/semantics/edge.js +6 -65
  73. package/dist/v1/compiled/admission/semantics/policy/callable.js +0 -2
  74. package/dist/v1/compiled/admission/semantics/policy/contracts.d.ts +5 -20
  75. package/dist/v1/compiled/admission/semantics/policy/contracts.js +8 -21
  76. package/dist/v1/compiled/admission/semantics/policy/parity.js +40 -43
  77. package/dist/v1/compiled/admission/semantics/policy/pattern.d.ts +4 -1
  78. package/dist/v1/compiled/admission/semantics/policy/pattern.js +4 -16
  79. package/dist/v1/compiled/admission/semantics/policy/typing.d.ts +2 -24
  80. package/dist/v1/compiled/admission/semantics/policy/typing.js +19 -201
  81. package/dist/v1/compiled/admission/wire/callable.js +11 -1
  82. package/dist/v1/compiled/admission/wire/class.js +73 -21
  83. package/dist/v1/compiled/admission/wire/policy.d.ts +4 -3
  84. package/dist/v1/compiled/admission/wire/policy.js +8 -7
  85. package/dist/v1/compiled/compiler/compile.js +8 -8
  86. package/dist/v1/compiled/diagnostic.js +2 -3
  87. package/dist/v1/compiled/loader/load.d.ts +3 -2
  88. package/dist/v1/compiled/loader/load.js +11 -45
  89. package/dist/v1/domain/create.js +5 -6
  90. package/dist/v1/domain/model/domain.d.ts +2 -7
  91. package/dist/v1/domain/model/input.d.ts +2 -1
  92. package/dist/v1/domain/namespace/member-collection.js +4 -6
  93. package/dist/v1/domain/projection/classes.js +4 -1
  94. package/dist/v1/domain/validation/create.d.ts +6 -3
  95. package/dist/v1/domain/validation/create.js +21 -69
  96. package/dist/v1/index.d.ts +2 -1
  97. package/dist/v1/language/.spec/schemas/draft-2020-12.schema.json +462 -0
  98. package/dist/v1/language/.spec/schemas/value-schema.schema.json +22 -0
  99. package/dist/v1/language/callable/admission.d.ts +15 -0
  100. package/dist/v1/language/callable/admission.js +43 -0
  101. package/dist/v1/language/callable/model/callable.d.ts +10 -0
  102. package/dist/v1/language/callable/resolved.d.ts +16 -3
  103. package/dist/v1/language/class/inheritance.d.ts +36 -5
  104. package/dist/v1/language/class/inheritance.js +87 -1
  105. package/dist/v1/language/class/lifetime.d.ts +50 -11
  106. package/dist/v1/language/class/lifetime.js +89 -20
  107. package/dist/v1/language/class/model/class.d.ts +45 -9
  108. package/dist/v1/language/class/model/edge.d.ts +31 -2
  109. package/dist/v1/language/class/model/property.d.ts +16 -7
  110. package/dist/v1/language/class/property-values.js +2 -4
  111. package/dist/v1/language/class/unique.d.ts +143 -0
  112. package/dist/v1/language/class/unique.js +264 -0
  113. package/dist/v1/language/core/facts.d.ts +85 -0
  114. package/dist/v1/language/core/facts.js +116 -0
  115. package/dist/v1/language/definition/metadata.d.ts +5 -0
  116. package/dist/v1/language/definition/metadata.js +7 -0
  117. package/dist/v1/language/freeze.d.ts +5 -0
  118. package/dist/v1/{schema/canonical → language}/freeze.js +4 -0
  119. package/dist/v1/language/index.d.ts +7 -2
  120. package/dist/v1/language/index.js +5 -1
  121. package/dist/v1/language/limits.d.ts +64 -0
  122. package/dist/v1/language/limits.js +60 -0
  123. package/dist/v1/language/policy/analysis/branches.d.ts +8 -0
  124. package/dist/v1/language/policy/analysis/branches.js +2 -1
  125. package/dist/v1/language/policy/analysis/budgets.d.ts +45 -12
  126. package/dist/v1/language/policy/analysis/budgets.js +133 -19
  127. package/dist/v1/language/policy/analysis/checks.js +3 -2
  128. package/dist/v1/language/policy/analysis/typing.d.ts +51 -0
  129. package/dist/v1/language/policy/analysis/typing.js +455 -0
  130. package/dist/v1/language/policy/model/policy.d.ts +49 -9
  131. package/dist/v1/language/policy/model/policy.js +14 -0
  132. package/dist/v1/language/policy/normalization/normalize.d.ts +6 -6
  133. package/dist/v1/language/policy/normalization/normalize.js +22 -21
  134. package/dist/v1/language/validation/diagnostic.d.ts +15 -1
  135. package/dist/v1/language/validation/diagnostic.js +21 -0
  136. package/dist/v1/language/validation/rule.d.ts +45 -0
  137. package/dist/v1/language/validation/rule.js +109 -0
  138. package/dist/v1/language/value/compile/fields.js +4 -8
  139. package/dist/v1/language/value/evaluate/program.d.ts +2 -1
  140. package/dist/v1/language/value/evaluate/program.js +2 -2
  141. package/dist/v1/language/value/evaluate/validate.d.ts +2 -2
  142. package/dist/v1/language/value/evaluate/validate.js +6 -2
  143. package/dist/v1/language/value/evaluate/validity.optimization.d.ts +14 -0
  144. package/dist/v1/language/value/evaluate/validity.optimization.js +342 -0
  145. package/dist/v1/language/value/inclusion/arrays.d.ts +3 -0
  146. package/dist/v1/language/value/inclusion/arrays.js +85 -0
  147. package/dist/v1/language/value/inclusion/assertions.d.ts +18 -0
  148. package/dist/v1/language/value/inclusion/assertions.js +29 -0
  149. package/dist/v1/language/value/inclusion/combinators.d.ts +3 -0
  150. package/dist/v1/language/value/inclusion/combinators.js +67 -0
  151. package/dist/v1/language/value/inclusion/equivalence.d.ts +4 -0
  152. package/dist/v1/language/value/inclusion/equivalence.js +74 -0
  153. package/dist/v1/language/value/inclusion/evidence.d.ts +34 -0
  154. package/dist/v1/language/value/inclusion/evidence.js +138 -0
  155. package/dist/v1/language/value/inclusion/includes.d.ts +17 -0
  156. package/dist/v1/language/value/inclusion/includes.js +25 -719
  157. package/dist/v1/language/value/inclusion/keywords.d.ts +5 -0
  158. package/dist/v1/language/value/inclusion/keywords.js +31 -0
  159. package/dist/v1/language/value/inclusion/kinds.d.ts +17 -0
  160. package/dist/v1/language/value/inclusion/kinds.js +57 -0
  161. package/dist/v1/language/value/inclusion/location.d.ts +24 -0
  162. package/dist/v1/language/value/inclusion/location.js +63 -0
  163. package/dist/v1/language/value/inclusion/numbers.d.ts +3 -0
  164. package/dist/v1/language/value/inclusion/numbers.js +41 -0
  165. package/dist/v1/language/value/inclusion/objects.d.ts +3 -0
  166. package/dist/v1/language/value/inclusion/objects.js +190 -0
  167. package/dist/v1/language/value/inclusion/strings.d.ts +3 -0
  168. package/dist/v1/language/value/inclusion/strings.js +29 -0
  169. package/dist/v1/language/value/inclusion/types.d.ts +3 -0
  170. package/dist/v1/language/value/inclusion/types.js +11 -0
  171. package/dist/v1/language/value/ordering.d.ts +14 -0
  172. package/dist/v1/language/value/ordering.js +86 -9
  173. package/dist/v1/language/value/profile/canonicalize.js +6 -5
  174. package/dist/v1/language/value/profile/json.validate.d.ts +3 -1
  175. package/dist/v1/language/value/profile/json.validate.js +3 -2
  176. package/dist/v1/language/value/profile/meta-schema.compiled.js +4 -1
  177. package/dist/v1/language/value/profile/rules.js +17 -43
  178. package/dist/v1/language/value/profile/validate.js +1 -10
  179. package/dist/v1/language/value/profile/vocabulary.d.ts +35 -2
  180. package/dist/v1/language/value/profile/vocabulary.js +52 -5
  181. package/dist/v1/language/value/reference/closure.js +3 -4
  182. package/dist/v1/language/value/reference/recursion.js +2 -22
  183. package/dist/v1/language/value/reference/registry.index.d.ts +2 -0
  184. package/dist/v1/language/value/reference/registry.index.js +33 -16
  185. package/dist/v1/language/value/regexp/ast.js +6 -7
  186. package/dist/v1/language/value/regexp/compile.d.ts +5 -11
  187. package/dist/v1/language/value/regexp/compile.js +14 -12
  188. package/dist/v1/language/value/regexp/syntax.js +3 -2
  189. package/dist/v1/language/value/syntax/keywords.d.ts +3 -3
  190. package/dist/v1/language/value/syntax/keywords.js +4 -20
  191. package/dist/v1/language/value/syntax/node-path/validate.js +2 -3
  192. package/dist/v1/language/value/syntax/pointer.d.ts +2 -0
  193. package/dist/v1/language/value/syntax/pointer.js +5 -2
  194. package/dist/v1/language/value/syntax/walk.d.ts +2 -0
  195. package/dist/v1/language/value/syntax/walk.js +19 -20
  196. package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +2519 -0
  197. package/dist/v1/schema/admission/accept.js +3 -14
  198. package/dist/v1/schema/admission/callable.d.ts +21 -0
  199. package/dist/v1/schema/admission/callable.js +96 -0
  200. package/dist/v1/schema/analysis/class.js +33 -15
  201. package/dist/v1/schema/canonical/revision.js +4 -4
  202. package/dist/v1/schema/compatibility/api.d.ts +2 -1
  203. package/dist/v1/schema/compatibility/api.js +1 -0
  204. package/dist/v1/schema/compatibility/comparators/callables.js +1 -1
  205. package/dist/v1/schema/compatibility/comparators/classes.d.ts +9 -4
  206. package/dist/v1/schema/compatibility/comparators/classes.js +29 -8
  207. package/dist/v1/schema/compatibility/comparators/index.d.ts +1 -1
  208. package/dist/v1/schema/compatibility/comparators/members.js +1 -1
  209. package/dist/v1/schema/compatibility/comparators/values.js +2 -2
  210. package/dist/v1/schema/compatibility/coordinate.js +3 -3
  211. package/dist/v1/schema/compatibility/diff.js +2 -4
  212. package/dist/v1/schema/compatibility/extension.d.ts +11 -0
  213. package/dist/v1/schema/compatibility/extension.js +22 -0
  214. package/dist/v1/schema/compatibility/fields.d.ts +20 -4
  215. package/dist/v1/schema/compatibility/fields.js +13 -3
  216. package/dist/v1/schema/compatibility/meaning/footprint.js +16 -50
  217. package/dist/v1/schema/compatibility/model.d.ts +17 -5
  218. package/dist/v1/schema/compatibility/report.js +1 -1
  219. package/dist/v1/schema/compatibility/scope.d.ts +2 -0
  220. package/dist/v1/schema/compatibility/scope.js +4 -1
  221. package/dist/v1/schema/compatibility/structure/index.js +6 -4
  222. package/dist/v1/schema/compatibility/structure/requirements.js +57 -58
  223. package/dist/v1/schema/compatibility/versioning/index.js +1 -1
  224. package/dist/v1/schema/compatibility/versioning/surface.d.ts +1 -1
  225. package/dist/v1/schema/compatibility/versioning/surface.js +30 -2
  226. package/dist/v1/schema/compatibility/walk.d.ts +17 -0
  227. package/dist/v1/schema/compatibility/walk.js +41 -0
  228. package/dist/v1/schema/diagnostic.d.ts +17 -3
  229. package/dist/v1/schema/diagnostic.js +14 -11
  230. package/dist/v1/schema/index.d.ts +10 -1
  231. package/dist/v1/schema/index.js +4 -0
  232. package/dist/v1/schema/model/schema.d.ts +20 -1
  233. package/dist/v1/schema/model/schema.js +1 -1
  234. package/dist/v1/schema/normalization/domain.d.ts +2 -0
  235. package/dist/v1/schema/normalization/domain.js +29 -9
  236. package/dist/v1/schema/resolution/closure.js +2 -1
  237. package/dist/v1/schema/resolution/context.js +13 -13
  238. package/dist/v1/schema/resolution/domain.d.ts +5 -0
  239. package/dist/v1/schema/resolution/domain.js +2 -2
  240. package/dist/v1/schema/resolution/origins.js +3 -2
  241. package/dist/v1/schema/rule.d.ts +26 -0
  242. package/dist/v1/schema/rule.js +440 -0
  243. package/dist/v1/schema/validation/callable.d.ts +35 -2
  244. package/dist/v1/schema/validation/callable.js +65 -9
  245. package/dist/v1/schema/validation/class.d.ts +6 -2
  246. package/dist/v1/schema/validation/class.js +127 -104
  247. package/dist/v1/schema/validation/core.js +90 -168
  248. package/dist/v1/schema/validation/domain.js +4 -13
  249. package/dist/v1/schema/validation/domain.json-schema.d.ts +15 -0
  250. package/dist/v1/schema/validation/domain.json-schema.js +197 -0
  251. package/dist/v1/schema/validation/domain.route.d.ts +6 -1
  252. package/dist/v1/schema/validation/domain.route.js +18 -0
  253. package/dist/v1/schema/validation/domain.structure.d.ts +7780 -0
  254. package/dist/v1/schema/validation/domain.structure.js +398 -147
  255. package/dist/v1/schema/validation/json.json-schema.d.ts +40 -0
  256. package/dist/v1/schema/validation/json.json-schema.js +46 -0
  257. package/dist/v1/schema/validation/policy/budgets.d.ts +33 -2
  258. package/dist/v1/schema/validation/policy/budgets.js +87 -121
  259. package/dist/v1/schema/validation/policy/context.d.ts +6 -5
  260. package/dist/v1/schema/validation/policy/context.js +7 -10
  261. package/dist/v1/schema/validation/policy/objects.d.ts +1 -1
  262. package/dist/v1/schema/validation/policy/objects.js +6 -14
  263. package/dist/v1/schema/validation/policy/references.js +11 -27
  264. package/dist/v1/schema/validation/policy/typing.d.ts +3 -18
  265. package/dist/v1/schema/validation/policy/typing.js +22 -356
  266. package/dist/v1/schema/validation/policy/uses.d.ts +40 -3
  267. package/dist/v1/schema/validation/policy/uses.js +66 -89
  268. package/dist/v1/schema/validation/policy/validate.js +5 -5
  269. package/dist/v1/schema/validation/value-api.d.ts +22 -0
  270. package/dist/v1/schema/validation/value-api.js +34 -1
  271. package/dist/v1/schema/validation/value.d.ts +3 -0
  272. package/dist/v1/schema/validation/value.js +9 -17
  273. package/dist/v1/schema/validation/value.json-schema.d.ts +49 -0
  274. package/dist/v1/schema/validation/value.json-schema.js +137 -0
  275. package/dist/v1/schema/validation/view.js +4 -8
  276. package/dist/value/canonical.d.ts +6 -0
  277. package/dist/value/canonical.js +36 -0
  278. package/dist/value/decode.d.ts +7 -0
  279. package/dist/value/decode.js +159 -9
  280. package/dist/value/index.d.ts +1 -1
  281. package/dist/value/json.d.ts +2 -2
  282. package/dist/value/json.js +2 -2
  283. package/dist/value/types.d.ts +6 -0
  284. package/dist/value/unicode.d.ts +2 -1
  285. package/dist/value/unicode.js +2 -0
  286. package/package.json +8 -1
  287. package/dist/v1/builder/lowering/core-properties.d.ts +0 -10
  288. package/dist/v1/builder/lowering/core-properties.js +0 -76
  289. package/dist/v1/compiled/admission/semantics/cardinality.d.ts +0 -5
  290. package/dist/v1/compiled/admission/semantics/cardinality.js +0 -113
  291. package/dist/v1/compiled/admission/semantics/policy/equality.d.ts +0 -8
  292. package/dist/v1/compiled/admission/semantics/policy/equality.js +0 -27
  293. package/dist/v1/compiled/admission/semantics/policy/measure.d.ts +0 -9
  294. package/dist/v1/compiled/admission/semantics/policy/measure.js +0 -81
  295. package/dist/v1/compiled/admission/semantics/policy/variants.d.ts +0 -10
  296. package/dist/v1/compiled/admission/semantics/policy/variants.js +0 -65
  297. package/dist/v1/language/class/index.d.ts +0 -8
  298. package/dist/v1/language/class/index.js +0 -1
  299. package/dist/v1/language/core/index.d.ts +0 -2
  300. package/dist/v1/language/value/index.d.ts +0 -11
  301. package/dist/v1/language/value/index.js +0 -8
  302. package/dist/v1/schema/canonical/domain.d.ts +0 -3
  303. package/dist/v1/schema/canonical/domain.digest.d.ts +0 -3
  304. package/dist/v1/schema/canonical/domain.digest.js +0 -5
  305. package/dist/v1/schema/canonical/domain.js +0 -5
  306. package/dist/v1/schema/canonical/freeze.d.ts +0 -1
  307. /package/dist/v1/{language/core/index.js → builder/class/input.js} +0 -0
@@ -1,48 +1,63 @@
1
+ import { equalDefinitionRefs } from '../../addressing/definition-ref.js';
1
2
  import { Key } from '../../addressing/key.js';
2
3
  import { definitionRefIndex } from '../../addressing/ordering.js';
3
4
  import { isMethodExecutable } from '../../language/callable/executable.js';
4
- import { classSatisfies as satisfiesClass, isGraphRoot } from '../../language/class/inheritance.js';
5
+ import { classSatisfies as satisfiesClass, edgeContractRefines as refinesEdgeContract, } from '../../language/class/inheritance.js';
6
+ import { lifetimeBreaches, lifetimeScope } from '../../language/class/lifetime.js';
7
+ import { keyableProperty } from '../../language/class/unique.js';
5
8
  import { admitStateTopology } from '../../language/state/topology.js';
6
9
  import { isBlobValueSchema } from '../../language/value/model/blob.js';
7
10
  import { classifyOrdering } from '../../language/value/ordering.js';
11
+ import { escapePointerToken } from '../../language/value/syntax/pointer.js';
8
12
  import { diagnostic } from '../diagnostic.js';
9
- import { createValueSchemaRegistry } from '../resolution/value.registry.js';
13
+ import { createDomainValueSchemaRegistry } from '../resolution/value.registry.js';
10
14
  const formatDefinitionRef = Key.of;
11
- /** Validate the semantic contracts owned by Class declarations. */
12
- export function validateDefinitions(environment, analysis) {
15
+ /**
16
+ * Validate the semantic contracts owned by Class declarations. `registry` is the candidate's value
17
+ * registry; each dependency's is built at most once, so the Classes share them.
18
+ */
19
+ export function validateDefinitions(environment, analysis, registry) {
13
20
  const diagnostics = [...analysis.diagnostics];
14
21
  const schema = environment.candidate;
22
+ let scope;
23
+ const registries = new Map([[schema.origin, registry]]);
24
+ const registryOf = (origin) => {
25
+ if (!registries.has(origin)) {
26
+ const dependency = environment.dependencies.get(origin);
27
+ registries.set(origin, dependency === undefined ? undefined : createDomainValueSchemaRegistry(dependency));
28
+ }
29
+ return registries.get(origin);
30
+ };
15
31
  for (const [name, declaration] of Object.entries(schema.classes)) {
16
- const ref = localRef(schema.origin, name);
17
- const pointer = `/classes/${escapePointer(name)}`;
18
- if (!sameRef(declaration.ref, ref)) {
32
+ const ref = { origin: schema.origin, kind: 'class', name };
33
+ const pointer = `/classes/${escapePointerToken(name)}`;
34
+ if (!equalDefinitionRefs(declaration.ref, ref)) {
19
35
  diagnostics.push(diagnostic('DV_REF_RESOLVES', `${pointer}/ref`, `Class ref must be exactly ${formatDefinitionRef(ref)}.`, formatDefinitionRef(declaration.ref)));
20
36
  }
21
- if (declaration.abstract &&
22
- (declaration.policies.read !== undefined || declaration.policies.traverse !== undefined)) {
23
- diagnostics.push(diagnostic('DV_CLASS_ABSTRACT', `${pointer}/policies`, 'Abstract Classes cannot declare observation Policies.'));
37
+ if (declaration.abstract && Object.keys(declaration.policies).length > 0) {
38
+ diagnostics.push(diagnostic('DV_CLASS_ABSTRACT', `${pointer}/policies`, 'Abstract Classes cannot declare Class Policies.'));
24
39
  }
25
- validateFacet(environment, declaration, pointer, diagnostics);
40
+ validateFacet(registry, declaration, pointer, diagnostics);
26
41
  validateParents(environment, ref, declaration.kind, declaration.extends, diagnostics);
27
42
  if (declaration.kind === 'edge') {
28
- validateEdgeContract(environment, ref, declaration, diagnostics);
43
+ validateEdgeContract(environment, ref, declaration, pointer, diagnostics);
44
+ scope ??= lifetimeScope([...environment.definitions.values()].map(({ declaration }) => declaration));
45
+ validateEdgeLifetimes(scope, ref, declaration, pointer, diagnostics);
46
+ validateEdgeClassComposition(environment, ref, declaration, pointer, diagnostics);
29
47
  }
48
+ validateUniqueProperties(registryOf, analysis, ref, declaration, pointer, diagnostics);
30
49
  }
31
- validateEdgeClassComposition(environment, diagnostics);
32
50
  return Object.freeze(diagnostics);
33
51
  }
34
- function validateFacet(environment, declaration, pointer, diagnostics) {
35
- const valueRegistry = createValueSchemaRegistry(environment);
52
+ function validateFacet(valueRegistry, declaration, pointer, diagnostics) {
36
53
  for (const [name, property] of Object.entries(declaration.properties)) {
37
- const propertyPointer = `${pointer}/properties/${escapePointer(name)}`;
54
+ const propertyPointer = `${pointer}/properties/${escapePointerToken(name)}`;
55
+ // The structure refuses a blob on an Edge Property and a State Property on an Edge Class.
38
56
  if (isBlobValueSchema(property.schema) &&
39
- (declaration.kind !== 'node' || property.auto !== undefined || property.state !== undefined)) {
57
+ (property.auto !== undefined || property.state !== undefined)) {
40
58
  diagnostics.push(diagnostic('DV_VALUE_SCHEMA', `${propertyPointer}/schema/x-astrale-value-kind`, 'Blob values are allowed only on non-automatic, non-state Node properties.'));
41
59
  }
42
60
  if (property.state !== undefined) {
43
- if (declaration.kind !== 'node') {
44
- diagnostics.push(diagnostic('DV_PROPERTY_STATE', `${propertyPointer}/state`, 'A State Property may be declared only on a Node Class.'));
45
- }
46
61
  try {
47
62
  const topology = admitStateTopology(property.state);
48
63
  const expected = Object.keys(topology.transitions).sort();
@@ -86,16 +101,22 @@ function validateFacet(environment, declaration, pointer, diagnostics) {
86
101
  if (declaration.kind !== 'node')
87
102
  return;
88
103
  for (const [name, method] of Object.entries(declaration.methods)) {
89
- const methodPointer = `${pointer}/methods/${escapePointer(name)}`;
90
- if (!isMethodExecutable(declaration, method) && method.policy !== undefined) {
104
+ const methodPointer = `${pointer}/methods/${escapePointerToken(name)}`;
105
+ if (isMethodExecutable(declaration, method))
106
+ continue;
107
+ if (method.policy !== undefined) {
91
108
  diagnostics.push(diagnostic('DV_METHOD_ABSTRACT', `${methodPointer}/policy`, 'Abstract Methods cannot have a Policy.'));
92
109
  }
110
+ // An admission age qualifies the admission of an executable declaration, like its Policy.
111
+ if (method.admission !== undefined) {
112
+ diagnostics.push(diagnostic('DV_METHOD_ABSTRACT', `${methodPointer}/admission`, 'Abstract Methods cannot have an admission age.'));
113
+ }
93
114
  }
94
115
  }
95
116
  function validateParents(environment, owner, ownerKind, parents, diagnostics) {
96
117
  for (const [index, parent] of parents.entries()) {
97
118
  const target = environment.resolve(parent);
98
- const pointer = `/classes/${escapePointer(owner.name)}/extends/${index}`;
119
+ const pointer = `/classes/${escapePointerToken(owner.name)}/extends/${index}`;
99
120
  if (target === undefined) {
100
121
  diagnostics.push(diagnostic('DV_REF_RESOLVES', pointer, `Parent ${formatDefinitionRef(parent)} does not resolve exactly.`, formatDefinitionRef(parent)));
101
122
  continue;
@@ -103,105 +124,116 @@ function validateParents(environment, owner, ownerKind, parents, diagnostics) {
103
124
  if (target.ref.kind !== 'class' || target.declaration.kind !== ownerKind) {
104
125
  diagnostics.push(diagnostic('DV_CLASS_KIND', pointer, `${formatDefinitionRef(owner)} cannot extend different-kind Class ${formatDefinitionRef(parent)}.`, formatDefinitionRef(parent)));
105
126
  }
127
+ else if (target.declaration.inheritance === 'final') {
128
+ diagnostics.push(diagnostic('DV_CLASS_INHERITANCE', pointer, `${formatDefinitionRef(owner)} cannot extend final Class ${formatDefinitionRef(parent)}.`, formatDefinitionRef(parent)));
129
+ }
106
130
  }
107
131
  }
108
- function validateEdgeContract(environment, owner, declaration, diagnostics) {
109
- const endpoints = declaration.orientation === 'directed'
132
+ function validateEdgeContract(environment, owner, declaration, pointer, diagnostics) {
133
+ const directed = declaration.orientation === 'directed';
134
+ const endpoints = directed
110
135
  ? [declaration.endpoints.source, declaration.endpoints.target]
111
136
  : declaration.endpoints;
137
+ const keys = directed ? ['source', 'target'] : ['0', '1'];
112
138
  if (endpoints[0].role === endpoints[1].role) {
113
- diagnostics.push(diagnostic('DV_EDGE_ROLE', '', `${formatDefinitionRef(owner)} has equal endpoint roles.`));
139
+ diagnostics.push(diagnostic('DV_EDGE_ROLE', `${pointer}/endpoints/${keys[1]}/role`, `${formatDefinitionRef(owner)} has equal endpoint roles.`));
114
140
  }
115
- const counts = declaration.orientation === 'directed'
116
- ? [declaration.endpoints.source.outgoing, declaration.endpoints.target.incoming]
117
- : [declaration.endpoints[0].incident, declaration.endpoints[1].incident];
118
- validateEndpoint(environment, owner, endpoints[0], counts[0], declaration.orientation === 'directed' ? 'source' : 'first endpoint', diagnostics);
119
- validateEndpoint(environment, owner, endpoints[1], counts[1], declaration.orientation === 'directed' ? 'target' : 'second endpoint', diagnostics);
120
- if (declaration.orientation === 'undirected') {
121
- if (declaration.constraints.acyclic === true) {
122
- diagnostics.push(diagnostic('DV_EDGE_CONSTRAINT', '', 'acyclic is valid only for directed Edge contracts.'));
141
+ const counts = directed
142
+ ? [
143
+ ['outgoing', declaration.endpoints.source.outgoing],
144
+ ['incoming', declaration.endpoints.target.incoming],
145
+ ]
146
+ : [
147
+ ['incident', declaration.endpoints[0].incident],
148
+ ['incident', declaration.endpoints[1].incident],
149
+ ];
150
+ validateEndpoint(environment, owner, endpoints[0], counts[0], directed ? 'source' : 'first endpoint', `${pointer}/endpoints/${keys[0]}`, diagnostics);
151
+ validateEndpoint(environment, owner, endpoints[1], counts[1], directed ? 'target' : 'second endpoint', `${pointer}/endpoints/${keys[1]}`, diagnostics);
152
+ }
153
+ /**
154
+ * Each Property a key tuple names is one of the Class's own or inherited Properties, and keyable:
155
+ * a single-kind scalar by its ordering facet that no automatic program writes (D-S1). Properties
156
+ * inherit without redefinition, so the key reads one meaning on every descendant it covers. A Node
157
+ * tuple names Properties only; an Edge tuple names them as `{ property }` beside its other fields.
158
+ */
159
+ function validateUniqueProperties(registryOf, analysis, owner, declaration, pointer, diagnostics) {
160
+ if (declaration.kind === 'edge' && declaration.orientation !== 'directed')
161
+ return;
162
+ if (declaration.unique === undefined)
163
+ return;
164
+ const properties = analysis.definitions.get(definitionRefIndex(owner))?.properties;
165
+ if (properties === undefined)
166
+ return;
167
+ const code = declaration.kind === 'node' ? 'DV_NODE_UNIQUE' : 'DV_EDGE_UNIQUE';
168
+ const check = (key, at) => {
169
+ const property = properties.get(key);
170
+ if (property === undefined) {
171
+ diagnostics.push(diagnostic(code, at, `${key} is not a Property of the Class.`));
172
+ return;
123
173
  }
174
+ const registry = registryOf(property.owner.origin);
175
+ const ordering = registry === undefined ? undefined : classifyOrdering(property.schema, registry);
176
+ if (!keyableProperty(ordering, property.auto)) {
177
+ diagnostics.push(diagnostic(code, at, 'A key Property is a string, number, boolean or instant that no automatic program writes.'));
178
+ }
179
+ };
180
+ if (declaration.kind === 'node') {
181
+ declaration.unique.forEach((tuple, t) => tuple.forEach((key, f) => check(key, `${pointer}/unique/${t}/${f}`)));
182
+ return;
124
183
  }
184
+ declaration.unique.forEach((tuple, t) => tuple.forEach((field, f) => {
185
+ if (typeof field !== 'string')
186
+ check(field.property, `${pointer}/unique/${t}/${f}/property`);
187
+ }));
125
188
  }
126
- function validateEndpoint(environment, owner, endpoint, count, side, diagnostics) {
189
+ // The structure refuses a repeated accepted Class before semantics: `accepts` is a set.
190
+ function validateEndpoint(environment, owner, endpoint, [countField, count], side, pointer, diagnostics) {
127
191
  // An Edge Class binds only the Nodes of its Domain and its dependents: it cannot oblige the
128
192
  // Nodes of a Class another Domain owns to carry its Edges.
129
193
  const foreign = endpoint.accepts.find((ref) => ref.origin !== owner.origin);
130
194
  if (foreign !== undefined && (count === '1' || count === '1..*')) {
131
- diagnostics.push(diagnostic('DV_EDGE_FOREIGN_MINIMUM', '', `${formatDefinitionRef(owner)} ${side} accepts ${formatDefinitionRef(foreign)} of another Domain and cannot require ${count} Edges.`));
195
+ diagnostics.push(diagnostic('DV_EDGE_FOREIGN_MINIMUM', `${pointer}/${countField}`, `${formatDefinitionRef(owner)} ${side} accepts ${formatDefinitionRef(foreign)} of another Domain and cannot require ${count} Edges.`));
132
196
  }
133
- const seen = new Set();
134
- for (const ref of endpoint.accepts) {
135
- const key = definitionRefIndex(ref);
136
- if (seen.has(key)) {
137
- diagnostics.push(diagnostic('DV_EDGE_ACCEPTS', '', `${formatDefinitionRef(owner)} ${side} repeats ${formatDefinitionRef(ref)}.`));
138
- }
139
- seen.add(key);
197
+ for (const [index, ref] of endpoint.accepts.entries()) {
140
198
  const resolved = environment.resolve(ref);
141
199
  if (resolved === undefined ||
142
200
  resolved.ref.kind !== 'class' ||
143
201
  resolved.declaration.kind !== 'node') {
144
- diagnostics.push(diagnostic('DV_EDGE_ACCEPTS', '', `${formatDefinitionRef(owner)} ${side} ref ${formatDefinitionRef(ref)} does not resolve to a Node Class.`));
202
+ diagnostics.push(diagnostic('DV_EDGE_ACCEPTS', `${pointer}/accepts/${index}`, `${formatDefinitionRef(owner)} ${side} ref ${formatDefinitionRef(ref)} does not resolve to a Node Class.`));
145
203
  }
146
204
  }
147
205
  }
148
- /** Edge inheritance is refinement, not a second interface composition mechanism. */
149
- function validateEdgeClassComposition(environment, diagnostics) {
150
- for (const resolved of environment.definitions.values()) {
151
- if (resolved.ref.kind !== 'class' || resolved.declaration.kind !== 'edge')
206
+ /**
207
+ * The lifetime laws over the lifetimes a Schema declares. What an endpoint derives where it
208
+ * declares nothing is never refused here: a stored Registry decoded again keeps it.
209
+ */
210
+ function validateEdgeLifetimes(scope, owner, declaration, pointer, diagnostics) {
211
+ const sides = declaration.orientation === 'directed'
212
+ ? ['source', 'target']
213
+ : ['first endpoint', 'second endpoint'];
214
+ const keys = declaration.orientation === 'directed' ? ['source', 'target'] : ['0', '1'];
215
+ for (const { index, reason } of lifetimeBreaches(declaration, scope)) {
216
+ diagnostics.push(diagnostic('DV_EDGE_LIFETIME', `${pointer}/endpoints/${keys[index]}/onDelete`, `${formatDefinitionRef(owner)} ${sides[index]} ${reason}`));
217
+ }
218
+ }
219
+ /**
220
+ * Edge inheritance is refinement, not a second interface composition mechanism. A dependency
221
+ * Edge Class was judged against this same exact closure when it was accepted.
222
+ */
223
+ function validateEdgeClassComposition(environment, owner, child, pointer, diagnostics) {
224
+ for (const [index, parentRef] of child.extends.entries()) {
225
+ const parent = environment.resolveAny(parentRef);
226
+ if (parent === undefined || parent.ref.kind !== 'class' || parent.declaration.kind !== 'edge') {
152
227
  continue;
153
- const child = resolved.declaration;
154
- for (const parentRef of child.extends) {
155
- const parent = environment.resolveAny(parentRef);
156
- if (parent === undefined ||
157
- parent.ref.kind !== 'class' ||
158
- parent.declaration.kind !== 'edge') {
159
- continue;
160
- }
161
- if (!edgeContractRefines(environment, child, parent.declaration)) {
162
- diagnostics.push(diagnostic('DV_EDGE_CLASS_COMPOSITION', '', `${formatDefinitionRef(resolved.ref)} does not safely refine ${formatDefinitionRef(parentRef)}.`));
163
- }
228
+ }
229
+ if (!edgeContractRefines(environment, child, parent.declaration)) {
230
+ diagnostics.push(diagnostic('DV_EDGE_CLASS_COMPOSITION', `${pointer}/extends/${index}`, `${formatDefinitionRef(owner)} does not safely refine ${formatDefinitionRef(parentRef)}.`));
164
231
  }
165
232
  }
166
233
  }
167
234
  /** Whether `child` safely refines its parent Edge contract `parent`. */
168
235
  export function edgeContractRefines(environment, child, parent) {
169
- if (isGraphRoot(parent))
170
- return true;
171
- if (child.orientation !== parent.orientation)
172
- return false;
173
- if (parent.constraints.noSelf === true && child.constraints.noSelf !== true)
174
- return false;
175
- if (parent.constraints.acyclic === true && child.constraints.acyclic !== true)
176
- return false;
177
- if (child.orientation === 'directed' && parent.orientation === 'directed') {
178
- return (endpointRefines(environment, child.endpoints.source, parent.endpoints.source) &&
179
- countRefines(child.endpoints.source.outgoing, parent.endpoints.source.outgoing) &&
180
- endpointRefines(environment, child.endpoints.target, parent.endpoints.target) &&
181
- countRefines(child.endpoints.target.incoming, parent.endpoints.target.incoming));
182
- }
183
- if (child.orientation !== 'undirected' || parent.orientation !== 'undirected')
184
- return false;
185
- const refines = (childIndex, parentIndex) => endpointRefines(environment, child.endpoints[childIndex], parent.endpoints[parentIndex]) &&
186
- countRefines(child.endpoints[childIndex].incident, parent.endpoints[parentIndex].incident);
187
- const direct = refines(0, 0) && refines(1, 1);
188
- if (direct)
189
- return true;
190
- return refines(0, 1) && refines(1, 0);
191
- }
192
- function endpointRefines(environment, child, parent) {
193
- return child.accepts.every((accepted) => classSatisfies(environment, accepted, parent.accepts));
194
- }
195
- function countRefines(child, parent) {
196
- const bounds = {
197
- '0..*': [0, Number.POSITIVE_INFINITY],
198
- '1..*': [1, Number.POSITIVE_INFINITY],
199
- '0..1': [0, 1],
200
- '1': [1, 1],
201
- };
202
- const [childMinimum, childMaximum] = bounds[child];
203
- const [parentMinimum, parentMaximum] = bounds[parent];
204
- return childMinimum >= parentMinimum && childMaximum <= parentMaximum;
236
+ return refinesEdgeContract(child, parent, (candidate, accepts) => classSatisfies(environment, candidate, accepts));
205
237
  }
206
238
  /** Whether `candidate` is, or is a Node Class extending, one of `accepts`. */
207
239
  export function classSatisfies(environment, candidate, accepts) {
@@ -216,12 +248,3 @@ export function classSatisfies(environment, candidate, accepts) {
216
248
  return required !== undefined && satisfiesClass(actual, required, resolve);
217
249
  });
218
250
  }
219
- function localRef(origin, name) {
220
- return { origin, kind: 'class', name };
221
- }
222
- function sameRef(left, right) {
223
- return left.origin === right.origin && left.kind === right.kind && left.name === right.name;
224
- }
225
- function escapePointer(value) {
226
- return value.replaceAll('~', '~0').replaceAll('/', '~1');
227
- }
@@ -1,26 +1,44 @@
1
+ import { equalDefinitionRefs } from '../../addressing/definition-ref.js';
1
2
  import { Key } from '../../addressing/key.js';
2
3
  import { compareUnicode, definitionRefIndex } from '../../addressing/ordering.js';
3
4
  import { isMethodExecutable } from '../../language/callable/executable.js';
5
+ import { coveringEdgeUniqueTuples, coveringNodeUniqueTuples, edgeUniqueKeys, nodeUniqueKeys, } from '../../language/class/unique.js';
6
+ import { checkCoreEdgeSet, checkHeldKeys, coreEndpointsSatisfy, DOMAIN_ENDPOINT_CLASS, } from '../../language/core/facts.js';
4
7
  import { policyObjectKey, policyObjectOrigin, projectedMemberClass, } from '../../language/policy/model/policy.js';
5
8
  import { validateJsonInstance } from '../../language/value/evaluate/validate.js';
6
- import { diagnostic } from '../diagnostic.js';
9
+ import { escapePointerToken } from '../../language/value/syntax/pointer.js';
10
+ import { diagnostic, valueDiagnostic } from '../diagnostic.js';
7
11
  import { classSatisfies } from './class.js';
8
12
  const formatDefinitionRef = Key.of;
9
13
  export function validateCore(environment, analysis, registry) {
10
14
  const diagnostics = [];
11
15
  const nodesByRef = resolveCoreNodes(environment, analysis, registry, diagnostics);
12
16
  const edges = resolveCoreEdges(environment, analysis, registry, nodesByRef, diagnostics);
17
+ validateCoreNodeKeys(environment, nodesByRef, diagnostics);
13
18
  validateCoreEdgeSet(environment, edges, nodesByRef, diagnostics);
14
19
  return Object.freeze(diagnostics);
15
20
  }
21
+ /** No two Core Nodes hold one key under a tuple covering their Classes, whichever declares it. */
22
+ function validateCoreNodeKeys(environment, nodesByRef, diagnostics) {
23
+ const resolve = (ref) => environment.resolveAny(ref)?.declaration;
24
+ checkHeldKeys(nodesByRef.values(), (node) => {
25
+ const declaration = resolve(node.classRef);
26
+ if (declaration === undefined)
27
+ return [];
28
+ return nodeUniqueKeys(coveringNodeUniqueTuples(declaration, resolve), {
29
+ props: environment.candidate.core.nodes[node.slug].properties,
30
+ });
31
+ }, (node, previous) => diagnostics.push(diagnostic('DV_CORE_NODE_UNIQUE', nodePointer(node), `Core Node holds a key the Core Node at ${nodePointer(previous)} holds.`)));
32
+ }
33
+ function nodePointer(node) {
34
+ return `/core/nodes/${escapePointerToken(node.slug)}`;
35
+ }
16
36
  function resolveCoreNodes(environment, analysis, registry, diagnostics) {
17
37
  const nodes = new Map();
18
38
  for (const [slug, node] of Object.entries(environment.candidate.core.nodes)) {
19
- const pointer = `/core/nodes/${escapePointer(slug)}`;
39
+ const pointer = `/core/nodes/${escapePointerToken(slug)}`;
20
40
  const ref = { origin: environment.candidate.origin, kind: 'core', name: slug };
21
- if (node.ref.origin !== ref.origin ||
22
- node.ref.kind !== ref.kind ||
23
- node.ref.name !== ref.name) {
41
+ if (!equalDefinitionRefs(node.ref, ref)) {
24
42
  diagnostics.push(diagnostic('DV_REF_RESOLVES', `${pointer}/ref`, `Core ref must be exactly ${formatDefinitionRef(ref)}.`, formatDefinitionRef(node.ref)));
25
43
  }
26
44
  const resolved = environment.resolve(node.class);
@@ -63,15 +81,11 @@ function resolveCoreEdges(environment, analysis, registry, nodesByRef, diagnosti
63
81
  const declaration = resolved.declaration;
64
82
  if (source === undefined || target === undefined)
65
83
  continue;
66
- const endpointsSatisfy = declaration.orientation === 'directed'
67
- ? endpointSatisfies(environment, source, declaration.endpoints.source.accepts) &&
68
- endpointSatisfies(environment, target, declaration.endpoints.target.accepts)
69
- : (endpointSatisfies(environment, source, declaration.endpoints[0].accepts) &&
70
- endpointSatisfies(environment, target, declaration.endpoints[1].accepts)) ||
71
- (endpointSatisfies(environment, source, declaration.endpoints[1].accepts) &&
72
- endpointSatisfies(environment, target, declaration.endpoints[0].accepts));
84
+ const endpointsSatisfy = coreEndpointsSatisfy(declaration, source, target, (endpoint, accepts) => endpointSatisfies(environment, endpoint, accepts));
73
85
  if (!endpointsSatisfy) {
74
- diagnostics.push(diagnostic('DV_CORE_EDGE_CLASS', pointer, `Core Edge endpoints do not satisfy ${formatDefinitionRef(edge.class)}.`, formatDefinitionRef(edge.class)));
86
+ diagnostics.push(diagnostic('DV_CORE_EDGE_CLASS', pointer, `Core Edge endpoints do not satisfy ${formatDefinitionRef(edge.class)}.${source.kind === 'domain' || target.kind === 'domain'
87
+ ? ` The owning Domain is a ${formatDefinitionRef(DOMAIN_ENDPOINT_CLASS)} Node: the endpoint that names it must accept that Class or one it extends.`
88
+ : ''}`, formatDefinitionRef(edge.class)));
75
89
  continue;
76
90
  }
77
91
  edges.push({ source, target, declaration, value: edge, pointer });
@@ -80,7 +94,7 @@ function resolveCoreEdges(environment, analysis, registry, nodesByRef, diagnosti
80
94
  }
81
95
  function resolveCoreEndpoint(environment, ref, pointer, nodesByRef, diagnostics) {
82
96
  if (ref.kind === 'domain')
83
- return { kind: 'domain', ref, key: 'domain' };
97
+ return { kind: 'domain', ref, key: 'domain', classRef: DOMAIN_ENDPOINT_CLASS };
84
98
  const undeclared = () => {
85
99
  const key = policyObjectKey(ref);
86
100
  diagnostics.push(diagnostic('DV_CORE_REF', pointer, `Core endpoint ${key} does not name a declared local Definition.`, key));
@@ -110,10 +124,14 @@ function declaresProjectedMember(environment, ref) {
110
124
  return Object.hasOwn(candidate.policies, ref.name);
111
125
  return Object.hasOwn(candidate.views, ref.name);
112
126
  }
127
+ /**
128
+ * A Core Node is admitted by its own authored Class, a projected member by the Kernel declaration
129
+ * Class it projects as, and the owning Domain by the Kernel `Domain` Class of its Node: the write
130
+ * that projects the Edge binds exactly that Node, so an Edge Class that does not accept it can
131
+ * never be installed (DV-CORE-EDGE-CLASS).
132
+ */
113
133
  function endpointSatisfies(environment, endpoint, accepts) {
114
- // The owning Domain is a graph-projection identity, not a Definition declared by this DSL.
115
- // Its concrete Class contract is therefore admitted by the projection compiler.
116
- return endpoint.kind === 'domain' || classSatisfies(environment, endpoint.classRef, accepts);
134
+ return classSatisfies(environment, endpoint.classRef, accepts);
117
135
  }
118
136
  function validateCreationProperties(definition, properties, registry, code, pointer, diagnostics) {
119
137
  const schema = {
@@ -127,166 +145,70 @@ function validateCreationProperties(definition, properties, registry, code, poin
127
145
  .sort(compareUnicode),
128
146
  additionalProperties: false,
129
147
  };
130
- for (const failure of validateJsonInstance(schema, properties, registry, pointer)) {
131
- diagnostics.push(diagnostic(code, failure.pointer, failure.message, failure.ref));
148
+ for (const refusal of validateJsonInstance(schema, properties, registry, pointer)) {
149
+ diagnostics.push(valueDiagnostic(code, refusal));
132
150
  }
133
151
  }
134
152
  function validateCoreEdgeSet(environment, edges, nodesByRef, diagnostics) {
135
- const seen = new Map();
136
- const edgeSlices = new Map();
137
- for (const [name, candidate] of Object.entries(environment.candidate.classes)) {
138
- if (candidate.kind !== 'edge')
139
- continue;
140
- const ref = {
141
- origin: environment.candidate.origin,
142
- kind: 'class',
143
- name,
144
- };
145
- const resolved = environment.resolve(ref);
146
- if (resolved === undefined || resolved.ref.kind !== 'class' || resolved.kind !== 'edge') {
147
- continue;
148
- }
149
- edgeSlices.set(definitionRefIndex(ref), {
150
- declaration: resolved.declaration,
151
- edges: [],
152
- });
153
- }
154
- for (const edge of edges) {
155
- const classKey = definitionRefIndex(edge.value.class);
156
- const [source, target] = edge.declaration.orientation === 'undirected' &&
157
- compareUnicode(edge.source.key, edge.target.key) > 0
158
- ? [edge.target.key, edge.source.key]
159
- : [edge.source.key, edge.target.key];
160
- const key = `${classKey}\u0000${source}\u0000${target}`;
161
- const previous = seen.get(key);
162
- if (previous !== undefined) {
163
- diagnostics.push(diagnostic('DV_CORE_EDGE_KEY', edge.pointer, `Core Edge duplicates the normalized EdgeKey declared at ${previous}.`));
164
- }
165
- else {
166
- seen.set(key, edge.pointer);
167
- }
168
- const slice = edgeSlices.get(classKey) ?? {
169
- declaration: edge.declaration,
170
- edges: [],
171
- };
172
- slice.edges.push(edge);
173
- edgeSlices.set(classKey, slice);
174
- if (edge.declaration.constraints.noSelf === true && edge.source.key === edge.target.key) {
175
- diagnostics.push(diagnostic('DV_CORE_EDGE_CONSTRAINTS', edge.pointer, 'Core Edge violates noSelf.'));
176
- }
177
- }
178
- for (const { declaration, edges: classEdges } of edgeSlices.values()) {
179
- if (declaration.constraints.acyclic === true && hasDirectedCycle(classEdges)) {
180
- diagnostics.push(diagnostic('DV_CORE_EDGE_CONSTRAINTS', '/core/edges', 'Core Edge set violates exact-Class acyclicity.'));
181
- }
182
- validateCardinality(environment, declaration, classEdges, nodesByRef, diagnostics);
183
- }
184
- }
185
- function validateCardinality(environment, declaration, edges, nodesByRef, diagnostics) {
186
- const subjects = cardinalitySubjects(edges, nodesByRef);
187
- if (declaration.orientation === 'undirected') {
188
- const [first, second] = declaration.endpoints;
189
- for (const subject of subjects) {
190
- const matchesFirst = classSatisfies(environment, subject.classRef, first.accepts);
191
- const matchesSecond = classSatisfies(environment, subject.classRef, second.accepts);
192
- if (!matchesFirst && !matchesSecond)
193
- continue;
194
- const degree = edges.filter((edge) => edge.source.key === subject.key || edge.target.key === subject.key).length;
195
- if (matchesFirst)
196
- checkBound(first, first.incident, degree, subject, diagnostics);
197
- if (matchesSecond && !sameIncidentEndpointContract(first, second)) {
198
- checkBound(second, second.incident, degree, subject, diagnostics);
153
+ const { candidate } = environment;
154
+ const resolve = (ref) => environment.resolveAny(ref)?.declaration;
155
+ checkCoreEdgeSet({
156
+ edges: edges.map((edge) => ({
157
+ edge,
158
+ class: definitionRefIndex(edge.value.class),
159
+ contract: edge.declaration,
160
+ source: factEndpoint(edge.source, edge.pointer),
161
+ target: factEndpoint(edge.target, edge.pointer),
162
+ })),
163
+ classes: Object.entries(candidate.classes).flatMap(([name, declaration]) => {
164
+ if (declaration.kind !== 'edge')
165
+ return [];
166
+ const ref = { origin: candidate.origin, kind: 'class', name };
167
+ const resolved = environment.resolve(ref);
168
+ if (resolved === undefined || resolved.ref.kind !== 'class' || resolved.kind !== 'edge') {
169
+ return [];
199
170
  }
200
- }
201
- return;
202
- }
203
- for (const subject of subjects) {
204
- if (classSatisfies(environment, subject.classRef, declaration.endpoints.source.accepts)) {
205
- const degree = edges.filter((edge) => edge.source.key === subject.key).length;
206
- checkBound(declaration.endpoints.source, declaration.endpoints.source.outgoing, degree, subject, diagnostics);
207
- }
208
- if (classSatisfies(environment, subject.classRef, declaration.endpoints.target.accepts)) {
209
- const degree = edges.filter((edge) => edge.target.key === subject.key).length;
210
- checkBound(declaration.endpoints.target, declaration.endpoints.target.incoming, degree, subject, diagnostics);
211
- }
212
- }
213
- }
214
- /**
215
- * Every declared Core Node proves its own complete degree, so both bounds hold. A Definition
216
- * endpoint is only named by the Edges that reach it: the Domain may also create ordinary Edges of
217
- * the same Class against it, so a minimum is unprovable here and only the maximum is enforced.
218
- */
219
- function cardinalitySubjects(edges, nodesByRef) {
220
- const subjects = new Map();
221
- for (const node of nodesByRef.values()) {
222
- subjects.set(node.key, {
171
+ return [
172
+ { key: definitionRefIndex(ref), contract: resolved.declaration },
173
+ ];
174
+ }),
175
+ nodes: [...nodesByRef.values()].map((node) => ({
223
176
  key: node.key,
224
- classRef: node.classRef,
225
- pointer: `/core/nodes/${escapePointer(node.slug)}`,
226
- minimum: true,
227
- });
228
- }
229
- for (const edge of edges) {
230
- for (const endpoint of [edge.source, edge.target]) {
231
- if (endpoint.kind !== 'member' || subjects.has(endpoint.key))
232
- continue;
233
- subjects.set(endpoint.key, {
234
- key: endpoint.key,
235
- classRef: endpoint.classRef,
236
- pointer: edge.pointer,
237
- minimum: false,
238
- });
239
- }
240
- }
241
- return Object.freeze([...subjects.values()]);
177
+ class: node.classRef,
178
+ pointer: nodePointer(node),
179
+ })),
180
+ satisfies: (ref, accepts) => classSatisfies(environment, ref, accepts),
181
+ keys: ({ edge }) => edgeUniqueKeys(coveringEdgeUniqueTuples(edge.declaration, resolve), {
182
+ source: edge.source.key,
183
+ target: edge.target.key,
184
+ props: edge.value.properties,
185
+ }),
186
+ sameIncident: sameIncidentEndpointContract,
187
+ report: (issue) => diagnostics.push(coreEdgeDiagnostic(issue)),
188
+ });
189
+ }
190
+ /** A projected member's degree, and the owning Domain's, is reported at the Edge that names it. */
191
+ function factEndpoint(endpoint, pointer) {
192
+ return endpoint.kind === 'core'
193
+ ? endpoint
194
+ : { key: endpoint.key, member: { class: endpoint.classRef, pointer } };
242
195
  }
243
- function checkBound(endpoint, count, degree, subject, diagnostics) {
244
- const [minimum, maximum] = countBounds(count);
245
- if (degree <= maximum && (degree >= minimum || !subject.minimum))
246
- return;
247
- diagnostics.push(diagnostic('DV_CORE_EDGE_CONSTRAINTS', subject.pointer, `Core degree ${degree} violates ${endpoint.role} Edge count ${count}.`));
196
+ function coreEdgeDiagnostic(issue) {
197
+ switch (issue.kind) {
198
+ case 'identity':
199
+ return diagnostic('DV_CORE_EDGE_KEY', issue.edge.pointer, `Core Edge duplicates the normalized EdgeKey declared at ${issue.previous.pointer}.`);
200
+ case 'no-self':
201
+ return diagnostic('DV_CORE_EDGE_CONSTRAINTS', issue.edge.pointer, 'Core Edge violates noSelf.');
202
+ case 'key':
203
+ return diagnostic('DV_CORE_EDGE_UNIQUE', issue.edge.pointer, `Core Edge holds a key the Core Edge at ${issue.previous.pointer} holds.`);
204
+ case 'acyclic':
205
+ return diagnostic('DV_CORE_EDGE_CONSTRAINTS', '/core/edges', 'Core Edge set violates exact-Class acyclicity.');
206
+ case 'degree':
207
+ return diagnostic('DV_CORE_EDGE_CONSTRAINTS', issue.pointer, `Core degree ${issue.degree} violates ${issue.endpoint.role} Edge count ${issue.count}.`);
208
+ }
248
209
  }
249
210
  function sameIncidentEndpointContract(left, right) {
250
211
  return (left.incident === right.incident &&
251
212
  left.accepts.length === right.accepts.length &&
252
213
  left.accepts.every((ref, index) => definitionRefIndex(ref) === definitionRefIndex(right.accepts[index])));
253
214
  }
254
- function countBounds(value) {
255
- switch (value) {
256
- case '0..*':
257
- return [0, Number.POSITIVE_INFINITY];
258
- case '1..*':
259
- return [1, Number.POSITIVE_INFINITY];
260
- case '0..1':
261
- return [0, 1];
262
- case '1':
263
- return [1, 1];
264
- }
265
- }
266
- function hasDirectedCycle(edges) {
267
- const outgoing = new Map();
268
- for (const edge of edges) {
269
- const targets = outgoing.get(edge.source.key) ?? [];
270
- targets.push(edge.target.key);
271
- outgoing.set(edge.source.key, targets);
272
- }
273
- const visiting = new Set();
274
- const visited = new Set();
275
- const visit = (key) => {
276
- if (visiting.has(key))
277
- return true;
278
- if (visited.has(key))
279
- return false;
280
- visiting.add(key);
281
- for (const target of outgoing.get(key) ?? [])
282
- if (visit(target))
283
- return true;
284
- visiting.delete(key);
285
- visited.add(key);
286
- return false;
287
- };
288
- return [...outgoing.keys()].some(visit);
289
- }
290
- function escapePointer(value) {
291
- return value.replaceAll('~', '~0').replaceAll('/', '~1');
292
- }