@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,75 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Bigint Safe
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Ensures the input is a string representing a BigInt that
7
+ * falls within JavaScript’s safe integer range.
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 string representing a BigInt that falls
19
+ * within JavaScript’s safe integer range (Number.MIN_SAFE_INTEGER
20
+ * through Number.MAX_SAFE_INTEGER).
21
+ *
22
+ * - Non-string values emit `bigint.not.string`.
23
+ * - Empty strings and non-parseable strings emit `bigint.not.bigint`.
24
+ * - Parsed BigInt values outside the safe range emit `bigint.not.safe`.
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 bigintSafe = async (value, path) => {
31
+ const structuralType = detectStructuralType(value);
32
+ // ------------------------------------------------------------
33
+ // Type check — the input must be a string.
34
+ // ------------------------------------------------------------
35
+ if (typeof value !== 'string') {
36
+ return [
37
+ validationEvent('error', 'bigint.not.string', path, `Expected 'string' but received '${structuralType}'.`, `Please provide a valid bigint string.`, { value, expected: 'string', actual: structuralType }),
38
+ ];
39
+ }
40
+ // ------------------------------------------------------------
41
+ // Empty-string check — BigInt("") parses as 0n, which is
42
+ // rarely what users intend. We treat empty strings as invalid.
43
+ // ------------------------------------------------------------
44
+ if (value.length === 0) {
45
+ return [
46
+ validationEvent('error', 'bigint.not.bigint', path, `${safeStringify(value)} is not a valid bigint literal.`, `Please provide a valid bigint string.`, { value, expected: 'bigint literal', actual: value }),
47
+ ];
48
+ }
49
+ // ------------------------------------------------------------
50
+ // Parse check — attempt to convert the string into a BigInt.
51
+ // ------------------------------------------------------------
52
+ let parsed;
53
+ try {
54
+ parsed = BigInt(value);
55
+ }
56
+ catch {
57
+ return [
58
+ validationEvent('error', 'bigint.not.bigint', path, `${safeStringify(value)} is not a valid bigint literal.`, `Please provide a valid bigint string.`, { value, expected: 'bigint literal', actual: value }),
59
+ ];
60
+ }
61
+ // ------------------------------------------------------------
62
+ // Safe-range check — ensure MIN_SAFE ≤ parsed ≤ MAX_SAFE.
63
+ // ------------------------------------------------------------
64
+ const maxSafe = BigInt(Number.MAX_SAFE_INTEGER);
65
+ const minSafe = BigInt(Number.MIN_SAFE_INTEGER);
66
+ if (parsed > maxSafe || parsed < minSafe) {
67
+ return [
68
+ validationEvent('error', 'bigint.not.safe', path, `${safeStringify(value)} exceeds JavaScript’s safe integer range.`, `Please provide a bigint within safe bounds.`, { value, expected: `${minSafe}–${maxSafe}`, actual: parsed }),
69
+ ];
70
+ }
71
+ // ------------------------------------------------------------
72
+ // Valid — return an empty array to indicate success.
73
+ // ------------------------------------------------------------
74
+ return [];
75
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Bigint
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Ensures the input is a native JavaScript BigInt value.
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
+ * Ensures the input is a native JavaScript BigInt value.
17
+ *
18
+ * - Non-bigint values emit `bigint.not.bigint`.
19
+ *
20
+ * This rule is pure, total, async-compatible, and preserves the
21
+ * provided path. It supports userMessage overrides applied at the
22
+ * pipeline level.
23
+ */
24
+ export const bigint = (value, path) => {
25
+ const structuralType = detectStructuralType(value);
26
+ // ------------------------------------------------------------
27
+ // Type check — the input must be a native BigInt.
28
+ // ------------------------------------------------------------
29
+ if (typeof value !== 'string') {
30
+ return [
31
+ validationEvent('error', 'bigint.not.string', path, `Expected 'bigint' but received '${structuralType}'.`, `Please provide a valid bigint.`, { value, expected: 'bigint', actual: structuralType }),
32
+ ];
33
+ }
34
+ // ------------------------------------------------------------
35
+ // Valid — return an empty array to indicate success.
36
+ // ------------------------------------------------------------
37
+ return [];
38
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Boolean
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates that the value is a boolean.
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 a boolean.
17
+ *
18
+ * - Non‑boolean values emit `boolean.not.boolean`.
19
+ * - Returns an empty array when the value is a valid boolean.
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 boolean = async (value, path) => {
26
+ const structuralType = detectStructuralType(value);
27
+ // ------------------------------------------------------------
28
+ // Type check
29
+ // ------------------------------------------------------------
30
+ if (typeof value !== 'boolean') {
31
+ return [
32
+ validationEvent('error', 'boolean.not.boolean', path, `Expected 'boolean' but received '${structuralType}'.`, `Please enter true or false.`, { value, expected: 'boolean', actual: structuralType }),
33
+ ];
34
+ }
35
+ // ------------------------------------------------------------
36
+ // Valid
37
+ // ------------------------------------------------------------
38
+ return [];
39
+ };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is False
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates that the value is the boolean literal `false`.
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 the boolean literal `false`.
17
+ *
18
+ * - Non‑boolean values emit `boolean.not.boolean`.
19
+ * - The boolean literal `true` emits `boolean.not.false`.
20
+ * - Returns an empty array when the value is exactly `false`.
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 isFalse = async (value, path) => {
27
+ const structuralType = detectStructuralType(value);
28
+ // ------------------------------------------------------------
29
+ // Type check
30
+ // ------------------------------------------------------------
31
+ if (typeof value !== 'boolean') {
32
+ return [
33
+ validationEvent('error', 'boolean.not.boolean', path, `Expected 'boolean' but received '${structuralType}'.`, `Please enter true or false.`, { value, expected: 'boolean', actual: structuralType }),
34
+ ];
35
+ }
36
+ // ------------------------------------------------------------
37
+ // Literal false check
38
+ // ------------------------------------------------------------
39
+ if (value !== false) {
40
+ return [
41
+ validationEvent('error', 'boolean.not.false', path, `Value must be false.`, `Please enter false.`, { value, expected: false, actual: value }),
42
+ ];
43
+ }
44
+ // ------------------------------------------------------------
45
+ // Valid
46
+ // ------------------------------------------------------------
47
+ return [];
48
+ };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is True
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates that the value is the boolean literal `true`.
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 the boolean literal `true`.
17
+ *
18
+ * - Non‑boolean values emit `boolean.not.boolean`.
19
+ * - The boolean literal `false` emits `boolean.not.true`.
20
+ * - Returns an empty array when the value is exactly `true`.
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 isTrue = async (value, path) => {
27
+ const structuralType = detectStructuralType(value);
28
+ // ------------------------------------------------------------
29
+ // Type check
30
+ // ------------------------------------------------------------
31
+ if (typeof value !== 'boolean') {
32
+ return [
33
+ validationEvent('error', 'boolean.not.boolean', path, `Expected 'boolean' but received '${structuralType}'.`, `Please enter true or false.`, { value, expected: 'boolean', actual: structuralType }),
34
+ ];
35
+ }
36
+ // ------------------------------------------------------------
37
+ // Literal true check
38
+ // ------------------------------------------------------------
39
+ if (value !== true) {
40
+ return [
41
+ validationEvent('error', 'boolean.not.true', path, `Value must be true.`, `Please enter true.`, { value, expected: true, actual: value }),
42
+ ];
43
+ }
44
+ // ------------------------------------------------------------
45
+ // Valid
46
+ // ------------------------------------------------------------
47
+ return [];
48
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Country Code
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates ISO‑3166‑1 alpha‑2 country codes.
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
+ * Validates ISO‑3166‑1 alpha‑2 country codes.
18
+ *
19
+ * Country codes must be exactly two uppercase letters. This helps ensure
20
+ * consistent, standards‑aligned identifiers for geographic data.
21
+ */
22
+ export const isCountryCode = async (value, path) => {
23
+ const type = detectStructuralType(value);
24
+ if (type !== 'string') {
25
+ return [
26
+ validationEvent('error', 'type.not.valid', path, `Expected 'string' but received '${type}'.`, `Please provide a valid country code.`, { value, expected: 'string', actual: type }),
27
+ ];
28
+ }
29
+ const re = /^[A-Z]{2}$/;
30
+ if (!re.test(value)) {
31
+ return [
32
+ validationEvent('error', 'string.not.country-code', path, `${safeStringify(value)} is not a valid ISO-3166-1 alpha-2 code.`, `Please provide a valid country code.`, { value }),
33
+ ];
34
+ }
35
+ return [];
36
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Country Code
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates ISO‑4217 currency codes.
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
+ * Validates ISO‑4217 currency codes.
18
+ *
19
+ * Currency codes must be three uppercase letters. This prevents malformed or
20
+ * non‑standard currency identifiers from entering downstream systems.
21
+ */
22
+ export const isCurrencyCode = async (value, path) => {
23
+ const type = detectStructuralType(value);
24
+ if (type !== 'string') {
25
+ return [
26
+ validationEvent('error', 'type.not.valid', path, `Expected 'string' but received '${type}'.`, `Please provide a valid currency code.`, { value, expected: 'string', actual: type }),
27
+ ];
28
+ }
29
+ const re = /^[A-Z]{3}$/;
30
+ if (!re.test(value)) {
31
+ return [
32
+ validationEvent('error', 'string.not.currency-code', path, `${safeStringify(value)} is not a valid ISO‑4217 currency code.`, `Please provide a valid currency code.`, { value }),
33
+ ];
34
+ }
35
+ return [];
36
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Email Strict
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates strict RFC‑style email addresses.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { safeStringify } from '../../common';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validates strict RFC‑style email addresses.
18
+ *
19
+ * Enforces a detailed pattern for both the local part and domain, catching
20
+ * malformed or ambiguous email strings before they propagate further.
21
+ */
22
+ export const isEmailStrict = async (value, path) => {
23
+ const type = detectStructuralType(value);
24
+ if (type !== 'string') {
25
+ return [
26
+ validationEvent('error', 'type.not.valid', path, `Expected 'string' but received '${type}'.`, `Please provide a valid email address.`, { value, expected: 'string', actual: type }),
27
+ ];
28
+ }
29
+ const re = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$/;
30
+ if (!re.test(value)) {
31
+ return [
32
+ validationEvent('error', 'string.not.strict-email', path, `${safeStringify(value)} is not a valid RFC‑style email address.`, `Please provide a valid email address.`, { value }),
33
+ ];
34
+ }
35
+ return [];
36
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Email
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates general‑purpose email addresses.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { safeStringify } from '../../common';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validates general‑purpose email addresses.
18
+ *
19
+ * Uses a permissive but reliable pattern suitable for everyday user input,
20
+ * ensuring the value resembles a conventional email address.
21
+ */
22
+ export const isEmail = async (value, path) => {
23
+ const type = detectStructuralType(value);
24
+ if (type !== 'string') {
25
+ return [
26
+ validationEvent('error', 'type.not.valid', path, `Expected 'string' but received '${type}'.`, `Please provide a valid email address.`, { value, expected: 'string', actual: type }),
27
+ ];
28
+ }
29
+ const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
30
+ if (!re.test(value)) {
31
+ return [
32
+ validationEvent('error', 'string.not.email', path, `${safeStringify(value)} is not a valid email address.`, `Please provide a valid email address.`, { value }),
33
+ ];
34
+ }
35
+ return [];
36
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is IP (v4 and v6)
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates IPv4 and IPv6 addresses.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { safeStringify } from '../../common';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validates IPv4 and IPv6 addresses.
18
+ *
19
+ * Ensures the string matches canonical patterns for either protocol, helping
20
+ * prevent malformed network identifiers from entering the system.
21
+ */
22
+ export const isIp = async (value, path) => {
23
+ const type = detectStructuralType(value);
24
+ if (type !== 'string') {
25
+ return [
26
+ validationEvent('error', 'type.not.valid', path, `Expected 'string' but received '${type}'.`, `Please provide a valid IP address.`, { value, expected: 'string', actual: type }),
27
+ ];
28
+ }
29
+ const ipv4 = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/;
30
+ const ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::1|::)$/;
31
+ if (!ipv4.test(value) && !ipv6.test(value)) {
32
+ return [
33
+ validationEvent('error', 'string.not.ip', path, `${safeStringify(value)} is not a valid IPv4 or IPv6 address.`, `Please provide a valid IP address.`, { value }),
34
+ ];
35
+ }
36
+ return [];
37
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Phone Strict
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates strict E.164 international phone numbers.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { safeStringify } from '../../common';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validates strict E.164 international phone numbers.
18
+ *
19
+ * Requires a leading plus sign and 8–15 digits, ensuring globally portable,
20
+ * unambiguous phone identifiers.
21
+ */
22
+ export const isPhoneStrict = async (value, path) => {
23
+ const type = detectStructuralType(value);
24
+ if (type !== 'string') {
25
+ return [
26
+ validationEvent('error', 'type.not.valid', path, `Expected 'string' but received '${type}'.`, `Please provide a valid phone number.`, { value, expected: 'string', actual: type }),
27
+ ];
28
+ }
29
+ const re = /^\+[1-9]\d{7,14}$/;
30
+ if (!re.test(value)) {
31
+ return [
32
+ validationEvent('error', 'string.not.strict-phone', path, `${safeStringify(value)} is not a valid E.164 phone number.`, `Please provide a valid international phone number.`, { value }),
33
+ ];
34
+ }
35
+ return [];
36
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Phone
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates loosely formatted phone numbers.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { safeStringify } from '../../common';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validates loosely formatted phone numbers.
18
+ *
19
+ * Extracts digits and enforces a minimum length, providing a flexible check
20
+ * suitable for user‑entered phone fields.
21
+ */
22
+ export const isPhone = async (value, path) => {
23
+ const type = detectStructuralType(value);
24
+ if (type !== 'string') {
25
+ return [
26
+ validationEvent('error', 'type.not.valid', path, `Expected 'string' but received '${type}'.`, `Please provide a valid phone number.`, { value, expected: 'string', actual: type }),
27
+ ];
28
+ }
29
+ const digits = value.replace(/\D/g, '');
30
+ if (digits.length < 7) {
31
+ return [
32
+ validationEvent('error', 'string.not.phone', path, `${safeStringify(value)} is not a valid phone number.`, `Please provide a valid phone number.`, { value }),
33
+ ];
34
+ }
35
+ return [];
36
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Port
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates TCP/UDP port numbers.
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 TCP/UDP port numbers.
17
+ *
18
+ * Ports must be integers between 0 and 65535. This prevents invalid or
19
+ * out‑of‑range port values from being accepted.
20
+ */
21
+ export const isPort = async (value, path) => {
22
+ const type = detectStructuralType(value);
23
+ if (type !== 'number') {
24
+ return [
25
+ validationEvent('error', 'type.not.valid', path, `Expected 'number' but received '${type}'.`, `Please provide a valid port number.`, { value, expected: 'number', actual: type }),
26
+ ];
27
+ }
28
+ const n = value;
29
+ if (!Number.isInteger(n) || n < 0 || n > 65535) {
30
+ return [
31
+ validationEvent('error', 'number.not.in-port-range', path, `Port must be an integer between 0 and 65535.`, `Please provide a valid port number.`, { value, min: 0, max: 65535 }),
32
+ ];
33
+ }
34
+ return [];
35
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Postal Code
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates alphanumeric postal codes.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { safeStringify } from '../../common';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validates alphanumeric postal codes.
18
+ *
19
+ * Accepts letters, digits, spaces, and hyphens within a reasonable length
20
+ * range, supporting a wide variety of international postal formats.
21
+ */
22
+ export const isPostalCode = async (value, path) => {
23
+ const type = detectStructuralType(value);
24
+ if (type !== 'string') {
25
+ return [
26
+ validationEvent('error', 'type.not.valid', path, `Expected 'string' but received '${type}'.`, `Please provide a valid postal code.`, { value, expected: 'string', actual: type }),
27
+ ];
28
+ }
29
+ const re = /^[A-Za-z0-9 \\-]{3,12}$/;
30
+ if (!re.test(value)) {
31
+ return [
32
+ validationEvent('error', 'string.not.postal-code', path, `${safeStringify(value)} is not a valid postal code.`, `Please provide a valid postal code.`, { value }),
33
+ ];
34
+ }
35
+ return [];
36
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is URL
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates URLs using the WHATWG URL parser.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { safeStringify } from '../../common';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validates URLs using the WHATWG URL parser.
18
+ *
19
+ * Ensures the string is a syntactically valid absolute URL, preventing malformed
20
+ * or incomplete URL values from being accepted.
21
+ */
22
+ export const isUrl = async (value, path) => {
23
+ const type = detectStructuralType(value);
24
+ if (type !== 'string') {
25
+ return [
26
+ validationEvent('error', 'string.not.string', path, `Expected 'string' but received '${type}'.`, `Please provide a valid URL.`, { value, expected: 'string', actual: type }),
27
+ ];
28
+ }
29
+ try {
30
+ new URL(value);
31
+ return [];
32
+ }
33
+ catch {
34
+ return [
35
+ validationEvent('error', 'string.not.url', path, `${safeStringify(value)} is not a valid URL.`, `Please provide a valid URL.`, { value }),
36
+ ];
37
+ }
38
+ };