@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,43 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Minimum Keys (Min Keys)
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validate that a plain object contains at least a specified
7
+ * number of keys.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ import { isPlainObject } from '../../common';
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Implementation *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ /**
18
+ * Validate that a plain object contains at least a specified number of keys.
19
+ *
20
+ * This rule accepts only JSON-compatible plain objects (prototype === Object.prototype).
21
+ * Arrays, null, class instances, Maps, Sets, Dates, and other non-plain objects are rejected.
22
+ *
23
+ * If the value is not a plain object, an object.not.plain-object event is emitted.
24
+ * If the object contains fewer than the required number of keys, an object.too.few-keys
25
+ * event is emitted.
26
+ *
27
+ * The rule is async-compatible and returns a readonly array of JaneEvent objects.
28
+ */
29
+ export const minKeys = (minimum) => async (value, path) => {
30
+ const structuralType = detectStructuralType(value);
31
+ if (!isPlainObject(value)) {
32
+ return [
33
+ validationEvent('error', 'object.not.plain-object', path, `Expected a plain object but received '${structuralType}'.`, `Please provide a JSON-compatible object.`, { expected: 'plain object', actual: structuralType }),
34
+ ];
35
+ }
36
+ const keyCount = Object.keys(value).length;
37
+ if (keyCount < minimum) {
38
+ return [
39
+ validationEvent('error', 'object.too.few-keys', path, `Object has ${keyCount} keys but must have ≥ ${minimum}.`, `Please provide at least ${minimum} keys.`, { expected: `≥ ${minimum}`, actual: keyCount }),
40
+ ];
41
+ }
42
+ return [];
43
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Missing Keys
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validate that a plain object contains all required own
7
+ * enumerable keys.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ import { isPlainObject } from '../../common';
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Implementation *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ /**
18
+ * Validate that a plain object contains all required own enumerable keys.
19
+ *
20
+ * This rule accepts only JSON-compatible plain objects (prototype === Object.prototype).
21
+ * Arrays, null, class instances, Maps, Sets, Dates, and other non-plain objects are rejected.
22
+ *
23
+ * If the value is not a plain object, an object.not.plain-object event is emitted.
24
+ * If one or more required keys are missing, an object.missing-keys event is emitted.
25
+ *
26
+ * The rule is async-compatible and returns a readonly array of JaneEvent objects.
27
+ */
28
+ export const missingKey = (requiredKeys) => async (value, path) => {
29
+ const structuralType = detectStructuralType(value);
30
+ if (!isPlainObject(value)) {
31
+ return [
32
+ validationEvent('error', 'object.not.plain-object', path, `Expected a plain object but received '${structuralType}'.`, `Please provide a JSON-compatible object.`, { expected: 'plain object', actual: structuralType }),
33
+ ];
34
+ }
35
+ const missing = requiredKeys.filter((key) => !Object.prototype.hasOwnProperty.call(value, key));
36
+ if (missing.length > 0) {
37
+ return [
38
+ validationEvent('error', 'object.has.missing-key', path, `Object is missing required keys: ${missing.join(', ')}.`, `Please include all required keys.`, { expected: requiredKeys, actual: Object.keys(value) }),
39
+ ];
40
+ }
41
+ return [];
42
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | No Empty Array Values
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validate that a plain object does not contain any empty
7
+ * array values.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ import { isPlainObject } from '../../common';
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Implementation *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ /**
18
+ * Validate that a plain object does not contain any empty array values.
19
+ *
20
+ * This rule accepts only JSON-compatible plain objects (prototype === Object.prototype).
21
+ * Arrays, null, class instances, Maps, Sets, Dates, and other non-plain objects are rejected.
22
+ *
23
+ * Each own enumerable property is inspected. If any property value is an array with length 0,
24
+ * an object.empty-array-value event is emitted.
25
+ *
26
+ * The rule is async-compatible and returns a readonly array of JaneEvent objects.
27
+ */
28
+ export const noEmptyArrayValues = async (value, path) => {
29
+ const structuralType = detectStructuralType(value);
30
+ if (!isPlainObject(value)) {
31
+ return [
32
+ validationEvent('error', 'object.not.plain-object', path, `Expected a plain object but received '${structuralType}'.`, `Please provide a JSON-compatible object.`, { expected: 'plain object', actual: structuralType }),
33
+ ];
34
+ }
35
+ for (const key of Object.keys(value)) {
36
+ const propertyValue = value[key];
37
+ if (Array.isArray(propertyValue) && propertyValue.length === 0) {
38
+ return [
39
+ validationEvent('error', 'object.has.empty-array', path, `Object must not contain empty arrays (found at key '${key}').`, `Please remove empty arrays.`, { expected: 'no empty arrays', actual: key }),
40
+ ];
41
+ }
42
+ }
43
+ return [];
44
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | No Empty Object Values
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validate that a plain object does not contain any empty
7
+ * plain-object values.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ import { isPlainObject } from '../../common';
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Implementation *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ /**
18
+ * Validate that a plain object does not contain any empty plain-object values.
19
+ *
20
+ * This rule accepts only JSON-compatible plain objects (prototype === Object.prototype).
21
+ * Arrays, null, class instances, Maps, Sets, Dates, and other non-plain objects are rejected.
22
+ *
23
+ * Each own enumerable property is inspected. If any property value is a plain object
24
+ * with zero keys, an object.empty-object-value event is emitted.
25
+ *
26
+ * The rule is async-compatible and returns a readonly array of JaneEvent objects.
27
+ */
28
+ export const noEmptyObjectValues = async (value, path) => {
29
+ const structuralType = detectStructuralType(value);
30
+ if (!isPlainObject(value)) {
31
+ return [
32
+ validationEvent('error', 'object.not.plain-object', path, `Expected a plain object but received '${structuralType}'.`, `Please provide a JSON-compatible object.`, { expected: 'plain object', actual: structuralType }),
33
+ ];
34
+ }
35
+ for (const key of Object.keys(value)) {
36
+ const propertyValue = value[key];
37
+ if (isPlainObject(propertyValue) && Object.keys(propertyValue).length === 0) {
38
+ return [
39
+ validationEvent('error', 'object.has.empty-object', path, `Object must not contain empty objects (found at key '${key}').`, `Please remove empty objects.`, { expected: 'no empty objects', actual: key }),
40
+ ];
41
+ }
42
+ }
43
+ return [];
44
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | No Null Values
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validate that a plain object does not contain any null
7
+ * values.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ import { isPlainObject } from '../../common';
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Implementation *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ /**
18
+ * Validate that a plain object does not contain any null values.
19
+ *
20
+ * This rule accepts only JSON-compatible plain objects (prototype === Object.prototype).
21
+ * Arrays, null, class instances, Maps, Sets, Dates, and other non-plain objects are rejected.
22
+ *
23
+ * Each own enumerable property is inspected. If any property value is null,
24
+ * an object.null-value event is emitted.
25
+ *
26
+ * The rule is async-compatible and returns a readonly array of JaneEvent objects.
27
+ */
28
+ export const noNullValues = async (value, path) => {
29
+ const structuralType = detectStructuralType(value);
30
+ if (!isPlainObject(value)) {
31
+ return [
32
+ validationEvent('error', 'object.not.plain-object', path, `Expected a plain object but received '${structuralType}'.`, `Please provide a JSON-compatible object.`, { expected: 'plain object', actual: structuralType }),
33
+ ];
34
+ }
35
+ for (const key of Object.keys(value)) {
36
+ const propertyValue = value[key];
37
+ if (propertyValue === null) {
38
+ return [
39
+ validationEvent('error', 'object.has.null-value', path, `Object must not contain null values (found at key '${key}').`, `Please remove null values.`, { expected: 'no null values', actual: key }),
40
+ ];
41
+ }
42
+ }
43
+ return [];
44
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | No Undefined Values
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validate that a plain object does not contain any
7
+ * undefined values.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ import { isPlainObject } from '../../common';
14
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
15
+ |* Implementation *|
16
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
17
+ /**
18
+ * Validate that a plain object does not contain any undefined values.
19
+ *
20
+ * This rule accepts only JSON-compatible plain objects (prototype === Object.prototype).
21
+ * Arrays, null, class instances, Maps, Sets, Dates, and other non-plain objects are rejected.
22
+ *
23
+ * Each own enumerable property is inspected. If any property value is undefined,
24
+ * an object.undefined-value event is emitted.
25
+ *
26
+ * The rule is async-compatible and returns a readonly array of JaneEvent objects.
27
+ */
28
+ export const noUndefinedValues = async (value, path) => {
29
+ const structuralType = detectStructuralType(value);
30
+ if (!isPlainObject(value)) {
31
+ return [
32
+ validationEvent('error', 'object.not.plain-object', path, `Expected a plain object but received '${structuralType}'.`, `Please provide a JSON-compatible object.`, { expected: 'plain object', actual: structuralType }),
33
+ ];
34
+ }
35
+ for (const key of Object.keys(value)) {
36
+ const propertyValue = value[key];
37
+ if (propertyValue === undefined) {
38
+ return [
39
+ validationEvent('error', 'object.has.undefined-value', path, `Object must not contain undefined values (found at key '${key}').`, `Please remove undefined values.`, { expected: 'no undefined values', actual: key }),
40
+ ];
41
+ }
42
+ }
43
+ return [];
44
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Non-Empty Object
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validate that a plain object is not empty.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { detectStructuralType } from '../../pipeline/scan';
12
+ import { isPlainObject } from '../../common';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validate that a plain object is not empty.
18
+ *
19
+ * This rule accepts only JSON-compatible plain objects (prototype === Object.prototype).
20
+ * Arrays, null, class instances, Maps, Sets, Dates, and other non-plain objects are rejected.
21
+ *
22
+ * If the object contains zero own enumerable keys, an object.empty-object event is emitted.
23
+ *
24
+ * The rule is async-compatible and returns a readonly array of JaneEvent objects.
25
+ */
26
+ export const nonEmptyObject = async (value, path) => {
27
+ const structuralType = detectStructuralType(value);
28
+ if (!isPlainObject(value)) {
29
+ return [
30
+ validationEvent('error', 'object.not.plain-object', path, `Expected a plain object but received '${structuralType}'.`, `Please provide a JSON-compatible object.`, { expected: 'plain object', actual: structuralType }),
31
+ ];
32
+ }
33
+ const keyCount = Object.keys(value).length;
34
+ if (keyCount === 0) {
35
+ return [
36
+ validationEvent('error', 'object.is.empty', path, `Object must not be empty.`, `Please provide at least one key.`, { expected: 'non-empty object', actual: 0 }),
37
+ ];
38
+ }
39
+ return [];
40
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Only Keys
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validate that a plain object contains only the allowed keys.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { detectStructuralType } from '../../pipeline/scan';
12
+ import { isPlainObject } from '../../common';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validate that a plain object contains only the allowed keys.
18
+ *
19
+ * This rule accepts only JSON-compatible plain objects (prototype === Object.prototype).
20
+ * Arrays, null, class instances, Maps, Sets, Dates, and other non-plain objects
21
+ * are rejected.
22
+ *
23
+ * If the object contains any keys not included in the allowed list,
24
+ * an object.has.extra-keys event is emitted.
25
+ *
26
+ * The rule is async-compatible and returns a readonly array of JaneEvent objects.
27
+ */
28
+ export const onlyKeys = (allowed) => async (value, path) => {
29
+ const structuralType = detectStructuralType(value);
30
+ if (!isPlainObject(value)) {
31
+ return [
32
+ validationEvent('error', 'object.not.plain-object', path, `Expected a plain object but received '${structuralType}'.`, `Please provide a JSON-compatible object.`, { expected: 'plain object', actual: structuralType }),
33
+ ];
34
+ }
35
+ const keys = Object.keys(value);
36
+ const extraKeys = keys.filter((key) => !allowed.includes(key));
37
+ if (extraKeys.length > 0) {
38
+ return [
39
+ validationEvent('error', 'object.has.extra-key', path, `Object contains disallowed keys: ${extraKeys.join(', ')}.`, `Please remove the extra keys.`, { expected: allowed, actual: keys }),
40
+ ];
41
+ }
42
+ return [];
43
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Object
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validate that the value is a JSON-compatible plain object.
7
+ * @see https://jane-io.com
8
+ * ----------------------------------------------------------------------------
9
+ */
10
+ import { validationEvent } from '../../pipeline';
11
+ import { detectStructuralType } from '../../pipeline/scan';
12
+ import { isPlainObject } from '../../common';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validate that the value is a JSON-compatible plain object.
18
+ *
19
+ * This rule accepts only plain objects (prototype === Object.prototype).
20
+ * Arrays, null, class instances, Maps, Sets, Dates, and other non-plain objects
21
+ * are rejected.
22
+ *
23
+ * If the value is not a plain object, an object.not.plain-object event is emitted.
24
+ *
25
+ * The rule is async-compatible and returns a readonly array of JaneEvent objects.
26
+ */
27
+ export const plainObject = async (value, path) => {
28
+ const structuralType = detectStructuralType(value);
29
+ if (!isPlainObject(value) || false) {
30
+ return [
31
+ validationEvent('error', 'object.not.plain-object', path, `Expected a plain object but received '${structuralType}'.`, `Please provide a JSON-compatible object.`, { expected: 'plain object', actual: structuralType }),
32
+ ];
33
+ }
34
+ return [];
35
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Alpha-Numeric (Alpha Num)
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates that the value is a string containing only ASCII
7
+ * letters and digits.
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
+ * Validates that the value is a string containing only ASCII letters and digits.
19
+ *
20
+ * - Non‑string values emit `type.not.valid`.
21
+ * - Strings containing characters outside `[A-Za-z0-9]` emit `string.not.alphanumeric`.
22
+ * - Returns an empty array when the value is valid.
23
+ *
24
+ * This rule is pure, total, async‑compatible, and returns a readonly array of
25
+ * JaneEvent objects. It preserves the provided path and supports userMessage
26
+ * overrides applied at the pipeline level.
27
+ */
28
+ export const alphaNum = async (value, path) => {
29
+ const structuralType = detectStructuralType(value);
30
+ // ------------------------------------------------------------
31
+ // Type check
32
+ // ------------------------------------------------------------
33
+ if (typeof value !== 'string') {
34
+ return [
35
+ validationEvent('error', 'type.not.valid', path, `Expected 'string' but received '${structuralType}'.`, `Please enter a valid string.`, { value, expected: 'string', actual: structuralType }),
36
+ ];
37
+ }
38
+ // ------------------------------------------------------------
39
+ // Alphanumeric check
40
+ // ------------------------------------------------------------
41
+ if (!/^[A-Za-z0-9]+$/.test(value)) {
42
+ return [
43
+ validationEvent('error', 'string.not.alphanumeric', path, `${safeStringify(value)} must contain only letters and numbers.`, `Please use only letters and numbers.`, { value, expected: 'A-Z, 0-9', actual: value }),
44
+ ];
45
+ }
46
+ // ------------------------------------------------------------
47
+ // Valid
48
+ // ------------------------------------------------------------
49
+ return [];
50
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Alphabetic (Alpha)
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates that the value is a string containing only ASCII
7
+ * alphabetic characters (`A–Z` and `a–z`).
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
+ * Validates that the value is a string containing only ASCII alphabetic
19
+ * characters (`A–Z` and `a–z`).
20
+ *
21
+ * - Non‑string values emit `type.not.valid`.
22
+ * - Strings containing characters outside `[A-Za-z]` emit `string.not.alpha`.
23
+ * - Returns an empty array when the value is valid.
24
+ *
25
+ * This rule is pure, total, async‑compatible, and returns a readonly array of
26
+ * JaneEvent objects. It preserves the provided path and supports userMessage
27
+ * overrides applied at the pipeline level.
28
+ */
29
+ export const alpha = async (value, path) => {
30
+ const structuralType = detectStructuralType(value);
31
+ // ------------------------------------------------------------
32
+ // Type check
33
+ // ------------------------------------------------------------
34
+ if (typeof value !== 'string') {
35
+ return [
36
+ validationEvent('error', 'type.not.valid', path, `Expected 'string' but received '${structuralType}'.`, `Please enter a valid string.`, { value, expected: 'string', actual: structuralType }),
37
+ ];
38
+ }
39
+ // ------------------------------------------------------------
40
+ // Alphabetic check
41
+ // ------------------------------------------------------------
42
+ if (!/^[A-Za-z]+$/.test(value)) {
43
+ return [
44
+ validationEvent('error', 'string.not.alpha', path, `${safeStringify(value)} must contain only alphabetic characters.`, `Please use only letters.`, { value, expected: 'A–Z only', actual: value }),
45
+ ];
46
+ }
47
+ // ------------------------------------------------------------
48
+ // Valid
49
+ // ------------------------------------------------------------
50
+ return [];
51
+ };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Characters Equal (Chars Equal)
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates that the value is a string whose length is
7
+ * exactly `exact`.
8
+ * @see https://jane-io.com
9
+ * ----------------------------------------------------------------------------
10
+ */
11
+ import { validationEvent } from '../../pipeline';
12
+ import { detectStructuralType } from '../../pipeline/scan';
13
+ /* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
14
+ |* Implementation *|
15
+ \* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
16
+ /**
17
+ * Validates that the value is a string whose length is exactly `exact`.
18
+ *
19
+ * - Non‑string values emit `type.not.valid`.
20
+ * - Strings whose length does not match `exact` emit `string.has.invalid-length`.
21
+ * - Returns an empty array when the value is valid.
22
+ *
23
+ * This rule is pure, total, async‑compatible, and returns a readonly array of
24
+ * JaneEvent objects. It preserves the provided path and supports userMessage
25
+ * overrides applied at the pipeline level.
26
+ */
27
+ export const charsEqual = (exact) => async (value, path) => {
28
+ const structuralType = detectStructuralType(value);
29
+ // ------------------------------------------------------------
30
+ // Type check
31
+ // ------------------------------------------------------------
32
+ if (typeof value !== 'string') {
33
+ return [
34
+ validationEvent('error', 'type.not.valid', path, `Expected 'string' but received '${structuralType}'.`, `Please enter a valid string.`, { value, expected: 'string', actual: structuralType }),
35
+ ];
36
+ }
37
+ // ------------------------------------------------------------
38
+ // Exact-length check
39
+ // ------------------------------------------------------------
40
+ if (value.length !== exact) {
41
+ return [
42
+ validationEvent('error', 'string.has.invalid-length', path, `String length ${value.length} must be exactly ${exact}.`, `Please enter a string that is exactly ${exact} characters long.`, { value, expected: exact, actual: value.length }),
43
+ ];
44
+ }
45
+ // ------------------------------------------------------------
46
+ // Valid
47
+ // ------------------------------------------------------------
48
+ return [];
49
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Ends With
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates that the value is a string ending with the
7
+ * provided `suffix`.
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
+ * Validates that the value is a string ending with the provided `suffix`.
19
+ *
20
+ * - Non‑string values emit `string.not.string`.
21
+ * - Strings that do not end with `suffix` emit `string.must.end-with`.
22
+ * - Returns an empty array when the value is valid.
23
+ *
24
+ * This rule is pure, total, async‑compatible, and returns a readonly array of
25
+ * JaneEvent objects. It preserves the provided path and supports userMessage
26
+ * overrides applied at the pipeline level.
27
+ */
28
+ export const endsWith = (suffix) => async (value, path) => {
29
+ const structuralType = detectStructuralType(value);
30
+ // ------------------------------------------------------------
31
+ // Type check
32
+ // ------------------------------------------------------------
33
+ if (typeof value !== 'string') {
34
+ return [
35
+ validationEvent('error', 'string.not.string', path, `Expected 'string' but received '${structuralType}'.`, `Please provide a valid string.`, { value, expected: 'string', actual: structuralType }),
36
+ ];
37
+ }
38
+ // ------------------------------------------------------------
39
+ // Ends-with check
40
+ // ------------------------------------------------------------
41
+ if (!value.endsWith(suffix)) {
42
+ return [
43
+ validationEvent('error', 'string.must.end-with', path, `${safeStringify(value)} must end with ${safeStringify(suffix)}.`, `Please end the value with ${suffix}.`, { value, expected: `endsWith(${suffix})`, actual: value }),
44
+ ];
45
+ }
46
+ // ------------------------------------------------------------
47
+ // Valid
48
+ // ------------------------------------------------------------
49
+ return [];
50
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * ----------------------------------------------------------------------------
3
+ * Validators | Is Ascii
4
+ * ----------------------------------------------------------------------------
5
+ * @package @clementine-solutions/jane
6
+ * @description Validates that the value is a string containing only ASCII
7
+ * characters (code points 0x00–0x7F).
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
+ * Validates that the value is a string containing only ASCII characters
19
+ * (code points 0x00–0x7F).
20
+ *
21
+ * - Non‑string values emit `string.not.string`.
22
+ * - Strings containing characters outside the ASCII range emit `string.not.ascii`.
23
+ * - Returns an empty array when the value is valid.
24
+ *
25
+ * This rule is pure, total, async‑compatible, and returns a readonly array of
26
+ * JaneEvent objects. It preserves the provided path and supports userMessage
27
+ * overrides applied at the pipeline level.
28
+ */
29
+ export const isAscii = async (value, path) => {
30
+ const structuralType = detectStructuralType(value);
31
+ // ------------------------------------------------------------
32
+ // Type check
33
+ // ------------------------------------------------------------
34
+ if (typeof value !== 'string') {
35
+ return [
36
+ validationEvent('error', 'string.not.string', path, `Expected 'string' but received '${structuralType}'.`, `Please enter a valid string.`, { value, expected: 'string', actual: structuralType }),
37
+ ];
38
+ }
39
+ // ------------------------------------------------------------
40
+ // ASCII check
41
+ // ------------------------------------------------------------
42
+ // eslint-disable-next-line no-control-regex
43
+ const nonAsciiPattern = new RegExp('[^\\x00-\\x7F]');
44
+ if (nonAsciiPattern.test(value)) {
45
+ return [
46
+ validationEvent('error', 'string.not.ascii', path, `${safeStringify(value)} must contain only ASCII characters.`, `Please use only ASCII characters.`, { value, expected: 'ASCII', actual: value }),
47
+ ];
48
+ }
49
+ // ------------------------------------------------------------
50
+ // Valid
51
+ // ------------------------------------------------------------
52
+ return [];
53
+ };