@buildnbuzz/buzzform 0.1.0 → 0.1.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/LICENSE +21 -21
- package/README.md +146 -138
- package/dist/{adapter-BT9v2OVg.d.mts → adapter-nQW28cyO.d.mts} +8 -2
- package/dist/{adapter-BT9v2OVg.d.ts → adapter-nQW28cyO.d.ts} +8 -2
- package/dist/chunk-63LF7K4O.mjs +171 -0
- package/dist/chunk-63LF7K4O.mjs.map +1 -0
- package/dist/chunk-HWDQN57Q.mjs +423 -0
- package/dist/chunk-HWDQN57Q.mjs.map +1 -0
- package/dist/{chunk-DDDGBPVU.mjs → chunk-IMJ5FRK5.mjs} +1 -1
- package/dist/chunk-IMJ5FRK5.mjs.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +95 -176
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +12 -12
- package/dist/index.mjs.map +1 -1
- package/dist/rhf.d.mts +1 -1
- package/dist/rhf.d.ts +1 -1
- package/dist/rhf.js.map +1 -1
- package/dist/rhf.mjs +1 -1
- package/dist/rhf.mjs.map +1 -1
- package/dist/schema.d.mts +5 -32
- package/dist/schema.d.ts +5 -32
- package/dist/schema.js +160 -171
- package/dist/schema.js.map +1 -1
- package/dist/schema.mjs +18 -12
- package/dist/utils-BRY27BLX.d.mts +223 -0
- package/dist/utils-DgwUn6tN.d.ts +223 -0
- package/dist/zod.d.mts +6 -24
- package/dist/zod.d.ts +6 -24
- package/dist/zod.js +151 -17
- package/dist/zod.js.map +1 -1
- package/dist/zod.mjs +69 -16
- package/dist/zod.mjs.map +1 -1
- package/package.json +4 -3
- package/dist/chunk-DDDGBPVU.mjs.map +0 -1
- package/dist/chunk-K42S5YX3.mjs +0 -599
- package/dist/chunk-K42S5YX3.mjs.map +0 -1
- package/dist/utils-BgwyUFGB.d.mts +0 -233
- package/dist/utils-DVLpbOoW.d.ts +0 -233
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { F as Field, T as TextField, E as EmailField, P as PasswordField, o as TextareaField, N as NumberField, D as DateField, p as DatetimeField, r as SelectField, s as CheckboxField, t as SwitchField, u as RadioField, w as TagsField, x as UploadField, G as GroupField, y as ArrayField, z as RowField, I as TabsField, J as CollapsibleField, V as ValidationContext, K as FieldType, i as ValidationFn, A as ArrayHelpers } from './adapter-nQW28cyO.js';
|
|
3
|
+
|
|
4
|
+
type ExtractL1<F> = F extends {
|
|
5
|
+
type: "row" | "collapsible";
|
|
6
|
+
fields: infer Nested extends readonly Field[];
|
|
7
|
+
} ? Nested[number] : F extends {
|
|
8
|
+
type: "tabs";
|
|
9
|
+
tabs: readonly (infer T)[];
|
|
10
|
+
} ? T extends {
|
|
11
|
+
fields: infer TabFields extends readonly Field[];
|
|
12
|
+
} ? TabFields[number] : never : F;
|
|
13
|
+
type ExtractL2<F> = ExtractL1<ExtractL1<F>>;
|
|
14
|
+
type ExtractL3<F> = ExtractL1<ExtractL2<F>>;
|
|
15
|
+
type ExtractL4<F> = ExtractL1<ExtractL3<F>>;
|
|
16
|
+
type FlattenedFields<F> = ExtractL4<F>;
|
|
17
|
+
type MakeOptional<T extends z.ZodTypeAny, F extends Field> = F extends {
|
|
18
|
+
required: true;
|
|
19
|
+
} ? T : z.ZodOptional<T>;
|
|
20
|
+
type InnerFieldsShape<T extends readonly Field[]> = {
|
|
21
|
+
[K in FlattenedFields<T[number]> as K extends {
|
|
22
|
+
name: infer N extends string;
|
|
23
|
+
} ? N : never]: K extends Field ? InnerFieldToZod<K> : never;
|
|
24
|
+
};
|
|
25
|
+
type InnerFieldToZod<F extends Field> = MakeOptional<InnerBaseFieldType<F>, F>;
|
|
26
|
+
type InnerBaseFieldType<F extends Field> = F extends {
|
|
27
|
+
schema: infer S extends z.ZodTypeAny;
|
|
28
|
+
} ? S : F extends {
|
|
29
|
+
type: "text";
|
|
30
|
+
} ? z.ZodString : F extends {
|
|
31
|
+
type: "email";
|
|
32
|
+
} ? z.ZodString : F extends {
|
|
33
|
+
type: "password";
|
|
34
|
+
} ? z.ZodString : F extends {
|
|
35
|
+
type: "textarea";
|
|
36
|
+
} ? z.ZodString : F extends {
|
|
37
|
+
type: "number";
|
|
38
|
+
} ? z.ZodNumber : F extends {
|
|
39
|
+
type: "date";
|
|
40
|
+
} ? z.ZodDate : F extends {
|
|
41
|
+
type: "datetime";
|
|
42
|
+
} ? z.ZodDate : F extends {
|
|
43
|
+
type: "checkbox";
|
|
44
|
+
} ? z.ZodBoolean : F extends {
|
|
45
|
+
type: "switch";
|
|
46
|
+
} ? z.ZodBoolean : F extends {
|
|
47
|
+
type: "select";
|
|
48
|
+
hasMany: true;
|
|
49
|
+
} ? z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>> : F extends {
|
|
50
|
+
type: "select";
|
|
51
|
+
} ? z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]> : F extends {
|
|
52
|
+
type: "radio";
|
|
53
|
+
} ? z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]> : F extends {
|
|
54
|
+
type: "tags";
|
|
55
|
+
} ? z.ZodArray<z.ZodString> : F extends {
|
|
56
|
+
type: "upload";
|
|
57
|
+
hasMany: true;
|
|
58
|
+
} ? z.ZodArray<z.ZodUnion<[z.ZodType<File>, z.ZodString]>> : F extends {
|
|
59
|
+
type: "upload";
|
|
60
|
+
} ? z.ZodUnion<[z.ZodType<File>, z.ZodString]> : F extends {
|
|
61
|
+
type: "group";
|
|
62
|
+
fields: infer Nested extends readonly Field[];
|
|
63
|
+
} ? z.ZodObject<InnerFieldsShape<Nested>> : F extends {
|
|
64
|
+
type: "array";
|
|
65
|
+
fields: infer Nested extends readonly Field[];
|
|
66
|
+
} ? z.ZodArray<z.ZodObject<InnerFieldsShape<Nested>>> : z.ZodTypeAny;
|
|
67
|
+
type FieldToZod<F extends Field> = MakeOptional<InnerBaseFieldType<F>, F>;
|
|
68
|
+
type FieldsToShape<T extends readonly Field[]> = InnerFieldsShape<T>;
|
|
69
|
+
type SchemaBuilder<TField extends Field = Field> = (field: TField) => z.ZodTypeAny;
|
|
70
|
+
type SchemaBuilderMap = {
|
|
71
|
+
[K in Field["type"]]?: SchemaBuilder<Extract<Field, {
|
|
72
|
+
type: K;
|
|
73
|
+
}>>;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Infer the TypeScript type from a BuzzForm schema.
|
|
77
|
+
*/
|
|
78
|
+
type InferType<T extends z.ZodTypeAny> = z.infer<T>;
|
|
79
|
+
/**
|
|
80
|
+
* @deprecated Use `InferType` instead. This will be removed in a future version.
|
|
81
|
+
*/
|
|
82
|
+
type InferSchema<T extends z.ZodTypeAny> = InferType<T>;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Strict field typing utilities for compile-time validation.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Maps field type literals to their corresponding interface.
|
|
90
|
+
*/
|
|
91
|
+
interface FieldTypeMap {
|
|
92
|
+
text: TextField;
|
|
93
|
+
email: EmailField;
|
|
94
|
+
password: PasswordField;
|
|
95
|
+
textarea: TextareaField;
|
|
96
|
+
number: NumberField;
|
|
97
|
+
date: DateField;
|
|
98
|
+
datetime: DatetimeField;
|
|
99
|
+
select: SelectField;
|
|
100
|
+
checkbox: CheckboxField;
|
|
101
|
+
switch: SwitchField;
|
|
102
|
+
radio: RadioField;
|
|
103
|
+
tags: TagsField;
|
|
104
|
+
upload: UploadField;
|
|
105
|
+
group: GroupField;
|
|
106
|
+
array: ArrayField;
|
|
107
|
+
row: RowField;
|
|
108
|
+
tabs: TabsField;
|
|
109
|
+
collapsible: CollapsibleField;
|
|
110
|
+
}
|
|
111
|
+
type FieldTypeLiteral = keyof FieldTypeMap;
|
|
112
|
+
/**
|
|
113
|
+
* Resolves a field object to its strict interface based on the `type` property.
|
|
114
|
+
*/
|
|
115
|
+
type StrictFieldByType<T> = T extends {
|
|
116
|
+
type: infer Type extends FieldTypeLiteral;
|
|
117
|
+
} ? FieldTypeMap[Type] : T extends Field ? T : never;
|
|
118
|
+
/**
|
|
119
|
+
* Validates an array of fields, ensuring each matches its declared type's interface.
|
|
120
|
+
*/
|
|
121
|
+
type StrictFieldArray<T extends readonly unknown[]> = {
|
|
122
|
+
[K in keyof T]: StrictFieldByType<T[K]>;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Creates a Zod schema from field definitions with strict type validation.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* const schema = createSchema([
|
|
130
|
+
* { type: 'email', name: 'email', required: true },
|
|
131
|
+
* { type: 'password', name: 'password', minLength: 8 },
|
|
132
|
+
* ]);
|
|
133
|
+
*
|
|
134
|
+
* type FormData = z.infer<typeof schema>;
|
|
135
|
+
*/
|
|
136
|
+
declare function createSchema<const T extends readonly Field[]>(fields: StrictFieldArray<T> & T): z.ZodObject<FieldsToShape<T>> & {
|
|
137
|
+
fields: T;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Converts field definitions to a Zod schema.
|
|
142
|
+
*
|
|
143
|
+
* Note: Custom validation (field.validate) is handled by the zodResolver,
|
|
144
|
+
* not at the schema level. This ensures custom validators run even when
|
|
145
|
+
* other fields have errors.
|
|
146
|
+
*/
|
|
147
|
+
declare function fieldsToZodSchema<T extends readonly Field[]>(fields: T): z.ZodObject<FieldsToShape<T>>;
|
|
148
|
+
|
|
149
|
+
type ExtractableValidationFn = (value: unknown, context: ValidationContext) => true | string | Promise<true | string>;
|
|
150
|
+
interface ExtractedValidationConfig {
|
|
151
|
+
fn?: ExtractableValidationFn;
|
|
152
|
+
isLive: boolean;
|
|
153
|
+
debounceMs?: number;
|
|
154
|
+
}
|
|
155
|
+
declare function extractValidationConfig(validate?: unknown): ExtractedValidationConfig;
|
|
156
|
+
interface FieldValidator {
|
|
157
|
+
path: string;
|
|
158
|
+
fn: ValidationFn;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Recursively collects all field validators from a field array.
|
|
162
|
+
*/
|
|
163
|
+
declare function collectFieldValidators(fields: readonly Field[], basePath?: string): FieldValidator[];
|
|
164
|
+
/**
|
|
165
|
+
* Gets the parent object containing the field at the given path.
|
|
166
|
+
*/
|
|
167
|
+
declare function getSiblingData(data: Record<string, unknown>, path: string): Record<string, unknown>;
|
|
168
|
+
/**
|
|
169
|
+
* Gets a value at a dot-notation path.
|
|
170
|
+
*/
|
|
171
|
+
declare function getValueByPath(data: Record<string, unknown>, path: string): unknown;
|
|
172
|
+
declare function makeOptional(schema: z.ZodTypeAny, fieldType: FieldType): z.ZodTypeAny;
|
|
173
|
+
declare function coerceToNumber(val: unknown): number | undefined;
|
|
174
|
+
declare function coerceToDate(val: unknown): Date | undefined;
|
|
175
|
+
declare function getPatternErrorMessage(pattern: string | RegExp): string;
|
|
176
|
+
declare function isFileLike(value: unknown): value is File;
|
|
177
|
+
declare function isFileTypeAccepted(file: File, accept: string): boolean;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Creates a standardized set of array field manipulation methods.
|
|
181
|
+
* Abstracts the difference between getting/setting values in different form libraries.
|
|
182
|
+
*
|
|
183
|
+
* @param getArray - Function to get current array value at a path
|
|
184
|
+
* @param setArray - Function to set array value at a path
|
|
185
|
+
*/
|
|
186
|
+
declare function createArrayHelpers(getArray: (path: string) => unknown[], setArray: (path: string, value: unknown[]) => void): ArrayHelpers;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Generate a unique field ID from the field path.
|
|
190
|
+
* Converts dot notation to dashes and prefixes with 'field-'.
|
|
191
|
+
* Used for accessibility (htmlFor, id attributes).
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* generateFieldId('user.profile.email') => 'field-user-profile-email'
|
|
195
|
+
* generateFieldId('items[0].name') => 'field-items-0-name'
|
|
196
|
+
*/
|
|
197
|
+
declare function generateFieldId(path: string): string;
|
|
198
|
+
/**
|
|
199
|
+
* Safely retrieve a nested value from an object using a dot-notation path.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* getNestedValue({ user: { name: 'John' } }, 'user.name') => 'John'
|
|
203
|
+
* getNestedValue({ items: [{ id: 1 }] }, 'items.0.id') => 1
|
|
204
|
+
*/
|
|
205
|
+
declare function getNestedValue(obj: unknown, path: string): unknown;
|
|
206
|
+
/**
|
|
207
|
+
* Set a nested value in an object using a dot-notation path.
|
|
208
|
+
* Creates intermediate objects/arrays as needed.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* setNestedValue({}, 'user.name', 'John') => { user: { name: 'John' } }
|
|
212
|
+
*/
|
|
213
|
+
declare function setNestedValue<T extends Record<string, unknown>>(obj: T, path: string, value: unknown): T;
|
|
214
|
+
/**
|
|
215
|
+
* Format bytes into a human-readable string.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* formatBytes(1024) => '1 KB'
|
|
219
|
+
* formatBytes(1234567) => '1.18 MB'
|
|
220
|
+
*/
|
|
221
|
+
declare function formatBytes(bytes: number, decimals?: number): string;
|
|
222
|
+
|
|
223
|
+
export { type FieldToZod as F, type InferType as I, type SchemaBuilder as S, type FieldsToShape as a, type SchemaBuilderMap as b, type InferSchema as c, createSchema as d, extractValidationConfig as e, fieldsToZodSchema as f, coerceToNumber as g, coerceToDate as h, getPatternErrorMessage as i, isFileLike as j, isFileTypeAccepted as k, createArrayHelpers as l, makeOptional as m, generateFieldId as n, getNestedValue as o, formatBytes as p, collectFieldValidators as q, getSiblingData as r, setNestedValue as s, getValueByPath as t, type FieldValidator as u };
|
package/dist/zod.d.mts
CHANGED
|
@@ -1,32 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { ZodSchema, z } from 'zod';
|
|
3
|
-
import { e as Resolver } from './adapter-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export { ZodType as ZodSchema, z } from 'zod';
|
|
3
|
+
import { e as Resolver } from './adapter-nQW28cyO.mjs';
|
|
4
4
|
import 'react';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Creates a validation resolver from a Zod schema.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* - `{ errors }` if validation fails (with field-level error messages)
|
|
12
|
-
*
|
|
13
|
-
* @param schema - A Zod schema to validate against
|
|
14
|
-
* @returns A Resolver function compatible with BuzzForm adapters
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* import { z } from '@buildnbuzz/buzzform/zod';
|
|
18
|
-
* import { zodResolver } from '@buildnbuzz/buzzform';
|
|
19
|
-
*
|
|
20
|
-
* const schema = z.object({
|
|
21
|
-
* email: z.string().email('Invalid email'),
|
|
22
|
-
* age: z.number().min(18, 'Must be at least 18'),
|
|
23
|
-
* });
|
|
24
|
-
*
|
|
25
|
-
* const form = useRhf({
|
|
26
|
-
* resolver: zodResolver(schema),
|
|
27
|
-
* defaultValues: { email: '', age: 0 },
|
|
28
|
-
* });
|
|
9
|
+
* Custom field validators (field.validate) are run separately from the base schema
|
|
10
|
+
* to ensure they execute even when other fields have errors.
|
|
29
11
|
*/
|
|
30
|
-
declare function zodResolver<TData>(schema:
|
|
12
|
+
declare function zodResolver<TData>(schema: z.ZodType<TData>): Resolver<TData>;
|
|
31
13
|
|
|
32
14
|
export { zodResolver };
|
package/dist/zod.d.ts
CHANGED
|
@@ -1,32 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { ZodSchema, z } from 'zod';
|
|
3
|
-
import { e as Resolver } from './adapter-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export { ZodType as ZodSchema, z } from 'zod';
|
|
3
|
+
import { e as Resolver } from './adapter-nQW28cyO.js';
|
|
4
4
|
import 'react';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Creates a validation resolver from a Zod schema.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* - `{ errors }` if validation fails (with field-level error messages)
|
|
12
|
-
*
|
|
13
|
-
* @param schema - A Zod schema to validate against
|
|
14
|
-
* @returns A Resolver function compatible with BuzzForm adapters
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* import { z } from '@buildnbuzz/buzzform/zod';
|
|
18
|
-
* import { zodResolver } from '@buildnbuzz/buzzform';
|
|
19
|
-
*
|
|
20
|
-
* const schema = z.object({
|
|
21
|
-
* email: z.string().email('Invalid email'),
|
|
22
|
-
* age: z.number().min(18, 'Must be at least 18'),
|
|
23
|
-
* });
|
|
24
|
-
*
|
|
25
|
-
* const form = useRhf({
|
|
26
|
-
* resolver: zodResolver(schema),
|
|
27
|
-
* defaultValues: { email: '', age: 0 },
|
|
28
|
-
* });
|
|
9
|
+
* Custom field validators (field.validate) are run separately from the base schema
|
|
10
|
+
* to ensure they execute even when other fields have errors.
|
|
29
11
|
*/
|
|
30
|
-
declare function zodResolver<TData>(schema:
|
|
12
|
+
declare function zodResolver<TData>(schema: z.ZodType<TData>): Resolver<TData>;
|
|
31
13
|
|
|
32
14
|
export { zodResolver };
|
package/dist/zod.js
CHANGED
|
@@ -20,30 +20,158 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/resolvers/zod.ts
|
|
21
21
|
var zod_exports = {};
|
|
22
22
|
__export(zod_exports, {
|
|
23
|
-
z: () =>
|
|
23
|
+
z: () => import_zod2.z,
|
|
24
24
|
zodResolver: () => zodResolver
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(zod_exports);
|
|
27
|
+
|
|
28
|
+
// src/schema/helpers.ts
|
|
27
29
|
var import_zod = require("zod");
|
|
30
|
+
function extractValidationConfig(validate) {
|
|
31
|
+
if (!validate) {
|
|
32
|
+
return { fn: void 0, isLive: false };
|
|
33
|
+
}
|
|
34
|
+
if (typeof validate === "function") {
|
|
35
|
+
return { fn: validate, isLive: false };
|
|
36
|
+
}
|
|
37
|
+
if (typeof validate === "object" && "fn" in validate) {
|
|
38
|
+
const obj = validate;
|
|
39
|
+
const fn = typeof obj.fn === "function" ? obj.fn : void 0;
|
|
40
|
+
if (!obj.live) {
|
|
41
|
+
return { fn, isLive: false };
|
|
42
|
+
}
|
|
43
|
+
const debounceMs = typeof obj.live === "object" ? obj.live.debounceMs : void 0;
|
|
44
|
+
return { fn, isLive: true, debounceMs };
|
|
45
|
+
}
|
|
46
|
+
return { fn: void 0, isLive: false };
|
|
47
|
+
}
|
|
48
|
+
function collectFieldValidators(fields, basePath = "") {
|
|
49
|
+
const validators = [];
|
|
50
|
+
for (const field of fields) {
|
|
51
|
+
if ("name" in field && field.name) {
|
|
52
|
+
const fieldPath = basePath ? `${basePath}.${field.name}` : field.name;
|
|
53
|
+
if ("validate" in field && field.validate) {
|
|
54
|
+
const config = extractValidationConfig(field.validate);
|
|
55
|
+
if (config.fn) {
|
|
56
|
+
validators.push({
|
|
57
|
+
path: fieldPath,
|
|
58
|
+
fn: config.fn
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (field.type === "group" && "fields" in field) {
|
|
63
|
+
validators.push(...collectFieldValidators(field.fields, fieldPath));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (field.type === "row" && "fields" in field) {
|
|
67
|
+
validators.push(...collectFieldValidators(field.fields, basePath));
|
|
68
|
+
}
|
|
69
|
+
if (field.type === "collapsible" && "fields" in field) {
|
|
70
|
+
validators.push(...collectFieldValidators(field.fields, basePath));
|
|
71
|
+
}
|
|
72
|
+
if (field.type === "tabs" && "tabs" in field) {
|
|
73
|
+
for (const tab of field.tabs) {
|
|
74
|
+
const tabPath = tab.name ? basePath ? `${basePath}.${tab.name}` : tab.name : basePath;
|
|
75
|
+
validators.push(...collectFieldValidators(tab.fields, tabPath));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return validators;
|
|
80
|
+
}
|
|
81
|
+
function getSiblingData(data, path) {
|
|
82
|
+
const parts = path.split(".");
|
|
83
|
+
if (parts.length <= 1) {
|
|
84
|
+
return data;
|
|
85
|
+
}
|
|
86
|
+
const parentParts = parts.slice(0, -1);
|
|
87
|
+
let current = data;
|
|
88
|
+
for (const part of parentParts) {
|
|
89
|
+
if (current && typeof current === "object" && current !== null) {
|
|
90
|
+
current = current[part];
|
|
91
|
+
} else {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (current && typeof current === "object" && current !== null) {
|
|
96
|
+
return current;
|
|
97
|
+
}
|
|
98
|
+
return {};
|
|
99
|
+
}
|
|
100
|
+
function getValueByPath(data, path) {
|
|
101
|
+
const parts = path.split(".");
|
|
102
|
+
let current = data;
|
|
103
|
+
for (const part of parts) {
|
|
104
|
+
if (current && typeof current === "object" && current !== null) {
|
|
105
|
+
current = current[part];
|
|
106
|
+
} else {
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return current;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/resolvers/zod.ts
|
|
114
|
+
var import_zod2 = require("zod");
|
|
28
115
|
function zodResolver(schema) {
|
|
116
|
+
const schemaWithFields = schema;
|
|
117
|
+
const fieldValidators = schemaWithFields.fields ? collectFieldValidators(schemaWithFields.fields) : [];
|
|
29
118
|
return async (values) => {
|
|
119
|
+
const errors = {};
|
|
120
|
+
let parsedValues;
|
|
30
121
|
try {
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
values: parsed,
|
|
34
|
-
errors: {}
|
|
35
|
-
};
|
|
122
|
+
parsedValues = await schema.parseAsync(values);
|
|
36
123
|
} catch (error) {
|
|
37
124
|
if (isZodError(error)) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
};
|
|
125
|
+
Object.assign(errors, mapZodErrors(error));
|
|
126
|
+
} else {
|
|
127
|
+
throw error;
|
|
42
128
|
}
|
|
43
|
-
throw error;
|
|
44
129
|
}
|
|
130
|
+
if (fieldValidators.length > 0) {
|
|
131
|
+
const customErrors = await runFieldValidators(
|
|
132
|
+
fieldValidators,
|
|
133
|
+
values
|
|
134
|
+
);
|
|
135
|
+
for (const [path, error] of Object.entries(customErrors)) {
|
|
136
|
+
if (!errors[path]) {
|
|
137
|
+
errors[path] = error;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (Object.keys(errors).length === 0 && parsedValues !== void 0) {
|
|
142
|
+
return { values: parsedValues, errors: {} };
|
|
143
|
+
}
|
|
144
|
+
return { values: {}, errors };
|
|
45
145
|
};
|
|
46
146
|
}
|
|
147
|
+
async function runFieldValidators(validators, data) {
|
|
148
|
+
const errors = {};
|
|
149
|
+
await Promise.all(
|
|
150
|
+
validators.map(async ({ path, fn }) => {
|
|
151
|
+
const value = getValueByPath(data, path);
|
|
152
|
+
const siblingData = getSiblingData(data, path);
|
|
153
|
+
try {
|
|
154
|
+
const result = await fn(value, {
|
|
155
|
+
data,
|
|
156
|
+
siblingData,
|
|
157
|
+
path: path.split(".")
|
|
158
|
+
});
|
|
159
|
+
if (result !== true) {
|
|
160
|
+
errors[path] = {
|
|
161
|
+
type: "custom",
|
|
162
|
+
message: typeof result === "string" ? result : "Validation failed"
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
errors[path] = {
|
|
167
|
+
type: "custom",
|
|
168
|
+
message: error instanceof Error ? error.message : "Validation error"
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
);
|
|
173
|
+
return errors;
|
|
174
|
+
}
|
|
47
175
|
function mapZodErrors(error) {
|
|
48
176
|
const errors = {};
|
|
49
177
|
for (const issue of error.issues) {
|
|
@@ -63,18 +191,24 @@ function issuePath(issue) {
|
|
|
63
191
|
function issueType(issue) {
|
|
64
192
|
switch (issue.code) {
|
|
65
193
|
case "invalid_type":
|
|
66
|
-
if (issue.received === "undefined") return "required";
|
|
194
|
+
if ("received" in issue && issue.received === "undefined") return "required";
|
|
67
195
|
return "type";
|
|
68
196
|
case "too_small":
|
|
69
|
-
|
|
197
|
+
if ("origin" in issue && issue.origin === "string") return "minLength";
|
|
198
|
+
return "min";
|
|
70
199
|
case "too_big":
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
200
|
+
if ("origin" in issue && issue.origin === "string") return "maxLength";
|
|
201
|
+
return "max";
|
|
202
|
+
case "invalid_format": {
|
|
203
|
+
const formatIssue = issue;
|
|
204
|
+
if (formatIssue.format === "regex") return "pattern";
|
|
205
|
+
if (typeof formatIssue.format === "string") return formatIssue.format;
|
|
206
|
+
return "pattern";
|
|
207
|
+
}
|
|
74
208
|
case "custom":
|
|
75
209
|
return "custom";
|
|
76
210
|
default:
|
|
77
|
-
return issue.code;
|
|
211
|
+
return issue.code ?? "validation";
|
|
78
212
|
}
|
|
79
213
|
}
|
|
80
214
|
function isZodError(error) {
|
package/dist/zod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/resolvers/zod.ts"],"sourcesContent":["import type { ZodSchema, ZodError, ZodIssue } from 'zod';\r\nimport type { Resolver, FieldError, ResolverResult } from '../types';\r\n\r\n// =============================================================================\r\n// ZOD RESOLVER\r\n// =============================================================================\r\n\r\n/**\r\n * Creates a validation resolver from a Zod schema.\r\n * \r\n * The resolver validates form values against the schema and returns:\r\n * - `{ values }` if validation passes (with transformed/parsed data)\r\n * - `{ errors }` if validation fails (with field-level error messages)\r\n * \r\n * @param schema - A Zod schema to validate against\r\n * @returns A Resolver function compatible with BuzzForm adapters\r\n * \r\n * @example\r\n * import { z } from '@buildnbuzz/buzzform/zod';\r\n * import { zodResolver } from '@buildnbuzz/buzzform';\r\n * \r\n * const schema = z.object({\r\n * email: z.string().email('Invalid email'),\r\n * age: z.number().min(18, 'Must be at least 18'),\r\n * });\r\n * \r\n * const form = useRhf({\r\n * resolver: zodResolver(schema),\r\n * defaultValues: { email: '', age: 0 },\r\n * });\r\n */\r\nexport function zodResolver<TData>(\r\n schema: ZodSchema<TData>\r\n): Resolver<TData> {\r\n return async (values: TData): Promise<ResolverResult<TData>> => {\r\n try {\r\n // Parse and validate - this also transforms the data\r\n const parsed = await schema.parseAsync(values);\r\n\r\n return {\r\n values: parsed,\r\n errors: {},\r\n };\r\n } catch (error) {\r\n // Handle Zod validation errors\r\n if (isZodError(error)) {\r\n return {\r\n values: {} as TData,\r\n errors: mapZodErrors(error),\r\n };\r\n }\r\n\r\n // Re-throw unexpected errors\r\n throw error;\r\n }\r\n };\r\n}\r\n\r\n// =============================================================================\r\n// ERROR MAPPING\r\n// =============================================================================\r\n\r\n/**\r\n * Maps Zod validation errors to our FieldError format.\r\n * Handles nested paths (e.g., \"address.city\", \"items.0.name\").\r\n */\r\nfunction mapZodErrors(error: ZodError): Record<string, FieldError> {\r\n const errors: Record<string, FieldError> = {};\r\n\r\n for (const issue of error.issues) {\r\n const path = issuePath(issue);\r\n\r\n // Only set the first error for each path\r\n if (!errors[path]) {\r\n errors[path] = {\r\n type: issueType(issue),\r\n message: issue.message,\r\n };\r\n }\r\n }\r\n\r\n return errors;\r\n}\r\n\r\n/**\r\n * Convert Zod issue path to dot-notation string.\r\n * ['address', 'city'] → 'address.city'\r\n * ['items', 0, 'name'] → 'items.0.name'\r\n */\r\nfunction issuePath(issue: ZodIssue): string {\r\n return issue.path.map(String).join('.');\r\n}\r\n\r\n/**\r\n * Map Zod issue code to a simpler type string.\r\n */\r\nfunction issueType(issue: ZodIssue): string {\r\n switch (issue.code) {\r\n case 'invalid_type':\r\n if (issue.received === 'undefined') return 'required';\r\n return 'type';\r\n case 'too_small':\r\n return issue.type === 'string' ? 'minLength' : 'min';\r\n case 'too_big':\r\n return issue.type === 'string' ? 'maxLength' : 'max';\r\n case 'invalid_string':\r\n return issue.validation?.toString() || 'pattern';\r\n case 'custom':\r\n return 'custom';\r\n default:\r\n return issue.code;\r\n }\r\n}\r\n\r\n/**\r\n * Type guard to check if an error is a ZodError.\r\n */\r\nfunction isZodError(error: unknown): error is ZodError {\r\n return (\r\n typeof error === 'object' &&\r\n error !== null &&\r\n 'issues' in error &&\r\n Array.isArray((error as ZodError).issues)\r\n );\r\n}\r\n\r\n// =============================================================================\r\n// RE-EXPORTS FOR CONVENIENCE\r\n// =============================================================================\r\n\r\nexport type { ZodSchema } from 'zod';\r\nexport { z } from 'zod';"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmIA,iBAAkB;AApGX,SAAS,YACZ,QACe;AACf,SAAO,OAAO,WAAkD;AAC5D,QAAI;AAEA,YAAM,SAAS,MAAM,OAAO,WAAW,MAAM;AAE7C,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ,CAAC;AAAA,MACb;AAAA,IACJ,SAAS,OAAO;AAEZ,UAAI,WAAW,KAAK,GAAG;AACnB,eAAO;AAAA,UACH,QAAQ,CAAC;AAAA,UACT,QAAQ,aAAa,KAAK;AAAA,QAC9B;AAAA,MACJ;AAGA,YAAM;AAAA,IACV;AAAA,EACJ;AACJ;AAUA,SAAS,aAAa,OAA6C;AAC/D,QAAM,SAAqC,CAAC;AAE5C,aAAW,SAAS,MAAM,QAAQ;AAC9B,UAAM,OAAO,UAAU,KAAK;AAG5B,QAAI,CAAC,OAAO,IAAI,GAAG;AACf,aAAO,IAAI,IAAI;AAAA,QACX,MAAM,UAAU,KAAK;AAAA,QACrB,SAAS,MAAM;AAAA,MACnB;AAAA,IACJ;AAAA,EACJ;AAEA,SAAO;AACX;AAOA,SAAS,UAAU,OAAyB;AACxC,SAAO,MAAM,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG;AAC1C;AAKA,SAAS,UAAU,OAAyB;AACxC,UAAQ,MAAM,MAAM;AAAA,IAChB,KAAK;AACD,UAAI,MAAM,aAAa,YAAa,QAAO;AAC3C,aAAO;AAAA,IACX,KAAK;AACD,aAAO,MAAM,SAAS,WAAW,cAAc;AAAA,IACnD,KAAK;AACD,aAAO,MAAM,SAAS,WAAW,cAAc;AAAA,IACnD,KAAK;AACD,aAAO,MAAM,YAAY,SAAS,KAAK;AAAA,IAC3C,KAAK;AACD,aAAO;AAAA,IACX;AACI,aAAO,MAAM;AAAA,EACrB;AACJ;AAKA,SAAS,WAAW,OAAmC;AACnD,SACI,OAAO,UAAU,YACjB,UAAU,QACV,YAAY,SACZ,MAAM,QAAS,MAAmB,MAAM;AAEhD;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/resolvers/zod.ts","../src/schema/helpers.ts"],"sourcesContent":["import { type z } from 'zod';\nimport type { Resolver, FieldError, ResolverResult, Field } from '../types';\nimport { collectFieldValidators, getSiblingData, getValueByPath, type FieldValidator } from '../schema/helpers';\n\n// =============================================================================\n// ZOD RESOLVER\n// =============================================================================\n\ntype ZodIssue = z.core.$ZodIssue;\ntype ZodError = z.ZodError;\n\n// Schema with attached fields from createSchema()\ntype SchemaWithFields = z.ZodType & { fields?: readonly Field[] };\n\n/**\n * Creates a validation resolver from a Zod schema.\n * \n * Custom field validators (field.validate) are run separately from the base schema\n * to ensure they execute even when other fields have errors.\n */\nexport function zodResolver<TData>(\n schema: z.ZodType<TData>\n): Resolver<TData> {\n // Extract field validators from schema.fields (if present)\n const schemaWithFields = schema as SchemaWithFields;\n const fieldValidators = schemaWithFields.fields\n ? collectFieldValidators(schemaWithFields.fields)\n : [];\n\n return async (values: TData): Promise<ResolverResult<TData>> => {\n const errors: Record<string, FieldError> = {};\n let parsedValues: TData | undefined;\n\n // Phase 1: Run base schema validation\n try {\n parsedValues = await schema.parseAsync(values);\n } catch (error) {\n if (isZodError(error)) {\n Object.assign(errors, mapZodErrors(error));\n } else {\n throw error;\n }\n }\n\n // Phase 2: Run custom field validators (always runs, even if base fails)\n if (fieldValidators.length > 0) {\n const customErrors = await runFieldValidators(\n fieldValidators,\n values as Record<string, unknown>\n );\n // Merge, but don't overwrite existing errors\n for (const [path, error] of Object.entries(customErrors)) {\n if (!errors[path]) {\n errors[path] = error;\n }\n }\n }\n\n // Return result\n if (Object.keys(errors).length === 0 && parsedValues !== undefined) {\n return { values: parsedValues, errors: {} };\n }\n\n return { values: {} as TData, errors };\n };\n}\n\n/**\n * Run all field validators and collect errors.\n */\nasync function runFieldValidators(\n validators: FieldValidator[],\n data: Record<string, unknown>\n): Promise<Record<string, FieldError>> {\n const errors: Record<string, FieldError> = {};\n\n await Promise.all(\n validators.map(async ({ path, fn }) => {\n const value = getValueByPath(data, path);\n const siblingData = getSiblingData(data, path);\n\n try {\n const result = await fn(value, {\n data,\n siblingData,\n path: path.split('.'),\n });\n\n if (result !== true) {\n errors[path] = {\n type: 'custom',\n message: typeof result === 'string' ? result : 'Validation failed',\n };\n }\n } catch (error) {\n errors[path] = {\n type: 'custom',\n message: error instanceof Error ? error.message : 'Validation error',\n };\n }\n })\n );\n\n return errors;\n}\n\n// =============================================================================\n// ERROR MAPPING\n// =============================================================================\n\nfunction mapZodErrors(error: ZodError): Record<string, FieldError> {\n const errors: Record<string, FieldError> = {};\n\n for (const issue of error.issues) {\n const path = issuePath(issue);\n if (!errors[path]) {\n errors[path] = {\n type: issueType(issue),\n message: issue.message,\n };\n }\n }\n\n return errors;\n}\n\nfunction issuePath(issue: ZodIssue): string {\n return issue.path.map(String).join('.');\n}\n\nfunction issueType(issue: ZodIssue): string {\n switch (issue.code) {\n case 'invalid_type':\n if ('received' in issue && issue.received === 'undefined') return 'required';\n return 'type';\n case 'too_small':\n if ('origin' in issue && issue.origin === 'string') return 'minLength';\n return 'min';\n case 'too_big':\n if ('origin' in issue && issue.origin === 'string') return 'maxLength';\n return 'max';\n case 'invalid_format': {\n const formatIssue = issue as { format?: string };\n if (formatIssue.format === 'regex') return 'pattern';\n if (typeof formatIssue.format === 'string') return formatIssue.format;\n return 'pattern';\n }\n case 'custom':\n return 'custom';\n default:\n return issue.code ?? 'validation';\n }\n}\n\nfunction isZodError(error: unknown): error is ZodError {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'issues' in error &&\n Array.isArray((error as ZodError).issues)\n );\n}\n\n// =============================================================================\n// RE-EXPORTS\n// =============================================================================\n\nexport type { ZodType as ZodSchema } from 'zod';\nexport { z } from 'zod';","import { z } from 'zod';\nimport type { Field, FieldType, ValidationContext, ValidationFn } from '../types';\n\n// =============================================================================\n// VALIDATION CONFIG EXTRACTION\n// =============================================================================\n\ntype ExtractableValidationFn = (\n value: unknown,\n context: ValidationContext\n) => true | string | Promise<true | string>;\n\nexport interface ExtractedValidationConfig {\n fn?: ExtractableValidationFn;\n isLive: boolean;\n debounceMs?: number;\n}\n\nexport function extractValidationConfig(\n validate?: unknown\n): ExtractedValidationConfig {\n if (!validate) {\n return { fn: undefined, isLive: false };\n }\n\n if (typeof validate === 'function') {\n return { fn: validate as ExtractableValidationFn, isLive: false };\n }\n\n if (typeof validate === 'object' && 'fn' in validate) {\n const obj = validate as { fn?: unknown; live?: boolean | { debounceMs?: number } };\n const fn = typeof obj.fn === 'function' ? obj.fn as ExtractableValidationFn : undefined;\n\n if (!obj.live) {\n return { fn, isLive: false };\n }\n\n const debounceMs = typeof obj.live === 'object' ? obj.live.debounceMs : undefined;\n return { fn, isLive: true, debounceMs };\n }\n\n return { fn: undefined, isLive: false };\n}\n\n// =============================================================================\n// FIELD VALIDATOR COLLECTION\n// =============================================================================\n\nexport interface FieldValidator {\n path: string;\n fn: ValidationFn;\n}\n\n/**\n * Recursively collects all field validators from a field array.\n */\nexport function collectFieldValidators(\n fields: readonly Field[],\n basePath: string = ''\n): FieldValidator[] {\n const validators: FieldValidator[] = [];\n\n for (const field of fields) {\n if ('name' in field && field.name) {\n const fieldPath = basePath ? `${basePath}.${field.name}` : field.name;\n\n if ('validate' in field && field.validate) {\n const config = extractValidationConfig(field.validate);\n if (config.fn) {\n validators.push({\n path: fieldPath,\n fn: config.fn as ValidationFn,\n });\n }\n }\n\n if (field.type === 'group' && 'fields' in field) {\n validators.push(...collectFieldValidators(field.fields, fieldPath));\n }\n }\n\n // Layout fields pass through without adding to path\n if (field.type === 'row' && 'fields' in field) {\n validators.push(...collectFieldValidators(field.fields, basePath));\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n validators.push(...collectFieldValidators(field.fields, basePath));\n }\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n const tabPath = tab.name\n ? (basePath ? `${basePath}.${tab.name}` : tab.name)\n : basePath;\n validators.push(...collectFieldValidators(tab.fields, tabPath));\n }\n }\n }\n\n return validators;\n}\n\n// =============================================================================\n// SIBLING DATA EXTRACTION\n// =============================================================================\n\n/**\n * Gets the parent object containing the field at the given path.\n */\nexport function getSiblingData(\n data: Record<string, unknown>,\n path: string\n): Record<string, unknown> {\n const parts = path.split('.');\n\n if (parts.length <= 1) {\n return data;\n }\n\n const parentParts = parts.slice(0, -1);\n let current: unknown = data;\n\n for (const part of parentParts) {\n if (current && typeof current === 'object' && current !== null) {\n current = (current as Record<string, unknown>)[part];\n } else {\n return {};\n }\n }\n\n if (current && typeof current === 'object' && current !== null) {\n return current as Record<string, unknown>;\n }\n\n return {};\n}\n\n/**\n * Gets a value at a dot-notation path.\n */\nexport function getValueByPath(\n data: Record<string, unknown>,\n path: string\n): unknown {\n const parts = path.split('.');\n let current: unknown = data;\n\n for (const part of parts) {\n if (current && typeof current === 'object' && current !== null) {\n current = (current as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n\n return current;\n}\n\n/**\n * Creates a superRefine that runs all field validators with full form context.\n */\nexport function createRootValidationRefinement(\n validators: FieldValidator[]\n): (data: Record<string, unknown>, ctx: z.RefinementCtx) => Promise<void> {\n return async (data, ctx) => {\n const validationPromises = validators.map(async ({ path, fn }) => {\n const value = getValueByPath(data, path);\n const siblingData = getSiblingData(data, path);\n\n try {\n const result = await fn(value, {\n data,\n siblingData,\n path: path.split('.'),\n });\n\n if (result !== true) {\n ctx.addIssue({\n code: 'custom',\n path: path.split('.'),\n message: typeof result === 'string' ? result : 'Validation failed',\n });\n }\n } catch (error) {\n ctx.addIssue({\n code: 'custom',\n path: path.split('.'),\n message: error instanceof Error ? error.message : 'Validation error',\n });\n }\n });\n\n await Promise.all(validationPromises);\n };\n}\n\n// =============================================================================\n// OPTIONAL HANDLING\n// =============================================================================\n\nexport function makeOptional(\n schema: z.ZodTypeAny,\n fieldType: FieldType\n): z.ZodTypeAny {\n switch (fieldType) {\n case 'text':\n case 'textarea':\n case 'email':\n case 'password':\n return schema.optional().or(z.literal(''));\n\n case 'number':\n case 'date':\n case 'select':\n case 'radio':\n return schema.optional().nullable();\n\n case 'checkbox':\n case 'switch':\n return schema;\n\n case 'tags':\n case 'array':\n return schema.optional().default([]);\n\n case 'upload':\n return schema.optional().nullable();\n\n default:\n return schema.optional();\n }\n}\n\n// =============================================================================\n// COERCION HELPERS\n// =============================================================================\n\nexport function coerceToNumber(val: unknown): number | undefined {\n if (val === '' || val === null || val === undefined) {\n return undefined;\n }\n const num = Number(val);\n return isNaN(num) ? undefined : num;\n}\n\nexport function coerceToDate(val: unknown): Date | undefined {\n if (val === '' || val === null || val === undefined) {\n return undefined;\n }\n if (val instanceof Date) {\n return isNaN(val.getTime()) ? undefined : val;\n }\n if (typeof val === 'string' || typeof val === 'number') {\n const d = new Date(val);\n return isNaN(d.getTime()) ? undefined : d;\n }\n return undefined;\n}\n\n// =============================================================================\n// PATTERN VALIDATION\n// =============================================================================\n\nconst PATTERN_MESSAGES: Record<string, string> = {\n '^[a-zA-Z0-9_]+$': 'Only letters, numbers, and underscores allowed',\n '^[a-z0-9-]+$': 'Only lowercase letters, numbers, and hyphens allowed',\n '^\\\\S+@\\\\S+\\\\.\\\\S+$': 'Invalid email format',\n '^https?://': 'Must start with http:// or https://',\n};\n\nexport function getPatternErrorMessage(pattern: string | RegExp): string {\n const patternStr = typeof pattern === 'string' ? pattern : pattern.source;\n return PATTERN_MESSAGES[patternStr] || `Must match pattern: ${patternStr}`;\n}\n\n// =============================================================================\n// FILE VALIDATION HELPERS\n// =============================================================================\n\nexport function isFileLike(value: unknown): value is File {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'name' in value &&\n 'size' in value &&\n 'type' in value\n );\n}\n\nexport function isFileTypeAccepted(\n file: File,\n accept: string\n): boolean {\n if (accept === '*' || !accept) return true;\n\n const acceptTypes = accept.split(',').map(t => t.trim().toLowerCase());\n const fileType = file.type.toLowerCase();\n const fileName = file.name.toLowerCase();\n\n return acceptTypes.some(acceptType => {\n if (acceptType.endsWith('/*')) {\n const category = acceptType.replace('/*', '');\n return fileType.startsWith(category + '/');\n }\n if (acceptType.startsWith('.')) {\n return fileName.endsWith(acceptType);\n }\n return fileType === acceptType;\n });\n}\n"],"mappings":"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","names":["import_zod"]}
|