@clementine-solutions/jane-io 1.0.1 → 1.0.2

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 (204) hide show
  1. package/dist/core/analysis/diff.js +88 -0
  2. package/dist/core/analysis/explain.js +117 -0
  3. package/dist/core/analysis/index.js +26 -0
  4. package/dist/core/analysis/replay.js +68 -0
  5. package/dist/core/analysis/telemetry.js +123 -0
  6. package/dist/core/boundary-rules/at-most-one.js +38 -0
  7. package/dist/core/boundary-rules/conditionally-required.js +44 -0
  8. package/dist/core/boundary-rules/date-range.js +39 -0
  9. package/dist/core/boundary-rules/index.js +21 -0
  10. package/dist/core/boundary-rules/mutually-exclusive.js +38 -0
  11. package/dist/core/boundary-rules/no-unknown-fields.js +39 -0
  12. package/dist/core/boundary-rules/require-all.js +39 -0
  13. package/dist/core/boundary-rules/require-one.js +41 -0
  14. package/dist/core/common/events.js +82 -0
  15. package/dist/core/common/fluent.js +429 -0
  16. package/dist/core/common/index.js +31 -0
  17. package/dist/core/common/policy.js +550 -0
  18. package/dist/core/common/utilities.js +177 -0
  19. package/dist/core/common/wildcard.js +63 -0
  20. package/dist/core/field-path/construct.js +189 -0
  21. package/dist/core/field-path/format.js +154 -0
  22. package/dist/core/field-path/index.js +26 -0
  23. package/dist/core/field-path/utilities.js +138 -0
  24. package/dist/core/field-path/walk.js +80 -0
  25. package/dist/core/fluent-registry.js +151 -0
  26. package/dist/core/normalizers/array/compact-sparse-array.js +40 -0
  27. package/dist/core/normalizers/array/flatten-one-level.js +45 -0
  28. package/dist/core/normalizers/array/remove-empty-string-items.js +34 -0
  29. package/dist/core/normalizers/array/remove-null-items.js +34 -0
  30. package/dist/core/normalizers/array/remove-undefined-items.js +34 -0
  31. package/dist/core/normalizers/date/invalid-date-to-undefined.js +35 -0
  32. package/dist/core/normalizers/index.js +46 -0
  33. package/dist/core/normalizers/normalizer-register.js +41 -0
  34. package/dist/core/normalizers/number/infinity-to-undefined.js +35 -0
  35. package/dist/core/normalizers/number/nan-to-undefined.js +34 -0
  36. package/dist/core/normalizers/number/normalize-negative-zero.js +33 -0
  37. package/dist/core/normalizers/object/remove-empty-array-keys.js +38 -0
  38. package/dist/core/normalizers/object/remove-empty-object-keys.js +42 -0
  39. package/dist/core/normalizers/object/remove-empty-string-keys.js +37 -0
  40. package/dist/core/normalizers/object/remove-null-keys.js +37 -0
  41. package/dist/core/normalizers/object/remove-undefined-keys.js +37 -0
  42. package/dist/core/normalizers/string/collapse-whitespace.js +35 -0
  43. package/dist/core/normalizers/string/empty-to-undefined.js +33 -0
  44. package/dist/core/normalizers/string/trim.js +34 -0
  45. package/dist/core/parsers/index.js +43 -0
  46. package/dist/core/parsers/parse-array-string.js +36 -0
  47. package/dist/core/parsers/parse-bigint-string.js +33 -0
  48. package/dist/core/parsers/parse-binary-string.js +33 -0
  49. package/dist/core/parsers/parse-boolean-string.js +27 -0
  50. package/dist/core/parsers/parse-date-string.js +30 -0
  51. package/dist/core/parsers/parse-duration-string.js +37 -0
  52. package/dist/core/parsers/parse-hex-string.js +29 -0
  53. package/dist/core/parsers/parse-integer-string.js +30 -0
  54. package/dist/core/parsers/parse-json-string.js +35 -0
  55. package/dist/core/parsers/parse-numeric-string.js +29 -0
  56. package/dist/core/parsers/parse-object-string.js +36 -0
  57. package/dist/core/parsers/parse-octal-string.js +33 -0
  58. package/dist/core/parsers/parse-scientific-notation-string.js +30 -0
  59. package/dist/core/parsers/parse-url-string.js +36 -0
  60. package/dist/core/pipeline/boundary.js +256 -0
  61. package/dist/core/pipeline/contain.js +339 -0
  62. package/dist/core/pipeline/index.js +37 -0
  63. package/dist/core/pipeline/normalize.js +76 -0
  64. package/dist/core/pipeline/parse.js +91 -0
  65. package/dist/core/pipeline/pipeline.js +418 -0
  66. package/dist/core/pipeline/scan.js +115 -0
  67. package/dist/core/pipeline/validate.js +74 -0
  68. package/dist/core/scanners/any/scan-for-sentinels.js +35 -0
  69. package/dist/core/scanners/array/array-is-deep.js +38 -0
  70. package/dist/core/scanners/array/array-is-heterogenous.js +39 -0
  71. package/dist/core/scanners/array/array-is-large.js +32 -0
  72. package/dist/core/scanners/bigint/bigint-is-large.js +34 -0
  73. package/dist/core/scanners/bigint/bigint-not-safe.js +34 -0
  74. package/dist/core/scanners/date/date-is-before-epoch.js +32 -0
  75. package/dist/core/scanners/date/date-is-far-future.js +32 -0
  76. package/dist/core/scanners/date/date-is-invalid.js +31 -0
  77. package/dist/core/scanners/index.js +58 -0
  78. package/dist/core/scanners/number/number-is-infinite.js +31 -0
  79. package/dist/core/scanners/number/number-is-nan.js +31 -0
  80. package/dist/core/scanners/number/number-is-too-large.js +33 -0
  81. package/dist/core/scanners/number/number-is-unsafe-integer.js +31 -0
  82. package/dist/core/scanners/object/object-has-circular-references.js +43 -0
  83. package/dist/core/scanners/object/object-has-many-keys.js +33 -0
  84. package/dist/core/scanners/object/object-is-deep.js +38 -0
  85. package/dist/core/scanners/scanner-registry.js +36 -0
  86. package/dist/core/scanners/string/string-has-unsafe-unicode.js +32 -0
  87. package/dist/core/scanners/string/string-has-whitespace-edges.js +31 -0
  88. package/dist/core/scanners/string/string-is-long.js +32 -0
  89. package/dist/core/scanners/unknown/unknown-not-scannable.js +34 -0
  90. package/dist/core/shapes/analysis.js +11 -0
  91. package/dist/core/shapes/boundary.js +11 -0
  92. package/dist/core/shapes/events.js +10 -0
  93. package/dist/core/shapes/field-path.js +10 -0
  94. package/dist/core/shapes/index.js +11 -0
  95. package/dist/core/shapes/normalize.js +11 -0
  96. package/dist/core/shapes/parse.js +11 -0
  97. package/dist/core/shapes/pipeline.js +11 -0
  98. package/dist/core/shapes/policy.js +11 -0
  99. package/dist/core/shapes/public.js +10 -0
  100. package/dist/core/shapes/scan.js +11 -0
  101. package/dist/core/shapes/validate.js +11 -0
  102. package/dist/core/validators/array/array-max-items.js +42 -0
  103. package/dist/core/validators/array/array-min-items.js +42 -0
  104. package/dist/core/validators/array/array.js +34 -0
  105. package/dist/core/validators/array/excludes.js +47 -0
  106. package/dist/core/validators/array/has-unique-items.js +46 -0
  107. package/dist/core/validators/array/includes.js +46 -0
  108. package/dist/core/validators/array/items-equal.js +42 -0
  109. package/dist/core/validators/array/no-empty-string-items.js +46 -0
  110. package/dist/core/validators/array/no-null-items.js +46 -0
  111. package/dist/core/validators/array/no-undefined-items.js +45 -0
  112. package/dist/core/validators/array/non-empty-array.js +45 -0
  113. package/dist/core/validators/array/not-sparse.js +44 -0
  114. package/dist/core/validators/bigint/bigint-equals.js +57 -0
  115. package/dist/core/validators/bigint/bigint-max.js +63 -0
  116. package/dist/core/validators/bigint/bigint-min.js +87 -0
  117. package/dist/core/validators/bigint/bigint-negative.js +73 -0
  118. package/dist/core/validators/bigint/bigint-non-negative.js +72 -0
  119. package/dist/core/validators/bigint/bigint-non-positive.js +72 -0
  120. package/dist/core/validators/bigint/bigint-positive.js +72 -0
  121. package/dist/core/validators/bigint/bigint-safe.js +75 -0
  122. package/dist/core/validators/bigint/bigint.js +38 -0
  123. package/dist/core/validators/boolean/boolean.js +39 -0
  124. package/dist/core/validators/boolean/is-false.js +48 -0
  125. package/dist/core/validators/boolean/is-true.js +48 -0
  126. package/dist/core/validators/common/is-country-code.js +36 -0
  127. package/dist/core/validators/common/is-currency-code.js +36 -0
  128. package/dist/core/validators/common/is-email-strict.js +36 -0
  129. package/dist/core/validators/common/is-email.js +36 -0
  130. package/dist/core/validators/common/is-ip.js +37 -0
  131. package/dist/core/validators/common/is-phone-strict.js +36 -0
  132. package/dist/core/validators/common/is-phone.js +36 -0
  133. package/dist/core/validators/common/is-port.js +35 -0
  134. package/dist/core/validators/common/is-postal-code.js +36 -0
  135. package/dist/core/validators/common/is-url.js +38 -0
  136. package/dist/core/validators/common/is-uuid.js +36 -0
  137. package/dist/core/validators/date/before-epoch.js +56 -0
  138. package/dist/core/validators/date/date-now-required.js +48 -0
  139. package/dist/core/validators/date/is-date.js +47 -0
  140. package/dist/core/validators/date/is-far-future.js +46 -0
  141. package/dist/core/validators/date/is-future.js +59 -0
  142. package/dist/core/validators/date/is-past.js +59 -0
  143. package/dist/core/validators/date/not-after.js +66 -0
  144. package/dist/core/validators/date/not-before.js +66 -0
  145. package/dist/core/validators/date/same-day.js +60 -0
  146. package/dist/core/validators/date/same-month.js +59 -0
  147. package/dist/core/validators/date/same-year.js +56 -0
  148. package/dist/core/validators/date/too-early.js +57 -0
  149. package/dist/core/validators/date/too-late.js +57 -0
  150. package/dist/core/validators/date/weekday.js +65 -0
  151. package/dist/core/validators/date/weekend.js +56 -0
  152. package/dist/core/validators/index.js +139 -0
  153. package/dist/core/validators/nullish/is-null-or-undefined.js +40 -0
  154. package/dist/core/validators/nullish/is-null.js +39 -0
  155. package/dist/core/validators/nullish/is-undefined.js +39 -0
  156. package/dist/core/validators/number/finite.js +40 -0
  157. package/dist/core/validators/number/integer.js +40 -0
  158. package/dist/core/validators/number/less-than.js +39 -0
  159. package/dist/core/validators/number/max.js +39 -0
  160. package/dist/core/validators/number/min.js +39 -0
  161. package/dist/core/validators/number/more-than.js +39 -0
  162. package/dist/core/validators/number/negative.js +38 -0
  163. package/dist/core/validators/number/non-negative.js +37 -0
  164. package/dist/core/validators/number/non-positive.js +37 -0
  165. package/dist/core/validators/number/number.js +31 -0
  166. package/dist/core/validators/number/positive.js +38 -0
  167. package/dist/core/validators/number/safe-integer.js +42 -0
  168. package/dist/core/validators/object/deep-equals.js +47 -0
  169. package/dist/core/validators/object/has-key.js +42 -0
  170. package/dist/core/validators/object/has-value.js +59 -0
  171. package/dist/core/validators/object/keys-equal.js +47 -0
  172. package/dist/core/validators/object/max-keys.js +43 -0
  173. package/dist/core/validators/object/min-keys.js +43 -0
  174. package/dist/core/validators/object/missing-key.js +42 -0
  175. package/dist/core/validators/object/no-empty-array-values.js +44 -0
  176. package/dist/core/validators/object/no-empty-object-values.js +44 -0
  177. package/dist/core/validators/object/no-null-values.js +44 -0
  178. package/dist/core/validators/object/no-undefined-values.js +44 -0
  179. package/dist/core/validators/object/non-empty-object.js +40 -0
  180. package/dist/core/validators/object/only-keys.js +43 -0
  181. package/dist/core/validators/object/plain-object.js +35 -0
  182. package/dist/core/validators/string/alpha-num.js +50 -0
  183. package/dist/core/validators/string/alpha.js +51 -0
  184. package/dist/core/validators/string/chars-equal.js +49 -0
  185. package/dist/core/validators/string/ends-with.js +50 -0
  186. package/dist/core/validators/string/is-ascii.js +53 -0
  187. package/dist/core/validators/string/is-printable.js +53 -0
  188. package/dist/core/validators/string/matches.js +50 -0
  189. package/dist/core/validators/string/max-length.js +50 -0
  190. package/dist/core/validators/string/min-length.js +50 -0
  191. package/dist/core/validators/string/no-lead-space.js +50 -0
  192. package/dist/core/validators/string/no-repeat-space.js +52 -0
  193. package/dist/core/validators/string/no-space.js +51 -0
  194. package/dist/core/validators/string/no-trail-space.js +50 -0
  195. package/dist/core/validators/string/non-empty.js +48 -0
  196. package/dist/core/validators/string/not-one-of.js +51 -0
  197. package/dist/core/validators/string/num-string.js +50 -0
  198. package/dist/core/validators/string/one-of.js +50 -0
  199. package/dist/core/validators/string/starts-with.js +50 -0
  200. package/dist/core/validators/string/string.js +39 -0
  201. package/dist/core/validators/string/trimmed.js +51 -0
  202. package/dist/index.js +26 -0
  203. package/dist/test.js +12 -0
  204. package/package.json +1 -1
@@ -0,0 +1,56 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Same Year
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Ensures the input is a Date instance representing a date in
7
+ * the same calendar year as the provided `other` date.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { safeStringify } from '../../common';
13
+ import { detectStructuralType } from '../../pipeline/scan';
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Implementation *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ /**
18
+ * Ensures the input is a Date instance representing a date in the
19
+ * same calendar year as the provided `other` date.
20
+ *
21
+ * - Non-Date values emit `date.not.date`.
22
+ * - Invalid Date instances emit `date.is.invalid`.
23
+ * - Dates not matching the same year emit `date.not.same-year`.
24
+ *
25
+ * This rule is pure, total, async-compatible, and preserves the
26
+ * provided path. It supports userMessage overrides applied at the
27
+ * pipeline level.
28
+ */
29
+ export const sameYear = (other) => {
30
+ const rule = async (value, path) => {
31
+ const structuralType = detectStructuralType(value);
32
+ if (!(value instanceof Date)) {
33
+ return [
34
+ validationEvent('error', 'date.not.date', path, `Expected 'Date' but received '${structuralType}'.`, `Please provide a valid date.`, { value, expected: 'Date', actual: structuralType }),
35
+ ];
36
+ }
37
+ if (isNaN(value.getTime())) {
38
+ return [
39
+ validationEvent('error', 'date.is.invalid', path, `Date value is invalid.`, `Please provide a valid date.`, { value, expected: 'valid Date', actual: value }),
40
+ ];
41
+ }
42
+ if (value.getUTCFullYear() !== other.getUTCFullYear()) {
43
+ return [
44
+ validationEvent('error', 'date.not.same-year', path, `${safeStringify(value)} must be in the same year as ${other.toISOString()}.`, `Please provide a date in the correct year.`, {
45
+ value,
46
+ expected: other.toISOString().slice(0, 4),
47
+ actual: value.toISOString().slice(0, 4),
48
+ }),
49
+ ];
50
+ }
51
+ return [];
52
+ };
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ rule.__janeFactory = true;
55
+ return rule;
56
+ };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Too Early
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Ensures the input is a Date instance that is not earlier
7
+ * than the provided `min` boundary.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ import { safeStringify } from '../../common';
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Implementation *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ /**
18
+ * Ensures the input is a Date instance that is not earlier than the
19
+ * provided `min` boundary. The comparison is inclusive: value must be
20
+ * ≥ min.
21
+ *
22
+ * - Non-Date values emit `date.not.date`.
23
+ * - Invalid Date instances emit `date.is.invalid`.
24
+ * - Dates < min emit `date.too.early`.
25
+ *
26
+ * This rule is pure, total, async-compatible, and preserves the
27
+ * provided path. It supports userMessage overrides applied at the
28
+ * pipeline level.
29
+ */
30
+ export const tooEarly = (min) => {
31
+ const rule = async (value, path) => {
32
+ const structuralType = detectStructuralType(value);
33
+ if (!(value instanceof Date)) {
34
+ return [
35
+ validationEvent('error', 'date.not.date', path, `Expected 'Date' but received '${structuralType}'.`, `Please provide a valid date.`, { value, expected: 'Date', actual: structuralType }),
36
+ ];
37
+ }
38
+ if (isNaN(value.getTime())) {
39
+ return [
40
+ validationEvent('error', 'date.is.invalid', path, `Date value is invalid.`, `Please provide a valid date.`, { value, expected: 'valid Date', actual: value }),
41
+ ];
42
+ }
43
+ if (value.getTime() < min.getTime()) {
44
+ return [
45
+ validationEvent('error', 'date.too.early', path, `${safeStringify(value)} must not be earlier than ${min.toISOString()}.`, `Please provide a later date.`, {
46
+ value: value.toISOString(),
47
+ expected: `>= ${min.toISOString()}`,
48
+ actual: value.toISOString(),
49
+ }),
50
+ ];
51
+ }
52
+ return [];
53
+ };
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ rule.__janeFactory = true;
56
+ return rule;
57
+ };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Too Early
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Ensures the input is a Date instance that is not later than
7
+ * the provided `max` boundary.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ import { safeStringify } from '../../common';
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Implementation *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ /**
18
+ * Ensures the input is a Date instance that is not later than the
19
+ * provided `max` boundary. The comparison is inclusive: value must be
20
+ * ≤ max.
21
+ *
22
+ * - Non-Date values emit `date.not.date`.
23
+ * - Invalid Date instances emit `date.is.invalid`.
24
+ * - Dates > max emit `date.too.late`.
25
+ *
26
+ * This rule is pure, total, async-compatible, and preserves the
27
+ * provided path. It supports userMessage overrides applied at the
28
+ * pipeline level.
29
+ */
30
+ export const tooLate = (max) => {
31
+ const rule = async (value, path) => {
32
+ const structuralType = detectStructuralType(value);
33
+ if (!(value instanceof Date)) {
34
+ return [
35
+ validationEvent('error', 'date.not.date', path, `Expected 'Date' but received '${structuralType}'.`, `Please provide a valid date.`, { value, expected: 'Date', actual: structuralType }),
36
+ ];
37
+ }
38
+ if (isNaN(value.getTime())) {
39
+ return [
40
+ validationEvent('error', 'date.is.invalid', path, `Date value is invalid.`, `Please provide a valid date.`, { value, expected: 'valid Date', actual: value }),
41
+ ];
42
+ }
43
+ if (value.getTime() > max.getTime()) {
44
+ return [
45
+ validationEvent('error', 'date.too.late', path, `${safeStringify(value)} must not be later than ${max.toISOString()}.`, `Please provide an earlier date.`, {
46
+ value: value.toISOString(),
47
+ expected: `<= ${max.toISOString()}`,
48
+ actual: value.toISOString(),
49
+ }),
50
+ ];
51
+ }
52
+ return [];
53
+ };
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ rule.__janeFactory = true;
56
+ return rule;
57
+ };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Weekday
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Ensures the input is a Date instance whose weekday (0–6)
7
+ * matches the required weekday.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ import { safeStringify } from '../../common';
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Implementation *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ /**
18
+ * Ensures the input is a Date instance whose weekday (0–6) matches
19
+ * the required weekday. JavaScript uses:
20
+ *
21
+ * 0 = Sunday
22
+ * 1 = Monday
23
+ * 2 = Tuesday
24
+ * 3 = Wednesday
25
+ * 4 = Thursday
26
+ * 5 = Friday
27
+ * 6 = Saturday
28
+ *
29
+ * - Non-Date values emit `date.not.date`.
30
+ * - Invalid Date instances emit `date.is.invalid`.
31
+ * - Mismatched weekdays emit `date.not.weekday`.
32
+ *
33
+ * This rule is pure, total, async-compatible, and preserves the
34
+ * provided path. It supports userMessage overrides applied at the
35
+ * pipeline level.
36
+ */
37
+ export const weekday = (required) => {
38
+ const rule = async (value, path) => {
39
+ const structuralType = detectStructuralType(value);
40
+ if (!(value instanceof Date)) {
41
+ return [
42
+ validationEvent('error', 'date.not.date', path, `Expected 'Date' but received '${structuralType}'.`, `Please provide a valid date.`, { value, expected: 'Date', actual: structuralType }),
43
+ ];
44
+ }
45
+ if (isNaN(value.getTime())) {
46
+ return [
47
+ validationEvent('error', 'date.is.invalid', path, `Date value is invalid.`, `Please provide a valid date.`, { value, expected: 'valid Date', actual: value }),
48
+ ];
49
+ }
50
+ const actualWeekday = value.getUTCDay();
51
+ if (actualWeekday !== required) {
52
+ return [
53
+ validationEvent('error', 'date.not.weekday', path, `${safeStringify(value)} must fall on weekday ${required}.`, `Please provide a date on the correct weekday.`, {
54
+ value: value.toISOString(),
55
+ expected: required,
56
+ actual: actualWeekday,
57
+ }),
58
+ ];
59
+ }
60
+ return [];
61
+ };
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ rule.__janeFactory = true;
64
+ return rule;
65
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Weekend
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Ensures the input is a Date instance that falls on a
7
+ * weekend.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ import { safeStringify } from '../../common';
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Implementation *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ /**
18
+ * Ensures the input is a Date instance that falls on a weekend.
19
+ *
20
+ * JavaScript weekday values:
21
+ * 0 = Sunday
22
+ * 6 = Saturday
23
+ *
24
+ * - Non-Date values emit `date.not.date`.
25
+ * - Invalid Date instances emit `date.is.invalid`.
26
+ * - Weekdays (1–5) emit `date.not.weekend`.
27
+ *
28
+ * This rule is pure, total, async-compatible, and preserves the
29
+ * provided path. It supports userMessage overrides applied at the
30
+ * pipeline level.
31
+ */
32
+ export const weekend = async (value, path) => {
33
+ const structuralType = detectStructuralType(value);
34
+ if (!(value instanceof Date)) {
35
+ return [
36
+ validationEvent('error', 'date.not.date', path, `Expected 'Date' but received '${structuralType}'.`, `Please provide a valid date.`, { value, expected: 'Date', actual: structuralType }),
37
+ ];
38
+ }
39
+ if (isNaN(value.getTime())) {
40
+ return [
41
+ validationEvent('error', 'date.is.invalid', path, `Date value is invalid.`, `Please provide a valid date.`, { value, expected: 'valid Date', actual: value }),
42
+ ];
43
+ }
44
+ const weekday = value.getUTCDay();
45
+ const isWeekend = weekday === 0 || weekday === 6;
46
+ if (!isWeekend) {
47
+ return [
48
+ validationEvent('error', 'date.not.weekend', path, `${safeStringify(value)} must fall on a weekend.`, `Please provide a Saturday or Sunday date.`, {
49
+ value: value.toISOString(),
50
+ expected: 'weekend',
51
+ actual: weekday,
52
+ }),
53
+ ];
54
+ }
55
+ return [];
56
+ };
@@ -0,0 +1,139 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Barrel File
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Re‑exports all built‑in validation rules, providing a single
7
+ * cohesive entry point for Jane’s validation subsystem. This
8
+ * keeps contributor onboarding simple and ensures consistent,
9
+ * predictable resolution of validator names across the
10
+ * framework.
11
+ * @see https://jane-io.com
12
+ * ----------------------------------------------------------------------------
13
+ */
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Array Values *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ export { array } from './array/array';
18
+ export { arrayMaxItems } from './array/array-max-items';
19
+ export { arrayMinItems } from './array/array-min-items';
20
+ export { excludes } from './array/excludes';
21
+ export { hasUniqueItems } from './array/has-unique-items';
22
+ export { includes } from './array/includes';
23
+ export { itemsEqual } from './array/items-equal';
24
+ export { noEmptyStringItems } from './array/no-empty-string-items';
25
+ export { noNullItems } from './array/no-null-items';
26
+ export { noUndefinedItems } from './array/no-undefined-items';
27
+ export { nonEmptyArray } from './array/non-empty-array';
28
+ export { notSparse } from './array/not-sparse';
29
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
30
+ |* Bigint Values *|
31
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
32
+ export { bigint } from './bigint/bigint';
33
+ export { bigintEquals } from './bigint/bigint-equals';
34
+ export { bigintMax } from './bigint/bigint-max';
35
+ export { bigintMin } from './bigint/bigint-min';
36
+ export { bigintNegative } from './bigint/bigint-negative';
37
+ export { bigintNonNegative } from './bigint/bigint-non-negative';
38
+ export { bigintNonPositive } from './bigint/bigint-non-positive';
39
+ export { bigintPositive } from './bigint/bigint-positive';
40
+ export { bigintSafe } from './bigint/bigint-safe';
41
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
42
+ |* Boolean Values *|
43
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
44
+ export { boolean } from './boolean/boolean';
45
+ export { isFalse } from './boolean/is-false';
46
+ export { isTrue } from './boolean/is-true';
47
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
48
+ |* Common Values *|
49
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
50
+ export { isCountryCode } from './common/is-country-code';
51
+ export { isCurrencyCode } from './common/is-currency-code';
52
+ export { isEmail } from './common/is-email';
53
+ export { isEmailStrict } from './common/is-email-strict';
54
+ export { isIp } from './common/is-ip';
55
+ export { isPhone } from './common/is-phone';
56
+ export { isPhoneStrict } from './common/is-phone-strict';
57
+ export { isPort } from './common/is-port';
58
+ export { isPostalCode } from './common/is-postal-code';
59
+ export { isUrl } from './common/is-url';
60
+ export { isUuid } from './common/is-uuid';
61
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
62
+ |* Date Values *|
63
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
64
+ export { beforeEpoch } from './date/before-epoch';
65
+ export { dateNowRequired } from './date/date-now-required';
66
+ export { isDate } from './date/is-date';
67
+ export { isFarFuture } from './date/is-far-future';
68
+ export { isFuture } from './date/is-future';
69
+ export { isPast } from './date/is-past';
70
+ export { notAfter } from './date/not-after';
71
+ export { notBefore } from './date/not-before';
72
+ export { sameDay } from './date/same-day';
73
+ export { sameMonth } from './date/same-month';
74
+ export { sameYear } from './date/same-year';
75
+ export { tooEarly } from './date/too-early';
76
+ export { tooLate } from './date/too-late';
77
+ export { weekday } from './date/weekday';
78
+ export { weekend } from './date/weekend';
79
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
80
+ |* Nullish Values *|
81
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
82
+ export { isNull } from './nullish/is-null';
83
+ export { isNullOrUndefined } from './nullish/is-null-or-undefined';
84
+ export { isUndefined } from './nullish/is-undefined';
85
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
86
+ |* Number Values *|
87
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
88
+ export { finite } from './number/finite';
89
+ export { integer } from './number/integer';
90
+ export { lessThan } from './number/less-than';
91
+ export { max } from './number/max';
92
+ export { min } from './number/min';
93
+ export { moreThan } from './number/more-than';
94
+ export { negative } from './number/negative';
95
+ export { nonNegative } from './number/non-negative';
96
+ export { nonPositive } from './number/non-positive';
97
+ export { number } from './number/number';
98
+ export { positive } from './number/positive';
99
+ export { safeInteger } from './number/safe-integer';
100
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
101
+ |* Object Values *|
102
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
103
+ export { deepEquals } from './object/deep-equals';
104
+ export { hasKey } from './object/has-key';
105
+ export { hasValue } from './object/has-value';
106
+ export { keysEqual } from './object/keys-equal';
107
+ export { maxKeys } from './object/max-keys';
108
+ export { minKeys } from './object/min-keys';
109
+ export { missingKey } from './object/missing-key';
110
+ export { noEmptyArrayValues } from './object/no-empty-array-values';
111
+ export { noEmptyObjectValues } from './object/no-empty-object-values';
112
+ export { noNullValues } from './object/no-null-values';
113
+ export { noUndefinedValues } from './object/no-undefined-values';
114
+ export { nonEmptyObject } from './object/non-empty-object';
115
+ export { plainObject } from './object/plain-object';
116
+ export { onlyKeys } from './object/only-keys';
117
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
118
+ |* String Values *|
119
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
120
+ export { alpha } from './string/alpha';
121
+ export { alphaNum } from './string/alpha-num';
122
+ export { charsEqual } from './string/chars-equal';
123
+ export { endsWith } from './string/ends-with';
124
+ export { isAscii } from './string/is-ascii';
125
+ export { isPrintable } from './string/is-printable';
126
+ export { matches } from './string/matches';
127
+ export { maxLength } from './string/max-length';
128
+ export { minLength } from './string/min-length';
129
+ export { noLeadSpace } from './string/no-lead-space';
130
+ export { noRepeatSpace } from './string/no-repeat-space';
131
+ export { noSpace } from './string/no-space';
132
+ export { noTrailSpace } from './string/no-trail-space';
133
+ export { nonEmpty } from './string/non-empty';
134
+ export { notOneOf } from './string/not-one-of';
135
+ export { numString } from './string/num-string';
136
+ export { oneOf } from './string/one-of';
137
+ export { startsWith } from './string/starts-with';
138
+ export { string } from './string/string';
139
+ export { trimmed } from './string/trimmed';
@@ -0,0 +1,40 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Null Or Undefined
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates that the value is either `null` or `undefined`.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { detectStructuralType } from '../../pipeline/scan';
12
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
13
+ |* Implementation *|
14
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
15
+ /**
16
+ * Validates that the value is either `null` or `undefined`.
17
+ *
18
+ * - Any value other than `null` or `undefined` emits
19
+ * `value.not.null-or-undefined`.
20
+ * - Returns an empty array when the value is valid.
21
+ *
22
+ * This rule is pure, total, async‑compatible, and returns a readonly array of
23
+ * JaneEvent objects. It preserves the provided path and supports userMessage
24
+ * overrides applied at the pipeline level.
25
+ */
26
+ export const isNullOrUndefined = async (value, path) => {
27
+ const structuralType = detectStructuralType(value);
28
+ // ------------------------------------------------------------
29
+ // Nullish check
30
+ // ------------------------------------------------------------
31
+ if (value !== null && value !== undefined) {
32
+ return [
33
+ validationEvent('error', 'value.not.null-or-undefined', path, `Value must be null or undefined.`, `Please provide a null or undefined value.`, { value, expected: 'null | undefined', actual: structuralType }),
34
+ ];
35
+ }
36
+ // ------------------------------------------------------------
37
+ // Valid
38
+ // ------------------------------------------------------------
39
+ return [];
40
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Null
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates that the value is exactly `null`.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { detectStructuralType } from '../../pipeline/scan';
12
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
13
+ |* Implementation *|
14
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
15
+ /**
16
+ * Validates that the value is exactly `null`.
17
+ *
18
+ * - Any value other than `null` emits `value.not.null`.
19
+ * - Returns an empty array when the value is valid.
20
+ *
21
+ * This rule is pure, total, async‑compatible, and returns a readonly array of
22
+ * JaneEvent objects. It preserves the provided path and supports userMessage
23
+ * overrides applied at the pipeline level.
24
+ */
25
+ export const isNull = async (value, path) => {
26
+ const structuralType = detectStructuralType(value);
27
+ // ------------------------------------------------------------
28
+ // Null check
29
+ // ------------------------------------------------------------
30
+ if (value !== null) {
31
+ return [
32
+ validationEvent('error', 'value.not.null', path, `Value must be null.`, `Please provide a null value.`, { value, expected: 'null', actual: structuralType }),
33
+ ];
34
+ }
35
+ // ------------------------------------------------------------
36
+ // Valid
37
+ // ------------------------------------------------------------
38
+ return [];
39
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Undefined
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates that the value is exactly `undefined`.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { detectStructuralType } from '../../pipeline/scan';
12
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
13
+ |* Implementation *|
14
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
15
+ /**
16
+ * Validates that the value is exactly `undefined`.
17
+ *
18
+ * - Any value other than `undefined` emits `value.not.undefined`.
19
+ * - Returns an empty array when the value is valid.
20
+ *
21
+ * This rule is pure, total, async‑compatible, and returns a readonly array of
22
+ * JaneEvent objects. It preserves the provided path and supports userMessage
23
+ * overrides applied at the pipeline level.
24
+ */
25
+ export const isUndefined = async (value, path) => {
26
+ const structuralType = detectStructuralType(value);
27
+ // ------------------------------------------------------------
28
+ // Undefined check
29
+ // ------------------------------------------------------------
30
+ if (value !== undefined) {
31
+ return [
32
+ validationEvent('error', 'value.not.undefined', path, `Value must be undefined.`, `Please provide an undefined value.`, { value, expected: 'undefined', actual: structuralType }),
33
+ ];
34
+ }
35
+ // ------------------------------------------------------------
36
+ // Valid
37
+ // ------------------------------------------------------------
38
+ return [];
39
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Finite
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validate that the value is a finite number.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { detectStructuralType } from '../../pipeline/scan';
12
+ import { safeStringify } from '../../common';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validate that the value is a finite number.
18
+ *
19
+ * This rule accepts only JavaScript numbers that are both numeric and finite.
20
+ * Non-number values, NaN, Infinity, and -Infinity are rejected.
21
+ *
22
+ * If the value is not a number, a type.not.valid event is emitted.
23
+ * If the value is a number but not finite, a number.not.finite event is emitted.
24
+ *
25
+ * The rule is async-compatible and returns a readonly array of JaneEvent objects.
26
+ */
27
+ export const finite = async (value, path) => {
28
+ const structuralType = detectStructuralType(value);
29
+ if (typeof value !== 'number') {
30
+ return [
31
+ validationEvent('error', 'type.not.valid', path, `Expected 'number' but received '${structuralType}'.`, `Please enter a valid number.`, { expected: 'number', actual: structuralType }),
32
+ ];
33
+ }
34
+ if (!Number.isFinite(value)) {
35
+ return [
36
+ validationEvent('error', 'number.not.finite', path, `${safeStringify(value)} must be a valid, bounded number.`, `Please enter a valid number.`, { expected: 'finite number', actual: value }),
37
+ ];
38
+ }
39
+ return [];
40
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Integer
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validate that the value is an integer.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { detectStructuralType } from '../../pipeline/scan';
12
+ import { safeStringify } from '../../common';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validate that the value is an integer.
18
+ *
19
+ * This rule accepts only JavaScript numbers that are both numeric and integer‑valued.
20
+ * Non-number values and non-integer numbers are rejected.
21
+ *
22
+ * If the value is not a number, a type.not.valid event is emitted.
23
+ * If the value is a number but not an integer, a number.not.integer event is emitted.
24
+ *
25
+ * The rule is async-compatible and returns a readonly array of JaneEvent objects.
26
+ */
27
+ export const integer = async (value, path) => {
28
+ const structuralType = detectStructuralType(value);
29
+ if (typeof value !== 'number') {
30
+ return [
31
+ validationEvent('error', 'type.not.valid', path, `Expected 'number' but received '${structuralType}'.`, `Please enter a valid number.`, { expected: 'number', actual: structuralType }),
32
+ ];
33
+ }
34
+ if (!Number.isInteger(value)) {
35
+ return [
36
+ validationEvent('error', 'number.not.integer', path, `${safeStringify(value)} must be an integer-based number.`, `Please enter a valid number.`, { expected: 'integer', actual: value }),
37
+ ];
38
+ }
39
+ return [];
40
+ };