@buildnbuzz/buzzform 0.1.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.
@@ -0,0 +1,599 @@
1
+ // src/schema/fields-to-schema.ts
2
+ import { z as z10 } from "zod";
3
+
4
+ // src/schema/builders/text.ts
5
+ import { z as z2 } from "zod";
6
+
7
+ // src/schema/helpers.ts
8
+ import { z } from "zod";
9
+ function extractValidationConfig(validate) {
10
+ if (!validate) {
11
+ return { fn: void 0, isLive: false };
12
+ }
13
+ if (typeof validate === "function") {
14
+ return { fn: validate, isLive: false };
15
+ }
16
+ if (typeof validate === "object" && "fn" in validate) {
17
+ const obj = validate;
18
+ const fn = typeof obj.fn === "function" ? obj.fn : void 0;
19
+ if (!obj.live) {
20
+ return { fn, isLive: false };
21
+ }
22
+ const debounceMs = typeof obj.live === "object" ? obj.live.debounceMs : void 0;
23
+ return { fn, isLive: true, debounceMs };
24
+ }
25
+ return { fn: void 0, isLive: false };
26
+ }
27
+ function applyCustomValidation(schema, field, fieldPath = "") {
28
+ if (!("validate" in field)) {
29
+ return schema;
30
+ }
31
+ const fieldWithValidate = field;
32
+ if (!fieldWithValidate.validate) {
33
+ return schema;
34
+ }
35
+ const config = extractValidationConfig(fieldWithValidate.validate);
36
+ if (!config.fn) {
37
+ return schema;
38
+ }
39
+ return schema.superRefine(async (val, ctx) => {
40
+ const result = await config.fn(val, {
41
+ data: {},
42
+ siblingData: {},
43
+ path: fieldPath.split(".")
44
+ });
45
+ if (result !== true) {
46
+ ctx.addIssue({
47
+ code: z.ZodIssueCode.custom,
48
+ message: typeof result === "string" ? result : "Validation failed"
49
+ });
50
+ }
51
+ });
52
+ }
53
+ function makeOptional(schema, fieldType) {
54
+ switch (fieldType) {
55
+ // String types: allow empty string
56
+ case "text":
57
+ case "textarea":
58
+ case "email":
59
+ case "password":
60
+ return schema.optional().or(z.literal(""));
61
+ // Nullable types
62
+ case "number":
63
+ case "date":
64
+ case "select":
65
+ case "radio":
66
+ return schema.optional().nullable();
67
+ // Boolean types: always have a value
68
+ case "checkbox":
69
+ case "switch":
70
+ return schema;
71
+ // Booleans are never "optional" in the traditional sense
72
+ // Array types
73
+ case "tags":
74
+ case "array":
75
+ return schema.optional().default([]);
76
+ // Upload
77
+ case "upload":
78
+ return schema.optional().nullable();
79
+ // Default
80
+ default:
81
+ return schema.optional();
82
+ }
83
+ }
84
+ function coerceToNumber(val) {
85
+ if (val === "" || val === null || val === void 0) {
86
+ return void 0;
87
+ }
88
+ const num = Number(val);
89
+ return isNaN(num) ? void 0 : num;
90
+ }
91
+ function coerceToDate(val) {
92
+ if (val === "" || val === null || val === void 0) {
93
+ return void 0;
94
+ }
95
+ if (val instanceof Date) {
96
+ return isNaN(val.getTime()) ? void 0 : val;
97
+ }
98
+ if (typeof val === "string" || typeof val === "number") {
99
+ const d = new Date(val);
100
+ return isNaN(d.getTime()) ? void 0 : d;
101
+ }
102
+ return void 0;
103
+ }
104
+ var PATTERN_MESSAGES = {
105
+ "^[a-zA-Z0-9_]+$": "Only letters, numbers, and underscores allowed",
106
+ "^[a-z0-9-]+$": "Only lowercase letters, numbers, and hyphens allowed",
107
+ "^\\S+@\\S+\\.\\S+$": "Invalid email format",
108
+ "^https?://": "Must start with http:// or https://"
109
+ };
110
+ function getPatternErrorMessage(pattern) {
111
+ const patternStr = typeof pattern === "string" ? pattern : pattern.source;
112
+ return PATTERN_MESSAGES[patternStr] || `Must match pattern: ${patternStr}`;
113
+ }
114
+ function isFileLike(value) {
115
+ return typeof value === "object" && value !== null && "name" in value && "size" in value && "type" in value;
116
+ }
117
+ function isFileTypeAccepted(file, accept) {
118
+ if (accept === "*" || !accept) return true;
119
+ const acceptTypes = accept.split(",").map((t) => t.trim().toLowerCase());
120
+ const fileType = file.type.toLowerCase();
121
+ const fileName = file.name.toLowerCase();
122
+ return acceptTypes.some((acceptType) => {
123
+ if (acceptType.endsWith("/*")) {
124
+ const category = acceptType.replace("/*", "");
125
+ return fileType.startsWith(category + "/");
126
+ }
127
+ if (acceptType.startsWith(".")) {
128
+ return fileName.endsWith(acceptType);
129
+ }
130
+ return fileType === acceptType;
131
+ });
132
+ }
133
+
134
+ // src/schema/builders/text.ts
135
+ function createTextFieldSchema(field) {
136
+ let schema = z2.string();
137
+ if ("pattern" in field && field.pattern) {
138
+ const pattern = typeof field.pattern === "string" ? new RegExp(field.pattern) : field.pattern;
139
+ schema = schema.regex(pattern, {
140
+ message: getPatternErrorMessage(field.pattern)
141
+ });
142
+ }
143
+ if (field.minLength) {
144
+ schema = schema.min(field.minLength, {
145
+ message: `Must be at least ${field.minLength} characters`
146
+ });
147
+ }
148
+ if (field.maxLength) {
149
+ schema = schema.max(field.maxLength, {
150
+ message: `Must be no more than ${field.maxLength} characters`
151
+ });
152
+ }
153
+ if (field.required) {
154
+ schema = schema.min(1, { message: "This field is required" });
155
+ }
156
+ let finalSchema = schema;
157
+ if ("trim" in field && field.trim) {
158
+ finalSchema = z2.preprocess((val) => {
159
+ return typeof val === "string" ? val.trim() : val;
160
+ }, schema);
161
+ }
162
+ finalSchema = applyCustomValidation(finalSchema, field, field.name);
163
+ if (!field.required) {
164
+ return makeOptional(finalSchema, field.type);
165
+ }
166
+ return finalSchema;
167
+ }
168
+ function createEmailFieldSchema(field) {
169
+ let schema = z2.string().email({ message: "Invalid email address" });
170
+ if (field.minLength) {
171
+ schema = schema.min(field.minLength, {
172
+ message: `Must be at least ${field.minLength} characters`
173
+ });
174
+ }
175
+ if (field.maxLength) {
176
+ schema = schema.max(field.maxLength, {
177
+ message: `Must be no more than ${field.maxLength} characters`
178
+ });
179
+ }
180
+ if (field.required) {
181
+ schema = schema.min(1, { message: "Email is required" });
182
+ }
183
+ const finalSchema = applyCustomValidation(schema, field, field.name);
184
+ if (!field.required) {
185
+ return makeOptional(finalSchema, "email");
186
+ }
187
+ return finalSchema;
188
+ }
189
+ function createPasswordFieldSchema(field) {
190
+ let schema = z2.string();
191
+ if (field.minLength) {
192
+ schema = schema.min(field.minLength, {
193
+ message: `Password must be at least ${field.minLength} characters`
194
+ });
195
+ }
196
+ if (field.maxLength) {
197
+ schema = schema.max(field.maxLength, {
198
+ message: `Password must be no more than ${field.maxLength} characters`
199
+ });
200
+ }
201
+ if (field.required) {
202
+ schema = schema.min(1, { message: "Password is required" });
203
+ }
204
+ const finalSchema = applyCustomValidation(schema, field, field.name);
205
+ if (!field.required) {
206
+ return makeOptional(finalSchema, "password");
207
+ }
208
+ return finalSchema;
209
+ }
210
+
211
+ // src/schema/builders/number.ts
212
+ import { z as z3 } from "zod";
213
+ function createNumberFieldSchema(field) {
214
+ let numSchema = z3.number({ invalid_type_error: "Must be a number" });
215
+ if (field.min !== void 0) {
216
+ numSchema = numSchema.min(field.min, `Must be at least ${field.min}`);
217
+ }
218
+ if (field.max !== void 0) {
219
+ numSchema = numSchema.max(field.max, `Must be at most ${field.max}`);
220
+ }
221
+ let schema = z3.preprocess(coerceToNumber, numSchema);
222
+ schema = applyCustomValidation(schema, field, field.name);
223
+ if (field.required) {
224
+ return schema;
225
+ }
226
+ return makeOptional(schema, "number");
227
+ }
228
+
229
+ // src/schema/builders/date.ts
230
+ import { z as z4 } from "zod";
231
+ function toDate(value) {
232
+ if (!value) return void 0;
233
+ if (value instanceof Date) return isNaN(value.getTime()) ? void 0 : value;
234
+ const parsed = new Date(value);
235
+ return isNaN(parsed.getTime()) ? void 0 : parsed;
236
+ }
237
+ function createDateFieldSchema(field) {
238
+ const isDatetime = field.type === "datetime";
239
+ const minDate = toDate(field.minDate);
240
+ const maxDate = toDate(field.maxDate);
241
+ let dateSchema = z4.date({ invalid_type_error: "Please enter a valid date" });
242
+ if (minDate) {
243
+ const formattedDate = isDatetime ? minDate.toLocaleString() : minDate.toDateString();
244
+ dateSchema = dateSchema.min(minDate, {
245
+ message: `Date must be on or after ${formattedDate}`
246
+ });
247
+ }
248
+ if (maxDate) {
249
+ const formattedDate = isDatetime ? maxDate.toLocaleString() : maxDate.toDateString();
250
+ dateSchema = dateSchema.max(maxDate, {
251
+ message: `Date must be on or before ${formattedDate}`
252
+ });
253
+ }
254
+ let schema = z4.preprocess(coerceToDate, dateSchema);
255
+ schema = applyCustomValidation(schema, field, field.name);
256
+ if (field.required) {
257
+ return schema.refine(
258
+ (val) => val instanceof Date && !isNaN(val.getTime()),
259
+ "Date is required"
260
+ );
261
+ }
262
+ return makeOptional(schema, "date");
263
+ }
264
+
265
+ // src/schema/builders/select.ts
266
+ import { z as z5 } from "zod";
267
+ var selectValueSchema = z5.union([z5.string(), z5.number(), z5.boolean()]);
268
+ function createSelectFieldSchema(field) {
269
+ if (field.hasMany) {
270
+ let arraySchema = z5.array(selectValueSchema);
271
+ if (field.required) {
272
+ arraySchema = arraySchema.min(1, "Select at least one option");
273
+ }
274
+ const schema2 = applyCustomValidation(arraySchema, field, field.name);
275
+ if (!field.required) {
276
+ return schema2.optional().default([]);
277
+ }
278
+ return schema2;
279
+ }
280
+ let schema = selectValueSchema;
281
+ if (field.required) {
282
+ schema = selectValueSchema.refine(
283
+ (val) => val !== "" && val !== null && val !== void 0,
284
+ "Please select an option"
285
+ );
286
+ }
287
+ schema = applyCustomValidation(schema, field, field.name);
288
+ if (!field.required) {
289
+ return makeOptional(schema, "select");
290
+ }
291
+ return schema;
292
+ }
293
+ function createRadioFieldSchema(field) {
294
+ let schema = selectValueSchema;
295
+ if (field.required) {
296
+ schema = selectValueSchema.refine(
297
+ (val) => val !== "" && val !== null && val !== void 0,
298
+ "Please select an option"
299
+ );
300
+ }
301
+ schema = applyCustomValidation(schema, field, field.name);
302
+ if (!field.required) {
303
+ return makeOptional(schema, "radio");
304
+ }
305
+ return schema;
306
+ }
307
+
308
+ // src/schema/builders/boolean.ts
309
+ import { z as z6 } from "zod";
310
+ function createCheckboxFieldSchema(field) {
311
+ let schema = z6.boolean();
312
+ if (field.required) {
313
+ schema = z6.boolean().refine((val) => val === true, {
314
+ message: "This field is required"
315
+ });
316
+ }
317
+ return applyCustomValidation(schema, field, field.name);
318
+ }
319
+ function createSwitchFieldSchema(field) {
320
+ let schema = z6.boolean();
321
+ if (field.required) {
322
+ schema = z6.boolean().refine((val) => val === true, {
323
+ message: "This field is required"
324
+ });
325
+ }
326
+ return applyCustomValidation(schema, field, field.name);
327
+ }
328
+
329
+ // src/schema/builders/upload.ts
330
+ import { z as z7 } from "zod";
331
+ function validateSingleFile(value, field) {
332
+ if (value === null || value === void 0 || value === "") {
333
+ return { valid: true };
334
+ }
335
+ if (typeof value === "string") {
336
+ return { valid: true };
337
+ }
338
+ if (!isFileLike(value)) {
339
+ return { valid: false, message: "Invalid file" };
340
+ }
341
+ if (field.maxSize && value.size > field.maxSize) {
342
+ const sizeMB = (field.maxSize / 1024 / 1024).toFixed(1);
343
+ return {
344
+ valid: false,
345
+ message: `File must be smaller than ${sizeMB}MB`
346
+ };
347
+ }
348
+ const accept = field.ui?.accept;
349
+ if (accept && accept !== "*") {
350
+ if (!isFileTypeAccepted(value, accept)) {
351
+ return {
352
+ valid: false,
353
+ message: `File type not allowed. Accepted: ${accept}`
354
+ };
355
+ }
356
+ }
357
+ return { valid: true };
358
+ }
359
+ function createUploadFieldSchema(field) {
360
+ if (field.hasMany) {
361
+ const schema2 = z7.array(z7.any()).superRefine((files, ctx) => {
362
+ for (let i = 0; i < files.length; i++) {
363
+ const file = files[i];
364
+ const result = validateSingleFile(file, field);
365
+ if (!result.valid) {
366
+ ctx.addIssue({
367
+ code: z7.ZodIssueCode.custom,
368
+ message: result.message || "Invalid file",
369
+ path: [i]
370
+ });
371
+ }
372
+ }
373
+ if (field.required) {
374
+ const validFiles = files.filter((f) => f !== null && f !== void 0);
375
+ if (validFiles.length === 0) {
376
+ ctx.addIssue({
377
+ code: z7.ZodIssueCode.custom,
378
+ message: "At least one file is required"
379
+ });
380
+ }
381
+ }
382
+ if (field.minFiles !== void 0 && field.minFiles > 0) {
383
+ const validFiles = files.filter((f) => f !== null && f !== void 0);
384
+ if (validFiles.length < field.minFiles) {
385
+ ctx.addIssue({
386
+ code: z7.ZodIssueCode.custom,
387
+ message: `At least ${field.minFiles} file(s) required`
388
+ });
389
+ }
390
+ }
391
+ if (field.maxFiles !== void 0) {
392
+ const validFiles = files.filter((f) => f !== null && f !== void 0);
393
+ if (validFiles.length > field.maxFiles) {
394
+ ctx.addIssue({
395
+ code: z7.ZodIssueCode.custom,
396
+ message: `Maximum ${field.maxFiles} file(s) allowed`
397
+ });
398
+ }
399
+ }
400
+ });
401
+ if (!field.required) {
402
+ return schema2.optional().default([]);
403
+ }
404
+ return schema2;
405
+ }
406
+ const schema = z7.any().superRefine((value, ctx) => {
407
+ if (field.required && (value === null || value === void 0 || value === "")) {
408
+ ctx.addIssue({
409
+ code: z7.ZodIssueCode.custom,
410
+ message: "File is required"
411
+ });
412
+ return;
413
+ }
414
+ if (value === null || value === void 0 || value === "") {
415
+ return;
416
+ }
417
+ const result = validateSingleFile(value, field);
418
+ if (!result.valid) {
419
+ ctx.addIssue({
420
+ code: z7.ZodIssueCode.custom,
421
+ message: result.message || "Invalid file"
422
+ });
423
+ }
424
+ });
425
+ if (!field.required) {
426
+ return schema.optional().nullable();
427
+ }
428
+ return schema;
429
+ }
430
+
431
+ // src/schema/builders/tags.ts
432
+ import { z as z8 } from "zod";
433
+ function createTagsFieldSchema(field) {
434
+ const tagSchema = z8.string();
435
+ let schema = z8.array(tagSchema);
436
+ if (field.minTags !== void 0) {
437
+ schema = schema.min(field.minTags, `At least ${field.minTags} tag(s) required`);
438
+ }
439
+ if (field.maxTags !== void 0) {
440
+ schema = schema.max(field.maxTags, `Maximum ${field.maxTags} tag(s) allowed`);
441
+ }
442
+ let finalSchema = applyCustomValidation(schema, field, field.name);
443
+ if (field.required) {
444
+ finalSchema = finalSchema.refine(
445
+ (arr) => Array.isArray(arr) && arr.length > 0,
446
+ "At least one tag is required"
447
+ );
448
+ return finalSchema;
449
+ }
450
+ return finalSchema.optional().default([]);
451
+ }
452
+
453
+ // src/schema/builders/composite.ts
454
+ import { z as z9 } from "zod";
455
+ function createArrayFieldSchema(field, fieldsToZodSchema2) {
456
+ const itemSchema = fieldsToZodSchema2(field.fields);
457
+ let schema = z9.array(itemSchema);
458
+ if (field.minRows !== void 0) {
459
+ schema = schema.min(
460
+ field.minRows,
461
+ `At least ${field.minRows} row${field.minRows !== 1 ? "s" : ""} required`
462
+ );
463
+ }
464
+ if (field.maxRows !== void 0) {
465
+ schema = schema.max(
466
+ field.maxRows,
467
+ `Maximum ${field.maxRows} row${field.maxRows !== 1 ? "s" : ""} allowed`
468
+ );
469
+ }
470
+ if (field.required) {
471
+ return schema;
472
+ }
473
+ return schema.optional().default([]);
474
+ }
475
+ function createGroupFieldSchema(field, fieldsToZodSchema2) {
476
+ const schema = fieldsToZodSchema2(field.fields);
477
+ if (!field.required) {
478
+ return schema.optional();
479
+ }
480
+ return schema;
481
+ }
482
+
483
+ // src/schema/fields-to-schema.ts
484
+ function fieldToZod(field) {
485
+ if ("schema" in field && field.schema) {
486
+ return field.schema;
487
+ }
488
+ switch (field.type) {
489
+ // Text-based fields
490
+ case "text":
491
+ return createTextFieldSchema(field);
492
+ case "email":
493
+ return createEmailFieldSchema(field);
494
+ case "password":
495
+ return createPasswordFieldSchema(field);
496
+ case "textarea":
497
+ return createTextFieldSchema(field);
498
+ // Number
499
+ case "number":
500
+ return createNumberFieldSchema(field);
501
+ // Date
502
+ case "date":
503
+ case "datetime":
504
+ return createDateFieldSchema(field);
505
+ // Selection
506
+ case "select":
507
+ return createSelectFieldSchema(field);
508
+ case "radio":
509
+ return createRadioFieldSchema(field);
510
+ // Boolean
511
+ case "checkbox":
512
+ return createCheckboxFieldSchema(field);
513
+ case "switch":
514
+ return createSwitchFieldSchema(field);
515
+ // Upload
516
+ case "upload":
517
+ return createUploadFieldSchema(field);
518
+ // Tags
519
+ case "tags":
520
+ return createTagsFieldSchema(field);
521
+ // Composite (recursive)
522
+ case "array":
523
+ return createArrayFieldSchema(field, fieldsToZodSchema);
524
+ case "group":
525
+ return createGroupFieldSchema(field, fieldsToZodSchema);
526
+ // Layout fields don't produce schemas directly
527
+ case "row":
528
+ case "collapsible":
529
+ case "tabs":
530
+ return z10.any();
531
+ default:
532
+ return z10.any();
533
+ }
534
+ }
535
+ function processTabsField(field) {
536
+ const shape = {};
537
+ for (const tab of field.tabs) {
538
+ if (tab.name) {
539
+ const tabSchema = fieldsToZodSchema(tab.fields);
540
+ shape[tab.name] = tabSchema;
541
+ } else {
542
+ const tabFieldsSchema = fieldsToZodSchema(tab.fields);
543
+ if (tabFieldsSchema instanceof z10.ZodObject) {
544
+ Object.assign(shape, tabFieldsSchema.shape);
545
+ }
546
+ }
547
+ }
548
+ return shape;
549
+ }
550
+ function fieldsToZodSchema(fields) {
551
+ const shape = {};
552
+ for (const field of fields) {
553
+ if ("name" in field && field.name) {
554
+ shape[field.name] = fieldToZod(field);
555
+ } else if (field.type === "tabs") {
556
+ const tabsShape = processTabsField(field);
557
+ Object.assign(shape, tabsShape);
558
+ } else if (field.type === "row" || field.type === "collapsible") {
559
+ const nestedSchema = fieldsToZodSchema(field.fields);
560
+ if (nestedSchema instanceof z10.ZodObject) {
561
+ Object.assign(shape, nestedSchema.shape);
562
+ }
563
+ }
564
+ }
565
+ return z10.object(shape);
566
+ }
567
+
568
+ // src/schema/create-schema.ts
569
+ function createSchema(fields) {
570
+ const schema = fieldsToZodSchema(fields);
571
+ return Object.assign(schema, { fields });
572
+ }
573
+
574
+ export {
575
+ extractValidationConfig,
576
+ applyCustomValidation,
577
+ makeOptional,
578
+ coerceToNumber,
579
+ coerceToDate,
580
+ getPatternErrorMessage,
581
+ isFileLike,
582
+ isFileTypeAccepted,
583
+ createTextFieldSchema,
584
+ createEmailFieldSchema,
585
+ createPasswordFieldSchema,
586
+ createNumberFieldSchema,
587
+ createDateFieldSchema,
588
+ createSelectFieldSchema,
589
+ createRadioFieldSchema,
590
+ createCheckboxFieldSchema,
591
+ createSwitchFieldSchema,
592
+ createUploadFieldSchema,
593
+ createTagsFieldSchema,
594
+ createArrayFieldSchema,
595
+ createGroupFieldSchema,
596
+ fieldsToZodSchema,
597
+ createSchema
598
+ };
599
+ //# sourceMappingURL=chunk-K42S5YX3.mjs.map