@gzl10/nexus-sdk 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -5,14 +5,9 @@ export { CookieOptions, NextFunction, Request, RequestHandler, Response, Router
5
5
  import { Logger } from 'pino';
6
6
 
7
7
  /**
8
- * Generadores de código desde EntityDefinition
9
- *
10
- * Solo genera modelos TypeScript (interfaces).
11
- * Schemas Zod y migraciones se generan en runtime por el backend.
8
+ * Type guards y utilidades para EntityDefinitions
12
9
  */
13
10
 
14
- /** Directorio estándar para código generado */
15
- declare const GENERATED_DIR = "__generated__";
16
11
  /**
17
12
  * Entidades que persisten en BD local con tabla propia
18
13
  * Excluye: action, external, virtual, computed, single (usa tabla compartida)
@@ -34,23 +29,6 @@ declare function isSingletonEntity(entity: EntityDefinition): entity is SingleEn
34
29
  * Type guard para verificar si una entidad tiene tabla propia (para migraciones)
35
30
  */
36
31
  declare function hasTable(entity: EntityDefinition): entity is PersistentEntityDefinition;
37
- /**
38
- * Genera interface TypeScript desde EntityDefinition
39
- *
40
- * @example
41
- * const code = generateModel(postEntity)
42
- * // Genera:
43
- * // export interface Post {
44
- * // id: string
45
- * // title: string
46
- * // ...
47
- * // }
48
- */
49
- declare function generateModel(entity: PersistentEntityDefinition): string;
50
- /**
51
- * Genera interface TypeScript para entidades read-only
52
- */
53
- declare function generateReadOnlyModel(entity: ComputedEntityDefinition | ExternalEntityDefinition | VirtualEntityDefinition): string;
54
32
  /**
55
33
  * Obtiene el nombre de una entidad en PascalCase singular
56
34
  * 'cms_posts' → 'Post', 'rol_role_permissions' → 'RolePermission'
@@ -60,15 +38,6 @@ declare function getEntityName(entity: EntityDefinition): string;
60
38
  * Obtiene el subject CASL de una entidad
61
39
  */
62
40
  declare function getEntitySubject(entity: PersistentEntityDefinition): string;
63
- /**
64
- * Genera archivo completo de modelos TypeScript para múltiples entidades
65
- * Consolida todas las entidades de un módulo en un solo archivo
66
- *
67
- * @example
68
- * const code = generateModelsFile([userEntity, roleEntity])
69
- * writeFileSync('users.models.ts', code)
70
- */
71
- declare function generateModelsFile(definitions: EntityDefinition[]): string;
72
41
 
73
42
  /**
74
43
  * @gzl10/nexus-sdk
@@ -145,7 +114,7 @@ type DbType = 'string' | 'text' | 'integer' | 'decimal' | 'boolean' | 'date' | '
145
114
  /**
146
115
  * Tipos de input en UI
147
116
  */
148
- type InputType = 'text' | 'email' | 'password' | 'url' | 'tel' | 'number' | 'decimal' | 'textarea' | 'markdown' | 'select' | 'multiselect' | 'checkbox' | 'switch' | 'date' | 'datetime' | 'file' | 'fileArray' | 'image' | 'imageArray' | 'hidden';
117
+ type InputType = 'text' | 'email' | 'password' | 'url' | 'tel' | 'number' | 'decimal' | 'textarea' | 'markdown' | 'select' | 'multiselect' | 'tags' | 'checkbox' | 'switch' | 'date' | 'datetime' | 'file' | 'fileArray' | 'image' | 'imageArray' | 'hidden';
149
118
  /**
150
119
  * Configuración de base de datos para un campo
151
120
  */
@@ -262,7 +231,7 @@ interface EntityIndex {
262
231
  /**
263
232
  * Acciones CASL estándar
264
233
  */
265
- type CaslAction = 'manage' | 'create' | 'read' | 'update' | 'delete';
234
+ type CaslAction = 'manage' | 'create' | 'read' | 'update' | 'delete' | 'execute';
266
235
  /**
267
236
  * Condición de ownership para permisos
268
237
  * El campo de la entidad que referencia al usuario
@@ -446,6 +415,8 @@ interface ExternalEntityDefinition {
446
415
  ttl: number;
447
416
  /** Campo para generar cache key */
448
417
  key?: string;
418
+ /** Eventos que invalidan la cache (ej: ['db.users.*']) */
419
+ invalidateOn?: string[];
449
420
  };
450
421
  /** Autorización CASL */
451
422
  casl?: EntityCaslConfig;
@@ -491,6 +462,8 @@ interface ComputedEntityDefinition {
491
462
  cache?: {
492
463
  /** TTL en segundos (0 = sin cache) */
493
464
  ttl: number;
465
+ /** Eventos que invalidan la cache (ej: ['db.users.*']) */
466
+ invalidateOn?: string[];
494
467
  };
495
468
  /** Autorización CASL */
496
469
  casl?: EntityCaslConfig;
@@ -959,6 +932,8 @@ interface ModuleManifest {
959
932
  description?: string;
960
933
  /** Tipo de módulo */
961
934
  type?: 'core' | 'plugin' | 'auth-plugin' | 'custom';
935
+ /** Categoría del módulo para agrupar en sidebar */
936
+ category: Category;
962
937
  /** Dependencias de otros módulos */
963
938
  dependencies?: string[];
964
939
  /** Requisitos para activar el módulo */
@@ -981,9 +956,25 @@ interface ModuleManifest {
981
956
  definitions?: EntityDefinition[];
982
957
  }
983
958
  /**
984
- * Categorías disponibles para plugins
959
+ * Categorías disponibles para plugins y módulos
960
+ */
961
+ type Category = 'content' | 'data' | 'storage' | 'messaging' | 'jobs' | 'ai' | 'analytics' | 'integrations' | 'commerce' | 'security';
962
+ /**
963
+ * Metadata de una categoría
964
+ */
965
+ interface CategoryMeta {
966
+ label: string;
967
+ icon: string;
968
+ order: number;
969
+ }
970
+ /**
971
+ * Registro centralizado de categorías con su metadata
972
+ */
973
+ declare const CATEGORIES: Record<Category, CategoryMeta>;
974
+ /**
975
+ * Orden de categorías para sidebar (derivado de CATEGORIES)
985
976
  */
986
- type PluginCategory = 'content' | 'data' | 'storage' | 'messaging' | 'jobs' | 'ai' | 'analytics' | 'integrations' | 'commerce';
977
+ declare const CATEGORY_ORDER: Category[];
987
978
  /**
988
979
  * Manifest de un plugin Nexus
989
980
  */
@@ -997,7 +988,7 @@ interface PluginManifest {
997
988
  /** Icono del plugin (Iconify MDI). Se hereda a los módulos si no lo definen */
998
989
  icon?: string;
999
990
  /** Categoría del plugin para agrupar en sidebar */
1000
- category?: PluginCategory;
991
+ category: Category;
1001
992
  /** Versión del plugin (semver) */
1002
993
  version: string;
1003
994
  /** Descripción del plugin */
@@ -1006,4 +997,4 @@ interface PluginManifest {
1006
997
  modules: ModuleManifest[];
1007
998
  }
1008
999
 
1009
- export { type AbilityLike, type ActionEntityDefinition, type ActionEntityService, type AuthRequest, type BaseEntityService, type BaseMailService, type BaseRolesService, type BaseUser, type BaseUsersService, type CaslAction, type CollectionEntityDefinition, type CollectionEntityService, type ComputedEntityDefinition, type ComputedEntityService, type ConfigEntityDefinition, type ConfigEntityService, type ContextHelpers, type CoreServices, type CreateEntityServiceOptions, type DbType, type EntityCaslConfig, type EntityController, type EntityDefinition, type EntityHandler, type EntityIndex, type EntityQuery, type EntityServiceHooks, type EntityType, type EventEmitterLike, type EventEntityDefinition, type EventEntityService, type ExternalEntityDefinition, type ExternalEntityService, type FieldCaslAccess, type FieldDbConfig, type FieldDefinition, type FieldMeta, type FieldOptions, type FieldRelation, type FieldStorageConfig, type FieldValidationConfig, type ForbiddenErrorConstructor, type ForbiddenErrorInstance, GENERATED_DIR, type InputType, type KnexAlterTableBuilder, type KnexCreateTableBuilder, type KnexTransaction, type LoggerReporter, type ModuleAbilities, type ModuleContext, type ModuleManifest, type ModuleMiddlewares, type ModuleRequirements, type NonPersistentEntityDefinition, type OwnershipCondition, type PaginatedResult, type PaginationParams, type PersistentEntityDefinition, type PluginAuthRequest, type PluginCategory, type PluginManifest, type ReferenceEntityDefinition, type ReferenceEntityService, type RolePermission, type SendMailOptions, type SendMailResult, type SingleEntityDefinition, type SingleEntityService, type TempEntityDefinition, type TempEntityService, type ValidateSchemas, type ValidationSchema, type ViewEntityDefinition, type ViewEntityService, type VirtualEntityDefinition, type VirtualEntityService, generateModel, generateModelsFile, generateReadOnlyModel, getEntityName, getEntitySubject, hasTable, isPersistentEntity, isSingletonEntity };
1000
+ export { type AbilityLike, type ActionEntityDefinition, type ActionEntityService, type AuthRequest, type BaseEntityService, type BaseMailService, type BaseRolesService, type BaseUser, type BaseUsersService, CATEGORIES, CATEGORY_ORDER, type CaslAction, type Category, type CategoryMeta, type CollectionEntityDefinition, type CollectionEntityService, type ComputedEntityDefinition, type ComputedEntityService, type ConfigEntityDefinition, type ConfigEntityService, type ContextHelpers, type CoreServices, type CreateEntityServiceOptions, type DbType, type EntityCaslConfig, type EntityController, type EntityDefinition, type EntityHandler, type EntityIndex, type EntityQuery, type EntityServiceHooks, type EntityType, type EventEmitterLike, type EventEntityDefinition, type EventEntityService, type ExternalEntityDefinition, type ExternalEntityService, type FieldCaslAccess, type FieldDbConfig, type FieldDefinition, type FieldMeta, type FieldOptions, type FieldRelation, type FieldStorageConfig, type FieldValidationConfig, type ForbiddenErrorConstructor, type ForbiddenErrorInstance, type InputType, type KnexAlterTableBuilder, type KnexCreateTableBuilder, type KnexTransaction, type LoggerReporter, type ModuleAbilities, type ModuleContext, type ModuleManifest, type ModuleMiddlewares, type ModuleRequirements, type NonPersistentEntityDefinition, type OwnershipCondition, type PaginatedResult, type PaginationParams, type PersistentEntityDefinition, type PluginAuthRequest, type PluginManifest, type ReferenceEntityDefinition, type ReferenceEntityService, type RolePermission, type SendMailOptions, type SendMailResult, type SingleEntityDefinition, type SingleEntityService, type TempEntityDefinition, type TempEntityService, type ValidateSchemas, type ValidationSchema, type ViewEntityDefinition, type ViewEntityService, type VirtualEntityDefinition, type VirtualEntityService, getEntityName, getEntitySubject, hasTable, isPersistentEntity, isSingletonEntity };
package/dist/index.js CHANGED
@@ -1,19 +1,63 @@
1
- import {
2
- GENERATED_DIR,
3
- generateModel,
4
- generateModelsFile,
5
- generateReadOnlyModel,
6
- getEntityName,
7
- getEntitySubject,
8
- hasTable,
9
- isPersistentEntity,
10
- isSingletonEntity
11
- } from "./chunk-LQZB6HXM.js";
1
+ // src/generators.ts
2
+ function isPersistentEntity(entity) {
3
+ const withoutOwnTable = ["action", "external", "virtual", "computed", "single"];
4
+ return !withoutOwnTable.includes(entity.type ?? "collection");
5
+ }
6
+ function isSingletonEntity(entity) {
7
+ return entity.type === "single";
8
+ }
9
+ function hasTable(entity) {
10
+ return "table" in entity && typeof entity.table === "string";
11
+ }
12
+ function getEntityName(entity) {
13
+ if ("table" in entity) {
14
+ const withoutPrefix = entity.table.replace(/^[a-z]{2,4}_/, "");
15
+ return toPascalCase(toSingular(withoutPrefix));
16
+ } else if ("key" in entity) {
17
+ return toPascalCase(entity.key);
18
+ } else {
19
+ return toSingular(entity.label).replace(/\s+/g, "");
20
+ }
21
+ }
22
+ function getEntitySubject(entity) {
23
+ return entity.casl?.subject ?? tableToSubject(entity.table);
24
+ }
25
+ function toPascalCase(str) {
26
+ return str.split("_").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
27
+ }
28
+ function toSingular(str) {
29
+ if (str.endsWith("ies")) return str.slice(0, -3) + "y";
30
+ if (str.endsWith("s")) return str.slice(0, -1);
31
+ return str;
32
+ }
33
+ function tableToSubject(table) {
34
+ const match = table.match(/^([a-z]{2,4})_(.+)$/);
35
+ if (!match) {
36
+ const withoutPrefix = table.replace(/^[a-z]{2,4}_/, "");
37
+ return toPascalCase(toSingular(withoutPrefix));
38
+ }
39
+ const [, prefix, rest] = match;
40
+ const prefixPascal = prefix.charAt(0).toUpperCase() + prefix.slice(1);
41
+ return prefixPascal + toPascalCase(toSingular(rest));
42
+ }
43
+
44
+ // src/index.ts
45
+ var CATEGORIES = {
46
+ data: { label: "Data", icon: "mdi:database", order: 1 },
47
+ content: { label: "Content", icon: "mdi:file-document-outline", order: 2 },
48
+ storage: { label: "Storage", icon: "mdi:folder-outline", order: 3 },
49
+ messaging: { label: "Messaging", icon: "mdi:message-outline", order: 4 },
50
+ jobs: { label: "Jobs", icon: "mdi:clock-outline", order: 5 },
51
+ ai: { label: "AI", icon: "mdi:robot-outline", order: 6 },
52
+ analytics: { label: "Analytics", icon: "mdi:chart-line", order: 7 },
53
+ integrations: { label: "Integrations", icon: "mdi:puzzle-outline", order: 8 },
54
+ commerce: { label: "Commerce", icon: "mdi:shopping-outline", order: 9 },
55
+ security: { label: "Security", icon: "mdi:shield-lock-outline", order: 10 }
56
+ };
57
+ var CATEGORY_ORDER = Object.keys(CATEGORIES).sort((a, b) => CATEGORIES[a].order - CATEGORIES[b].order);
12
58
  export {
13
- GENERATED_DIR,
14
- generateModel,
15
- generateModelsFile,
16
- generateReadOnlyModel,
59
+ CATEGORIES,
60
+ CATEGORY_ORDER,
17
61
  getEntityName,
18
62
  getEntitySubject,
19
63
  hasTable,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gzl10/nexus-sdk",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "SDK types for creating Nexus plugins and modules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,9 +11,6 @@
11
11
  "import": "./dist/index.js"
12
12
  }
13
13
  },
14
- "bin": {
15
- "nexus-sdk": "./dist/cli/index.js"
16
- },
17
14
  "files": [
18
15
  "dist"
19
16
  ],
@@ -63,15 +60,8 @@
63
60
  "access": "public",
64
61
  "registry": "https://registry.npmjs.org"
65
62
  },
66
- "dependencies": {
67
- "chokidar": "^5.0.0",
68
- "commander": "^14.0.2",
69
- "fast-glob": "^3.3.3",
70
- "jiti": "^2.6.1",
71
- "picocolors": "^1.1.1"
72
- },
73
63
  "scripts": {
74
- "build": "tsup src/index.ts src/cli/index.ts --format esm --dts --clean",
64
+ "build": "tsup src/index.ts --format esm --dts --clean",
75
65
  "typecheck": "tsc --noEmit"
76
66
  }
77
67
  }
@@ -1,194 +0,0 @@
1
- // src/generators.ts
2
- var GENERATED_DIR = "__generated__";
3
- function isPersistentEntity(entity) {
4
- const withoutOwnTable = ["action", "external", "virtual", "computed", "single"];
5
- return !withoutOwnTable.includes(entity.type ?? "collection");
6
- }
7
- function isSingletonEntity(entity) {
8
- return entity.type === "single";
9
- }
10
- function hasTable(entity) {
11
- return "table" in entity && typeof entity.table === "string";
12
- }
13
- function generateModel(entity) {
14
- const { table, fields } = entity;
15
- const timestamps = "timestamps" in entity ? entity.timestamps : false;
16
- const audit = "audit" in entity ? entity.audit : false;
17
- const entityName = tableToEntityName(table);
18
- const lines = [
19
- `/**`,
20
- ` * ${entity.label}`,
21
- ` * Generated from EntityDefinition`,
22
- ` */`,
23
- `export interface ${entityName} {`
24
- ];
25
- for (const [name, field] of Object.entries(fields)) {
26
- const tsType = dbTypeToTsType(field.db);
27
- const optional = field.db.nullable ? "?" : "";
28
- lines.push(` ${name}${optional}: ${tsType}`);
29
- }
30
- if (timestamps) {
31
- if (!fields["created_at"]) {
32
- lines.push(` created_at: Date`);
33
- }
34
- if (!fields["updated_at"]) {
35
- lines.push(` updated_at: Date`);
36
- }
37
- }
38
- if (audit) {
39
- if (!fields["created_by"]) {
40
- lines.push(` created_by: string | null`);
41
- }
42
- if (!fields["updated_by"]) {
43
- lines.push(` updated_by: string | null`);
44
- }
45
- }
46
- lines.push(`}`);
47
- lines.push(``);
48
- return lines.join("\n");
49
- }
50
- function dbTypeToTsType(db) {
51
- switch (db.type) {
52
- case "string":
53
- case "text":
54
- case "uuid":
55
- return "string";
56
- case "integer":
57
- case "decimal":
58
- return "number";
59
- case "boolean":
60
- return "boolean";
61
- case "date":
62
- case "datetime":
63
- return "Date";
64
- case "json":
65
- return "Record<string, unknown>";
66
- default:
67
- return "unknown";
68
- }
69
- }
70
- function generateReadOnlyModel(entity) {
71
- const { label, fields } = entity;
72
- const entityName = labelToEntityName(label);
73
- const lines = [
74
- `/**`,
75
- ` * ${label}`,
76
- ` * Generated from EntityDefinition (${entity.type})`,
77
- ` */`,
78
- `export interface ${entityName} {`
79
- ];
80
- for (const [name, field] of Object.entries(fields)) {
81
- const tsType = dbTypeToTsType(field.db);
82
- const optional = field.db.nullable ? "?" : "";
83
- lines.push(` ${name}${optional}: ${tsType}`);
84
- }
85
- lines.push(`}`);
86
- lines.push(``);
87
- return lines.join("\n");
88
- }
89
- function getEntityName(entity) {
90
- if ("table" in entity) {
91
- const withoutPrefix = entity.table.replace(/^[a-z]{2,4}_/, "");
92
- return toPascalCase(toSingular(withoutPrefix));
93
- } else if ("key" in entity) {
94
- return toPascalCase(entity.key);
95
- } else {
96
- return toSingular(entity.label).replace(/\s+/g, "");
97
- }
98
- }
99
- function getEntitySubject(entity) {
100
- return entity.casl?.subject ?? tableToSubject(entity.table);
101
- }
102
- function dbTypeToTsSimple(type) {
103
- switch (type) {
104
- case "string":
105
- case "text":
106
- case "uuid":
107
- return "string";
108
- case "integer":
109
- case "decimal":
110
- return "number";
111
- case "boolean":
112
- return "boolean";
113
- case "date":
114
- case "datetime":
115
- return "Date";
116
- case "json":
117
- return "Record<string, unknown>";
118
- default:
119
- return "unknown";
120
- }
121
- }
122
- function generateModelsFile(definitions) {
123
- const lines = [
124
- "/**",
125
- " * AUTO-GENERATED - Do not edit manually",
126
- " * Generated from EntityDefinition via @gzl10/nexus-sdk generators",
127
- " */",
128
- ""
129
- ];
130
- for (const entity of definitions) {
131
- const name = getEntityName(entity);
132
- lines.push(`// ============================================================================`);
133
- lines.push(`// ${name.toUpperCase()} (${entity.type ?? "collection"})`);
134
- lines.push(`// ============================================================================`);
135
- lines.push("");
136
- if (isPersistentEntity(entity)) {
137
- const model = generateModel(entity);
138
- const modelLines = model.split("\n");
139
- lines.push(...modelLines);
140
- } else if (entity.type === "single") {
141
- lines.push(`/**`);
142
- lines.push(` * ${entity.label}`);
143
- lines.push(` * Generated from EntityDefinition (single)`);
144
- lines.push(` */`);
145
- lines.push(`export interface ${name} {`);
146
- for (const [fieldName, field] of Object.entries(entity.fields)) {
147
- const tsType = dbTypeToTsSimple(field.db.type);
148
- const optional = field.db.nullable ? "?" : "";
149
- lines.push(` ${fieldName}${optional}: ${tsType}`);
150
- }
151
- lines.push("}");
152
- } else {
153
- const model = generateReadOnlyModel(entity);
154
- lines.push(model);
155
- }
156
- lines.push("");
157
- }
158
- return lines.join("\n");
159
- }
160
- function toPascalCase(str) {
161
- return str.split("_").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
162
- }
163
- function toSingular(str) {
164
- if (str.endsWith("ies")) return str.slice(0, -3) + "y";
165
- if (str.endsWith("s")) return str.slice(0, -1);
166
- return str;
167
- }
168
- function tableToEntityName(table) {
169
- const withoutPrefix = table.replace(/^[a-z]{2,4}_/, "");
170
- return toPascalCase(toSingular(withoutPrefix));
171
- }
172
- function tableToSubject(table) {
173
- const match = table.match(/^([a-z]{2,4})_(.+)$/);
174
- if (!match) return tableToEntityName(table);
175
- const [, prefix, rest] = match;
176
- const prefixPascal = prefix.charAt(0).toUpperCase() + prefix.slice(1);
177
- return prefixPascal + toPascalCase(toSingular(rest));
178
- }
179
- function labelToEntityName(label) {
180
- const singular = label.endsWith("ies") ? label.slice(0, -3) + "y" : label.endsWith("s") ? label.slice(0, -1) : label;
181
- return singular.charAt(0).toUpperCase() + singular.slice(1).replace(/\s+/g, "");
182
- }
183
-
184
- export {
185
- GENERATED_DIR,
186
- isPersistentEntity,
187
- isSingletonEntity,
188
- hasTable,
189
- generateModel,
190
- generateReadOnlyModel,
191
- getEntityName,
192
- getEntitySubject,
193
- generateModelsFile
194
- };
@@ -1 +0,0 @@
1
- #!/usr/bin/env node
package/dist/cli/index.js DELETED
@@ -1,155 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- GENERATED_DIR,
4
- generateModelsFile
5
- } from "../chunk-LQZB6HXM.js";
6
-
7
- // src/cli/index.ts
8
- import { Command as Command2 } from "commander";
9
-
10
- // src/cli/commands/generate.ts
11
- import { Command } from "commander";
12
- import { join, dirname } from "path";
13
- import { writeFileSync, mkdirSync, existsSync } from "fs";
14
- import pc from "picocolors";
15
- import { watch } from "chokidar";
16
-
17
- // src/cli/discovery/module-finder.ts
18
- import fg from "fast-glob";
19
- async function findModules(opts) {
20
- const pattern = opts.module ? `**/${opts.module}/index.ts` : "**/index.ts";
21
- const files = await fg(pattern, {
22
- cwd: opts.path,
23
- absolute: true,
24
- ignore: [
25
- "**/node_modules/**",
26
- "**/dist/**",
27
- "**/__generated__/**",
28
- "index.ts"
29
- // Exclude root modules/index.ts (not nested ones)
30
- ]
31
- });
32
- return files.filter((file) => {
33
- const parts = file.split("/");
34
- const fileName = parts.pop();
35
- const folderName = parts.pop();
36
- return fileName === "index.ts" && folderName && !folderName.startsWith("_");
37
- });
38
- }
39
-
40
- // src/cli/discovery/manifest-extractor.ts
41
- import { createJiti } from "jiti";
42
- var jiti = createJiti(import.meta.url, {
43
- interopDefault: true,
44
- moduleCache: false
45
- // Don't cache for watch mode
46
- });
47
- function isModuleManifest(value) {
48
- return typeof value === "object" && value !== null && "name" in value && typeof value.name === "string";
49
- }
50
- async function extractManifest(modulePath, verbose = false) {
51
- try {
52
- const mod = await jiti.import(modulePath);
53
- for (const value of Object.values(mod)) {
54
- if (isModuleManifest(value)) {
55
- const manifest = value;
56
- if (manifest.definitions?.length) {
57
- return {
58
- path: modulePath,
59
- manifest,
60
- definitions: manifest.definitions
61
- };
62
- }
63
- }
64
- }
65
- return null;
66
- } catch (error) {
67
- if (verbose) {
68
- console.error(`Error loading ${modulePath}: ${getErrorMessage(error)}`);
69
- }
70
- return null;
71
- }
72
- }
73
- function getErrorMessage(error) {
74
- if (error instanceof Error) {
75
- return error.message;
76
- }
77
- return String(error);
78
- }
79
-
80
- // src/cli/commands/generate.ts
81
- async function runGenerate(opts) {
82
- const startTime = Date.now();
83
- console.log(pc.blue("Nexus SDK Generator"));
84
- console.log(pc.dim(`Scanning ${opts.path}...
85
- `));
86
- const modulePaths = await findModules({ path: opts.path, module: opts.module });
87
- if (opts.verbose) {
88
- console.log(pc.dim(`Found ${modulePaths.length} potential module(s)`));
89
- }
90
- if (modulePaths.length === 0) {
91
- console.log(pc.yellow("No modules found"));
92
- return;
93
- }
94
- const modules = [];
95
- for (const path of modulePaths) {
96
- const extracted = await extractManifest(path, opts.verbose);
97
- if (extracted) {
98
- modules.push(extracted);
99
- if (opts.verbose) {
100
- console.log(pc.dim(` \u2713 ${extracted.manifest.name}: ${extracted.definitions.length} entities`));
101
- }
102
- }
103
- }
104
- if (modules.length === 0) {
105
- console.log(pc.yellow("No modules with definitions found"));
106
- return;
107
- }
108
- let totalFiles = 0;
109
- for (const mod of modules) {
110
- const moduleDir = dirname(mod.path);
111
- const outputDir = join(moduleDir, opts.output || GENERATED_DIR);
112
- if (!opts.dryRun && !existsSync(outputDir)) {
113
- mkdirSync(outputDir, { recursive: true });
114
- }
115
- const content = generateModelsFile(mod.definitions);
116
- const filePath = join(outputDir, `${mod.manifest.name}.models.ts`);
117
- if (!opts.dryRun) writeFileSync(filePath, content);
118
- totalFiles++;
119
- const prefix = opts.dryRun ? pc.yellow("[dry-run]") : pc.green("\u2713");
120
- console.log(`${prefix} ${pc.bold(mod.manifest.name)}: models`);
121
- }
122
- const elapsed = Date.now() - startTime;
123
- console.log(
124
- `
125
- ${pc.green("\u2728")} Generated ${totalFiles} file(s) in ${modules.length} module(s) (${elapsed}ms)`
126
- );
127
- }
128
- function createGenerateCommand() {
129
- return new Command("generate").description("Generate TypeScript models from EntityDefinitions").option("-p, --path <dir>", "Base modules directory", "src/modules").option("-m, --module <name>", "Generate only specific module").option("-o, --output <dir>", "Output directory name", "__generated__").option("-w, --watch", "Watch for changes and regenerate").option("--dry-run", "Show what would be generated").option("--verbose", "Show detailed logs").action(async (opts) => {
130
- await runGenerate(opts);
131
- if (opts.watch) {
132
- console.log(pc.blue("\nWatching for changes..."));
133
- const watcher = watch(["**/*.entity.ts", "**/index.ts"], {
134
- cwd: opts.path,
135
- ignored: ["**/node_modules/**", "**/__generated__/**", "**/dist/**"],
136
- ignoreInitial: true
137
- });
138
- watcher.on("change", async (path) => {
139
- console.log(pc.dim(`
140
- Change detected: ${path}`));
141
- await runGenerate({ ...opts, watch: false });
142
- });
143
- watcher.on("add", async (path) => {
144
- console.log(pc.dim(`
145
- New file: ${path}`));
146
- await runGenerate({ ...opts, watch: false });
147
- });
148
- }
149
- });
150
- }
151
-
152
- // src/cli/index.ts
153
- var program = new Command2().name("nexus-sdk").description("Nexus SDK CLI - Code generation from EntityDefinitions").version("0.10.0");
154
- program.addCommand(createGenerateCommand());
155
- program.parse();