@page-speed/forms 0.5.2 → 0.5.4

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 (55) hide show
  1. package/dist/chunk-232KNGJI.js +207 -0
  2. package/dist/chunk-232KNGJI.js.map +1 -0
  3. package/dist/chunk-24RPM43T.js +373 -0
  4. package/dist/chunk-24RPM43T.js.map +1 -0
  5. package/dist/chunk-27JUYRDE.cjs +173 -0
  6. package/dist/chunk-27JUYRDE.cjs.map +1 -0
  7. package/dist/chunk-5NT5T5XY.js +4136 -0
  8. package/dist/chunk-5NT5T5XY.js.map +1 -0
  9. package/dist/chunk-AVAKC6R7.cjs +236 -0
  10. package/dist/chunk-AVAKC6R7.cjs.map +1 -0
  11. package/dist/chunk-DKLLPKZN.cjs +238 -0
  12. package/dist/chunk-DKLLPKZN.cjs.map +1 -0
  13. package/dist/chunk-EX6CRLKG.cjs +397 -0
  14. package/dist/chunk-EX6CRLKG.cjs.map +1 -0
  15. package/dist/chunk-H6NNFV64.js +127 -0
  16. package/dist/chunk-H6NNFV64.js.map +1 -0
  17. package/dist/chunk-JBEWTBFH.js +217 -0
  18. package/dist/chunk-JBEWTBFH.js.map +1 -0
  19. package/dist/chunk-JBEZLX3H.cjs +138 -0
  20. package/dist/chunk-JBEZLX3H.cjs.map +1 -0
  21. package/dist/chunk-VLGZG2VP.js +150 -0
  22. package/dist/chunk-VLGZG2VP.js.map +1 -0
  23. package/dist/chunk-ZYFTT6DB.cjs +4169 -0
  24. package/dist/chunk-ZYFTT6DB.cjs.map +1 -0
  25. package/dist/core.cjs +23 -733
  26. package/dist/core.cjs.map +1 -1
  27. package/dist/core.js +3 -716
  28. package/dist/core.js.map +1 -1
  29. package/dist/index.cjs +43 -738
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.js +3 -716
  32. package/dist/index.js.map +1 -1
  33. package/dist/inputs.cjs +44 -4359
  34. package/dist/inputs.cjs.map +1 -1
  35. package/dist/inputs.js +2 -4337
  36. package/dist/inputs.js.map +1 -1
  37. package/dist/integration.cjs +65 -4658
  38. package/dist/integration.cjs.map +1 -1
  39. package/dist/integration.d.cts +7 -1
  40. package/dist/integration.d.ts +7 -1
  41. package/dist/integration.js +42 -4635
  42. package/dist/integration.js.map +1 -1
  43. package/dist/validation-rules.cjs +75 -231
  44. package/dist/validation-rules.cjs.map +1 -1
  45. package/dist/validation-rules.js +1 -215
  46. package/dist/validation-rules.js.map +1 -1
  47. package/dist/validation-utils.cjs +43 -133
  48. package/dist/validation-utils.cjs.map +1 -1
  49. package/dist/validation-utils.js +1 -125
  50. package/dist/validation-utils.js.map +1 -1
  51. package/dist/validation.cjs +115 -364
  52. package/dist/validation.cjs.map +1 -1
  53. package/dist/validation.js +2 -339
  54. package/dist/validation.js.map +1 -1
  55. package/package.json +1 -1
@@ -1,138 +1,48 @@
1
1
  'use strict';
2
2
 
3
- // src/validation/utils.ts
4
- function debounce(validator, options = {}) {
5
- const { delay = 300, leading = false, trailing = true } = options;
6
- let timeoutId = null;
7
- let lastCallTime = 0;
8
- let lastResult;
9
- return async (value, allValues) => {
10
- const now = Date.now();
11
- const timeSinceLastCall = now - lastCallTime;
12
- if (timeoutId) {
13
- clearTimeout(timeoutId);
14
- timeoutId = null;
15
- }
16
- if (leading && timeSinceLastCall >= delay) {
17
- lastCallTime = now;
18
- lastResult = await validator(value, allValues);
19
- return lastResult;
20
- }
21
- if (trailing) {
22
- return new Promise((resolve) => {
23
- timeoutId = setTimeout(async () => {
24
- lastCallTime = Date.now();
25
- lastResult = await validator(value, allValues);
26
- resolve(lastResult);
27
- }, delay);
28
- });
29
- }
30
- return lastResult;
31
- };
32
- }
33
- var defaultMessages = {
34
- required: "This field is required",
35
- email: "Please enter a valid email address",
36
- url: "Please enter a valid URL",
37
- phone: "Please enter a valid phone number",
38
- minLength: ({ min }) => `Must be at least ${min} characters`,
39
- maxLength: ({ max }) => `Must be no more than ${max} characters`,
40
- min: ({ min }) => `Must be at least ${min}`,
41
- max: ({ max }) => `Must be no more than ${max}`,
42
- pattern: "Invalid format",
43
- matches: ({ field }) => `Must match ${field}`,
44
- oneOf: "Invalid value",
45
- creditCard: "Please enter a valid credit card number",
46
- postalCode: "Please enter a valid ZIP code",
47
- alpha: "Must contain only letters",
48
- alphanumeric: "Must contain only letters and numbers",
49
- numeric: "Must be a valid number",
50
- integer: "Must be a whole number"
51
- };
52
- var MessageRegistry = class {
53
- constructor() {
54
- this.messages = { ...defaultMessages };
55
- }
56
- /**
57
- * Set custom messages (for i18n or customization)
58
- */
59
- setMessages(messages) {
60
- this.messages = { ...this.messages, ...messages };
61
- }
62
- /**
63
- * Get message by key
64
- */
65
- getMessage(key, params) {
66
- const message = this.messages[key];
67
- if (!message) {
68
- return `Validation error: ${key}`;
69
- }
70
- if (typeof message === "function") {
71
- return message(params || {});
72
- }
73
- return message;
74
- }
75
- /**
76
- * Reset to default messages
77
- */
78
- reset() {
79
- this.messages = { ...defaultMessages };
80
- }
81
- };
82
- var messageRegistry = new MessageRegistry();
83
- function setErrorMessages(messages) {
84
- messageRegistry.setMessages(messages);
85
- }
86
- function getErrorMessage(key, params) {
87
- return messageRegistry.getMessage(key, params);
88
- }
89
- function resetErrorMessages() {
90
- messageRegistry.reset();
91
- }
92
- function crossFieldValidator(fields, validate) {
93
- return (_value, allValues) => {
94
- const fieldValues = fields.reduce(
95
- (acc, field) => {
96
- acc[field] = allValues[field];
97
- return acc;
98
- },
99
- {}
100
- );
101
- return validate(fieldValues);
102
- };
103
- }
104
- function when(condition, validator) {
105
- return (value, allValues) => {
106
- if (condition(allValues)) {
107
- return validator(value, allValues);
108
- }
109
- return void 0;
110
- };
111
- }
112
- function withRaceConditionPrevention(validator) {
113
- let latestCallId = 0;
114
- return async (value, allValues) => {
115
- const callId = ++latestCallId;
116
- const result = await validator(value, allValues);
117
- if (callId === latestCallId) {
118
- return result;
119
- }
120
- return void 0;
121
- };
122
- }
123
- function asyncValidator(validator, options = {}) {
124
- return debounce(withRaceConditionPrevention(validator), options);
125
- }
3
+ var chunkJBEZLX3H_cjs = require('./chunk-JBEZLX3H.cjs');
126
4
 
127
- exports.asyncValidator = asyncValidator;
128
- exports.crossFieldValidator = crossFieldValidator;
129
- exports.debounce = debounce;
130
- exports.defaultMessages = defaultMessages;
131
- exports.getErrorMessage = getErrorMessage;
132
- exports.messageRegistry = messageRegistry;
133
- exports.resetErrorMessages = resetErrorMessages;
134
- exports.setErrorMessages = setErrorMessages;
135
- exports.when = when;
136
- exports.withRaceConditionPrevention = withRaceConditionPrevention;
5
+
6
+
7
+ Object.defineProperty(exports, "asyncValidator", {
8
+ enumerable: true,
9
+ get: function () { return chunkJBEZLX3H_cjs.asyncValidator; }
10
+ });
11
+ Object.defineProperty(exports, "crossFieldValidator", {
12
+ enumerable: true,
13
+ get: function () { return chunkJBEZLX3H_cjs.crossFieldValidator; }
14
+ });
15
+ Object.defineProperty(exports, "debounce", {
16
+ enumerable: true,
17
+ get: function () { return chunkJBEZLX3H_cjs.debounce; }
18
+ });
19
+ Object.defineProperty(exports, "defaultMessages", {
20
+ enumerable: true,
21
+ get: function () { return chunkJBEZLX3H_cjs.defaultMessages; }
22
+ });
23
+ Object.defineProperty(exports, "getErrorMessage", {
24
+ enumerable: true,
25
+ get: function () { return chunkJBEZLX3H_cjs.getErrorMessage; }
26
+ });
27
+ Object.defineProperty(exports, "messageRegistry", {
28
+ enumerable: true,
29
+ get: function () { return chunkJBEZLX3H_cjs.messageRegistry; }
30
+ });
31
+ Object.defineProperty(exports, "resetErrorMessages", {
32
+ enumerable: true,
33
+ get: function () { return chunkJBEZLX3H_cjs.resetErrorMessages; }
34
+ });
35
+ Object.defineProperty(exports, "setErrorMessages", {
36
+ enumerable: true,
37
+ get: function () { return chunkJBEZLX3H_cjs.setErrorMessages; }
38
+ });
39
+ Object.defineProperty(exports, "when", {
40
+ enumerable: true,
41
+ get: function () { return chunkJBEZLX3H_cjs.when; }
42
+ });
43
+ Object.defineProperty(exports, "withRaceConditionPrevention", {
44
+ enumerable: true,
45
+ get: function () { return chunkJBEZLX3H_cjs.withRaceConditionPrevention; }
46
+ });
137
47
  //# sourceMappingURL=validation-utils.cjs.map
138
48
  //# sourceMappingURL=validation-utils.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/validation/utils.ts"],"names":[],"mappings":"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","file":"validation-utils.cjs","sourcesContent":["/**\n * @page-speed/forms - Validation Utilities\n *\n * Utilities for advanced validation scenarios\n */\n\nimport type { FieldValidator, FormValues } from \"../core/types\";\n\n/**\n * Debounce options for async validators\n */\nexport interface DebounceOptions {\n /**\n * Debounce delay in milliseconds\n * @default 300\n */\n delay?: number;\n\n /**\n * Leading edge - call immediately on first invocation\n * @default false\n */\n leading?: boolean;\n\n /**\n * Trailing edge - call after delay\n * @default true\n */\n trailing?: boolean;\n}\n\n/**\n * Debounce an async validator\n * Prevents rapid validation calls (e.g., for username availability checks)\n *\n * @example\n * ```tsx\n * const checkUsername = debounce(\n * async (value) => {\n * const available = await api.checkUsername(value);\n * return available ? undefined : 'Username is taken';\n * },\n * { delay: 500 }\n * );\n * ```\n */\nexport function debounce<T = any>(\n validator: FieldValidator<T>,\n options: DebounceOptions = {}\n): FieldValidator<T> {\n const { delay = 300, leading = false, trailing = true } = options;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let lastCallTime = 0;\n let lastResult: string | undefined;\n\n return async (value: T, allValues: FormValues) => {\n const now = Date.now();\n const timeSinceLastCall = now - lastCallTime;\n\n // Clear existing timeout\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n // Leading edge call\n if (leading && timeSinceLastCall >= delay) {\n lastCallTime = now;\n lastResult = await validator(value, allValues);\n return lastResult;\n }\n\n // Trailing edge call\n if (trailing) {\n return new Promise((resolve) => {\n timeoutId = setTimeout(async () => {\n lastCallTime = Date.now();\n lastResult = await validator(value, allValues);\n resolve(lastResult);\n }, delay);\n });\n }\n\n return lastResult;\n };\n}\n\n/**\n * Error message template function\n */\nexport type MessageTemplate = (params?: any) => string;\n\n/**\n * Error message templates for internationalization\n */\nexport interface ErrorMessages {\n [key: string]: string | MessageTemplate;\n}\n\n/**\n * Default error messages (English)\n */\nexport const defaultMessages: ErrorMessages = {\n required: \"This field is required\",\n email: \"Please enter a valid email address\",\n url: \"Please enter a valid URL\",\n phone: \"Please enter a valid phone number\",\n minLength: ({ min }: { min: number }) =>\n `Must be at least ${min} characters`,\n maxLength: ({ max }: { max: number }) =>\n `Must be no more than ${max} characters`,\n min: ({ min }: { min: number }) => `Must be at least ${min}`,\n max: ({ max }: { max: number }) => `Must be no more than ${max}`,\n pattern: \"Invalid format\",\n matches: ({ field }: { field: string }) => `Must match ${field}`,\n oneOf: \"Invalid value\",\n creditCard: \"Please enter a valid credit card number\",\n postalCode: \"Please enter a valid ZIP code\",\n alpha: \"Must contain only letters\",\n alphanumeric: \"Must contain only letters and numbers\",\n numeric: \"Must be a valid number\",\n integer: \"Must be a whole number\",\n};\n\n/**\n * Error message registry for i18n support\n */\nclass MessageRegistry {\n private messages: ErrorMessages = { ...defaultMessages };\n\n /**\n * Set custom messages (for i18n or customization)\n */\n setMessages(messages: ErrorMessages): void {\n this.messages = { ...this.messages, ...messages };\n }\n\n /**\n * Get message by key\n */\n getMessage(key: string, params?: Record<string, any>): string {\n const message = this.messages[key];\n\n if (!message) {\n return `Validation error: ${key}`;\n }\n\n if (typeof message === \"function\") {\n return message(params || {});\n }\n\n return message;\n }\n\n /**\n * Reset to default messages\n */\n reset(): void {\n this.messages = { ...defaultMessages };\n }\n}\n\n/**\n * Global message registry instance\n */\nexport const messageRegistry = new MessageRegistry();\n\n/**\n * Set custom error messages globally\n * Useful for internationalization\n *\n * @example\n * ```tsx\n * setErrorMessages({\n * required: 'Este campo es obligatorio',\n * email: 'Por favor ingrese un email válido',\n * });\n * ```\n */\nexport function setErrorMessages(messages: ErrorMessages): void {\n messageRegistry.setMessages(messages);\n}\n\n/**\n * Get error message by key\n */\nexport function getErrorMessage(\n key: string,\n params?: Record<string, any>\n): string {\n return messageRegistry.getMessage(key, params);\n}\n\n/**\n * Reset error messages to defaults\n */\nexport function resetErrorMessages(): void {\n messageRegistry.reset();\n}\n\n/**\n * Cross-field validator helper\n * Creates a validator that depends on multiple fields\n *\n * @example\n * ```tsx\n * const passwordMatch = crossFieldValidator(\n * ['password', 'confirmPassword'],\n * (values) => {\n * if (values.password !== values.confirmPassword) {\n * return 'Passwords must match';\n * }\n * return undefined;\n * }\n * );\n * ```\n */\nexport function crossFieldValidator<T extends FormValues = FormValues>(\n fields: (keyof T)[],\n validate: (values: Pick<T, keyof T>) => string | undefined | Promise<string | undefined>\n): FieldValidator {\n return (_value, allValues) => {\n const fieldValues = fields.reduce(\n (acc, field) => {\n acc[field] = allValues[field as string];\n return acc;\n },\n {} as Record<keyof T, any>\n );\n\n return validate(fieldValues as Pick<T, keyof T>);\n };\n}\n\n/**\n * Conditional validator\n * Only validates when condition is met\n *\n * @example\n * ```tsx\n * const conditionalRequired = when(\n * (values) => values.country === 'US',\n * required()\n * );\n * ```\n */\nexport function when<T = any>(\n condition: (allValues: FormValues) => boolean,\n validator: FieldValidator<T>\n): FieldValidator<T> {\n return (value, allValues) => {\n if (condition(allValues)) {\n return validator(value, allValues);\n }\n return undefined;\n };\n}\n\n/**\n * Async validator with race condition prevention\n * Ensures only the latest validation call resolves\n *\n * @example\n * ```tsx\n * const checkUsername = withRaceConditionPrevention(\n * async (value) => {\n * const available = await api.checkUsername(value);\n * return available ? undefined : 'Username is taken';\n * }\n * );\n * ```\n */\nexport function withRaceConditionPrevention<T = any>(\n validator: FieldValidator<T>\n): FieldValidator<T> {\n let latestCallId = 0;\n\n return async (value: T, allValues: FormValues) => {\n const callId = ++latestCallId;\n\n const result = await validator(value, allValues);\n\n // Only return result if this is still the latest call\n if (callId === latestCallId) {\n return result;\n }\n\n // Ignore stale results\n return undefined;\n };\n}\n\n/**\n * Combine debounce with race condition prevention\n * Best practice for async validators\n *\n * @example\n * ```tsx\n * const checkUsername = asyncValidator(\n * async (value) => {\n * const available = await api.checkUsername(value);\n * return available ? undefined : 'Username is taken';\n * },\n * { delay: 500 }\n * );\n * ```\n */\nexport function asyncValidator<T = any>(\n validator: FieldValidator<T>,\n options: DebounceOptions = {}\n): FieldValidator<T> {\n return debounce(withRaceConditionPrevention(validator), options);\n}\n"]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"validation-utils.cjs"}
@@ -1,127 +1,3 @@
1
- // src/validation/utils.ts
2
- function debounce(validator, options = {}) {
3
- const { delay = 300, leading = false, trailing = true } = options;
4
- let timeoutId = null;
5
- let lastCallTime = 0;
6
- let lastResult;
7
- return async (value, allValues) => {
8
- const now = Date.now();
9
- const timeSinceLastCall = now - lastCallTime;
10
- if (timeoutId) {
11
- clearTimeout(timeoutId);
12
- timeoutId = null;
13
- }
14
- if (leading && timeSinceLastCall >= delay) {
15
- lastCallTime = now;
16
- lastResult = await validator(value, allValues);
17
- return lastResult;
18
- }
19
- if (trailing) {
20
- return new Promise((resolve) => {
21
- timeoutId = setTimeout(async () => {
22
- lastCallTime = Date.now();
23
- lastResult = await validator(value, allValues);
24
- resolve(lastResult);
25
- }, delay);
26
- });
27
- }
28
- return lastResult;
29
- };
30
- }
31
- var defaultMessages = {
32
- required: "This field is required",
33
- email: "Please enter a valid email address",
34
- url: "Please enter a valid URL",
35
- phone: "Please enter a valid phone number",
36
- minLength: ({ min }) => `Must be at least ${min} characters`,
37
- maxLength: ({ max }) => `Must be no more than ${max} characters`,
38
- min: ({ min }) => `Must be at least ${min}`,
39
- max: ({ max }) => `Must be no more than ${max}`,
40
- pattern: "Invalid format",
41
- matches: ({ field }) => `Must match ${field}`,
42
- oneOf: "Invalid value",
43
- creditCard: "Please enter a valid credit card number",
44
- postalCode: "Please enter a valid ZIP code",
45
- alpha: "Must contain only letters",
46
- alphanumeric: "Must contain only letters and numbers",
47
- numeric: "Must be a valid number",
48
- integer: "Must be a whole number"
49
- };
50
- var MessageRegistry = class {
51
- constructor() {
52
- this.messages = { ...defaultMessages };
53
- }
54
- /**
55
- * Set custom messages (for i18n or customization)
56
- */
57
- setMessages(messages) {
58
- this.messages = { ...this.messages, ...messages };
59
- }
60
- /**
61
- * Get message by key
62
- */
63
- getMessage(key, params) {
64
- const message = this.messages[key];
65
- if (!message) {
66
- return `Validation error: ${key}`;
67
- }
68
- if (typeof message === "function") {
69
- return message(params || {});
70
- }
71
- return message;
72
- }
73
- /**
74
- * Reset to default messages
75
- */
76
- reset() {
77
- this.messages = { ...defaultMessages };
78
- }
79
- };
80
- var messageRegistry = new MessageRegistry();
81
- function setErrorMessages(messages) {
82
- messageRegistry.setMessages(messages);
83
- }
84
- function getErrorMessage(key, params) {
85
- return messageRegistry.getMessage(key, params);
86
- }
87
- function resetErrorMessages() {
88
- messageRegistry.reset();
89
- }
90
- function crossFieldValidator(fields, validate) {
91
- return (_value, allValues) => {
92
- const fieldValues = fields.reduce(
93
- (acc, field) => {
94
- acc[field] = allValues[field];
95
- return acc;
96
- },
97
- {}
98
- );
99
- return validate(fieldValues);
100
- };
101
- }
102
- function when(condition, validator) {
103
- return (value, allValues) => {
104
- if (condition(allValues)) {
105
- return validator(value, allValues);
106
- }
107
- return void 0;
108
- };
109
- }
110
- function withRaceConditionPrevention(validator) {
111
- let latestCallId = 0;
112
- return async (value, allValues) => {
113
- const callId = ++latestCallId;
114
- const result = await validator(value, allValues);
115
- if (callId === latestCallId) {
116
- return result;
117
- }
118
- return void 0;
119
- };
120
- }
121
- function asyncValidator(validator, options = {}) {
122
- return debounce(withRaceConditionPrevention(validator), options);
123
- }
124
-
125
- export { asyncValidator, crossFieldValidator, debounce, defaultMessages, getErrorMessage, messageRegistry, resetErrorMessages, setErrorMessages, when, withRaceConditionPrevention };
1
+ export { asyncValidator, crossFieldValidator, debounce, defaultMessages, getErrorMessage, messageRegistry, resetErrorMessages, setErrorMessages, when, withRaceConditionPrevention } from './chunk-H6NNFV64.js';
126
2
  //# sourceMappingURL=validation-utils.js.map
127
3
  //# sourceMappingURL=validation-utils.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/validation/utils.ts"],"names":[],"mappings":"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","file":"validation-utils.js","sourcesContent":["/**\n * @page-speed/forms - Validation Utilities\n *\n * Utilities for advanced validation scenarios\n */\n\nimport type { FieldValidator, FormValues } from \"../core/types\";\n\n/**\n * Debounce options for async validators\n */\nexport interface DebounceOptions {\n /**\n * Debounce delay in milliseconds\n * @default 300\n */\n delay?: number;\n\n /**\n * Leading edge - call immediately on first invocation\n * @default false\n */\n leading?: boolean;\n\n /**\n * Trailing edge - call after delay\n * @default true\n */\n trailing?: boolean;\n}\n\n/**\n * Debounce an async validator\n * Prevents rapid validation calls (e.g., for username availability checks)\n *\n * @example\n * ```tsx\n * const checkUsername = debounce(\n * async (value) => {\n * const available = await api.checkUsername(value);\n * return available ? undefined : 'Username is taken';\n * },\n * { delay: 500 }\n * );\n * ```\n */\nexport function debounce<T = any>(\n validator: FieldValidator<T>,\n options: DebounceOptions = {}\n): FieldValidator<T> {\n const { delay = 300, leading = false, trailing = true } = options;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let lastCallTime = 0;\n let lastResult: string | undefined;\n\n return async (value: T, allValues: FormValues) => {\n const now = Date.now();\n const timeSinceLastCall = now - lastCallTime;\n\n // Clear existing timeout\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n // Leading edge call\n if (leading && timeSinceLastCall >= delay) {\n lastCallTime = now;\n lastResult = await validator(value, allValues);\n return lastResult;\n }\n\n // Trailing edge call\n if (trailing) {\n return new Promise((resolve) => {\n timeoutId = setTimeout(async () => {\n lastCallTime = Date.now();\n lastResult = await validator(value, allValues);\n resolve(lastResult);\n }, delay);\n });\n }\n\n return lastResult;\n };\n}\n\n/**\n * Error message template function\n */\nexport type MessageTemplate = (params?: any) => string;\n\n/**\n * Error message templates for internationalization\n */\nexport interface ErrorMessages {\n [key: string]: string | MessageTemplate;\n}\n\n/**\n * Default error messages (English)\n */\nexport const defaultMessages: ErrorMessages = {\n required: \"This field is required\",\n email: \"Please enter a valid email address\",\n url: \"Please enter a valid URL\",\n phone: \"Please enter a valid phone number\",\n minLength: ({ min }: { min: number }) =>\n `Must be at least ${min} characters`,\n maxLength: ({ max }: { max: number }) =>\n `Must be no more than ${max} characters`,\n min: ({ min }: { min: number }) => `Must be at least ${min}`,\n max: ({ max }: { max: number }) => `Must be no more than ${max}`,\n pattern: \"Invalid format\",\n matches: ({ field }: { field: string }) => `Must match ${field}`,\n oneOf: \"Invalid value\",\n creditCard: \"Please enter a valid credit card number\",\n postalCode: \"Please enter a valid ZIP code\",\n alpha: \"Must contain only letters\",\n alphanumeric: \"Must contain only letters and numbers\",\n numeric: \"Must be a valid number\",\n integer: \"Must be a whole number\",\n};\n\n/**\n * Error message registry for i18n support\n */\nclass MessageRegistry {\n private messages: ErrorMessages = { ...defaultMessages };\n\n /**\n * Set custom messages (for i18n or customization)\n */\n setMessages(messages: ErrorMessages): void {\n this.messages = { ...this.messages, ...messages };\n }\n\n /**\n * Get message by key\n */\n getMessage(key: string, params?: Record<string, any>): string {\n const message = this.messages[key];\n\n if (!message) {\n return `Validation error: ${key}`;\n }\n\n if (typeof message === \"function\") {\n return message(params || {});\n }\n\n return message;\n }\n\n /**\n * Reset to default messages\n */\n reset(): void {\n this.messages = { ...defaultMessages };\n }\n}\n\n/**\n * Global message registry instance\n */\nexport const messageRegistry = new MessageRegistry();\n\n/**\n * Set custom error messages globally\n * Useful for internationalization\n *\n * @example\n * ```tsx\n * setErrorMessages({\n * required: 'Este campo es obligatorio',\n * email: 'Por favor ingrese un email válido',\n * });\n * ```\n */\nexport function setErrorMessages(messages: ErrorMessages): void {\n messageRegistry.setMessages(messages);\n}\n\n/**\n * Get error message by key\n */\nexport function getErrorMessage(\n key: string,\n params?: Record<string, any>\n): string {\n return messageRegistry.getMessage(key, params);\n}\n\n/**\n * Reset error messages to defaults\n */\nexport function resetErrorMessages(): void {\n messageRegistry.reset();\n}\n\n/**\n * Cross-field validator helper\n * Creates a validator that depends on multiple fields\n *\n * @example\n * ```tsx\n * const passwordMatch = crossFieldValidator(\n * ['password', 'confirmPassword'],\n * (values) => {\n * if (values.password !== values.confirmPassword) {\n * return 'Passwords must match';\n * }\n * return undefined;\n * }\n * );\n * ```\n */\nexport function crossFieldValidator<T extends FormValues = FormValues>(\n fields: (keyof T)[],\n validate: (values: Pick<T, keyof T>) => string | undefined | Promise<string | undefined>\n): FieldValidator {\n return (_value, allValues) => {\n const fieldValues = fields.reduce(\n (acc, field) => {\n acc[field] = allValues[field as string];\n return acc;\n },\n {} as Record<keyof T, any>\n );\n\n return validate(fieldValues as Pick<T, keyof T>);\n };\n}\n\n/**\n * Conditional validator\n * Only validates when condition is met\n *\n * @example\n * ```tsx\n * const conditionalRequired = when(\n * (values) => values.country === 'US',\n * required()\n * );\n * ```\n */\nexport function when<T = any>(\n condition: (allValues: FormValues) => boolean,\n validator: FieldValidator<T>\n): FieldValidator<T> {\n return (value, allValues) => {\n if (condition(allValues)) {\n return validator(value, allValues);\n }\n return undefined;\n };\n}\n\n/**\n * Async validator with race condition prevention\n * Ensures only the latest validation call resolves\n *\n * @example\n * ```tsx\n * const checkUsername = withRaceConditionPrevention(\n * async (value) => {\n * const available = await api.checkUsername(value);\n * return available ? undefined : 'Username is taken';\n * }\n * );\n * ```\n */\nexport function withRaceConditionPrevention<T = any>(\n validator: FieldValidator<T>\n): FieldValidator<T> {\n let latestCallId = 0;\n\n return async (value: T, allValues: FormValues) => {\n const callId = ++latestCallId;\n\n const result = await validator(value, allValues);\n\n // Only return result if this is still the latest call\n if (callId === latestCallId) {\n return result;\n }\n\n // Ignore stale results\n return undefined;\n };\n}\n\n/**\n * Combine debounce with race condition prevention\n * Best practice for async validators\n *\n * @example\n * ```tsx\n * const checkUsername = asyncValidator(\n * async (value) => {\n * const available = await api.checkUsername(value);\n * return available ? undefined : 'Username is taken';\n * },\n * { delay: 500 }\n * );\n * ```\n */\nexport function asyncValidator<T = any>(\n validator: FieldValidator<T>,\n options: DebounceOptions = {}\n): FieldValidator<T> {\n return debounce(withRaceConditionPrevention(validator), options);\n}\n"]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"validation-utils.js"}