@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.
- package/README.md +73 -5
- package/dist/chunk-XEPNN6IG.js +806 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +180 -0
- package/dist/index.d.ts +62 -11
- package/dist/index.js +18 -787
- package/package.json +13 -3
|
@@ -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,
|
|
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
|
-
/**
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
-
/**
|
|
535
|
-
|
|
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 };
|