@flusys/nestjs-core 1.0.0-beta → 1.0.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.
Files changed (56) hide show
  1. package/README.md +507 -61
  2. package/cjs/config/env-config.service.js +1 -1
  3. package/cjs/docs/docs.config.js +77 -3
  4. package/cjs/docs/index.js +0 -1
  5. package/cjs/interfaces/base-entity.interface.js +5 -3
  6. package/cjs/interfaces/database.interface.js +1 -3
  7. package/cjs/migration/datasource.factory.js +1 -3
  8. package/cjs/migration/index.js +0 -12
  9. package/cjs/migration/migration.cli.js +1 -17
  10. package/cjs/migration/migration.runner.js +37 -65
  11. package/cjs/seeders/base-seeder.js +6 -25
  12. package/cjs/seeders/cli.js +65 -172
  13. package/cjs/seeders/data-generator.js +96 -142
  14. package/cjs/seeders/entity-reader.js +0 -17
  15. package/cjs/seeders/field-patterns.js +172 -0
  16. package/cjs/seeders/index.js +16 -8
  17. package/cjs/seeders/seed-config.js +9 -48
  18. package/cjs/seeders/seed-runner.js +8 -14
  19. package/cjs/utils/datasource-config.builder.js +2 -14
  20. package/docs/docs.config.d.ts +7 -0
  21. package/docs/index.d.ts +0 -1
  22. package/fesm/config/env-config.service.js +1 -1
  23. package/fesm/docs/docs.config.js +68 -0
  24. package/fesm/docs/index.js +0 -1
  25. package/fesm/interfaces/app-config.interfaces.js +1 -3
  26. package/fesm/interfaces/base-entity.interface.js +5 -5
  27. package/fesm/interfaces/database.interface.js +1 -5
  28. package/fesm/migration/cli.js +1 -20
  29. package/fesm/migration/datasource.factory.js +3 -20
  30. package/fesm/migration/index.js +0 -14
  31. package/fesm/migration/migration.cli.js +1 -17
  32. package/fesm/migration/migration.runner.js +43 -132
  33. package/fesm/seeders/base-seeder.js +7 -51
  34. package/fesm/seeders/cli.js +65 -182
  35. package/fesm/seeders/data-generator.js +96 -149
  36. package/fesm/seeders/entity-reader.js +0 -17
  37. package/fesm/seeders/field-patterns.js +143 -0
  38. package/fesm/seeders/index.js +3 -7
  39. package/fesm/seeders/seed-config.js +9 -59
  40. package/fesm/seeders/seed-runner.js +8 -14
  41. package/fesm/utils/datasource-config.builder.js +2 -13
  42. package/interfaces/base-entity.interface.d.ts +3 -0
  43. package/package.json +2 -2
  44. package/seeders/data-generator.d.ts +1 -1
  45. package/seeders/entity-reader.d.ts +0 -1
  46. package/seeders/field-patterns.d.ts +12 -0
  47. package/seeders/index.d.ts +3 -3
  48. package/seeders/seed-config.d.ts +1 -0
  49. package/seeders/seed-runner.d.ts +1 -0
  50. package/utils/datasource-config.builder.d.ts +0 -1
  51. package/cjs/docs/docs.setup.js +0 -14
  52. package/cjs/seeders/template-generator.js +0 -297
  53. package/docs/docs.setup.d.ts +0 -3
  54. package/fesm/docs/docs.setup.js +0 -4
  55. package/fesm/seeders/template-generator.js +0 -257
  56. package/seeders/template-generator.d.ts +0 -16
@@ -2,31 +2,16 @@
2
2
  /**
3
3
  * Seed CLI Entry Point
4
4
  *
5
- * Command-line interface for seed data generation.
6
- *
7
5
  * Commands:
8
- * - generate: Generate seeder template files
9
6
  * - run: Execute seeders for all entities
10
7
  * - run:all: Execute seeders for all tenants (multi-tenant mode)
11
8
  * - clear: Clear all seeded data
12
9
  * - status: Show seeding status
13
- *
14
- * Usage:
15
- * npm run seed:generate
16
- * npm run seed:run
17
- * npm run seed:run -- --count=50 --clear
18
- * npm run seed:clear
19
- * npm run seed:status
20
10
  */ import * as path from 'path';
21
11
  import { DataSource } from 'typeorm';
22
12
  import { envConfig } from '../config';
23
- import { EntityReader } from './entity-reader';
24
- import { TemplateGenerator } from './template-generator';
25
13
  import { SeedRunner } from './seed-runner';
26
- import { shouldSkipEntity } from './seed-config';
27
- /**
28
- * Show help message
29
- */ function showHelp() {
14
+ function showHelp() {
30
15
  console.log(`
31
16
  Seed Data CLI
32
17
 
@@ -34,7 +19,6 @@ Usage:
34
19
  npm run seed:<command> [options]
35
20
 
36
21
  Commands:
37
- generate Generate seeder template files
38
22
  run Execute seeders for all entities
39
23
  run:all Execute seeders for all tenants (multi-tenant mode)
40
24
  clear Clear all seeded data
@@ -48,24 +32,17 @@ Options:
48
32
  --hard Hard delete when clearing (ignore soft delete)
49
33
  --tenant=<id> Target specific tenant (multi-tenant mode)
50
34
 
51
- Environment:
52
- MIGRATION_CONFIG Default config path (overrides auto-detection)
53
-
54
35
  Examples:
55
- npm run seed:generate
56
36
  npm run seed:run
57
37
  npm run seed:run -- --count=50 --clear
58
38
  npm run seed:run -- --entity=User --count=100
59
- npm run seed:run -- --config=src/persistence/migration.config.ts
60
39
  npm run seed:clear
61
40
  npm run seed:clear -- --hard
62
41
  npm run seed:status
63
42
  npm run seed:run:all
64
43
  `);
65
44
  }
66
- /**
67
- * Parse command-line arguments
68
- */ function parseArgs() {
45
+ function parseArgs() {
69
46
  const args = process.argv.slice(2);
70
47
  let command;
71
48
  let count;
@@ -74,8 +51,7 @@ Examples:
74
51
  let hard = false;
75
52
  let tenant;
76
53
  let configPath;
77
- for(let i = 0; i < args.length; i++){
78
- const arg = args[i];
54
+ for (const arg of args){
79
55
  if (arg.startsWith('--count=')) {
80
56
  count = parseInt(arg.split('=')[1], 10);
81
57
  } else if (arg === '--clear') {
@@ -102,21 +78,15 @@ Examples:
102
78
  configPath
103
79
  };
104
80
  }
105
- // Resolved config path (set once during CLI initialization)
106
81
  let resolvedConfigPath;
107
- /**
108
- * Resolve migration config path with auto-detection
109
- */ async function resolveConfigPath(explicitPath) {
82
+ async function resolveConfigPath(explicitPath) {
110
83
  if (resolvedConfigPath) return resolvedConfigPath;
111
- // Try paths in order: explicit, env var, then common defaults
112
84
  const envConfigPath = envConfig.tryGetValue('MIGRATION_CONFIG');
113
85
  const defaultPaths = [
114
86
  explicitPath,
115
87
  envConfigPath,
116
88
  'src/persistence/migration.config.ts',
117
- 'src/persistence/migration.config.js',
118
- 'persistence/migration.config.ts',
119
- 'persistence/migration.config.js'
89
+ 'src/persistence/migration.config.js'
120
90
  ].filter(Boolean);
121
91
  for (const tryPath of defaultPaths){
122
92
  const absolutePath = path.isAbsolute(tryPath) ? tryPath : path.resolve(process.cwd(), tryPath);
@@ -129,97 +99,41 @@ let resolvedConfigPath;
129
99
  }
130
100
  }
131
101
  console.error('āŒ Config not found. Use --config=<path> or set MIGRATION_CONFIG env var');
132
- console.error(' Tried paths:', defaultPaths.join(', '));
133
102
  process.exit(1);
134
103
  }
135
- /**
136
- * Load DataSource configuration
137
- */ async function loadDataSource(tenantId, configPath) {
104
+ async function loadDataSource(tenantId, configPath) {
138
105
  const resolvedPath = await resolveConfigPath(configPath);
139
- try {
140
- const module1 = await import(resolvedPath);
141
- const migrationConfig = module1.migrationConfig || module1.default?.migrationConfig || module1;
142
- // Get database config for tenant
143
- const dbConfig = tenantId ? migrationConfig.tenants?.find((t)=>t.tenantId === tenantId || t.id === tenantId) : migrationConfig.defaultDatabaseConfig || migrationConfig.database;
144
- if (!dbConfig) {
145
- throw new Error(`Database configuration not found${tenantId ? ` for tenant: ${tenantId}` : ''}`);
146
- }
147
- // Get entities - handle both function and array
148
- const entities = typeof migrationConfig.entities === 'function' ? migrationConfig.entities() : migrationConfig.entities || [];
149
- // Create DataSource
150
- const dataSource = new DataSource({
151
- type: dbConfig.type || 'mysql',
152
- host: dbConfig.host,
153
- port: dbConfig.port,
154
- username: dbConfig.username,
155
- password: dbConfig.password,
156
- database: dbConfig.database,
157
- entities,
158
- synchronize: false,
159
- logging: false
160
- });
161
- await dataSource.initialize();
162
- return dataSource;
163
- } catch (error) {
164
- console.error('āŒ Failed to load DataSource configuration');
165
- console.error(' Error:', error instanceof Error ? error.message : String(error));
166
- throw error;
106
+ const module1 = await import(resolvedPath);
107
+ const migrationConfig = module1.migrationConfig || module1.default?.migrationConfig || module1;
108
+ const dbConfig = tenantId ? migrationConfig.tenants?.find((t)=>t.tenantId === tenantId || t.id === tenantId) : migrationConfig.defaultDatabaseConfig || migrationConfig.database;
109
+ if (!dbConfig) {
110
+ throw new Error(`Database configuration not found${tenantId ? ` for tenant: ${tenantId}` : ''}`);
167
111
  }
112
+ const entities = typeof migrationConfig.entities === 'function' ? migrationConfig.entities() : migrationConfig.entities || [];
113
+ const dataSource = new DataSource({
114
+ type: dbConfig.type || 'mysql',
115
+ host: dbConfig.host,
116
+ port: dbConfig.port,
117
+ username: dbConfig.username,
118
+ password: dbConfig.password,
119
+ database: dbConfig.database,
120
+ entities,
121
+ synchronize: false,
122
+ logging: false
123
+ });
124
+ await dataSource.initialize();
125
+ return dataSource;
168
126
  }
169
- /**
170
- * Get all tenant IDs from configuration
171
- */ async function getAllTenantIds(configPath) {
127
+ async function getAllTenantIds(configPath) {
172
128
  const resolvedPath = await resolveConfigPath(configPath);
173
- try {
174
- const module1 = await import(resolvedPath);
175
- const migrationConfig = module1.migrationConfig || module1.default?.migrationConfig || module1;
176
- if (migrationConfig.bootstrapAppConfig?.databaseMode !== 'multi-tenant' || !migrationConfig.tenants) {
177
- throw new Error('Multi-tenant mode is not configured');
178
- }
179
- return migrationConfig.tenants.map((t)=>t.tenantId || t.id);
180
- } catch (error) {
181
- console.error('āŒ Failed to load tenant configuration');
182
- throw error;
183
- }
184
- }
185
- /**
186
- * Generate seeder template files
187
- */ async function generateCommand(configPath) {
188
- console.log('šŸ” Discovering entities...\n');
189
- const dataSource = await loadDataSource(undefined, configPath);
190
- try {
191
- const reader = new EntityReader(dataSource);
192
- const generator = new TemplateGenerator();
193
- const entities = reader.getAllEntities();
194
- const outputDir = path.resolve(process.cwd(), 'src/seeders/generators');
195
- console.log(`šŸ“ Generating seeder files in ${outputDir}\n`);
196
- let generatedCount = 0;
197
- const entityInfos = [];
198
- for (const metadata of entities){
199
- if (shouldSkipEntity(metadata.name)) {
200
- continue;
201
- }
202
- try {
203
- const entityInfo = reader.getEntityInfo(metadata.name);
204
- entityInfos.push(entityInfo);
205
- const filePath = generator.generateSeederFile(entityInfo, outputDir);
206
- console.log(`āœ“ Generated: ${path.basename(filePath)}`);
207
- generatedCount++;
208
- } catch (error) {
209
- console.error(`āœ— Failed to generate ${metadata.name}:`, error instanceof Error ? error.message : String(error));
210
- }
211
- }
212
- // Generate index file
213
- const indexPath = generator.generateIndexFile(entityInfos, outputDir);
214
- console.log(`āœ“ Generated: ${path.basename(indexPath)}`);
215
- console.log(`\nāœ“ Generated ${generatedCount} seeder files\n`);
216
- } finally{
217
- await dataSource.destroy();
129
+ const module1 = await import(resolvedPath);
130
+ const migrationConfig = module1.migrationConfig || module1.default?.migrationConfig || module1;
131
+ if (migrationConfig.bootstrapAppConfig?.databaseMode !== 'multi-tenant' || !migrationConfig.tenants) {
132
+ throw new Error('Multi-tenant mode is not configured');
218
133
  }
134
+ return migrationConfig.tenants.map((t)=>t.tenantId || t.id);
219
135
  }
220
- /**
221
- * Run seeds for single database
222
- */ async function runCommand(options, configPath) {
136
+ async function runCommand(options, configPath) {
223
137
  const dataSource = await loadDataSource(undefined, configPath);
224
138
  try {
225
139
  const runner = new SeedRunner(dataSource);
@@ -234,18 +148,13 @@ let resolvedConfigPath;
234
148
  }
235
149
  } else {
236
150
  const results = await runner.runAll(options);
237
- const failedCount = results.filter((r)=>!r.success).length;
238
- if (failedCount > 0) {
239
- process.exit(1);
240
- }
151
+ if (results.some((r)=>!r.success)) process.exit(1);
241
152
  }
242
153
  } finally{
243
154
  await dataSource.destroy();
244
155
  }
245
156
  }
246
- /**
247
- * Run seeds for all tenants
248
- */ async function runAllTenantsCommand(options, configPath) {
157
+ async function runAllTenantsCommand(options, configPath) {
249
158
  console.log('šŸ¢ Running seeds for all tenants...\n');
250
159
  const tenantIds = await getAllTenantIds(configPath);
251
160
  let successCount = 0;
@@ -257,12 +166,7 @@ let resolvedConfigPath;
257
166
  try {
258
167
  const runner = new SeedRunner(dataSource);
259
168
  const results = await runner.runAll(options);
260
- const failed = results.filter((r)=>!r.success).length;
261
- if (failed === 0) {
262
- successCount++;
263
- } else {
264
- failCount++;
265
- }
169
+ results.some((r)=>!r.success) ? failCount++ : successCount++;
266
170
  } catch (error) {
267
171
  console.error(`āœ— Failed to seed tenant ${tenantId}:`, error);
268
172
  failCount++;
@@ -277,86 +181,65 @@ let resolvedConfigPath;
277
181
  process.exit(1);
278
182
  }
279
183
  }
280
- /**
281
- * Clear all seeded data
282
- */ async function clearCommand(hard = false, configPath) {
184
+ async function clearCommand(hard = false, configPath) {
283
185
  const dataSource = await loadDataSource(undefined, configPath);
284
186
  try {
285
- const runner = new SeedRunner(dataSource);
286
- await runner.clearAll(hard);
187
+ await new SeedRunner(dataSource).clearAll(hard);
287
188
  } finally{
288
189
  await dataSource.destroy();
289
190
  }
290
191
  }
291
- /**
292
- * Show seeding status
293
- */ async function statusCommand(configPath) {
192
+ async function statusCommand(configPath) {
294
193
  const dataSource = await loadDataSource(undefined, configPath);
295
194
  try {
296
- const runner = new SeedRunner(dataSource);
297
- const status = await runner.getStatus();
195
+ const status = await new SeedRunner(dataSource).getStatus();
298
196
  console.log('\nSeed Status:');
299
197
  console.log('ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”');
300
198
  console.log('│ Entity │ Records │ Status │');
301
199
  console.log('ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤');
302
200
  for (const item of status){
303
- const entityName = item.entity.padEnd(26);
304
- const count = item.count.toString().padEnd(9);
305
- const statusIcon = item.isEmpty ? '⚠ Empty' : 'āœ“ Ready ';
306
- console.log(`│ ${entityName} │ ${count} │ ${statusIcon} │`);
201
+ console.log(`│ ${item.entity.padEnd(26)} │ ${item.count.toString().padEnd(9)} │ ${item.isEmpty ? '⚠ Empty' : 'āœ“ Ready '} │`);
307
202
  }
308
203
  console.log('ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n');
309
204
  } finally{
310
205
  await dataSource.destroy();
311
206
  }
312
207
  }
313
- /**
314
- * Main CLI entry point
315
- */ async function main() {
208
+ async function main() {
316
209
  const args = parseArgs();
317
210
  if (!args.command) {
318
211
  showHelp();
319
212
  return;
320
213
  }
321
- // Initialize config path for all commands
322
214
  if (args.configPath) {
323
215
  await resolveConfigPath(args.configPath);
324
216
  }
325
- try {
326
- switch(args.command){
327
- case 'generate':
328
- await generateCommand(args.configPath);
329
- break;
330
- case 'run':
331
- await runCommand({
332
- count: args.count,
333
- clear: args.clear,
334
- entity: args.entity
335
- }, args.configPath);
336
- break;
337
- case 'run:all':
338
- await runAllTenantsCommand({
339
- count: args.count,
340
- clear: args.clear
341
- }, args.configPath);
342
- break;
343
- case 'clear':
344
- await clearCommand(args.hard, args.configPath);
345
- break;
346
- case 'status':
347
- await statusCommand(args.configPath);
348
- break;
349
- default:
350
- console.error(`āŒ Unknown command: ${args.command}`);
351
- showHelp();
352
- process.exit(1);
353
- }
354
- } catch (error) {
355
- console.error('\nāŒ CLI Error:', error instanceof Error ? error.message : String(error));
356
- process.exit(1);
217
+ switch(args.command){
218
+ case 'run':
219
+ await runCommand({
220
+ count: args.count,
221
+ clear: args.clear,
222
+ entity: args.entity
223
+ }, args.configPath);
224
+ break;
225
+ case 'run:all':
226
+ await runAllTenantsCommand({
227
+ count: args.count,
228
+ clear: args.clear
229
+ }, args.configPath);
230
+ break;
231
+ case 'clear':
232
+ await clearCommand(args.hard, args.configPath);
233
+ break;
234
+ case 'status':
235
+ await statusCommand(args.configPath);
236
+ break;
237
+ default:
238
+ console.error(`āŒ Unknown command: ${args.command}`);
239
+ showHelp();
240
+ process.exit(1);
357
241
  }
358
242
  }
359
- // Run CLI if called directly
360
243
  if (require.main === module) {
361
244
  main().catch((error)=>{
362
245
  console.error('Fatal error:', error);