@versionzero/schema 1.0.0

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 (401) hide show
  1. package/LICENSE +177 -0
  2. package/README.md +246 -0
  3. package/package.json +84 -0
  4. package/src/compilation/handler-compilation.js +28 -0
  5. package/src/compilation/metadata-compilation.js +35 -0
  6. package/src/compilation/schema-compilation.js +142 -0
  7. package/src/compilation/selection-compilation.js +84 -0
  8. package/src/compilation/union-compilation.js +510 -0
  9. package/src/compilation/values-compilation.js +35 -0
  10. package/src/compiled-schema.js +1709 -0
  11. package/src/constants.js +1 -0
  12. package/src/core-library/index.js +32 -0
  13. package/src/core-library/processors/aggregation-operators.js +75 -0
  14. package/src/core-library/processors/alpha-constraint.js +20 -0
  15. package/src/core-library/processors/alphanum-constraint.js +20 -0
  16. package/src/core-library/processors/array-operator.js +51 -0
  17. package/src/core-library/processors/assert-constraint.js +75 -0
  18. package/src/core-library/processors/base64-constraint.js +26 -0
  19. package/src/core-library/processors/camel-case-operator.js +24 -0
  20. package/src/core-library/processors/capitalize-operator.js +16 -0
  21. package/src/core-library/processors/cardnum-constraint.js +193 -0
  22. package/src/core-library/processors/ceil-operator.js +44 -0
  23. package/src/core-library/processors/collapse-operator.js +29 -0
  24. package/src/core-library/processors/compact-operator.js +34 -0
  25. package/src/core-library/processors/compile-operator.js +65 -0
  26. package/src/core-library/processors/concat-operator.js +51 -0
  27. package/src/core-library/processors/conditional-operators.js +301 -0
  28. package/src/core-library/processors/constant-case-operator.js +16 -0
  29. package/src/core-library/processors/data-size-operator.js +86 -0
  30. package/src/core-library/processors/date-object-operator.js +54 -0
  31. package/src/core-library/processors/date-operator.js +67 -0
  32. package/src/core-library/processors/date-range-constraint.js +76 -0
  33. package/src/core-library/processors/defined-constraint.js +30 -0
  34. package/src/core-library/processors/each-operator.js +57 -0
  35. package/src/core-library/processors/email-constraint.js +112 -0
  36. package/src/core-library/processors/entries-operator.js +25 -0
  37. package/src/core-library/processors/eq-constraint.js +37 -0
  38. package/src/core-library/processors/filter-operator.js +74 -0
  39. package/src/core-library/processors/find-schema-operator.js +45 -0
  40. package/src/core-library/processors/flatten-operator.js +40 -0
  41. package/src/core-library/processors/floor-operator.js +47 -0
  42. package/src/core-library/processors/get-operator.js +44 -0
  43. package/src/core-library/processors/group-by-operator.js +84 -0
  44. package/src/core-library/processors/has-prefix-constraint.js +37 -0
  45. package/src/core-library/processors/has-suffix-constraint.js +35 -0
  46. package/src/core-library/processors/hex-constraint.js +20 -0
  47. package/src/core-library/processors/hostname-constraint.js +22 -0
  48. package/src/core-library/processors/http-url-constraint.js +27 -0
  49. package/src/core-library/processors/in-constraint.js +66 -0
  50. package/src/core-library/processors/index-by-operator.js +98 -0
  51. package/src/core-library/processors/index.js +131 -0
  52. package/src/core-library/processors/input-operator.js +23 -0
  53. package/src/core-library/processors/instanceof-constraint.js +38 -0
  54. package/src/core-library/processors/integer-constraint.js +22 -0
  55. package/src/core-library/processors/invoke-operator.js +33 -0
  56. package/src/core-library/processors/ipv4-constraint.js +188 -0
  57. package/src/core-library/processors/ipv6-constraint.js +205 -0
  58. package/src/core-library/processors/is-array-constraint.js +21 -0
  59. package/src/core-library/processors/is-date-constraint.js +22 -0
  60. package/src/core-library/processors/is-number-constraint.js +21 -0
  61. package/src/core-library/processors/is-object-constraint.js +21 -0
  62. package/src/core-library/processors/is-string-constraint.js +21 -0
  63. package/src/core-library/processors/join-operator.js +41 -0
  64. package/src/core-library/processors/json-constraint.js +22 -0
  65. package/src/core-library/processors/json-decode-operator.js +25 -0
  66. package/src/core-library/processors/json-encode-operator.js +35 -0
  67. package/src/core-library/processors/kebab-case-operator.js +23 -0
  68. package/src/core-library/processors/keys-operator.js +20 -0
  69. package/src/core-library/processors/length-constraint.js +85 -0
  70. package/src/core-library/processors/lookup-operator.js +84 -0
  71. package/src/core-library/processors/lowercase-operator.js +14 -0
  72. package/src/core-library/processors/map-operator.js +84 -0
  73. package/src/core-library/processors/match-operator.js +64 -0
  74. package/src/core-library/processors/matches-constraint.js +54 -0
  75. package/src/core-library/processors/math-operators.js +151 -0
  76. package/src/core-library/processors/merge-deep-operator.js +61 -0
  77. package/src/core-library/processors/merge-operator.js +54 -0
  78. package/src/core-library/processors/metadata-operator.js +100 -0
  79. package/src/core-library/processors/negative-constraint.js +23 -0
  80. package/src/core-library/processors/never-constraint.js +69 -0
  81. package/src/core-library/processors/non-empty-constraint.js +59 -0
  82. package/src/core-library/processors/not-constraint.js +71 -0
  83. package/src/core-library/processors/number-operator.js +24 -0
  84. package/src/core-library/processors/numeric-constraint.js +22 -0
  85. package/src/core-library/processors/object-operator.js +38 -0
  86. package/src/core-library/processors/omit-operator.js +57 -0
  87. package/src/core-library/processors/parallel-operator.js +64 -0
  88. package/src/core-library/processors/pascal-case-operator.js +16 -0
  89. package/src/core-library/processors/phone-constraint.js +235 -0
  90. package/src/core-library/processors/pick-operator.js +62 -0
  91. package/src/core-library/processors/pipeline-operator.js +63 -0
  92. package/src/core-library/processors/port-constraint.js +22 -0
  93. package/src/core-library/processors/positive-constraint.js +23 -0
  94. package/src/core-library/processors/process-operator.js +55 -0
  95. package/src/core-library/processors/property-operator.js +49 -0
  96. package/src/core-library/processors/range-constraint.js +72 -0
  97. package/src/core-library/processors/reference-operator.js +79 -0
  98. package/src/core-library/processors/require-constraint.js +74 -0
  99. package/src/core-library/processors/reverse-operator.js +20 -0
  100. package/src/core-library/processors/round-operator.js +53 -0
  101. package/src/core-library/processors/schema-handler-operators.js +54 -0
  102. package/src/core-library/processors/semver-constraint.js +282 -0
  103. package/src/core-library/processors/sequence-processors.js +406 -0
  104. package/src/core-library/processors/sort-operator.js +52 -0
  105. package/src/core-library/processors/split-operator.js +43 -0
  106. package/src/core-library/processors/string-extra-operators.js +141 -0
  107. package/src/core-library/processors/string-operator.js +34 -0
  108. package/src/core-library/processors/target-operator.js +30 -0
  109. package/src/core-library/processors/template-operator.js +60 -0
  110. package/src/core-library/processors/title-case-operator.js +17 -0
  111. package/src/core-library/processors/trim-operator.js +14 -0
  112. package/src/core-library/processors/truthy-constraint.js +35 -0
  113. package/src/core-library/processors/type-operator.js +24 -0
  114. package/src/core-library/processors/unique-operator.js +21 -0
  115. package/src/core-library/processors/uppercase-operator.js +14 -0
  116. package/src/core-library/processors/url-constraint.js +31 -0
  117. package/src/core-library/processors/url-decode-operator.js +50 -0
  118. package/src/core-library/processors/url-encode-operator.js +44 -0
  119. package/src/core-library/processors/uuid-constraint.js +31 -0
  120. package/src/core-library/processors/values-operator.js +20 -0
  121. package/src/core-library/schemas/any-schema.js +23 -0
  122. package/src/core-library/schemas/array-schema.js +8 -0
  123. package/src/core-library/schemas/boolean-schema.js +10 -0
  124. package/src/core-library/schemas/date-schema.js +12 -0
  125. package/src/core-library/schemas/function-schema.js +40 -0
  126. package/src/core-library/schemas/number-schema.js +9 -0
  127. package/src/core-library/schemas/object-schema.js +10 -0
  128. package/src/core-library/schemas/root-schema.js +21 -0
  129. package/src/core-library/schemas/string-schema.js +9 -0
  130. package/src/core-library-node/index.js +47 -0
  131. package/src/core-library-node/processors/base64-decode-operator.js +20 -0
  132. package/src/core-library-node/processors/base64-encode-operator.js +20 -0
  133. package/src/core-library-node/processors/buffer-operator.js +39 -0
  134. package/src/core-library-node/processors/directory-constraint.js +35 -0
  135. package/src/core-library-node/processors/executable-constraint.js +34 -0
  136. package/src/core-library-node/processors/file-constraint.js +34 -0
  137. package/src/core-library-node/processors/file-size-constraint.js +94 -0
  138. package/src/core-library-node/processors/is-buffer-constraint.js +21 -0
  139. package/src/core-library-node/processors/reachable-constraint.js +28 -0
  140. package/src/core-library-node/processors/readable-constraint.js +34 -0
  141. package/src/core-library-node/processors/writable-constraint.js +59 -0
  142. package/src/core-library-node/schemas/buffer-schema.js +10 -0
  143. package/src/errors.js +209 -0
  144. package/src/executor/array-executor.js +78 -0
  145. package/src/executor/conditional-executor.js +134 -0
  146. package/src/executor/each-executor.js +68 -0
  147. package/src/executor/executor.js +123 -0
  148. package/src/executor/object-executor.js +98 -0
  149. package/src/executor/parallel-executor.js +43 -0
  150. package/src/executor/pipeline-executor.js +65 -0
  151. package/src/executor/sequence-executor.js +206 -0
  152. package/src/executor/serial-executor.js +24 -0
  153. package/src/executor/step-executor.js +68 -0
  154. package/src/helpers/case.js +124 -0
  155. package/src/helpers/data-size.js +144 -0
  156. package/src/helpers/debug-sink.js +15 -0
  157. package/src/helpers/deep.js +280 -0
  158. package/src/helpers/format.js +121 -0
  159. package/src/helpers/has-string-properties.js +30 -0
  160. package/src/helpers/index.js +16 -0
  161. package/src/helpers/object.js +115 -0
  162. package/src/helpers/parse-date.js +75 -0
  163. package/src/helpers/path.js +28 -0
  164. package/src/helpers/regex.js +18 -0
  165. package/src/helpers/stringify.js +309 -0
  166. package/src/helpers/to-data.js +64 -0
  167. package/src/helpers/truthy.js +55 -0
  168. package/src/index.js +29 -0
  169. package/src/schema-compiler.js +531 -0
  170. package/src/schema-location.js +200 -0
  171. package/src/schema-resolver.js +546 -0
  172. package/src/schema.js +1182 -0
  173. package/src/traversal/executors/check-condition.js +42 -0
  174. package/src/traversal/executors/check-input.js +27 -0
  175. package/src/traversal/executors/check-required.js +19 -0
  176. package/src/traversal/executors/check-schema.js +45 -0
  177. package/src/traversal/executors/defaults.js +21 -0
  178. package/src/traversal/executors/enter-existing.js +25 -0
  179. package/src/traversal/executors/enter-input.js +25 -0
  180. package/src/traversal/executors/enter.js +37 -0
  181. package/src/traversal/executors/exit.js +74 -0
  182. package/src/traversal/executors/finalize.js +64 -0
  183. package/src/traversal/executors/index.js +42 -0
  184. package/src/traversal/executors/normalize.js +38 -0
  185. package/src/traversal/executors/prepare-existing.js +27 -0
  186. package/src/traversal/executors/prepare-pending.js +54 -0
  187. package/src/traversal/executors/resolve-union.js +50 -0
  188. package/src/traversal/executors/serialize.js +48 -0
  189. package/src/traversal/executors/transform-early.js +51 -0
  190. package/src/traversal/executors/transform.js +68 -0
  191. package/src/traversal/executors/traversal-state-executor.js +46 -0
  192. package/src/traversal/executors/validate.js +63 -0
  193. package/src/traversal/traversal-context.js +231 -0
  194. package/src/traversal/traversal-state.js +809 -0
  195. package/src/types.js +102 -0
  196. package/src/value-processor/composed-value-processor.js +43 -0
  197. package/src/value-processor/defined-value-processor.js +72 -0
  198. package/src/value-processor/function-value-processor.js +68 -0
  199. package/src/value-processor/parameterized-value-processor.js +45 -0
  200. package/src/value-processor/parameters-value-processor.js +178 -0
  201. package/src/value-processor/spec.js +89 -0
  202. package/src/value-processor/value-processor.js +105 -0
  203. package/types/compilation/handler-compilation.d.ts +13 -0
  204. package/types/compilation/metadata-compilation.d.ts +6 -0
  205. package/types/compilation/schema-compilation.d.ts +32 -0
  206. package/types/compilation/selection-compilation.d.ts +9 -0
  207. package/types/compilation/union-compilation.d.ts +42 -0
  208. package/types/compilation/values-compilation.d.ts +12 -0
  209. package/types/compiled-schema.d.ts +883 -0
  210. package/types/constants.d.ts +1 -0
  211. package/types/core-library/index.d.ts +7 -0
  212. package/types/core-library/processors/aggregation-operators.d.ts +24 -0
  213. package/types/core-library/processors/alpha-constraint.d.ts +9 -0
  214. package/types/core-library/processors/alphanum-constraint.d.ts +9 -0
  215. package/types/core-library/processors/array-operator.d.ts +12 -0
  216. package/types/core-library/processors/assert-constraint.d.ts +30 -0
  217. package/types/core-library/processors/base64-constraint.d.ts +11 -0
  218. package/types/core-library/processors/camel-case-operator.d.ts +17 -0
  219. package/types/core-library/processors/capitalize-operator.d.ts +11 -0
  220. package/types/core-library/processors/cardnum-constraint.d.ts +51 -0
  221. package/types/core-library/processors/ceil-operator.d.ts +30 -0
  222. package/types/core-library/processors/collapse-operator.d.ts +24 -0
  223. package/types/core-library/processors/compact-operator.d.ts +29 -0
  224. package/types/core-library/processors/compile-operator.d.ts +34 -0
  225. package/types/core-library/processors/concat-operator.d.ts +23 -0
  226. package/types/core-library/processors/conditional-operators.d.ts +219 -0
  227. package/types/core-library/processors/constant-case-operator.d.ts +9 -0
  228. package/types/core-library/processors/data-size-operator.d.ts +31 -0
  229. package/types/core-library/processors/date-object-operator.d.ts +16 -0
  230. package/types/core-library/processors/date-operator.d.ts +21 -0
  231. package/types/core-library/processors/date-range-constraint.d.ts +26 -0
  232. package/types/core-library/processors/defined-constraint.d.ts +20 -0
  233. package/types/core-library/processors/each-operator.d.ts +34 -0
  234. package/types/core-library/processors/email-constraint.d.ts +54 -0
  235. package/types/core-library/processors/entries-operator.d.ts +13 -0
  236. package/types/core-library/processors/eq-constraint.d.ts +20 -0
  237. package/types/core-library/processors/filter-operator.d.ts +35 -0
  238. package/types/core-library/processors/find-schema-operator.d.ts +28 -0
  239. package/types/core-library/processors/flatten-operator.d.ts +26 -0
  240. package/types/core-library/processors/floor-operator.d.ts +33 -0
  241. package/types/core-library/processors/get-operator.d.ts +31 -0
  242. package/types/core-library/processors/group-by-operator.d.ts +36 -0
  243. package/types/core-library/processors/has-prefix-constraint.d.ts +22 -0
  244. package/types/core-library/processors/has-suffix-constraint.d.ts +20 -0
  245. package/types/core-library/processors/hex-constraint.d.ts +9 -0
  246. package/types/core-library/processors/hostname-constraint.d.ts +11 -0
  247. package/types/core-library/processors/http-url-constraint.d.ts +9 -0
  248. package/types/core-library/processors/in-constraint.d.ts +27 -0
  249. package/types/core-library/processors/index-by-operator.d.ts +26 -0
  250. package/types/core-library/processors/index.d.ts +8 -0
  251. package/types/core-library/processors/input-operator.d.ts +20 -0
  252. package/types/core-library/processors/instanceof-constraint.d.ts +23 -0
  253. package/types/core-library/processors/integer-constraint.d.ts +9 -0
  254. package/types/core-library/processors/invoke-operator.d.ts +12 -0
  255. package/types/core-library/processors/ipv4-constraint.d.ts +37 -0
  256. package/types/core-library/processors/ipv6-constraint.d.ts +34 -0
  257. package/types/core-library/processors/is-array-constraint.d.ts +10 -0
  258. package/types/core-library/processors/is-date-constraint.d.ts +10 -0
  259. package/types/core-library/processors/is-number-constraint.d.ts +10 -0
  260. package/types/core-library/processors/is-object-constraint.d.ts +10 -0
  261. package/types/core-library/processors/is-string-constraint.d.ts +10 -0
  262. package/types/core-library/processors/join-operator.d.ts +29 -0
  263. package/types/core-library/processors/json-constraint.d.ts +10 -0
  264. package/types/core-library/processors/json-decode-operator.d.ts +9 -0
  265. package/types/core-library/processors/json-encode-operator.d.ts +27 -0
  266. package/types/core-library/processors/kebab-case-operator.d.ts +16 -0
  267. package/types/core-library/processors/keys-operator.d.ts +9 -0
  268. package/types/core-library/processors/length-constraint.d.ts +34 -0
  269. package/types/core-library/processors/lookup-operator.d.ts +36 -0
  270. package/types/core-library/processors/lowercase-operator.d.ts +9 -0
  271. package/types/core-library/processors/map-operator.d.ts +38 -0
  272. package/types/core-library/processors/match-operator.d.ts +34 -0
  273. package/types/core-library/processors/matches-constraint.d.ts +29 -0
  274. package/types/core-library/processors/math-operators.d.ts +91 -0
  275. package/types/core-library/processors/merge-deep-operator.d.ts +32 -0
  276. package/types/core-library/processors/merge-operator.d.ts +26 -0
  277. package/types/core-library/processors/metadata-operator.d.ts +56 -0
  278. package/types/core-library/processors/negative-constraint.d.ts +13 -0
  279. package/types/core-library/processors/never-constraint.d.ts +26 -0
  280. package/types/core-library/processors/non-empty-constraint.d.ts +28 -0
  281. package/types/core-library/processors/not-constraint.d.ts +28 -0
  282. package/types/core-library/processors/number-operator.d.ts +9 -0
  283. package/types/core-library/processors/numeric-constraint.d.ts +10 -0
  284. package/types/core-library/processors/object-operator.d.ts +10 -0
  285. package/types/core-library/processors/omit-operator.d.ts +24 -0
  286. package/types/core-library/processors/parallel-operator.d.ts +41 -0
  287. package/types/core-library/processors/pascal-case-operator.d.ts +9 -0
  288. package/types/core-library/processors/phone-constraint.d.ts +65 -0
  289. package/types/core-library/processors/pick-operator.d.ts +27 -0
  290. package/types/core-library/processors/pipeline-operator.d.ts +40 -0
  291. package/types/core-library/processors/port-constraint.d.ts +11 -0
  292. package/types/core-library/processors/positive-constraint.d.ts +13 -0
  293. package/types/core-library/processors/process-operator.d.ts +37 -0
  294. package/types/core-library/processors/property-operator.d.ts +34 -0
  295. package/types/core-library/processors/range-constraint.d.ts +30 -0
  296. package/types/core-library/processors/reference-operator.d.ts +38 -0
  297. package/types/core-library/processors/require-constraint.d.ts +29 -0
  298. package/types/core-library/processors/reverse-operator.d.ts +9 -0
  299. package/types/core-library/processors/round-operator.d.ts +34 -0
  300. package/types/core-library/processors/schema-handler-operators.d.ts +28 -0
  301. package/types/core-library/processors/semver-constraint.d.ts +43 -0
  302. package/types/core-library/processors/sequence-processors.d.ts +213 -0
  303. package/types/core-library/processors/sort-operator.d.ts +31 -0
  304. package/types/core-library/processors/split-operator.d.ts +33 -0
  305. package/types/core-library/processors/string-extra-operators.d.ts +83 -0
  306. package/types/core-library/processors/string-operator.d.ts +10 -0
  307. package/types/core-library/processors/target-operator.d.ts +27 -0
  308. package/types/core-library/processors/template-operator.d.ts +31 -0
  309. package/types/core-library/processors/title-case-operator.d.ts +12 -0
  310. package/types/core-library/processors/trim-operator.d.ts +9 -0
  311. package/types/core-library/processors/truthy-constraint.d.ts +23 -0
  312. package/types/core-library/processors/type-operator.d.ts +11 -0
  313. package/types/core-library/processors/unique-operator.d.ts +10 -0
  314. package/types/core-library/processors/uppercase-operator.d.ts +9 -0
  315. package/types/core-library/processors/url-constraint.d.ts +20 -0
  316. package/types/core-library/processors/url-decode-operator.d.ts +31 -0
  317. package/types/core-library/processors/url-encode-operator.d.ts +36 -0
  318. package/types/core-library/processors/uuid-constraint.d.ts +20 -0
  319. package/types/core-library/processors/values-operator.d.ts +9 -0
  320. package/types/core-library/schemas/any-schema.d.ts +2 -0
  321. package/types/core-library/schemas/array-schema.d.ts +2 -0
  322. package/types/core-library/schemas/boolean-schema.d.ts +2 -0
  323. package/types/core-library/schemas/date-schema.d.ts +2 -0
  324. package/types/core-library/schemas/function-schema.d.ts +2 -0
  325. package/types/core-library/schemas/number-schema.d.ts +2 -0
  326. package/types/core-library/schemas/object-schema.d.ts +2 -0
  327. package/types/core-library/schemas/root-schema.d.ts +2 -0
  328. package/types/core-library/schemas/string-schema.d.ts +2 -0
  329. package/types/core-library-node/index.d.ts +12 -0
  330. package/types/core-library-node/processors/base64-decode-operator.d.ts +9 -0
  331. package/types/core-library-node/processors/base64-encode-operator.d.ts +9 -0
  332. package/types/core-library-node/processors/buffer-operator.d.ts +15 -0
  333. package/types/core-library-node/processors/directory-constraint.d.ts +14 -0
  334. package/types/core-library-node/processors/executable-constraint.d.ts +17 -0
  335. package/types/core-library-node/processors/file-constraint.d.ts +13 -0
  336. package/types/core-library-node/processors/file-size-constraint.d.ts +43 -0
  337. package/types/core-library-node/processors/is-buffer-constraint.d.ts +10 -0
  338. package/types/core-library-node/processors/reachable-constraint.d.ts +13 -0
  339. package/types/core-library-node/processors/readable-constraint.d.ts +17 -0
  340. package/types/core-library-node/processors/writable-constraint.d.ts +18 -0
  341. package/types/core-library-node/schemas/buffer-schema.d.ts +2 -0
  342. package/types/errors.d.ts +58 -0
  343. package/types/executor/array-executor.d.ts +17 -0
  344. package/types/executor/conditional-executor.d.ts +45 -0
  345. package/types/executor/each-executor.d.ts +15 -0
  346. package/types/executor/executor.d.ts +84 -0
  347. package/types/executor/object-executor.d.ts +14 -0
  348. package/types/executor/parallel-executor.d.ts +27 -0
  349. package/types/executor/pipeline-executor.d.ts +11 -0
  350. package/types/executor/sequence-executor.d.ts +32 -0
  351. package/types/executor/serial-executor.d.ts +16 -0
  352. package/types/executor/step-executor.d.ts +14 -0
  353. package/types/helpers/case.d.ts +30 -0
  354. package/types/helpers/data-size.d.ts +25 -0
  355. package/types/helpers/debug-sink.d.ts +9 -0
  356. package/types/helpers/deep.d.ts +33 -0
  357. package/types/helpers/format.d.ts +14 -0
  358. package/types/helpers/has-string-properties.d.ts +5 -0
  359. package/types/helpers/index.d.ts +13 -0
  360. package/types/helpers/object.d.ts +46 -0
  361. package/types/helpers/parse-date.d.ts +6 -0
  362. package/types/helpers/path.d.ts +13 -0
  363. package/types/helpers/regex.d.ts +7 -0
  364. package/types/helpers/stringify.d.ts +33 -0
  365. package/types/helpers/to-data.d.ts +13 -0
  366. package/types/helpers/truthy.d.ts +26 -0
  367. package/types/index.d.ts +6 -0
  368. package/types/schema-compiler.d.ts +49 -0
  369. package/types/schema-location.d.ts +64 -0
  370. package/types/schema-resolver.d.ts +145 -0
  371. package/types/schema.d.ts +586 -0
  372. package/types/traversal/executors/check-condition.d.ts +8 -0
  373. package/types/traversal/executors/check-input.d.ts +6 -0
  374. package/types/traversal/executors/check-required.d.ts +6 -0
  375. package/types/traversal/executors/check-schema.d.ts +7 -0
  376. package/types/traversal/executors/defaults.d.ts +8 -0
  377. package/types/traversal/executors/enter-existing.d.ts +6 -0
  378. package/types/traversal/executors/enter-input.d.ts +8 -0
  379. package/types/traversal/executors/enter.d.ts +7 -0
  380. package/types/traversal/executors/exit.d.ts +6 -0
  381. package/types/traversal/executors/finalize.d.ts +6 -0
  382. package/types/traversal/executors/index.d.ts +15 -0
  383. package/types/traversal/executors/normalize.d.ts +7 -0
  384. package/types/traversal/executors/prepare-existing.d.ts +6 -0
  385. package/types/traversal/executors/prepare-pending.d.ts +6 -0
  386. package/types/traversal/executors/resolve-union.d.ts +6 -0
  387. package/types/traversal/executors/serialize.d.ts +11 -0
  388. package/types/traversal/executors/transform-early.d.ts +6 -0
  389. package/types/traversal/executors/transform.d.ts +6 -0
  390. package/types/traversal/executors/traversal-state-executor.d.ts +19 -0
  391. package/types/traversal/executors/validate.d.ts +6 -0
  392. package/types/traversal/traversal-context.d.ts +67 -0
  393. package/types/traversal/traversal-state.d.ts +97 -0
  394. package/types/types.d.ts +218 -0
  395. package/types/value-processor/composed-value-processor.d.ts +17 -0
  396. package/types/value-processor/defined-value-processor.d.ts +16 -0
  397. package/types/value-processor/function-value-processor.d.ts +15 -0
  398. package/types/value-processor/parameterized-value-processor.d.ts +14 -0
  399. package/types/value-processor/parameters-value-processor.d.ts +28 -0
  400. package/types/value-processor/spec.d.ts +22 -0
  401. package/types/value-processor/value-processor.d.ts +92 -0
package/src/schema.js ADDED
@@ -0,0 +1,1182 @@
1
+ import { toData } from './helpers/to-data.js';
2
+ import { CompiledSchema } from './compiled-schema.js';
3
+ import { SchemaError } from './errors.js';
4
+ import { deepValue } from './helpers/deep.js';
5
+
6
+ /** @import { ValueProcessor, ValueProcessorFunction, ValueProcessorSpec } from './value-processor/value-processor.js' */
7
+ /** @import { ISchemaMetadata, ISchemaOptions, SchemaData, ISchema, } from './types.js' */
8
+
9
+ /** @typedef {ISchemaOptions} SchemaOptions */
10
+ /** @typedef {ISchemaMetadata} SchemaMetadata */
11
+ /** @typedef {{[key:string]: ISchema}} SchemaProperties */
12
+ /** @typedef {{[key:string]: ISchema}} SchemaUnionSchemas */
13
+
14
+ /**
15
+ * @typedef {object} SchemaHandlers
16
+ * @property {Array<ValueProcessorSpec>} [normalizers]
17
+ * @property {Array<ValueProcessorSpec>} [transformers]
18
+ * @property {Array<ValueProcessorSpec>} [finalizers]
19
+ * @property {Array<ValueProcessorSpec>} [validators]
20
+ * @property {Array<ValueProcessorSpec>} [serializers]
21
+ * @property {Array<ValueProcessorSpec>} [conditions]
22
+ * @property {Array<ValueProcessorSpec>} [discriminators]
23
+ */
24
+
25
+ /**
26
+ * Schema - allows the definition of structure and rules for data
27
+ *
28
+ * Essentially acts as a fluent builder; must be compiled by SchemaCompiler for use.
29
+ *
30
+ * @typedef {import("./types.js").ISchema} ISchema
31
+ * @augments {ISchema}
32
+ */
33
+ export class Schema
34
+ {
35
+ /** @type {string|undefined} */
36
+ #base;
37
+
38
+ /** @type {SchemaProperties} */
39
+ #properties = {};
40
+
41
+ /** @type {SchemaHandlers} */
42
+ #handlers = {};
43
+
44
+ /** @type {SchemaOptions} */
45
+ #options = {};
46
+
47
+ /** @type {ISchemaMetadata} */
48
+ #metadata = {};
49
+
50
+ /**@type {SchemaUnionSchemas} */
51
+ #unionSchemas = {};
52
+
53
+ /**
54
+ * Construct a Schema.
55
+ *
56
+ * Pass a string name of a registered schema to resolve as the base, or pass a schema-shaped object to extend.
57
+ *
58
+ * Prefer the fluent setters over passing in options/metadata or attributes.
59
+ *
60
+ * @param {string|ISchema|SchemaData} [base] - schema type or base to extend
61
+ * @param {object} [options] - schema options (also supports "attribute" shorthand syntax, but prefer being explicit)
62
+ * @param {ISchemaMetadata} [metadata] - schema metadata
63
+ */
64
+ constructor(base, options, metadata) {
65
+ if (typeof base === 'string') {
66
+ this.base = base;
67
+ if (options) {
68
+ this._setAttributes(options);
69
+ }
70
+ if (metadata) {
71
+ this.addMetadata(metadata);
72
+ }
73
+ }
74
+ else if ((base instanceof Schema) || (base instanceof CompiledSchema)) {
75
+ this.extend(base);
76
+ if (base instanceof Schema) {
77
+ this.base = base.base;
78
+ }
79
+ // Apply additional options/metadata after extending
80
+ if (options) {
81
+ this._setAttributes(options);
82
+ }
83
+ if (metadata) {
84
+ this.addMetadata(metadata);
85
+ }
86
+ }
87
+ else if (typeof base === 'object' && base !== null) {
88
+ // It's a SchemaData/ISchema object
89
+ this.extend(base);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Name of a schema registered in SchemaResolver that this schema extends
95
+ *
96
+ * @type {string|undefined}
97
+ */
98
+ get base() {
99
+ return this.#base;
100
+ }
101
+ set base(base) {
102
+ this.#base = base;
103
+ }
104
+
105
+ /**
106
+ * Properties are named child schemas.
107
+ *
108
+ * Use the property setter rather than direct access to ensure data consistency.
109
+ *
110
+ * @type {SchemaProperties}
111
+ */
112
+ get properties() {
113
+ return this.#properties;
114
+ }
115
+
116
+ /**
117
+ * Handlers are grouped lists of value processors.
118
+ *
119
+ * Assign using the individual value processor setters.
120
+ *
121
+ * @type {SchemaHandlers}
122
+ */
123
+ get handlers() {
124
+ return this.#handlers;
125
+ }
126
+
127
+ /**
128
+ * Options are settings that define how the schema behaves.
129
+ *
130
+ * @returns {SchemaOptions}
131
+ */
132
+ get options() {
133
+ return this.#options;
134
+ }
135
+
136
+ /**
137
+ * Metadata defines settings that describe how the schema should interact with users.
138
+ *
139
+ * @type {SchemaMetadata}
140
+ */
141
+ get metadata() {
142
+ return this.#metadata;
143
+ }
144
+
145
+ /**
146
+ * Unions are sets of alternative schemas; a discriminator selects which to use.
147
+ *
148
+ * @type {SchemaUnionSchemas}
149
+ */
150
+ get unionSchemas() {
151
+ return this.#unionSchemas; // overridden just for type narrowing
152
+ }
153
+
154
+ /**
155
+ * Extract the contents of this schema and its children as a regular object.
156
+ *
157
+ * @returns {SchemaData}
158
+ */
159
+ toData() {
160
+ return toData(this);
161
+ }
162
+
163
+ /**
164
+ * Attributes were initially a convenient shorthand for constructing schemas, but now just add confusion.
165
+ *
166
+ * @deprecated
167
+ * @param {string} attributeName
168
+ * @param {any} attributeValue
169
+ * @returns {Schema}
170
+ * @internal
171
+ */
172
+ _setAttribute(attributeName, attributeValue) {
173
+ if (attributeName === 'base') {
174
+ this.base = attributeValue;
175
+ return this;
176
+ }
177
+ else if (attributeName === 'metadata') {
178
+ return this.addMetadata(attributeValue);
179
+ }
180
+ else if (attributeName === 'options') {
181
+ return this.addOptions(attributeValue ?? {});
182
+ }
183
+ else if (attributeName === 'handlers') {
184
+ return this.addHandlers(attributeValue ?? {});
185
+ }
186
+ else if (attributeName === 'properties') {
187
+ return this.addProperties(attributeValue ?? {});
188
+ }
189
+ else if (attributeName === 'unionSchemas') {
190
+ return this.addUnionSchemas(attributeValue ?? {})
191
+ }
192
+ else if (Object.getPrototypeOf(this)?.hasOwnProperty(attributeName)) {
193
+ if (!(typeof this[attributeName] === 'function')) {
194
+ throw new SchemaError('Unknown attribute!')
195
+ }
196
+ try {
197
+ return this[attributeName].call(this, attributeValue);
198
+ }
199
+ catch (error) {
200
+ // ignore
201
+ }
202
+ }
203
+
204
+ if (attributeName.startsWith('_')) {
205
+ // This was a terrible hack. I'm still paying the price. Don't use this approach.
206
+ return this.meta(attributeName.slice(1), attributeValue);
207
+ }
208
+ else {
209
+ return this.option(attributeName, attributeValue);
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Attributes were initially a convenient shorthand for constructing schemas, but now just add confusion.
215
+ *
216
+ * @deprecated
217
+ * @param {object} attributes
218
+ * @returns {Schema}
219
+ * @internal
220
+ */
221
+ _setAttributes(attributes = {}) {
222
+ if (typeof attributes !== 'object') {
223
+ throw new SchemaError('Expected an object containing options and metadata attributes');
224
+ }
225
+ for (const [attributeName, attributeValue] of Object.entries(attributes)) {
226
+ this._setAttribute(attributeName, attributeValue);
227
+ }
228
+ return this;
229
+ }
230
+
231
+ /**
232
+ * Attach a named child schema
233
+ *
234
+ * @param {string} propertyName - property name
235
+ * @param {Schema|CompiledSchema|string|undefined} propertySchema - schema to associate with the property, undefined to delete current
236
+ * @returns {Schema} - returns self for fluent chaining
237
+ */
238
+ property(propertyName, propertySchema) {
239
+ if (typeof propertyName !== 'string') {
240
+ throw new SchemaError('Properties must be associated with a valid name');
241
+ }
242
+ if (propertySchema === 'string') {
243
+ // you usually wouldn't want to do this because you almost always want to edit the property schema
244
+ propertySchema = new Schema(propertySchema);
245
+ }
246
+
247
+ if (!(propertySchema instanceof Schema) && !(propertySchema instanceof CompiledSchema)) {
248
+ if (propertySchema === undefined) {
249
+ delete this.properties[propertyName];
250
+ return this;
251
+ }
252
+ else {
253
+ throw new SchemaError('Property value must be a schema');
254
+ }
255
+ }
256
+ this.properties[propertyName] = propertySchema;
257
+
258
+ // If you don't want the default object/array because their validation rules don't match your intent,
259
+ // either use "any" as the base, or explicitly set options.type ahead of time so that it's clear you
260
+ // know what you're doing. TODO - probably should only check this during compilation so that order doesn't matter!
261
+ if (this.base === undefined && this.options.type === undefined) {
262
+ //this.base = Number.isInteger(propertyName)? 'array' : 'object';
263
+ }
264
+ this.options.container ??= true;
265
+ return this;
266
+ }
267
+
268
+ /**
269
+ * Bulk-add properties
270
+ *
271
+ * @param {SchemaProperties} properties - property name
272
+ * @param {symbol} [policy] - specify whether to overwrite or only initialize
273
+ * @returns {Schema} - returns self for fluent chaining
274
+ * @internal
275
+ */
276
+ addProperties(properties, policy = SchemaPolicy.INITIALIZE) {
277
+ if (typeof properties !== 'object') {
278
+ throw new SchemaError('Invalid properties definition');
279
+ }
280
+ for (const [key, schema] of Object.entries(properties)) {
281
+ if (policy === SchemaPolicy.OVERWRITE || this.properties[key] === undefined) {
282
+ this.property(key, Schema.createFromModel(schema));
283
+ }
284
+ }
285
+ return this;
286
+ }
287
+
288
+ /**
289
+ * Define a schema option
290
+ *
291
+ * Options are settings that define how the schema behaves.
292
+ *
293
+ * @param {string} option - option
294
+ * @param {any} [value] - option value
295
+ * @returns {Schema} - returns self for fluent chaining
296
+ */
297
+ option(option, value) {
298
+ if (typeof option !== 'string') {
299
+ throw new SchemaError('Options must be associated with a valid key');
300
+ }
301
+ else if (option.startsWith('_')) {
302
+ throw new SchemaError('Option keys cannot have a leading underscore');
303
+ }
304
+ if (value === undefined) {
305
+ value = true;
306
+ }
307
+ if (value === null) {
308
+ delete this.options[option];
309
+ }
310
+ else {
311
+ this.options[option] = value;
312
+ }
313
+ return this;
314
+ }
315
+
316
+ /**
317
+ * Bulk add options
318
+ *
319
+ * @param {object} options
320
+ * @param {symbol} [policy]
321
+ * @returns {Schema}
322
+ * @internal
323
+ */
324
+ addOptions(options, policy = SchemaPolicy.INITIALIZE) {
325
+ if (policy !== SchemaPolicy.INITIALIZE && policy !== SchemaPolicy.OVERWRITE) {
326
+ throw new SchemaError('Unsupported policy');
327
+ }
328
+
329
+ if (typeof options !== 'object') {
330
+ throw new SchemaError('Options definition must be an object');
331
+ }
332
+ for (const [key, value] of Object.entries(options)) {
333
+ if (policy === SchemaPolicy.OVERWRITE || this.options[key] === undefined) {
334
+ this.option(key, value);
335
+ }
336
+ }
337
+ return this;
338
+ }
339
+
340
+ /**
341
+ * Helper function for the fluent handler api calls
342
+ *
343
+ * @param {string} handlerName
344
+ * @param {Array<ValueProcessorSpec>} specs
345
+ * @param {symbol} [policy]
346
+ * @returns {Schema}
347
+ * @private
348
+ */
349
+ handler(handlerName, specs = [], policy = SchemaPolicy.APPEND) {
350
+ if (typeof handlerName !== 'string') {
351
+ throw new SchemaError('Handlers must be associated with a valid key');
352
+ }
353
+ if (!Object.values(SchemaPolicy).includes(policy)) {
354
+ throw new SchemaError('Unknown policy');
355
+ }
356
+ if (specs === undefined) {
357
+ if (policy === SchemaPolicy.OVERWRITE) {
358
+ delete this.handlers[handlerName];
359
+ }
360
+ return this;
361
+ }
362
+ if (!Array.isArray(specs)) {
363
+ specs = [specs];
364
+ }
365
+ // sanitize values that lead to weird schema parsing issues
366
+ specs = specs.map(spec => {
367
+ if (spec === undefined) {
368
+ return '$undefined';
369
+ }
370
+ else if (spec === null) {
371
+ return '$null';
372
+ }
373
+ return spec;
374
+ })
375
+ if (policy === SchemaPolicy.INITIALIZE && Array.isArray(this.handlers[handlerName])) {
376
+ return this;
377
+ }
378
+ if (policy === SchemaPolicy.OVERWRITE || policy === SchemaPolicy.INITIALIZE) {
379
+ this.handlers[handlerName] = specs;
380
+ return this;
381
+ }
382
+ if (!Array.isArray(this.handlers[handlerName])) {
383
+ this.handlers[handlerName] = [];
384
+ }
385
+ if (policy === SchemaPolicy.PREPEND) {
386
+ this.handlers[handlerName].unshift(...specs);
387
+ }
388
+ else {
389
+ this.handlers[handlerName].push(...specs);
390
+ }
391
+ return this;
392
+ }
393
+
394
+ /**
395
+ * Bulk add handlers
396
+ *
397
+ * @param {object} handlers
398
+ * @param {symbol} [policy]
399
+ * @returns {Schema}
400
+ * @internal
401
+ */
402
+ addHandlers(handlers, policy = SchemaPolicy.INITIALIZE) {
403
+ if (typeof handlers !== 'object') {
404
+ throw new SchemaError('Handlers definition must be an object')
405
+ }
406
+ for (const [key, value] of Object.entries(handlers)) {
407
+ this.handler(key, value, policy);
408
+ }
409
+ return this;
410
+ }
411
+
412
+ /**
413
+ * Define schema metadata (like options, but for humans and ConfigurationSource hints) - todo: locale-aware
414
+ *
415
+ * (Note: named "meta" instead of "metadata" to differentiate from the object getter)
416
+ *
417
+ * @param {string} meta - metadata key
418
+ * @param {any} [value] - option value
419
+ * @returns {Schema} - returns self for fluent chaining
420
+ */
421
+ meta(meta, value) {
422
+
423
+ if (typeof meta !== 'string') {
424
+ throw new SchemaError('Metadata must be associated with a valid key');
425
+ }
426
+ if (value === undefined) {
427
+ value = true;
428
+ }
429
+ if (value === null) {
430
+ delete this.metadata[meta];
431
+ }
432
+ else {
433
+ if (meta.startsWith('_')) {
434
+ meta = meta.slice(1);
435
+ }
436
+ this.metadata[meta] = value;
437
+ }
438
+ return this;
439
+ }
440
+
441
+ /**
442
+ * Bulk-add metadata
443
+ *
444
+ * @param {object} metadata
445
+ * @param {symbol} [policy]
446
+ * @returns {Schema}
447
+ * @internal
448
+ */
449
+ addMetadata(metadata, policy = SchemaPolicy.INITIALIZE) {
450
+ if (typeof metadata !== 'object') {
451
+ throw new SchemaError('Invalid metadata definition');
452
+ }
453
+ for (const [key, value] of Object.entries(metadata)) {
454
+ if (policy === SchemaPolicy.OVERWRITE || this.metadata[key] === undefined) {
455
+ this.meta(key, value);
456
+ }
457
+ }
458
+ return this;
459
+ }
460
+
461
+ /**
462
+ * The discriminator handler returns the key or schema of the union member that should be used
463
+ * This function appends a single value processor to the handler pipeline.
464
+ *
465
+ * @param {ValueProcessorSpec} spec
466
+ * @returns {Schema} - returns self for fluent chaining
467
+ */
468
+ unionDiscriminator(spec) {
469
+ return this.unionDiscriminators(spec);
470
+ }
471
+
472
+ /**
473
+ * The discriminator handler returns the key or schema of the union member that should be used
474
+ * This function applies multiple value processors to the handler pipeline.
475
+ * (Note that it would be highly unusual to want more than one!)
476
+ *
477
+ * @param {Array<ValueProcessorSpec>} specs
478
+ * @param {symbol} [policy]
479
+ * @returns {Schema} - returns self for fluent chaining
480
+ */
481
+ unionDiscriminators(specs, policy) {
482
+ return this.handler('discriminators', specs, policy);
483
+ }
484
+
485
+ /**
486
+ * Add a schema as an alternative member of this schema's union.
487
+ *
488
+ * @param {string} key - union schema key (used by some discriminators to select this schema)
489
+ * @param {Schema|CompiledSchema} unionSchema - schema that the discriminator selects, or true/false override if a group
490
+ * @returns {Schema}
491
+ */
492
+ unionSchema(key, unionSchema) {
493
+
494
+ if (!(unionSchema instanceof Schema || unionSchema instanceof CompiledSchema)) {
495
+ throw new SchemaError(`Invalid schema for union member ${key}`);
496
+ }
497
+ this.unionSchemas[key] = unionSchema;
498
+
499
+ return this;
500
+ }
501
+
502
+ /**
503
+ * Bulk-add union schemas
504
+ *
505
+ * @param {SchemaUnionSchemas} unionSchemas
506
+ * @param {symbol} [policy]
507
+ * @returns {Schema}
508
+ * @internal
509
+ */
510
+ addUnionSchemas(unionSchemas, policy = SchemaPolicy.INITIALIZE) {
511
+ if (policy !== SchemaPolicy.INITIALIZE && policy !== SchemaPolicy.OVERWRITE) {
512
+ throw new SchemaError('Unsupported policy');
513
+ }
514
+
515
+ if (typeof unionSchemas !== 'object') {
516
+ throw new SchemaError('Invalid union schemas object');
517
+ }
518
+
519
+ for (const [key, unionSchema] of Object.entries(unionSchemas)) {
520
+ if (policy === SchemaPolicy.OVERWRITE || this.unionSchemas[key] === undefined) {
521
+ this.unionSchema(key, Schema.createFromModel(unionSchema));
522
+ }
523
+ }
524
+ return this;
525
+ }
526
+
527
+ /**
528
+ * Mark this schema as containing (and only permitting) storage of union keys.
529
+ *
530
+ * @param {boolean} [value]
531
+ * @returns {Schema}
532
+ */
533
+ unionKey(value) {
534
+ this.options.unionKey = Boolean(value ?? true);
535
+ return this;
536
+ }
537
+
538
+ /**
539
+ * Mark this schema as a selector.
540
+ *
541
+ * Selectors are a convenience wrapper for controlling selection conditions that only are true when
542
+ * the selector contains the correct selection value.
543
+ *
544
+ * @param {boolean} [value]
545
+ * @returns {Schema}
546
+ */
547
+ selector(value) {
548
+ this.options.selector = Boolean(value ?? true);
549
+
550
+ return this;
551
+ }
552
+
553
+ /**
554
+ * Mark this schema as a selection.
555
+ *
556
+ * A selection schema automatically creates a condition that only activates the schema if the corresponding
557
+ * selector has the correct value.
558
+ *
559
+ * With the default argument, the selection schema condition uses the property name as the selector value.
560
+ *
561
+ * @param {NonNullable<any>} [value]
562
+ * @returns {Schema}
563
+ */
564
+ selection(value) {
565
+ this.options.selection = value ?? true;
566
+ // fixme - this shouldn't need to be async! normalize during compilation!
567
+ this.condition(async (_, target, location) => {
568
+ if (!location.schema.isSelection) {
569
+ throw new SchemaError(`Conditional expected a selection schema!`, {location});
570
+ }
571
+
572
+ const selectionValue = (this.options.selection === true)? location.name : this.options.selection;
573
+
574
+ const selectorLocation = location.parent?.findPropertyLocation(pl => pl.schema.isSelector)
575
+
576
+ if (selectorLocation) {
577
+ const ss = selectorLocation.schema;
578
+ const selectorValue = deepValue(target, selectorLocation.path);
579
+
580
+ return (await ss.normalizeValue(selectorValue, target, selectorLocation) === (await ss.normalizeValue(selectionValue, target, selectorLocation)));
581
+ }
582
+ return false;
583
+ })
584
+
585
+ return this;
586
+ }
587
+
588
+ /**
589
+ * Mark this schema as defining a required value (or not)
590
+ *
591
+ * Schema requirements are enforced during validation.
592
+ *
593
+ * Requirements are shallow; this can be changed via the deep() option.
594
+ *
595
+ * @param {boolean} [value]
596
+ * @returns {Schema}
597
+ */
598
+ required(value) {
599
+ this.options.required = value ?? true;
600
+ return this;
601
+ }
602
+
603
+ /**
604
+ * Mark this schema as defining an optional value (or not); the default is optional.
605
+ *
606
+ * (Syntactic sugar to negate required())
607
+ * Schema requirements are enforced during validation.
608
+ *
609
+ * Requirements are shallow; this can be changed via the deep() option.
610
+ *
611
+ * @param {boolean} [value]
612
+ * @returns {Schema}
613
+ */
614
+ optional(value) {
615
+ this.options.required = !(value);
616
+ return this;
617
+ }
618
+
619
+ /**
620
+ * Define a default value for this schema to use if there is no input.
621
+ *
622
+ * Defaults are shallow and will not cause children of undefined inputs to populate;
623
+ * this can be changed via the deep() option.
624
+ *
625
+ * @param {NonNullable<any>|ValueProcessorFunction|ValueProcessor} value
626
+ * @returns {Schema}
627
+ */
628
+ default(value) {
629
+ this.options.default = value;
630
+ return this;
631
+ }
632
+
633
+ /**
634
+ * Indicate that this schema should be deeply traversed even if the input is empty
635
+ * (e.g. to enable deep defaults and requirements)
636
+ *
637
+ * @param {boolean} [value]
638
+ * @returns {Schema}
639
+ */
640
+ deep(value) {
641
+ this.options.deep = value ?? true;
642
+ return this;
643
+ }
644
+
645
+ /**
646
+ * Mark this array/string as allowing empty values.
647
+ *
648
+ * @param {boolean} [value]
649
+ * @returns {Schema}
650
+ */
651
+ allowEmpty(value) {
652
+ this.options.allowEmpty = value ?? true;
653
+ return this;
654
+ }
655
+
656
+ /**
657
+ * Mark this schema as allowing incremental assignment to children.
658
+ *
659
+ * (The default is true for any schema with children, so most of the time you'd be disabling it.)
660
+ *
661
+ * Deprecated; use the "opaque" option as it more clearly indicates the actual intent.
662
+ *
663
+ * @param {boolean} [value]
664
+ * @returns {Schema}
665
+ * @deprecated
666
+ */
667
+ allowIncremental(value) {
668
+ this.options.allowIncremental = value ?? true;
669
+ return this;
670
+ }
671
+
672
+ /**
673
+ * Mark this schema as defining a value whose internal structure is hidden after transformation.
674
+ *
675
+ * This has implications both for assignment processing and validation.
676
+ *
677
+ * Deep property assignments usually result in any mid-path containers being automatically created
678
+ * (normalized and transformed) and property values are incrementally assigned.
679
+ *
680
+ * Opaque schemas do not allow incremental assignments, so they only create normalized mid-path containers
681
+ * that are staged until all relevant assignments are complete. The transform is then run, and passed
682
+ * the staged normalized container contents as input.
683
+ *
684
+ * Validators for opaque schemas only run on the value itself, and do not traverse into any child properties.
685
+ * Opaque schemas thus generally require custom validators that know how to properly handle the value.
686
+ *
687
+ * @param {boolean} [value]
688
+ * @returns {Schema}
689
+ */
690
+ opaque(value = true) {
691
+ this.options.allowIncremental = !value;
692
+ return this;
693
+ }
694
+
695
+ /**
696
+ * Mark this schema as requiring strict enforcement (the default)
697
+ *
698
+ * Strict mode means that the data cannot have any extra data, and exactly matches the schema definition.
699
+ * Lax mode allows a more "fuzzy" interpretation of the data, but the data still must pass all value processors
700
+ * including the validation phases. (To prevent exceptions during validation, wrap the processors in a
701
+ * $filter, which will just return undefined.)
702
+ *
703
+ * @param {boolean} [value]
704
+ * @returns {Schema}
705
+ */
706
+ strict(value) {
707
+ this.options.strict = value ?? true;
708
+ return this;
709
+ }
710
+
711
+ /**
712
+ * Syntactic sugar for the opposite of strict
713
+ *
714
+ * @param {boolean} [value]
715
+ * @returns {Schema}
716
+ */
717
+ lax(value) {
718
+ this.options.strict = (value === undefined) ? false : !value;
719
+ return this;
720
+ }
721
+
722
+ /**
723
+ * Mark this schema as implicit in the transformed output (so no need to assign or check)
724
+ *
725
+ * @param {boolean} [value]
726
+ * @returns {Schema}
727
+ */
728
+ implicit(value) {
729
+ this.options.implicit = value ?? true;
730
+ return this;
731
+ }
732
+
733
+
734
+ /**
735
+ * Define a legal input value this schema will accept.
736
+ *
737
+ * Values will be normalized for comparison.
738
+ *
739
+ * @param {NonNullable<any>} v
740
+ * @returns {Schema}
741
+ */
742
+ value(v) {
743
+ return this.values([v])
744
+ }
745
+
746
+ /**
747
+ * Define a list of one or more legal values this schema will accept
748
+ *
749
+ * Values will be normalized for comparison.
750
+ *
751
+ * @param {Array<NonNullable<any>>} va
752
+ * @param {symbol} [policy]
753
+ * @returns {Schema}
754
+ */
755
+ values(va = [], policy = SchemaPolicy.APPEND) {
756
+
757
+ if (policy === SchemaPolicy.INITIALIZE && Array.isArray(this.options.values)) {
758
+ return this;
759
+ }
760
+ if (policy === SchemaPolicy.OVERWRITE || !Array.isArray(this.options.values)) {
761
+ this.options.values = [];
762
+ }
763
+ if (!Array.isArray(va)) {
764
+ va = [va];
765
+ }
766
+ if (policy === SchemaPolicy.PREPEND) {
767
+ this.options.values.unshift(...va);
768
+ }
769
+ else {
770
+ this.options.values.push(...va);
771
+ }
772
+ return this;
773
+ }
774
+
775
+ /**
776
+ * The condition handler determines if the schema should be processed at all.
777
+ * This function appends a single value processor to the handler pipeline
778
+ *
779
+ * @param {ValueProcessorSpec} spec
780
+ * @returns {Schema}
781
+ */
782
+ condition(spec) {
783
+ return this.conditions(spec);
784
+ }
785
+
786
+ /**
787
+ * The condition handler determines if the schema should be processed at all.
788
+ * This call applies one or more value processors to the handler pipeline (default policy = append)
789
+ *
790
+ * @param {Array<ValueProcessorSpec>} specs
791
+ * @param {symbol} [policy]
792
+ * @returns {Schema}
793
+ */
794
+ conditions(specs, policy) {
795
+ return this.handler('conditions', specs, policy);
796
+ }
797
+
798
+ /**
799
+ * The normalizer handler ensures input is in a format that the transformer can handle.
800
+ * This call appends a single value processor to the handler pipeline.
801
+ *
802
+ * @param {ValueProcessorSpec} spec
803
+ * @returns {Schema}
804
+ */
805
+ normalizer(spec) {
806
+ return this.normalizers(spec);
807
+ }
808
+
809
+ /**
810
+ * The normalizer handler ensures input is in a format that the transformer can handle.
811
+ * This call applies one or more value processors to the handler pipeline.
812
+ *
813
+ * @param {Array<ValueProcessorSpec>} specs
814
+ * @param {symbol} [policy]
815
+ * @returns {Schema}
816
+ */
817
+ normalizers(specs, policy) {
818
+ return this.handler('normalizers', specs, policy);
819
+ }
820
+
821
+ /**
822
+ * The transformer handler converts a normalized input value into the final output value for the schema.
823
+ * This call appends a single value processor to the handler pipeline.
824
+ *
825
+ * @param {ValueProcessorSpec} spec
826
+ * @returns {Schema}
827
+ */
828
+ transformer(spec) {
829
+ return this.transformers(spec);
830
+ }
831
+
832
+ /**
833
+ * The transformer handler converts a normalized input value into the final output value for the schema.
834
+ * This call applies one or more value processors to the handler pipeline.
835
+ *
836
+ * @param {Array<ValueProcessorSpec>} specs
837
+ * @param {symbol} [policy]
838
+ * @returns {Schema}
839
+ */
840
+ transformers(specs, policy) {
841
+ return this.handler('transformers', specs, policy);
842
+ }
843
+
844
+ /**
845
+ * The finalizer handler performs any required post-processing of a transformed value.
846
+ *
847
+ * @param {ValueProcessorSpec} spec
848
+ * @returns {Schema}
849
+ */
850
+ finalizer(spec) {
851
+ return this.finalizers(spec);
852
+ }
853
+
854
+ /**
855
+ * The finalizer handler does any required post-processing of a transformed value.
856
+ * This call applies one or more value processors to the handler pipeline.
857
+ *
858
+ * @param {Array<ValueProcessorSpec>} specs
859
+ * @param {symbol} [policy]
860
+ * @returns {Schema}
861
+ */
862
+ finalizers(specs, policy) {
863
+ return this.handler('finalizers', specs, policy);
864
+ }
865
+
866
+
867
+ /**
868
+ * The validator handler ensures an input value matches the schema, and returns a (potentially enhanced) fully validated output value.
869
+ * This call appends a single value processor to the handler pipeline.
870
+ *
871
+ * @param {ValueProcessorSpec} spec
872
+ * @returns {Schema}
873
+ */
874
+ validator(spec) {
875
+ return this.validators(spec);
876
+ }
877
+
878
+ /**
879
+ * The validator handler ensures an input value matches the schema, and returns a (potentially enhanced) fully validated output value.
880
+ * This call applies one or more value processors to the handler pipeline.
881
+ *
882
+ * @param {Array<ValueProcessorSpec>} specs
883
+ * @param {symbol} [policy]
884
+ * @returns {Schema}
885
+ */
886
+ validators(specs, policy) {
887
+ return this.handler('validators', specs, policy);
888
+ }
889
+
890
+ /**
891
+ * The serialize handler restores a configuration value to its pre-transform normalized form.
892
+ * This call appends a single value processor to the handler pipeline.
893
+ *
894
+ * @param {ValueProcessorSpec} spec
895
+ * @returns {Schema}
896
+ */
897
+ serializer(spec) {
898
+ return this.serializers(spec);
899
+ }
900
+
901
+ /**
902
+ * The serialize handler restores a configuration value to its pre-transform normalized form.
903
+ * This call applies one or more value processors to the handler pipeline.
904
+ *
905
+ * @param {Array<ValueProcessorSpec>} specs
906
+ * @param {symbol} [policy]
907
+ * @returns {Schema}
908
+ */
909
+ serializers(specs, policy) {
910
+ return this.handler('serializers', specs, policy);
911
+ }
912
+
913
+
914
+ /**
915
+ * Use another schema to extend the current one without overwriting.
916
+ *
917
+ * @param {ISchema|SchemaData} otherSchema - source schema
918
+ * @param {Map<any,any>} [seen]
919
+ * @returns {Schema} - returns self
920
+ */
921
+ extend(otherSchema, seen = new Map()) {
922
+ if (typeof otherSchema !== 'object') {
923
+ throw new SchemaError(`Invalid schema to extend`)
924
+ }
925
+
926
+ // Set base if not already set
927
+ if (!this.base && otherSchema.base) {
928
+ this.base = otherSchema.base;
929
+ }
930
+
931
+ this.addProperties(Object.fromEntries(
932
+ Object.entries(otherSchema.properties ?? {})
933
+ .map(([propertyName, propertySchema]) => [propertyName, Schema.createFromModel(propertySchema, seen)])));
934
+ this.addUnionSchemas(Object.fromEntries(
935
+ Object.entries(otherSchema.unionSchemas ?? {})
936
+ .map(([unionKey, unionSchema]) => [unionKey, Schema.createFromModel(unionSchema, seen)])));
937
+
938
+ this.addOptions(otherSchema.options ?? {});
939
+ this.addMetadata(otherSchema.metadata ?? {});
940
+ this.addHandlers(otherSchema.handlers ?? {}); // todo - this behaves differently than compilation!?
941
+
942
+ return this;
943
+ }
944
+
945
+ /**
946
+ * Make a copy of this schema
947
+ *
948
+ * @returns {Schema}
949
+ */
950
+ clone() {
951
+ return Schema.createFromModel(this);
952
+ }
953
+
954
+ /**
955
+ * Create a new Schema from something schema-shaped
956
+ *
957
+ * @param {ISchema|SchemaData|string} model
958
+ * @param {Map<any,any>} [seen]
959
+ * @returns {Schema}
960
+ */
961
+ static createFromModel(model, seen = new Map()) {
962
+ if (typeof model === 'string') {
963
+ return new Schema(model);
964
+ }
965
+
966
+ if (seen.has(model)) {
967
+ return seen.get(model);
968
+ }
969
+
970
+ const schema = new Schema();
971
+ seen.set(model, schema);
972
+
973
+ if (model.base) {
974
+ schema.base = model.base;
975
+ }
976
+ return schema.extend(model, seen);
977
+ }
978
+ /**
979
+ * Static schema factory (useful for aliasing to reduce typing!)
980
+ *
981
+ * Prefer using fluent setters over passing options/metadata to this call
982
+ *
983
+ * @param {string|ISchema|SchemaData|Schema|CompiledSchema} [base] - schema
984
+ * @param {object} [options] - schema options
985
+ * @param {ISchemaMetadata} [metadata] - schema metadata
986
+ * @returns {Schema}
987
+ */
988
+ static create(base, options, metadata) {
989
+ return new Schema(base, options, metadata);
990
+ }
991
+
992
+ /**
993
+ * Static schema factory for special schemas that ignore assignments and produce a single defined value
994
+ *
995
+ * Prefer using fluent setters over passing options/metadata to this call
996
+ *
997
+ * @param {any} literalValue - the value this schema will always emit
998
+ * @param {object} [options] - additional options
999
+ * @param {ISchemaMetadata} [metadata] - additional metadata
1000
+ * @returns {Schema}
1001
+ */
1002
+ static literal(literalValue, options, metadata) {
1003
+ let base = 'any';
1004
+ if (typeof literalValue === 'string') {
1005
+ base = 'string';
1006
+ }
1007
+ else if (typeof literalValue === 'number') {
1008
+ base = 'number';
1009
+ }
1010
+ else if (typeof literalValue === 'boolean') {
1011
+ base = 'boolean';
1012
+ }
1013
+
1014
+ const schema = new Schema(base)
1015
+ .option('values', [literalValue])
1016
+ .option('default', literalValue)
1017
+ .option('compileHook', (eventName, hookSchema) => {
1018
+ if (eventName === 'finalize') {
1019
+ // Verify that the schema wasn't mangled into something weird
1020
+ if (hookSchema.options.values?.length !== 1) {
1021
+ throw new SchemaError(`Literal schema needs one value defined`);
1022
+ }
1023
+ if (hookSchema.hasChildren) {
1024
+ throw new SchemaError(`Literal schema should not have child properties`)
1025
+ }
1026
+ if (hookSchema.isUnion) {
1027
+ throw new SchemaError(`Literal schema should not be a union`)
1028
+ }
1029
+ }
1030
+ })
1031
+ // Important note: we do *not* return the literal during normalization
1032
+ // because hoisting union discriminators wants a compatible normalizer.
1033
+ // The values option prevents assignment if the normalized input does not
1034
+ // match the required literal value.
1035
+ .transformer(() => literalValue)
1036
+
1037
+ if (options) {
1038
+ schema._setAttributes(options);
1039
+ }
1040
+ if (metadata) {
1041
+ schema.addMetadata(metadata);
1042
+ }
1043
+
1044
+ return schema;
1045
+ }
1046
+
1047
+ /**
1048
+ * Static schema factory for creating a schema that inherits its value from the first parent with the property name.
1049
+ * If no property name is provided, the inherit schema's property name is used, so it will look for the same name
1050
+ * higher in the schema hierarchy.
1051
+ *
1052
+ * TODO - restore compilation hook for checking whether this is a legal setup
1053
+ *
1054
+ * @returns {Schema}
1055
+ * @param {string} [propertyName]
1056
+ * @internal
1057
+ */
1058
+ static inherit(propertyName) {
1059
+
1060
+ return new Schema()
1061
+ .option('reference', true)
1062
+ .transformer(/** @type {ValueProcessorFunction} */ (_value, config, location, options) => {
1063
+ const name = propertyName ?? location.name;
1064
+ if (location.parent === undefined) {
1065
+ if (location.schema.strict !== false) {
1066
+ throw new SchemaError('A top-level schema cannot have an inherited value');
1067
+ }
1068
+ return undefined;
1069
+ }
1070
+ let ancestorLocation = location.parent?.parent;
1071
+
1072
+ while (ancestorLocation !== undefined) {
1073
+ const candidate = ancestorLocation.relative(name);
1074
+
1075
+ if (candidate !== undefined) {
1076
+ return deepValue(config, candidate.path);
1077
+ }
1078
+ ancestorLocation = ancestorLocation.parent;
1079
+ }
1080
+ if (location.schema.strict !== false) {
1081
+ throw new SchemaError(`Inherited property "${name}" not found in any ancestor of "${location}"`);
1082
+ }
1083
+ return undefined;
1084
+
1085
+ })
1086
+
1087
+ .serializer(() => undefined)
1088
+ .default(/** @type {ValueProcessorFunction} */ (_value, config, location) => {
1089
+ // todo - check for dynamic=false during compilation!
1090
+ return propertyName ?? location.name;
1091
+ })
1092
+ .meta('omitFromSerialize')
1093
+ .meta('internal')
1094
+ }
1095
+
1096
+ /**
1097
+ * Static schema factory for creating a schema that gets its value from another location based on a path.
1098
+ *
1099
+ * By default, `path` is interpreted as an absolute path from the root. If the `relative` flag is
1100
+ * set or navigation metacharacters start the path, it will be relative to the current schema.
1101
+ * - An absolute path prefixed with `/` starts at the root.
1102
+ * - A path prefixed with `^` navigates up one level from the current schema path.
1103
+ *
1104
+ * TODO - restore hook for checking whether the provided path is known at compilation time
1105
+ *
1106
+ * @param {string} path
1107
+ * @param {boolean} [relative]
1108
+ * @returns {Schema}
1109
+ * @internal
1110
+ */
1111
+ static reference(path, relative = false) {
1112
+
1113
+ const c = path?.charAt(0);
1114
+
1115
+ if (c === '^' || c === '/' || c === '.') {
1116
+ relative = true;
1117
+ }
1118
+ return new Schema()
1119
+ .option('reference', true)
1120
+ .default(path)
1121
+ .normalizer(() => path)
1122
+ .transformer(/** @type {ValueProcessorFunction} */ (_, target, location, options) => {
1123
+ const referenceLocation = relative? location.relative(path) : location.absolute(path);
1124
+ if (referenceLocation === undefined) {
1125
+ throw new SchemaError(`Reference path ${path} not found`);
1126
+ }
1127
+ const referenceSchema = referenceLocation.schema;
1128
+ if (referenceSchema === undefined) {
1129
+ if (options?.context?.final) {
1130
+ throw new SchemaError(`Schema for reference path ${path} not found`);
1131
+ }
1132
+ return undefined;
1133
+ }
1134
+ return deepValue(target, referenceLocation.path);
1135
+ })
1136
+ .validator(/** @type {ValueProcessorFunction} */ (value, target, location, options) => {
1137
+ const referenceLocation = relative? location.relative(path) : location.absolute(path);
1138
+ if (referenceLocation === undefined) {
1139
+ throw new SchemaError(`Reference path ${path} not found`);
1140
+ }
1141
+ const referenceSchema = referenceLocation.schema;
1142
+ if (referenceSchema === undefined) {
1143
+ if (options?.context?.final) {
1144
+ throw new SchemaError(`Schema for reference path ${path} not found`);
1145
+ }
1146
+ return undefined;
1147
+ }
1148
+ const targetValue = deepValue(target, referenceLocation.path);
1149
+
1150
+ // If identical, we're done.
1151
+ if (targetValue === value) {
1152
+ return value;
1153
+ }
1154
+
1155
+ // simple values and opaque values must have an identical reference
1156
+ if (!referenceSchema?.hasChildren || referenceSchema.isOpaque) {
1157
+ throw new SchemaError(`Reference is not exactly the same as ${path}`)
1158
+ }
1159
+
1160
+ // this feels wrong, but there's no guarantee a container wasn't rebuilt during validation
1161
+ return value;
1162
+
1163
+ })
1164
+ .serializer(() => undefined)
1165
+ .meta('omitFromSerialize')
1166
+ .meta('internal')
1167
+ }
1168
+
1169
+ }
1170
+
1171
+ /**
1172
+ * Policies for fine-grained control of composite schema internals
1173
+ * @readonly
1174
+ * @enum {symbol}
1175
+ */
1176
+ export const SchemaPolicy = Object.freeze({
1177
+ INITIALIZE: Symbol('INITIALIZE'), // only set if not already set
1178
+ OVERWRITE: Symbol('OVERWRITE'), // overwrite
1179
+ APPEND: Symbol('APPEND'), // add values to the end
1180
+ PREPEND: Symbol('PREPEND') // add values to the beginning
1181
+ });
1182
+