@warlock.js/seal 5.30.0 → 5.32.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 (318) hide show
  1. package/CHANGELOG.md +46 -7
  2. package/README.md +57 -9
  3. package/cjs/index.cjs +41 -29
  4. package/cjs/index.cjs.map +1 -1
  5. package/cjs/{object--LXRt5RF.cjs → object-BoVGJ68E.cjs} +43 -24
  6. package/cjs/object-BoVGJ68E.cjs.map +1 -0
  7. package/cjs/object.cjs +2 -2
  8. package/cjs/{required-methods-DYsRO9aZ.cjs → required-methods-BJlnLnwa.cjs} +343 -258
  9. package/cjs/required-methods-BJlnLnwa.cjs.map +1 -0
  10. package/cjs/{string-DsqnKoSB.cjs → string-CM8x0u2w.cjs} +46 -44
  11. package/cjs/string-CM8x0u2w.cjs.map +1 -0
  12. package/cjs/string.cjs +2 -2
  13. package/esm/config.d.mts +7 -8
  14. package/esm/config.d.mts.map +1 -1
  15. package/esm/config.mjs.map +1 -1
  16. package/esm/factory/index.d.mts +2 -1
  17. package/esm/factory/index.mjs +1 -1
  18. package/esm/factory/validate.d.mts +3 -6
  19. package/esm/factory/validate.d.mts.map +1 -1
  20. package/esm/factory/validators.d.mts +16 -13
  21. package/esm/factory/validators.d.mts.map +1 -1
  22. package/esm/factory/validators.mjs +7 -1
  23. package/esm/factory/validators.mjs.map +1 -1
  24. package/esm/helpers/date-helpers.d.mts +1 -2
  25. package/esm/helpers/date-helpers.d.mts.map +1 -1
  26. package/esm/helpers/file.utils.d.mts +2 -4
  27. package/esm/helpers/file.utils.d.mts.map +1 -1
  28. package/esm/helpers/get-field-value.d.mts +2 -3
  29. package/esm/helpers/get-field-value.d.mts.map +1 -1
  30. package/esm/helpers/get-field-value.mjs +2 -1
  31. package/esm/helpers/get-field-value.mjs.map +1 -1
  32. package/esm/helpers/index.d.mts +6 -0
  33. package/esm/helpers/index.mjs +1 -1
  34. package/esm/helpers/path-helpers.d.mts +1 -2
  35. package/esm/helpers/path-helpers.d.mts.map +1 -1
  36. package/esm/helpers/validation-helpers.d.mts +4 -10
  37. package/esm/helpers/validation-helpers.d.mts.map +1 -1
  38. package/esm/index.d.mts +7 -0
  39. package/esm/mutators/array-mutators.d.mts +6 -7
  40. package/esm/mutators/array-mutators.d.mts.map +1 -1
  41. package/esm/mutators/boolean-mutators.d.mts +3 -4
  42. package/esm/mutators/boolean-mutators.d.mts.map +1 -1
  43. package/esm/mutators/date-mutators.d.mts +18 -19
  44. package/esm/mutators/date-mutators.d.mts.map +1 -1
  45. package/esm/mutators/index.d.mts +8 -0
  46. package/esm/mutators/index.mjs +1 -1
  47. package/esm/mutators/number-mutators.d.mts +10 -11
  48. package/esm/mutators/number-mutators.d.mts.map +1 -1
  49. package/esm/mutators/object-mutators.d.mts +4 -5
  50. package/esm/mutators/object-mutators.d.mts.map +1 -1
  51. package/esm/mutators/object-mutators.mjs +5 -4
  52. package/esm/mutators/object-mutators.mjs.map +1 -1
  53. package/esm/mutators/scalar-mutators.d.mts +2 -3
  54. package/esm/mutators/scalar-mutators.d.mts.map +1 -1
  55. package/esm/mutators/string-mutators.d.mts +37 -38
  56. package/esm/mutators/string-mutators.d.mts.map +1 -1
  57. package/esm/mutators/string-mutators.mjs +5 -3
  58. package/esm/mutators/string-mutators.mjs.map +1 -1
  59. package/esm/object.d.mts +3 -5
  60. package/esm/object.d.mts.map +1 -1
  61. package/esm/object.mjs.map +1 -1
  62. package/esm/plugins/plugin-system.d.mts +11 -9
  63. package/esm/plugins/plugin-system.d.mts.map +1 -1
  64. package/esm/rules/array/array-rules.d.mts +3 -4
  65. package/esm/rules/array/array-rules.d.mts.map +1 -1
  66. package/esm/rules/array/index.d.mts +2 -0
  67. package/esm/rules/array/index.mjs +1 -1
  68. package/esm/rules/color/color-rules.d.mts +8 -9
  69. package/esm/rules/color/color-rules.d.mts.map +1 -1
  70. package/esm/rules/color/index.d.mts +2 -0
  71. package/esm/rules/color/index.mjs +1 -1
  72. package/esm/rules/common/enum.d.mts +5 -6
  73. package/esm/rules/common/enum.d.mts.map +1 -1
  74. package/esm/rules/common/equals-field-rules.d.mts +3 -4
  75. package/esm/rules/common/equals-field-rules.d.mts.map +1 -1
  76. package/esm/rules/common/index.d.mts +7 -0
  77. package/esm/rules/common/index.mjs +1 -1
  78. package/esm/rules/common/instanceof.d.mts +2 -3
  79. package/esm/rules/common/instanceof.d.mts.map +1 -1
  80. package/esm/rules/common/literal.d.mts +2 -3
  81. package/esm/rules/common/literal.d.mts.map +1 -1
  82. package/esm/rules/common/literal.mjs.map +1 -1
  83. package/esm/rules/common/type-rules.d.mts +10 -11
  84. package/esm/rules/common/type-rules.d.mts.map +1 -1
  85. package/esm/rules/common/unknown-key.d.mts +2 -3
  86. package/esm/rules/common/unknown-key.d.mts.map +1 -1
  87. package/esm/rules/conditional/forbidden-if-rules.d.mts +7 -8
  88. package/esm/rules/conditional/forbidden-if-rules.d.mts.map +1 -1
  89. package/esm/rules/conditional/index.d.mts +11 -0
  90. package/esm/rules/conditional/index.mjs +1 -1
  91. package/esm/rules/conditional/present-if-rules.d.mts +6 -7
  92. package/esm/rules/conditional/present-if-rules.d.mts.map +1 -1
  93. package/esm/rules/conditional/present-unless-rules.d.mts +2 -3
  94. package/esm/rules/conditional/present-unless-rules.d.mts.map +1 -1
  95. package/esm/rules/conditional/present-with-rules.d.mts +4 -5
  96. package/esm/rules/conditional/present-with-rules.d.mts.map +1 -1
  97. package/esm/rules/conditional/present-without-rules.d.mts +4 -5
  98. package/esm/rules/conditional/present-without-rules.d.mts.map +1 -1
  99. package/esm/rules/conditional/required-if-rules.d.mts +10 -11
  100. package/esm/rules/conditional/required-if-rules.d.mts.map +1 -1
  101. package/esm/rules/conditional/required-unless-rules.d.mts +2 -3
  102. package/esm/rules/conditional/required-unless-rules.d.mts.map +1 -1
  103. package/esm/rules/conditional/required-when-rule.d.mts +2 -3
  104. package/esm/rules/conditional/required-when-rule.d.mts.map +1 -1
  105. package/esm/rules/conditional/required-with-rules.d.mts +4 -5
  106. package/esm/rules/conditional/required-with-rules.d.mts.map +1 -1
  107. package/esm/rules/conditional/required-without-rules.d.mts +4 -5
  108. package/esm/rules/conditional/required-without-rules.d.mts.map +1 -1
  109. package/esm/rules/core/equal.d.mts +2 -3
  110. package/esm/rules/core/equal.d.mts.map +1 -1
  111. package/esm/rules/core/forbidden.d.mts +2 -3
  112. package/esm/rules/core/forbidden.d.mts.map +1 -1
  113. package/esm/rules/core/index.d.mts +6 -0
  114. package/esm/rules/core/index.mjs +1 -1
  115. package/esm/rules/core/required.d.mts +3 -4
  116. package/esm/rules/core/required.d.mts.map +1 -1
  117. package/esm/rules/core/union.d.mts +10 -4
  118. package/esm/rules/core/union.d.mts.map +1 -1
  119. package/esm/rules/core/union.mjs +56 -10
  120. package/esm/rules/core/union.mjs.map +1 -1
  121. package/esm/rules/core/when.d.mts +2 -3
  122. package/esm/rules/core/when.d.mts.map +1 -1
  123. package/esm/rules/core/when.mjs.map +1 -1
  124. package/esm/rules/date/date-comparison-rules.d.mts +6 -7
  125. package/esm/rules/date/date-comparison-rules.d.mts.map +1 -1
  126. package/esm/rules/date/date-day-rules.d.mts +5 -6
  127. package/esm/rules/date/date-day-rules.d.mts.map +1 -1
  128. package/esm/rules/date/date-field-comparison-rules.d.mts +4 -5
  129. package/esm/rules/date/date-field-comparison-rules.d.mts.map +1 -1
  130. package/esm/rules/date/date-field-comparison-rules.mjs +6 -3
  131. package/esm/rules/date/date-field-comparison-rules.mjs.map +1 -1
  132. package/esm/rules/date/date-period-rules.d.mts +16 -16
  133. package/esm/rules/date/date-period-rules.d.mts.map +1 -1
  134. package/esm/rules/date/date-period-rules.mjs +24 -12
  135. package/esm/rules/date/date-period-rules.mjs.map +1 -1
  136. package/esm/rules/date/date-relative-rules.d.mts +4 -5
  137. package/esm/rules/date/date-relative-rules.d.mts.map +1 -1
  138. package/esm/rules/date/date-relative-rules.mjs +3 -3
  139. package/esm/rules/date/date-relative-rules.mjs.map +1 -1
  140. package/esm/rules/date/date-special-rules.d.mts +4 -5
  141. package/esm/rules/date/date-special-rules.d.mts.map +1 -1
  142. package/esm/rules/date/date.d.mts +16 -17
  143. package/esm/rules/date/date.d.mts.map +1 -1
  144. package/esm/rules/date/date.mjs +4 -2
  145. package/esm/rules/date/date.mjs.map +1 -1
  146. package/esm/rules/date/index.d.mts +8 -0
  147. package/esm/rules/date/index.mjs +1 -1
  148. package/esm/rules/file/dimensions.d.mts +5 -6
  149. package/esm/rules/file/dimensions.d.mts.map +1 -1
  150. package/esm/rules/file/file-size.d.mts +3 -4
  151. package/esm/rules/file/file-size.d.mts.map +1 -1
  152. package/esm/rules/file/index.d.mts +3 -0
  153. package/esm/rules/file/index.mjs +1 -1
  154. package/esm/rules/index.d.mts +56 -0
  155. package/esm/rules/index.mjs +1 -1
  156. package/esm/rules/length/index.d.mts +2 -0
  157. package/esm/rules/length/index.mjs +1 -1
  158. package/esm/rules/length/length-rules.d.mts +8 -9
  159. package/esm/rules/length/length-rules.d.mts.map +1 -1
  160. package/esm/rules/number/index.d.mts +2 -0
  161. package/esm/rules/number/index.mjs +1 -1
  162. package/esm/rules/number/number-rules.d.mts +11 -12
  163. package/esm/rules/number/number-rules.d.mts.map +1 -1
  164. package/esm/rules/number/number-rules.mjs +12 -6
  165. package/esm/rules/number/number-rules.mjs.map +1 -1
  166. package/esm/rules/scalar/index.mjs +1 -1
  167. package/esm/rules/string/alpha.d.mts +4 -5
  168. package/esm/rules/string/alpha.d.mts.map +1 -1
  169. package/esm/rules/string/credit-card.d.mts +2 -3
  170. package/esm/rules/string/credit-card.d.mts.map +1 -1
  171. package/esm/rules/string/email.d.mts +2 -3
  172. package/esm/rules/string/email.d.mts.map +1 -1
  173. package/esm/rules/string/id-formats.d.mts +6 -7
  174. package/esm/rules/string/id-formats.d.mts.map +1 -1
  175. package/esm/rules/string/index.d.mts +12 -0
  176. package/esm/rules/string/index.mjs +1 -1
  177. package/esm/rules/string/ip.d.mts +7 -8
  178. package/esm/rules/string/ip.d.mts.map +1 -1
  179. package/esm/rules/string/matches.d.mts +2 -3
  180. package/esm/rules/string/matches.d.mts.map +1 -1
  181. package/esm/rules/string/pattern.d.mts +2 -3
  182. package/esm/rules/string/pattern.d.mts.map +1 -1
  183. package/esm/rules/string/string-comparison.d.mts +5 -6
  184. package/esm/rules/string/string-comparison.d.mts.map +1 -1
  185. package/esm/rules/string/strong-password-rule.d.mts +2 -3
  186. package/esm/rules/string/strong-password-rule.d.mts.map +1 -1
  187. package/esm/rules/string/url.d.mts +2 -3
  188. package/esm/rules/string/url.d.mts.map +1 -1
  189. package/esm/rules/string/without-whitespace.d.mts +2 -3
  190. package/esm/rules/string/without-whitespace.d.mts.map +1 -1
  191. package/esm/standard-schema/index.d.mts +4 -0
  192. package/esm/standard-schema/json-schema.d.mts +5 -7
  193. package/esm/standard-schema/json-schema.d.mts.map +1 -1
  194. package/esm/standard-schema/json-schema.mjs.map +1 -1
  195. package/esm/standard-schema/map-result.d.mts +2 -3
  196. package/esm/standard-schema/map-result.d.mts.map +1 -1
  197. package/esm/standard-schema/map-result.mjs.map +1 -1
  198. package/esm/standard-schema/types.d.mts +4 -5
  199. package/esm/standard-schema/types.d.mts.map +1 -1
  200. package/esm/string.d.mts +2 -3
  201. package/esm/string.d.mts.map +1 -1
  202. package/esm/types/conditional-types.d.mts +8 -6
  203. package/esm/types/conditional-types.d.mts.map +1 -1
  204. package/esm/types/context-types.d.mts +24 -15
  205. package/esm/types/context-types.d.mts.map +1 -1
  206. package/esm/types/data-transformer-types.d.mts +3 -5
  207. package/esm/types/data-transformer-types.d.mts.map +1 -1
  208. package/esm/types/date-types.d.mts +2 -3
  209. package/esm/types/date-types.d.mts.map +1 -1
  210. package/esm/types/file.types.d.mts +1 -2
  211. package/esm/types/file.types.d.mts.map +1 -1
  212. package/esm/types/index.d.mts +11 -0
  213. package/esm/types/inference-types.d.mts +15 -14
  214. package/esm/types/inference-types.d.mts.map +1 -1
  215. package/esm/types/mutator-types.d.mts +9 -7
  216. package/esm/types/mutator-types.d.mts.map +1 -1
  217. package/esm/types/result-types.d.mts +12 -7
  218. package/esm/types/result-types.d.mts.map +1 -1
  219. package/esm/types/rule-types.d.mts +33 -15
  220. package/esm/types/rule-types.d.mts.map +1 -1
  221. package/esm/types/schema-types.d.mts +1 -3
  222. package/esm/types/schema-types.d.mts.map +1 -1
  223. package/esm/validators/any-validator.d.mts +1 -3
  224. package/esm/validators/any-validator.d.mts.map +1 -1
  225. package/esm/validators/array-validator.d.mts +2 -3
  226. package/esm/validators/array-validator.d.mts.map +1 -1
  227. package/esm/validators/array-validator.mjs +3 -3
  228. package/esm/validators/array-validator.mjs.map +1 -1
  229. package/esm/validators/base-validator.d.mts +10 -11
  230. package/esm/validators/base-validator.d.mts.map +1 -1
  231. package/esm/validators/base-validator.mjs +52 -21
  232. package/esm/validators/base-validator.mjs.map +1 -1
  233. package/esm/validators/boolean-validator.d.mts +1 -3
  234. package/esm/validators/boolean-validator.d.mts.map +1 -1
  235. package/esm/validators/computed-validator.d.mts +3 -4
  236. package/esm/validators/computed-validator.d.mts.map +1 -1
  237. package/esm/validators/computed-validator.mjs +4 -4
  238. package/esm/validators/computed-validator.mjs.map +1 -1
  239. package/esm/validators/date-validator.d.mts +2 -3
  240. package/esm/validators/date-validator.d.mts.map +1 -1
  241. package/esm/validators/discriminated-union-validator.d.mts +3 -3
  242. package/esm/validators/discriminated-union-validator.d.mts.map +1 -1
  243. package/esm/validators/discriminated-union-validator.mjs +1 -1
  244. package/esm/validators/discriminated-union-validator.mjs.map +1 -1
  245. package/esm/validators/float-validator.d.mts +1 -3
  246. package/esm/validators/float-validator.d.mts.map +1 -1
  247. package/esm/validators/index.d.mts +2 -1
  248. package/esm/validators/index.mjs +1 -1
  249. package/esm/validators/instanceof-validator.d.mts +1 -3
  250. package/esm/validators/instanceof-validator.d.mts.map +1 -1
  251. package/esm/validators/int-validator.d.mts +1 -3
  252. package/esm/validators/int-validator.d.mts.map +1 -1
  253. package/esm/validators/lazy-validator.d.mts +2 -3
  254. package/esm/validators/lazy-validator.d.mts.map +1 -1
  255. package/esm/validators/lazy-validator.mjs +1 -1
  256. package/esm/validators/lazy-validator.mjs.map +1 -1
  257. package/esm/validators/literal-validator.d.mts +1 -3
  258. package/esm/validators/literal-validator.d.mts.map +1 -1
  259. package/esm/validators/managed-validator.d.mts +3 -4
  260. package/esm/validators/managed-validator.d.mts.map +1 -1
  261. package/esm/validators/methods/equality-conditional-methods.d.mts +3 -4
  262. package/esm/validators/methods/equality-conditional-methods.d.mts.map +1 -1
  263. package/esm/validators/methods/forbidden-methods.d.mts +2 -3
  264. package/esm/validators/methods/forbidden-methods.d.mts.map +1 -1
  265. package/esm/validators/methods/present-methods.d.mts +2 -3
  266. package/esm/validators/methods/present-methods.d.mts.map +1 -1
  267. package/esm/validators/methods/required-methods.d.mts +3 -4
  268. package/esm/validators/methods/required-methods.d.mts.map +1 -1
  269. package/esm/validators/number-validator.d.mts +1 -3
  270. package/esm/validators/number-validator.d.mts.map +1 -1
  271. package/esm/validators/number-validator.mjs +12 -8
  272. package/esm/validators/number-validator.mjs.map +1 -1
  273. package/esm/validators/numeric-validator.d.mts +1 -3
  274. package/esm/validators/numeric-validator.d.mts.map +1 -1
  275. package/esm/validators/object-validator.d.mts +2 -3
  276. package/esm/validators/object-validator.d.mts.map +1 -1
  277. package/esm/validators/object-validator.mjs +9 -7
  278. package/esm/validators/object-validator.mjs.map +1 -1
  279. package/esm/validators/primitive-validator.d.mts +1 -3
  280. package/esm/validators/primitive-validator.d.mts.map +1 -1
  281. package/esm/validators/record-validator.d.mts +2 -3
  282. package/esm/validators/record-validator.d.mts.map +1 -1
  283. package/esm/validators/record-validator.mjs +3 -3
  284. package/esm/validators/record-validator.mjs.map +1 -1
  285. package/esm/validators/scalar-validator.d.mts +1 -3
  286. package/esm/validators/scalar-validator.d.mts.map +1 -1
  287. package/esm/validators/scalar-validator.mjs.map +1 -1
  288. package/esm/validators/string-validator.d.mts +1 -3
  289. package/esm/validators/string-validator.d.mts.map +1 -1
  290. package/esm/validators/tuple-validator.d.mts +2 -3
  291. package/esm/validators/tuple-validator.d.mts.map +1 -1
  292. package/esm/validators/tuple-validator.mjs +3 -3
  293. package/esm/validators/tuple-validator.mjs.map +1 -1
  294. package/esm/validators/union-validator.d.mts +4 -4
  295. package/esm/validators/union-validator.d.mts.map +1 -1
  296. package/esm/validators/union-validator.mjs +3 -1
  297. package/esm/validators/union-validator.mjs.map +1 -1
  298. package/llms-full.txt +656 -527
  299. package/llms.txt +1 -1
  300. package/package.json +1 -1
  301. package/skills/bridge-standard-schema/SKILL.md +60 -36
  302. package/skills/compose-seal-modifiers/SKILL.md +89 -53
  303. package/skills/define-structural-shape/SKILL.md +41 -27
  304. package/skills/define-structural-shape/array-methods.md +27 -27
  305. package/skills/define-structural-shape/object-methods.md +38 -30
  306. package/skills/extend-seal-with-plugins/SKILL.md +39 -5
  307. package/skills/generate-json-schema/SKILL.md +13 -13
  308. package/skills/handle-seal-errors/SKILL.md +23 -23
  309. package/skills/index.md +4 -1
  310. package/skills/overview/SKILL.md +10 -2
  311. package/skills/pick-seal-primitive/SKILL.md +45 -45
  312. package/skills/pick-seal-primitive/date-methods.md +96 -96
  313. package/skills/pick-seal-primitive/number-methods.md +55 -55
  314. package/skills/pick-seal-primitive/string-methods.md +107 -102
  315. package/skills/seal-basics/SKILL.md +14 -14
  316. package/cjs/object--LXRt5RF.cjs.map +0 -1
  317. package/cjs/required-methods-DYsRO9aZ.cjs.map +0 -1
  318. package/cjs/string-DsqnKoSB.cjs.map +0 -1
@@ -4,41 +4,41 @@ The inner validator runs against each element. Failure on any element fails the
4
4
 
5
5
  ## Length
6
6
 
7
- | Method | Args | JSON Schema | Example |
8
- |---|---|---|---|
9
- | `.min(n, msg?)` | n/a — see note below | — | use `.minLength(n)` |
10
- | `.minLength(n, msg?)` | inclusive lower bound | `minItems: n` | `v.array(v.string()).minLength(1)` |
11
- | `.maxLength(n, msg?)` | inclusive upper bound | `maxItems: n` | `v.array(v.string()).maxLength(10)` |
12
- | `.length(n, msg?)` | exact length | `minItems=maxItems=n` | `v.array(v.string()).length(3)` |
13
- | `.between(a, b, msg?)` | inclusive range | `minItems: a, maxItems: b` | `v.array(v.string()).between(1, 10)` |
14
- | `.lengthBetween(a, b, msg?)` | alias for `.between()` | `minItems: a, maxItems: b` | — |
7
+ | Method | Args | JSON Schema | Example |
8
+ | ---------------------------- | ---------------------- | -------------------------- | ------------------------------------ |
9
+ | `.min(n, msg?)` | n/a — see note below | — | use `.minLength(n)` |
10
+ | `.minLength(n, msg?)` | inclusive lower bound | `minItems: n` | `v.array(v.string()).minLength(1)` |
11
+ | `.maxLength(n, msg?)` | inclusive upper bound | `maxItems: n` | `v.array(v.string()).maxLength(10)` |
12
+ | `.length(n, msg?)` | exact length | `minItems=maxItems=n` | `v.array(v.string()).length(3)` |
13
+ | `.between(a, b, msg?)` | inclusive range | `minItems: a, maxItems: b` | `v.array(v.string()).between(1, 10)` |
14
+ | `.lengthBetween(a, b, msg?)` | alias for `.between()` | `minItems: a, maxItems: b` | — |
15
15
 
16
16
  **Note on `.min`/`.max`:** the array validator does not expose `.min`/`.max` directly — use `.minLength`/`.maxLength` (or `.between`/`.length`). This is intentional: `.min`/`.max` would conflict with primitive value-comparison semantics if they ever bled into here.
17
17
 
18
18
  ## Uniqueness & sort
19
19
 
20
- | Method | Args | Effect |
21
- |---|---|---|
22
- | `.unique(msg?)` | — | every element is distinct |
20
+ | Method | Args | Effect |
21
+ | --------------------------- | ----------------------------- | ----------------------------- |
22
+ | `.unique(msg?)` | — | every element is distinct |
23
23
  | `.sorted(direction?, msg?)` | `"asc"` (default) or `"desc"` | array is monotonically sorted |
24
24
 
25
25
  ## Mutators (pre-validation reshape)
26
26
 
27
- | Method | Args | Effect |
28
- |---|---|---|
29
- | `.flip()` | — | reverse the array |
30
- | `.reverse()` | — | alias for `.flip()` |
31
- | `.onlyUnique()` | — | dedupe before validation |
32
- | `.sort(direction?, key?)` | `"asc"` (default) or `"desc"`; optional sort key for object items | sort before validation |
27
+ | Method | Args | Effect |
28
+ | ------------------------- | ----------------------------------------------------------------- | ------------------------ |
29
+ | `.flip()` | — | reverse the array |
30
+ | `.reverse()` | — | alias for `.flip()` |
31
+ | `.onlyUnique()` | — | dedupe before validation |
32
+ | `.sort(direction?, key?)` | `"asc"` (default) or `"desc"`; optional sort key for object items | sort before validation |
33
33
 
34
34
  `onlyUnique()` and `unique()` differ: the mutator silently dedupes; the rule fails when duplicates are present. Pick by intent — coerce vs reject.
35
35
 
36
36
  ## Inner validator notes
37
37
 
38
38
  ```ts
39
- v.array(v.string().email()) // every element must be a valid email
40
- v.array(userSchema) // every element must satisfy userSchema
41
- v.array(v.array(v.int())) // matrix-ish — Infer is recursive
39
+ v.array(v.string().email()); // every element must be a valid email
40
+ v.array(userSchema); // every element must satisfy userSchema
41
+ v.array(v.array(v.int())); // matrix-ish — Infer is recursive
42
42
  ```
43
43
 
44
44
  Element validators run with element-level context. `path` becomes `${parent}.${index}` for error messages. Cross-field rules inside element schemas resolve to that element's fields, not the array.
@@ -56,23 +56,23 @@ Element validators run with element-level context. `path` becomes `${parent}.${i
56
56
 
57
57
  ```ts
58
58
  // Tags
59
- v.array(v.string().min(1)).unique().between(1, 10)
59
+ v.array(v.string().min(1)).unique().between(1, 10);
60
60
 
61
61
  // User list
62
- v.array(userSchema).minLength(1)
62
+ v.array(userSchema).minLength(1);
63
63
 
64
64
  // Pre-sorted, deduped
65
- v.array(v.string()).onlyUnique().sort("asc")
65
+ v.array(v.string()).onlyUnique().sort("asc");
66
66
 
67
67
  // Reverse-emit
68
- v.array(v.string()).flip()
68
+ v.array(v.string()).flip();
69
69
 
70
70
  // Matrix
71
- v.array(v.array(v.number())).minLength(1)
71
+ v.array(v.array(v.number())).minLength(1);
72
72
 
73
73
  // Sorted ids
74
- v.array(v.string().uuid()).sorted("asc").unique()
74
+ v.array(v.string().uuid()).sorted("asc").unique();
75
75
 
76
76
  // Optional list with default
77
- v.array(v.string()).default([]).optional()
77
+ v.array(v.string()).default([]).optional();
78
78
  ```
@@ -9,10 +9,12 @@ These return a new `ObjectValidator` with a transformed schema. The TypeScript i
9
9
  ### `.extend(schemaOrValidator)` — add fields, keep config
10
10
 
11
11
  ```ts
12
- const baseUser = v.object({
13
- name: v.string(),
14
- email: v.string().email(),
15
- }).allowUnknown();
12
+ const baseUser = v
13
+ .object({
14
+ name: v.string(),
15
+ email: v.string().email(),
16
+ })
17
+ .allowUnknown();
16
18
 
17
19
  const adminUser = baseUser.extend({
18
20
  role: v.string().oneOf(["admin", "superadmin"]),
@@ -26,10 +28,12 @@ Accepts a plain schema object **or** another `ObjectValidator` (only its schema
26
28
 
27
29
  ```ts
28
30
  const base = v.object({ name: v.string() }).allowUnknown();
29
- const audit = v.object({
30
- createdAt: v.date(),
31
- updatedAt: v.date(),
32
- }).stripUnknown();
31
+ const audit = v
32
+ .object({
33
+ createdAt: v.date(),
34
+ updatedAt: v.date(),
35
+ })
36
+ .stripUnknown();
33
37
 
34
38
  const merged = base.merge(audit);
35
39
  // type: { name; createdAt; updatedAt }
@@ -66,8 +70,8 @@ Returns `ObjectValidator<Omit<TSchema, K>>`. Inverse of `.pick`. Keeps original
66
70
  ### `.partial(...keys?)` — mark fields optional
67
71
 
68
72
  ```ts
69
- fullUser.partial() // every field becomes optional
70
- fullUser.partial("password") // only `password` becomes optional
73
+ fullUser.partial(); // every field becomes optional
74
+ fullUser.partial("password"); // only `password` becomes optional
71
75
  ```
72
76
 
73
77
  Walks the schema and applies `.optional()` to each named field (or all if no keys given). `Infer<>` makes those keys optional.
@@ -75,7 +79,7 @@ Walks the schema and applies `.optional()` to each named field (or all if no key
75
79
  ### `.requiredFields(...keys?)` — mark fields required
76
80
 
77
81
  ```ts
78
- const partialUser = fullUser.partial(); // all optional
82
+ const partialUser = fullUser.partial(); // all optional
79
83
  const updateUser = partialUser.requiredFields("id", "email");
80
84
  // id and email are required again, others stay optional
81
85
  ```
@@ -86,29 +90,31 @@ Inverse of `.partial`.
86
90
 
87
91
  By default, extra keys in input are silently dropped from `data` (no error, no forward).
88
92
 
89
- | Method | Effect |
90
- |---|---|
91
- | `.allowUnknown(allow = true)` | extra **direct-child** keys forward as-is |
92
- | `.stripUnknown()` | explicit drop (mutator-based — affects `data` shape) |
93
- | `.allow(...keys)` | whitelist specific extras to forward without validation |
93
+ | Method | Effect |
94
+ | ----------------------------- | ------------------------------------------------------- |
95
+ | `.allowUnknown(allow = true)` | extra **direct-child** keys forward as-is |
96
+ | `.stripUnknown()` | explicit drop (mutator-based — affects `data` shape) |
97
+ | `.allow(...keys)` | whitelist specific extras to forward without validation |
94
98
 
95
99
  `.allowUnknown()` only affects direct children — nested objects keep their own policies. For a fully permissive object including nested children, set `.allowUnknown()` on each level.
96
100
 
97
101
  ### Output DTOs — stripping extras instead of rejecting them
98
102
 
99
- The default is to **reject** unknown keys, which is what you want for inbound payloads. For an *outbound* DTO — where the record legitimately carries internal fields you simply don't want to expose — use `.stripUnknown()`, which drops them and returns the clean object:
103
+ The default is to **reject** unknown keys, which is what you want for inbound payloads. For an _outbound_ DTO — where the record legitimately carries internal fields you simply don't want to expose — use `.stripUnknown()`, which drops them and returns the clean object:
100
104
 
101
105
  ```ts
102
- const publicArticle = v.object({
103
- id: v.string(),
104
- title: v.string(),
105
- }).stripUnknown();
106
+ const publicArticle = v
107
+ .object({
108
+ id: v.string(),
109
+ title: v.string(),
110
+ })
111
+ .stripUnknown();
106
112
 
107
113
  const { isValid, data } = await v.validate(publicArticle, record);
108
114
  // record had version / authorId / status; data has only id + title
109
115
  ```
110
116
 
111
- Without `.stripUnknown()` the same record fails with *"The schema contains unknown keys: …"* rather than being cleaned.
117
+ Without `.stripUnknown()` the same record fails with _"The schema contains unknown keys: …"_ rather than being cleaned.
112
118
 
113
119
  :::note[`data` is `undefined` on failure — since 4.9.2]
114
120
  A failed validation returns `data: undefined`, never the input it rejected. Before 4.9.2 an `object` returned the raw input on failure (while `discriminatedUnion` returned `undefined`), so a caller who read `data` without branching on `isValid` forwarded the exact fields the schema existed to exclude. Always branch on `isValid`.
@@ -116,8 +122,8 @@ A failed validation returns `data: undefined`, never the input it rejected. Befo
116
122
 
117
123
  ## Object-level mutators
118
124
 
119
- | Method | Args | Effect |
120
- |---|---|---|
125
+ | Method | Args | Effect |
126
+ | ------------------- | -------------- | ------------------------------------------------ |
121
127
  | `.trim(recursive?)` | default `true` | recursively trim string values across the object |
122
128
 
123
129
  For per-field mutators, attach on the field validator instead.
@@ -130,7 +136,7 @@ The cross-field methods (`.sameAs`, `.requiredIf`, `.requiredWith`, etc.) are **
130
136
  v.object({
131
137
  password: v.string(),
132
138
  passwordConfirm: v.string().sameAs("password"),
133
- })
139
+ });
134
140
  ```
135
141
 
136
142
  If you call cross-field rules outside a `v.object`, sibling resolution silently passes — there's nobody to compare against.
@@ -138,7 +144,7 @@ If you call cross-field rules outside a `v.object`, sibling resolution silently
138
144
  ## `Infer<>` semantics
139
145
 
140
146
  ```ts
141
- Infer<typeof userSchema>
147
+ Infer<typeof userSchema>;
142
148
  // { reqField: T; optField?: T }
143
149
  ```
144
150
 
@@ -171,10 +177,12 @@ const timestamps = v.object({
171
177
  const userWithAudit = baseUser.extend(timestamps);
172
178
 
173
179
  // Permissive container that forwards extras
174
- const eventEnvelope = v.object({
175
- type: v.literal("user.created", "user.updated"),
176
- payload: v.object({}).allowUnknown(),
177
- }).allowUnknown();
180
+ const eventEnvelope = v
181
+ .object({
182
+ type: v.literal("user.created", "user.updated"),
183
+ payload: v.object({}).allowUnknown(),
184
+ })
185
+ .allowUnknown();
178
186
 
179
187
  // Confirmation pattern with omit
180
188
  const signupSchema = v.object({
@@ -21,7 +21,7 @@ Seal exposes a plugin system so you can add validator methods without forking th
21
21
  import type { SealPlugin } from "@warlock.js/seal";
22
22
 
23
23
  type SealPlugin = {
24
- name: string; // unique identifier — duplicates warn and skip install
24
+ name: string; // unique identifier — duplicates warn and skip install
25
25
  version?: string;
26
26
  description?: string;
27
27
  install: (context: { name: string; version?: string }) => void | Promise<void>;
@@ -59,8 +59,8 @@ import { registerPlugin, unregisterPlugin, hasPlugin, getInstalledPlugins } from
59
59
  await registerPlugin(slugPlugin);
60
60
  // console.warn if "slug" is already installed; otherwise install() runs and the plugin is tracked.
61
61
 
62
- hasPlugin("slug"); // → true
63
- getInstalledPlugins(); // → [slugPlugin]
62
+ hasPlugin("slug"); // → true
63
+ getInstalledPlugins(); // → [slugPlugin]
64
64
 
65
65
  await unregisterPlugin("slug");
66
66
  // runs slugPlugin.uninstall?.(); removes from registry.
@@ -163,8 +163,8 @@ For a method that creates a **new validator** (not chained from an existing one)
163
163
  ## Introspection — checking what's loaded
164
164
 
165
165
  ```ts
166
- hasPlugin("slug"); // boolean
167
- getInstalledPlugins(); // SealPlugin[]
166
+ hasPlugin("slug"); // boolean
167
+ getInstalledPlugins(); // SealPlugin[]
168
168
  ```
169
169
 
170
170
  Use these in startup diagnostics or in tests that need to assert a plugin is registered before exercising a method that depends on it.
@@ -176,3 +176,37 @@ Use these in startup diagnostics or in tests that need to assert a plugin is reg
176
176
  - Don't make the plugin's `install` function depend on shared state mutable from elsewhere. Plugins should be idempotent — installing twice (or registering across hot-reloads) should not break anything.
177
177
  - Don't ship a plugin that overrides a built-in method without a clear reason. If you must, `uninstall` should restore the original — but the better path is a different method name.
178
178
  - Don't author one plugin per method. Group related methods (e.g. "country-specific validators") into one plugin so the install/uninstall lifecycle is coherent.
179
+
180
+ ## Returning validated rule output
181
+
182
+ A custom rule's success result can contain `data` to replace the current value:
183
+ `{ isValid: true, data: replacement }`. Later rules, transformers and refinements
184
+ receive that replacement. Omitting `data` preserves the current value; a present
185
+ `data: undefined` explicitly clears it. This is also how a union preserves the
186
+ selected branch's validated defaults, catches and child transformations.
187
+
188
+ ## Reporting several errors from one rule
189
+
190
+ A failed rule result can carry `errors: [{ error, path }, ...]` to report more than
191
+ one error, each at its own path; every entry gets the rule's `name` as its `type`.
192
+ `v.union` uses this to relay the closest branch's errors. Return as many as fit the
193
+ caller's `context.configurations?.firstErrorOnly` (default `true`: one).
194
+
195
+ ```ts
196
+ import { invalidRule, type SchemaRule } from "@warlock.js/seal";
197
+
198
+ const bothDates: SchemaRule = {
199
+ name: "bothDates",
200
+ async validate(value: { from?: unknown; to?: unknown }, context) {
201
+ const missing = (["from", "to"] as const).filter((key) => !value?.[key]);
202
+ if (missing.length === 0) return { isValid: true };
203
+
204
+ const failed = invalidRule(this, context);
205
+ const errors = missing.map((key) => ({
206
+ error: `The ${key} date is required`,
207
+ path: context.path ? `${context.path}.${key}` : key,
208
+ }));
209
+ return { ...failed, errors };
210
+ },
211
+ };
212
+ ```
@@ -29,19 +29,19 @@ userSchema.toJsonSchema("draft-2020-12");
29
29
 
30
30
  ```ts
31
31
  type JsonSchemaTarget =
32
- | "draft-2020-12" // default — modern JSON Schema
33
- | "draft-07" // older tooling, Swagger 2.0
34
- | "openapi-3.0" // uses { nullable: true } instead of type unions
35
- | "openai-strict" // OpenAI Structured Outputs strict mode
32
+ | "draft-2020-12" // default — modern JSON Schema
33
+ | "draft-07" // older tooling, Swagger 2.0
34
+ | "openapi-3.0" // uses { nullable: true } instead of type unions
35
+ | "openai-strict"; // OpenAI Structured Outputs strict mode
36
36
  ```
37
37
 
38
38
  Pick by consumer:
39
39
 
40
- | Consumer | Target |
41
- | --- | --- |
42
- | Modern tooling, no specific reason otherwise | `"draft-2020-12"` |
43
- | Swagger 2.0 / older OpenAPI / older form builders | `"draft-07"` |
44
- | OpenAPI 3.0 spec (uses `nullable: true`) | `"openapi-3.0"` |
40
+ | Consumer | Target |
41
+ | --------------------------------------------------------------- | ----------------- |
42
+ | Modern tooling, no specific reason otherwise | `"draft-2020-12"` |
43
+ | Swagger 2.0 / older OpenAPI / older form builders | `"draft-07"` |
44
+ | OpenAPI 3.0 spec (uses `nullable: true`) | `"openapi-3.0"` |
45
45
  | OpenAI `response_format: { type: "json_schema", strict: true }` | `"openai-strict"` |
46
46
 
47
47
  ## OpenAI structured outputs (`openai-strict`)
@@ -122,8 +122,8 @@ Cleanly mapped:
122
122
 
123
123
  Some seal constructs have no JSON Schema representation:
124
124
 
125
- - **Cross-field rules** (`sameAs`, `requiredIf`, `requiredWith`, etc.) — runtime-only. The generated schema describes the *shape*, not the inter-field invariants.
126
- - **Transformers and mutators** — output reshaping doesn't appear in the schema; the schema reflects *post-mutator, pre-transformer* shape (since that's what rules see and what the LLM is asked to produce for `openai-strict`).
125
+ - **Cross-field rules** (`sameAs`, `requiredIf`, `requiredWith`, etc.) — runtime-only. The generated schema describes the _shape_, not the inter-field invariants.
126
+ - **Transformers and mutators** — output reshaping doesn't appear in the schema; the schema reflects _post-mutator, pre-transformer_ shape (since that's what rules see and what the LLM is asked to produce for `openai-strict`).
127
127
  - **`v.computed` / `v.managed`** — **skipped** entirely by the parent `v.object`; they never appear in `properties`. They aren't part of the data contract. (Calling `.toJsonSchema()` directly on one throws — the parent object is responsible for skipping them.)
128
128
  - **`v.instanceof(Ctor)`** — produces `{}`. Class identity isn't expressible. For `File`, attach `{ type: "string", format: "binary" }` manually after generation if needed for OpenAPI.
129
129
  - **`v.any()`** — produces `{}` deliberately (any value is valid).
@@ -132,10 +132,10 @@ Boolean rules `accepted` / `declined` and similar coercion-style rules are also
132
132
 
133
133
  ## When the generated schema rejects valid data
134
134
 
135
- If the schema validator itself accepts data but the *generated* JSON Schema rejects the same data downstream, the cause is usually one of:
135
+ If the schema validator itself accepts data but the _generated_ JSON Schema rejects the same data downstream, the cause is usually one of:
136
136
 
137
137
  - **Cross-field rule.** The generated schema doesn't enforce it, but a separate consumer might. (Or the runtime check fired at a different stage.)
138
- - **Transformer running on the wrong side.** The schema describes the input shape (or strict-mode normalized form). If your transformer reshapes `Date` to ISO string for `data`, the *input* still needs to be a Date-parseable thing.
138
+ - **Transformer running on the wrong side.** The schema describes the input shape (or strict-mode normalized form). If your transformer reshapes `Date` to ISO string for `data`, the _input_ still needs to be a Date-parseable thing.
139
139
  - **`openai-strict` quirk.** Optional fields show as `["T", "null"]` rather than omitted — if the model omits them entirely (without sending `null`), strict mode fails. The fix is on the prompt side: tell the model to send `null` for unused fields.
140
140
 
141
141
  ## Cost note
@@ -9,13 +9,13 @@ description: 'Read `ValidationResult` — `isValid`, `errors[]`, `data`. Branch
9
9
 
10
10
  ```ts
11
11
  type TypedValidationResult<TData> =
12
- | { isValid: true; data: TData; errors: ValidationError[] } // TData = Infer.Output<typeof schema>
12
+ | { isValid: true; data: TData; errors: ValidationError[] } // TData = Infer.Output<typeof schema>
13
13
  | { isValid: false; data: undefined; errors: ValidationError[] };
14
14
 
15
15
  type ValidationError = {
16
- type: string; // rule type that failed — "required", "email", "min", "string", ...
17
- error: string; // resolved message (translated, with attribute substitution)
18
- input: string; // input field name — "email" or "address.city"
16
+ type: string; // rule type that failed — "required", "email", "min", "string", ...
17
+ error: string; // resolved message (translated, with attribute substitution)
18
+ input: string; // input field name — "email" or "address.city"
19
19
  };
20
20
  ```
21
21
 
@@ -34,7 +34,7 @@ const schema = v.object({
34
34
  const result = await validate(schema, input);
35
35
 
36
36
  if (result.isValid) {
37
- return result.data; // typed as the inferred shape (post-transformer)
37
+ return result.data; // typed as the inferred shape (post-transformer)
38
38
  }
39
39
 
40
40
  return {
@@ -49,15 +49,13 @@ return {
49
49
  const result = await validate(schema, input);
50
50
 
51
51
  if (!result.isValid) {
52
- const emailMissing = result.errors.find(
53
- e => e.input === "email" && e.type === "required",
54
- );
52
+ const emailMissing = result.errors.find((e) => e.input === "email" && e.type === "required");
55
53
  if (emailMissing) {
56
54
  return { redirect: "/signup", reason: "no email" };
57
55
  }
58
56
 
59
57
  const ageInvalid = result.errors.find(
60
- e => e.input === "age" && (e.type === "int" || e.type === "min"),
58
+ (e) => e.input === "age" && (e.type === "int" || e.type === "min"),
61
59
  );
62
60
  if (ageInvalid) {
63
61
  return { error: "Age must be 13 or older" };
@@ -73,18 +71,20 @@ These are the strings you'll see in `error.type`:
73
71
 
74
72
  The `type` is the rule's own `name` field — not the method you called. Several methods map to one shared rule name (e.g. `.min()` / `.minLength()` on a string both surface as `minLength`; `.sameAs()` surfaces as `equalsField`). The table below lists the actual `type` strings:
75
73
 
76
- | Type | Produced by |
77
- | --- | --- |
78
- | `required`, `present` | `.required()`, `.present()` (note: `.optional()` has no error type — it clears the required rule) |
79
- | `requiredIf`, `requiredWith`, `requiredWithout`, `requiredUnless` | conditional required methods |
80
- | `string`, `number`, `int`, `float`, `boolean`, `scalar`, `object`, `array`, `date` | type guards from `matchesType` |
81
- | `minLength`, `maxLength`, `betweenLength`, `length` | string / array length rules (`.min`/`.max` on a string alias to `minLength`/`maxLength`) |
82
- | `min`, `max`, `betweenNumbers` | number range rules (`.min`/`.max`/`.between` on a number) |
83
- | `email`, `url`, `uuid`, `pattern`, `matches` | string format rules (`.pattern()` → `pattern`) |
84
- | `literal`, `enum`, `in`, `allowedValues`, `notAllowedValues` | value-membership rules (`.oneOf` aliases `in`; `.notIn`/`.forbids` → `notAllowedValues`; `.allowsOnly` → `allowedValues`) |
85
- | `instanceof` | `v.instanceof(Ctor)` |
86
- | `equalsField`, `notEqualsField` | cross-field equality (`.sameAs` → `equalsField`; `.differentFrom` → `notEqualsField`) |
87
- | `minDate`, `maxDate`, `beforeField`, `afterField`, `today`, `past`, `future`, `weekDay`, `weekend`, `businessDay`, `birthday` | date rules (`.min`/`.max` → `minDate`/`maxDate`; `.before`/`.after` → `beforeField`/`afterField`) |
74
+ | Type | Produced by |
75
+ | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
76
+ | `required`, `present` | `.required()`, `.present()` (note: `.optional()` has no error type — it clears the required rule) |
77
+ | `requiredIf`, `requiredWith`, `requiredWithout`, `requiredUnless` | conditional required methods |
78
+ | `string`, `number`, `int`, `float`, `boolean`, `scalar`, `object`, `array`, `date` | type guards from `matchesType` |
79
+ | `minLength`, `maxLength`, `betweenLength`, `length` | string / array length rules (`.min`/`.max` on a string alias to `minLength`/`maxLength`) |
80
+ | `min`, `max`, `betweenNumbers` | number range rules (`.min`/`.max`/`.between` on a number) |
81
+ | `email`, `url`, `uuid`, `pattern`, `matches` | string format rules (`.pattern()` → `pattern`) |
82
+ | `literal`, `enum`, `in`, `allowedValues`, `notAllowedValues` | value-membership rules (`.oneOf` aliases `in`; `.notIn`/`.forbids` → `notAllowedValues`; `.allowsOnly` → `allowedValues`) |
83
+ | `instanceof` | `v.instanceof(Ctor)` |
84
+ | `equalsField`, `notEqualsField` | cross-field equality (`.sameAs` → `equalsField`; `.differentFrom` → `notEqualsField`) |
85
+ | `minDate`, `maxDate`, `beforeField`, `afterField`, `today`, `past`, `future`, `weekDay`, `weekend`, `businessDay`, `birthday` | date rules (`.min`/`.max` → `minDate`/`maxDate`; `.before`/`.after` → `beforeField`/`afterField`) |
86
+
87
+ `v.union(...)` reports `union`: when no branch passes, it relays the closest branch's errors (fewest mismatched literals, then fewest errors, then deepest path) at their own paths, all typed `union`. A custom rule can do the same by returning `errors: [{ error, path }, ...]` with its failed result.
88
88
 
89
89
  If you write custom rules, the `type` name you set on the rule object is what shows up here. Pick stable, kebab-or-camel-case names — they become a public API.
90
90
 
@@ -116,7 +116,7 @@ For attribute display names ("email" → "Email Address"), use `.attributes({ em
116
116
 
117
117
  ## When to throw
118
118
 
119
- Don't wrap `validate()` in try/catch for *validation* failures — those land in `result.errors`. The only thing that *throws* is a programming bug:
119
+ Don't wrap `validate()` in try/catch for _validation_ failures — those land in `result.errors`. The only thing that _throws_ is a programming bug:
120
120
 
121
121
  - A rule's callback threw (e.g. you wrote `async validate() { throw new Error(...) }`).
122
122
  - A transformer threw on output.
@@ -132,7 +132,7 @@ If you're surfacing seal errors through HTTP / RPC, the typical shape is:
132
132
  if (!result.isValid) {
133
133
  return reply.code(422).send({
134
134
  error: "validation_failed",
135
- fields: result.errors.map(e => ({
135
+ fields: result.errors.map((e) => ({
136
136
  field: e.input,
137
137
  type: e.type,
138
138
  message: e.error,
package/skills/index.md CHANGED
@@ -1,11 +1,13 @@
1
1
  ---
2
- description: "Type-safe, framework-agnostic validation for Warlock.js: schemas, coercion, modifiers, JSON Schema, Standard Schema. Exports `v`, `validate`, `ObjectValidator`, `StringValidator`, `Infer`, reached as `v.object`, `v.string`, `v.array`, `v.enum`, `v.union`. Use for: \"validate this request body\", \"require an email and a min-length password\", \"infer the TypeScript type from a schema\", \"make a field optional or give it a default\", \"generate JSON Schema\", \"write a custom rule\". Not this package: database rules and file validators live in @warlock.js/core; model schemas and persistence are @warlock.js/cascade."
2
+ description: 'Type-safe, framework-agnostic validation for Warlock.js: schemas, coercion, modifiers, JSON Schema, Standard Schema. Exports `v`, `validate`, `ObjectValidator`, `StringValidator`, `Infer`, reached as `v.object`, `v.string`, `v.array`, `v.enum`, `v.union`. Use for: "validate this request body", "require an email and a min-length password", "infer the TypeScript type from a schema", "make a field optional or give it a default", "generate JSON Schema", "write a custom rule". Not this package: database rules and file validators live in @warlock.js/core; model schemas and persistence are @warlock.js/cascade.'
3
3
  ---
4
+
4
5
  # @warlock.js/seal
5
6
 
6
7
  Compose validators from the `v` factory (`v.object({ email: v.string().email() })`), run them with `validate(schema, data)`, and infer static types with `Infer`. Validators chain rules, mutators (transforms) and modifiers; the same schema works as a Standard Schema.
7
8
 
8
9
  ## The 80% path
10
+
9
11
  1. Orient: `overview.md`, `seal-basics.md`.
10
12
  2. Choose the right primitive: `pick-seal-primitive.md`.
11
13
  3. Shape nested data: `define-structural-shape.md`.
@@ -14,6 +16,7 @@ Compose validators from the `v` factory (`v.object({ email: v.string().email() }
14
16
  6. Interop: `bridge-standard-schema.md`, `generate-json-schema.md`; extend via `extend-seal-with-plugins.md`.
15
17
 
16
18
  ## Conventions and pitfalls
19
+
17
20
  - Validation is async; always `await validate(...)`.
18
21
  - The package root is framework-agnostic; database-backed rules (unique, exists) and `FileValidator` come from `@warlock.js/core`, not here.
19
22
  - Derive types with `Infer` instead of hand-writing parallel interfaces.
@@ -5,7 +5,7 @@ description: "@warlock.js/seal overview for orientation and choosing the focused
5
5
 
6
6
  # `@warlock.js/seal` — overview
7
7
 
8
- A type-safe, framework-agnostic validation library. You build a schema with the `v` factory, run it with `validate(schema, data)`, and pull the static type out with `Infer<typeof schema>`. One schema is the runtime check *and* the TypeScript type *and* (optionally) a JSON Schema.
8
+ A type-safe, framework-agnostic validation library. You build a schema with the `v` factory, run it with `validate(schema, data)`, and pull the static type out with `Infer<typeof schema>`. One schema is the runtime check _and_ the TypeScript type _and_ (optionally) a JSON Schema.
9
9
 
10
10
  Standalone — no framework required. Ships built-in with `@warlock.js/core`, which adds framework-specific validators (file uploads, database existence/uniqueness rules) on top.
11
11
 
@@ -28,31 +28,39 @@ Eight task skills. Most schemas only need `seal-basics` + `pick-seal-primitive`
28
28
  ### Foundations
29
29
 
30
30
  #### `seal-basics`
31
+
31
32
  Start here. The `v` factory, `validate(schema, data)`, and `Infer<typeof schema>`.
32
33
 
33
34
  ### Building schemas
34
35
 
35
36
  #### `pick-seal-primitive`
37
+
36
38
  Choose the right primitive — `string` / `int` / `literal` / `date` / `enum` / `computed` / `managed` / `instanceof` / `any`. Covers the close calls (`string` vs `scalar`, `int` vs `number`, `literal` vs `enum`).
37
39
 
38
40
  #### `define-structural-shape`
41
+
39
42
  Compose `v.object` / `v.array` / `v.record` / `v.tuple` / `v.union` / `v.discriminatedUnion` / `v.lazy` — object schemas, dynamic-keyed records, tagged unions, recursive shapes.
40
43
 
41
44
  #### `compose-seal-modifiers`
45
+
42
46
  Cross-cutting modifiers — `.optional` / `.nullable` / `.default` / `.catch` / `.omit` / membership rules — plus the mutator-vs-transformer pipeline and `Infer.Input` vs `Infer.Output`.
43
47
 
44
48
  ### Output + interop
45
49
 
46
50
  #### `handle-seal-errors`
51
+
47
52
  Read a `ValidationResult` — `isValid`, `errors[]`, `data`. Branch on `error.type`, customize messages, hook translation.
48
53
 
49
54
  #### `generate-json-schema`
55
+
50
56
  `schema.toJsonSchema(target)` — draft-2020-12 / draft-07 / openapi-3.0 / openai-strict. For OpenAI structured outputs, OpenAPI specs, any JSON-Schema consumer.
51
57
 
52
58
  #### `bridge-standard-schema`
53
- Standard Schema interop — why a `StandardSchemaV1<T>` slot might reject a schema, the phantom-intersection at the `v` factory return, Cascade `Model<TSchema>` variance. For migrating off `as unknown as` casts.
59
+
60
+ Standard Schema interop — why a `StandardSchemaV1<T>` slot might reject a schema, object input/output class parameters and primitive factory intersections, Cascade `Model<TSchema>` variance. For migrating off `as unknown as` casts.
54
61
 
55
62
  #### `extend-seal-with-plugins`
63
+
56
64
  Author a plugin to add validator methods (`.slug`, `.postalCode`, …) — the `SealPlugin` shape, `registerPlugin` lifecycle, TS prototype augmentation.
57
65
 
58
66
  ## Configuration