@satoshibits/functional 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.
- package/README.md +242 -0
- package/dist/array-utils.d.mts +317 -0
- package/dist/array-utils.d.mts.map +1 -0
- package/dist/array-utils.mjs +370 -0
- package/dist/array-utils.mjs.map +1 -0
- package/dist/composition.d.mts +603 -0
- package/dist/composition.d.mts.map +1 -0
- package/dist/composition.mjs +516 -0
- package/dist/composition.mjs.map +1 -0
- package/dist/object-utils.d.mts +267 -0
- package/dist/object-utils.d.mts.map +1 -0
- package/dist/object-utils.mjs +258 -0
- package/dist/object-utils.mjs.map +1 -0
- package/dist/option.d.mts +622 -0
- package/dist/option.d.mts.map +1 -0
- package/dist/option.mjs +637 -0
- package/dist/option.mjs.map +1 -0
- package/dist/performance.d.mts +265 -0
- package/dist/performance.d.mts.map +1 -0
- package/dist/performance.mjs +453 -0
- package/dist/performance.mjs.map +1 -0
- package/dist/pipeline.d.mts +431 -0
- package/dist/pipeline.d.mts.map +1 -0
- package/dist/pipeline.mjs +460 -0
- package/dist/pipeline.mjs.map +1 -0
- package/dist/predicates.d.mts +722 -0
- package/dist/predicates.d.mts.map +1 -0
- package/dist/predicates.mjs +802 -0
- package/dist/predicates.mjs.map +1 -0
- package/dist/reader-result.d.mts +422 -0
- package/dist/reader-result.d.mts.map +1 -0
- package/dist/reader-result.mjs +758 -0
- package/dist/reader-result.mjs.map +1 -0
- package/dist/result.d.mts +684 -0
- package/dist/result.d.mts.map +1 -0
- package/dist/result.mjs +814 -0
- package/dist/result.mjs.map +1 -0
- package/dist/types.d.mts +439 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +191 -0
- package/dist/types.mjs.map +1 -0
- package/dist/validation.d.mts +622 -0
- package/dist/validation.d.mts.map +1 -0
- package/dist/validation.mjs +852 -0
- package/dist/validation.mjs.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module validation
|
|
3
|
+
* @description Functional validation utilities using Result types for composable,
|
|
4
|
+
* type-safe data validation. This module provides a rich set of validators and
|
|
5
|
+
* combinators for building complex validation schemas. Unlike traditional validation
|
|
6
|
+
* libraries that throw exceptions, all validators return Result types, making
|
|
7
|
+
* error handling explicit and composable. Validators can be combined, transformed,
|
|
8
|
+
* and reused to build sophisticated validation logic.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { Validation, validators, schema, ValidationError } from './validation.mts';
|
|
13
|
+
*
|
|
14
|
+
* // simple validators
|
|
15
|
+
* const validateAge = validators.number.between(0, 150);
|
|
16
|
+
* const validateEmail = validators.string.email();
|
|
17
|
+
*
|
|
18
|
+
* // combining validators
|
|
19
|
+
* const validatePassword = Validation.all(
|
|
20
|
+
* validators.string.minLength(8),
|
|
21
|
+
* validators.string.matches(/[A-Z]/, 'Must contain uppercase'),
|
|
22
|
+
* validators.string.matches(/[0-9]/, 'Must contain number')
|
|
23
|
+
* );
|
|
24
|
+
*
|
|
25
|
+
* // object validation schema
|
|
26
|
+
* const userSchema = schema({
|
|
27
|
+
* name: validators.string.nonEmpty(),
|
|
28
|
+
* email: validateEmail,
|
|
29
|
+
* age: Validation.optional(validateAge),
|
|
30
|
+
* password: validatePassword
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* // using the validator
|
|
34
|
+
* const result = userSchema({
|
|
35
|
+
* name: 'John Doe',
|
|
36
|
+
* email: 'john@example.com',
|
|
37
|
+
* age: 30,
|
|
38
|
+
* password: 'SecurePass123'
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* if (result.success) {
|
|
42
|
+
* console.log('Valid user:', result.data);
|
|
43
|
+
* } else {
|
|
44
|
+
* console.error('Validation errors:', result.error.errors);
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @category Core
|
|
49
|
+
* @since 2025-07-03
|
|
50
|
+
*/
|
|
51
|
+
import { Result } from "./result.mjs";
|
|
52
|
+
/**
|
|
53
|
+
* Custom validation error that can hold multiple error messages.
|
|
54
|
+
* @description Extends the standard Error class to accumulate multiple validation
|
|
55
|
+
* errors. This allows validators to collect all errors rather than failing on
|
|
56
|
+
* the first error, providing better user experience for form validation.
|
|
57
|
+
*
|
|
58
|
+
* @category Errors
|
|
59
|
+
* @example
|
|
60
|
+
* // Creating validation errors
|
|
61
|
+
* const error = new ValidationError(['Name is required', 'Email is invalid']);
|
|
62
|
+
* console.log(error.message); // "Validation failed: Name is required, Email is invalid"
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* // Working with errors
|
|
66
|
+
* if (!result.success) {
|
|
67
|
+
* const validationError = result.error;
|
|
68
|
+
* console.log('First error:', validationError.firstError());
|
|
69
|
+
* console.log('All errors:', validationError.errors);
|
|
70
|
+
*
|
|
71
|
+
* if (validationError.hasError('Email is invalid')) {
|
|
72
|
+
* // Handle email error specifically
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* @since 2025-07-03
|
|
77
|
+
*/
|
|
78
|
+
export declare class ValidationError extends Error {
|
|
79
|
+
readonly errors: string[];
|
|
80
|
+
/**
|
|
81
|
+
* Creates a new ValidationError with the given error messages.
|
|
82
|
+
*
|
|
83
|
+
* @param {string[]} errors - Array of error messages
|
|
84
|
+
*/
|
|
85
|
+
constructor(errors: string[]);
|
|
86
|
+
/**
|
|
87
|
+
* Adds additional errors to this validation error.
|
|
88
|
+
* @description Creates a new ValidationError with the combined errors.
|
|
89
|
+
* The original error remains unchanged (immutable).
|
|
90
|
+
*
|
|
91
|
+
* @param {string[]} newErrors - Additional error messages to add
|
|
92
|
+
* @returns {ValidationError} A new ValidationError with all errors
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* const error1 = new ValidationError(['Name required']);
|
|
96
|
+
* const error2 = error1.addErrors(['Email invalid']);
|
|
97
|
+
* // error2.errors => ['Name required', 'Email invalid']
|
|
98
|
+
* // error1.errors => ['Name required'] (unchanged)
|
|
99
|
+
*/
|
|
100
|
+
addErrors(newErrors: string[]): ValidationError;
|
|
101
|
+
/**
|
|
102
|
+
* Checks if this error contains a specific error message.
|
|
103
|
+
* @description Useful for conditional error handling based on specific
|
|
104
|
+
* validation failures.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} error - The error message to check for
|
|
107
|
+
* @returns {boolean} True if the error message is present
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* if (!result.success && result.error.hasError('Email is invalid')) {
|
|
111
|
+
* showEmailHelp();
|
|
112
|
+
* }
|
|
113
|
+
*/
|
|
114
|
+
hasError(error: string): boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Gets the first error message.
|
|
117
|
+
* @description Returns the first error in the list, useful when you only
|
|
118
|
+
* want to display one error at a time.
|
|
119
|
+
*
|
|
120
|
+
* @returns {string | undefined} The first error message or undefined if no errors
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* const firstError = validationError.firstError();
|
|
124
|
+
* if (firstError) {
|
|
125
|
+
* showToast(firstError);
|
|
126
|
+
* }
|
|
127
|
+
*/
|
|
128
|
+
firstError(): string | undefined;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A validator is a function that takes a value and returns a Result.
|
|
132
|
+
* @description The core type of the validation system. Validators are pure functions
|
|
133
|
+
* that take a value and return either a successful Result with the (possibly transformed)
|
|
134
|
+
* value, or a failed Result with a ValidationError.
|
|
135
|
+
*
|
|
136
|
+
* @template T - The type of value being validated
|
|
137
|
+
*
|
|
138
|
+
* @category Types
|
|
139
|
+
* @example
|
|
140
|
+
* // Simple validator
|
|
141
|
+
* const isPositive: Validator<number> = (n) =>
|
|
142
|
+
* n > 0 ? Result.ok(n) : Result.err(new ValidationError(['Must be positive']));
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* // Transforming validator
|
|
146
|
+
* const trimString: Validator<string> = (s) =>
|
|
147
|
+
* Result.ok(s.trim());
|
|
148
|
+
*
|
|
149
|
+
* @since 2025-07-03
|
|
150
|
+
*/
|
|
151
|
+
export type Validator<T> = (value: T) => Result<T, ValidationError>;
|
|
152
|
+
/**
|
|
153
|
+
* Validation utilities for creating and composing validators.
|
|
154
|
+
* @description The main namespace for validation combinators and utilities.
|
|
155
|
+
* Provides methods for creating, combining, and transforming validators
|
|
156
|
+
* in a functional style.
|
|
157
|
+
*
|
|
158
|
+
* @category Utilities
|
|
159
|
+
* @since 2025-07-03
|
|
160
|
+
*/
|
|
161
|
+
export declare const Validation: {
|
|
162
|
+
/**
|
|
163
|
+
* Creates a validator that always succeeds.
|
|
164
|
+
* @description Useful as a default validator or when conditionally applying
|
|
165
|
+
* validation. The value passes through unchanged.
|
|
166
|
+
*
|
|
167
|
+
* @template T - The type of value
|
|
168
|
+
* @returns {Validator<T>} A validator that always returns success
|
|
169
|
+
*
|
|
170
|
+
* @category Constructors
|
|
171
|
+
* @example
|
|
172
|
+
* // Conditional validation
|
|
173
|
+
* const validator = shouldValidate
|
|
174
|
+
* ? validators.string.email()
|
|
175
|
+
* : Validation.success();
|
|
176
|
+
*
|
|
177
|
+
* @since 2025-07-03
|
|
178
|
+
*/
|
|
179
|
+
success: <T>() => Validator<T>;
|
|
180
|
+
/**
|
|
181
|
+
* Creates a validator that always fails with the given error.
|
|
182
|
+
* @description Useful for custom validation logic or placeholder validators
|
|
183
|
+
* during development.
|
|
184
|
+
*
|
|
185
|
+
* @template T - The type of value
|
|
186
|
+
* @param {string} error - The error message
|
|
187
|
+
* @returns {Validator<T>} A validator that always returns failure
|
|
188
|
+
*
|
|
189
|
+
* @category Constructors
|
|
190
|
+
* @example
|
|
191
|
+
* // Feature flag validation
|
|
192
|
+
* const validator = featureEnabled
|
|
193
|
+
* ? actualValidator
|
|
194
|
+
* : Validation.failure('Feature not available');
|
|
195
|
+
*
|
|
196
|
+
* @since 2025-07-03
|
|
197
|
+
*/
|
|
198
|
+
failure: <T>(error: string) => Validator<T>;
|
|
199
|
+
/**
|
|
200
|
+
* Creates a validator from a predicate function.
|
|
201
|
+
* @description The fundamental building block for custom validators. Converts
|
|
202
|
+
* a boolean-returning function into a validator.
|
|
203
|
+
*
|
|
204
|
+
* @template T - The type of value to validate
|
|
205
|
+
* @param {function(T): boolean} predicate - Function that returns true if valid
|
|
206
|
+
* @param {string} error - Error message if validation fails
|
|
207
|
+
* @returns {Validator<T>} A validator based on the predicate
|
|
208
|
+
*
|
|
209
|
+
* @category Constructors
|
|
210
|
+
* @example
|
|
211
|
+
* // Custom age validator
|
|
212
|
+
* const isAdult = Validation.fromPredicate(
|
|
213
|
+
* (age: number) => age >= 18,
|
|
214
|
+
* 'Must be 18 or older'
|
|
215
|
+
* );
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* // Complex validation
|
|
219
|
+
* const isValidUsername = Validation.fromPredicate(
|
|
220
|
+
* (username: string) => /^[a-zA-Z0-9_]{3,20}$/.test(username),
|
|
221
|
+
* 'Username must be 3-20 characters, alphanumeric or underscore'
|
|
222
|
+
* );
|
|
223
|
+
*
|
|
224
|
+
* @since 2025-07-03
|
|
225
|
+
*/
|
|
226
|
+
fromPredicate: <T>(predicate: (value: T) => boolean, error: string) => Validator<T>;
|
|
227
|
+
/**
|
|
228
|
+
* Combines multiple validators using AND logic.
|
|
229
|
+
* @description All validators must pass for the validation to succeed.
|
|
230
|
+
* Collects all errors from all validators before returning, providing
|
|
231
|
+
* comprehensive feedback.
|
|
232
|
+
*
|
|
233
|
+
* @template T - The type of value to validate
|
|
234
|
+
* @param {Validator<T>[]} validators - Validators to combine
|
|
235
|
+
* @returns {Validator<T>} A validator that requires all validations to pass
|
|
236
|
+
*
|
|
237
|
+
* @category Combinators
|
|
238
|
+
* @example
|
|
239
|
+
* // Password validation
|
|
240
|
+
* const validatePassword = Validation.all(
|
|
241
|
+
* validators.string.minLength(8),
|
|
242
|
+
* validators.string.matches(/[A-Z]/, 'Must contain uppercase'),
|
|
243
|
+
* validators.string.matches(/[0-9]/, 'Must contain number'),
|
|
244
|
+
* validators.string.matches(/[!@#$%]/, 'Must contain special character')
|
|
245
|
+
* );
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* // Numeric range validation
|
|
249
|
+
* const validatePercentage = Validation.all(
|
|
250
|
+
* validators.number.min(0),
|
|
251
|
+
* validators.number.max(100),
|
|
252
|
+
* validators.number.integer()
|
|
253
|
+
* );
|
|
254
|
+
*
|
|
255
|
+
* @since 2025-07-03
|
|
256
|
+
*/
|
|
257
|
+
all: <T>(...validators: Validator<T>[]) => Validator<T>;
|
|
258
|
+
/**
|
|
259
|
+
* Combines multiple validators using OR logic.
|
|
260
|
+
* @description At least one validator must pass for the validation to succeed.
|
|
261
|
+
* Returns the result of the first successful validator, or all errors if
|
|
262
|
+
* none succeed.
|
|
263
|
+
*
|
|
264
|
+
* @template T - The type of value to validate
|
|
265
|
+
* @param {Validator<T>[]} validators - Validators to try
|
|
266
|
+
* @returns {Validator<T>} A validator that requires at least one validation to pass
|
|
267
|
+
*
|
|
268
|
+
* @category Combinators
|
|
269
|
+
* @example
|
|
270
|
+
* // Multiple format support
|
|
271
|
+
* const validateDate = Validation.any(
|
|
272
|
+
* validators.string.matches(/^\d{4}-\d{2}-\d{2}$/, 'Invalid ISO date'),
|
|
273
|
+
* validators.string.matches(/^\d{2}\/\d{2}\/\d{4}$/, 'Invalid US date')
|
|
274
|
+
* );
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* // Flexible identifier
|
|
278
|
+
* const validateIdentifier = Validation.any(
|
|
279
|
+
* validators.string.matches(/^\d+$/, 'Not a numeric ID'),
|
|
280
|
+
* validators.string.email(),
|
|
281
|
+
* validators.string.matches(/^[A-Z]{2,}$/, 'Not a code')
|
|
282
|
+
* );
|
|
283
|
+
*
|
|
284
|
+
* @since 2025-07-03
|
|
285
|
+
*/
|
|
286
|
+
any: <T>(...validators: Validator<T>[]) => Validator<T>;
|
|
287
|
+
/**
|
|
288
|
+
* Transforms the validated value if validation passes.
|
|
289
|
+
* @description Note: The returned validator still takes an input of type T.
|
|
290
|
+
* This is the functor map operation for validators, allowing value transformation
|
|
291
|
+
* after successful validation.
|
|
292
|
+
*
|
|
293
|
+
* @template T - The input type
|
|
294
|
+
* @template U - The output type
|
|
295
|
+
* @param {function(T): U} fn - Function to transform the validated value
|
|
296
|
+
* @returns {function(Validator<T>): function(T): Result<U, ValidationError>} A function that transforms validators
|
|
297
|
+
*
|
|
298
|
+
* @category Transformations
|
|
299
|
+
* @example
|
|
300
|
+
* // Normalize email
|
|
301
|
+
* const normalizeEmail = Validation.map((email: string) => email.toLowerCase());
|
|
302
|
+
* const validateEmail = normalizeEmail(validators.string.email());
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* // Parse and validate
|
|
306
|
+
* const parseNumber = Validation.map((s: string) => parseInt(s, 10));
|
|
307
|
+
* const validateNumericString = parseNumber(
|
|
308
|
+
* validators.string.matches(/^\d+$/, 'Must be numeric')
|
|
309
|
+
* );
|
|
310
|
+
*
|
|
311
|
+
* @since 2025-07-03
|
|
312
|
+
*/
|
|
313
|
+
map: <T, U>(fn: (value: T) => U) => (validator: Validator<T>) => ((value: T) => Result<U, ValidationError>);
|
|
314
|
+
/**
|
|
315
|
+
* Chains validators together.
|
|
316
|
+
* @description The choice of the second validator depends on the successful
|
|
317
|
+
* result of the first. This is the monadic bind operation for validators,
|
|
318
|
+
* enabling dynamic validation based on previous results.
|
|
319
|
+
*
|
|
320
|
+
* @template T - The type being validated
|
|
321
|
+
* @param {function(T): Validator<T>} fn - Function that returns the next validator
|
|
322
|
+
* @returns {function(Validator<T>): Validator<T>} A function that chains validators
|
|
323
|
+
*
|
|
324
|
+
* @category Combinators
|
|
325
|
+
* @example
|
|
326
|
+
* // Conditional validation based on value
|
|
327
|
+
* const validateScore = Validation.flatMap((score: number) => {
|
|
328
|
+
* if (score < 0) return Validation.failure('Negative scores not allowed');
|
|
329
|
+
* if (score > 100) return validators.number.max(200); // Allow bonus points
|
|
330
|
+
* return Validation.success();
|
|
331
|
+
* });
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* // Dynamic validation
|
|
335
|
+
* const validateField = Validation.flatMap((field: { type: string; value: any }) => {
|
|
336
|
+
* switch (field.type) {
|
|
337
|
+
* case 'email': return validators.string.email();
|
|
338
|
+
* case 'number': return validators.number.positive();
|
|
339
|
+
* default: return Validation.success();
|
|
340
|
+
* }
|
|
341
|
+
* });
|
|
342
|
+
*
|
|
343
|
+
* @since 2025-07-03
|
|
344
|
+
*/
|
|
345
|
+
flatMap: <T>(fn: (value: T) => Validator<T>) => (validator: Validator<T>) => Validator<T>;
|
|
346
|
+
/**
|
|
347
|
+
* Validates an optional value.
|
|
348
|
+
* @description If the value is null or undefined, validation passes.
|
|
349
|
+
* Otherwise, applies the validator. Useful for optional form fields
|
|
350
|
+
* or nullable database columns.
|
|
351
|
+
*
|
|
352
|
+
* @template T - The type being validated
|
|
353
|
+
* @param {Validator<T>} validator - Validator to apply if value is present
|
|
354
|
+
* @returns {Validator<T | null | undefined>} A validator that handles optional values
|
|
355
|
+
*
|
|
356
|
+
* @category Modifiers
|
|
357
|
+
* @example
|
|
358
|
+
* // Optional email field
|
|
359
|
+
* const validateOptionalEmail = Validation.optional(
|
|
360
|
+
* validators.string.email()
|
|
361
|
+
* );
|
|
362
|
+
*
|
|
363
|
+
* validateOptionalEmail(null); // => { success: true, data: null }
|
|
364
|
+
* validateOptionalEmail('user@example.com'); // => validates as email
|
|
365
|
+
*
|
|
366
|
+
* @example
|
|
367
|
+
* // In object schema
|
|
368
|
+
* const userSchema = schema({
|
|
369
|
+
* name: validators.string.nonEmpty(),
|
|
370
|
+
* email: validators.string.email(),
|
|
371
|
+
* phone: Validation.optional(validators.string.matches(/^\d{10}$/))
|
|
372
|
+
* });
|
|
373
|
+
*
|
|
374
|
+
* @since 2025-07-03
|
|
375
|
+
*/
|
|
376
|
+
optional: <T>(validator: Validator<T>) => Validator<T | null | undefined>;
|
|
377
|
+
/**
|
|
378
|
+
* Makes a validator required.
|
|
379
|
+
* @description Fails if value is null or undefined. This is a type guard
|
|
380
|
+
* that narrows T | null | undefined to T.
|
|
381
|
+
*
|
|
382
|
+
* @template T - The required type
|
|
383
|
+
* @param {string} error - Error message if value is missing
|
|
384
|
+
* @returns {Validator<T | null | undefined>} A validator that requires presence
|
|
385
|
+
*
|
|
386
|
+
* @category Modifiers
|
|
387
|
+
* @example
|
|
388
|
+
* // Basic usage
|
|
389
|
+
* const required = Validation.required<string>();
|
|
390
|
+
* required(null); // => { success: false, error: 'Value is required' }
|
|
391
|
+
* required('hello'); // => { success: true, data: 'hello' }
|
|
392
|
+
*
|
|
393
|
+
* @example
|
|
394
|
+
* // Combining with other validators
|
|
395
|
+
* const validateName = Validation.all(
|
|
396
|
+
* Validation.required<string>('Name is required'),
|
|
397
|
+
* validators.string.minLength(2)
|
|
398
|
+
* );
|
|
399
|
+
*
|
|
400
|
+
* @since 2025-07-03
|
|
401
|
+
*/
|
|
402
|
+
required: <T>(error?: string) => Validator<T | null | undefined>;
|
|
403
|
+
/**
|
|
404
|
+
* Validates each item in an array.
|
|
405
|
+
* @description Applies the same validator to each element of an array,
|
|
406
|
+
* collecting all errors with their indices. Returns a new array with
|
|
407
|
+
* validated (and possibly transformed) items.
|
|
408
|
+
*
|
|
409
|
+
* @template T - The type of array elements
|
|
410
|
+
* @param {Validator<T>} itemValidator - Validator to apply to each item
|
|
411
|
+
* @returns {Validator<T[]>} A validator for arrays
|
|
412
|
+
*
|
|
413
|
+
* @category Collections
|
|
414
|
+
* @example
|
|
415
|
+
* // Validate array of emails
|
|
416
|
+
* const validateEmails = Validation.array(
|
|
417
|
+
* validators.string.email()
|
|
418
|
+
* );
|
|
419
|
+
*
|
|
420
|
+
* const result = validateEmails([
|
|
421
|
+
* 'user@example.com',
|
|
422
|
+
* 'invalid-email',
|
|
423
|
+
* 'admin@example.com'
|
|
424
|
+
* ]);
|
|
425
|
+
* // Error: "[1]: Invalid email format"
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* // Complex item validation
|
|
429
|
+
* const validateUsers = Validation.array(
|
|
430
|
+
* schema({
|
|
431
|
+
* name: validators.string.nonEmpty(),
|
|
432
|
+
* age: validators.number.positive()
|
|
433
|
+
* })
|
|
434
|
+
* );
|
|
435
|
+
*
|
|
436
|
+
* @since 2025-07-03
|
|
437
|
+
*/
|
|
438
|
+
array: <T>(itemValidator: Validator<T>) => Validator<T[]>;
|
|
439
|
+
/**
|
|
440
|
+
* Validates properties of an object.
|
|
441
|
+
* @description Validates specified properties of an object using individual
|
|
442
|
+
* validators. Unspecified properties are passed through unchanged. Errors
|
|
443
|
+
* are prefixed with the property name for clarity.
|
|
444
|
+
*
|
|
445
|
+
* @template T - The object type
|
|
446
|
+
* @param {object} validators - Map of property names to validators
|
|
447
|
+
* @returns {Validator<T>} A validator for the object
|
|
448
|
+
*
|
|
449
|
+
* @category Objects
|
|
450
|
+
* @example
|
|
451
|
+
* // Partial object validation
|
|
452
|
+
* const validatePerson = Validation.object({
|
|
453
|
+
* name: validators.string.nonEmpty(),
|
|
454
|
+
* age: validators.number.positive()
|
|
455
|
+
* });
|
|
456
|
+
*
|
|
457
|
+
* const result = validatePerson({
|
|
458
|
+
* name: 'John',
|
|
459
|
+
* age: -5,
|
|
460
|
+
* extra: 'ignored'
|
|
461
|
+
* });
|
|
462
|
+
* // Error: "age: Number must be positive"
|
|
463
|
+
*
|
|
464
|
+
* @see schema - Type-safe alternative for complete object validation
|
|
465
|
+
* @since 2025-07-03
|
|
466
|
+
*/
|
|
467
|
+
object: <T extends Record<string, any>>(validators: { [K in keyof T]?: Validator<T[K]>; }) => Validator<T>;
|
|
468
|
+
};
|
|
469
|
+
/**
|
|
470
|
+
* Common validators for primitive types.
|
|
471
|
+
* @description Pre-built validators for common validation scenarios.
|
|
472
|
+
* These validators can be used directly or combined with the Validation
|
|
473
|
+
* combinators to create more complex validation logic.
|
|
474
|
+
*
|
|
475
|
+
* @category Validators
|
|
476
|
+
* @example
|
|
477
|
+
* // Using validators directly
|
|
478
|
+
* const emailValidator = validators.string.email();
|
|
479
|
+
* const ageValidator = validators.number.between(0, 150);
|
|
480
|
+
*
|
|
481
|
+
* @example
|
|
482
|
+
* // Combining validators
|
|
483
|
+
* const strongPassword = Validation.all(
|
|
484
|
+
* validators.string.minLength(12),
|
|
485
|
+
* validators.string.matches(/[A-Z]/, 'Need uppercase'),
|
|
486
|
+
* validators.string.matches(/[a-z]/, 'Need lowercase'),
|
|
487
|
+
* validators.string.matches(/[0-9]/, 'Need number')
|
|
488
|
+
* );
|
|
489
|
+
*
|
|
490
|
+
* @since 2025-07-03
|
|
491
|
+
*/
|
|
492
|
+
export declare const validators: {
|
|
493
|
+
/**
|
|
494
|
+
* String validators.
|
|
495
|
+
* @description Validators for string values including length checks,
|
|
496
|
+
* format validation, and pattern matching.
|
|
497
|
+
*
|
|
498
|
+
* @category String Validators
|
|
499
|
+
* @since 2025-07-03
|
|
500
|
+
*/
|
|
501
|
+
string: {
|
|
502
|
+
minLength: (min: number) => Validator<string>;
|
|
503
|
+
maxLength: (max: number) => Validator<string>;
|
|
504
|
+
nonEmpty: () => Validator<string>;
|
|
505
|
+
email: () => Validator<string>;
|
|
506
|
+
url: () => Validator<string>;
|
|
507
|
+
matches: (pattern: RegExp, error?: string) => Validator<string>;
|
|
508
|
+
oneOf: (options: string[]) => Validator<string>;
|
|
509
|
+
};
|
|
510
|
+
/**
|
|
511
|
+
* Number validators.
|
|
512
|
+
*/
|
|
513
|
+
number: {
|
|
514
|
+
min: (min: number) => Validator<number>;
|
|
515
|
+
max: (max: number) => Validator<number>;
|
|
516
|
+
positive: () => Validator<number>;
|
|
517
|
+
nonNegative: () => Validator<number>;
|
|
518
|
+
integer: () => Validator<number>;
|
|
519
|
+
between: (min: number, max: number) => Validator<number>;
|
|
520
|
+
};
|
|
521
|
+
/**
|
|
522
|
+
* Array validators.
|
|
523
|
+
*/
|
|
524
|
+
array: {
|
|
525
|
+
minLength: <T>(min: number) => Validator<T[]>;
|
|
526
|
+
maxLength: <T>(max: number) => Validator<T[]>;
|
|
527
|
+
nonEmpty: <T>() => Validator<T[]>;
|
|
528
|
+
unique: <T>() => Validator<T[]>;
|
|
529
|
+
};
|
|
530
|
+
/**
|
|
531
|
+
* Date validators.
|
|
532
|
+
*/
|
|
533
|
+
date: {
|
|
534
|
+
after: (date: Date) => Validator<Date>;
|
|
535
|
+
before: (date: Date) => Validator<Date>;
|
|
536
|
+
future: () => Validator<Date>;
|
|
537
|
+
past: () => Validator<Date>;
|
|
538
|
+
};
|
|
539
|
+
/**
|
|
540
|
+
* Object validators.
|
|
541
|
+
*/
|
|
542
|
+
object: {
|
|
543
|
+
hasProperty: <T extends object>(prop: keyof T) => Validator<T>;
|
|
544
|
+
notEmpty: () => Validator<Record<string, any>>;
|
|
545
|
+
};
|
|
546
|
+
};
|
|
547
|
+
/**
|
|
548
|
+
* Utility for creating complex validation schemas.
|
|
549
|
+
* @description Type-safe wrapper around Validation.object that requires
|
|
550
|
+
* validators for all properties of the type. This ensures complete
|
|
551
|
+
* validation coverage for object types.
|
|
552
|
+
*
|
|
553
|
+
* @template T - The object type to validate
|
|
554
|
+
* @param {object} validators - Validators for each property of T
|
|
555
|
+
* @returns {Validator<T>} A validator for objects of type T
|
|
556
|
+
*
|
|
557
|
+
* @category Schema
|
|
558
|
+
* @example
|
|
559
|
+
* // Type-safe user validation
|
|
560
|
+
* interface User {
|
|
561
|
+
* name: string;
|
|
562
|
+
* email: string;
|
|
563
|
+
* age: number;
|
|
564
|
+
* }
|
|
565
|
+
*
|
|
566
|
+
* const userValidator = schema<User>({
|
|
567
|
+
* name: validators.string.nonEmpty(),
|
|
568
|
+
* email: validators.string.email(),
|
|
569
|
+
* age: validators.number.between(0, 150)
|
|
570
|
+
* });
|
|
571
|
+
*
|
|
572
|
+
* @example
|
|
573
|
+
* // Nested schemas
|
|
574
|
+
* const addressValidator = schema({
|
|
575
|
+
* street: validators.string.nonEmpty(),
|
|
576
|
+
* city: validators.string.nonEmpty(),
|
|
577
|
+
* zipCode: validators.string.matches(/^\d{5}$/)
|
|
578
|
+
* });
|
|
579
|
+
*
|
|
580
|
+
* const personValidator = schema({
|
|
581
|
+
* name: validators.string.nonEmpty(),
|
|
582
|
+
* address: addressValidator
|
|
583
|
+
* });
|
|
584
|
+
*
|
|
585
|
+
* @since 2025-07-03
|
|
586
|
+
*/
|
|
587
|
+
export declare const schema: <T extends Record<string, any>>(validators: { [K in keyof T]: Validator<T[K]>; }) => Validator<T>;
|
|
588
|
+
/**
|
|
589
|
+
* Validates a value and returns either the validated value or throws the error.
|
|
590
|
+
* @description Use sparingly, only when you're certain validation should pass.
|
|
591
|
+
* This bridges the Result-based validation system with exception-based code.
|
|
592
|
+
*
|
|
593
|
+
* @template T - The type being validated
|
|
594
|
+
* @param {Validator<T>} validator - The validator to apply
|
|
595
|
+
* @returns {function(T): T} A function that validates or throws
|
|
596
|
+
* @throws {ValidationError} If validation fails
|
|
597
|
+
*
|
|
598
|
+
* @category Utilities
|
|
599
|
+
* @example
|
|
600
|
+
* // Configuration validation at startup
|
|
601
|
+
* const validateConfig = validateOrThrow(
|
|
602
|
+
* schema({
|
|
603
|
+
* port: validators.number.between(1, 65535),
|
|
604
|
+
* host: validators.string.nonEmpty()
|
|
605
|
+
* })
|
|
606
|
+
* );
|
|
607
|
+
*
|
|
608
|
+
* // Throws if invalid, otherwise returns validated config
|
|
609
|
+
* const config = validateConfig(loadConfig());
|
|
610
|
+
*
|
|
611
|
+
* @example
|
|
612
|
+
* // Input sanitization
|
|
613
|
+
* const sanitizeEmail = validateOrThrow(
|
|
614
|
+
* Validation.map((s: string) => s.toLowerCase())(
|
|
615
|
+
* validators.string.email()
|
|
616
|
+
* )
|
|
617
|
+
* );
|
|
618
|
+
*
|
|
619
|
+
* @since 2025-07-03
|
|
620
|
+
*/
|
|
621
|
+
export declare const validateOrThrow: <T>(validator: Validator<T>) => (value: T) => T;
|
|
622
|
+
//# sourceMappingURL=validation.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.mts","sourceRoot":"","sources":["../src/validation.mts"],"names":[],"mappings":"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"}
|