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 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 `getInputErrors` and `getSchemaErrors` automatically detects and chooses the most performant checking method for your rule set.
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 `getInputErrorsSync`/`getSchemaErrorsSync` or `getInputErrorsAsync`/`getSchemaErrorsAsync` for improved performance on synchronous and asynchronous rule sets respectively.
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(getInputErrors(50, my_rules)); // []
20
- console.log(getInputErrors(11, my_rules)); // ["Input must be more than 40", "Input must be divisible by 10"]
21
- console.log(getInputErrors(30, my_rules)) // ["Input must be more than 40"]
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 { getInputErrors } from 'ivl';
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
- const EMAIL_REQUIREMENTS: RULES = {
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: RULES = {
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: RULES = {
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 = getInputErrors(email_value, EMAIL_REQUIREMENTS);
74
- const pw_errors = getInputErrors(pw_value, PASSWORD_REQUIREMENTS);
75
- const strong_pw_errors = getInputErrors(pw_value, STRONG_PASSWORD_REQUIREMENTS);
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)(REGISTRATION_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>>;
@@ -1,21 +1,2 @@
1
- // Generated by dts-bundle-generator v9.5.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';
@@ -1 +1 @@
1
- var u=(n)=>typeof n==="string"||n!==null&&typeof n==="object"&&("length"in n)&&typeof n.length==="number",c=(n)=>(e)=>typeof e===n,i=(n)=>(e)=>typeof e==="string"&&n.test(e),m=(n=1/0)=>(e)=>typeof e==="number"&&e<=n,y=(n=-1/0)=>(e)=>typeof e==="number"&&e>=n,f=(n=1/0)=>(e)=>u(e)&&e.length<=n,w=(n=0)=>(e)=>u(e)&&e.length>=n,a=(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,g=(n=[])=>async(e,...t)=>(await Promise.all(n.map((o)=>o(e,...t)))).some((o)=>o),h=(n=[])=>(e,...t)=>n.map((o)=>o(e,...t)).some((o)=>o);var l=(n)=>Object.entries(n).reduce((e,[t,o])=>({...e,[t]:(r,...s)=>typeof r==="undefined"?!0:o(r,...s)}),{}),x=(n,e)=>Object.entries(e).reduce((t,[o,r])=>({...t,[o]:(s,...p)=>r(n(s),...p)}),{});export{a as stringBetween,x as preprocess,L as numberBetween,w as minLength,y as min,f as maxLength,m as max,i as matchesRegex,c as isType,l as allowUndefined,h as acceptAnySync,g as acceptAnyAsync};
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
- // Generated by dts-bundle-generator v9.5.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 K={strict:!1},U=(x)=>{if(Array.isArray(x))return x.some((z)=>Object.values(z).some((W)=>typeof W==="function"&&W.constructor.name==="AsyncFunction"));return Object.values(x).some((z)=>typeof z==="function"&&z.constructor.name==="AsyncFunction")},Y=async(x,z,...W)=>{const Q={};return Object.entries(z).forEach(([G,X])=>{Q[G]=Promise.resolve(!1).then(()=>X(x,...W)).then((q)=>Q[G]=q).catch(()=>{return Q[G]=!1})}),await Promise.allSettled(Object.values(Q)),Object.entries(Q).reduce((G,[X,q])=>q?G:[...G,X],[])},I=async(x,z,W=K,...Q)=>{const G={},X=[];if(Object.entries(z).forEach(([q,$])=>{let Z;if(Array.isArray($))Z=Promise.allSettled($.map((R)=>Y(x[q],R,...Q))).then((R)=>{const f=R.map((J)=>{if(J.status==="fulfilled")return J.value;return["Unknown error occurred"]},[]);if(f.some((J)=>!J.length))G[q]=[];else G[q]=f}).catch((R)=>{if(R instanceof Error)G[q]=[R.message];else G[q]=["Unknown error occurred"]});else Z=Y(x[q],$,...Q).then((R)=>{G[q]=R}).catch((R)=>{G[q]=R});X.push(Z)}),W.strict){const q=Object.keys(x),$=new Set(Object.keys(z));q.filter((R)=>!$.has(R)).forEach((R)=>{G[R]=["Key not allowed"]})}return await Promise.allSettled(X),G},B=(x,z,...W)=>Object.entries(z).reduce((Q,[G,X])=>{try{return X(x,...W)?Q:[...Q,G]}catch(q){return[...Q,G]}},[]),L=(x,z,W=K,...Q)=>{const G=Object.entries(z).reduce((X,[q,$])=>{if(Array.isArray($)){const Z=$.map((R)=>B(x[q],R,...Q));if(Z.some((R)=>!R.length))return{...X,[q]:[]};return{...X,[q]:Z}}return{...X,[q]:B(x[q],$,...Q)}},{});if(W.strict){const X=Object.keys(x),q=new Set(Object.keys(z));X.filter((Z)=>!q.has(Z)).forEach((Z)=>{G[Z]=["Key not allowed"]})}return G},D=(x,z,...W)=>{if(U(z))return Y(x,z,...W);return B(x,z,...W)},w=(x)=>Object.values(x).some(U),F=(x,z,W=K,...Q)=>{if(w(z))return I(x,z,W,...Q);return L(x,z,W,...Q)};export{U as hasAsyncFunction,B as getValueErrorsSync,Y as getValueErrorsAsync,D as getValueErrors,L as getSchemaErrorsSync,I as getSchemaErrorsAsync,F as getSchemaErrors};
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};
@@ -1,14 +1 @@
1
- // Generated by dts-bundle-generator v9.5.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';
@@ -1 +1 @@
1
- var x=/^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{1,}$/,c=/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/,d=/^(https?:\/\/)?([^\s$.?#].[^\s]*)\.[a-z]{2,}(\/[^ \t\r\n\v\f]*)?$/i,f=/^(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:[+-][0-2]\d:[0-5]\d|Z)?)$/,j=/^(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d(?::[0-5]\d(?:\.\d+)?)?)$/,k=/^(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]))?$/,l=/[a-z]/,m=/[A-Z]/,p=/\d/,q=/[^\w\s]/;export{d as URL_PATTERN,c as MYSQL_TIMESTAMP_PATTERN,k as ISO_8601_TIME_PATTERN,f as ISO_8601_DATETIME_PATTERN_STRICT,j as ISO_8601_DATETIME_PATTERN,x as EMAIL_PATTERN,m as CONTAINS_UPPERCASE_CHARACTER_PATTERN,q as CONTAINS_SYMBOL_CHARACTER_PATTERN,l as CONTAINS_LOWERCASE_CHARACTER_PATTERN,p as CONTAINS_DIGIT_CHARACTER_PATTERN};
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.1",
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": "latest",
15
- "bun-plugin-dts": "^0.2.1",
16
- "mitata": "^0.1.11",
17
- "typescript": "^5.2.2",
18
- "yup": "^1.4.0",
19
- "zod": "^3.23.8"
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": "false",
58
+ "sideEffects": false,
56
59
  "types": "lib/index.d.ts"
57
60
  }