@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
@@ -0,0 +1,342 @@
1
+ import { isCanonicalBlobRef } from '../model/blob.js';
2
+ import { isInstantValue } from '../model/instant.js';
3
+ import { isValueObject as isJsonObject } from '../model/object.js';
4
+ import { codePointLength, isMultipleOf, jsonEqual, matchesType, unique } from './assertions.js';
5
+ /**
6
+ * Whether a value satisfies its prepared schema, as the evaluator decides it, without building a
7
+ * failure, an instance pointer or an annotation: the evaluator's verdict, or undefined when this
8
+ * check cannot give it. Only the evaluator reports why a value fails, so a caller keeps it for
9
+ * every value this check does not find valid.
10
+ *
11
+ * Each schema location is compiled once into a closure over its keywords, retained with the
12
+ * prepared schema. A schema whose closure holds an annotation-dependent keyword
13
+ * (`unevaluatedItems`, `unevaluatedProperties`) is left to the evaluator, as is a value nested
14
+ * deeper than this check follows, and any value whose check throws.
15
+ */
16
+ export function satisfies(compiled, value) {
17
+ let checks = compilations.get(compiled);
18
+ if (checks === undefined) {
19
+ checks = annotationFree(compiled) ? new Map() : null;
20
+ compilations.set(compiled, checks);
21
+ }
22
+ if (checks === null)
23
+ return undefined;
24
+ const outer = verdicts;
25
+ verdicts = new Map();
26
+ try {
27
+ return checkOf(compiled, checks, compiled.root)(value, 0);
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ finally {
33
+ verdicts = outer;
34
+ }
35
+ }
36
+ /** Per prepared schema: its checks by location, or null when its closure needs annotations. */
37
+ const compilations = new WeakMap();
38
+ /** Locations one evaluation follows before leaving the value to the evaluator. */
39
+ const MAXIMUM_DEPTH = 1_024;
40
+ const UNDECIDED = new Error('The value is left to the evaluator.');
41
+ /**
42
+ * The verdicts of the current evaluation by referenced location and value. Only a reference reaches
43
+ * one location from several places, so each referenced location is checked once per value, as the
44
+ * evaluator memoizes it, and a schema that references a location twice per level stays linear. A
45
+ * location entered again on the same value is a cycle, left to the evaluator.
46
+ */
47
+ let verdicts = new Map();
48
+ function annotationFree(compiled) {
49
+ for (const { schema } of compiled.locations.values()) {
50
+ if (isJsonObject(schema) &&
51
+ (Object.hasOwn(schema, 'unevaluatedItems') || Object.hasOwn(schema, 'unevaluatedProperties'))) {
52
+ return false;
53
+ }
54
+ }
55
+ return true;
56
+ }
57
+ /** The check of one location, compiled on its first use so that references may recur. */
58
+ function checkOf(compiled, checks, location) {
59
+ let check = checks.get(location);
60
+ if (check === undefined) {
61
+ let body;
62
+ check = (value, depth) => {
63
+ if (depth > MAXIMUM_DEPTH)
64
+ throw UNDECIDED;
65
+ body ??= compileLocation(compiled, checks, location);
66
+ return body(value, depth);
67
+ };
68
+ checks.set(location, check);
69
+ }
70
+ return check;
71
+ }
72
+ /**
73
+ * The conjunction of the location's keywords, each as the evaluator applies it: an applicator
74
+ * checks the same value at its child location, a keyword of one type passes every other type.
75
+ */
76
+ function compileLocation(compiled, checks, location) {
77
+ const schema = location.schema;
78
+ if (schema === true)
79
+ return () => true;
80
+ if (schema === false)
81
+ return () => false;
82
+ const child = (keyword, member) => checkOf(compiled, checks, compiled.child(location, keyword, member));
83
+ const parts = [];
84
+ if (Object.hasOwn(schema, '$ref')) {
85
+ const target = compiled.reference(location);
86
+ if (target === undefined)
87
+ throw UNDECIDED;
88
+ const check = checkOf(compiled, checks, target);
89
+ parts.push((value, depth) => {
90
+ let byValue = verdicts.get(target);
91
+ if (byValue === undefined)
92
+ verdicts.set(target, (byValue = new Map()));
93
+ const known = byValue.get(value);
94
+ if (known === null)
95
+ throw UNDECIDED;
96
+ if (known !== undefined)
97
+ return known;
98
+ byValue.set(value, null);
99
+ const verdict = check(value, depth + 1);
100
+ byValue.set(value, verdict);
101
+ return verdict;
102
+ });
103
+ }
104
+ compileCompositions(schema, child, parts);
105
+ compileCommon(schema, parts);
106
+ compileNumber(schema, parts);
107
+ compileString(compiled, schema, parts);
108
+ compileArray(schema, child, parts);
109
+ compileObject(compiled, schema, child, parts);
110
+ if (parts.length === 0)
111
+ return () => true;
112
+ if (parts.length === 1)
113
+ return parts[0];
114
+ return (value, depth) => {
115
+ for (const part of parts)
116
+ if (!part(value, depth))
117
+ return false;
118
+ return true;
119
+ };
120
+ }
121
+ function compileCompositions(schema, child, parts) {
122
+ const allOf = schemaList(schema.allOf)?.map((_, index) => child('allOf', index));
123
+ if (allOf !== undefined) {
124
+ parts.push((value, depth) => allOf.every((check) => check(value, depth + 1)));
125
+ }
126
+ const anyOf = schemaList(schema.anyOf)?.map((_, index) => child('anyOf', index));
127
+ if (anyOf !== undefined) {
128
+ parts.push((value, depth) => anyOf.some((check) => check(value, depth + 1)));
129
+ }
130
+ const oneOf = schemaList(schema.oneOf)?.map((_, index) => child('oneOf', index));
131
+ if (oneOf !== undefined) {
132
+ parts.push((value, depth) => {
133
+ let admitted = 0;
134
+ for (const check of oneOf)
135
+ if (check(value, depth + 1) && ++admitted > 1)
136
+ return false;
137
+ return admitted === 1;
138
+ });
139
+ }
140
+ if (isSchema(schema.not)) {
141
+ const not = child('not');
142
+ parts.push((value, depth) => !not(value, depth + 1));
143
+ }
144
+ if (isSchema(schema.if)) {
145
+ const condition = child('if');
146
+ const then = isSchema(schema.then) ? child('then') : undefined;
147
+ const otherwise = isSchema(schema.else) ? child('else') : undefined;
148
+ parts.push((value, depth) => {
149
+ const branch = condition(value, depth + 1) ? then : otherwise;
150
+ return branch === undefined || branch(value, depth + 1);
151
+ });
152
+ }
153
+ }
154
+ function compileCommon(schema, parts) {
155
+ const kind = schema['x-astrale-value-kind'];
156
+ if (kind === 'instant')
157
+ parts.push((value) => isInstantValue(value));
158
+ if (kind === 'blob')
159
+ parts.push((value) => isCanonicalBlobRef(value));
160
+ const type = schema.type;
161
+ if (typeof type === 'string')
162
+ parts.push((value) => matchesType(type, value));
163
+ else if (Array.isArray(type)) {
164
+ const types = type.filter((candidate) => typeof candidate === 'string');
165
+ parts.push((value) => types.some((candidate) => matchesType(candidate, value)));
166
+ }
167
+ if (Object.hasOwn(schema, 'const')) {
168
+ const expected = schema.const;
169
+ parts.push((value) => jsonEqual(value, expected));
170
+ }
171
+ const choices = schema.enum;
172
+ if (Array.isArray(choices)) {
173
+ parts.push((value) => choices.some((candidate) => jsonEqual(value, candidate)));
174
+ }
175
+ }
176
+ function compileNumber(schema, parts) {
177
+ const { multipleOf, maximum, exclusiveMaximum, minimum, exclusiveMinimum } = schema;
178
+ const bounded = typeof multipleOf === 'number' ||
179
+ typeof maximum === 'number' ||
180
+ typeof exclusiveMaximum === 'number' ||
181
+ typeof minimum === 'number' ||
182
+ typeof exclusiveMinimum === 'number';
183
+ if (!bounded)
184
+ return;
185
+ parts.push((value) => typeof value !== 'number' ||
186
+ ((typeof multipleOf !== 'number' || isMultipleOf(value, multipleOf)) &&
187
+ (typeof maximum !== 'number' || value <= maximum) &&
188
+ (typeof exclusiveMaximum !== 'number' || value < exclusiveMaximum) &&
189
+ (typeof minimum !== 'number' || value >= minimum) &&
190
+ (typeof exclusiveMinimum !== 'number' || value > exclusiveMinimum)));
191
+ }
192
+ function compileString(compiled, schema, parts) {
193
+ const { maxLength, minLength, pattern, fullMatch } = schema;
194
+ if (typeof maxLength === 'number' || typeof minLength === 'number') {
195
+ // For n UTF-16 units, ceil(n / 2) <= code points <= n: count only when the bounds cannot
196
+ // decide, as the evaluator does.
197
+ parts.push((value) => {
198
+ if (typeof value !== 'string')
199
+ return true;
200
+ if ((typeof maxLength !== 'number' || value.length <= maxLength) &&
201
+ (typeof minLength !== 'number' || Math.ceil(value.length / 2) >= minLength)) {
202
+ return true;
203
+ }
204
+ const length = codePointLength(value);
205
+ return ((typeof maxLength !== 'number' || length <= maxLength) &&
206
+ (typeof minLength !== 'number' || length >= minLength));
207
+ });
208
+ }
209
+ if (typeof pattern === 'string') {
210
+ const matcher = compiled.matcher(pattern);
211
+ parts.push((value) => typeof value !== 'string' || matcher.test(value));
212
+ }
213
+ if (typeof fullMatch === 'string') {
214
+ const matcher = compiled.matcher(fullMatch);
215
+ parts.push((value) => typeof value !== 'string' || matcher.matches(value));
216
+ }
217
+ }
218
+ function compileArray(schema, child, parts) {
219
+ const { maxItems, minItems } = schema;
220
+ if (typeof maxItems === 'number' || typeof minItems === 'number' || schema.uniqueItems === true) {
221
+ const distinct = schema.uniqueItems === true;
222
+ parts.push((value) => !Array.isArray(value) ||
223
+ ((typeof maxItems !== 'number' || value.length <= maxItems) &&
224
+ (typeof minItems !== 'number' || value.length >= minItems) &&
225
+ (!distinct || unique(value))));
226
+ }
227
+ const prefix = schemaList(schema.prefixItems)?.map((_, index) => child('prefixItems', index));
228
+ const items = isSchema(schema.items) ? child('items') : undefined;
229
+ if (prefix !== undefined || items !== undefined) {
230
+ const leading = prefix ?? [];
231
+ parts.push((value, depth) => {
232
+ if (!Array.isArray(value))
233
+ return true;
234
+ for (let index = 0; index < value.length; index++) {
235
+ const check = index < leading.length ? leading[index] : items;
236
+ if (check !== undefined && !check(value[index], depth + 1))
237
+ return false;
238
+ }
239
+ return true;
240
+ });
241
+ }
242
+ if (isSchema(schema.contains)) {
243
+ const contains = child('contains');
244
+ const least = typeof schema.minContains === 'number' ? schema.minContains : 1;
245
+ const most = typeof schema.maxContains === 'number' ? schema.maxContains : Number.POSITIVE_INFINITY;
246
+ parts.push((value, depth) => {
247
+ if (!Array.isArray(value))
248
+ return true;
249
+ let matches = 0;
250
+ for (const item of value)
251
+ if (contains(item, depth + 1))
252
+ matches++;
253
+ return matches >= least && matches <= most;
254
+ });
255
+ }
256
+ }
257
+ function compileObject(compiled, schema, child, parts) {
258
+ const { maxProperties, minProperties } = schema;
259
+ const required = Array.isArray(schema.required)
260
+ ? schema.required.filter((name) => typeof name === 'string')
261
+ : [];
262
+ const dependentRequired = isJsonObject(schema.dependentRequired)
263
+ ? Object.entries(schema.dependentRequired).flatMap(([name, names]) => Array.isArray(names)
264
+ ? [[name, names.filter((other) => typeof other === 'string')]]
265
+ : [])
266
+ : [];
267
+ if (typeof maxProperties === 'number' ||
268
+ typeof minProperties === 'number' ||
269
+ required.length > 0 ||
270
+ dependentRequired.length > 0) {
271
+ parts.push((value) => {
272
+ if (!isJsonObject(value))
273
+ return true;
274
+ if (typeof maxProperties === 'number' || typeof minProperties === 'number') {
275
+ const count = Object.keys(value).length;
276
+ if (typeof maxProperties === 'number' && count > maxProperties)
277
+ return false;
278
+ if (typeof minProperties === 'number' && count < minProperties)
279
+ return false;
280
+ }
281
+ for (const name of required)
282
+ if (!Object.hasOwn(value, name))
283
+ return false;
284
+ for (const [name, names] of dependentRequired) {
285
+ if (Object.hasOwn(value, name) && !names.every((other) => Object.hasOwn(value, other))) {
286
+ return false;
287
+ }
288
+ }
289
+ return true;
290
+ });
291
+ }
292
+ // Each member is checked by every pattern it matches, by its declared schema, and by the
293
+ // additional schema when neither declares it.
294
+ const declared = isJsonObject(schema.properties) ? schema.properties : {};
295
+ const properties = new Map(Object.keys(declared).map((name) => [name, child('properties', name)]));
296
+ const patterns = isJsonObject(schema.patternProperties)
297
+ ? Object.keys(schema.patternProperties).map((source) => [compiled.matcher(source), child('patternProperties', source)])
298
+ : [];
299
+ const additional = isSchema(schema.additionalProperties)
300
+ ? child('additionalProperties')
301
+ : undefined;
302
+ const names = isSchema(schema.propertyNames) ? child('propertyNames') : undefined;
303
+ if (properties.size > 0 || patterns.length > 0 || additional !== undefined || names) {
304
+ parts.push((value, depth) => {
305
+ if (!isJsonObject(value))
306
+ return true;
307
+ for (const name of Object.keys(value)) {
308
+ const member = value[name];
309
+ let matched = false;
310
+ for (const [matcher, check] of patterns) {
311
+ if (!matcher.test(name))
312
+ continue;
313
+ matched = true;
314
+ if (!check(member, depth + 1))
315
+ return false;
316
+ }
317
+ const check = Object.hasOwn(declared, name) ? properties.get(name) : undefined;
318
+ if (check !== undefined) {
319
+ if (!check(member, depth + 1))
320
+ return false;
321
+ }
322
+ else if (!matched && additional !== undefined && !additional(member, depth + 1)) {
323
+ return false;
324
+ }
325
+ if (names !== undefined && !names(name, depth + 1))
326
+ return false;
327
+ }
328
+ return true;
329
+ });
330
+ }
331
+ if (isJsonObject(schema.dependentSchemas)) {
332
+ const dependent = Object.keys(schema.dependentSchemas).map((name) => [name, child('dependentSchemas', name)]);
333
+ parts.push((value, depth) => !isJsonObject(value) ||
334
+ dependent.every(([name, check]) => !Object.hasOwn(value, name) || check(value, depth + 1)));
335
+ }
336
+ }
337
+ function schemaList(input) {
338
+ return Array.isArray(input) ? input : undefined;
339
+ }
340
+ function isSchema(input) {
341
+ return typeof input === 'boolean' || isJsonObject(input);
342
+ }
@@ -0,0 +1,3 @@
1
+ import type { Rule } from './assertions.js';
2
+ /** Rules for the array size, uniqueness, item and `contains` keywords. */
3
+ export declare const arrayRules: ReadonlyMap<string, Rule>;
@@ -0,0 +1,85 @@
1
+ import { has } from './evidence.js';
2
+ import { isTop } from './keywords.js';
3
+ import { ARRAY } from './kinds.js';
4
+ import { childOf, childrenOf, schemaOf } from './location.js';
5
+ /** Rules for the array size, uniqueness, item and `contains` keywords. */
6
+ export const arrayRules = new Map([
7
+ [
8
+ 'maxItems',
9
+ ({ value, evidence }) => typeof value !== 'number' ||
10
+ !has(evidence, ARRAY) ||
11
+ (evidence.maxItems !== undefined && evidence.maxItems <= value),
12
+ ],
13
+ [
14
+ 'minItems',
15
+ ({ value, evidence }) => typeof value !== 'number' ||
16
+ !has(evidence, ARRAY) ||
17
+ value <= 0 ||
18
+ evidence.minItems >= value,
19
+ ],
20
+ [
21
+ 'uniqueItems',
22
+ ({ value, evidence }) => value !== true ||
23
+ !has(evidence, ARRAY) ||
24
+ evidence.uniqueItems ||
25
+ (evidence.maxItems !== undefined && evidence.maxItems <= 1),
26
+ ],
27
+ ['prefixItems', itemsHold],
28
+ ['items', itemsHold],
29
+ ['contains', containsHolds],
30
+ ['minContains', containsHolds],
31
+ ['maxContains', containsHolds],
32
+ ]);
33
+ function itemsHold({ inclusion, outer, schema, evidence }) {
34
+ if (!has(evidence, ARRAY))
35
+ return true;
36
+ const prefix = childrenOf(outer, 'prefixItems');
37
+ const rest = Object.hasOwn(schema, 'items') ? childOf(outer, 'items') : undefined;
38
+ const width = Math.max(prefix.length, ...evidence.conjuncts.map((conjunct) => childrenOf(conjunct, 'prefixItems').length));
39
+ // Every index at or beyond `width` meets `items` on both sides.
40
+ for (let index = 0; index <= width; index++) {
41
+ const required = index < prefix.length ? prefix[index] : rest;
42
+ if (required === undefined || isTop(schemaOf(required)))
43
+ continue;
44
+ if (evidence.maxItems !== undefined && evidence.maxItems <= index)
45
+ continue;
46
+ const admitted = evidence.conjuncts.flatMap((conjunct) => {
47
+ const own = childrenOf(conjunct, 'prefixItems');
48
+ if (index < own.length)
49
+ return [own[index]];
50
+ const items = childOf(conjunct, 'items');
51
+ return items === undefined ? [] : [items];
52
+ });
53
+ if (!admitted.some((constraint) => inclusion.includes(required, constraint)))
54
+ return false;
55
+ }
56
+ return true;
57
+ }
58
+ function containsHolds({ inclusion, outer, schema, evidence }) {
59
+ const contains = childOf(outer, 'contains');
60
+ if (contains === undefined || !has(evidence, ARRAY))
61
+ return true;
62
+ const { minimum, maximum } = containsCounts(schema);
63
+ const ownContains = evidence.conjuncts.flatMap((conjunct) => {
64
+ const own = childOf(conjunct, 'contains');
65
+ if (own === undefined)
66
+ return [];
67
+ const conjunctSchema = schemaOf(conjunct);
68
+ return [{ at: own, ...containsCounts(conjunctSchema) }];
69
+ });
70
+ // Every inner match is an outer match, so outer counts at least as many.
71
+ const atLeast = minimum <= 0 ||
72
+ ownContains.some((own) => own.minimum >= minimum && inclusion.includes(contains, own.at));
73
+ // Every outer match is an inner match, so outer counts at most as many.
74
+ const atMost = maximum === Number.POSITIVE_INFINITY ||
75
+ (evidence.maxItems !== undefined && evidence.maxItems <= maximum) ||
76
+ ownContains.some((own) => own.maximum <= maximum && inclusion.includes(own.at, contains));
77
+ return atLeast && atMost;
78
+ }
79
+ /** The match counts `contains` requires, with the evaluator's defaults. */
80
+ function containsCounts(schema) {
81
+ return {
82
+ minimum: typeof schema.minContains === 'number' ? schema.minContains : 1,
83
+ maximum: typeof schema.maxContains === 'number' ? schema.maxContains : Number.POSITIVE_INFINITY,
84
+ };
85
+ }
@@ -0,0 +1,18 @@
1
+ import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
2
+ import type { Evidence } from './evidence.js';
3
+ import type { Inclusion } from './includes.js';
4
+ import type { At } from './location.js';
5
+ /** One keyword of `outer`, to be proved from the evidence of `inner`. */
6
+ export interface Assertion {
7
+ readonly inclusion: Inclusion;
8
+ readonly keyword: string;
9
+ readonly value: JsonValue | undefined;
10
+ readonly outer: At;
11
+ readonly schema: Readonly<Record<string, JsonValue>>;
12
+ readonly inner: At;
13
+ readonly evidence: Evidence;
14
+ }
15
+ /** Whether one outer keyword holds for every value the inner evidence admits. */
16
+ export type Rule = (assertion: Assertion) => boolean;
17
+ /** Whether every keyword of the outer `schema` holds for the values `inner` admits. */
18
+ export declare function assertionsHold(inclusion: Inclusion, outer: At, schema: Readonly<Record<string, JsonValue>>, inner: At, evidence: Evidence): boolean;
@@ -0,0 +1,29 @@
1
+ import { arrayRules } from './arrays.js';
2
+ import { combinatorRules } from './combinators.js';
3
+ import { INERT } from './keywords.js';
4
+ import { numberRules } from './numbers.js';
5
+ import { objectRules } from './objects.js';
6
+ import { stringRules } from './strings.js';
7
+ import { typeRules } from './types.js';
8
+ const RULES = new Map([
9
+ ...combinatorRules,
10
+ ...typeRules,
11
+ ...numberRules,
12
+ ...stringRules,
13
+ ...arrayRules,
14
+ ...objectRules,
15
+ ]);
16
+ /** Whether every keyword of the outer `schema` holds for the values `inner` admits. */
17
+ export function assertionsHold(inclusion, outer, schema, inner, evidence) {
18
+ // Annotation-dependent keywords are decided only by equivalence.
19
+ if (Object.hasOwn(schema, 'unevaluatedProperties'))
20
+ return false;
21
+ if (Object.hasOwn(schema, 'unevaluatedItems'))
22
+ return false;
23
+ return Object.keys(schema).every((keyword) => {
24
+ const rule = RULES.get(keyword);
25
+ if (rule === undefined)
26
+ return INERT.has(keyword);
27
+ return rule({ inclusion, keyword, value: schema[keyword], outer, schema, inner, evidence });
28
+ });
29
+ }
@@ -0,0 +1,3 @@
1
+ import type { Rule } from './assertions.js';
2
+ /** Rules for `$ref`, the boolean combinators and the conditional keywords. */
3
+ export declare const combinatorRules: ReadonlyMap<string, Rule>;
@@ -0,0 +1,67 @@
1
+ import { equivalent } from './equivalence.js';
2
+ import { childOf, childrenOf, referenceOf } from './location.js';
3
+ /** Rules for `$ref`, the boolean combinators and the conditional keywords. */
4
+ export const combinatorRules = new Map([
5
+ [
6
+ '$ref',
7
+ ({ inclusion, outer, inner }) => {
8
+ const target = referenceOf(outer);
9
+ return target !== undefined && inclusion.includes(target, inner);
10
+ },
11
+ ],
12
+ [
13
+ 'allOf',
14
+ ({ inclusion, outer, inner }) => childrenOf(outer, 'allOf').every((branch) => inclusion.includes(branch, inner)),
15
+ ],
16
+ [
17
+ 'anyOf',
18
+ ({ inclusion, outer, inner }) => childrenOf(outer, 'anyOf').some((branch) => inclusion.includes(branch, inner)),
19
+ ],
20
+ ['oneOf', () => false],
21
+ [
22
+ 'not',
23
+ ({ inclusion, outer, evidence }) => {
24
+ const negated = childOf(outer, 'not');
25
+ return negated !== undefined && excludes(inclusion, negated, evidence);
26
+ },
27
+ ],
28
+ ['if', conditionalHolds],
29
+ ['then', conditionalHolds],
30
+ ['else', conditionalHolds],
31
+ ]);
32
+ /** Whether no value admitted by `evidence` satisfies `negated`. */
33
+ function excludes(inclusion, negated, evidence) {
34
+ if ((inclusion.evidence(negated).kinds & evidence.kinds) === 0)
35
+ return true;
36
+ return evidence.conjuncts.some((conjunct) => {
37
+ const own = childOf(conjunct, 'not');
38
+ return own !== undefined && inclusion.includes(own, negated);
39
+ });
40
+ }
41
+ function conditionalHolds({ inclusion, outer, inner, evidence }) {
42
+ const condition = childOf(outer, 'if');
43
+ if (condition === undefined)
44
+ return true;
45
+ const then = childOf(outer, 'then');
46
+ const otherwise = childOf(outer, 'else');
47
+ const thenHolds = (fallback) => then === undefined ||
48
+ inclusion.includes(then, inner) ||
49
+ (fallback !== undefined && inclusion.includes(then, fallback));
50
+ const elseHolds = (fallback) => otherwise === undefined ||
51
+ inclusion.includes(otherwise, inner) ||
52
+ (fallback !== undefined && inclusion.includes(otherwise, fallback));
53
+ if (thenHolds() && elseHolds())
54
+ return true;
55
+ for (const conjunct of evidence.conjuncts) {
56
+ const own = childOf(conjunct, 'if');
57
+ if (own === undefined || !equivalent(inclusion, own, condition))
58
+ continue;
59
+ if (thenHolds(childOf(conjunct, 'then')) && elseHolds(childOf(conjunct, 'else')))
60
+ return true;
61
+ }
62
+ if (inclusion.includes(condition, inner))
63
+ return thenHolds();
64
+ if (excludes(inclusion, condition, evidence))
65
+ return elseHolds();
66
+ return false;
67
+ }
@@ -0,0 +1,4 @@
1
+ import type { Inclusion } from './includes.js';
2
+ import type { At } from './location.js';
3
+ /** Whether the two locations are bisimilar: equal assertions over paired subschemas. */
4
+ export declare function equivalent(inclusion: Inclusion, left: At, right: At): boolean;
@@ -0,0 +1,74 @@
1
+ import { jsonEqual } from '../evaluate/assertions.js';
2
+ import { INERT } from './keywords.js';
3
+ import { at, referenceOf, schemaOf } from './location.js';
4
+ /** Keywords whose subschemas the equivalence check compares positionally. */
5
+ const SUBSCHEMA_KEYWORDS = new Set([
6
+ 'additionalProperties',
7
+ 'allOf',
8
+ 'anyOf',
9
+ 'contains',
10
+ 'dependentSchemas',
11
+ 'else',
12
+ 'if',
13
+ 'items',
14
+ 'not',
15
+ 'oneOf',
16
+ 'patternProperties',
17
+ 'prefixItems',
18
+ 'properties',
19
+ 'propertyNames',
20
+ 'then',
21
+ 'unevaluatedItems',
22
+ 'unevaluatedProperties',
23
+ ]);
24
+ /** Whether the two locations are bisimilar: equal assertions over paired subschemas. */
25
+ export function equivalent(inclusion, left, right) {
26
+ const pending = [[left, right]];
27
+ const seen = new Set();
28
+ while (pending.length > 0) {
29
+ const [first, second] = pending.pop();
30
+ const key = inclusion.pair(first, second);
31
+ if (seen.has(key))
32
+ continue;
33
+ seen.add(key);
34
+ const a = schemaOf(first);
35
+ const b = schemaOf(second);
36
+ if (typeof a === 'boolean' || typeof b === 'boolean') {
37
+ if (a !== b)
38
+ return false;
39
+ continue;
40
+ }
41
+ const assertions = assertionKeys(a);
42
+ if (assertions.length !== assertionKeys(b).length)
43
+ return false;
44
+ for (const keyword of assertions) {
45
+ if (!Object.hasOwn(b, keyword) || !jsonEqual(a[keyword], b[keyword]))
46
+ return false;
47
+ }
48
+ const firstChildren = subschemaChildren(first);
49
+ const secondChildren = subschemaChildren(second);
50
+ if (firstChildren.size !== secondChildren.size)
51
+ return false;
52
+ for (const [path, index] of firstChildren) {
53
+ const other = secondChildren.get(path);
54
+ if (other === undefined)
55
+ return false;
56
+ pending.push([at(first.program, index), at(second.program, other)]);
57
+ }
58
+ const firstReference = referenceOf(first);
59
+ const secondReference = referenceOf(second);
60
+ if ((firstReference === undefined) !== (secondReference === undefined))
61
+ return false;
62
+ if (firstReference !== undefined)
63
+ pending.push([firstReference, secondReference]);
64
+ }
65
+ return true;
66
+ }
67
+ function assertionKeys(schema) {
68
+ return Object.keys(schema).filter((keyword) => !INERT.has(keyword) && !SUBSCHEMA_KEYWORDS.has(keyword) && keyword !== '$ref');
69
+ }
70
+ /** Subschema children keyed by their keyword path, without `$defs` or inert annotations. */
71
+ function subschemaChildren(location) {
72
+ const children = location.program.nodes[location.index]?.children ?? {};
73
+ return new Map(Object.entries(children).filter(([path]) => SUBSCHEMA_KEYWORDS.has(path.split('/', 1)[0].replaceAll('~1', '/').replaceAll('~0', '~'))));
74
+ }
@@ -0,0 +1,34 @@
1
+ import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
2
+ import type { At } from './location.js';
3
+ /** One numeric bound, inclusive unless `exclusive`. */
4
+ export interface Bound {
5
+ readonly value: number;
6
+ readonly exclusive: boolean;
7
+ }
8
+ /** Over-approximation of the values one location admits, collected from its conjuncts. */
9
+ export interface Evidence {
10
+ /** The location, its `$ref` targets and its `allOf` branches, transitively. */
11
+ readonly conjuncts: readonly At[];
12
+ readonly kinds: number;
13
+ /** A finite superset of the admitted values, when one is known. */
14
+ readonly values?: readonly JsonValue[];
15
+ readonly upper?: Bound;
16
+ readonly lower?: Bound;
17
+ readonly multipleOf: readonly number[];
18
+ readonly maxLength?: number;
19
+ readonly minLength: number;
20
+ readonly patterns: ReadonlySet<string>;
21
+ readonly fullMatches: ReadonlySet<string>;
22
+ readonly maxItems?: number;
23
+ readonly minItems: number;
24
+ readonly uniqueItems: boolean;
25
+ readonly maxProperties?: number;
26
+ readonly minProperties: number;
27
+ readonly required: ReadonlySet<string>;
28
+ readonly dependentRequired: ReadonlyMap<string, ReadonlySet<string>>;
29
+ readonly valueKinds: ReadonlySet<string>;
30
+ }
31
+ /** Whether `evidence` may admit a value of one of `kinds`. */
32
+ export declare function has(evidence: Evidence, kinds: number): boolean;
33
+ /** The evidence the conjuncts of `location` carry together. */
34
+ export declare function evidenceOf(location: At): Evidence;