ivl 0.3.1 → 0.4.1
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 +54 -19
- package/lib/core.d.ts +86 -0
- package/lib/helpers/index.d.ts +2 -21
- package/lib/helpers/index.js +1 -1
- package/lib/helpers/rules.d.ts +14 -0
- package/lib/helpers/schema.d.ts +23 -0
- package/lib/index.d.ts +2 -94
- package/lib/index.js +1 -1
- package/lib/patterns/index.d.ts +1 -14
- package/lib/patterns/index.js +1 -1
- package/lib/patterns/patterns.d.ts +10 -0
- package/lib/types.d.ts +82 -0
- package/package.json +13 -10
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
This is a lightweight library for user input validation.
|
|
3
3
|
Main focus is on speed and flexibility of the validation rules.
|
|
4
4
|
|
|
5
|
-
By default `
|
|
5
|
+
By default `getValueErrors` and `getSchemaErrors` automatically detects and chooses the most performant checking method for your rule set.
|
|
6
6
|
|
|
7
|
-
If your rule set and/or schema are very large or complex, you may want to directly use `
|
|
7
|
+
If your rule set and/or schema are very large or complex, you may want to directly use `getValueErrorsSync`/`getSchemaErrorsSync` or `getValueErrorsAsync`/`getSchemaErrorsAsync` for improved performance on synchronous and asynchronous rule sets respectively.
|
|
8
8
|
|
|
9
9
|
Use synchronous versions of the functions for better performance if you don't need to support asynchronous checks on your inputs.
|
|
10
10
|
|
|
@@ -16,10 +16,41 @@ const my_rules = {
|
|
|
16
16
|
"Input must be more than 40": (i) => i > 40,
|
|
17
17
|
"Input must be divisible by 10": (i) => !(i % 10)
|
|
18
18
|
}
|
|
19
|
-
console.log(
|
|
20
|
-
console.log(
|
|
21
|
-
console.log(
|
|
19
|
+
console.log(getValueErrors(50, my_rules)); // []
|
|
20
|
+
console.log(getValueErrors(11, my_rules)); // ["Input must be more than 40", "Input must be divisible by 10"]
|
|
21
|
+
console.log(getValueErrors(30, my_rules)) // ["Input must be more than 40"]
|
|
22
22
|
```
|
|
23
|
+
# Type inference
|
|
24
|
+
`getValueErrors` and `getSchemaErrors` infer their return type from the rules you pass in:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
const sync_rules = { "Is string": (i: unknown) => typeof i === 'string' };
|
|
28
|
+
const async_rules = { "Exists": async (i: unknown) => await lookup(i) };
|
|
29
|
+
|
|
30
|
+
getValueErrors('x', sync_rules); // string[]
|
|
31
|
+
getValueErrors('x', async_rules); // Promise<string[]>
|
|
32
|
+
|
|
33
|
+
const errors = getSchemaErrors(input, {
|
|
34
|
+
name: sync_rules,
|
|
35
|
+
ids: [sync_rules, { "Is number": (i: unknown) => typeof i === 'number' }],
|
|
36
|
+
});
|
|
37
|
+
errors.name; // string[]
|
|
38
|
+
errors.ids; // string[][] - one error list per alternative rule set
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Rules and rule sets are generic over the value type and any extra "overload" arguments:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import type { RULE } from 'ivl';
|
|
45
|
+
|
|
46
|
+
// A rule that only accepts strings and needs a context object passed as an overload
|
|
47
|
+
const inDatabase: RULE<string, [ctx: { db: Database }]> = async (value, ctx) => ctx.db.has(value);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> **Note:** annotating a rule set as `RULES` (or a schema as `SCHEMA`) widens every rule to
|
|
51
|
+
> "may be sync or async", so the return type becomes `string[] | Promise<string[]>`. Prefer
|
|
52
|
+
> `satisfies RULES` / `satisfies SCHEMA`, which validates the shape without losing the inferred types.
|
|
53
|
+
|
|
23
54
|
# Installing
|
|
24
55
|
```typescript
|
|
25
56
|
bun add ivl // bun.js
|
|
@@ -35,44 +66,46 @@ pnpm install ivl // pnpm
|
|
|
35
66
|
## Frontend example
|
|
36
67
|
### `index.ts`
|
|
37
68
|
```typescript
|
|
38
|
-
import {
|
|
69
|
+
import { getValueErrors } from 'ivl';
|
|
39
70
|
import type { RULES } from 'ivl';
|
|
40
71
|
import { matchesRegex, minLength, maxLength, isType } from 'ivl/helpers';
|
|
41
72
|
|
|
42
73
|
// This pattern is also exportable from 'ivl/patterns'
|
|
43
74
|
const EMAIL_PATTERN = /^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{1,}$/;
|
|
44
75
|
|
|
45
|
-
|
|
76
|
+
// `satisfies` checks the object against RULES while keeping each rule's exact type,
|
|
77
|
+
// so `getValueErrors` can infer that this rule set is async.
|
|
78
|
+
const EMAIL_REQUIREMENTS = {
|
|
46
79
|
"Must be string": isType('string'),
|
|
47
80
|
"Must be less then 100 characters": maxLength(100),
|
|
48
81
|
"Not a valid email address": matchesRegex(EMAIL_PATTERN),
|
|
49
|
-
"That email is already in use": async (i) => {
|
|
82
|
+
"That email is already in use": async (i: unknown) => {
|
|
50
83
|
// Fetch info from whatever backend and make a decision based on that asynchronously
|
|
51
84
|
const email_in_use = await fetch(`https://mybackend/email-exists/${i}`)
|
|
52
85
|
return !email_in_use // We will return true if the email is not already taken
|
|
53
86
|
}
|
|
54
|
-
};
|
|
87
|
+
} satisfies RULES;
|
|
55
88
|
|
|
56
|
-
const PASSWORD_REQUIREMENTS
|
|
89
|
+
const PASSWORD_REQUIREMENTS = {
|
|
57
90
|
"Must be string": isType('string'),
|
|
58
91
|
"Must be at least 8 characters": minLength(8),
|
|
59
92
|
"Must contain at least one upper case character": matchesRegex(/[A-Z]/),
|
|
60
93
|
"Must contain at least one lower case character": matchesRegex(/[a-z]/),
|
|
61
|
-
};
|
|
94
|
+
} satisfies RULES;
|
|
62
95
|
|
|
63
96
|
// You can of course expand on your existing rules:
|
|
64
|
-
const STRONG_PASSWORD_REQUIREMENTS
|
|
97
|
+
const STRONG_PASSWORD_REQUIREMENTS = {
|
|
65
98
|
...PASSWORD_REQUIREMENTS,
|
|
66
99
|
"Must contain at least one digit": matchesRegex(/\d/),
|
|
67
100
|
"Must contain at least one symbol": matchesRegex(/[^\w\s]/),
|
|
68
|
-
};
|
|
101
|
+
} satisfies RULES;
|
|
69
102
|
|
|
70
103
|
const email_value = "some-value";
|
|
71
104
|
const pw_value = "Passesweakpw";
|
|
72
105
|
|
|
73
|
-
const email_errors =
|
|
74
|
-
const pw_errors =
|
|
75
|
-
const strong_pw_errors =
|
|
106
|
+
const email_errors = await getValueErrors(email_value, EMAIL_REQUIREMENTS); // Promise<string[]> - one rule is async
|
|
107
|
+
const pw_errors = getValueErrors(pw_value, PASSWORD_REQUIREMENTS); // string[] - all rules are sync
|
|
108
|
+
const strong_pw_errors = getValueErrors(pw_value, STRONG_PASSWORD_REQUIREMENTS); // string[]
|
|
76
109
|
|
|
77
110
|
console.log({email_errors, pw_errors, strong_pw_errors});
|
|
78
111
|
```
|
|
@@ -84,7 +117,7 @@ console.log({email_errors, pw_errors, strong_pw_errors});
|
|
|
84
117
|
```typescript
|
|
85
118
|
import { isType, minLength, maxLength, matchesRegex } from 'ivl/helpers';
|
|
86
119
|
import { EMAIL_PATTERN } from 'ivl/patterns';
|
|
87
|
-
import type { SCHEMA } from 'ivl';
|
|
120
|
+
import type { RULE, SCHEMA } from 'ivl';
|
|
88
121
|
import { checkValueInDatabase } from 'my-database-controller';
|
|
89
122
|
|
|
90
123
|
const existsInDatabase = (key: string, table: string, exists: boolean = true): RULE =>
|
|
@@ -114,7 +147,7 @@ export const LOGIN_SCHEMA: SCHEMA = {
|
|
|
114
147
|
export const REGISTER_SCHEMA: SCHEMA = {
|
|
115
148
|
// We can pass in an empty rule set to allow any value
|
|
116
149
|
// Or we can omit the argument entirely and set the strict flag to false when checking the schema
|
|
117
|
-
organization_name: {}
|
|
150
|
+
organization_name: {},
|
|
118
151
|
email: {
|
|
119
152
|
...EMAIL_REQUIREMENTS,
|
|
120
153
|
"Email already registered": existsInDatabase('email','users', false)
|
|
@@ -125,7 +158,7 @@ export const REGISTER_SCHEMA: SCHEMA = {
|
|
|
125
158
|
// Registering via invitation needs all the same values except organization name
|
|
126
159
|
// inherit parts of rule sets, as opposed to extending the rule set as show in the
|
|
127
160
|
// frontend example
|
|
128
|
-
export const INVITE_REGISTER_SCHEMA = (({ organization_name, ...invite_schema }) => invite_schema)(
|
|
161
|
+
export const INVITE_REGISTER_SCHEMA = (({ organization_name, ...invite_schema }) => invite_schema)(REGISTER_SCHEMA)
|
|
129
162
|
|
|
130
163
|
export const INVITATION_PARAM: SCHEMA = {
|
|
131
164
|
invitation_code: {
|
|
@@ -138,8 +171,10 @@ export const INVITATION_PARAM: SCHEMA = {
|
|
|
138
171
|
### `index.ts`
|
|
139
172
|
```typescript
|
|
140
173
|
import { Hono, ValidationTargets } from 'hono';
|
|
174
|
+
import { HTTPException } from 'hono/http-exception';
|
|
141
175
|
import { validator } from 'hono/validator';
|
|
142
176
|
import { getSchemaErrors } from 'ivl';
|
|
177
|
+
import type { SCHEMA, CHECKABLE_OBJECT } from 'ivl';
|
|
143
178
|
import { LOGIN_SCHEMA, REGISTER_SCHEMA, INVITE_REGISTER_SCHEMA, INVITATION_PARAM } from './rules.ts';
|
|
144
179
|
|
|
145
180
|
// Wrapper for hono validator middleware
|
package/lib/core.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { RULES, RULES_SYNC, SCHEMA, SCHEMA_SYNC, SCHEMA_OPTIONS, CHECKABLE_OBJECT, CHECKED_SCHEMA, CHECKED_SCHEMA_SYNC, RULES_KIND, SCHEMA_KIND, MAYBE_ASYNC } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Detect if a function is async or not
|
|
4
|
+
*
|
|
5
|
+
* @param {unknown} fn the function to inspect
|
|
6
|
+
* @returns {boolean} true if `fn` is declared with the `async` keyword, otherwise false
|
|
7
|
+
*/
|
|
8
|
+
export declare const isAsyncFunction: (fn: unknown) => fn is (...args: never[]) => Promise<unknown>;
|
|
9
|
+
/**
|
|
10
|
+
* Detect if a RULES object has any async rules in it
|
|
11
|
+
*
|
|
12
|
+
* @param {RULES|RULES[]} rules a rules object
|
|
13
|
+
* @returns {boolean} true if any of the rules is an async function otherwise false
|
|
14
|
+
*/
|
|
15
|
+
export declare const hasAsyncFunction: (rules: {
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
} | {
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}[]) => boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Check the input against all provided validation rules asynchronously
|
|
22
|
+
*
|
|
23
|
+
* @param {V} value the value to check
|
|
24
|
+
* @param {RULES<V>} rules rules object to use for validating the provided value
|
|
25
|
+
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
26
|
+
* @returns {Promise<string[]>} a list of rule names which failed validation
|
|
27
|
+
*/
|
|
28
|
+
export declare const getValueErrorsAsync: <V>(value: V, rules: RULES<V>, ...overload: unknown[]) => Promise<string[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Check a schema against all provided validation rules asynchronously
|
|
31
|
+
*
|
|
32
|
+
* @param {CHECKABLE_OBJECT} object_to_check object to be validated
|
|
33
|
+
* @param {S} schema schema to validate against
|
|
34
|
+
* @param {SCHEMA_OPTIONS} options options affecting how the rules are run
|
|
35
|
+
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
36
|
+
* @returns {CHECKED_SCHEMA<S>} an object containing keys and their respective lists of failed rule names
|
|
37
|
+
*/
|
|
38
|
+
export declare const getSchemaErrorsAsync: <S extends SCHEMA>(object_to_check: CHECKABLE_OBJECT, schema: S, options?: SCHEMA_OPTIONS, ...overload: unknown[]) => CHECKED_SCHEMA<S>;
|
|
39
|
+
/**
|
|
40
|
+
* Check the input against all provided validation rules synchronously
|
|
41
|
+
*
|
|
42
|
+
* @param {V} value the value to check
|
|
43
|
+
* @param {RULES_SYNC<V>} rules rules object to use for validating the provided value
|
|
44
|
+
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
45
|
+
* @returns {string[]} a list of rule names which failed validation
|
|
46
|
+
*/
|
|
47
|
+
export declare const getValueErrorsSync: <V>(value: V, rules: RULES_SYNC<V>, ...overload: unknown[]) => string[];
|
|
48
|
+
/**
|
|
49
|
+
* Check a schema against all provided validation rules synchronously
|
|
50
|
+
*
|
|
51
|
+
* @param {CHECKABLE_OBJECT} object object to be validated
|
|
52
|
+
* @param {S} schema schema to validate against
|
|
53
|
+
* @param {SCHEMA_OPTIONS} options options affecting how the rules are run
|
|
54
|
+
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
55
|
+
* @returns {CHECKED_SCHEMA_SYNC<S>} an object containing keys and their respective lists of failed rule names
|
|
56
|
+
*/
|
|
57
|
+
export declare const getSchemaErrorsSync: <S extends SCHEMA_SYNC>(object: CHECKABLE_OBJECT, schema: S, options?: SCHEMA_OPTIONS, ...overload: unknown[]) => CHECKED_SCHEMA_SYNC<S>;
|
|
58
|
+
/**
|
|
59
|
+
* Check the input against all provided validation rules, choosing the sync or async
|
|
60
|
+
* implementation based on whether any rule is an `async` function.
|
|
61
|
+
*
|
|
62
|
+
* The return type is inferred from the rules: all-sync rules yield `string[]`,
|
|
63
|
+
* any `async` rule yields `Promise<string[]>`. Rules whose return type is declared
|
|
64
|
+
* as `boolean | Promise<boolean>` (e.g. annotated as `RULE`) yield the union.
|
|
65
|
+
*
|
|
66
|
+
* @param {V} value the value to check
|
|
67
|
+
* @param {R} rules rules object to use for validating the provided value
|
|
68
|
+
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
69
|
+
* @returns {Promise<string[]>|string[]} a list of rule names which failed validation
|
|
70
|
+
*/
|
|
71
|
+
export declare const getValueErrors: <V, R extends RULES<V>>(value: V, rules: R, ...overload: unknown[]) => MAYBE_ASYNC<RULES_KIND<R>, string[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Check a schema against all provided validation rules, choosing the sync or async
|
|
74
|
+
* implementation based on whether any rule is an `async` function.
|
|
75
|
+
*
|
|
76
|
+
* The return type is inferred from the schema: all-sync rules yield the result object
|
|
77
|
+
* directly, any `async` rule yields a `Promise` of it. Rules whose return type is declared
|
|
78
|
+
* as `boolean | Promise<boolean>` (e.g. a schema annotated as `SCHEMA`) yield the union.
|
|
79
|
+
*
|
|
80
|
+
* @param {CHECKABLE_OBJECT} object object to be validated
|
|
81
|
+
* @param {S} schema schema to validate against
|
|
82
|
+
* @param {SCHEMA_OPTIONS} options options affecting how the rules are run
|
|
83
|
+
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
84
|
+
* @returns {CHECKED_SCHEMA<S>|CHECKED_SCHEMA_SYNC<S>} an object containing keys and their respective lists of failed rule names
|
|
85
|
+
*/
|
|
86
|
+
export declare const getSchemaErrors: <S extends SCHEMA>(object: CHECKABLE_OBJECT, schema: S, options?: SCHEMA_OPTIONS, ...overload: unknown[]) => MAYBE_ASYNC<SCHEMA_KIND<S>, CHECKED_SCHEMA_SYNC<S>>;
|
package/lib/helpers/index.d.ts
CHANGED
|
@@ -1,21 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type RULE_SYNC = (value: unknown, ...overload: unknown[]) => boolean;
|
|
4
|
-
export type RULE = (value: unknown, ...overload: unknown[]) => boolean | Promise<boolean>;
|
|
5
|
-
export type RULES = {
|
|
6
|
-
[index: string]: RULE;
|
|
7
|
-
};
|
|
8
|
-
export declare const isType: (type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function") => RULE_SYNC;
|
|
9
|
-
export declare const matchesRegex: (regex: RegExp) => RULE_SYNC;
|
|
10
|
-
export declare const max: (max_value?: number) => RULE_SYNC;
|
|
11
|
-
export declare const min: (min_value?: number) => RULE_SYNC;
|
|
12
|
-
export declare const maxLength: (max_length?: number) => RULE_SYNC;
|
|
13
|
-
export declare const minLength: (min_length?: number) => RULE_SYNC;
|
|
14
|
-
export declare const stringBetween: (max?: number, min?: number) => RULE_SYNC;
|
|
15
|
-
export declare const numberBetween: (max?: number, min?: number) => RULE_SYNC;
|
|
16
|
-
export declare const acceptAnyAsync: (rules?: RULE[]) => RULE;
|
|
17
|
-
export declare const acceptAnySync: (rules?: RULE_SYNC[]) => RULE_SYNC;
|
|
18
|
-
export declare const allowUndefined: (rules: RULES) => {};
|
|
19
|
-
export declare const preprocess: (fn: Function, rules: RULES) => {};
|
|
20
|
-
|
|
21
|
-
export {};
|
|
1
|
+
export * from './rules';
|
|
2
|
+
export * from './schema';
|
package/lib/helpers/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var S=(n)=>typeof n==="string"||n!==null&&typeof n==="object"&&("length"in n)&&typeof n.length==="number",u=(n)=>(e)=>typeof e===n,c=(n)=>(e)=>typeof e==="string"&&n.test(e),C=(n=1/0)=>(e)=>typeof e==="number"&&e<=n,a=(n=-1/0)=>(e)=>typeof e==="number"&&e>=n,R=(n=1/0)=>(e)=>S(e)&&e.length<=n,_=(n=0)=>(e)=>S(e)&&e.length>=n,p=(n=1/0,e=0)=>(t)=>typeof t==="string"&&t.length>=e&&t.length<=n,l=(n=1/0,e=-1/0)=>(t)=>typeof t==="number"&&t>=e&&t<=n,m=(n=[])=>async(e,...t)=>(await Promise.all(n.map((r)=>r(e,...t)))).some((r)=>r),y=(n=[])=>(e,...t)=>n.map((r)=>r(e,...t)).some((r)=>r);var i=(n)=>typeof n==="function"&&n.constructor.name==="AsyncFunction";var N=(n)=>Object.entries(n).reduce((e,[t,r])=>{if(i(r))return{...e,[t]:async(o,...s)=>typeof o>"u"?!0:r(o,...s)};return{...e,[t]:(o,...s)=>typeof o>"u"?!0:r(o,...s)}},{}),O=(n,e)=>Object.entries(e).reduce((t,[r,o])=>{if(i(o))return{...t,[r]:async(s,...E)=>o(n(s),...E)};return{...t,[r]:(s,...E)=>o(n(s),...E)}},{});export{m as acceptAnyAsync,y as acceptAnySync,N as allowUndefined,u as isType,c as matchesRegex,C as max,R as maxLength,a as min,_ as minLength,l as numberBetween,O as preprocess,p as stringBetween};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RULE, RULE_SYNC } from '../types';
|
|
2
|
+
export type TYPEOF_RESULT = "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function";
|
|
3
|
+
export declare const isType: (type: TYPEOF_RESULT) => RULE_SYNC;
|
|
4
|
+
export declare const matchesRegex: (regex: RegExp) => RULE_SYNC;
|
|
5
|
+
export declare const max: (max_value?: number) => RULE_SYNC;
|
|
6
|
+
export declare const min: (min_value?: number) => RULE_SYNC;
|
|
7
|
+
export declare const maxLength: (max_length?: number) => RULE_SYNC;
|
|
8
|
+
export declare const minLength: (min_length?: number) => RULE_SYNC;
|
|
9
|
+
export declare const stringBetween: (max?: number, min?: number) => RULE_SYNC;
|
|
10
|
+
export declare const numberBetween: (max?: number, min?: number) => RULE_SYNC;
|
|
11
|
+
/** Passes if *any* of the given rules passes. Always async. */
|
|
12
|
+
export declare const acceptAnyAsync: <V = unknown, O extends unknown[] = unknown[]>(rules?: RULE<V, O>[]) => RULE<V, O>;
|
|
13
|
+
/** Passes if *any* of the given rules passes. All rules must be synchronous. */
|
|
14
|
+
export declare const acceptAnySync: <V = unknown, O extends unknown[] = unknown[]>(rules?: RULE_SYNC<V, O>[]) => RULE_SYNC<V, O>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RULES } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Re-declares each rule so that the parameters of the returned rule are `V` instead of the
|
|
4
|
+
* original rule's parameters, while keeping the original return type (sync stays sync,
|
|
5
|
+
* async stays async).
|
|
6
|
+
*/
|
|
7
|
+
type REWRAPPED<R extends RULES<never>, V> = {
|
|
8
|
+
[K in keyof R]: R[K] extends (value: never, ...overload: infer O) => infer Ret ? (value: V, ...overload: O) => Ret : never;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Wraps every rule so that an `undefined` value always passes.
|
|
12
|
+
* Sync rules stay sync and async rules stay async, so `getValueErrors`/`getSchemaErrors`
|
|
13
|
+
* still pick the correct execution path.
|
|
14
|
+
*/
|
|
15
|
+
export declare const allowUndefined: <R extends RULES>(rules: R) => R;
|
|
16
|
+
/**
|
|
17
|
+
* Wraps every rule so that the value is passed through `fn` before being validated.
|
|
18
|
+
* The rules may be typed against the *output* of `fn`; the returned rules accept `unknown`.
|
|
19
|
+
* Sync rules stay sync and async rules stay async, so `getValueErrors`/`getSchemaErrors`
|
|
20
|
+
* still pick the correct execution path.
|
|
21
|
+
*/
|
|
22
|
+
export declare const preprocess: <V, R extends RULES<V>>(fn: (value: unknown) => V, rules: R) => REWRAPPED<R, unknown>;
|
|
23
|
+
export {};
|
package/lib/index.d.ts
CHANGED
|
@@ -1,94 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type RULE_SYNC = (value: unknown, ...overload: unknown[]) => boolean;
|
|
4
|
-
export type RULE = (value: unknown, ...overload: unknown[]) => boolean | Promise<boolean>;
|
|
5
|
-
export type RULES_SYNC = {
|
|
6
|
-
[index: string]: RULE_SYNC;
|
|
7
|
-
};
|
|
8
|
-
export type RULES = {
|
|
9
|
-
[index: string]: RULE;
|
|
10
|
-
};
|
|
11
|
-
export type SCHEMA_SYNC = {
|
|
12
|
-
[index: string]: RULES_SYNC | RULES_SYNC[];
|
|
13
|
-
};
|
|
14
|
-
export type SCHEMA = {
|
|
15
|
-
[index: string]: RULES | RULES[];
|
|
16
|
-
};
|
|
17
|
-
/**
|
|
18
|
-
* @arg {boolean} strict --- Don't allow object to have keys not included in the schema
|
|
19
|
-
*/
|
|
20
|
-
export type SCHEMA_OPTIONS = {
|
|
21
|
-
strict?: boolean;
|
|
22
|
-
};
|
|
23
|
-
export type CHECKABLE_OBJECT = {
|
|
24
|
-
[index: string]: unknown;
|
|
25
|
-
};
|
|
26
|
-
export type CHECKED_SCHEMA_SYNC<T extends keyof CHECKABLE_OBJECT> = {
|
|
27
|
-
[K in T]: string[] | string[][];
|
|
28
|
-
};
|
|
29
|
-
export type CHECKED_SCHEMA<T extends keyof CHECKABLE_OBJECT> = Promise<CHECKED_SCHEMA_SYNC<T>>;
|
|
30
|
-
/**
|
|
31
|
-
* Detect if a RULES object has any async rules in it
|
|
32
|
-
*
|
|
33
|
-
* @param {Record<string, RULE>|Record<string, RULE>[]} rules a rules object
|
|
34
|
-
* @returns {boolean} true if any of the rules is an async function otherwise false
|
|
35
|
-
*/
|
|
36
|
-
export declare const hasAsyncFunction: (rules: Record<string, RULE> | Record<string, RULE>[]) => boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Check the input against all provided validation rules asynchronously
|
|
39
|
-
*
|
|
40
|
-
* @param {unknown} value the value to check
|
|
41
|
-
* @param {RULES} rules rules object to use for validating the provided value
|
|
42
|
-
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
43
|
-
* @returns {string[]} a list of rule names which failed validation
|
|
44
|
-
*/
|
|
45
|
-
export declare const getValueErrorsAsync: (value: unknown, rules: RULES, ...overload: unknown[]) => Promise<string[]>;
|
|
46
|
-
/**
|
|
47
|
-
* Check a schema against all provided validation rules asynchronously
|
|
48
|
-
*
|
|
49
|
-
* @param {CHECKABLE_OBJECT} object_to_check object to be validated
|
|
50
|
-
* @param {SCHEMA} schema schema to validate against
|
|
51
|
-
* @param {SCHEMA_OPTIONS} options options affecting how the rules are run
|
|
52
|
-
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
53
|
-
* @returns {CHECKED_SCHEMA<T>} an object containing keys and their respective lists of failed rule names
|
|
54
|
-
*/
|
|
55
|
-
export declare const getSchemaErrorsAsync: <T extends keyof CHECKABLE_OBJECT>(object_to_check: Pick<CHECKABLE_OBJECT, T>, schema: {
|
|
56
|
-
[K in T]: RULES | RULES[];
|
|
57
|
-
}, options?: SCHEMA_OPTIONS, ...overload: unknown[]) => Promise<CHECKED_SCHEMA_SYNC<T>>;
|
|
58
|
-
/**
|
|
59
|
-
*
|
|
60
|
-
* @param {unknown} value the value to check
|
|
61
|
-
* @param {RULES_SYNC} rules rules object to use for validating the provided value
|
|
62
|
-
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
63
|
-
* @returns {string[]} a list of rule names which failed validation
|
|
64
|
-
*/
|
|
65
|
-
export declare const getValueErrorsSync: (value: unknown, rules: RULES_SYNC, ...overload: unknown[]) => string[];
|
|
66
|
-
/**
|
|
67
|
-
* Check a schema against all provided validation rules asynchronously
|
|
68
|
-
*
|
|
69
|
-
* @param {CHECKABLE_OBJECT} object object to be validated
|
|
70
|
-
* @param {SCHEMA} schema schema to validate against
|
|
71
|
-
* @param {SCHEMA_OPTIONS} options options affecting how the rules are run
|
|
72
|
-
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
73
|
-
* @returns {CHECKED_SCHEMA_SYNC<T>} an object containing keys and their respective lists of failed rule names
|
|
74
|
-
*/
|
|
75
|
-
export declare const getSchemaErrorsSync: <T extends keyof CHECKABLE_OBJECT>(object: CHECKABLE_OBJECT, schema: SCHEMA_SYNC, options?: SCHEMA_OPTIONS, ...overload: unknown[]) => CHECKED_SCHEMA_SYNC<T>;
|
|
76
|
-
/**
|
|
77
|
-
* @param {unknown} value the value to check
|
|
78
|
-
* @param {RULES|RULES_SYNC} rules rules object to use for validating the provided value
|
|
79
|
-
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
80
|
-
* @returns {Promise<string[]>|string[]} a list of rule names which failed validation
|
|
81
|
-
*/
|
|
82
|
-
export declare const getValueErrors: (value: unknown, rules: RULES | RULES_SYNC, ...overload: unknown[]) => Promise<string[]> | string[];
|
|
83
|
-
/**
|
|
84
|
-
* Check a schema against all provided validation rules
|
|
85
|
-
*
|
|
86
|
-
* @param {CHECKABLE_OBJECT} object object to be validated
|
|
87
|
-
* @param {SCHEMA|SCHEMA_SYNC} schema schema to validate against
|
|
88
|
-
* @param {SCHEMA_OPTIONS} options options affecting how the rules are run
|
|
89
|
-
* @param {unknown[]} overload array of extra values the validator rules might need to properly validate the value
|
|
90
|
-
* @returns {CHECKED_SCHEMA} an object containing keys and their respective lists of failed rule names
|
|
91
|
-
*/
|
|
92
|
-
export declare const getSchemaErrors: <T extends keyof CHECKABLE_OBJECT>(object: CHECKABLE_OBJECT, schema: SCHEMA_SYNC | SCHEMA, options?: SCHEMA_OPTIONS, ...overload: unknown[]) => CHECKED_SCHEMA<T> | CHECKED_SCHEMA_SYNC<T>;
|
|
93
|
-
|
|
94
|
-
export {};
|
|
1
|
+
export * from './core';
|
|
2
|
+
export * from './types';
|
package/lib/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var A={strict:!1};var u=(n)=>typeof n==="function"&&n.constructor.name==="AsyncFunction",N=(n)=>{if(Array.isArray(n))return n.some((s)=>Object.values(s).some(u));return Object.values(n).some(u)},a=async(n,s,...S)=>{let r={};return Object.entries(s).forEach(([t,E])=>{r[t]=Promise.resolve(!1).then(()=>E(n,...S)).then((e)=>r[t]=e).catch(()=>r[t]=!1)}),await Promise.allSettled(Object.values(r)),Object.entries(r).reduce((t,[E,e])=>e?t:[...t,E],[])},O=async(n,s,S=A,...r)=>{let t={},E=[];if(Object.entries(s).forEach(([e,C])=>{let i;if(Array.isArray(C))i=Promise.allSettled(C.map((o)=>a(n[e],o,...r))).then((o)=>{let l=o.map((c)=>{if(c.status==="fulfilled")return c.value;return["Unknown error occurred"]});if(l.some((c)=>!c.length))t[e]=[];else t[e]=l}).catch((o)=>{if(o instanceof Error)t[e]=[o.message];else t[e]=["Unknown error occurred"]});else i=a(n[e],C,...r).then((o)=>{t[e]=o}).catch((o)=>{t[e]=[o instanceof Error?o.message:"Unknown error occurred"]});E.push(i)}),S.strict){let e=Object.keys(n),C=new Set(Object.keys(s));e.filter((o)=>!C.has(o)).forEach((o)=>{t[o]=["Key not allowed"]})}return await Promise.allSettled(E),t},_=(n,s,...S)=>Object.entries(s).reduce((r,[t,E])=>{try{return E(n,...S)?r:[...r,t]}catch(e){return[...r,t]}},[]),H=(n,s,S=A,...r)=>{let t=Object.entries(s).reduce((E,[e,C])=>{if(Array.isArray(C)){let i=C.map((o)=>_(n[e],o,...r));if(i.some((o)=>!o.length))return{...E,[e]:[]};return{...E,[e]:i}}return{...E,[e]:_(n[e],C,...r)}},{});if(S.strict){let E=Object.keys(n),e=new Set(Object.keys(s));E.filter((i)=>!e.has(i)).forEach((i)=>{t[i]=["Key not allowed"]})}return t},M=(n,s,...S)=>N(s)?a(n,s,...S):_(n,s,...S),m=(n)=>Object.values(n).some(N),R=(n,s,S=A,...r)=>m(s)?O(n,s,S,...r):H(n,s,S,...r);export{R as getSchemaErrors,O as getSchemaErrorsAsync,H as getSchemaErrorsSync,M as getValueErrors,a as getValueErrorsAsync,_ as getValueErrorsSync,N as hasAsyncFunction,u as isAsyncFunction};
|
package/lib/patterns/index.d.ts
CHANGED
|
@@ -1,14 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare const EMAIL_PATTERN: RegExp;
|
|
4
|
-
export declare const MYSQL_TIMESTAMP_PATTERN: RegExp;
|
|
5
|
-
export declare const URL_PATTERN: RegExp;
|
|
6
|
-
export declare const ISO_8601_DATETIME_PATTERN_STRICT: RegExp;
|
|
7
|
-
export declare const ISO_8601_DATETIME_PATTERN: RegExp;
|
|
8
|
-
export declare const ISO_8601_TIME_PATTERN: RegExp;
|
|
9
|
-
export declare const CONTAINS_LOWERCASE_CHARACTER_PATTERN: RegExp;
|
|
10
|
-
export declare const CONTAINS_UPPERCASE_CHARACTER_PATTERN: RegExp;
|
|
11
|
-
export declare const CONTAINS_DIGIT_CHARACTER_PATTERN: RegExp;
|
|
12
|
-
export declare const CONTAINS_SYMBOL_CHARACTER_PATTERN: RegExp;
|
|
13
|
-
|
|
14
|
-
export {};
|
|
1
|
+
export * from './patterns';
|
package/lib/patterns/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var T=/^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{1,}$/,A=/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/,_=/^(https?:\/\/)?([^\s$.?#].[^\s]*)\.[a-z]{2,}(\/[^ \t\r\n\v\f]*)?$/i,E=/^(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:[+-][0-2]\d:[0-5]\d|Z)?)$/,t=/^(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d(?::[0-5]\d(?:\.\d+)?)?)$/,d=/^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(Z|[+-](?:2[0-3]|[01][0-9]):([0-5][0-9]))?$/,o=/[a-z]/,R=/[A-Z]/,N=/\d/,C=/[^\w\s]/;export{N as CONTAINS_DIGIT_CHARACTER_PATTERN,o as CONTAINS_LOWERCASE_CHARACTER_PATTERN,C as CONTAINS_SYMBOL_CHARACTER_PATTERN,R as CONTAINS_UPPERCASE_CHARACTER_PATTERN,T as EMAIL_PATTERN,t as ISO_8601_DATETIME_PATTERN,E as ISO_8601_DATETIME_PATTERN_STRICT,d as ISO_8601_TIME_PATTERN,A as MYSQL_TIMESTAMP_PATTERN,_ as URL_PATTERN};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const EMAIL_PATTERN: RegExp;
|
|
2
|
+
export declare const MYSQL_TIMESTAMP_PATTERN: RegExp;
|
|
3
|
+
export declare const URL_PATTERN: RegExp;
|
|
4
|
+
export declare const ISO_8601_DATETIME_PATTERN_STRICT: RegExp;
|
|
5
|
+
export declare const ISO_8601_DATETIME_PATTERN: RegExp;
|
|
6
|
+
export declare const ISO_8601_TIME_PATTERN: RegExp;
|
|
7
|
+
export declare const CONTAINS_LOWERCASE_CHARACTER_PATTERN: RegExp;
|
|
8
|
+
export declare const CONTAINS_UPPERCASE_CHARACTER_PATTERN: RegExp;
|
|
9
|
+
export declare const CONTAINS_DIGIT_CHARACTER_PATTERN: RegExp;
|
|
10
|
+
export declare const CONTAINS_SYMBOL_CHARACTER_PATTERN: RegExp;
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A synchronous validation rule.
|
|
3
|
+
*
|
|
4
|
+
* @template V type of the value being validated (defaults to `unknown`)
|
|
5
|
+
* @template O tuple type of any extra "overload" arguments passed through by the caller
|
|
6
|
+
*/
|
|
7
|
+
export type RULE_SYNC<V = unknown, O extends unknown[] = unknown[]> = (value: V, ...overload: O) => boolean;
|
|
8
|
+
/**
|
|
9
|
+
* An asynchronous validation rule.
|
|
10
|
+
*
|
|
11
|
+
* @template V type of the value being validated (defaults to `unknown`)
|
|
12
|
+
* @template O tuple type of any extra "overload" arguments passed through by the caller
|
|
13
|
+
*/
|
|
14
|
+
export type RULE_ASYNC<V = unknown, O extends unknown[] = unknown[]> = (value: V, ...overload: O) => Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* A validation rule which may be either synchronous or asynchronous.
|
|
17
|
+
*
|
|
18
|
+
* @template V type of the value being validated (defaults to `unknown`)
|
|
19
|
+
* @template O tuple type of any extra "overload" arguments passed through by the caller
|
|
20
|
+
*/
|
|
21
|
+
export type RULE<V = unknown, O extends unknown[] = unknown[]> = (value: V, ...overload: O) => boolean | Promise<boolean>;
|
|
22
|
+
/** A named set of synchronous rules. Keys are the error messages returned on failure. */
|
|
23
|
+
export type RULES_SYNC<V = unknown, O extends unknown[] = unknown[]> = {
|
|
24
|
+
[rule_name: string]: RULE_SYNC<V, O>;
|
|
25
|
+
};
|
|
26
|
+
/** A named set of rules, any of which may be asynchronous. Keys are the error messages returned on failure. */
|
|
27
|
+
export type RULES<V = unknown, O extends unknown[] = unknown[]> = {
|
|
28
|
+
[rule_name: string]: RULE<V, O>;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Maps object keys to a rule set, or to an array of alternative rule sets
|
|
32
|
+
* (the value passes if it satisfies *any* of the alternatives).
|
|
33
|
+
*/
|
|
34
|
+
export type SCHEMA_SYNC = {
|
|
35
|
+
[key: string]: RULES_SYNC | RULES_SYNC[];
|
|
36
|
+
};
|
|
37
|
+
export type SCHEMA = {
|
|
38
|
+
[key: string]: RULES | RULES[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* @arg {boolean} strict --- Don't allow object to have keys not included in the schema
|
|
42
|
+
*/
|
|
43
|
+
export type SCHEMA_OPTIONS = {
|
|
44
|
+
strict?: boolean;
|
|
45
|
+
};
|
|
46
|
+
export type CHECKABLE_OBJECT = {
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* The result of checking a single schema entry.
|
|
51
|
+
* A plain rule set yields `string[]`; an array of alternative rule sets yields `string[][]`
|
|
52
|
+
* (one error list per alternative, or `[]` if any alternative passed).
|
|
53
|
+
*/
|
|
54
|
+
export type CHECKED_RULES<R> = R extends readonly unknown[] ? string[][] : string[];
|
|
55
|
+
/**
|
|
56
|
+
* The result of checking a schema: one entry per schema key, with a shape that
|
|
57
|
+
* depends on that key's rule set. When `strict: true` is used, keys not present in
|
|
58
|
+
* the schema may also appear with the value `['Key not allowed']`.
|
|
59
|
+
*/
|
|
60
|
+
export type CHECKED_SCHEMA_SYNC<S extends SCHEMA = SCHEMA> = {
|
|
61
|
+
[K in keyof S]: CHECKED_RULES<S[K]>;
|
|
62
|
+
} & {
|
|
63
|
+
[extra_key: string]: string[] | string[][];
|
|
64
|
+
};
|
|
65
|
+
export type CHECKED_SCHEMA<S extends SCHEMA = SCHEMA> = Promise<CHECKED_SCHEMA_SYNC<S>>;
|
|
66
|
+
/**
|
|
67
|
+
* Classifies a rule by its declared return type:
|
|
68
|
+
* - `'sync'` – returns `boolean`
|
|
69
|
+
* - `'async'` – returns `Promise<boolean>`
|
|
70
|
+
* - `'either'` – declared as returning `boolean | Promise<boolean>` (e.g. annotated as `RULE`),
|
|
71
|
+
* so it can't be determined statically
|
|
72
|
+
*/
|
|
73
|
+
export type RULE_KIND<R> = R extends (...args: never[]) => infer Ret ? [Ret] extends [boolean] ? 'sync' : [Ret] extends [Promise<boolean>] ? 'async' : 'either' : never;
|
|
74
|
+
/** Union of the kinds of every rule in a rule set (or in an array of rule sets). */
|
|
75
|
+
export type RULES_KIND<R> = R extends readonly (infer E)[] ? RULES_KIND<E> : R extends object ? RULE_KIND<R[keyof R]> : never;
|
|
76
|
+
/** Union of the kinds of every rule in a schema. */
|
|
77
|
+
export type SCHEMA_KIND<S> = RULES_KIND<S[keyof S]>;
|
|
78
|
+
/**
|
|
79
|
+
* Wraps a result type according to the kinds found in a rule set / schema:
|
|
80
|
+
* any definitely-async rule => `Promise<X>`; all sync => `X`; otherwise `X | Promise<X>`.
|
|
81
|
+
*/
|
|
82
|
+
export type MAYBE_ASYNC<K, X> = 'async' extends K ? Promise<X> : 'either' extends K ? X | Promise<X> : X;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ivl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Oskar Voorel",
|
|
6
6
|
"email": "oskar@voorel.com"
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
},
|
|
12
12
|
"main": "lib/index.js",
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"@types/bun": "
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"typescript": "^
|
|
18
|
-
"yup": "^1.
|
|
19
|
-
"zod": "^
|
|
14
|
+
"@types/bun": "^1.4.0",
|
|
15
|
+
"mitata": "^1.0.34",
|
|
16
|
+
"tsc-alias": "^1.9.2",
|
|
17
|
+
"typescript": "^7.0.2",
|
|
18
|
+
"yup": "^1.7.1",
|
|
19
|
+
"zod": "^4.5.2"
|
|
20
20
|
},
|
|
21
21
|
"exports": {
|
|
22
22
|
".": {
|
|
@@ -48,10 +48,13 @@
|
|
|
48
48
|
],
|
|
49
49
|
"license": "MIT",
|
|
50
50
|
"scripts": {
|
|
51
|
-
"build": "rm -rf ./lib && bun run build.mjs",
|
|
51
|
+
"build": "rm -rf ./lib && bun run build.mjs && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
52
52
|
"prepublishOnly": "bun run build",
|
|
53
|
-
"benchmark": "bun run ./benchmark/index.ts"
|
|
53
|
+
"benchmark": "bun run ./benchmark/index.ts",
|
|
54
|
+
"typecheck": "tsc --noEmit -p tsconfig.test.json",
|
|
55
|
+
"test": "bun test && bun run typecheck",
|
|
56
|
+
"release": "bun run ./scripts/release.ts"
|
|
54
57
|
},
|
|
55
|
-
"sideEffects":
|
|
58
|
+
"sideEffects": false,
|
|
56
59
|
"types": "lib/index.d.ts"
|
|
57
60
|
}
|