@gzl10/nexus-sdk 0.4.1 → 0.5.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 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ GENERATED_DIR,
4
+ generateCaslSeed,
5
+ generateMigrationsFile,
6
+ generateModelsFile,
7
+ generateSchemasFile
8
+ } from "../chunk-XEPNN6IG.js";
9
+
10
+ // src/cli/index.ts
11
+ import { Command as Command2 } from "commander";
12
+
13
+ // src/cli/commands/generate.ts
14
+ import { Command } from "commander";
15
+ import { join, dirname } from "path";
16
+ import { writeFileSync, mkdirSync, existsSync } from "fs";
17
+ import pc from "picocolors";
18
+ import { watch } from "chokidar";
19
+
20
+ // src/cli/discovery/module-finder.ts
21
+ import fg from "fast-glob";
22
+ async function findModules(opts) {
23
+ const pattern = opts.module ? `**/${opts.module}/index.ts` : "**/index.ts";
24
+ const files = await fg(pattern, {
25
+ cwd: opts.path,
26
+ absolute: true,
27
+ ignore: [
28
+ "**/node_modules/**",
29
+ "**/dist/**",
30
+ "**/__generated__/**",
31
+ "index.ts"
32
+ // Exclude root modules/index.ts (not nested ones)
33
+ ]
34
+ });
35
+ return files.filter((file) => {
36
+ const parts = file.split("/");
37
+ const fileName = parts.pop();
38
+ const folderName = parts.pop();
39
+ return fileName === "index.ts" && folderName && !folderName.startsWith("_");
40
+ });
41
+ }
42
+
43
+ // src/cli/discovery/manifest-extractor.ts
44
+ import { createJiti } from "jiti";
45
+ var jiti = createJiti(import.meta.url, {
46
+ interopDefault: true,
47
+ moduleCache: false
48
+ // Don't cache for watch mode
49
+ });
50
+ function isModuleManifest(value) {
51
+ return typeof value === "object" && value !== null && "name" in value && typeof value.name === "string";
52
+ }
53
+ async function extractManifest(modulePath, verbose = false) {
54
+ try {
55
+ const mod = await jiti.import(modulePath);
56
+ for (const value of Object.values(mod)) {
57
+ if (isModuleManifest(value)) {
58
+ const manifest = value;
59
+ if (manifest.definitions?.length) {
60
+ return {
61
+ path: modulePath,
62
+ manifest,
63
+ definitions: manifest.definitions
64
+ };
65
+ }
66
+ }
67
+ }
68
+ return null;
69
+ } catch (error) {
70
+ if (verbose) {
71
+ console.error(`Error loading ${modulePath}: ${getErrorMessage(error)}`);
72
+ }
73
+ return null;
74
+ }
75
+ }
76
+ function getErrorMessage(error) {
77
+ if (error instanceof Error) {
78
+ return error.message;
79
+ }
80
+ return String(error);
81
+ }
82
+
83
+ // src/cli/commands/generate.ts
84
+ async function runGenerate(opts) {
85
+ const startTime = Date.now();
86
+ console.log(pc.blue("Nexus SDK Generator"));
87
+ console.log(pc.dim(`Scanning ${opts.path}...
88
+ `));
89
+ const modulePaths = await findModules({ path: opts.path, module: opts.module });
90
+ if (opts.verbose) {
91
+ console.log(pc.dim(`Found ${modulePaths.length} potential module(s)`));
92
+ }
93
+ if (modulePaths.length === 0) {
94
+ console.log(pc.yellow("No modules found"));
95
+ return;
96
+ }
97
+ const modules = [];
98
+ for (const path of modulePaths) {
99
+ const extracted = await extractManifest(path, opts.verbose);
100
+ if (extracted) {
101
+ modules.push(extracted);
102
+ if (opts.verbose) {
103
+ console.log(pc.dim(` \u2713 ${extracted.manifest.name}: ${extracted.definitions.length} entities`));
104
+ }
105
+ }
106
+ }
107
+ if (modules.length === 0) {
108
+ console.log(pc.yellow("No modules with definitions found"));
109
+ return;
110
+ }
111
+ let totalFiles = 0;
112
+ for (const mod of modules) {
113
+ const moduleDir = dirname(mod.path);
114
+ const outputDir = join(moduleDir, opts.output || GENERATED_DIR);
115
+ if (!opts.dryRun && !existsSync(outputDir)) {
116
+ mkdirSync(outputDir, { recursive: true });
117
+ }
118
+ const files = [];
119
+ if (!opts.skipSchemas) {
120
+ const content = generateSchemasFile(mod.definitions);
121
+ const filePath = join(outputDir, `${mod.manifest.name}.schemas.ts`);
122
+ if (!opts.dryRun) writeFileSync(filePath, content);
123
+ files.push("schemas");
124
+ }
125
+ if (!opts.skipModels) {
126
+ const content = generateModelsFile(mod.definitions);
127
+ const filePath = join(outputDir, `${mod.manifest.name}.models.ts`);
128
+ if (!opts.dryRun) writeFileSync(filePath, content);
129
+ files.push("models");
130
+ }
131
+ if (!opts.skipMigrations) {
132
+ const content = generateMigrationsFile(mod.definitions);
133
+ const filePath = join(outputDir, `${mod.manifest.name}.migrate.ts`);
134
+ if (!opts.dryRun) writeFileSync(filePath, content);
135
+ files.push("migrate");
136
+ }
137
+ if (!opts.skipCasl) {
138
+ const content = generateCaslSeed(mod.definitions);
139
+ const filePath = join(outputDir, `${mod.manifest.name}.casl.ts`);
140
+ if (!opts.dryRun) writeFileSync(filePath, content);
141
+ files.push("casl");
142
+ }
143
+ totalFiles += files.length;
144
+ const prefix = opts.dryRun ? pc.yellow("[dry-run]") : pc.green("\u2713");
145
+ console.log(`${prefix} ${pc.bold(mod.manifest.name)}: ${files.join(", ")}`);
146
+ }
147
+ const elapsed = Date.now() - startTime;
148
+ console.log(
149
+ `
150
+ ${pc.green("\u2728")} Generated ${totalFiles} files in ${modules.length} module(s) (${elapsed}ms)`
151
+ );
152
+ }
153
+ function createGenerateCommand() {
154
+ return new Command("generate").description("Generate code 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("--skip-schemas", "Skip .schemas.ts generation").option("--skip-models", "Skip .models.ts generation").option("--skip-migrations", "Skip .migrate.ts generation").option("--skip-casl", "Skip .casl.ts generation").option("--verbose", "Show detailed logs").action(async (opts) => {
155
+ await runGenerate(opts);
156
+ if (opts.watch) {
157
+ console.log(pc.blue("\nWatching for changes..."));
158
+ const watcher = watch(["**/*.entity.ts", "**/index.ts"], {
159
+ cwd: opts.path,
160
+ ignored: ["**/node_modules/**", "**/__generated__/**", "**/dist/**"],
161
+ ignoreInitial: true
162
+ });
163
+ watcher.on("change", async (path) => {
164
+ console.log(pc.dim(`
165
+ Change detected: ${path}`));
166
+ await runGenerate({ ...opts, watch: false });
167
+ });
168
+ watcher.on("add", async (path) => {
169
+ console.log(pc.dim(`
170
+ New file: ${path}`));
171
+ await runGenerate({ ...opts, watch: false });
172
+ });
173
+ }
174
+ });
175
+ }
176
+
177
+ // src/cli/index.ts
178
+ var program = new Command2().name("nexus-sdk").description("Nexus SDK CLI - Code generation from EntityDefinitions").version("0.5.0");
179
+ program.addCommand(createGenerateCommand());
180
+ program.parse();
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Knex } from 'knex';
2
2
  export { Knex } from 'knex';
3
- import { Request, RequestHandler, Router } from 'express';
3
+ import { Request, Router, RequestHandler } from 'express';
4
4
  export { CookieOptions, NextFunction, Request, RequestHandler, Response, Router } from 'express';
5
5
  import { Logger } from 'pino';
6
6
 
@@ -427,6 +427,8 @@ interface CollectionEntityDefinition extends BaseEntityDefinition {
427
427
  audit?: boolean;
428
428
  /** Índices compuestos */
429
429
  indexes?: EntityIndex[];
430
+ /** Usar deleted_at en vez de DELETE físico */
431
+ softDelete?: boolean;
430
432
  }
431
433
  /**
432
434
  * Entidad singleton - Un solo registro en tabla compartida sys_settings
@@ -445,6 +447,8 @@ interface SingleEntityDefinition {
445
447
  label: string;
446
448
  /** Definición de campos (estructura del JSON value) */
447
449
  fields: Record<string, FieldDefinition>;
450
+ /** Valores por defecto al crear */
451
+ defaults?: Record<string, unknown>;
448
452
  /** Autorización CASL */
449
453
  casl?: EntityCaslConfig;
450
454
  }
@@ -459,6 +463,10 @@ interface ReferenceEntityDefinition extends BaseEntityDefinition {
459
463
  timestamps?: boolean;
460
464
  /** Índices compuestos */
461
465
  indexes?: EntityIndex[];
466
+ /** Datos iniciales para seed */
467
+ seed?: Array<Record<string, unknown>>;
468
+ /** Permitir CRUD para admin (default: false, solo lectura) */
469
+ allowAdminEdit?: boolean;
462
470
  }
463
471
  /**
464
472
  * Entidad de eventos - Logs de auditoría, append-only
@@ -469,6 +477,13 @@ interface EventEntityDefinition extends BaseEntityDefinition {
469
477
  labelField: string;
470
478
  /** Siempre true para eventos */
471
479
  timestamps: true;
480
+ /** Política de retención automática */
481
+ retention?: {
482
+ /** Eliminar registros más antiguos que N días */
483
+ days?: number;
484
+ /** Mantener solo los últimos N registros */
485
+ maxRows?: number;
486
+ };
472
487
  }
473
488
  /**
474
489
  * Entidad de acción - Comandos/operaciones sin persistencia
@@ -477,8 +492,16 @@ interface ActionEntityDefinition {
477
492
  type: 'action';
478
493
  /** Nombre para mostrar en UI */
479
494
  label: string;
480
- /** Solo campos de formulario, sin BD */
495
+ /** Solo campos de formulario para input, sin BD */
481
496
  fields: Record<string, FieldDefinition>;
497
+ /** Schema Zod de input (se valida antes de ejecutar) */
498
+ inputSchema?: ValidationSchema;
499
+ /** Schema Zod de output (documenta respuesta) */
500
+ outputSchema?: ValidationSchema;
501
+ /** Handler que ejecuta la acción */
502
+ handler?: (ctx: ModuleContext, input: unknown) => Promise<unknown>;
503
+ /** Autorización CASL */
504
+ casl?: EntityCaslConfig;
482
505
  }
483
506
  /**
484
507
  * Entidad externa - Datos de APIs externas (stripe_customers, github_repos)
@@ -492,12 +515,21 @@ interface ExternalEntityDefinition {
492
515
  labelField: string;
493
516
  /** Definición de campos (estructura esperada del API externo) */
494
517
  fields: Record<string, FieldDefinition>;
495
- /** Configuración del origen externo */
496
- source: {
497
- /** Tipo de origen */
498
- provider: string;
499
- /** Endpoint o recurso */
518
+ /** Nombre del adapter registrado (ej: 'stripe', 'github') */
519
+ adapter: string;
520
+ /** Configuración del origen externo (pasada al adapter) */
521
+ source?: {
522
+ /** Endpoint o recurso específico */
500
523
  endpoint?: string;
524
+ /** Config adicional */
525
+ [key: string]: unknown;
526
+ };
527
+ /** Configuración de cache */
528
+ cache?: {
529
+ /** TTL en segundos */
530
+ ttl: number;
531
+ /** Campo para generar cache key */
532
+ key?: string;
501
533
  };
502
534
  /** Autorización CASL */
503
535
  casl?: EntityCaslConfig;
@@ -514,8 +546,10 @@ interface VirtualEntityDefinition {
514
546
  labelField: string;
515
547
  /** Definición de campos (esquema unificado) */
516
548
  fields: Record<string, FieldDefinition>;
517
- /** Fuentes de datos que se combinan */
549
+ /** Fuentes de datos que se combinan (nombres de entidades/tables) */
518
550
  sources: string[];
551
+ /** Función que combina los datos de las fuentes */
552
+ resolver?: (sources: Record<string, unknown[]>, ctx: ModuleContext) => unknown[] | Promise<unknown[]>;
519
553
  /** Autorización CASL */
520
554
  casl?: EntityCaslConfig;
521
555
  }
@@ -531,8 +565,13 @@ interface ComputedEntityDefinition {
531
565
  labelField?: string;
532
566
  /** Definición de campos (estructura del resultado) */
533
567
  fields: Record<string, FieldDefinition>;
534
- /** Tiempo de cache en segundos (0 = sin cache) */
535
- cacheTtl?: number;
568
+ /** Función que calcula los datos */
569
+ compute?: (ctx: ModuleContext, params?: Record<string, unknown>) => Promise<unknown[]>;
570
+ /** Configuración de cache */
571
+ cache?: {
572
+ /** TTL en segundos (0 = sin cache) */
573
+ ttl: number;
574
+ };
536
575
  /** Autorización CASL */
537
576
  casl?: EntityCaslConfig;
538
577
  }
@@ -552,6 +591,8 @@ interface ViewEntityDefinition {
552
591
  fields: Record<string, FieldDefinition>;
553
592
  /** Entidad fuente de la que deriva */
554
593
  sourceEntity?: string;
594
+ /** Query SQL o builder para crear la vista */
595
+ query?: string | ((db: Knex) => Knex.QueryBuilder);
555
596
  /** Autorización CASL */
556
597
  casl?: EntityCaslConfig;
557
598
  }
@@ -561,8 +602,12 @@ interface ViewEntityDefinition {
561
602
  */
562
603
  interface ConfigEntityDefinition extends BaseEntityDefinition {
563
604
  type: 'config';
605
+ /** Campo que identifica el scope (ej: 'module_name', 'tenant_id') */
606
+ scopeField?: string;
564
607
  /** Scope de la configuración */
565
608
  scope?: 'global' | 'module' | 'tenant' | 'user';
609
+ /** Valores por defecto */
610
+ defaults?: Record<string, unknown>;
566
611
  /** Añadir updated_at */
567
612
  timestamps?: boolean;
568
613
  /** Añadir updated_by */
@@ -576,11 +621,17 @@ interface TempEntityDefinition extends BaseEntityDefinition {
576
621
  type: 'temp';
577
622
  /** Tiempo de vida en segundos */
578
623
  ttl: number;
624
+ /** Campo que almacena la fecha de expiración (default: 'expires_at') */
625
+ ttlField?: string;
579
626
  /** Campo para mostrar en listas (opcional) */
580
627
  labelField?: string;
581
628
  /** Índices para búsqueda rápida */
582
629
  indexes?: EntityIndex[];
583
630
  }
631
+ /**
632
+ * Todos los tipos de entidad disponibles
633
+ */
634
+ type EntityType = 'collection' | 'single' | 'external' | 'virtual' | 'computed' | 'view' | 'reference' | 'config' | 'event' | 'temp' | 'action';
584
635
  /**
585
636
  * Union de todas las definiciones de entidad
586
637
  *
@@ -775,4 +826,4 @@ interface PluginManifest {
775
826
  modules: ModuleManifest[];
776
827
  }
777
828
 
778
- export { type AbilityLike, type ActionEntityDefinition, type AuthRequest, type BaseUser, type CaslAction, type CollectionEntityDefinition, type ComputedEntityDefinition, type ConfigEntityDefinition, type DbType, type EntityCaslConfig, type EntityDefinition, type EntityIndex, type EventEntityDefinition, type ExternalEntityDefinition, type FieldCaslAccess, type FieldDbConfig, type FieldDefinition, type FieldMeta, type FieldOptions, type FieldRelation, type FieldValidation, type FieldValidationConfig, type ForbiddenErrorConstructor, type ForbiddenErrorInstance, type FormField, type FormFieldType, GENERATED_DIR, type GeneratedPermission, type InputType, type KnexAlterTableBuilder, type KnexCreateTableBuilder, type KnexTransaction, type ListType, type MigrationHelpers, 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 RolePermission, type SingleEntityDefinition, type TempEntityDefinition, type ValidateSchemas, type ValidationSchema, type ViewEntityDefinition, type VirtualEntityDefinition, generateCaslPermissions, generateCaslSeed, generateMigration, generateMigrationsFile, generateModel, generateModelsFile, generateReadOnlyModel, generateReadOnlySchema, generateSchemasFile, generateZodSchema, getEntityName, getEntitySubject, hasTable, isPersistentEntity, isSingletonEntity };
829
+ export { type AbilityLike, type ActionEntityDefinition, type AuthRequest, type BaseUser, type CaslAction, type CollectionEntityDefinition, type ComputedEntityDefinition, type ConfigEntityDefinition, type DbType, type EntityCaslConfig, type EntityDefinition, type EntityIndex, type EntityType, type EventEntityDefinition, type ExternalEntityDefinition, type FieldCaslAccess, type FieldDbConfig, type FieldDefinition, type FieldMeta, type FieldOptions, type FieldRelation, type FieldValidation, type FieldValidationConfig, type ForbiddenErrorConstructor, type ForbiddenErrorInstance, type FormField, type FormFieldType, GENERATED_DIR, type GeneratedPermission, type InputType, type KnexAlterTableBuilder, type KnexCreateTableBuilder, type KnexTransaction, type ListType, type MigrationHelpers, 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 RolePermission, type SingleEntityDefinition, type TempEntityDefinition, type ValidateSchemas, type ValidationSchema, type ViewEntityDefinition, type VirtualEntityDefinition, generateCaslPermissions, generateCaslSeed, generateMigration, generateMigrationsFile, generateModel, generateModelsFile, generateReadOnlyModel, generateReadOnlySchema, generateSchemasFile, generateZodSchema, getEntityName, getEntitySubject, hasTable, isPersistentEntity, isSingletonEntity };