@famgia/omnify-laravel 0.0.4 → 0.0.6

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,1106 @@
1
+ // src/migration/schema-builder.ts
2
+ var TYPE_METHOD_MAP = {
3
+ String: "string",
4
+ Int: "integer",
5
+ BigInt: "bigInteger",
6
+ Float: "double",
7
+ Boolean: "boolean",
8
+ Text: "text",
9
+ LongText: "longText",
10
+ Date: "date",
11
+ Time: "time",
12
+ Timestamp: "timestamp",
13
+ Json: "json",
14
+ Email: "string",
15
+ Password: "string",
16
+ File: "string",
17
+ MultiFile: "json",
18
+ Enum: "enum",
19
+ Select: "string",
20
+ Lookup: "unsignedBigInteger"
21
+ };
22
+ var PK_METHOD_MAP = {
23
+ Int: "increments",
24
+ BigInt: "bigIncrements",
25
+ Uuid: "uuid",
26
+ String: "string"
27
+ };
28
+ function toColumnName(propertyName) {
29
+ return propertyName.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
30
+ }
31
+ function toTableName(schemaName) {
32
+ const snakeCase = schemaName.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
33
+ if (snakeCase.endsWith("y")) {
34
+ return snakeCase.slice(0, -1) + "ies";
35
+ } else if (snakeCase.endsWith("s") || snakeCase.endsWith("x") || snakeCase.endsWith("ch") || snakeCase.endsWith("sh")) {
36
+ return snakeCase + "es";
37
+ } else {
38
+ return snakeCase + "s";
39
+ }
40
+ }
41
+ function propertyToColumnMethod(propertyName, property) {
42
+ if (property.type === "Association") {
43
+ return null;
44
+ }
45
+ const columnName = toColumnName(propertyName);
46
+ const method = TYPE_METHOD_MAP[property.type] ?? "string";
47
+ const args = [columnName];
48
+ const modifiers = [];
49
+ const propWithLength = property;
50
+ if (method === "string" && propWithLength.length) {
51
+ args.push(propWithLength.length);
52
+ }
53
+ if (property.type === "Enum") {
54
+ const enumProp = property;
55
+ if (enumProp.enum && enumProp.enum.length > 0) {
56
+ args.push(enumProp.enum);
57
+ }
58
+ }
59
+ const baseProp = property;
60
+ if (baseProp.nullable) {
61
+ modifiers.push({ method: "nullable" });
62
+ }
63
+ if (baseProp.unique) {
64
+ modifiers.push({ method: "unique" });
65
+ }
66
+ if (baseProp.default !== void 0) {
67
+ const defaultValue = typeof baseProp.default === "string" ? baseProp.default : JSON.stringify(baseProp.default);
68
+ modifiers.push({ method: "default", args: [defaultValue] });
69
+ }
70
+ if (baseProp.unsigned && (method === "integer" || method === "bigInteger")) {
71
+ modifiers.push({ method: "unsigned" });
72
+ }
73
+ return {
74
+ name: columnName,
75
+ method,
76
+ args,
77
+ modifiers
78
+ };
79
+ }
80
+ function generatePrimaryKeyColumn(pkType = "BigInt") {
81
+ const method = PK_METHOD_MAP[pkType] ?? "bigIncrements";
82
+ if (pkType === "Uuid") {
83
+ return {
84
+ name: "id",
85
+ method: "uuid",
86
+ args: ["id"],
87
+ modifiers: [{ method: "primary" }]
88
+ };
89
+ }
90
+ if (pkType === "String") {
91
+ return {
92
+ name: "id",
93
+ method: "string",
94
+ args: ["id", 255],
95
+ modifiers: [{ method: "primary" }]
96
+ };
97
+ }
98
+ return {
99
+ name: "id",
100
+ method,
101
+ args: ["id"],
102
+ modifiers: []
103
+ };
104
+ }
105
+ function generateTimestampColumns() {
106
+ return [
107
+ {
108
+ name: "created_at",
109
+ method: "timestamp",
110
+ args: ["created_at"],
111
+ modifiers: [{ method: "nullable" }]
112
+ },
113
+ {
114
+ name: "updated_at",
115
+ method: "timestamp",
116
+ args: ["updated_at"],
117
+ modifiers: [{ method: "nullable" }]
118
+ }
119
+ ];
120
+ }
121
+ function generateSoftDeleteColumn() {
122
+ return {
123
+ name: "deleted_at",
124
+ method: "timestamp",
125
+ args: ["deleted_at"],
126
+ modifiers: [{ method: "nullable" }]
127
+ };
128
+ }
129
+ function generateForeignKey(propertyName, property, allSchemas) {
130
+ if (property.type !== "Association") {
131
+ return null;
132
+ }
133
+ const assocProp = property;
134
+ if (assocProp.relation !== "ManyToOne" && assocProp.relation !== "OneToOne") {
135
+ return null;
136
+ }
137
+ if (assocProp.mappedBy) {
138
+ return null;
139
+ }
140
+ const columnName = toColumnName(propertyName) + "_id";
141
+ const targetSchema = assocProp.target ? allSchemas[assocProp.target] : void 0;
142
+ const targetTable = assocProp.target ? toTableName(assocProp.target) : "unknown";
143
+ const targetPkType = targetSchema?.options?.primaryKeyType ?? "BigInt";
144
+ let method = "unsignedBigInteger";
145
+ if (targetPkType === "Int") {
146
+ method = "unsignedInteger";
147
+ } else if (targetPkType === "Uuid") {
148
+ method = "uuid";
149
+ } else if (targetPkType === "String") {
150
+ method = "string";
151
+ }
152
+ const column = {
153
+ name: columnName,
154
+ method,
155
+ args: [columnName],
156
+ modifiers: assocProp.relation === "ManyToOne" ? [{ method: "nullable" }] : []
157
+ };
158
+ const foreignKey = {
159
+ columns: [columnName],
160
+ references: "id",
161
+ on: [targetTable],
162
+ onDelete: assocProp.onDelete ?? "restrict",
163
+ onUpdate: assocProp.onUpdate ?? "cascade"
164
+ };
165
+ const index = {
166
+ name: `idx_${columnName}`,
167
+ columns: [columnName],
168
+ unique: false
169
+ };
170
+ return { column, foreignKey, index };
171
+ }
172
+ function schemaToBlueprint(schema, allSchemas) {
173
+ const tableName = toTableName(schema.name);
174
+ const columns = [];
175
+ const foreignKeys = [];
176
+ const indexes = [];
177
+ const pkType = schema.options?.primaryKeyType ?? "BigInt";
178
+ columns.push(generatePrimaryKeyColumn(pkType));
179
+ if (schema.properties) {
180
+ for (const [propName, property] of Object.entries(schema.properties)) {
181
+ const columnMethod = propertyToColumnMethod(propName, property);
182
+ if (columnMethod) {
183
+ columns.push(columnMethod);
184
+ }
185
+ const fkResult = generateForeignKey(propName, property, allSchemas);
186
+ if (fkResult) {
187
+ columns.push(fkResult.column);
188
+ foreignKeys.push(fkResult.foreignKey);
189
+ indexes.push(fkResult.index);
190
+ }
191
+ }
192
+ }
193
+ if (schema.options?.timestamps !== false) {
194
+ columns.push(...generateTimestampColumns());
195
+ }
196
+ if (schema.options?.softDelete) {
197
+ columns.push(generateSoftDeleteColumn());
198
+ }
199
+ if (schema.options?.indexes) {
200
+ for (const index of schema.options.indexes) {
201
+ indexes.push({
202
+ name: index.name,
203
+ columns: index.columns.map(toColumnName),
204
+ unique: index.unique ?? false
205
+ });
206
+ }
207
+ }
208
+ if (schema.options?.unique) {
209
+ const uniqueConstraints = Array.isArray(schema.options.unique[0]) ? schema.options.unique : [schema.options.unique];
210
+ for (const constraint of uniqueConstraints) {
211
+ indexes.push({
212
+ columns: constraint.map(toColumnName),
213
+ unique: true
214
+ });
215
+ }
216
+ }
217
+ return {
218
+ tableName,
219
+ columns,
220
+ primaryKey: ["id"],
221
+ foreignKeys,
222
+ indexes
223
+ };
224
+ }
225
+ function formatColumnMethod(column) {
226
+ const args = column.args.map((arg) => {
227
+ if (typeof arg === "string") {
228
+ return `'${arg}'`;
229
+ }
230
+ if (Array.isArray(arg)) {
231
+ return `[${arg.map((v) => `'${v}'`).join(", ")}]`;
232
+ }
233
+ return String(arg);
234
+ }).join(", ");
235
+ let code = `$table->${column.method}(${args})`;
236
+ for (const modifier of column.modifiers) {
237
+ if (modifier.args && modifier.args.length > 0) {
238
+ const modArgs = modifier.args.map((arg) => {
239
+ if (typeof arg === "string") {
240
+ return `'${arg}'`;
241
+ }
242
+ return String(arg);
243
+ }).join(", ");
244
+ code += `->${modifier.method}(${modArgs})`;
245
+ } else {
246
+ code += `->${modifier.method}()`;
247
+ }
248
+ }
249
+ return code + ";";
250
+ }
251
+ function formatForeignKey(fk) {
252
+ const column = fk.columns[0];
253
+ const table = fk.on[0];
254
+ let code = `$table->foreign('${column}')->references('${fk.references}')->on('${table}')`;
255
+ if (fk.onDelete) {
256
+ code += `->onDelete('${fk.onDelete}')`;
257
+ }
258
+ if (fk.onUpdate) {
259
+ code += `->onUpdate('${fk.onUpdate}')`;
260
+ }
261
+ return code + ";";
262
+ }
263
+ function formatIndex(index) {
264
+ const columns = index.columns.length === 1 ? `'${index.columns[0]}'` : `[${index.columns.map((c) => `'${c}'`).join(", ")}]`;
265
+ const method = index.unique ? "unique" : "index";
266
+ const name = index.name ? `, '${index.name}'` : "";
267
+ return `$table->${method}(${columns}${name});`;
268
+ }
269
+ function generatePivotTableName(sourceTable, targetTable, customName) {
270
+ if (customName) {
271
+ return customName;
272
+ }
273
+ const tables = [sourceTable, targetTable].sort();
274
+ const singular1 = tables[0].replace(/ies$/, "y").replace(/s$/, "");
275
+ const singular2 = tables[1].replace(/ies$/, "y").replace(/s$/, "");
276
+ return `${singular1}_${singular2}`;
277
+ }
278
+ function extractManyToManyRelations(schema, allSchemas) {
279
+ const pivotTables = [];
280
+ if (!schema.properties) {
281
+ return pivotTables;
282
+ }
283
+ const sourceTable = toTableName(schema.name);
284
+ const sourcePkType = schema.options?.primaryKeyType ?? "BigInt";
285
+ for (const [, property] of Object.entries(schema.properties)) {
286
+ if (property.type !== "Association") {
287
+ continue;
288
+ }
289
+ const assocProp = property;
290
+ if (assocProp.relation !== "ManyToMany") {
291
+ continue;
292
+ }
293
+ const targetName = assocProp.target;
294
+ if (!targetName) {
295
+ continue;
296
+ }
297
+ const targetSchema = allSchemas[targetName];
298
+ const targetTable = toTableName(targetName);
299
+ const targetPkType = targetSchema?.options?.primaryKeyType ?? "BigInt";
300
+ const isOwningSide = assocProp.owning ?? schema.name < targetName;
301
+ if (!isOwningSide) {
302
+ continue;
303
+ }
304
+ const pivotTableName = generatePivotTableName(sourceTable, targetTable, assocProp.joinTable);
305
+ const sourceColumn = sourceTable.replace(/ies$/, "y").replace(/s$/, "") + "_id";
306
+ const targetColumn = targetTable.replace(/ies$/, "y").replace(/s$/, "") + "_id";
307
+ pivotTables.push({
308
+ tableName: pivotTableName,
309
+ sourceTable,
310
+ targetTable,
311
+ sourceColumn,
312
+ targetColumn,
313
+ sourcePkType,
314
+ targetPkType,
315
+ onDelete: assocProp.onDelete,
316
+ onUpdate: assocProp.onUpdate
317
+ });
318
+ }
319
+ return pivotTables;
320
+ }
321
+ function generatePivotTableBlueprint(pivot) {
322
+ const columns = [];
323
+ const foreignKeys = [];
324
+ const indexes = [];
325
+ const getMethodForPkType = (pkType) => {
326
+ switch (pkType) {
327
+ case "Int":
328
+ return "unsignedInteger";
329
+ case "Uuid":
330
+ return "uuid";
331
+ case "String":
332
+ return "string";
333
+ default:
334
+ return "unsignedBigInteger";
335
+ }
336
+ };
337
+ columns.push({
338
+ name: pivot.sourceColumn,
339
+ method: getMethodForPkType(pivot.sourcePkType),
340
+ args: [pivot.sourceColumn],
341
+ modifiers: []
342
+ });
343
+ columns.push({
344
+ name: pivot.targetColumn,
345
+ method: getMethodForPkType(pivot.targetPkType),
346
+ args: [pivot.targetColumn],
347
+ modifiers: []
348
+ });
349
+ foreignKeys.push({
350
+ columns: [pivot.sourceColumn],
351
+ references: "id",
352
+ on: [pivot.sourceTable],
353
+ onDelete: pivot.onDelete ?? "cascade",
354
+ onUpdate: pivot.onUpdate ?? "cascade"
355
+ });
356
+ foreignKeys.push({
357
+ columns: [pivot.targetColumn],
358
+ references: "id",
359
+ on: [pivot.targetTable],
360
+ onDelete: pivot.onDelete ?? "cascade",
361
+ onUpdate: pivot.onUpdate ?? "cascade"
362
+ });
363
+ indexes.push({
364
+ columns: [pivot.sourceColumn, pivot.targetColumn],
365
+ unique: true
366
+ });
367
+ indexes.push({
368
+ name: `idx_${pivot.sourceColumn}`,
369
+ columns: [pivot.sourceColumn],
370
+ unique: false
371
+ });
372
+ indexes.push({
373
+ name: `idx_${pivot.targetColumn}`,
374
+ columns: [pivot.targetColumn],
375
+ unique: false
376
+ });
377
+ return {
378
+ tableName: pivot.tableName,
379
+ columns,
380
+ primaryKey: [pivot.sourceColumn, pivot.targetColumn],
381
+ foreignKeys,
382
+ indexes
383
+ };
384
+ }
385
+
386
+ // src/migration/generator.ts
387
+ function generateTimestamp() {
388
+ const now = /* @__PURE__ */ new Date();
389
+ const year = now.getFullYear();
390
+ const month = String(now.getMonth() + 1).padStart(2, "0");
391
+ const day = String(now.getDate()).padStart(2, "0");
392
+ const hours = String(now.getHours()).padStart(2, "0");
393
+ const minutes = String(now.getMinutes()).padStart(2, "0");
394
+ const seconds = String(now.getSeconds()).padStart(2, "0");
395
+ return `${year}_${month}_${day}_${hours}${minutes}${seconds}`;
396
+ }
397
+ function toClassName(tableName, type) {
398
+ const pascalCase = tableName.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
399
+ switch (type) {
400
+ case "create":
401
+ return `Create${pascalCase}Table`;
402
+ case "alter":
403
+ return `Alter${pascalCase}Table`;
404
+ case "drop":
405
+ return `Drop${pascalCase}Table`;
406
+ }
407
+ }
408
+ function generateFileName(tableName, type, timestamp) {
409
+ const ts = timestamp ?? generateTimestamp();
410
+ const action = type === "create" ? "create" : type === "drop" ? "drop" : "update";
411
+ return `${ts}_${action}_${tableName}_table.php`;
412
+ }
413
+ function renderCreateTableUp(blueprint) {
414
+ const lines = [];
415
+ for (const column of blueprint.columns) {
416
+ lines.push(` ${formatColumnMethod(column)}`);
417
+ }
418
+ return lines.join("\n");
419
+ }
420
+ function renderForeignKeys(blueprint) {
421
+ if (blueprint.foreignKeys.length === 0) {
422
+ return "";
423
+ }
424
+ const lines = blueprint.foreignKeys.map((fk) => ` ${formatForeignKey(fk)}`);
425
+ return "\n" + lines.join("\n");
426
+ }
427
+ function renderIndexes(blueprint) {
428
+ const customIndexes = blueprint.indexes.filter((idx) => {
429
+ if (idx.unique && idx.columns.length === 1) {
430
+ return false;
431
+ }
432
+ return true;
433
+ });
434
+ if (customIndexes.length === 0) {
435
+ return "";
436
+ }
437
+ const lines = customIndexes.map((idx) => ` ${formatIndex(idx)}`);
438
+ return "\n" + lines.join("\n");
439
+ }
440
+ function generateCreateMigration(blueprint, options = {}) {
441
+ const className = toClassName(blueprint.tableName, "create");
442
+ const fileName = generateFileName(blueprint.tableName, "create", options.timestamp);
443
+ const connection = options.connection ? `
444
+ protected $connection = '${options.connection}';
445
+ ` : "";
446
+ const upContent = renderCreateTableUp(blueprint);
447
+ const foreignKeyContent = renderForeignKeys(blueprint);
448
+ const indexContent = renderIndexes(blueprint);
449
+ const content = `<?php
450
+
451
+ use Illuminate\\Database\\Migrations\\Migration;
452
+ use Illuminate\\Database\\Schema\\Blueprint;
453
+ use Illuminate\\Support\\Facades\\Schema;
454
+
455
+ return new class extends Migration
456
+ {${connection}
457
+ /**
458
+ * Run the migrations.
459
+ */
460
+ public function up(): void
461
+ {
462
+ Schema::create('${blueprint.tableName}', function (Blueprint $table) {
463
+ ${upContent}${foreignKeyContent}${indexContent}
464
+ });
465
+ }
466
+
467
+ /**
468
+ * Reverse the migrations.
469
+ */
470
+ public function down(): void
471
+ {
472
+ Schema::dropIfExists('${blueprint.tableName}');
473
+ }
474
+ };
475
+ `;
476
+ return {
477
+ fileName,
478
+ className,
479
+ content,
480
+ tables: [blueprint.tableName],
481
+ type: "create"
482
+ };
483
+ }
484
+ function generateDropMigration(tableName, options = {}) {
485
+ const className = toClassName(tableName, "drop");
486
+ const fileName = generateFileName(tableName, "drop", options.timestamp);
487
+ const connection = options.connection ? `
488
+ protected $connection = '${options.connection}';
489
+ ` : "";
490
+ const content = `<?php
491
+
492
+ use Illuminate\\Database\\Migrations\\Migration;
493
+ use Illuminate\\Database\\Schema\\Blueprint;
494
+ use Illuminate\\Support\\Facades\\Schema;
495
+
496
+ return new class extends Migration
497
+ {${connection}
498
+ /**
499
+ * Run the migrations.
500
+ */
501
+ public function up(): void
502
+ {
503
+ Schema::dropIfExists('${tableName}');
504
+ }
505
+
506
+ /**
507
+ * Reverse the migrations.
508
+ */
509
+ public function down(): void
510
+ {
511
+ // Cannot recreate table without schema information
512
+ // This is a one-way migration
513
+ }
514
+ };
515
+ `;
516
+ return {
517
+ fileName,
518
+ className,
519
+ content,
520
+ tables: [tableName],
521
+ type: "drop"
522
+ };
523
+ }
524
+ function generateMigrations(schemas, options = {}) {
525
+ const migrations = [];
526
+ const pivotTablesGenerated = /* @__PURE__ */ new Set();
527
+ let timestampOffset = 0;
528
+ for (const schema of Object.values(schemas)) {
529
+ if (schema.kind === "enum") {
530
+ continue;
531
+ }
532
+ const timestamp = options.timestamp ?? generateTimestamp();
533
+ const offsetTimestamp = incrementTimestamp(timestamp, timestampOffset);
534
+ timestampOffset++;
535
+ const blueprint = schemaToBlueprint(schema, schemas);
536
+ const migration = generateCreateMigration(blueprint, {
537
+ ...options,
538
+ timestamp: offsetTimestamp
539
+ });
540
+ migrations.push(migration);
541
+ }
542
+ for (const schema of Object.values(schemas)) {
543
+ if (schema.kind === "enum") {
544
+ continue;
545
+ }
546
+ const pivotTables = extractManyToManyRelations(schema, schemas);
547
+ for (const pivot of pivotTables) {
548
+ if (pivotTablesGenerated.has(pivot.tableName)) {
549
+ continue;
550
+ }
551
+ pivotTablesGenerated.add(pivot.tableName);
552
+ const timestamp = options.timestamp ?? generateTimestamp();
553
+ const offsetTimestamp = incrementTimestamp(timestamp, timestampOffset);
554
+ timestampOffset++;
555
+ const blueprint = generatePivotTableBlueprint(pivot);
556
+ const migration = generateCreateMigration(blueprint, {
557
+ ...options,
558
+ timestamp: offsetTimestamp
559
+ });
560
+ migrations.push(migration);
561
+ }
562
+ }
563
+ return migrations;
564
+ }
565
+ function incrementTimestamp(timestamp, seconds) {
566
+ if (seconds === 0) return timestamp;
567
+ const parts = timestamp.split("_");
568
+ if (parts.length < 4) {
569
+ return timestamp;
570
+ }
571
+ const yearPart = parts[0] ?? "2024";
572
+ const monthPart = parts[1] ?? "01";
573
+ const dayPart = parts[2] ?? "01";
574
+ const timePart = parts[3] ?? "000000";
575
+ const year = parseInt(yearPart, 10);
576
+ const month = parseInt(monthPart, 10) - 1;
577
+ const day = parseInt(dayPart, 10);
578
+ const hours = parseInt(timePart.substring(0, 2), 10);
579
+ const minutes = parseInt(timePart.substring(2, 4), 10);
580
+ const secs = parseInt(timePart.substring(4, 6), 10);
581
+ const date = new Date(year, month, day, hours, minutes, secs + seconds);
582
+ const newYear = date.getFullYear();
583
+ const newMonth = String(date.getMonth() + 1).padStart(2, "0");
584
+ const newDay = String(date.getDate()).padStart(2, "0");
585
+ const newHours = String(date.getHours()).padStart(2, "0");
586
+ const newMinutes = String(date.getMinutes()).padStart(2, "0");
587
+ const newSecs = String(date.getSeconds()).padStart(2, "0");
588
+ return `${newYear}_${newMonth}_${newDay}_${newHours}${newMinutes}${newSecs}`;
589
+ }
590
+ function generateMigrationFromSchema(schema, allSchemas, options = {}) {
591
+ const blueprint = schemaToBlueprint(schema, allSchemas);
592
+ return generateCreateMigration(blueprint, options);
593
+ }
594
+ function generateDropMigrationForTable(tableName, options = {}) {
595
+ return generateDropMigration(tableName, options);
596
+ }
597
+ function formatMigrationFile(migration) {
598
+ return migration.content;
599
+ }
600
+ function getMigrationPath(migration, outputDir = "database/migrations") {
601
+ return `${outputDir}/${migration.fileName}`;
602
+ }
603
+
604
+ // src/typescript/interface-generator.ts
605
+ var TYPE_MAP = {
606
+ String: "string",
607
+ Int: "number",
608
+ BigInt: "number",
609
+ Float: "number",
610
+ Boolean: "boolean",
611
+ Text: "string",
612
+ LongText: "string",
613
+ Date: "string",
614
+ Time: "string",
615
+ Timestamp: "string",
616
+ Json: "unknown",
617
+ Email: "string",
618
+ Password: "string",
619
+ File: "string",
620
+ MultiFile: "string[]",
621
+ Enum: "string",
622
+ Select: "string",
623
+ Lookup: "number"
624
+ };
625
+ var PK_TYPE_MAP = {
626
+ Int: "number",
627
+ BigInt: "number",
628
+ Uuid: "string",
629
+ String: "string"
630
+ };
631
+ function toPropertyName(name) {
632
+ return name;
633
+ }
634
+ function toInterfaceName(schemaName) {
635
+ return schemaName;
636
+ }
637
+ function getPropertyType(property, _allSchemas) {
638
+ if (property.type === "Association") {
639
+ const assocProp = property;
640
+ const targetName = assocProp.target ?? "unknown";
641
+ switch (assocProp.relation) {
642
+ case "OneToOne":
643
+ case "ManyToOne":
644
+ return targetName;
645
+ case "OneToMany":
646
+ case "ManyToMany":
647
+ return `${targetName}[]`;
648
+ default:
649
+ return "unknown";
650
+ }
651
+ }
652
+ if (property.type === "Enum") {
653
+ const enumProp = property;
654
+ if (typeof enumProp.enum === "string") {
655
+ return enumProp.enum;
656
+ }
657
+ if (Array.isArray(enumProp.enum)) {
658
+ return enumProp.enum.map((v) => `'${v}'`).join(" | ");
659
+ }
660
+ }
661
+ if (property.type === "Select") {
662
+ const selectProp = property;
663
+ if (selectProp.options && selectProp.options.length > 0) {
664
+ return selectProp.options.map((v) => `'${v}'`).join(" | ");
665
+ }
666
+ }
667
+ return TYPE_MAP[property.type] ?? "unknown";
668
+ }
669
+ function propertyToTSProperty(propertyName, property, allSchemas, options = {}) {
670
+ const type = getPropertyType(property, allSchemas);
671
+ const baseProp = property;
672
+ return {
673
+ name: toPropertyName(propertyName),
674
+ type,
675
+ optional: baseProp.nullable ?? false,
676
+ readonly: options.readonly ?? true,
677
+ comment: baseProp.displayName
678
+ };
679
+ }
680
+ function schemaToInterface(schema, allSchemas, options = {}) {
681
+ const properties = [];
682
+ const pkType = schema.options?.primaryKeyType ?? "BigInt";
683
+ properties.push({
684
+ name: "id",
685
+ type: PK_TYPE_MAP[pkType] ?? "number",
686
+ optional: false,
687
+ readonly: options.readonly ?? true,
688
+ comment: "Primary key"
689
+ });
690
+ if (schema.properties) {
691
+ for (const [propName, property] of Object.entries(schema.properties)) {
692
+ properties.push(propertyToTSProperty(propName, property, allSchemas, options));
693
+ }
694
+ }
695
+ if (schema.options?.timestamps !== false) {
696
+ properties.push(
697
+ {
698
+ name: "createdAt",
699
+ type: "string",
700
+ optional: true,
701
+ readonly: options.readonly ?? true,
702
+ comment: "Creation timestamp"
703
+ },
704
+ {
705
+ name: "updatedAt",
706
+ type: "string",
707
+ optional: true,
708
+ readonly: options.readonly ?? true,
709
+ comment: "Last update timestamp"
710
+ }
711
+ );
712
+ }
713
+ if (schema.options?.softDelete) {
714
+ properties.push({
715
+ name: "deletedAt",
716
+ type: "string",
717
+ optional: true,
718
+ readonly: options.readonly ?? true,
719
+ comment: "Soft delete timestamp"
720
+ });
721
+ }
722
+ return {
723
+ name: toInterfaceName(schema.name),
724
+ properties,
725
+ comment: schema.displayName ?? schema.name
726
+ };
727
+ }
728
+ function formatProperty(property) {
729
+ const readonly = property.readonly ? "readonly " : "";
730
+ const optional = property.optional ? "?" : "";
731
+ const comment = property.comment ? ` /** ${property.comment} */
732
+ ` : "";
733
+ return `${comment} ${readonly}${property.name}${optional}: ${property.type};`;
734
+ }
735
+ function formatInterface(iface) {
736
+ const comment = iface.comment ? `/**
737
+ * ${iface.comment}
738
+ */
739
+ ` : "";
740
+ const extendsClause = iface.extends && iface.extends.length > 0 ? ` extends ${iface.extends.join(", ")}` : "";
741
+ const properties = iface.properties.map(formatProperty).join("\n");
742
+ return `${comment}export interface ${iface.name}${extendsClause} {
743
+ ${properties}
744
+ }`;
745
+ }
746
+ function generateInterfaces(schemas, options = {}) {
747
+ const interfaces = [];
748
+ for (const schema of Object.values(schemas)) {
749
+ if (schema.kind === "enum") {
750
+ continue;
751
+ }
752
+ interfaces.push(schemaToInterface(schema, schemas, options));
753
+ }
754
+ return interfaces;
755
+ }
756
+
757
+ // src/typescript/enum-generator.ts
758
+ function toEnumMemberName(value) {
759
+ return value.split(/[-_\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("").replace(/[^a-zA-Z0-9]/g, "");
760
+ }
761
+ function toEnumName(schemaName) {
762
+ return schemaName;
763
+ }
764
+ function schemaToEnum(schema) {
765
+ if (schema.kind !== "enum" || !schema.values) {
766
+ return null;
767
+ }
768
+ const values = schema.values.map((value) => ({
769
+ name: toEnumMemberName(value),
770
+ value
771
+ }));
772
+ return {
773
+ name: toEnumName(schema.name),
774
+ values,
775
+ comment: schema.displayName ?? schema.name
776
+ };
777
+ }
778
+ function generateEnums(schemas) {
779
+ const enums = [];
780
+ for (const schema of Object.values(schemas)) {
781
+ if (schema.kind === "enum") {
782
+ const enumDef = schemaToEnum(schema);
783
+ if (enumDef) {
784
+ enums.push(enumDef);
785
+ }
786
+ }
787
+ }
788
+ return enums;
789
+ }
790
+ function formatEnum(enumDef) {
791
+ const comment = enumDef.comment ? `/**
792
+ * ${enumDef.comment}
793
+ */
794
+ ` : "";
795
+ const values = enumDef.values.map((v) => ` ${v.name} = '${v.value}',`).join("\n");
796
+ return `${comment}export enum ${enumDef.name} {
797
+ ${values}
798
+ }`;
799
+ }
800
+ function enumToUnionType(enumDef) {
801
+ const type = enumDef.values.map((v) => `'${v.value}'`).join(" | ");
802
+ return {
803
+ name: enumDef.name,
804
+ type,
805
+ comment: enumDef.comment
806
+ };
807
+ }
808
+ function formatTypeAlias(alias) {
809
+ const comment = alias.comment ? `/**
810
+ * ${alias.comment}
811
+ */
812
+ ` : "";
813
+ return `${comment}export type ${alias.name} = ${alias.type};`;
814
+ }
815
+ function extractInlineEnums(schemas) {
816
+ const typeAliases = [];
817
+ for (const schema of Object.values(schemas)) {
818
+ if (schema.kind === "enum" || !schema.properties) {
819
+ continue;
820
+ }
821
+ for (const [propName, property] of Object.entries(schema.properties)) {
822
+ if (property.type === "Enum") {
823
+ const enumProp = property;
824
+ if (Array.isArray(enumProp.enum) && enumProp.enum.length > 0) {
825
+ const typeName = `${schema.name}${propName.charAt(0).toUpperCase() + propName.slice(1)}`;
826
+ typeAliases.push({
827
+ name: typeName,
828
+ type: enumProp.enum.map((v) => `'${v}'`).join(" | "),
829
+ comment: enumProp.displayName ?? `${schema.name} ${propName} enum`
830
+ });
831
+ }
832
+ }
833
+ if (property.type === "Select") {
834
+ const selectProp = property;
835
+ if (selectProp.options && selectProp.options.length > 0) {
836
+ const typeName = `${schema.name}${propName.charAt(0).toUpperCase() + propName.slice(1)}`;
837
+ typeAliases.push({
838
+ name: typeName,
839
+ type: selectProp.options.map((v) => `'${v}'`).join(" | "),
840
+ comment: selectProp.displayName ?? `${schema.name} ${propName} options`
841
+ });
842
+ }
843
+ }
844
+ }
845
+ }
846
+ return typeAliases;
847
+ }
848
+
849
+ // src/typescript/generator.ts
850
+ var DEFAULT_OPTIONS = {
851
+ singleFile: true,
852
+ fileName: "types.ts",
853
+ readonly: true,
854
+ strictNullChecks: true
855
+ };
856
+ function generateHeader() {
857
+ return `/**
858
+ * Auto-generated TypeScript types from Omnify schemas.
859
+ * DO NOT EDIT - This file is automatically generated.
860
+ */
861
+
862
+ `;
863
+ }
864
+ function generateTypeScriptFile(schemas, options = {}) {
865
+ const opts = { ...DEFAULT_OPTIONS, ...options };
866
+ const parts = [generateHeader()];
867
+ const types = [];
868
+ const enums = generateEnums(schemas);
869
+ if (enums.length > 0) {
870
+ parts.push("// Enums\n");
871
+ for (const enumDef of enums) {
872
+ parts.push(formatEnum(enumDef));
873
+ parts.push("\n\n");
874
+ types.push(enumDef.name);
875
+ }
876
+ }
877
+ const inlineEnums = extractInlineEnums(schemas);
878
+ if (inlineEnums.length > 0) {
879
+ parts.push("// Type Aliases\n");
880
+ for (const alias of inlineEnums) {
881
+ parts.push(formatTypeAlias(alias));
882
+ parts.push("\n\n");
883
+ types.push(alias.name);
884
+ }
885
+ }
886
+ const interfaces = generateInterfaces(schemas, opts);
887
+ if (interfaces.length > 0) {
888
+ parts.push("// Interfaces\n");
889
+ for (const iface of interfaces) {
890
+ parts.push(formatInterface(iface));
891
+ parts.push("\n\n");
892
+ types.push(iface.name);
893
+ }
894
+ }
895
+ return {
896
+ fileName: opts.fileName ?? "types.ts",
897
+ content: parts.join("").trim() + "\n",
898
+ types
899
+ };
900
+ }
901
+ function generateTypeScriptFiles(schemas, options = {}) {
902
+ const opts = { ...DEFAULT_OPTIONS, ...options };
903
+ const files = [];
904
+ const enums = generateEnums(schemas);
905
+ if (enums.length > 0) {
906
+ const content = generateHeader() + enums.map(formatEnum).join("\n\n") + "\n";
907
+ files.push({
908
+ fileName: "enums.ts",
909
+ content,
910
+ types: enums.map((e) => e.name)
911
+ });
912
+ }
913
+ const inlineEnums = extractInlineEnums(schemas);
914
+ if (inlineEnums.length > 0) {
915
+ const content = generateHeader() + inlineEnums.map(formatTypeAlias).join("\n\n") + "\n";
916
+ files.push({
917
+ fileName: "type-aliases.ts",
918
+ content,
919
+ types: inlineEnums.map((a) => a.name)
920
+ });
921
+ }
922
+ const interfaces = generateInterfaces(schemas, opts);
923
+ for (const iface of interfaces) {
924
+ const imports = collectImports(iface, enums, inlineEnums, interfaces);
925
+ const importStatement = formatImports(imports);
926
+ const content = generateHeader() + (importStatement ? importStatement + "\n\n" : "") + formatInterface(iface) + "\n";
927
+ files.push({
928
+ fileName: `${toKebabCase(iface.name)}.ts`,
929
+ content,
930
+ types: [iface.name]
931
+ });
932
+ }
933
+ const indexContent = generateIndexFile(files);
934
+ files.push({
935
+ fileName: "index.ts",
936
+ content: indexContent,
937
+ types: []
938
+ });
939
+ return files;
940
+ }
941
+ function toKebabCase(name) {
942
+ return name.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
943
+ }
944
+ function collectImports(iface, enums, typeAliases, allInterfaces) {
945
+ const imports = /* @__PURE__ */ new Map();
946
+ const enumNames = new Set(enums.map((e) => e.name));
947
+ const aliasNames = new Set(typeAliases.map((a) => a.name));
948
+ const interfaceNames = new Set(allInterfaces.map((i) => i.name));
949
+ for (const prop of iface.properties) {
950
+ if (enumNames.has(prop.type)) {
951
+ const existing = imports.get("./enums.js") ?? [];
952
+ if (!existing.includes(prop.type)) {
953
+ imports.set("./enums.js", [...existing, prop.type]);
954
+ }
955
+ }
956
+ if (aliasNames.has(prop.type)) {
957
+ const existing = imports.get("./type-aliases.js") ?? [];
958
+ if (!existing.includes(prop.type)) {
959
+ imports.set("./type-aliases.js", [...existing, prop.type]);
960
+ }
961
+ }
962
+ const baseType = prop.type.replace("[]", "");
963
+ if (interfaceNames.has(baseType) && baseType !== iface.name) {
964
+ const fileName = `./${toKebabCase(baseType)}.js`;
965
+ const existing = imports.get(fileName) ?? [];
966
+ if (!existing.includes(baseType)) {
967
+ imports.set(fileName, [...existing, baseType]);
968
+ }
969
+ }
970
+ }
971
+ return imports;
972
+ }
973
+ function formatImports(imports) {
974
+ if (imports.size === 0) return "";
975
+ const lines = [];
976
+ for (const [path, names] of imports) {
977
+ lines.push(`import type { ${names.join(", ")} } from '${path}';`);
978
+ }
979
+ return lines.join("\n");
980
+ }
981
+ function generateIndexFile(files) {
982
+ const exports = [generateHeader().trim(), ""];
983
+ for (const file of files) {
984
+ if (file.fileName === "index.ts") continue;
985
+ const moduleName = file.fileName.replace(".ts", ".js");
986
+ exports.push(`export * from './${moduleName}';`);
987
+ }
988
+ return exports.join("\n") + "\n";
989
+ }
990
+ function generateTypeScript(schemas, options = {}) {
991
+ const opts = { ...DEFAULT_OPTIONS, ...options };
992
+ if (opts.singleFile) {
993
+ return [generateTypeScriptFile(schemas, opts)];
994
+ }
995
+ return generateTypeScriptFiles(schemas, opts);
996
+ }
997
+ function getTypeScriptPath(file, outputDir = "src/types") {
998
+ return `${outputDir}/${file.fileName}`;
999
+ }
1000
+
1001
+ // src/plugin.ts
1002
+ function resolveOptions(options) {
1003
+ return {
1004
+ migrationsPath: options?.migrationsPath ?? "database/migrations",
1005
+ typesPath: options?.typesPath ?? "types",
1006
+ singleFile: options?.singleFile ?? true,
1007
+ connection: options?.connection,
1008
+ timestamp: options?.timestamp,
1009
+ generateTypes: options?.generateTypes ?? true,
1010
+ generateMigrations: options?.generateMigrations ?? true
1011
+ };
1012
+ }
1013
+ function laravelPlugin(options) {
1014
+ const resolved = resolveOptions(options);
1015
+ return {
1016
+ name: "@famgia/omnify-laravel",
1017
+ version: "0.0.6",
1018
+ generators: [
1019
+ // Laravel Migrations Generator
1020
+ ...resolved.generateMigrations ? [
1021
+ {
1022
+ name: "laravel-migrations",
1023
+ description: "Generate Laravel migration files",
1024
+ generate: async (ctx) => {
1025
+ const migrationOptions = {
1026
+ connection: resolved.connection,
1027
+ timestamp: resolved.timestamp
1028
+ };
1029
+ const migrations = generateMigrations(ctx.schemas, migrationOptions);
1030
+ return migrations.map((migration) => ({
1031
+ path: getMigrationPath(migration, resolved.migrationsPath),
1032
+ content: migration.content,
1033
+ type: "migration",
1034
+ metadata: {
1035
+ tableName: migration.tables[0],
1036
+ migrationType: migration.type
1037
+ }
1038
+ }));
1039
+ }
1040
+ }
1041
+ ] : [],
1042
+ // TypeScript Types Generator
1043
+ ...resolved.generateTypes ? [
1044
+ {
1045
+ name: "typescript-types",
1046
+ description: "Generate TypeScript type definitions",
1047
+ generate: async (ctx) => {
1048
+ const tsOptions = {
1049
+ singleFile: resolved.singleFile
1050
+ };
1051
+ const files = generateTypeScript(ctx.schemas, tsOptions);
1052
+ return files.map((file) => ({
1053
+ path: `${resolved.typesPath}/${file.fileName}`,
1054
+ content: file.content,
1055
+ type: "type",
1056
+ metadata: {
1057
+ types: file.types
1058
+ }
1059
+ }));
1060
+ }
1061
+ }
1062
+ ] : []
1063
+ ]
1064
+ };
1065
+ }
1066
+
1067
+ export {
1068
+ toColumnName,
1069
+ toTableName,
1070
+ propertyToColumnMethod,
1071
+ generatePrimaryKeyColumn,
1072
+ generateTimestampColumns,
1073
+ generateSoftDeleteColumn,
1074
+ generateForeignKey,
1075
+ schemaToBlueprint,
1076
+ formatColumnMethod,
1077
+ formatForeignKey,
1078
+ formatIndex,
1079
+ generateMigrations,
1080
+ generateMigrationFromSchema,
1081
+ generateDropMigrationForTable,
1082
+ formatMigrationFile,
1083
+ getMigrationPath,
1084
+ toPropertyName,
1085
+ toInterfaceName,
1086
+ getPropertyType,
1087
+ propertyToTSProperty,
1088
+ schemaToInterface,
1089
+ formatProperty,
1090
+ formatInterface,
1091
+ generateInterfaces,
1092
+ toEnumMemberName,
1093
+ toEnumName,
1094
+ schemaToEnum,
1095
+ generateEnums,
1096
+ formatEnum,
1097
+ enumToUnionType,
1098
+ formatTypeAlias,
1099
+ extractInlineEnums,
1100
+ generateTypeScriptFile,
1101
+ generateTypeScriptFiles,
1102
+ generateTypeScript,
1103
+ getTypeScriptPath,
1104
+ laravelPlugin
1105
+ };
1106
+ //# sourceMappingURL=chunk-BA63DET3.js.map