unischema 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +780 -228
  2. package/dist/adapters/backend/index.d.mts +2 -1
  3. package/dist/adapters/backend/index.d.ts +2 -1
  4. package/dist/adapters/backend/index.js +17 -441
  5. package/dist/adapters/backend/index.mjs +9 -433
  6. package/dist/adapters/frontend/index.d.mts +2 -1
  7. package/dist/adapters/frontend/index.d.ts +2 -1
  8. package/dist/adapters/frontend/index.js +10 -421
  9. package/dist/adapters/frontend/index.mjs +8 -419
  10. package/dist/chunk-5A4ITJVD.mjs +124 -0
  11. package/dist/chunk-66RFUBVU.js +131 -0
  12. package/dist/chunk-75YSYC4K.mjs +85 -0
  13. package/dist/chunk-76BBWQDH.js +90 -0
  14. package/dist/chunk-7XES4A3M.mjs +237 -0
  15. package/dist/chunk-BVRXGZLS.js +17 -0
  16. package/dist/chunk-COMVAVFU.mjs +335 -0
  17. package/dist/chunk-DT2TQZU7.js +796 -0
  18. package/dist/chunk-FPCCH55A.js +103 -0
  19. package/dist/chunk-IUXRLMET.js +206 -0
  20. package/dist/chunk-JEW6U6CB.js +353 -0
  21. package/dist/chunk-KZCV5IW4.mjs +97 -0
  22. package/dist/chunk-KZZ7NVU3.mjs +41 -0
  23. package/dist/chunk-MFEBMQAU.mjs +779 -0
  24. package/dist/chunk-OIYG5D2I.js +50 -0
  25. package/dist/chunk-RW6HDA5H.mjs +194 -0
  26. package/dist/chunk-TTK77YBI.mjs +15 -0
  27. package/dist/chunk-TXT36BCE.js +248 -0
  28. package/dist/index-C17xs-fU.d.mts +140 -0
  29. package/dist/index-C17xs-fU.d.ts +140 -0
  30. package/dist/index.d.mts +26 -7
  31. package/dist/index.d.ts +26 -7
  32. package/dist/index.js +769 -499
  33. package/dist/index.mjs +695 -487
  34. package/dist/{schema-D9DGC9E_.d.ts → schema-DYE8Wz8X.d.mts} +264 -79
  35. package/dist/{schema-D9DGC9E_.d.mts → schema-Dtp-joeT.d.ts} +264 -79
  36. package/dist/validators/array.d.mts +15 -0
  37. package/dist/validators/array.d.ts +15 -0
  38. package/dist/validators/array.js +31 -0
  39. package/dist/validators/array.mjs +2 -0
  40. package/dist/validators/common.d.mts +13 -0
  41. package/dist/validators/common.d.ts +13 -0
  42. package/dist/validators/common.js +27 -0
  43. package/dist/validators/common.mjs +2 -0
  44. package/dist/validators/date.d.mts +23 -0
  45. package/dist/validators/date.d.ts +23 -0
  46. package/dist/validators/date.js +47 -0
  47. package/dist/validators/date.mjs +2 -0
  48. package/dist/validators/index.d.mts +46 -0
  49. package/dist/validators/index.d.ts +46 -0
  50. package/dist/validators/index.js +256 -0
  51. package/dist/validators/index.mjs +7 -0
  52. package/dist/validators/number.d.mts +25 -0
  53. package/dist/validators/number.d.ts +25 -0
  54. package/dist/validators/number.js +51 -0
  55. package/dist/validators/number.mjs +2 -0
  56. package/dist/validators/object.d.mts +11 -0
  57. package/dist/validators/object.d.ts +11 -0
  58. package/dist/validators/object.js +23 -0
  59. package/dist/validators/object.mjs +2 -0
  60. package/dist/validators/string.d.mts +37 -0
  61. package/dist/validators/string.d.ts +37 -0
  62. package/dist/validators/string.js +75 -0
  63. package/dist/validators/string.mjs +2 -0
  64. package/package.json +82 -5
  65. package/dist/adapters/backend/index.js.map +0 -1
  66. package/dist/adapters/backend/index.mjs.map +0 -1
  67. package/dist/adapters/frontend/index.js.map +0 -1
  68. package/dist/adapters/frontend/index.mjs.map +0 -1
  69. package/dist/index.js.map +0 -1
  70. package/dist/index.mjs.map +0 -1
@@ -0,0 +1,779 @@
1
+ import { strictValidator, omitValidator, pickValidator, keysValidator } from './chunk-75YSYC4K.mjs';
2
+ import { dependsOnValidator, whenValidator, lessThanValidator, greaterThanValidator, notMatchesValidator } from './chunk-KZCV5IW4.mjs';
3
+ import { endsWithValidator, startsWithValidator, containsValidator, lengthValidator, jsonValidator, base64Validator, hexValidator, slugValidator, uppercaseValidator, lowercaseValidator, numericValidator, alphanumericValidator, alphaValidator, ipv6Validator, ipAddressValidator, urlValidator, emailValidator } from './chunk-COMVAVFU.mjs';
4
+ import { finiteValidator, safeValidator, oddValidator, evenValidator, multipleOfValidator, divisibleByValidator, betweenValidator as betweenValidator$1, percentageValidator, longitudeValidator, latitudeValidator, portValidator } from './chunk-RW6HDA5H.mjs';
5
+ import { betweenValidator, ageValidator, weekendValidator, weekdayValidator, thisYearValidator, thisMonthValidator, thisWeekValidator, tomorrowValidator, yesterdayValidator, todayValidator } from './chunk-7XES4A3M.mjs';
6
+ import { compactValidator, sortedValidator, notEmptyValidator, emptyValidator, excludesValidator, includesValidator } from './chunk-5A4ITJVD.mjs';
7
+
8
+ // src/core/validators.ts
9
+ function createError(context, code, message, soft = false, received, expected) {
10
+ return {
11
+ field: context.path,
12
+ code,
13
+ message,
14
+ severity: soft ? "soft" : "hard",
15
+ received,
16
+ expected
17
+ };
18
+ }
19
+ var typeValidators = {
20
+ string: (value, _params, context) => {
21
+ if (value !== void 0 && value !== null && typeof value !== "string") {
22
+ return createError(context, "INVALID_TYPE", `Expected string, got ${typeof value}`);
23
+ }
24
+ return null;
25
+ },
26
+ number: (value, _params, context) => {
27
+ if (value !== void 0 && value !== null && typeof value !== "number") {
28
+ return createError(context, "INVALID_TYPE", `Expected number, got ${typeof value}`);
29
+ }
30
+ if (typeof value === "number" && isNaN(value)) {
31
+ return createError(context, "INVALID_NUMBER", "Value is not a valid number");
32
+ }
33
+ return null;
34
+ },
35
+ boolean: (value, _params, context) => {
36
+ if (value !== void 0 && value !== null && typeof value !== "boolean") {
37
+ return createError(context, "INVALID_TYPE", `Expected boolean, got ${typeof value}`);
38
+ }
39
+ return null;
40
+ },
41
+ date: (value, _params, context) => {
42
+ if (value === void 0 || value === null) return null;
43
+ if (value instanceof Date) {
44
+ if (isNaN(value.getTime())) {
45
+ return createError(context, "INVALID_DATE", "Invalid date value");
46
+ }
47
+ return null;
48
+ }
49
+ if (typeof value === "string") {
50
+ const parsed = new Date(value);
51
+ if (isNaN(parsed.getTime())) {
52
+ return createError(context, "INVALID_DATE", "Invalid date format");
53
+ }
54
+ return null;
55
+ }
56
+ return createError(context, "INVALID_TYPE", `Expected date, got ${typeof value}`);
57
+ },
58
+ array: (value, _params, context) => {
59
+ if (value !== void 0 && value !== null && !Array.isArray(value)) {
60
+ return createError(context, "INVALID_TYPE", `Expected array, got ${typeof value}`);
61
+ }
62
+ return null;
63
+ },
64
+ object: (value, _params, context) => {
65
+ if (value !== void 0 && value !== null) {
66
+ if (typeof value !== "object" || Array.isArray(value)) {
67
+ return createError(context, "INVALID_TYPE", `Expected object, got ${typeof value}`);
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+ };
73
+ var ruleValidators = {
74
+ required: (value, _params, context) => {
75
+ const isEmpty = value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
76
+ if (isEmpty) {
77
+ return createError(context, "REQUIRED", "This field is required");
78
+ }
79
+ return null;
80
+ },
81
+ min: (value, params, context) => {
82
+ const min = params?.value;
83
+ const soft = params?.soft;
84
+ const message = params?.message;
85
+ if (value === void 0 || value === null) return null;
86
+ if (typeof value === "number") {
87
+ if (value < min) {
88
+ return createError(
89
+ context,
90
+ "MIN_VALUE",
91
+ message || `Value must be at least ${min}`,
92
+ soft,
93
+ value,
94
+ { min }
95
+ );
96
+ }
97
+ }
98
+ if (typeof value === "string") {
99
+ if (value.length < min) {
100
+ return createError(
101
+ context,
102
+ "MIN_LENGTH",
103
+ message || `Must be at least ${min} characters`,
104
+ soft,
105
+ value,
106
+ { min }
107
+ );
108
+ }
109
+ }
110
+ if (Array.isArray(value)) {
111
+ if (value.length < min) {
112
+ return createError(
113
+ context,
114
+ "MIN_ITEMS",
115
+ message || `Must have at least ${min} items`,
116
+ soft,
117
+ value,
118
+ { min }
119
+ );
120
+ }
121
+ }
122
+ return null;
123
+ },
124
+ max: (value, params, context) => {
125
+ const max = params?.value;
126
+ const soft = params?.soft;
127
+ const message = params?.message;
128
+ if (value === void 0 || value === null) return null;
129
+ if (typeof value === "number") {
130
+ if (value > max) {
131
+ return createError(
132
+ context,
133
+ "MAX_VALUE",
134
+ message || `Value must be at most ${max}`,
135
+ soft,
136
+ value,
137
+ { max }
138
+ );
139
+ }
140
+ }
141
+ if (typeof value === "string") {
142
+ if (value.length > max) {
143
+ return createError(
144
+ context,
145
+ "MAX_LENGTH",
146
+ message || `Must be at most ${max} characters`,
147
+ soft,
148
+ value,
149
+ { max }
150
+ );
151
+ }
152
+ }
153
+ if (Array.isArray(value)) {
154
+ if (value.length > max) {
155
+ return createError(
156
+ context,
157
+ "MAX_ITEMS",
158
+ message || `Must have at most ${max} items`,
159
+ soft,
160
+ value,
161
+ { max }
162
+ );
163
+ }
164
+ }
165
+ return null;
166
+ },
167
+ // String validators
168
+ email: emailValidator,
169
+ url: urlValidator,
170
+ ipAddress: ipAddressValidator,
171
+ ipv6: ipv6Validator,
172
+ alpha: alphaValidator,
173
+ alphanumeric: alphanumericValidator,
174
+ numeric: numericValidator,
175
+ lowercase: lowercaseValidator,
176
+ uppercase: uppercaseValidator,
177
+ slug: slugValidator,
178
+ hex: hexValidator,
179
+ base64: base64Validator,
180
+ json: jsonValidator,
181
+ length: lengthValidator,
182
+ contains: containsValidator,
183
+ startsWith: startsWithValidator,
184
+ endsWith: endsWithValidator,
185
+ // Number validators
186
+ port: portValidator,
187
+ latitude: latitudeValidator,
188
+ longitude: longitudeValidator,
189
+ percentage: percentageValidator,
190
+ numberBetween: betweenValidator$1,
191
+ divisibleBy: divisibleByValidator,
192
+ multipleOf: multipleOfValidator,
193
+ even: evenValidator,
194
+ odd: oddValidator,
195
+ safe: safeValidator,
196
+ finite: finiteValidator,
197
+ // Date validators
198
+ today: todayValidator,
199
+ yesterday: yesterdayValidator,
200
+ tomorrow: tomorrowValidator,
201
+ thisWeek: thisWeekValidator,
202
+ thisMonth: thisMonthValidator,
203
+ thisYear: thisYearValidator,
204
+ weekday: weekdayValidator,
205
+ weekend: weekendValidator,
206
+ age: ageValidator,
207
+ dateBetween: betweenValidator,
208
+ // Array validators
209
+ includes: includesValidator,
210
+ excludes: excludesValidator,
211
+ empty: emptyValidator,
212
+ notEmpty: notEmptyValidator,
213
+ sorted: sortedValidator,
214
+ compact: compactValidator,
215
+ // Object validators
216
+ keys: keysValidator,
217
+ pick: pickValidator,
218
+ omit: omitValidator,
219
+ strict: strictValidator,
220
+ // Cross-field validators
221
+ notMatches: notMatchesValidator,
222
+ greaterThan: greaterThanValidator,
223
+ lessThan: lessThanValidator,
224
+ when: whenValidator,
225
+ dependsOn: dependsOnValidator,
226
+ pattern: (value, params, context) => {
227
+ if (value === void 0 || value === null || value === "") return null;
228
+ const pattern = params?.pattern;
229
+ const soft = params?.soft;
230
+ const message = params?.message;
231
+ if (typeof value !== "string") return null;
232
+ const regex = new RegExp(pattern);
233
+ if (!regex.test(value)) {
234
+ return createError(
235
+ context,
236
+ "PATTERN_MISMATCH",
237
+ message || `Value does not match required pattern`,
238
+ soft
239
+ );
240
+ }
241
+ return null;
242
+ },
243
+ enum: (value, params, context) => {
244
+ if (value === void 0 || value === null) return null;
245
+ const values = params?.values;
246
+ const soft = params?.soft;
247
+ const message = params?.message;
248
+ if (!values.includes(value)) {
249
+ return createError(
250
+ context,
251
+ "INVALID_ENUM",
252
+ message || `Value must be one of: ${values.join(", ")}`,
253
+ soft
254
+ );
255
+ }
256
+ return null;
257
+ },
258
+ custom: (value, params, context) => {
259
+ const validate2 = params?.validate;
260
+ const soft = params?.soft;
261
+ const message = params?.message;
262
+ if (!validate2) return null;
263
+ const result = validate2(value, context);
264
+ if (typeof result === "boolean") {
265
+ if (!result) {
266
+ return createError(
267
+ context,
268
+ "CUSTOM_VALIDATION",
269
+ message || "Validation failed",
270
+ soft
271
+ );
272
+ }
273
+ return null;
274
+ }
275
+ if (!result.valid) {
276
+ return createError(
277
+ context,
278
+ "CUSTOM_VALIDATION",
279
+ result.message || message || "Validation failed",
280
+ soft
281
+ );
282
+ }
283
+ return null;
284
+ },
285
+ // Enterprise patterns - matches field against another field
286
+ matches: (value, params, context) => {
287
+ if (value === void 0 || value === null) return null;
288
+ const otherField = params?.field;
289
+ const soft = params?.soft;
290
+ const message = params?.message;
291
+ const root = context.root;
292
+ const otherValue = root[otherField];
293
+ if (value !== otherValue) {
294
+ return createError(
295
+ context,
296
+ "FIELD_MISMATCH",
297
+ message || `Must match ${otherField}`,
298
+ soft
299
+ );
300
+ }
301
+ return null;
302
+ },
303
+ // Integer validation
304
+ integer: (value, params, context) => {
305
+ if (value === void 0 || value === null) return null;
306
+ const soft = params?.soft;
307
+ const message = params?.message;
308
+ if (typeof value !== "number" || !Number.isInteger(value)) {
309
+ return createError(
310
+ context,
311
+ "NOT_INTEGER",
312
+ message || "Value must be an integer",
313
+ soft
314
+ );
315
+ }
316
+ return null;
317
+ },
318
+ // Positive number validation
319
+ positive: (value, params, context) => {
320
+ if (value === void 0 || value === null) return null;
321
+ const soft = params?.soft;
322
+ const message = params?.message;
323
+ if (typeof value === "number" && value <= 0) {
324
+ return createError(
325
+ context,
326
+ "NOT_POSITIVE",
327
+ message || "Value must be positive",
328
+ soft
329
+ );
330
+ }
331
+ return null;
332
+ },
333
+ // Negative number validation
334
+ negative: (value, params, context) => {
335
+ if (value === void 0 || value === null) return null;
336
+ const soft = params?.soft;
337
+ const message = params?.message;
338
+ if (typeof value === "number" && value >= 0) {
339
+ return createError(
340
+ context,
341
+ "NOT_NEGATIVE",
342
+ message || "Value must be negative",
343
+ soft
344
+ );
345
+ }
346
+ return null;
347
+ }
348
+ };
349
+ var customValidators = /* @__PURE__ */ new Map();
350
+ function registerValidator(name, validator) {
351
+ customValidators.set(name, validator);
352
+ }
353
+ function getValidator(name) {
354
+ return ruleValidators[name] ?? customValidators.get(name);
355
+ }
356
+ function getTypeValidator(type) {
357
+ return typeValidators[type];
358
+ }
359
+
360
+ // src/core/engine.ts
361
+ function validateField(fieldDef, value, context) {
362
+ const errors = [];
363
+ let processedValue = value;
364
+ if (value === null || value === void 0) {
365
+ if (fieldDef.nullish || fieldDef.nullable && value === null) {
366
+ if (!fieldDef.required) {
367
+ return errors;
368
+ }
369
+ }
370
+ }
371
+ if (fieldDef.preprocess) {
372
+ processedValue = fieldDef.preprocess(value);
373
+ }
374
+ if (fieldDef.transforms && fieldDef.transforms.length > 0) {
375
+ for (const transform of fieldDef.transforms) {
376
+ processedValue = transform(processedValue);
377
+ }
378
+ }
379
+ const typeValidator = getTypeValidator(fieldDef.type);
380
+ if (typeValidator) {
381
+ const typeError = typeValidator(processedValue, void 0, context);
382
+ if (typeError) {
383
+ errors.push({
384
+ ...typeError,
385
+ path: context.path.split(".").filter((p) => p !== "")
386
+ });
387
+ return errors;
388
+ }
389
+ }
390
+ if (fieldDef.required) {
391
+ const isMissing = processedValue === void 0 && !fieldDef.nullish || processedValue === null && !fieldDef.nullable && !fieldDef.nullish || processedValue === "" && !fieldDef.nullable && !fieldDef.nullish || Array.isArray(processedValue) && processedValue.length === 0;
392
+ if (isMissing) {
393
+ const requiredValidator = getValidator("required");
394
+ if (requiredValidator) {
395
+ const error = requiredValidator(processedValue, void 0, context);
396
+ if (error) {
397
+ errors.push({
398
+ ...error,
399
+ path: context.path.split(".").filter((p) => p !== "")
400
+ });
401
+ return errors;
402
+ }
403
+ }
404
+ }
405
+ } else if (value === void 0 || value === null || value === "") {
406
+ return errors;
407
+ }
408
+ for (const rule of fieldDef.rules) {
409
+ const validator = getValidator(rule.type);
410
+ if (!validator) {
411
+ console.warn(`Unknown validator: ${rule.type}`);
412
+ continue;
413
+ }
414
+ const params = {
415
+ ...rule.params,
416
+ soft: rule.soft,
417
+ message: rule.message
418
+ };
419
+ const error = validator(processedValue, params, context);
420
+ if (error) {
421
+ errors.push({
422
+ ...error,
423
+ path: context.path.split(".").filter((p) => p !== "")
424
+ });
425
+ }
426
+ }
427
+ if (fieldDef.type === "object" && fieldDef.schema && processedValue !== null && processedValue !== void 0) {
428
+ const nestedResult = validateSchema(fieldDef.schema, processedValue, context.path, context.root);
429
+ errors.push(...nestedResult.hardErrors, ...nestedResult.softErrors);
430
+ }
431
+ if (fieldDef.type === "array" && fieldDef.items && Array.isArray(processedValue)) {
432
+ for (let i = 0; i < processedValue.length; i++) {
433
+ const itemContext = {
434
+ path: `${context.path}[${i}]`,
435
+ root: context.root,
436
+ parent: processedValue
437
+ };
438
+ const itemErrors = validateField(fieldDef.items, processedValue[i], itemContext);
439
+ errors.push(...itemErrors);
440
+ }
441
+ }
442
+ return errors;
443
+ }
444
+ function validateSchema(schema, data, basePath = "", root, options) {
445
+ const hardErrors = [];
446
+ const softErrors = [];
447
+ const rootData = root ?? data;
448
+ for (const [fieldName, fieldDef] of Object.entries(schema.fields)) {
449
+ const value = data[fieldName];
450
+ const path = basePath ? `${basePath}.${fieldName}` : fieldName;
451
+ const context = {
452
+ path,
453
+ root: rootData,
454
+ parent: data
455
+ };
456
+ const errors = validateField(fieldDef, value, context);
457
+ for (let error of errors) {
458
+ if (options?.errorMap) {
459
+ const mapped = options.errorMap(error);
460
+ if ("message" in mapped && !("field" in mapped)) {
461
+ error = { ...error, message: mapped.message };
462
+ } else {
463
+ error = mapped;
464
+ }
465
+ }
466
+ if (error.severity === "soft") {
467
+ softErrors.push(error);
468
+ } else {
469
+ hardErrors.push(error);
470
+ if (options?.abortEarly) {
471
+ return buildValidationResult(hardErrors, softErrors, options);
472
+ }
473
+ }
474
+ }
475
+ }
476
+ return buildValidationResult(hardErrors, softErrors, options);
477
+ }
478
+ function buildValidationResult(hardErrors, softErrors, options) {
479
+ const result = {
480
+ valid: hardErrors.length === 0,
481
+ hardErrors,
482
+ softErrors
483
+ };
484
+ if (options?.aggregateByField) {
485
+ const errorsByField = {};
486
+ for (const error of [...hardErrors, ...softErrors]) {
487
+ const field = error.field;
488
+ if (!errorsByField[field]) {
489
+ errorsByField[field] = [];
490
+ }
491
+ errorsByField[field].push(error);
492
+ }
493
+ result.errorsByField = errorsByField;
494
+ }
495
+ return result;
496
+ }
497
+ function validate(schema, data, options) {
498
+ return validateSchema(schema, data, "", void 0, options);
499
+ }
500
+ function isValid(schema, data, options) {
501
+ return validate(schema, data, options).valid;
502
+ }
503
+ function assertValid(schema, data, options) {
504
+ const result = validate(schema, data, options);
505
+ if (!result.valid) {
506
+ const error = new Error("Validation failed");
507
+ error.errors = result.hardErrors;
508
+ throw error;
509
+ }
510
+ return data;
511
+ }
512
+ function mergeResults(...results) {
513
+ const hardErrors = [];
514
+ const softErrors = [];
515
+ for (const result of results) {
516
+ hardErrors.push(...result.hardErrors);
517
+ softErrors.push(...result.softErrors);
518
+ }
519
+ return {
520
+ valid: hardErrors.length === 0,
521
+ hardErrors,
522
+ softErrors
523
+ };
524
+ }
525
+ function validResult() {
526
+ return {
527
+ valid: true,
528
+ hardErrors: [],
529
+ softErrors: []
530
+ };
531
+ }
532
+ function errorResult(field, code, message, soft = false) {
533
+ const error = {
534
+ field,
535
+ code,
536
+ message,
537
+ severity: soft ? "soft" : "hard"
538
+ };
539
+ return {
540
+ valid: soft,
541
+ hardErrors: soft ? [] : [error],
542
+ softErrors: soft ? [error] : []
543
+ };
544
+ }
545
+
546
+ // src/core/async-engine.ts
547
+ var debounceCache = /* @__PURE__ */ new Map();
548
+ function createDebouncedValidation(key, validate2, value, context, message, soft = false, timeout = 5e3, debounceMs = 300) {
549
+ const cache = debounceCache.get(key) || {};
550
+ if (cache.timeoutId !== void 0) {
551
+ clearTimeout(cache.timeoutId);
552
+ }
553
+ if (cache.reject) {
554
+ cache.reject(new Error("Validation cancelled - new input received"));
555
+ }
556
+ return new Promise((resolve, reject) => {
557
+ cache.resolve = resolve;
558
+ cache.reject = reject;
559
+ cache.timeoutId = setTimeout(async () => {
560
+ try {
561
+ const result = await executeAsyncValidator(validate2, value, context, message, soft, timeout);
562
+ resolve(result);
563
+ debounceCache.delete(key);
564
+ } catch (error) {
565
+ reject(error);
566
+ debounceCache.delete(key);
567
+ }
568
+ }, debounceMs);
569
+ debounceCache.set(key, cache);
570
+ });
571
+ }
572
+ function withTimeout(promise, timeoutMs, errorMessage) {
573
+ return Promise.race([
574
+ promise,
575
+ new Promise(
576
+ (_, reject) => setTimeout(() => reject(new Error(errorMessage)), timeoutMs)
577
+ )
578
+ ]);
579
+ }
580
+ async function executeAsyncValidator(validate2, value, context, message, soft = false, timeout = 5e3) {
581
+ try {
582
+ const result = await withTimeout(
583
+ validate2(value),
584
+ timeout,
585
+ `Async validation timed out after ${timeout}ms`
586
+ );
587
+ if (typeof result === "boolean") {
588
+ if (result) {
589
+ return null;
590
+ }
591
+ return {
592
+ field: context.path,
593
+ path: context.path.split("."),
594
+ code: "ASYNC_VALIDATION_FAILED",
595
+ message: message || "Async validation failed",
596
+ severity: soft ? "soft" : "hard",
597
+ received: value
598
+ };
599
+ }
600
+ if (!result.valid) {
601
+ return {
602
+ field: context.path,
603
+ path: context.path.split("."),
604
+ code: "ASYNC_VALIDATION_FAILED",
605
+ message: result.message || message || "Async validation failed",
606
+ severity: soft ? "soft" : "hard",
607
+ received: value
608
+ };
609
+ }
610
+ return null;
611
+ } catch (error) {
612
+ return {
613
+ field: context.path,
614
+ path: context.path.split("."),
615
+ code: "ASYNC_VALIDATION_ERROR",
616
+ message: error instanceof Error ? error.message : "Async validation error",
617
+ severity: soft ? "soft" : "hard",
618
+ received: value
619
+ };
620
+ }
621
+ }
622
+ async function validateFieldAsync(fieldDef, value, context) {
623
+ const errors = [];
624
+ const asyncValidations = [];
625
+ const typeValidator = getTypeValidator(fieldDef.type);
626
+ if (typeValidator) {
627
+ const typeError = typeValidator(value, void 0, context);
628
+ if (typeError) {
629
+ errors.push({
630
+ ...typeError,
631
+ path: context.path.split(".")
632
+ });
633
+ return errors;
634
+ }
635
+ }
636
+ if (fieldDef.required) {
637
+ const requiredValidator = getValidator("required");
638
+ if (requiredValidator) {
639
+ const error = requiredValidator(value, void 0, context);
640
+ if (error) {
641
+ errors.push({
642
+ ...error,
643
+ path: context.path.split(".")
644
+ });
645
+ return errors;
646
+ }
647
+ }
648
+ } else if (value === void 0 || value === null || value === "") {
649
+ return errors;
650
+ }
651
+ for (const rule of fieldDef.rules) {
652
+ if (rule.async && rule.type === "refineAsync") {
653
+ const validate2 = rule.params?.validate;
654
+ if (!validate2) continue;
655
+ if (rule.debounce && rule.debounce > 0) {
656
+ const key = `${context.path}_${rule.type}`;
657
+ asyncValidations.push(
658
+ createDebouncedValidation(
659
+ key,
660
+ validate2,
661
+ value,
662
+ context,
663
+ rule.message,
664
+ rule.soft,
665
+ rule.timeout || 5e3,
666
+ rule.debounce
667
+ )
668
+ );
669
+ } else {
670
+ asyncValidations.push(
671
+ executeAsyncValidator(
672
+ validate2,
673
+ value,
674
+ context,
675
+ rule.message,
676
+ rule.soft,
677
+ rule.timeout || 5e3
678
+ )
679
+ );
680
+ }
681
+ } else {
682
+ const validator = getValidator(rule.type);
683
+ if (!validator) {
684
+ console.warn(`Unknown validator: ${rule.type}`);
685
+ continue;
686
+ }
687
+ const params = {
688
+ ...rule.params,
689
+ soft: rule.soft,
690
+ message: rule.message
691
+ };
692
+ const error = validator(value, params, context);
693
+ if (error) {
694
+ errors.push({
695
+ ...error,
696
+ path: context.path.split(".")
697
+ });
698
+ }
699
+ }
700
+ }
701
+ const asyncErrors = await Promise.all(asyncValidations);
702
+ for (const error of asyncErrors) {
703
+ if (error) {
704
+ errors.push(error);
705
+ }
706
+ }
707
+ if (fieldDef.type === "object" && fieldDef.schema && value !== null && value !== void 0) {
708
+ const nestedResult = await validateSchemaAsync(
709
+ fieldDef.schema,
710
+ value,
711
+ context.path,
712
+ context.root
713
+ );
714
+ errors.push(...nestedResult.hardErrors, ...nestedResult.softErrors);
715
+ }
716
+ if (fieldDef.type === "array" && fieldDef.items && Array.isArray(value)) {
717
+ const itemValidations = value.map(async (item, i) => {
718
+ const itemContext = {
719
+ path: `${context.path}[${i}]`,
720
+ root: context.root,
721
+ parent: value
722
+ };
723
+ return validateFieldAsync(fieldDef.items, item, itemContext);
724
+ });
725
+ const itemResults = await Promise.all(itemValidations);
726
+ for (const itemErrors of itemResults) {
727
+ errors.push(...itemErrors);
728
+ }
729
+ }
730
+ return errors;
731
+ }
732
+ async function validateSchemaAsync(schema, data, basePath = "", root) {
733
+ const hardErrors = [];
734
+ const softErrors = [];
735
+ const rootData = root ?? data;
736
+ const fieldValidations = Object.entries(schema.fields).map(async ([fieldName, fieldDef]) => {
737
+ const value = data[fieldName];
738
+ const path = basePath ? `${basePath}.${fieldName}` : fieldName;
739
+ const context = {
740
+ path,
741
+ root: rootData,
742
+ parent: data
743
+ };
744
+ return validateFieldAsync(fieldDef, value, context);
745
+ });
746
+ const results = await Promise.all(fieldValidations);
747
+ for (const errors of results) {
748
+ for (const error of errors) {
749
+ if (error.severity === "soft") {
750
+ softErrors.push(error);
751
+ } else {
752
+ hardErrors.push(error);
753
+ }
754
+ }
755
+ }
756
+ return {
757
+ valid: hardErrors.length === 0,
758
+ hardErrors,
759
+ softErrors
760
+ };
761
+ }
762
+ async function validateAsync(schema, data) {
763
+ return validateSchemaAsync(schema, data);
764
+ }
765
+ async function isValidAsync(schema, data) {
766
+ const result = await validateAsync(schema, data);
767
+ return result.valid;
768
+ }
769
+ async function assertValidAsync(schema, data) {
770
+ const result = await validateAsync(schema, data);
771
+ if (!result.valid) {
772
+ const error = new Error("Async validation failed");
773
+ error.errors = result.hardErrors;
774
+ throw error;
775
+ }
776
+ return data;
777
+ }
778
+
779
+ export { assertValid, assertValidAsync, errorResult, getTypeValidator, getValidator, isValid, isValidAsync, mergeResults, registerValidator, ruleValidators, typeValidators, validResult, validate, validateAsync, validateSchema, validateSchemaAsync };