@thi.ng/validate 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -90,13 +90,17 @@ try {
90
90
  - [`isPositive()`](https://docs.thi.ng/umbrella/validate/functions/isPositive.html)
91
91
  - [`isRegExp()`](https://docs.thi.ng/umbrella/validate/functions/isRegExp.html)
92
92
  - [`isString()`](https://docs.thi.ng/umbrella/validate/functions/isString.html)
93
+ - [`isTypedArray()`](https://docs.thi.ng/umbrella/validate/functions/isTypedArray.html)
94
+ - [`isU8Array()`](https://docs.thi.ng/umbrella/validate/functions/isU8Array.html)
93
95
  - [`isUndefined()`](https://docs.thi.ng/umbrella/validate/functions/isUndefined.html)
94
96
  - [`matchesRegexp()`](https://docs.thi.ng/umbrella/validate/functions/matchesRegexp.html)
95
97
 
96
98
  ### Combinators
97
99
 
98
- - [`oneOf()`](https://docs.thi.ng/umbrella/validate/functions/oneOf.html)
100
+ - [`every()`](https://docs.thi.ng/umbrella/validate/functions/every.html)
101
+ - [`not()`](https://docs.thi.ng/umbrella/validate/functions/not.html)
99
102
  - [`optional()`](https://docs.thi.ng/umbrella/validate/functions/optional.html)
103
+ - [`some()`](https://docs.thi.ng/umbrella/validate/functions/some.html)
100
104
 
101
105
  ## Status
102
106
 
@@ -130,7 +134,7 @@ For Node.js REPL:
130
134
  const val = await import("@thi.ng/validate");
131
135
  ```
132
136
 
133
- Package sizes (brotli'd, pre-treeshake): ESM: 1021 bytes
137
+ Package sizes (brotli'd, pre-treeshake): ESM: 1.19 KB
134
138
 
135
139
  ## Dependencies
136
140
 
package/api.d.ts CHANGED
@@ -6,7 +6,7 @@ export interface Validator {
6
6
  coerce?: Fn<any, any>;
7
7
  /**
8
8
  * Validation predicate. If the function returns false, the
9
- * {@link volidator} wrapper will throw an error.
9
+ * {@link validator} wrapper will throw an error.
10
10
  */
11
11
  valid: Predicate<any>;
12
12
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/validate",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Functional, composable, fully extensible, predicate-based value validation with customizable error messages",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -85,5 +85,5 @@
85
85
  "status": "alpha",
86
86
  "year": 2026
87
87
  },
88
- "gitHead": "92be63f24030506f09d6d156804da2798c381dc4"
88
+ "gitHead": "7e7cd1f3214bf256630f87efaa1f2f5f53454133"
89
89
  }
package/validators.d.ts CHANGED
@@ -56,20 +56,54 @@ export declare const ValidationError: {
56
56
  */
57
57
  export declare const validator: (...validators: Validator[]) => (x: any) => boolean;
58
58
  /**
59
- * Higher order validator. Takes an existing validator and returns an augmented
60
- * version which also allows value to be nullish.
59
+ * Utility validator which always passes.
60
+ */
61
+ export declare const ALWAYS: Validator;
62
+ /**
63
+ * Utility validator which always fails.
64
+ */
65
+ export declare const NEVER: Validator;
66
+ /**
67
+ * Higher order validator. Takes existing validator(s) and returns an augmented
68
+ * version which also allows values to be nullish. If more than one validator is
69
+ * given, they're first combined via {@link every}.
70
+ *
71
+ * @param first
72
+ * @param rest
73
+ */
74
+ export declare const optional: (first: Validator, ...rest: Validator[]) => Validator;
75
+ /**
76
+ * Higher order validator. Takes existing validator and optional error message.
77
+ * Returns an augmented validator which applies logical negation of the original
78
+ * validator.
61
79
  *
62
80
  * @param validator
81
+ * @param msg
63
82
  */
64
- export declare const optional: ({ coerce, valid, msg }: Validator) => Validator;
83
+ export declare const not: ({ coerce, valid, msg: $msg }: Validator, msg?: Validator["msg"]) => Validator;
65
84
  /**
66
- * Higher order validator. Takes a numer of validators and returns a new one
67
- * which will apply the validators in given order, but return when the first one
68
- * succeeds and only fails if all of the validators failed.
85
+ * Higher order validator. Takes one or more validators and returns a new one
86
+ * which will apply the validators in given order, and return successfully with
87
+ * the first one passed and only fails if **none** of the validators passed.
69
88
  *
70
- * @param validators
89
+ * @param first
90
+ * @param rest
71
91
  */
72
- export declare const oneOf: (validators: (Validator | Validator[])[]) => Validator;
92
+ export declare const some: (first: Validator | Validator[], ...rest: (Validator | Validator[])[]) => Validator;
93
+ /**
94
+ * Higher order validator. Takes a number of validators and returns a new one
95
+ * which will apply the validators in given order, and only returns success if
96
+ * **all** of the validators passed.
97
+ *
98
+ * @remarks
99
+ * Essentially the same as {@link validator}, but returns a {@link Validator}
100
+ * object (presumably for further composition) instead of a predicate function
101
+ * to apply directly.
102
+ *
103
+ * @param first
104
+ * @param rest
105
+ */
106
+ export declare const every: (first: Validator, ...rest: Validator[]) => Validator;
73
107
  /**
74
108
  * Returns validator to check if value is undefined.
75
109
  *
@@ -82,11 +116,19 @@ export declare const isUndefined: (msg?: Validator["msg"]) => Validator;
82
116
  * @param msg
83
117
  */
84
118
  export declare const isNullish: (msg?: Validator["msg"]) => Validator;
119
+ export declare const isArray: (msg?: Validator["msg"]) => Validator;
120
+ export declare const isArrayOf: (validators: Validator | Validator[], msg?: Validator["msg"]) => Validator;
85
121
  export declare const isBoolean: (msg?: Validator["msg"]) => Validator;
86
122
  export declare const isNumber: (msg?: Validator["msg"]) => Validator;
87
123
  export declare const isString: (msg?: Validator["msg"]) => Validator;
88
124
  export declare const isDate: (msg?: Validator["msg"]) => Validator;
125
+ export declare const isFunction: (msg?: Validator["msg"]) => Validator;
89
126
  export declare const isRegExp: (msg?: Validator["msg"]) => Validator;
127
+ /**
128
+ * Returns validator which checks that given value is a plain object (not class).
129
+ *
130
+ * @param msg
131
+ */
90
132
  export declare const isObject: (msg?: Validator["msg"]) => Validator;
91
133
  /**
92
134
  * Returns validator to check if value is a plain object and the values of all
@@ -96,12 +138,39 @@ export declare const isObject: (msg?: Validator["msg"]) => Validator;
96
138
  * @param msg
97
139
  */
98
140
  export declare const isObjectOf: (validators: Validator | Validator[], msg?: Validator["msg"]) => Validator;
99
- export declare const isArray: (msg?: Validator["msg"]) => Validator;
100
- export declare const isArrayOf: (validators: Validator | Validator[], msg?: Validator["msg"]) => Validator;
101
- export declare const hasRequiredKeys: (keys: string[], msg?: Validator["msg"]) => Validator;
102
- export declare const hasKeysOf: (validators: Record<PropertyKey, Validator | Validator[]>) => Validator;
141
+ export declare const isTypedArray: (msg?: Validator["msg"]) => Validator;
142
+ /**
143
+ * Returns validator to check if value is a `Uint8Array`.
144
+ *
145
+ * @param msg
146
+ */
147
+ export declare const isU8Array: (msg?: Validator["msg"]) => Validator;
148
+ /**
149
+ * Returns validator to ensure given `keys` are present in an object, optionally
150
+ * also checks their values are `nonNullish` (default: false). If `onlyKeys` is
151
+ * true (default: false), the validator also checks that no other keys are
152
+ * defined in the object.
153
+ *
154
+ * @param keys
155
+ * @param nonNullish
156
+ * @param onlyKeys
157
+ * @param msg
158
+ */
159
+ export declare const hasRequiredKeys: (keys: string[], nonNullish?: boolean, onlyKeys?: boolean, msg?: Validator["msg"]) => Validator;
160
+ /**
161
+ * Takes object of validators to check values of different keys in an object.
162
+ * Returns validator which applies all checks, optionally also ensures no other
163
+ * keys are defined (if `onlyKeys` is true, default: false).
164
+ *
165
+ * @param validators
166
+ * @param onlyKeys
167
+ */
168
+ export declare const hasKeysOf: (validators: Record<PropertyKey, Validator | Validator[]>, onlyKeys?: boolean) => Validator;
169
+ export declare const hasRequiredPatternKeys: (pattern: RegExp, nonNullish?: boolean, onlyKeys?: boolean, msg?: Validator["msg"]) => Validator;
170
+ export declare const hasPatternKeysOf: (pattern: RegExp, validators: Validator | Validator[], onlyKeys?: boolean) => Validator;
103
171
  /**
104
- * Returns validator to check if value is one of the given options.
172
+ * Returns validator to check if value is one of the given options. Values are
173
+ * checked via `opts.includes(x)`.
105
174
  *
106
175
  * @param opts
107
176
  * @param msg
package/validators.js CHANGED
@@ -2,38 +2,55 @@ import { isArray as $isArray } from "@thi.ng/checks/is-array";
2
2
  import { isArrayOf as $isArrayOf } from "@thi.ng/checks/is-array-of";
3
3
  import { isBoolean as $isBoolean } from "@thi.ng/checks/is-boolean";
4
4
  import { isDate as $isDate } from "@thi.ng/checks/is-date";
5
- import { isFunction } from "@thi.ng/checks/is-function";
5
+ import { isFunction as $isFunction } from "@thi.ng/checks/is-function";
6
6
  import { isNumber as $isNumber } from "@thi.ng/checks/is-number";
7
7
  import { isObjectOf as $isObjectOf } from "@thi.ng/checks/is-object-of";
8
8
  import { isPlainObject } from "@thi.ng/checks/is-plain-object";
9
9
  import { isRegExp as $isRegExp } from "@thi.ng/checks/is-regexp";
10
10
  import { isString as $isString } from "@thi.ng/checks/is-string";
11
+ import { isTypedArray as $isTypedArray } from "@thi.ng/checks/is-typedarray";
11
12
  import { defError } from "@thi.ng/errors/deferror";
12
13
  const ValidationError = defError(() => `validation error`);
13
14
  const validator = (...validators) => (x) => {
14
15
  for (let { coerce, valid, msg } of validators) {
15
16
  if (coerce) x = coerce(x);
16
17
  if (!valid(x)) {
17
- if (isFunction(msg)) msg = msg(x);
18
+ if ($isFunction(msg)) msg = msg(x);
18
19
  throw new ValidationError(msg ?? "failed validation");
19
20
  }
20
21
  }
21
22
  return true;
22
23
  };
23
- const optional = ({ coerce, valid, msg }) => ({
24
+ const __asArray = (v) => $isArray(v) ? v : [v];
25
+ const ALWAYS = { valid: () => true };
26
+ const NEVER = { valid: () => false };
27
+ const optional = (first, ...rest) => {
28
+ const { coerce, valid, msg } = rest.length ? every(first, ...rest) : first;
29
+ return {
30
+ coerce,
31
+ valid: (x) => x == null || valid(x),
32
+ msg
33
+ };
34
+ };
35
+ const not = ({ coerce, valid, msg: $msg }, msg) => ({
24
36
  coerce,
25
- valid: (x) => x == null || valid(x),
26
- msg
37
+ valid: (x) => {
38
+ try {
39
+ return !valid(x);
40
+ } catch (e) {
41
+ return true;
42
+ }
43
+ },
44
+ msg: msg ?? $msg
27
45
  });
28
- const __asArray = (v) => $isArray(v) ? v : [v];
29
- const oneOf = (validators) => {
46
+ const some = (first, ...rest) => {
30
47
  let lastMsg;
31
48
  return {
32
49
  valid: (x) => {
33
50
  lastMsg = void 0;
34
- for (let v of validators) {
51
+ for (let $v of [first, ...rest]) {
35
52
  try {
36
- return validator(...__asArray(v))(x);
53
+ return validator(...__asArray($v))(x);
37
54
  } catch (e) {
38
55
  lastMsg = e.origMessage ?? e.message;
39
56
  }
@@ -43,6 +60,9 @@ const oneOf = (validators) => {
43
60
  msg: () => lastMsg ?? "invalid value"
44
61
  };
45
62
  };
63
+ const every = (first, ...rest) => ({
64
+ valid: (x) => validator(first, ...rest)(x)
65
+ });
46
66
  const isUndefined = (msg) => ({
47
67
  valid: (x) => x === void 0,
48
68
  msg: msg ?? `expected undefined value`
@@ -51,6 +71,14 @@ const isNullish = (msg) => ({
51
71
  valid: (x) => x === null,
52
72
  msg: msg ?? `expected nullish value`
53
73
  });
74
+ const isArray = (msg) => ({
75
+ valid: $isArray,
76
+ msg: msg ?? `required array value`
77
+ });
78
+ const isArrayOf = (validators, msg) => ({
79
+ valid: $isArrayOf(validator(...__asArray(validators))),
80
+ msg: msg ?? `required array value`
81
+ });
54
82
  const isBoolean = (msg) => ({
55
83
  valid: $isBoolean,
56
84
  msg: msg ?? `required boolean value`
@@ -67,48 +95,96 @@ const isDate = (msg) => ({
67
95
  valid: $isDate,
68
96
  msg: msg ?? `required date value`
69
97
  });
98
+ const isFunction = (msg) => ({
99
+ valid: $isFunction,
100
+ msg: msg ?? `required function value`
101
+ });
70
102
  const isRegExp = (msg) => ({
71
103
  valid: $isRegExp,
72
104
  msg: msg ?? `required regexp value`
73
105
  });
74
106
  const isObject = (msg) => ({
75
107
  valid: isPlainObject,
76
- msg: msg ?? `required object value`
108
+ msg: msg ?? `required plain object value`
77
109
  });
78
110
  const isObjectOf = (validators, msg) => ({
79
111
  valid: $isObjectOf(validator(...__asArray(validators))),
80
112
  msg: msg ?? `required object value`
81
113
  });
82
- const isArray = (msg) => ({
83
- valid: $isArray,
84
- msg: msg ?? `required array value`
114
+ const isTypedArray = (msg) => ({
115
+ valid: $isTypedArray,
116
+ msg: msg ?? `required typed array value`
85
117
  });
86
- const isArrayOf = (validators, msg) => ({
87
- valid: $isArrayOf(validator(...__asArray(validators))),
88
- msg: msg ?? `required array value`
118
+ const isU8Array = (msg) => ({
119
+ valid: (x) => x instanceof Uint8Array,
120
+ msg: msg ?? `required byte array value`
89
121
  });
90
- const hasRequiredKeys = (keys, msg) => ({
122
+ const hasRequiredKeys = (keys, nonNullish = false, onlyKeys = false, msg) => ({
91
123
  valid: (x) => {
92
- const $keys = new Set(Object.keys(x));
93
- return keys.every((k) => $keys.has(k));
124
+ if (onlyKeys) __onlyKeys(keys, x);
125
+ return keys.every((k) => k in x && (nonNullish ? x[k] != null : true));
94
126
  },
95
- msg: msg ?? `required keys: ${keys.join(", ")}`
127
+ msg: msg ?? `required keys: ${keys.join(", ")}${nonNullish ? " (values must be non-nullish)" : ""}`
96
128
  });
97
- const hasKeysOf = (validators) => ({
129
+ const hasKeysOf = (validators, onlyKeys = false) => ({
98
130
  valid: (x) => {
99
- if (x == null) return false;
100
- for (let [key, v] of Object.entries(validators)) {
131
+ if (onlyKeys) __onlyKeys(Object.keys(validators), x);
132
+ for (let k in validators) {
101
133
  try {
102
- validator(...__asArray(v))(x[key]);
134
+ validator(...__asArray(validators[k]))(x[k]);
103
135
  } catch (e) {
104
136
  throw new ValidationError(
105
- (e.origMessage ?? e.message) + ` (key: ${key})`
137
+ (e.origMessage ?? e.message) + ` (key: ${k})`
106
138
  );
107
139
  }
108
140
  }
109
141
  return true;
110
142
  }
111
143
  });
144
+ const hasRequiredPatternKeys = (pattern, nonNullish = false, onlyKeys = false, msg) => ({
145
+ valid: (x) => {
146
+ let found = false;
147
+ for (let k in x) {
148
+ if (pattern.test(k)) {
149
+ if (nonNullish && x[k] == null) return false;
150
+ found = true;
151
+ } else if (onlyKeys) {
152
+ __disallowedKey(k);
153
+ }
154
+ }
155
+ return found;
156
+ },
157
+ msg: msg ?? `required pattern keys: ${pattern}${nonNullish ? " (values must be non-nullish)" : ""}`
158
+ });
159
+ const hasPatternKeysOf = (pattern, validators, onlyKeys = false) => {
160
+ const fn = validator(...__asArray(validators));
161
+ return {
162
+ valid: (x) => {
163
+ for (let k in x) {
164
+ if (pattern.test(k)) {
165
+ try {
166
+ fn(x[k]);
167
+ } catch (e) {
168
+ throw new ValidationError(
169
+ (e.origMessage ?? e.message) + ` (key: ${k})`
170
+ );
171
+ }
172
+ } else if (onlyKeys) {
173
+ __disallowedKey(k);
174
+ }
175
+ }
176
+ return true;
177
+ }
178
+ };
179
+ };
180
+ const __onlyKeys = (keys, x) => {
181
+ for (let k in x) {
182
+ if (!keys.includes(k)) __disallowedKey(k);
183
+ }
184
+ };
185
+ const __disallowedKey = (key) => {
186
+ throw new ValidationError(`key: ${key} not allowed`);
187
+ };
112
188
  const isEnum = (opts, msg) => ({
113
189
  valid: (x) => opts.includes(x),
114
190
  msg: msg ?? `required value to be one of: ${opts.join(", ")}`
@@ -154,14 +230,20 @@ const matchesRegexp = (re, msg) => ({
154
230
  msg: msg ?? `doesn't match pattern`
155
231
  });
156
232
  export {
233
+ ALWAYS,
234
+ NEVER,
157
235
  ValidationError,
236
+ every,
158
237
  hasKeysOf,
238
+ hasPatternKeysOf,
159
239
  hasRequiredKeys,
240
+ hasRequiredPatternKeys,
160
241
  isArray,
161
242
  isArrayOf,
162
243
  isBoolean,
163
244
  isDate,
164
245
  isEnum,
246
+ isFunction,
165
247
  isInRange,
166
248
  isLength,
167
249
  isMaxLength,
@@ -177,9 +259,12 @@ export {
177
259
  isPositive,
178
260
  isRegExp,
179
261
  isString,
262
+ isTypedArray,
263
+ isU8Array,
180
264
  isUndefined,
181
265
  matchesRegexp,
182
- oneOf,
266
+ not,
183
267
  optional,
268
+ some,
184
269
  validator
185
270
  };