@flusys/nestjs-core 1.1.0-beta → 1.2.0-rc.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/README.md +136 -162
- package/cjs/config/env-config.service.js +14 -10
- package/cjs/docs/docs.config.js +99 -15
- package/cjs/docs/index.js +0 -1
- package/cjs/index.js +0 -1
- package/cjs/interfaces/base-entity.interface.js +5 -3
- package/cjs/interfaces/database.interface.js +1 -3
- package/cjs/migration/datasource.factory.js +1 -3
- package/cjs/migration/index.js +0 -12
- package/cjs/migration/migration.cli.js +1 -17
- package/cjs/migration/migration.runner.js +37 -65
- package/cjs/utils/datasource-config.builder.js +2 -14
- package/config/env-config.service.d.ts +3 -1
- package/docs/docs.config.d.ts +8 -0
- package/docs/index.d.ts +0 -1
- package/fesm/config/env-config.service.js +14 -10
- package/fesm/docs/docs.config.js +89 -15
- package/fesm/docs/index.js +0 -1
- package/fesm/index.js +0 -2
- package/fesm/interfaces/app-config.interfaces.js +1 -3
- package/fesm/interfaces/base-entity.interface.js +5 -5
- package/fesm/interfaces/database.interface.js +1 -5
- package/fesm/migration/cli.js +1 -20
- package/fesm/migration/datasource.factory.js +3 -20
- package/fesm/migration/index.js +0 -14
- package/fesm/migration/migration.cli.js +1 -17
- package/fesm/migration/migration.runner.js +43 -132
- package/fesm/utils/datasource-config.builder.js +2 -13
- package/index.d.ts +0 -1
- package/interfaces/app-config.interfaces.d.ts +1 -0
- package/interfaces/base-entity.interface.d.ts +2 -2
- package/interfaces/database.interface.d.ts +2 -1
- package/package.json +9 -9
- package/utils/datasource-config.builder.d.ts +0 -1
- package/cjs/docs/docs.setup.js +0 -14
- package/cjs/seeders/base-seeder.js +0 -95
- package/cjs/seeders/cli.js +0 -398
- package/cjs/seeders/data-generator.js +0 -247
- package/cjs/seeders/entity-reader.js +0 -168
- package/cjs/seeders/index.js +0 -77
- package/cjs/seeders/seed-config.js +0 -101
- package/cjs/seeders/seed-runner.js +0 -320
- package/cjs/seeders/template-generator.js +0 -297
- package/docs/docs.setup.d.ts +0 -3
- package/fesm/docs/docs.setup.js +0 -4
- package/fesm/seeders/base-seeder.js +0 -110
- package/fesm/seeders/cli.js +0 -366
- package/fesm/seeders/data-generator.js +0 -249
- package/fesm/seeders/entity-reader.js +0 -170
- package/fesm/seeders/index.js +0 -11
- package/fesm/seeders/seed-config.js +0 -85
- package/fesm/seeders/seed-runner.js +0 -323
- package/fesm/seeders/template-generator.js +0 -257
- package/seeders/base-seeder.d.ts +0 -14
- package/seeders/cli.d.ts +0 -3
- package/seeders/data-generator.d.ts +0 -15
- package/seeders/entity-reader.d.ts +0 -45
- package/seeders/index.d.ts +0 -7
- package/seeders/seed-config.d.ts +0 -11
- package/seeders/seed-runner.d.ts +0 -47
- package/seeders/template-generator.d.ts +0 -16
package/fesm/seeders/cli.js
DELETED
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Seed CLI Entry Point
|
|
4
|
-
*
|
|
5
|
-
* Command-line interface for seed data generation.
|
|
6
|
-
*
|
|
7
|
-
* Commands:
|
|
8
|
-
* - generate: Generate seeder template files
|
|
9
|
-
* - run: Execute seeders for all entities
|
|
10
|
-
* - run:all: Execute seeders for all tenants (multi-tenant mode)
|
|
11
|
-
* - clear: Clear all seeded data
|
|
12
|
-
* - 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
|
-
*/ import * as path from 'path';
|
|
21
|
-
import { DataSource } from 'typeorm';
|
|
22
|
-
import { envConfig } from '../config';
|
|
23
|
-
import { EntityReader } from './entity-reader';
|
|
24
|
-
import { TemplateGenerator } from './template-generator';
|
|
25
|
-
import { SeedRunner } from './seed-runner';
|
|
26
|
-
import { shouldSkipEntity } from './seed-config';
|
|
27
|
-
/**
|
|
28
|
-
* Show help message
|
|
29
|
-
*/ function showHelp() {
|
|
30
|
-
console.log(`
|
|
31
|
-
Seed Data CLI
|
|
32
|
-
|
|
33
|
-
Usage:
|
|
34
|
-
npm run seed:<command> [options]
|
|
35
|
-
|
|
36
|
-
Commands:
|
|
37
|
-
generate Generate seeder template files
|
|
38
|
-
run Execute seeders for all entities
|
|
39
|
-
run:all Execute seeders for all tenants (multi-tenant mode)
|
|
40
|
-
clear Clear all seeded data
|
|
41
|
-
status Show seeding status
|
|
42
|
-
|
|
43
|
-
Options:
|
|
44
|
-
--config=<path> Migration config file (default: src/persistence/migration.config.ts)
|
|
45
|
-
--count=<number> Number of records per entity (default: from seed-config)
|
|
46
|
-
--clear Clear existing data before seeding
|
|
47
|
-
--entity=<name> Seed only specific entity
|
|
48
|
-
--hard Hard delete when clearing (ignore soft delete)
|
|
49
|
-
--tenant=<id> Target specific tenant (multi-tenant mode)
|
|
50
|
-
|
|
51
|
-
Environment:
|
|
52
|
-
MIGRATION_CONFIG Default config path (overrides auto-detection)
|
|
53
|
-
|
|
54
|
-
Examples:
|
|
55
|
-
npm run seed:generate
|
|
56
|
-
npm run seed:run
|
|
57
|
-
npm run seed:run -- --count=50 --clear
|
|
58
|
-
npm run seed:run -- --entity=User --count=100
|
|
59
|
-
npm run seed:run -- --config=src/persistence/migration.config.ts
|
|
60
|
-
npm run seed:clear
|
|
61
|
-
npm run seed:clear -- --hard
|
|
62
|
-
npm run seed:status
|
|
63
|
-
npm run seed:run:all
|
|
64
|
-
`);
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Parse command-line arguments
|
|
68
|
-
*/ function parseArgs() {
|
|
69
|
-
const args = process.argv.slice(2);
|
|
70
|
-
let command;
|
|
71
|
-
let count;
|
|
72
|
-
let clear = false;
|
|
73
|
-
let entity;
|
|
74
|
-
let hard = false;
|
|
75
|
-
let tenant;
|
|
76
|
-
let configPath;
|
|
77
|
-
for(let i = 0; i < args.length; i++){
|
|
78
|
-
const arg = args[i];
|
|
79
|
-
if (arg.startsWith('--count=')) {
|
|
80
|
-
count = parseInt(arg.split('=')[1], 10);
|
|
81
|
-
} else if (arg === '--clear') {
|
|
82
|
-
clear = true;
|
|
83
|
-
} else if (arg.startsWith('--entity=')) {
|
|
84
|
-
entity = arg.split('=')[1];
|
|
85
|
-
} else if (arg === '--hard') {
|
|
86
|
-
hard = true;
|
|
87
|
-
} else if (arg.startsWith('--tenant=')) {
|
|
88
|
-
tenant = arg.split('=')[1];
|
|
89
|
-
} else if (arg.startsWith('--config=')) {
|
|
90
|
-
configPath = arg.split('=')[1];
|
|
91
|
-
} else if (!arg.startsWith('-') && !command) {
|
|
92
|
-
command = arg;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
command,
|
|
97
|
-
count,
|
|
98
|
-
clear,
|
|
99
|
-
entity,
|
|
100
|
-
hard,
|
|
101
|
-
tenant,
|
|
102
|
-
configPath
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
// Resolved config path (set once during CLI initialization)
|
|
106
|
-
let resolvedConfigPath;
|
|
107
|
-
/**
|
|
108
|
-
* Resolve migration config path with auto-detection
|
|
109
|
-
*/ async function resolveConfigPath(explicitPath) {
|
|
110
|
-
if (resolvedConfigPath) return resolvedConfigPath;
|
|
111
|
-
// Try paths in order: explicit, env var, then common defaults
|
|
112
|
-
const envConfigPath = envConfig.tryGetValue('MIGRATION_CONFIG');
|
|
113
|
-
const defaultPaths = [
|
|
114
|
-
explicitPath,
|
|
115
|
-
envConfigPath,
|
|
116
|
-
'src/persistence/migration.config.ts',
|
|
117
|
-
'src/persistence/migration.config.js',
|
|
118
|
-
'persistence/migration.config.ts',
|
|
119
|
-
'persistence/migration.config.js'
|
|
120
|
-
].filter(Boolean);
|
|
121
|
-
for (const tryPath of defaultPaths){
|
|
122
|
-
const absolutePath = path.isAbsolute(tryPath) ? tryPath : path.resolve(process.cwd(), tryPath);
|
|
123
|
-
try {
|
|
124
|
-
await import(absolutePath);
|
|
125
|
-
resolvedConfigPath = absolutePath;
|
|
126
|
-
return absolutePath;
|
|
127
|
-
} catch {
|
|
128
|
-
// Try next path
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
console.error('❌ Config not found. Use --config=<path> or set MIGRATION_CONFIG env var');
|
|
132
|
-
console.error(' Tried paths:', defaultPaths.join(', '));
|
|
133
|
-
process.exit(1);
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Load DataSource configuration
|
|
137
|
-
*/ async function loadDataSource(tenantId, configPath) {
|
|
138
|
-
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;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Get all tenant IDs from configuration
|
|
171
|
-
*/ async function getAllTenantIds(configPath) {
|
|
172
|
-
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();
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Run seeds for single database
|
|
222
|
-
*/ async function runCommand(options, configPath) {
|
|
223
|
-
const dataSource = await loadDataSource(undefined, configPath);
|
|
224
|
-
try {
|
|
225
|
-
const runner = new SeedRunner(dataSource);
|
|
226
|
-
if (options.entity) {
|
|
227
|
-
console.log(`🌱 Seeding entity: ${options.entity}\n`);
|
|
228
|
-
const result = await runner.runSingle(options.entity, options);
|
|
229
|
-
if (result.success) {
|
|
230
|
-
console.log(`\n✓ ${result.entity}: ${result.count} records created\n`);
|
|
231
|
-
} else {
|
|
232
|
-
console.error(`\n✗ ${result.entity}: ${result.error}\n`);
|
|
233
|
-
process.exit(1);
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
const results = await runner.runAll(options);
|
|
237
|
-
const failedCount = results.filter((r)=>!r.success).length;
|
|
238
|
-
if (failedCount > 0) {
|
|
239
|
-
process.exit(1);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
} finally{
|
|
243
|
-
await dataSource.destroy();
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Run seeds for all tenants
|
|
248
|
-
*/ async function runAllTenantsCommand(options, configPath) {
|
|
249
|
-
console.log('🏢 Running seeds for all tenants...\n');
|
|
250
|
-
const tenantIds = await getAllTenantIds(configPath);
|
|
251
|
-
let successCount = 0;
|
|
252
|
-
let failCount = 0;
|
|
253
|
-
for (const tenantId of tenantIds){
|
|
254
|
-
console.log(`\n📦 Tenant: ${tenantId}`);
|
|
255
|
-
console.log('─'.repeat(50));
|
|
256
|
-
const dataSource = await loadDataSource(tenantId, configPath);
|
|
257
|
-
try {
|
|
258
|
-
const runner = new SeedRunner(dataSource);
|
|
259
|
-
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
|
-
}
|
|
266
|
-
} catch (error) {
|
|
267
|
-
console.error(`✗ Failed to seed tenant ${tenantId}:`, error);
|
|
268
|
-
failCount++;
|
|
269
|
-
} finally{
|
|
270
|
-
await dataSource.destroy();
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
console.log('\n' + '='.repeat(50));
|
|
274
|
-
console.log(`\n✓ Completed: ${successCount} tenants seeded`);
|
|
275
|
-
if (failCount > 0) {
|
|
276
|
-
console.log(`✗ Failed: ${failCount} tenants\n`);
|
|
277
|
-
process.exit(1);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Clear all seeded data
|
|
282
|
-
*/ async function clearCommand(hard = false, configPath) {
|
|
283
|
-
const dataSource = await loadDataSource(undefined, configPath);
|
|
284
|
-
try {
|
|
285
|
-
const runner = new SeedRunner(dataSource);
|
|
286
|
-
await runner.clearAll(hard);
|
|
287
|
-
} finally{
|
|
288
|
-
await dataSource.destroy();
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Show seeding status
|
|
293
|
-
*/ async function statusCommand(configPath) {
|
|
294
|
-
const dataSource = await loadDataSource(undefined, configPath);
|
|
295
|
-
try {
|
|
296
|
-
const runner = new SeedRunner(dataSource);
|
|
297
|
-
const status = await runner.getStatus();
|
|
298
|
-
console.log('\nSeed Status:');
|
|
299
|
-
console.log('┌────────────────────────────┬───────────┬─────────┐');
|
|
300
|
-
console.log('│ Entity │ Records │ Status │');
|
|
301
|
-
console.log('├────────────────────────────┼───────────┼─────────┤');
|
|
302
|
-
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} │`);
|
|
307
|
-
}
|
|
308
|
-
console.log('└────────────────────────────┴───────────┴─────────┘\n');
|
|
309
|
-
} finally{
|
|
310
|
-
await dataSource.destroy();
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Main CLI entry point
|
|
315
|
-
*/ async function main() {
|
|
316
|
-
const args = parseArgs();
|
|
317
|
-
if (!args.command) {
|
|
318
|
-
showHelp();
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
// Initialize config path for all commands
|
|
322
|
-
if (args.configPath) {
|
|
323
|
-
await resolveConfigPath(args.configPath);
|
|
324
|
-
}
|
|
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);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
// Run CLI if called directly
|
|
360
|
-
if (require.main === module) {
|
|
361
|
-
main().catch((error)=>{
|
|
362
|
-
console.error('Fatal error:', error);
|
|
363
|
-
process.exit(1);
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
export { main as runSeedCli };
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import { faker } from '@faker-js/faker';
|
|
2
|
-
/**
|
|
3
|
-
* Data Generator Service
|
|
4
|
-
*
|
|
5
|
-
* Generates realistic sample data based on column metadata.
|
|
6
|
-
* Uses @faker-js/faker for data generation with intelligent field detection.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* ```typescript
|
|
10
|
-
* const generator = new DataGenerator();
|
|
11
|
-
* const value = generator.generateValue(columnInfo);
|
|
12
|
-
* const entity = generator.generateEntity(columns);
|
|
13
|
-
* ```
|
|
14
|
-
*/ export class DataGenerator {
|
|
15
|
-
/**
|
|
16
|
-
* Generate value for a single column based on its metadata
|
|
17
|
-
*/ generateValue(column) {
|
|
18
|
-
// Skip generated fields
|
|
19
|
-
if (column.isGenerated) {
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
// Handle nullable fields (50% chance of null)
|
|
23
|
-
if (column.isNullable && faker.datatype.boolean()) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
// Check for enum
|
|
27
|
-
if (column.enum && column.enum.length > 0) {
|
|
28
|
-
return faker.helpers.arrayElement(column.enum);
|
|
29
|
-
}
|
|
30
|
-
// Generate based on column name pattern
|
|
31
|
-
const nameLower = column.propertyName.toLowerCase();
|
|
32
|
-
const value = this.generateByName(nameLower, column);
|
|
33
|
-
if (value !== undefined) {
|
|
34
|
-
return value;
|
|
35
|
-
}
|
|
36
|
-
// Generate based on column type
|
|
37
|
-
return this.generateByType(column);
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Generate entity object from column definitions
|
|
41
|
-
*/ generateEntity(columns) {
|
|
42
|
-
const entity = {};
|
|
43
|
-
for (const column of columns){
|
|
44
|
-
const value = this.generateValue(column);
|
|
45
|
-
if (value !== undefined) {
|
|
46
|
-
entity[column.propertyName] = value;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return entity;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Generate value based on column name patterns
|
|
53
|
-
*/ generateByName(nameLower, column) {
|
|
54
|
-
// Identity fields (skip, handled by database)
|
|
55
|
-
if ([
|
|
56
|
-
'id',
|
|
57
|
-
'createdat',
|
|
58
|
-
'updatedat',
|
|
59
|
-
'deletedat'
|
|
60
|
-
].includes(nameLower)) {
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
// Audit fields (skip, handled by interceptors)
|
|
64
|
-
if ([
|
|
65
|
-
'createdbyid',
|
|
66
|
-
'updatedbyid',
|
|
67
|
-
'deletedbyid'
|
|
68
|
-
].includes(nameLower)) {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
// Name fields
|
|
72
|
-
if (nameLower.includes('firstname')) return faker.person.firstName();
|
|
73
|
-
if (nameLower.includes('lastname')) return faker.person.lastName();
|
|
74
|
-
if (nameLower.includes('fullname') || nameLower === 'name') {
|
|
75
|
-
return faker.person.fullName();
|
|
76
|
-
}
|
|
77
|
-
// Contact fields
|
|
78
|
-
if (nameLower.includes('email')) return faker.internet.email().toLowerCase();
|
|
79
|
-
if (nameLower.includes('phone')) return faker.phone.number();
|
|
80
|
-
if (nameLower.includes('mobile')) return faker.phone.number();
|
|
81
|
-
// Location fields
|
|
82
|
-
if (nameLower.includes('address')) return faker.location.streetAddress();
|
|
83
|
-
if (nameLower.includes('street')) return faker.location.street();
|
|
84
|
-
if (nameLower.includes('city')) return faker.location.city();
|
|
85
|
-
if (nameLower.includes('state')) return faker.location.state();
|
|
86
|
-
if (nameLower.includes('country')) return faker.location.country();
|
|
87
|
-
if (nameLower.includes('zipcode') || nameLower.includes('postalcode')) {
|
|
88
|
-
return faker.location.zipCode();
|
|
89
|
-
}
|
|
90
|
-
// URL/Web fields
|
|
91
|
-
if (nameLower.includes('url')) return faker.internet.url();
|
|
92
|
-
if (nameLower.includes('website')) return faker.internet.url();
|
|
93
|
-
if (nameLower.includes('domain')) return faker.internet.domainName();
|
|
94
|
-
if (nameLower.includes('slug')) {
|
|
95
|
-
return faker.helpers.slugify(faker.lorem.words(3)).toLowerCase();
|
|
96
|
-
}
|
|
97
|
-
// Text fields
|
|
98
|
-
if (nameLower.includes('description')) return faker.lorem.paragraph();
|
|
99
|
-
if (nameLower.includes('summary')) return faker.lorem.sentence();
|
|
100
|
-
if (nameLower.includes('content')) return faker.lorem.paragraphs(3);
|
|
101
|
-
if (nameLower.includes('title')) return faker.lorem.sentence();
|
|
102
|
-
// User fields
|
|
103
|
-
if (nameLower.includes('username')) return faker.internet.username();
|
|
104
|
-
if (nameLower.includes('password')) return '$2b$12$dummy.hashed.password.value'; // Dummy bcrypt hash
|
|
105
|
-
// Status/Active fields
|
|
106
|
-
if (nameLower.includes('isactive')) return faker.datatype.boolean();
|
|
107
|
-
if (nameLower.includes('isenabled')) return faker.datatype.boolean();
|
|
108
|
-
if (nameLower.includes('ispublic')) return faker.datatype.boolean();
|
|
109
|
-
if (nameLower.includes('isverified')) return faker.datatype.boolean();
|
|
110
|
-
// Date fields
|
|
111
|
-
if (nameLower.includes('birthdate') || nameLower.includes('dateofbirth')) {
|
|
112
|
-
return faker.date.birthdate({
|
|
113
|
-
min: 18,
|
|
114
|
-
max: 80,
|
|
115
|
-
mode: 'age'
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
if (nameLower.includes('expiresat') || nameLower.includes('expirydate')) {
|
|
119
|
-
return faker.date.future();
|
|
120
|
-
}
|
|
121
|
-
if (nameLower.includes('verifiedat')) {
|
|
122
|
-
return faker.datatype.boolean() ? faker.date.recent() : null;
|
|
123
|
-
}
|
|
124
|
-
// Serial/Order fields
|
|
125
|
-
if (nameLower.includes('serial') || nameLower.includes('order')) {
|
|
126
|
-
return faker.number.int({
|
|
127
|
-
min: 1,
|
|
128
|
-
max: 100
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
// Company/Organization
|
|
132
|
-
if (nameLower.includes('company') && !nameLower.includes('id')) {
|
|
133
|
-
return faker.company.name();
|
|
134
|
-
}
|
|
135
|
-
return undefined;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Generate value based on column type
|
|
139
|
-
*/ generateByType(column) {
|
|
140
|
-
const type = column.type.toLowerCase();
|
|
141
|
-
switch(type){
|
|
142
|
-
// String types
|
|
143
|
-
case 'varchar':
|
|
144
|
-
case 'character varying':
|
|
145
|
-
case 'text':
|
|
146
|
-
case 'string':
|
|
147
|
-
return this.generateString(column);
|
|
148
|
-
// Number types
|
|
149
|
-
case 'int':
|
|
150
|
-
case 'integer':
|
|
151
|
-
case 'smallint':
|
|
152
|
-
case 'bigint':
|
|
153
|
-
return faker.number.int({
|
|
154
|
-
min: 1,
|
|
155
|
-
max: 1000
|
|
156
|
-
});
|
|
157
|
-
case 'decimal':
|
|
158
|
-
case 'numeric':
|
|
159
|
-
case 'float':
|
|
160
|
-
case 'double':
|
|
161
|
-
case 'real':
|
|
162
|
-
return faker.number.float({
|
|
163
|
-
min: 0,
|
|
164
|
-
max: 10000,
|
|
165
|
-
fractionDigits: column.scale || 2
|
|
166
|
-
});
|
|
167
|
-
// Boolean
|
|
168
|
-
case 'boolean':
|
|
169
|
-
case 'bool':
|
|
170
|
-
return faker.datatype.boolean();
|
|
171
|
-
// Date/Time
|
|
172
|
-
case 'date':
|
|
173
|
-
return faker.date.recent({
|
|
174
|
-
days: 30
|
|
175
|
-
});
|
|
176
|
-
case 'timestamp':
|
|
177
|
-
case 'datetime':
|
|
178
|
-
return faker.date.recent({
|
|
179
|
-
days: 30
|
|
180
|
-
});
|
|
181
|
-
case 'time':
|
|
182
|
-
return faker.date.recent().toTimeString().split(' ')[0];
|
|
183
|
-
// UUID
|
|
184
|
-
case 'uuid':
|
|
185
|
-
return faker.string.uuid();
|
|
186
|
-
// JSON
|
|
187
|
-
case 'json':
|
|
188
|
-
case 'jsonb':
|
|
189
|
-
return {
|
|
190
|
-
key1: faker.lorem.word(),
|
|
191
|
-
key2: faker.number.int({
|
|
192
|
-
min: 1,
|
|
193
|
-
max: 100
|
|
194
|
-
})
|
|
195
|
-
};
|
|
196
|
-
// Array
|
|
197
|
-
case 'array':
|
|
198
|
-
return [
|
|
199
|
-
faker.lorem.word(),
|
|
200
|
-
faker.lorem.word()
|
|
201
|
-
];
|
|
202
|
-
// Default
|
|
203
|
-
default:
|
|
204
|
-
return faker.lorem.word();
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Generate string value respecting length constraints
|
|
209
|
-
*/ generateString(column) {
|
|
210
|
-
const maxLength = typeof column.length === 'number' ? column.length : 255;
|
|
211
|
-
if (maxLength <= 50) {
|
|
212
|
-
return faker.lorem.word().substring(0, maxLength);
|
|
213
|
-
} else if (maxLength <= 255) {
|
|
214
|
-
return faker.lorem.sentence().substring(0, maxLength);
|
|
215
|
-
} else {
|
|
216
|
-
return faker.lorem.paragraph().substring(0, maxLength);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Generate related entity ID (for foreign keys)
|
|
221
|
-
* @param relatedEntities Array of entities to pick from
|
|
222
|
-
*/ generateRelationId(relatedEntities) {
|
|
223
|
-
if (relatedEntities.length === 0) {
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
return faker.helpers.arrayElement(relatedEntities).id;
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Generate multiple related entity IDs (for many-to-many)
|
|
230
|
-
* @param relatedEntities Array of entities to pick from
|
|
231
|
-
* @param min Minimum number of relations
|
|
232
|
-
* @param max Maximum number of relations
|
|
233
|
-
*/ generateRelationIds(relatedEntities, min = 1, max = 3) {
|
|
234
|
-
if (relatedEntities.length === 0) {
|
|
235
|
-
return [];
|
|
236
|
-
}
|
|
237
|
-
const count = faker.number.int({
|
|
238
|
-
min,
|
|
239
|
-
max: Math.min(max, relatedEntities.length)
|
|
240
|
-
});
|
|
241
|
-
const selected = faker.helpers.arrayElements(relatedEntities, count);
|
|
242
|
-
return selected.map((e)=>e.id);
|
|
243
|
-
}
|
|
244
|
-
constructor(locale = 'en'){
|
|
245
|
-
// Note: faker v8+ uses faker.locale instead of setLocale
|
|
246
|
-
// For v8+: faker.locale = locale;
|
|
247
|
-
// Locale is set globally, not needed for basic usage
|
|
248
|
-
}
|
|
249
|
-
}
|