@betterstart/cli 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,906 @@
1
+ import {
2
+ quotePropertyName,
3
+ singularize,
4
+ toPascalCase
5
+ } from "../chunk-GEH43BA4.js";
6
+ import "../chunk-PWRI4LKM.js";
7
+ import {
8
+ CodegenError,
9
+ ConfigurationError,
10
+ ERROR_CODES,
11
+ FileOperationError,
12
+ GeneratorError,
13
+ MissingFieldError,
14
+ PluginError,
15
+ SchemaValidationError,
16
+ UnsupportedFieldTypeError,
17
+ createBufferedLogger,
18
+ createConsoleLogger,
19
+ createSilentLogger,
20
+ getLogger,
21
+ isCodegenError,
22
+ logger,
23
+ resetLogger,
24
+ setLogger,
25
+ wrapError
26
+ } from "../chunk-WY6BC55D.js";
27
+
28
+ // src/core/constants.ts
29
+ var FIELD_TYPES = {
30
+ // Primitives
31
+ STRING: "string",
32
+ NUMBER: "number",
33
+ BOOLEAN: "boolean",
34
+ TEXT: "text",
35
+ DECIMAL: "decimal",
36
+ VARCHAR: "varchar",
37
+ SERIAL: "serial",
38
+ // Date/Time
39
+ DATE: "date",
40
+ TIMESTAMP: "timestamp",
41
+ TIME: "time",
42
+ // Rich content
43
+ MARKDOWN: "markdown",
44
+ IMAGE: "image",
45
+ VIDEO: "video",
46
+ MEDIA: "media",
47
+ ICON: "icon",
48
+ // Structure
49
+ GROUP: "group",
50
+ TABS: "tabs",
51
+ LIST: "list",
52
+ SEPARATOR: "separator",
53
+ // Relations
54
+ SELECT: "select",
55
+ RELATIONSHIP: "relationship",
56
+ // Complex content types
57
+ CURRICULUM: "curriculum"
58
+ };
59
+ var NESTED_FIELD_TYPES = [FIELD_TYPES.GROUP, FIELD_TYPES.TABS, FIELD_TYPES.LIST];
60
+ var RICH_TEXT_FIELD_TYPES = [FIELD_TYPES.MARKDOWN, FIELD_TYPES.TEXT];
61
+ var LONG_TEXT_FIELD_TYPES = [
62
+ FIELD_TYPES.TEXT,
63
+ FIELD_TYPES.MARKDOWN,
64
+ FIELD_TYPES.LIST,
65
+ FIELD_TYPES.MEDIA,
66
+ FIELD_TYPES.VIDEO,
67
+ FIELD_TYPES.CURRICULUM
68
+ ];
69
+ var COLUMN_TYPES = {
70
+ TEXT: "text",
71
+ BADGE: "badge",
72
+ DATE: "date",
73
+ CUSTOM: "custom",
74
+ AVATAR: "avatar",
75
+ LINK: "link",
76
+ IMAGE: "image",
77
+ EMAIL: "email",
78
+ NUMBER: "number",
79
+ BOOLEAN: "boolean"
80
+ };
81
+ var FORM_FIELD_TYPES = {
82
+ TEXT: "text",
83
+ TEXTAREA: "textarea",
84
+ EMAIL: "email",
85
+ PHONE: "phone",
86
+ NUMBER: "number",
87
+ URL: "url",
88
+ DATE: "date",
89
+ SELECT: "select",
90
+ RADIO: "radio",
91
+ CHECKBOX: "checkbox",
92
+ MULTISELECT: "multiselect",
93
+ FILE: "file",
94
+ UPLOAD: "upload",
95
+ GROUP: "group",
96
+ TIMEZONE: "timezone",
97
+ LIST: "list"
98
+ };
99
+ var AUTO_FIELDS = {
100
+ ID: "id",
101
+ CREATED_AT: "createdAt",
102
+ UPDATED_AT: "updatedAt",
103
+ PUBLISHED: "published",
104
+ SLUG: "slug"
105
+ };
106
+ var FORM_SUBMISSION_FIELDS = {
107
+ SUBMITTED_AT: "submittedAt",
108
+ IP_ADDRESS: "ipAddress",
109
+ USER_AGENT: "userAgent"
110
+ };
111
+ var DRIZZLE_DEFAULTS = {
112
+ VARCHAR_LENGTH: 255,
113
+ DECIMAL_PRECISION: 10,
114
+ DECIMAL_SCALE: 2
115
+ };
116
+ var CORE_GENERATORS = [
117
+ "database",
118
+ "actions",
119
+ "hooks",
120
+ "columns",
121
+ "table",
122
+ "page",
123
+ "page-content",
124
+ "form",
125
+ "create-page",
126
+ "edit-page",
127
+ "navigation"
128
+ ];
129
+ var OPTIONAL_GENERATORS = [];
130
+ var FILE_EXTENSIONS = {
131
+ TYPESCRIPT: ".ts",
132
+ TSX: ".tsx",
133
+ JSON: ".json",
134
+ SQL: ".sql"
135
+ };
136
+ function isFieldType(value) {
137
+ return Object.values(FIELD_TYPES).includes(value);
138
+ }
139
+ function isNestedFieldType(type) {
140
+ return NESTED_FIELD_TYPES.includes(type);
141
+ }
142
+ function isLongTextFieldType(type) {
143
+ return LONG_TEXT_FIELD_TYPES.includes(type);
144
+ }
145
+
146
+ // src/core/field-helpers.ts
147
+ var DEFAULT_WALK_OPTIONS = {
148
+ includeLists: true,
149
+ includeGroups: true,
150
+ includeTabs: true
151
+ };
152
+ function walkFields(fields, callback, options = DEFAULT_WALK_OPTIONS) {
153
+ function walk(fieldsToWalk, depth, parent) {
154
+ for (const field of fieldsToWalk) {
155
+ callback(field, depth, parent);
156
+ if (options.includeGroups && field.type === "group" && field.fields) {
157
+ walk(field.fields, depth + 1, field);
158
+ }
159
+ if (options.includeLists && field.type === "list" && field.fields) {
160
+ walk(field.fields, depth + 1, field);
161
+ }
162
+ if (options.includeTabs && field.type === "tabs" && field.tabs) {
163
+ for (const tab of field.tabs) {
164
+ if (tab.fields) {
165
+ walk(tab.fields, depth + 1, field);
166
+ }
167
+ }
168
+ }
169
+ }
170
+ }
171
+ walk(fields, 0);
172
+ }
173
+ function hasFieldType(fields, type, options) {
174
+ let found = false;
175
+ walkFields(
176
+ fields,
177
+ (field) => {
178
+ if (field.type === type) {
179
+ found = true;
180
+ }
181
+ },
182
+ options
183
+ );
184
+ return found;
185
+ }
186
+ function hasAnyFieldType(fields, types, options) {
187
+ let found = false;
188
+ walkFields(
189
+ fields,
190
+ (field) => {
191
+ if (types.includes(field.type)) {
192
+ found = true;
193
+ }
194
+ },
195
+ options
196
+ );
197
+ return found;
198
+ }
199
+ function hasIconUsage(fields, options) {
200
+ let found = false;
201
+ walkFields(
202
+ fields,
203
+ (field) => {
204
+ if (field.type === "icon" || field.hasIcon) {
205
+ found = true;
206
+ }
207
+ },
208
+ options
209
+ );
210
+ return found;
211
+ }
212
+ function hasRelationshipField(fields, options) {
213
+ return hasFieldType(fields, FIELD_TYPES.RELATIONSHIP, options);
214
+ }
215
+ function hasMarkdownField(fields, options) {
216
+ return hasFieldType(fields, FIELD_TYPES.MARKDOWN, options);
217
+ }
218
+ function hasTextareaField(fields, options) {
219
+ return hasFieldType(fields, FIELD_TYPES.TEXT, options);
220
+ }
221
+ function collectFieldsByType(fields, type, options) {
222
+ const result = [];
223
+ walkFields(
224
+ fields,
225
+ (field) => {
226
+ if (field.type === type) {
227
+ result.push(field);
228
+ }
229
+ },
230
+ options
231
+ );
232
+ return result;
233
+ }
234
+ function getManyToManyFields(fields) {
235
+ const flat = flattenFields(fields);
236
+ return flat.filter((f) => f.type === "relationship" && f.multiple === true && f.relationship);
237
+ }
238
+ function getNestedRelationshipFields(fields, options) {
239
+ const result = [];
240
+ walkFields(
241
+ fields,
242
+ (field) => {
243
+ if (field.type === "relationship" && field.relationship && !field.multiple) {
244
+ result.push(field);
245
+ }
246
+ },
247
+ options
248
+ );
249
+ return result;
250
+ }
251
+ function collectRelationshipFieldsTopLevel(fields) {
252
+ const result = [];
253
+ function collect(fieldsToCheck) {
254
+ for (const f of fieldsToCheck) {
255
+ if (f.type === "relationship" && f.relationship) {
256
+ result.push(f);
257
+ }
258
+ if (f.type === "group" && f.fields) {
259
+ collect(f.fields);
260
+ }
261
+ }
262
+ }
263
+ collect(fields);
264
+ return result;
265
+ }
266
+ function collectAllRelationshipFields(fields) {
267
+ return collectFieldsByType(fields, FIELD_TYPES.RELATIONSHIP);
268
+ }
269
+ function getListFieldsWithNestedFields(fields) {
270
+ const result = [];
271
+ function collect(fieldsToCheck) {
272
+ for (const f of fieldsToCheck) {
273
+ if (f.type === "list" && f.fields && f.fields.length > 0) {
274
+ result.push(f);
275
+ }
276
+ if (f.type === "group" && f.fields) {
277
+ collect(f.fields);
278
+ }
279
+ }
280
+ }
281
+ collect(fields);
282
+ return result;
283
+ }
284
+ function getNestedListFields(fields) {
285
+ const result = [];
286
+ const listFields = getListFieldsWithNestedFields(fields);
287
+ for (const parent of listFields) {
288
+ if (parent.fields) {
289
+ for (const nested of parent.fields) {
290
+ if (nested.type === "list" && nested.fields && nested.fields.length > 0) {
291
+ result.push({ parent, nested });
292
+ }
293
+ }
294
+ }
295
+ }
296
+ return result;
297
+ }
298
+ var AUTO_ID_FIELD = {
299
+ name: "id",
300
+ type: "serial",
301
+ primaryKey: true
302
+ };
303
+ function ensureIdField(fields) {
304
+ const hasIdField = fields.some((f) => f.primaryKey || f.name === "id");
305
+ if (hasIdField) {
306
+ return fields;
307
+ }
308
+ return [AUTO_ID_FIELD, ...fields];
309
+ }
310
+ function flattenFields(fields) {
311
+ const fieldsWithId = ensureIdField(fields);
312
+ return flattenFieldsWithoutIdCheck(fieldsWithId);
313
+ }
314
+ function flattenFieldsWithoutIdCheck(fields) {
315
+ const flattened = [];
316
+ for (const field of fields) {
317
+ if (field.type === "group" && field.fields) {
318
+ flattened.push(...flattenFieldsWithoutIdCheck(field.fields));
319
+ } else if (field.type === "tabs" && field.tabs) {
320
+ for (const tab of field.tabs) {
321
+ if (tab.fields) {
322
+ flattened.push(...flattenFieldsWithoutIdCheck(tab.fields));
323
+ }
324
+ }
325
+ } else {
326
+ flattened.push(field);
327
+ }
328
+ }
329
+ return flattened;
330
+ }
331
+ function getAllFormFields(schema) {
332
+ const allFields = [];
333
+ if (schema.steps) {
334
+ for (const step of schema.steps) {
335
+ for (const field of step.fields) {
336
+ if (field.type === "group" && field.fields) {
337
+ for (const nestedField of field.fields) {
338
+ allFields.push({
339
+ ...nestedField,
340
+ condition: nestedField.condition || field.condition
341
+ });
342
+ }
343
+ } else {
344
+ allFields.push(field);
345
+ }
346
+ }
347
+ }
348
+ } else if (schema.fields) {
349
+ for (const field of schema.fields) {
350
+ if (field.type === "group" && field.fields) {
351
+ for (const nestedField of field.fields) {
352
+ allFields.push({
353
+ ...nestedField,
354
+ condition: nestedField.condition || field.condition
355
+ });
356
+ }
357
+ } else {
358
+ allFields.push(field);
359
+ }
360
+ }
361
+ }
362
+ return allFields;
363
+ }
364
+ function isMultiStepForm(schema) {
365
+ return Array.isArray(schema.steps) && schema.steps.length > 0;
366
+ }
367
+ function analyzeFields(fields) {
368
+ const fieldTypes = /* @__PURE__ */ new Set();
369
+ let hasIcon = false;
370
+ let hasNestedList = false;
371
+ walkFields(fields, (field) => {
372
+ fieldTypes.add(field.type);
373
+ if (field.type === "icon" || field.hasIcon) {
374
+ hasIcon = true;
375
+ }
376
+ });
377
+ const listFields = getListFieldsWithNestedFields(fields);
378
+ for (const listField of listFields) {
379
+ if (listField.fields?.some((f) => f.type === "list")) {
380
+ hasNestedList = true;
381
+ break;
382
+ }
383
+ }
384
+ return {
385
+ hasBoolean: fieldTypes.has(FIELD_TYPES.BOOLEAN),
386
+ hasImage: fieldTypes.has(FIELD_TYPES.IMAGE),
387
+ hasVideo: fieldTypes.has(FIELD_TYPES.VIDEO),
388
+ hasMedia: fieldTypes.has(FIELD_TYPES.MEDIA),
389
+ hasDate: fieldTypes.has(FIELD_TYPES.DATE),
390
+ hasList: fieldTypes.has(FIELD_TYPES.LIST),
391
+ hasNestedList,
392
+ hasSelect: fieldTypes.has(FIELD_TYPES.SELECT),
393
+ hasMarkdown: fieldTypes.has(FIELD_TYPES.MARKDOWN),
394
+ hasTextarea: fieldTypes.has(FIELD_TYPES.TEXT),
395
+ hasSeparator: fieldTypes.has(FIELD_TYPES.SEPARATOR),
396
+ hasIcon,
397
+ hasRelationship: fieldTypes.has(FIELD_TYPES.RELATIONSHIP),
398
+ hasManyToMany: getManyToManyFields(fields).length > 0,
399
+ fieldTypes
400
+ };
401
+ }
402
+
403
+ // src/core/type-mappers.ts
404
+ function isUrlField(fieldName) {
405
+ const urlKeywords = ["url", "link", "href", "website", "avatar", "logo", "thumbnail"];
406
+ const lowerName = fieldName.toLowerCase();
407
+ return urlKeywords.some((keyword) => lowerName.includes(keyword));
408
+ }
409
+ function toDrizzleType(field, requiredImports) {
410
+ switch (field.type) {
411
+ case "serial":
412
+ requiredImports.add("serial");
413
+ return "serial()";
414
+ case "string":
415
+ case "varchar":
416
+ if (isUrlField(field.name) && !field.length) {
417
+ requiredImports.add("text");
418
+ return "text()";
419
+ }
420
+ requiredImports.add("varchar");
421
+ return field.length ? `varchar({ length: ${field.length} })` : `varchar({ length: ${DRIZZLE_DEFAULTS.VARCHAR_LENGTH} })`;
422
+ case "image":
423
+ case "video":
424
+ case "media":
425
+ requiredImports.add("text");
426
+ return "text()";
427
+ case "text":
428
+ case "markdown":
429
+ case "richtext":
430
+ requiredImports.add("text");
431
+ return "text()";
432
+ case "number":
433
+ requiredImports.add("integer");
434
+ return "integer()";
435
+ case "decimal":
436
+ requiredImports.add("decimal");
437
+ if (field.precision && field.scale) {
438
+ return `decimal({ precision: ${field.precision}, scale: ${field.scale} })`;
439
+ }
440
+ return `decimal({ precision: ${DRIZZLE_DEFAULTS.DECIMAL_PRECISION}, scale: ${DRIZZLE_DEFAULTS.DECIMAL_SCALE} })`;
441
+ case "boolean":
442
+ requiredImports.add("boolean");
443
+ return "boolean()";
444
+ case "timestamp":
445
+ requiredImports.add("timestamp");
446
+ return "timestamp({ precision: 3, mode: 'string' })";
447
+ case "date":
448
+ requiredImports.add("date");
449
+ return "date({ mode: 'string' })";
450
+ case "time":
451
+ case "select":
452
+ case "icon":
453
+ requiredImports.add("varchar");
454
+ return field.length ? `varchar({ length: ${field.length} })` : `varchar({ length: ${DRIZZLE_DEFAULTS.VARCHAR_LENGTH} })`;
455
+ case "curriculum":
456
+ requiredImports.add("jsonb");
457
+ return `jsonb().$type<{ mode: 'sequential'; items: Array<{ title?: string; description?: string }> } | { mode: 'weekly'; weeks: Array<{ weekNumber: number; weekTitle?: string; weekDescription?: string; durationHours?: number; items: Array<{ title?: string; description?: string }> }> }>()`;
458
+ case "list":
459
+ requiredImports.add("jsonb");
460
+ if (field.fields && field.fields.length > 0) {
461
+ const nestedType = buildNestedListType(field.fields);
462
+ return `jsonb().$type<Array<{ ${nestedType} }>>()`;
463
+ }
464
+ return "jsonb().$type<string[]>()";
465
+ case "relationship":
466
+ requiredImports.add("integer");
467
+ return "integer()";
468
+ default:
469
+ requiredImports.add("text");
470
+ return "text()";
471
+ }
472
+ }
473
+ function buildNestedListType(fields) {
474
+ return fields.flatMap((f) => {
475
+ let type;
476
+ if (f.type === "list" && f.fields && f.fields.length > 0) {
477
+ const innerType = f.fields.map((nf) => {
478
+ let nfType;
479
+ if (nf.type === "boolean") {
480
+ nfType = "boolean";
481
+ } else if (nf.type === "number" || nf.type === "decimal") {
482
+ nfType = "number";
483
+ } else {
484
+ nfType = "string";
485
+ }
486
+ return `${quotePropertyName(nf.name)}?: ${nfType}`;
487
+ }).join("; ");
488
+ type = `Array<{ ${innerType} }>`;
489
+ } else if (f.type === "list") {
490
+ type = "string[]";
491
+ } else if (f.type === "number" || f.type === "decimal") {
492
+ type = "number";
493
+ } else if (f.type === "boolean") {
494
+ type = "boolean";
495
+ } else {
496
+ type = "string";
497
+ }
498
+ const result = [`${quotePropertyName(f.name)}?: ${type}`];
499
+ if (f.hasIcon) {
500
+ result.push(`${quotePropertyName(`${f.name}Icon`)}?: string`);
501
+ }
502
+ return result;
503
+ }).join("; ");
504
+ }
505
+ function toZodType(field) {
506
+ const label = field.label || field.name;
507
+ switch (field.type) {
508
+ case "serial":
509
+ case "number":
510
+ case "decimal":
511
+ return field.required ? `z.number({ message: '${label} is required' })` : "z.number().optional()";
512
+ case "boolean":
513
+ return "z.boolean()";
514
+ case "string":
515
+ case "varchar":
516
+ case "text":
517
+ case "markdown":
518
+ case "richtext": {
519
+ if (field.name.toLowerCase().includes("email")) {
520
+ const base2 = field.required ? `z.string().min(1, '${label} is required').email('Please enter a valid email address')` : `z.string().email('Please enter a valid email address').optional()`;
521
+ return field.length ? `${base2}.max(${field.length})` : base2;
522
+ }
523
+ const base = field.required ? `z.string().min(1, '${label} is required')` : "z.string()";
524
+ return field.length ? `${base}.max(${field.length})` : base;
525
+ }
526
+ case "date":
527
+ if (!field.required) {
528
+ return 'z.string().transform(val => val === "" ? undefined : val).optional()';
529
+ }
530
+ return `z.string().min(1, '${label} is required')`;
531
+ case "timestamp":
532
+ return field.required ? `z.string().min(1, '${label} is required')` : "z.string().optional()";
533
+ case "image":
534
+ case "video":
535
+ case "media": {
536
+ if (!field.required) {
537
+ return 'z.string().transform(val => val === "" ? undefined : val).optional()';
538
+ }
539
+ const mediaType = field.type === "image" ? "an image" : field.type === "video" ? "a video" : "media";
540
+ const base = `z.string().min(1, 'Please upload ${mediaType} for ${label}').url('Please provide a valid ${field.type} URL')`;
541
+ return field.length ? `${base}.max(${field.length})` : base;
542
+ }
543
+ case "list":
544
+ return buildZodListType(field, label);
545
+ case "select": {
546
+ if (field.options && field.options.length > 0) {
547
+ const values = field.options.map((opt) => `'${opt.value}'`).join(", ");
548
+ const valuesArray = `[${values}] as const`;
549
+ return field.required ? `z.string().min(1, '${label} is required').refine((val) => (${valuesArray} as readonly string[]).includes(val), { message: 'Invalid ${label}' })` : `z.string().refine((val) => !val || (${valuesArray} as readonly string[]).includes(val), { message: 'Invalid ${label}' }).optional()`;
550
+ }
551
+ return field.required ? `z.string().min(1, '${label} is required')` : "z.string().optional()";
552
+ }
553
+ case "icon":
554
+ return field.required ? `z.string().min(1, '${label} is required')` : "z.string().optional()";
555
+ case "relationship": {
556
+ if (field.multiple) {
557
+ return field.required ? `z.array(z.number()).min(1, '${label} is required')` : "z.array(z.number()).optional()";
558
+ }
559
+ return field.required ? `z.string().min(1, '${label} is required')` : "z.string().optional()";
560
+ }
561
+ case "curriculum":
562
+ return `z.object({
563
+ mode: z.enum(['sequential', 'weekly']),
564
+ items: z.array(z.object({
565
+ title: z.string().max(255).optional(),
566
+ description: z.string().max(500).optional()
567
+ })).max(50),
568
+ weeks: z.array(z.object({
569
+ weekNumber: z.number().int().positive(),
570
+ weekTitle: z.string().max(100).optional(),
571
+ weekDescription: z.string().max(1000).optional(),
572
+ durationHours: z.number().positive().optional(),
573
+ items: z.array(z.object({
574
+ title: z.string().max(255).optional(),
575
+ description: z.string().max(500).optional()
576
+ })).max(20)
577
+ })).max(20)
578
+ }).optional()`;
579
+ default:
580
+ return field.required ? `z.string().min(1, '${label} is required')` : "z.string().optional()";
581
+ }
582
+ }
583
+ function buildZodListType(field, label) {
584
+ if (field.fields && field.fields.length > 0) {
585
+ const nestedFields = field.fields.flatMap((nestedField) => {
586
+ const defs = [];
587
+ const nestedZodType = toZodType(nestedField);
588
+ const alreadyOptional = nestedZodType.includes(".optional()");
589
+ let nestedDef = ` ${quotePropertyName(nestedField.name)}: ${nestedZodType}`;
590
+ if (!nestedField.required && !alreadyOptional) {
591
+ nestedDef += ".optional()";
592
+ }
593
+ defs.push(nestedDef);
594
+ if (nestedField.hasIcon) {
595
+ defs.push(` ${quotePropertyName(`${nestedField.name}Icon`)}: z.string().optional()`);
596
+ }
597
+ return defs;
598
+ }).join(",\n");
599
+ const objectSchema = `z.object({
600
+ ${nestedFields}
601
+ })`;
602
+ const arraySchema = field.maxItems ? `z.array(${objectSchema}).max(${field.maxItems}, '${label} cannot exceed ${field.maxItems} items')` : `z.array(${objectSchema})`;
603
+ return field.required ? `${arraySchema}.min(1, '${label} must have at least one item')` : `${arraySchema}.optional()`;
604
+ }
605
+ if (field.items?.type === "string" || field.items?.type === "varchar") {
606
+ const itemSchema = field.items.length ? `z.string().max(${field.items.length})` : "z.string()";
607
+ const arraySchema = field.maxItems ? `z.array(${itemSchema}).max(${field.maxItems}, '${label} cannot exceed ${field.maxItems} items')` : `z.array(${itemSchema})`;
608
+ return field.required ? `${arraySchema}.min(1, '${label} must have at least one item')` : `${arraySchema}.optional()`;
609
+ }
610
+ return field.required ? `z.array(z.string()).min(1, '${label} must have at least one item')` : "z.array(z.string()).optional()";
611
+ }
612
+ function toTypeScriptType(field, mode = "output") {
613
+ switch (field.type) {
614
+ case "serial":
615
+ case "number":
616
+ case "decimal":
617
+ return "number";
618
+ case "boolean":
619
+ return "boolean";
620
+ case "string":
621
+ case "varchar":
622
+ case "text":
623
+ case "markdown":
624
+ case "richtext":
625
+ case "date":
626
+ case "time":
627
+ case "timestamp":
628
+ case "image":
629
+ case "video":
630
+ case "media":
631
+ case "icon":
632
+ case "select":
633
+ return "string";
634
+ case "relationship":
635
+ if (mode === "input") {
636
+ return field.multiple ? "number[]" : "string";
637
+ }
638
+ if (field.relationship) {
639
+ const relationshipSingular = singularize(field.relationship);
640
+ const relationshipPascal = toPascalCase(relationshipSingular);
641
+ return field.multiple ? `${relationshipPascal}Data[]` : `${relationshipPascal}Data | null`;
642
+ }
643
+ return "string";
644
+ case "list":
645
+ if (field.fields && field.fields.length > 0) {
646
+ const nestedType = field.fields.flatMap((f) => {
647
+ const type = toTypeScriptType(f, "input");
648
+ const fields = [`${quotePropertyName(f.name)}?: ${type}`];
649
+ if (f.hasIcon) {
650
+ fields.push(`${quotePropertyName(`${f.name}Icon`)}?: string`);
651
+ }
652
+ return fields;
653
+ }).join("; ");
654
+ return `Array<{ ${nestedType} }>`;
655
+ }
656
+ return "string[]";
657
+ case "curriculum":
658
+ return '{ mode: "sequential" | "weekly"; items: Array<{ title?: string; description?: string }>; weeks: Array<{ weekNumber: number; weekTitle?: string; weekDescription?: string; durationHours?: number; items: Array<{ title?: string; description?: string }> }> }';
659
+ default:
660
+ return "string";
661
+ }
662
+ }
663
+ function toFormFieldType(field) {
664
+ switch (field.type) {
665
+ case "boolean":
666
+ return "checkbox";
667
+ case "text":
668
+ return "textarea";
669
+ case "markdown":
670
+ return "markdown";
671
+ case "richtext":
672
+ return "richtext";
673
+ case "number":
674
+ case "decimal":
675
+ return "number";
676
+ case "date":
677
+ return "date";
678
+ case "time":
679
+ return "time";
680
+ case "timestamp":
681
+ return "datetime-local";
682
+ default:
683
+ return "text";
684
+ }
685
+ }
686
+ function formFieldToDrizzleType(field, requiredImports) {
687
+ switch (field.type) {
688
+ case "text":
689
+ requiredImports.add("varchar");
690
+ return field.maxLength ? `varchar({ length: ${field.maxLength} })` : `varchar({ length: ${DRIZZLE_DEFAULTS.VARCHAR_LENGTH} })`;
691
+ case "textarea":
692
+ requiredImports.add("text");
693
+ return "text()";
694
+ case "email":
695
+ requiredImports.add("varchar");
696
+ return `varchar({ length: ${DRIZZLE_DEFAULTS.VARCHAR_LENGTH} })`;
697
+ case "phone":
698
+ requiredImports.add("varchar");
699
+ return "varchar({ length: 50 })";
700
+ case "number":
701
+ requiredImports.add("integer");
702
+ return "integer()";
703
+ case "url":
704
+ requiredImports.add("varchar");
705
+ return "varchar({ length: 500 })";
706
+ case "date":
707
+ requiredImports.add("date");
708
+ return "date({ mode: 'string' })";
709
+ case "select":
710
+ case "radio":
711
+ case "timezone":
712
+ requiredImports.add("varchar");
713
+ return `varchar({ length: ${DRIZZLE_DEFAULTS.VARCHAR_LENGTH} })`;
714
+ case "checkbox":
715
+ requiredImports.add("boolean");
716
+ return "boolean()";
717
+ case "multiselect":
718
+ requiredImports.add("jsonb");
719
+ return "jsonb().$type<string[]>()";
720
+ case "list":
721
+ requiredImports.add("jsonb");
722
+ if (field.fields && field.fields.length > 0) {
723
+ return "jsonb().$type<Record<string, unknown>[]>()";
724
+ }
725
+ return "jsonb().$type<string[]>()";
726
+ case "file":
727
+ case "upload":
728
+ requiredImports.add("text");
729
+ return "text()";
730
+ default:
731
+ requiredImports.add("text");
732
+ return "text()";
733
+ }
734
+ }
735
+ function formFieldToZodType(field, options = {}) {
736
+ const label = field.label;
737
+ const isRequired = field.required && !options.treatAsOptional;
738
+ switch (field.type) {
739
+ case "text": {
740
+ let zodType = isRequired ? `z.string().min(1, '${label} is required')` : "z.string()";
741
+ if (field.minLength) {
742
+ zodType += `.min(${field.minLength}, '${label} must be at least ${field.minLength} characters')`;
743
+ }
744
+ if (field.maxLength) {
745
+ zodType += `.max(${field.maxLength}, '${label} must be at most ${field.maxLength} characters')`;
746
+ }
747
+ if (field.pattern) {
748
+ zodType += `.regex(new RegExp('${field.pattern}'), 'Invalid ${label} format')`;
749
+ }
750
+ return isRequired ? zodType : `${zodType}.optional()`;
751
+ }
752
+ case "textarea": {
753
+ let zodType = isRequired ? `z.string().min(1, '${label} is required')` : "z.string()";
754
+ if (field.minLength) {
755
+ zodType += `.min(${field.minLength}, '${label} must be at least ${field.minLength} characters')`;
756
+ }
757
+ if (field.maxLength) {
758
+ zodType += `.max(${field.maxLength}, '${label} must be at most ${field.maxLength} characters')`;
759
+ }
760
+ return isRequired ? zodType : `${zodType}.optional()`;
761
+ }
762
+ case "email":
763
+ return isRequired ? `z.string().min(1, '${label} is required').email('Please enter a valid email address')` : `z.string().optional().refine((val) => !val || val.trim() === '' || z.string().email().safeParse(val).success, { message: 'Please enter a valid email address' })`;
764
+ case "phone": {
765
+ const pattern = field.pattern || "^[+]?[(]?[0-9]{1,4}[)]?[-\\s\\.]?[(]?[0-9]{1,4}[)]?[-\\s\\.]?[0-9]{1,9}$";
766
+ return isRequired ? `z.string().min(1, '${label} is required').regex(new RegExp('${pattern}'), 'Please enter a valid phone number')` : `z.string().optional().refine((val) => !val || val.trim() === '' || new RegExp('${pattern}').test(val), { message: 'Please enter a valid phone number' })`;
767
+ }
768
+ case "number": {
769
+ let zodType = isRequired ? `z.number({ message: '${label} is required' })` : "z.number()";
770
+ if (field.min !== void 0) {
771
+ zodType += `.min(${field.min}, '${label} must be at least ${field.min}')`;
772
+ }
773
+ if (field.max !== void 0) {
774
+ zodType += `.max(${field.max}, '${label} must be at most ${field.max}')`;
775
+ }
776
+ return isRequired ? zodType : `${zodType}.optional()`;
777
+ }
778
+ case "url":
779
+ return isRequired ? `z.string().min(1, '${label} is required').url('Please enter a valid URL')` : `z.string().optional().refine((val) => !val || val.trim() === '' || z.string().url().safeParse(val).success, { message: 'Please enter a valid URL' })`;
780
+ case "date":
781
+ return isRequired ? `z.string().min(1, '${label} is required')` : 'z.string().transform(val => val === "" ? undefined : val).optional()';
782
+ case "select":
783
+ case "radio":
784
+ if (field.options && field.options.length > 0) {
785
+ const values = field.options.map((opt) => `'${opt.value}'`).join(", ");
786
+ return isRequired ? `z.enum([${values}], { message: '${label} is required' })` : `z.enum([${values}]).optional()`;
787
+ }
788
+ return isRequired ? `z.string().min(1, '${label} is required')` : "z.string().optional()";
789
+ case "checkbox":
790
+ return isRequired ? `z.literal(true, { message: '${label} is required' })` : "z.boolean().optional()";
791
+ case "multiselect":
792
+ return isRequired ? `z.array(z.string()).min(1, '${label} is required')` : "z.array(z.string()).optional()";
793
+ case "timezone":
794
+ return isRequired ? `z.string().min(1, '${label} is required')` : "z.string().optional()";
795
+ case "list":
796
+ if (field.fields && field.fields.length > 0) {
797
+ const nestedFields = field.fields.map((f) => `${f.name}: ${formFieldToZodType(f, options)}`).join(", ");
798
+ return isRequired ? `z.array(z.object({ ${nestedFields} })).min(1, '${label} is required')` : `z.array(z.object({ ${nestedFields} })).optional()`;
799
+ }
800
+ return isRequired ? `z.array(z.string()).min(1, '${label} is required')` : "z.array(z.string()).optional()";
801
+ case "file":
802
+ case "upload":
803
+ return isRequired ? `z.string().min(1, '${label} is required')` : "z.string().optional()";
804
+ default:
805
+ return isRequired ? `z.string().min(1, '${label} is required')` : "z.string().optional()";
806
+ }
807
+ }
808
+ function toSQLType(field) {
809
+ switch (field.type) {
810
+ case "serial":
811
+ return "SERIAL";
812
+ case "string":
813
+ case "varchar":
814
+ return field.length ? `VARCHAR(${field.length})` : `VARCHAR(${DRIZZLE_DEFAULTS.VARCHAR_LENGTH})`;
815
+ case "text":
816
+ case "markdown":
817
+ case "richtext":
818
+ case "image":
819
+ case "video":
820
+ case "media":
821
+ return "TEXT";
822
+ case "number":
823
+ return "INTEGER";
824
+ case "decimal":
825
+ return `DECIMAL(${field.precision || DRIZZLE_DEFAULTS.DECIMAL_PRECISION}, ${field.scale || DRIZZLE_DEFAULTS.DECIMAL_SCALE})`;
826
+ case "boolean":
827
+ return "BOOLEAN";
828
+ case "timestamp":
829
+ return "TIMESTAMP(3)";
830
+ case "date":
831
+ return "DATE";
832
+ case "time":
833
+ case "select":
834
+ case "icon":
835
+ return `VARCHAR(${DRIZZLE_DEFAULTS.VARCHAR_LENGTH})`;
836
+ case "list":
837
+ case "curriculum":
838
+ return "JSONB";
839
+ case "relationship":
840
+ return "INTEGER";
841
+ default:
842
+ return "TEXT";
843
+ }
844
+ }
845
+ export {
846
+ AUTO_FIELDS,
847
+ COLUMN_TYPES,
848
+ CORE_GENERATORS,
849
+ CodegenError,
850
+ ConfigurationError,
851
+ DRIZZLE_DEFAULTS,
852
+ ERROR_CODES,
853
+ FIELD_TYPES,
854
+ FILE_EXTENSIONS,
855
+ FORM_FIELD_TYPES,
856
+ FORM_SUBMISSION_FIELDS,
857
+ FileOperationError,
858
+ GeneratorError,
859
+ LONG_TEXT_FIELD_TYPES,
860
+ MissingFieldError,
861
+ NESTED_FIELD_TYPES,
862
+ OPTIONAL_GENERATORS,
863
+ PluginError,
864
+ RICH_TEXT_FIELD_TYPES,
865
+ SchemaValidationError,
866
+ UnsupportedFieldTypeError,
867
+ analyzeFields,
868
+ collectAllRelationshipFields,
869
+ collectFieldsByType,
870
+ collectRelationshipFieldsTopLevel,
871
+ createBufferedLogger,
872
+ createConsoleLogger,
873
+ createSilentLogger,
874
+ ensureIdField,
875
+ flattenFields,
876
+ formFieldToDrizzleType,
877
+ formFieldToZodType,
878
+ getAllFormFields,
879
+ getListFieldsWithNestedFields,
880
+ getLogger,
881
+ getManyToManyFields,
882
+ getNestedListFields,
883
+ getNestedRelationshipFields,
884
+ hasAnyFieldType,
885
+ hasFieldType,
886
+ hasIconUsage,
887
+ hasMarkdownField,
888
+ hasRelationshipField,
889
+ hasTextareaField,
890
+ isCodegenError,
891
+ isFieldType,
892
+ isLongTextFieldType,
893
+ isMultiStepForm,
894
+ isNestedFieldType,
895
+ logger,
896
+ resetLogger,
897
+ setLogger,
898
+ toDrizzleType,
899
+ toFormFieldType,
900
+ toSQLType,
901
+ toTypeScriptType,
902
+ toZodType,
903
+ walkFields,
904
+ wrapError
905
+ };
906
+ //# sourceMappingURL=index.js.map