@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,5 @@
1
+ import type { JsonSchema } from '../model/schema.js';
2
+ /** Keywords the evaluator never asserts. */
3
+ export declare const INERT: ReadonlySet<string>;
4
+ /** Whether a schema asserts nothing. */
5
+ export declare function isTop(schema: JsonSchema): boolean;
@@ -0,0 +1,31 @@
1
+ /** Keywords the evaluator never asserts. */
2
+ export const INERT = new Set([
3
+ '$anchor',
4
+ '$comment',
5
+ '$defs',
6
+ '$id',
7
+ '$schema',
8
+ 'contentEncoding',
9
+ 'contentMediaType',
10
+ 'contentSchema',
11
+ 'default',
12
+ 'deprecated',
13
+ 'description',
14
+ 'examples',
15
+ 'format',
16
+ 'readOnly',
17
+ 'title',
18
+ 'writeOnly',
19
+ 'x-astrale-path',
20
+ ]);
21
+ /** Whether a schema asserts nothing. */
22
+ export function isTop(schema) {
23
+ if (schema === true)
24
+ return true;
25
+ if (schema === false)
26
+ return false;
27
+ return Object.keys(schema).every((keyword) => INERT.has(keyword) ||
28
+ ((keyword === 'then' || keyword === 'else') && !Object.hasOwn(schema, 'if')) ||
29
+ ((keyword === 'minContains' || keyword === 'maxContains') &&
30
+ !Object.hasOwn(schema, 'contains')));
31
+ }
@@ -0,0 +1,17 @@
1
+ import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
2
+ /** Bit masks over the JSON kinds a schema may admit; integers and fractions split `number`. */
3
+ export declare const NULL = 1;
4
+ export declare const BOOLEAN = 2;
5
+ export declare const OBJECT = 4;
6
+ export declare const ARRAY = 8;
7
+ export declare const STRING = 16;
8
+ export declare const INTEGER = 32;
9
+ export declare const FRACTION = 64;
10
+ export declare const NUMBER: number;
11
+ export declare const ANY: number;
12
+ /** The kinds a `type` keyword value admits; an absent `type` admits every kind. */
13
+ export declare function typeMask(type: JsonValue | undefined): number;
14
+ /** The single kind of one JSON value. */
15
+ export declare function kindOf(value: JsonValue): number;
16
+ /** The finite value set of a kind mask made only of null and booleans. */
17
+ export declare function finiteKinds(kinds: number): readonly JsonValue[] | undefined;
@@ -0,0 +1,57 @@
1
+ /** Bit masks over the JSON kinds a schema may admit; integers and fractions split `number`. */
2
+ export const NULL = 1;
3
+ export const BOOLEAN = 2;
4
+ export const OBJECT = 4;
5
+ export const ARRAY = 8;
6
+ export const STRING = 16;
7
+ export const INTEGER = 32;
8
+ export const FRACTION = 64;
9
+ export const NUMBER = INTEGER | FRACTION;
10
+ export const ANY = NULL | BOOLEAN | OBJECT | ARRAY | STRING | NUMBER;
11
+ /** The kinds a `type` keyword value admits; an absent `type` admits every kind. */
12
+ export function typeMask(type) {
13
+ if (type === undefined)
14
+ return ANY;
15
+ const names = typeof type === 'string' ? [type] : Array.isArray(type) ? type : [];
16
+ return names.reduce((mask, name) => {
17
+ switch (name) {
18
+ case 'null':
19
+ return mask | NULL;
20
+ case 'boolean':
21
+ return mask | BOOLEAN;
22
+ case 'object':
23
+ return mask | OBJECT;
24
+ case 'array':
25
+ return mask | ARRAY;
26
+ case 'string':
27
+ return mask | STRING;
28
+ case 'integer':
29
+ return mask | INTEGER;
30
+ case 'number':
31
+ return mask | NUMBER;
32
+ default:
33
+ return mask;
34
+ }
35
+ }, 0);
36
+ }
37
+ /** The single kind of one JSON value. */
38
+ export function kindOf(value) {
39
+ if (value === null)
40
+ return NULL;
41
+ if (typeof value === 'boolean')
42
+ return BOOLEAN;
43
+ if (typeof value === 'string')
44
+ return STRING;
45
+ if (typeof value === 'number')
46
+ return Number.isInteger(value) ? INTEGER : FRACTION;
47
+ return Array.isArray(value) ? ARRAY : OBJECT;
48
+ }
49
+ /** The finite value set of a kind mask made only of null and booleans. */
50
+ export function finiteKinds(kinds) {
51
+ if ((kinds & ~(NULL | BOOLEAN)) !== 0)
52
+ return undefined;
53
+ return [
54
+ ...((kinds & NULL) !== 0 ? [null] : []),
55
+ ...((kinds & BOOLEAN) !== 0 ? [true, false] : []),
56
+ ];
57
+ }
@@ -0,0 +1,24 @@
1
+ import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
2
+ import type { ValueProgram } from '../compile/model.js';
3
+ import type { JsonSchema } from '../model/schema.js';
4
+ /** One schema location inside one admitted program. */
5
+ export interface At {
6
+ readonly program: ValueProgram;
7
+ readonly index: number;
8
+ }
9
+ /** The location `index` of `program`. */
10
+ export declare function at(program: ValueProgram, index: number): At;
11
+ /** The schema written at a location. */
12
+ export declare function schemaOf(location: At): JsonSchema;
13
+ /** The subschema under `keyword`, or under its `member`, when the program compiled one. */
14
+ export declare function childOf(location: At, keyword: string, member?: string | number): At | undefined;
15
+ /** The subschemas of an array-valued `keyword`, in order. */
16
+ export declare function childrenOf(location: At, keyword: string): readonly At[];
17
+ /** The resolved `$ref` target of a location. */
18
+ export declare function referenceOf(location: At): At | undefined;
19
+ /** The location with its `$ref` targets and `allOf` branches; admission makes this acyclic. */
20
+ export declare function conjunctsOf(location: At): readonly At[];
21
+ /** Whether `name` matches `pattern` under the program's regular-expression dialect. */
22
+ export declare function matches(location: At, pattern: string, name: string): boolean;
23
+ /** Whether the V1 evaluator accepts `value` at a location. */
24
+ export declare function accepts(location: At, value: JsonValue): boolean;
@@ -0,0 +1,63 @@
1
+ import { loadValueProgram } from '../compile/load.js';
2
+ import { evaluateInstance } from '../evaluate/evaluator.js';
3
+ import { isValueObject as isJsonObject } from '../model/object.js';
4
+ import { appendPointer } from '../syntax/pointer.js';
5
+ /** The location `index` of `program`. */
6
+ export function at(program, index) {
7
+ return { program, index };
8
+ }
9
+ /** The schema written at a location. */
10
+ export function schemaOf(location) {
11
+ const node = location.program.nodes[location.index];
12
+ if (node === undefined)
13
+ throw new TypeError('Value program location is absent.');
14
+ return node.schema;
15
+ }
16
+ /** The subschema under `keyword`, or under its `member`, when the program compiled one. */
17
+ export function childOf(location, keyword, member) {
18
+ const base = appendPointer('', keyword);
19
+ const key = (member === undefined ? base : appendPointer(base, member)).slice(1);
20
+ const index = location.program.nodes[location.index]?.children[key];
21
+ return index === undefined ? undefined : at(location.program, index);
22
+ }
23
+ /** The subschemas of an array-valued `keyword`, in order. */
24
+ export function childrenOf(location, keyword) {
25
+ const schema = schemaOf(location);
26
+ if (!isJsonObject(schema) || !Array.isArray(schema[keyword]))
27
+ return [];
28
+ return schema[keyword].flatMap((_, index) => {
29
+ const child = childOf(location, keyword, index);
30
+ return child === undefined ? [] : [child];
31
+ });
32
+ }
33
+ /** The resolved `$ref` target of a location. */
34
+ export function referenceOf(location) {
35
+ const index = location.program.nodes[location.index]?.reference;
36
+ return index === undefined ? undefined : at(location.program, index);
37
+ }
38
+ /** The location with its `$ref` targets and `allOf` branches; admission makes this acyclic. */
39
+ export function conjunctsOf(location) {
40
+ const seen = new Set();
41
+ const conjuncts = [];
42
+ const visit = (current) => {
43
+ if (seen.has(current.index))
44
+ return;
45
+ seen.add(current.index);
46
+ conjuncts.push(current);
47
+ const reference = referenceOf(current);
48
+ if (reference !== undefined)
49
+ visit(reference);
50
+ for (const branch of childrenOf(current, 'allOf'))
51
+ visit(branch);
52
+ };
53
+ visit(location);
54
+ return conjuncts;
55
+ }
56
+ /** Whether `name` matches `pattern` under the program's regular-expression dialect. */
57
+ export function matches(location, pattern, name) {
58
+ return loadValueProgram(location.program).matcher(pattern).test(name);
59
+ }
60
+ /** Whether the V1 evaluator accepts `value` at a location. */
61
+ export function accepts(location, value) {
62
+ return evaluateInstance(loadValueProgram(location.program, location.index), value).valid;
63
+ }
@@ -0,0 +1,3 @@
1
+ import type { Rule } from './assertions.js';
2
+ /** Rules for the numeric bound and divisibility keywords. */
3
+ export declare const numberRules: ReadonlyMap<string, Rule>;
@@ -0,0 +1,41 @@
1
+ import { isMultipleOf } from '../evaluate/assertions.js';
2
+ import { has } from './evidence.js';
3
+ import { FRACTION, NUMBER } from './kinds.js';
4
+ /** Rules for the numeric bound and divisibility keywords. */
5
+ export const numberRules = new Map([
6
+ [
7
+ 'maximum',
8
+ ({ value, evidence }) => typeof value !== 'number' ||
9
+ !has(evidence, NUMBER) ||
10
+ (evidence.upper !== undefined && evidence.upper.value <= value),
11
+ ],
12
+ [
13
+ 'exclusiveMaximum',
14
+ ({ value, evidence }) => typeof value !== 'number' ||
15
+ !has(evidence, NUMBER) ||
16
+ (evidence.upper !== undefined &&
17
+ (evidence.upper.value < value ||
18
+ (evidence.upper.exclusive && evidence.upper.value <= value))),
19
+ ],
20
+ [
21
+ 'minimum',
22
+ ({ value, evidence }) => typeof value !== 'number' ||
23
+ !has(evidence, NUMBER) ||
24
+ (evidence.lower !== undefined && evidence.lower.value >= value),
25
+ ],
26
+ [
27
+ 'exclusiveMinimum',
28
+ ({ value, evidence }) => typeof value !== 'number' ||
29
+ !has(evidence, NUMBER) ||
30
+ (evidence.lower !== undefined &&
31
+ (evidence.lower.value > value ||
32
+ (evidence.lower.exclusive && evidence.lower.value >= value))),
33
+ ],
34
+ [
35
+ 'multipleOf',
36
+ ({ value, evidence }) => typeof value !== 'number' ||
37
+ !has(evidence, NUMBER) ||
38
+ evidence.multipleOf.some((divisor) => isMultipleOf(divisor, value)) ||
39
+ (!has(evidence, FRACTION) && isMultipleOf(1, value)),
40
+ ],
41
+ ]);
@@ -0,0 +1,3 @@
1
+ import type { Rule } from './assertions.js';
2
+ /** Rules for the object size, required-name and property-subschema keywords. */
3
+ export declare const objectRules: ReadonlyMap<string, Rule>;
@@ -0,0 +1,190 @@
1
+ import { isValueObject as isJsonObject } from '../model/object.js';
2
+ import { has } from './evidence.js';
3
+ import { isTop } from './keywords.js';
4
+ import { OBJECT } from './kinds.js';
5
+ import { accepts, childOf, matches, schemaOf } from './location.js';
6
+ /** Rules for the object size, required-name and property-subschema keywords. */
7
+ export const objectRules = new Map([
8
+ [
9
+ 'maxProperties',
10
+ ({ inclusion, value, evidence }) => typeof value !== 'number' ||
11
+ !has(evidence, OBJECT) ||
12
+ (evidence.maxProperties !== undefined && evidence.maxProperties <= value) ||
13
+ evidence.conjuncts.some((conjunct) => {
14
+ const names = closedNames(inclusion, conjunct);
15
+ return names !== undefined && names.length <= value;
16
+ }),
17
+ ],
18
+ [
19
+ 'minProperties',
20
+ ({ value, evidence }) => typeof value !== 'number' ||
21
+ !has(evidence, OBJECT) ||
22
+ value <= 0 ||
23
+ evidence.minProperties >= value ||
24
+ evidence.required.size >= value,
25
+ ],
26
+ [
27
+ 'required',
28
+ ({ value, evidence }) => !Array.isArray(value) ||
29
+ !has(evidence, OBJECT) ||
30
+ value.every((name) => typeof name !== 'string' || evidence.required.has(name)),
31
+ ],
32
+ ['dependentRequired', dependentRequiredHolds],
33
+ ['properties', propertiesHold],
34
+ ['patternProperties', patternPropertiesHold],
35
+ ['additionalProperties', additionalHolds],
36
+ ['propertyNames', propertyNamesHold],
37
+ ['dependentSchemas', dependentSchemasHold],
38
+ ]);
39
+ function dependentRequiredHolds({ inclusion, value, evidence }) {
40
+ return (!isJsonObject(value) ||
41
+ !has(evidence, OBJECT) ||
42
+ Object.entries(value).every(([name, dependencies]) => !Array.isArray(dependencies) ||
43
+ forbids(inclusion, evidence, name) ||
44
+ dependencies.every((dependency) => typeof dependency !== 'string' ||
45
+ evidence.required.has(dependency) ||
46
+ evidence.dependentRequired.get(name)?.has(dependency) === true)));
47
+ }
48
+ function propertiesHold({ inclusion, value, outer, evidence }) {
49
+ return (!isJsonObject(value) ||
50
+ !has(evidence, OBJECT) ||
51
+ Object.keys(value).every((name) => {
52
+ const member = childOf(outer, 'properties', name);
53
+ return (member === undefined ||
54
+ isTop(schemaOf(member)) ||
55
+ memberConstraints(evidence, name).some((constraint) => inclusion.includes(member, constraint)));
56
+ }));
57
+ }
58
+ function patternPropertiesHold({ inclusion, value, outer, evidence }) {
59
+ return (!isJsonObject(value) ||
60
+ !has(evidence, OBJECT) ||
61
+ Object.keys(value).every((pattern) => {
62
+ const member = childOf(outer, 'patternProperties', pattern);
63
+ if (member === undefined || isTop(schemaOf(member)))
64
+ return true;
65
+ return evidence.conjuncts.some((conjunct) => {
66
+ const same = childOf(conjunct, 'patternProperties', pattern);
67
+ if (same !== undefined && inclusion.includes(member, same))
68
+ return true;
69
+ return coversNames(inclusion, conjunct, member, (name) => matches(outer, pattern, name));
70
+ });
71
+ }));
72
+ }
73
+ function additionalHolds({ inclusion, outer, schema, evidence }) {
74
+ const additional = childOf(outer, 'additionalProperties');
75
+ if (additional === undefined || isTop(schemaOf(additional)) || !has(evidence, OBJECT)) {
76
+ return true;
77
+ }
78
+ const declared = isJsonObject(schema.properties) ? schema.properties : {};
79
+ const patterns = isJsonObject(schema.patternProperties)
80
+ ? Object.keys(schema.patternProperties)
81
+ : [];
82
+ const isAdditional = (name) => !Object.hasOwn(declared, name) && !patterns.some((pattern) => matches(outer, pattern, name));
83
+ return evidence.conjuncts.some((conjunct) => coversNames(inclusion, conjunct, additional, isAdditional, patterns));
84
+ }
85
+ function propertyNamesHold({ inclusion, outer, evidence }) {
86
+ const names = childOf(outer, 'propertyNames');
87
+ if (names === undefined || isTop(schemaOf(names)) || !has(evidence, OBJECT))
88
+ return true;
89
+ return evidence.conjuncts.some((conjunct) => {
90
+ const own = childOf(conjunct, 'propertyNames');
91
+ if (own !== undefined && inclusion.includes(names, own))
92
+ return true;
93
+ const closed = closedNames(inclusion, conjunct);
94
+ return closed !== undefined && closed.every((name) => accepts(names, name));
95
+ });
96
+ }
97
+ function dependentSchemasHold({ inclusion, value, outer, evidence }) {
98
+ return (!isJsonObject(value) ||
99
+ !has(evidence, OBJECT) ||
100
+ Object.keys(value).every((name) => {
101
+ const dependent = childOf(outer, 'dependentSchemas', name);
102
+ if (dependent === undefined || isTop(schemaOf(dependent)))
103
+ return true;
104
+ if (forbids(inclusion, evidence, name))
105
+ return true;
106
+ if (evidence.conjuncts.some((conjunct) => inclusion.includes(dependent, conjunct))) {
107
+ return true;
108
+ }
109
+ return evidence.conjuncts.some((conjunct) => {
110
+ const own = childOf(conjunct, 'dependentSchemas', name);
111
+ return own !== undefined && inclusion.includes(dependent, own);
112
+ });
113
+ }));
114
+ }
115
+ /**
116
+ * Whether every property of a `conjunct`-admitted object whose name is `relevant` satisfies
117
+ * `required`. Names matching a pattern in `settled` are never relevant.
118
+ */
119
+ function coversNames(inclusion, conjunct, required, relevant, settled = []) {
120
+ const schema = schemaOf(conjunct);
121
+ if (!isJsonObject(schema))
122
+ return false;
123
+ const rest = childOf(conjunct, 'additionalProperties');
124
+ if (rest === undefined || !inclusion.includes(required, rest))
125
+ return false;
126
+ const declared = isJsonObject(schema.properties) ? Object.keys(schema.properties) : [];
127
+ const patterns = isJsonObject(schema.patternProperties)
128
+ ? Object.keys(schema.patternProperties)
129
+ : [];
130
+ return (declared.every((name) => {
131
+ if (!relevant(name))
132
+ return true;
133
+ const member = childOf(conjunct, 'properties', name);
134
+ return member !== undefined && inclusion.includes(required, member);
135
+ }) &&
136
+ patterns.every((pattern) => {
137
+ if (settled.includes(pattern))
138
+ return true;
139
+ const member = childOf(conjunct, 'patternProperties', pattern);
140
+ return member !== undefined && inclusion.includes(required, member);
141
+ }));
142
+ }
143
+ /** Locations one conjunct applies to the value of property `name`. */
144
+ function memberConstraints(evidence, name) {
145
+ return evidence.conjuncts.flatMap((conjunct) => {
146
+ const schema = schemaOf(conjunct);
147
+ if (!isJsonObject(schema))
148
+ return [];
149
+ const constraints = [];
150
+ const declared = isJsonObject(schema.properties) && Object.hasOwn(schema.properties, name);
151
+ if (declared) {
152
+ const member = childOf(conjunct, 'properties', name);
153
+ if (member !== undefined)
154
+ constraints.push(member);
155
+ }
156
+ let matched = false;
157
+ if (isJsonObject(schema.patternProperties)) {
158
+ for (const pattern of Object.keys(schema.patternProperties)) {
159
+ if (!matches(conjunct, pattern, name))
160
+ continue;
161
+ matched = true;
162
+ const member = childOf(conjunct, 'patternProperties', pattern);
163
+ if (member !== undefined)
164
+ constraints.push(member);
165
+ }
166
+ }
167
+ if (!declared && !matched) {
168
+ const rest = childOf(conjunct, 'additionalProperties');
169
+ if (rest !== undefined)
170
+ constraints.push(rest);
171
+ }
172
+ return constraints;
173
+ });
174
+ }
175
+ /** Whether an admitted object never has property `name`. */
176
+ function forbids(inclusion, evidence, name) {
177
+ return memberConstraints(evidence, name).some((constraint) => inclusion.evidence(constraint).kinds === 0);
178
+ }
179
+ /** The complete property-name set of a closed object conjunct, when it is one. */
180
+ function closedNames(inclusion, conjunct) {
181
+ const schema = schemaOf(conjunct);
182
+ if (!isJsonObject(schema))
183
+ return undefined;
184
+ if (isJsonObject(schema.patternProperties) && Object.keys(schema.patternProperties).length > 0)
185
+ return undefined;
186
+ const rest = childOf(conjunct, 'additionalProperties');
187
+ if (rest === undefined || inclusion.evidence(rest).kinds !== 0)
188
+ return undefined;
189
+ return isJsonObject(schema.properties) ? Object.keys(schema.properties) : [];
190
+ }
@@ -0,0 +1,3 @@
1
+ import type { Rule } from './assertions.js';
2
+ /** Rules for the string length and pattern keywords. */
3
+ export declare const stringRules: ReadonlyMap<string, Rule>;
@@ -0,0 +1,29 @@
1
+ import { has } from './evidence.js';
2
+ import { STRING } from './kinds.js';
3
+ /** Rules for the string length and pattern keywords. */
4
+ export const stringRules = new Map([
5
+ [
6
+ 'maxLength',
7
+ ({ value, evidence }) => typeof value !== 'number' ||
8
+ !has(evidence, STRING) ||
9
+ (evidence.maxLength !== undefined && evidence.maxLength <= value),
10
+ ],
11
+ [
12
+ 'minLength',
13
+ ({ value, evidence }) => typeof value !== 'number' ||
14
+ !has(evidence, STRING) ||
15
+ value <= 0 ||
16
+ evidence.minLength >= value,
17
+ ],
18
+ [
19
+ 'pattern',
20
+ ({ value, evidence }) => typeof value !== 'string' ||
21
+ !has(evidence, STRING) ||
22
+ evidence.patterns.has(value) ||
23
+ evidence.fullMatches.has(value),
24
+ ],
25
+ [
26
+ 'fullMatch',
27
+ ({ value, evidence }) => typeof value !== 'string' || !has(evidence, STRING) || evidence.fullMatches.has(value),
28
+ ],
29
+ ]);
@@ -0,0 +1,3 @@
1
+ import type { Rule } from './assertions.js';
2
+ /** Rules for the keywords that restrict the kind or the exact value. */
3
+ export declare const typeRules: ReadonlyMap<string, Rule>;
@@ -0,0 +1,11 @@
1
+ import { typeMask } from './kinds.js';
2
+ /** Rules for the keywords that restrict the kind or the exact value. */
3
+ export const typeRules = new Map([
4
+ ['type', ({ value, evidence }) => (evidence.kinds & ~typeMask(value)) === 0],
5
+ ['const', () => false],
6
+ ['enum', () => false],
7
+ [
8
+ 'x-astrale-value-kind',
9
+ ({ value, evidence }) => typeof value === 'string' && evidence.valueKinds.has(value),
10
+ ],
11
+ ]);
@@ -1,3 +1,4 @@
1
+ import type { ValueKind } from '@astrale-os/kernel-dsl/value';
1
2
  import type { ValueProgram } from './compile/model.js';
2
3
  import type { CompiledInstanceSchema } from './evaluate/types.js';
3
4
  import type { JsonSchema } from './model/schema.js';
@@ -13,3 +14,16 @@ export declare function classifyOrdering(contract: JsonSchema, registry: ValueSc
13
14
  /** Recompute the scalar ordering classification from one admitted portable value program. */
14
15
  export declare function classifyValueProgramOrdering(program: ValueProgram): OrderingKind | undefined;
15
16
  export declare function classifyPreparedOrdering(compiled: CompiledInstanceSchema): OrderingKind | undefined;
17
+ /**
18
+ * Every value kind `classifyValueKind` returns, frozen, in the order `ValueKind` names them. A kind
19
+ * added to `ValueKind` does not compile until it is listed here.
20
+ */
21
+ export declare const VALUE_KINDS: readonly ValueKind[];
22
+ /**
23
+ * The value kind of a property schema (page "Fondations Query v7", § Atoms et value kinds, Y4):
24
+ * the one kind its non-null values admit, `null` being a point of every kind. An annotated
25
+ * instant is `instant`, a finite set of strings `enum`, an array of unique items `set`; an
26
+ * ambiguous union, an object, an array with repetitions or a domain without values is `json`.
27
+ */
28
+ export declare function classifyValueKind(contract: JsonSchema, registry: ValueSchemaRegistry): ValueKind;
29
+ export declare function classifyPreparedValueKind(compiled: CompiledInstanceSchema): ValueKind;
@@ -25,7 +25,8 @@ export function classifyValueProgramOrdering(program) {
25
25
  return classifyPreparedOrdering(loadValueProgram(program));
26
26
  }
27
27
  export function classifyPreparedOrdering(compiled) {
28
- const candidates = new OrderingClassifier(compiled).classify(compiled.root);
28
+ // An enum orders as the strings it lists; a set is an array.
29
+ const candidates = new Set([...new OrderingClassifier(compiled).classify(compiled.root)].map((kind) => kind === 'enum' ? 'string' : kind === 'set' ? 'array' : kind));
29
30
  if (candidates.size !== 1)
30
31
  return undefined;
31
32
  const [kind] = candidates;
@@ -33,6 +34,51 @@ export function classifyPreparedOrdering(compiled) {
33
34
  ? kind
34
35
  : undefined;
35
36
  }
37
+ const valueKinds = {
38
+ text: true,
39
+ number: true,
40
+ instant: true,
41
+ enum: true,
42
+ boolean: true,
43
+ set: true,
44
+ json: true,
45
+ };
46
+ /**
47
+ * Every value kind `classifyValueKind` returns, frozen, in the order `ValueKind` names them. A kind
48
+ * added to `ValueKind` does not compile until it is listed here.
49
+ */
50
+ export const VALUE_KINDS = Object.freeze(Object.keys(valueKinds));
51
+ /**
52
+ * The value kind of a property schema (page "Fondations Query v7", § Atoms et value kinds, Y4):
53
+ * the one kind its non-null values admit, `null` being a point of every kind. An annotated
54
+ * instant is `instant`, a finite set of strings `enum`, an array of unique items `set`; an
55
+ * ambiguous union, an object, an array with repetitions or a domain without values is `json`.
56
+ */
57
+ export function classifyValueKind(contract, registry) {
58
+ const prepared = prepareValueSchema(contract, registry, '');
59
+ if (!prepared.compiled)
60
+ return 'json';
61
+ return classifyPreparedValueKind(prepared.value);
62
+ }
63
+ export function classifyPreparedValueKind(compiled) {
64
+ const candidates = new Set(new OrderingClassifier(compiled).classify(compiled.root));
65
+ candidates.delete('null');
66
+ if (candidates.size !== 1)
67
+ return 'json';
68
+ const [kind] = candidates;
69
+ switch (kind) {
70
+ case 'string':
71
+ return 'text';
72
+ case 'number':
73
+ case 'boolean':
74
+ case 'instant':
75
+ case 'enum':
76
+ case 'set':
77
+ return kind;
78
+ default:
79
+ return 'json';
80
+ }
81
+ }
36
82
  class OrderingClassifier {
37
83
  compiled;
38
84
  active = new Set();
@@ -72,6 +118,9 @@ class OrderingClassifier {
72
118
  const declaredTypes = typesOf(schema.type);
73
119
  if (declaredTypes !== undefined)
74
120
  candidates = intersect(candidates, declaredTypes);
121
+ if (schema.uniqueItems === true) {
122
+ candidates = new Set([...candidates].map((kind) => (kind === 'array' ? 'set' : kind)));
123
+ }
75
124
  if (Object.hasOwn(schema, 'const')) {
76
125
  candidates = intersect(candidates, new Set([kindOfValue(schema.const)]));
77
126
  }
@@ -130,6 +179,7 @@ function typeSet(types) {
130
179
  }
131
180
  return result;
132
181
  }
182
+ /** The kind of one listed value: a listed string belongs to a finite set of strings. */
133
183
  function kindOfValue(value) {
134
184
  if (value === null)
135
185
  return 'null';
@@ -141,18 +191,35 @@ function kindOfValue(value) {
141
191
  return 'boolean';
142
192
  if (typeof value === 'number')
143
193
  return 'number';
144
- return 'string';
194
+ return 'enum';
145
195
  }
196
+ /** The kind both admit: equal kinds, or the narrower of a kind and one of its subsets. */
197
+ function meet(left, right) {
198
+ if (left === right)
199
+ return left;
200
+ for (const [narrow, wide] of NARROWER) {
201
+ if ((left === narrow && right === wide) || (left === wide && right === narrow))
202
+ return narrow;
203
+ }
204
+ return undefined;
205
+ }
206
+ /**
207
+ * The narrower kind of two that meet: a subset and its kind, and an enum of instants, which
208
+ * orders as instants.
209
+ */
210
+ const NARROWER = [
211
+ ['instant', 'string'],
212
+ ['enum', 'string'],
213
+ ['instant', 'enum'],
214
+ ['set', 'array'],
215
+ ];
146
216
  function intersect(left, right) {
147
217
  const result = new Set();
148
218
  for (const leftKind of left) {
149
219
  for (const rightKind of right) {
150
- if (leftKind === rightKind)
151
- result.add(leftKind);
152
- else if ((leftKind === 'string' && rightKind === 'instant') ||
153
- (leftKind === 'instant' && rightKind === 'string')) {
154
- result.add('instant');
155
- }
220
+ const kind = meet(leftKind, rightKind);
221
+ if (kind !== undefined)
222
+ result.add(kind);
156
223
  }
157
224
  }
158
225
  return normalize(result);
@@ -160,9 +227,19 @@ function intersect(left, right) {
160
227
  function union(left, right) {
161
228
  return normalize(new Set([...left, ...right]));
162
229
  }
230
+ /** A kind absorbs its subsets; strings of two subsets are strings. */
163
231
  function normalize(input) {
164
- if (input.has('string'))
232
+ if (input.has('enum') && input.has('instant')) {
233
+ input.delete('enum');
165
234
  input.delete('instant');
235
+ input.add('string');
236
+ }
237
+ if (input.has('string')) {
238
+ input.delete('instant');
239
+ input.delete('enum');
240
+ }
241
+ if (input.has('array'))
242
+ input.delete('set');
166
243
  return input;
167
244
  }
168
245
  function schemaList(input) {