@famgia/omnify-typescript 0.0.1 → 0.0.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.
@@ -0,0 +1,638 @@
1
+ // src/interface-generator.ts
2
+ import { resolveLocalizedString } from "@famgia/omnify-types";
3
+ var TYPE_MAP = {
4
+ String: "string",
5
+ Int: "number",
6
+ BigInt: "number",
7
+ Float: "number",
8
+ Boolean: "boolean",
9
+ Text: "string",
10
+ LongText: "string",
11
+ Date: "string",
12
+ Time: "string",
13
+ Timestamp: "string",
14
+ Json: "unknown",
15
+ Email: "string",
16
+ Password: "string",
17
+ Enum: "string",
18
+ Select: "string",
19
+ Lookup: "number"
20
+ };
21
+ var FILE_INTERFACE_NAME = "File";
22
+ var PK_TYPE_MAP = {
23
+ Int: "number",
24
+ BigInt: "number",
25
+ Uuid: "string",
26
+ String: "string"
27
+ };
28
+ function resolveDisplayName(value, options = {}) {
29
+ if (value === void 0) {
30
+ return void 0;
31
+ }
32
+ return resolveLocalizedString(value, {
33
+ locale: options.locale,
34
+ config: options.localeConfig
35
+ });
36
+ }
37
+ function toPropertyName(name) {
38
+ return name;
39
+ }
40
+ function toInterfaceName(schemaName) {
41
+ return schemaName;
42
+ }
43
+ function getPropertyType(property, _allSchemas) {
44
+ if (property.type === "File") {
45
+ const fileProp = property;
46
+ if (fileProp.multiple) {
47
+ return `${FILE_INTERFACE_NAME}[]`;
48
+ }
49
+ return `${FILE_INTERFACE_NAME} | null`;
50
+ }
51
+ if (property.type === "Association") {
52
+ const assocProp = property;
53
+ const targetName = assocProp.target ?? "unknown";
54
+ switch (assocProp.relation) {
55
+ // Standard relations
56
+ case "OneToOne":
57
+ case "ManyToOne":
58
+ return targetName;
59
+ case "OneToMany":
60
+ case "ManyToMany":
61
+ return `${targetName}[]`;
62
+ // Polymorphic relations
63
+ case "MorphTo":
64
+ if (assocProp.targets && assocProp.targets.length > 0) {
65
+ return assocProp.targets.join(" | ");
66
+ }
67
+ return "unknown";
68
+ case "MorphOne":
69
+ return targetName;
70
+ case "MorphMany":
71
+ case "MorphToMany":
72
+ case "MorphedByMany":
73
+ return `${targetName}[]`;
74
+ default:
75
+ return "unknown";
76
+ }
77
+ }
78
+ if (property.type === "Enum") {
79
+ const enumProp = property;
80
+ if (typeof enumProp.enum === "string") {
81
+ return enumProp.enum;
82
+ }
83
+ if (Array.isArray(enumProp.enum)) {
84
+ return enumProp.enum.map((v) => `'${v}'`).join(" | ");
85
+ }
86
+ }
87
+ if (property.type === "Select") {
88
+ const selectProp = property;
89
+ if (selectProp.options && selectProp.options.length > 0) {
90
+ return selectProp.options.map((v) => `'${v}'`).join(" | ");
91
+ }
92
+ }
93
+ return TYPE_MAP[property.type] ?? "unknown";
94
+ }
95
+ function propertyToTSProperties(propertyName, property, allSchemas, options = {}) {
96
+ const baseProp = property;
97
+ const isReadonly = options.readonly ?? true;
98
+ const displayName = resolveDisplayName(baseProp.displayName, options);
99
+ if (property.type === "Association") {
100
+ const assocProp = property;
101
+ if (assocProp.relation === "MorphTo" && assocProp.targets && assocProp.targets.length > 0) {
102
+ const propBaseName = toPropertyName(propertyName);
103
+ const targetUnion = assocProp.targets.map((t) => `'${t}'`).join(" | ");
104
+ const relationUnion = assocProp.targets.join(" | ");
105
+ return [
106
+ {
107
+ name: `${propBaseName}Type`,
108
+ type: targetUnion,
109
+ optional: true,
110
+ // Polymorphic columns are nullable
111
+ readonly: isReadonly,
112
+ comment: `Polymorphic type for ${propertyName}`
113
+ },
114
+ {
115
+ name: `${propBaseName}Id`,
116
+ type: "number",
117
+ optional: true,
118
+ readonly: isReadonly,
119
+ comment: `Polymorphic ID for ${propertyName}`
120
+ },
121
+ {
122
+ name: propBaseName,
123
+ type: `${relationUnion} | null`,
124
+ optional: true,
125
+ readonly: isReadonly,
126
+ comment: displayName ?? `Polymorphic relation to ${assocProp.targets.join(", ")}`
127
+ }
128
+ ];
129
+ }
130
+ }
131
+ const type = getPropertyType(property, allSchemas);
132
+ return [{
133
+ name: toPropertyName(propertyName),
134
+ type,
135
+ optional: baseProp.nullable ?? false,
136
+ readonly: isReadonly,
137
+ comment: displayName
138
+ }];
139
+ }
140
+ function propertyToTSProperty(propertyName, property, allSchemas, options = {}) {
141
+ return propertyToTSProperties(propertyName, property, allSchemas, options)[0];
142
+ }
143
+ function schemaToInterface(schema, allSchemas, options = {}) {
144
+ const properties = [];
145
+ if (schema.options?.id !== false) {
146
+ const pkType = schema.options?.idType ?? "BigInt";
147
+ properties.push({
148
+ name: "id",
149
+ type: PK_TYPE_MAP[pkType] ?? "number",
150
+ optional: false,
151
+ readonly: options.readonly ?? true,
152
+ comment: "Primary key"
153
+ });
154
+ }
155
+ if (schema.properties) {
156
+ for (const [propName, property] of Object.entries(schema.properties)) {
157
+ properties.push(...propertyToTSProperties(propName, property, allSchemas, options));
158
+ }
159
+ }
160
+ if (schema.options?.timestamps !== false) {
161
+ properties.push(
162
+ {
163
+ name: "createdAt",
164
+ type: "string",
165
+ optional: true,
166
+ readonly: options.readonly ?? true,
167
+ comment: "Creation timestamp"
168
+ },
169
+ {
170
+ name: "updatedAt",
171
+ type: "string",
172
+ optional: true,
173
+ readonly: options.readonly ?? true,
174
+ comment: "Last update timestamp"
175
+ }
176
+ );
177
+ }
178
+ if (schema.options?.softDelete) {
179
+ properties.push({
180
+ name: "deletedAt",
181
+ type: "string",
182
+ optional: true,
183
+ readonly: options.readonly ?? true,
184
+ comment: "Soft delete timestamp"
185
+ });
186
+ }
187
+ const schemaDisplayName = resolveDisplayName(schema.displayName, options);
188
+ return {
189
+ name: toInterfaceName(schema.name),
190
+ properties,
191
+ comment: schemaDisplayName ?? schema.name
192
+ };
193
+ }
194
+ function formatProperty(property) {
195
+ const readonly = property.readonly ? "readonly " : "";
196
+ const optional = property.optional ? "?" : "";
197
+ const comment = property.comment ? ` /** ${property.comment} */
198
+ ` : "";
199
+ return `${comment} ${readonly}${property.name}${optional}: ${property.type};`;
200
+ }
201
+ function formatInterface(iface) {
202
+ const comment = iface.comment ? `/**
203
+ * ${iface.comment}
204
+ */
205
+ ` : "";
206
+ const extendsClause = iface.extends && iface.extends.length > 0 ? ` extends ${iface.extends.join(", ")}` : "";
207
+ const properties = iface.properties.map(formatProperty).join("\n");
208
+ return `${comment}export interface ${iface.name}${extendsClause} {
209
+ ${properties}
210
+ }`;
211
+ }
212
+ function generateInterfaces(schemas, options = {}) {
213
+ const interfaces = [];
214
+ for (const schema of Object.values(schemas)) {
215
+ if (schema.kind === "enum") {
216
+ continue;
217
+ }
218
+ interfaces.push(schemaToInterface(schema, schemas, options));
219
+ }
220
+ return interfaces;
221
+ }
222
+
223
+ // src/enum-generator.ts
224
+ import { resolveLocalizedString as resolveLocalizedString2 } from "@famgia/omnify-types";
225
+ function resolveDisplayName2(value, options = {}) {
226
+ if (value === void 0) {
227
+ return void 0;
228
+ }
229
+ return resolveLocalizedString2(value, {
230
+ locale: options.locale,
231
+ config: options.localeConfig
232
+ });
233
+ }
234
+ function toEnumMemberName(value) {
235
+ return value.split(/[-_\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("").replace(/[^a-zA-Z0-9]/g, "");
236
+ }
237
+ function toEnumName(schemaName) {
238
+ return schemaName;
239
+ }
240
+ function parseEnumValue(value) {
241
+ if (typeof value === "string") {
242
+ return {
243
+ name: toEnumMemberName(value),
244
+ value
245
+ // No label or extra - will fallback to value
246
+ };
247
+ }
248
+ return {
249
+ name: toEnumMemberName(value.value),
250
+ value: value.value,
251
+ label: value.label,
252
+ extra: value.extra
253
+ };
254
+ }
255
+ function schemaToEnum(schema, options = {}) {
256
+ if (schema.kind !== "enum" || !schema.values) {
257
+ return null;
258
+ }
259
+ const values = schema.values.map((value) => parseEnumValue(value));
260
+ const displayName = resolveDisplayName2(schema.displayName, options);
261
+ return {
262
+ name: toEnumName(schema.name),
263
+ values,
264
+ comment: displayName ?? schema.name
265
+ };
266
+ }
267
+ function generateEnums(schemas, options = {}) {
268
+ const enums = [];
269
+ for (const schema of Object.values(schemas)) {
270
+ if (schema.kind === "enum") {
271
+ const enumDef = schemaToEnum(schema, options);
272
+ if (enumDef) {
273
+ enums.push(enumDef);
274
+ }
275
+ }
276
+ }
277
+ return enums;
278
+ }
279
+ function formatEnum(enumDef) {
280
+ const { name, values, comment } = enumDef;
281
+ const parts = [];
282
+ if (comment) {
283
+ parts.push(`/**
284
+ * ${comment}
285
+ */
286
+ `);
287
+ }
288
+ const enumValues = values.map((v) => ` ${v.name} = '${v.value}',`).join("\n");
289
+ parts.push(`export enum ${name} {
290
+ ${enumValues}
291
+ }
292
+
293
+ `);
294
+ parts.push(`/** All ${name} values */
295
+ `);
296
+ parts.push(`export const ${name}Values = Object.values(${name});
297
+
298
+ `);
299
+ parts.push(`/** Type guard for ${name} */
300
+ `);
301
+ parts.push(`export function is${name}(value: unknown): value is ${name} {
302
+ `);
303
+ parts.push(` return ${name}Values.includes(value as ${name});
304
+ `);
305
+ parts.push(`}
306
+
307
+ `);
308
+ const hasLabels = values.some((v) => v.label !== void 0);
309
+ if (hasLabels) {
310
+ const labelEntries = values.filter((v) => v.label !== void 0).map((v) => ` [${name}.${v.name}]: '${v.label}',`).join("\n");
311
+ parts.push(`const ${lowerFirst(name)}Labels: Partial<Record<${name}, string>> = {
312
+ ${labelEntries}
313
+ };
314
+
315
+ `);
316
+ }
317
+ parts.push(`/** Get label for ${name} value (fallback to value if no label) */
318
+ `);
319
+ parts.push(`export function get${name}Label(value: ${name}): string {
320
+ `);
321
+ if (hasLabels) {
322
+ parts.push(` return ${lowerFirst(name)}Labels[value] ?? value;
323
+ `);
324
+ } else {
325
+ parts.push(` return value;
326
+ `);
327
+ }
328
+ parts.push(`}
329
+
330
+ `);
331
+ const hasExtra = values.some((v) => v.extra !== void 0);
332
+ if (hasExtra) {
333
+ const extraEntries = values.filter((v) => v.extra !== void 0).map((v) => ` [${name}.${v.name}]: ${JSON.stringify(v.extra)},`).join("\n");
334
+ parts.push(`const ${lowerFirst(name)}Extra: Partial<Record<${name}, Record<string, unknown>>> = {
335
+ ${extraEntries}
336
+ };
337
+
338
+ `);
339
+ parts.push(`/** Get extra metadata for ${name} value (undefined if not defined) */
340
+ `);
341
+ parts.push(`export function get${name}Extra(value: ${name}): Record<string, unknown> | undefined {
342
+ `);
343
+ parts.push(` return ${lowerFirst(name)}Extra[value];
344
+ `);
345
+ parts.push(`}`);
346
+ } else {
347
+ parts.push(`/** Get extra metadata for ${name} value (undefined if not defined) */
348
+ `);
349
+ parts.push(`export function get${name}Extra(_value: ${name}): Record<string, unknown> | undefined {
350
+ `);
351
+ parts.push(` return undefined;
352
+ `);
353
+ parts.push(`}`);
354
+ }
355
+ return parts.join("");
356
+ }
357
+ function lowerFirst(str) {
358
+ return str.charAt(0).toLowerCase() + str.slice(1);
359
+ }
360
+ function enumToUnionType(enumDef) {
361
+ const type = enumDef.values.map((v) => `'${v.value}'`).join(" | ");
362
+ return {
363
+ name: enumDef.name,
364
+ type,
365
+ comment: enumDef.comment
366
+ };
367
+ }
368
+ function formatTypeAlias(alias) {
369
+ const { name, type, comment } = alias;
370
+ const parts = [];
371
+ if (comment) {
372
+ parts.push(`/**
373
+ * ${comment}
374
+ */
375
+ `);
376
+ }
377
+ parts.push(`export type ${name} = ${type};
378
+
379
+ `);
380
+ const values = type.split(" | ").map((v) => v.trim());
381
+ parts.push(`/** All ${name} values */
382
+ `);
383
+ parts.push(`export const ${name}Values: ${name}[] = [${values.join(", ")}];
384
+
385
+ `);
386
+ parts.push(`/** Type guard for ${name} */
387
+ `);
388
+ parts.push(`export function is${name}(value: unknown): value is ${name} {
389
+ `);
390
+ parts.push(` return ${name}Values.includes(value as ${name});
391
+ `);
392
+ parts.push(`}
393
+
394
+ `);
395
+ parts.push(`/** Get label for ${name} value (returns value as-is) */
396
+ `);
397
+ parts.push(`export function get${name}Label(value: ${name}): string {
398
+ `);
399
+ parts.push(` return value;
400
+ `);
401
+ parts.push(`}
402
+
403
+ `);
404
+ parts.push(`/** Get extra metadata for ${name} value (always undefined for type aliases) */
405
+ `);
406
+ parts.push(`export function get${name}Extra(_value: ${name}): Record<string, unknown> | undefined {
407
+ `);
408
+ parts.push(` return undefined;
409
+ `);
410
+ parts.push(`}`);
411
+ return parts.join("");
412
+ }
413
+ function extractInlineEnums(schemas, options = {}) {
414
+ const typeAliases = [];
415
+ for (const schema of Object.values(schemas)) {
416
+ if (schema.kind === "enum" || !schema.properties) {
417
+ continue;
418
+ }
419
+ for (const [propName, property] of Object.entries(schema.properties)) {
420
+ if (property.type === "Enum") {
421
+ const enumProp = property;
422
+ if (Array.isArray(enumProp.enum) && enumProp.enum.length > 0) {
423
+ const typeName = `${schema.name}${propName.charAt(0).toUpperCase() + propName.slice(1)}`;
424
+ const values = enumProp.enum.map(
425
+ (v) => typeof v === "string" ? v : v.value
426
+ );
427
+ const displayName = resolveDisplayName2(enumProp.displayName, options);
428
+ typeAliases.push({
429
+ name: typeName,
430
+ type: values.map((v) => `'${v}'`).join(" | "),
431
+ comment: displayName ?? `${schema.name} ${propName} enum`
432
+ });
433
+ }
434
+ }
435
+ if (property.type === "Select") {
436
+ const selectProp = property;
437
+ if (selectProp.options && selectProp.options.length > 0) {
438
+ const typeName = `${schema.name}${propName.charAt(0).toUpperCase() + propName.slice(1)}`;
439
+ const displayName = resolveDisplayName2(selectProp.displayName, options);
440
+ typeAliases.push({
441
+ name: typeName,
442
+ type: selectProp.options.map((v) => `'${v}'`).join(" | "),
443
+ comment: displayName ?? `${schema.name} ${propName} options`
444
+ });
445
+ }
446
+ }
447
+ }
448
+ }
449
+ return typeAliases;
450
+ }
451
+
452
+ // src/generator.ts
453
+ var DEFAULT_OPTIONS = {
454
+ readonly: true,
455
+ strictNullChecks: true
456
+ };
457
+ function generateBaseHeader() {
458
+ return `/**
459
+ * Auto-generated TypeScript types from Omnify schemas.
460
+ * DO NOT EDIT - This file is automatically generated and will be overwritten.
461
+ */
462
+
463
+ `;
464
+ }
465
+ function generateModelHeader(schemaName) {
466
+ return `/**
467
+ * ${schemaName} Model
468
+ *
469
+ * This file extends the auto-generated base interface.
470
+ * You can add custom methods, computed properties, or override types here.
471
+ * This file will NOT be overwritten by the generator.
472
+ */
473
+
474
+ `;
475
+ }
476
+ function generateBaseInterfaceFile(schemaName, schemas, options) {
477
+ const interfaces = generateInterfaces(schemas, options);
478
+ const iface = interfaces.find((i) => i.name === schemaName);
479
+ if (!iface) {
480
+ throw new Error(`Interface not found for schema: ${schemaName}`);
481
+ }
482
+ const parts = [generateBaseHeader()];
483
+ parts.push(formatInterface(iface));
484
+ parts.push("\n");
485
+ return {
486
+ filePath: `base/${schemaName}.ts`,
487
+ content: parts.join(""),
488
+ types: [schemaName],
489
+ overwrite: true
490
+ };
491
+ }
492
+ function generateEnumFile(enumDef) {
493
+ const parts = [generateBaseHeader()];
494
+ parts.push(formatEnum(enumDef));
495
+ parts.push("\n");
496
+ return {
497
+ filePath: `enum/${enumDef.name}.ts`,
498
+ content: parts.join(""),
499
+ types: [enumDef.name],
500
+ overwrite: true
501
+ };
502
+ }
503
+ function generateTypeAliasFile(alias) {
504
+ const parts = [generateBaseHeader()];
505
+ parts.push(formatTypeAlias(alias));
506
+ parts.push("\n");
507
+ return {
508
+ filePath: `enum/${alias.name}.ts`,
509
+ content: parts.join(""),
510
+ types: [alias.name],
511
+ overwrite: true
512
+ };
513
+ }
514
+ function generateModelFile(schemaName) {
515
+ const parts = [generateModelHeader(schemaName)];
516
+ parts.push(`import type { ${schemaName} as ${schemaName}Base } from './base/${schemaName}.js';
517
+
518
+ `);
519
+ parts.push(`/**
520
+ * ${schemaName} model interface.
521
+ * Add custom properties or methods here.
522
+ */
523
+ `);
524
+ parts.push(`export interface ${schemaName} extends ${schemaName}Base {
525
+ `);
526
+ parts.push(` // Add custom properties here
527
+ `);
528
+ parts.push(`}
529
+
530
+ `);
531
+ parts.push(`// Re-export base type for internal use
532
+ `);
533
+ parts.push(`export type { ${schemaName}Base };
534
+ `);
535
+ return {
536
+ filePath: `${schemaName}.ts`,
537
+ content: parts.join(""),
538
+ types: [schemaName],
539
+ overwrite: false
540
+ // Never overwrite user models
541
+ };
542
+ }
543
+ function generateIndexFile(schemas, enums, typeAliases) {
544
+ const parts = [generateBaseHeader()];
545
+ if (enums.length > 0 || typeAliases.length > 0) {
546
+ parts.push(`// Enums
547
+ `);
548
+ for (const enumDef of enums) {
549
+ parts.push(`export {
550
+ `);
551
+ parts.push(` ${enumDef.name},
552
+ `);
553
+ parts.push(` ${enumDef.name}Values,
554
+ `);
555
+ parts.push(` is${enumDef.name},
556
+ `);
557
+ parts.push(` get${enumDef.name}Label,
558
+ `);
559
+ parts.push(` get${enumDef.name}Extra,
560
+ `);
561
+ parts.push(`} from './enum/${enumDef.name}.js';
562
+ `);
563
+ }
564
+ for (const alias of typeAliases) {
565
+ parts.push(`export {
566
+ `);
567
+ parts.push(` type ${alias.name},
568
+ `);
569
+ parts.push(` ${alias.name}Values,
570
+ `);
571
+ parts.push(` is${alias.name},
572
+ `);
573
+ parts.push(` get${alias.name}Label,
574
+ `);
575
+ parts.push(` get${alias.name}Extra,
576
+ `);
577
+ parts.push(`} from './enum/${alias.name}.js';
578
+ `);
579
+ }
580
+ parts.push("\n");
581
+ }
582
+ parts.push(`// Models
583
+ `);
584
+ for (const schema of Object.values(schemas)) {
585
+ if (schema.kind === "enum") continue;
586
+ parts.push(`export type { ${schema.name} } from './${schema.name}.js';
587
+ `);
588
+ }
589
+ return {
590
+ filePath: "index.ts",
591
+ content: parts.join(""),
592
+ types: [],
593
+ overwrite: true
594
+ };
595
+ }
596
+ function generateTypeScript(schemas, options = {}) {
597
+ const opts = { ...DEFAULT_OPTIONS, ...options };
598
+ const files = [];
599
+ const enums = generateEnums(schemas);
600
+ for (const enumDef of enums) {
601
+ files.push(generateEnumFile(enumDef));
602
+ }
603
+ const typeAliases = extractInlineEnums(schemas);
604
+ for (const alias of typeAliases) {
605
+ files.push(generateTypeAliasFile(alias));
606
+ }
607
+ for (const schema of Object.values(schemas)) {
608
+ if (schema.kind === "enum") continue;
609
+ files.push(generateBaseInterfaceFile(schema.name, schemas, opts));
610
+ }
611
+ for (const schema of Object.values(schemas)) {
612
+ if (schema.kind === "enum") continue;
613
+ files.push(generateModelFile(schema.name));
614
+ }
615
+ files.push(generateIndexFile(schemas, enums, typeAliases));
616
+ return files;
617
+ }
618
+
619
+ export {
620
+ toPropertyName,
621
+ toInterfaceName,
622
+ getPropertyType,
623
+ propertyToTSProperty,
624
+ schemaToInterface,
625
+ formatProperty,
626
+ formatInterface,
627
+ generateInterfaces,
628
+ toEnumMemberName,
629
+ toEnumName,
630
+ schemaToEnum,
631
+ generateEnums,
632
+ formatEnum,
633
+ enumToUnionType,
634
+ formatTypeAlias,
635
+ extractInlineEnums,
636
+ generateTypeScript
637
+ };
638
+ //# sourceMappingURL=chunk-J46F3EBS.js.map