@flusys/nestjs-core 1.1.0 → 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.
Files changed (37) hide show
  1. package/README.md +113 -581
  2. package/cjs/config/env-config.service.js +13 -9
  3. package/cjs/docs/docs.config.js +25 -15
  4. package/cjs/index.js +0 -1
  5. package/config/env-config.service.d.ts +3 -1
  6. package/docs/docs.config.d.ts +1 -0
  7. package/fesm/config/env-config.service.js +13 -9
  8. package/fesm/docs/docs.config.js +27 -21
  9. package/fesm/index.js +0 -2
  10. package/index.d.ts +0 -1
  11. package/interfaces/base-entity.interface.d.ts +0 -3
  12. package/interfaces/database.interface.d.ts +1 -1
  13. package/package.json +8 -8
  14. package/cjs/seeders/base-seeder.js +0 -76
  15. package/cjs/seeders/cli.js +0 -291
  16. package/cjs/seeders/data-generator.js +0 -215
  17. package/cjs/seeders/entity-reader.js +0 -151
  18. package/cjs/seeders/field-patterns.js +0 -176
  19. package/cjs/seeders/index.js +0 -85
  20. package/cjs/seeders/seed-config.js +0 -62
  21. package/cjs/seeders/seed-runner.js +0 -314
  22. package/fesm/seeders/base-seeder.js +0 -66
  23. package/fesm/seeders/cli.js +0 -249
  24. package/fesm/seeders/data-generator.js +0 -210
  25. package/fesm/seeders/entity-reader.js +0 -153
  26. package/fesm/seeders/field-patterns.js +0 -147
  27. package/fesm/seeders/index.js +0 -7
  28. package/fesm/seeders/seed-config.js +0 -35
  29. package/fesm/seeders/seed-runner.js +0 -317
  30. package/seeders/base-seeder.d.ts +0 -14
  31. package/seeders/cli.d.ts +0 -3
  32. package/seeders/data-generator.d.ts +0 -15
  33. package/seeders/entity-reader.d.ts +0 -44
  34. package/seeders/field-patterns.d.ts +0 -12
  35. package/seeders/index.d.ts +0 -7
  36. package/seeders/seed-config.d.ts +0 -12
  37. package/seeders/seed-runner.d.ts +0 -48
@@ -1,291 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "runSeedCli", {
7
- enumerable: true,
8
- get: function() {
9
- return main;
10
- }
11
- });
12
- const _path = /*#__PURE__*/ _interop_require_wildcard(require("path"));
13
- const _typeorm = require("typeorm");
14
- const _config = require("../config");
15
- const _seedrunner = require("./seed-runner");
16
- function _getRequireWildcardCache(nodeInterop) {
17
- if (typeof WeakMap !== "function") return null;
18
- var cacheBabelInterop = new WeakMap();
19
- var cacheNodeInterop = new WeakMap();
20
- return (_getRequireWildcardCache = function(nodeInterop) {
21
- return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
22
- })(nodeInterop);
23
- }
24
- function _interop_require_wildcard(obj, nodeInterop) {
25
- if (!nodeInterop && obj && obj.__esModule) {
26
- return obj;
27
- }
28
- if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
29
- return {
30
- default: obj
31
- };
32
- }
33
- var cache = _getRequireWildcardCache(nodeInterop);
34
- if (cache && cache.has(obj)) {
35
- return cache.get(obj);
36
- }
37
- var newObj = {
38
- __proto__: null
39
- };
40
- var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
41
- for(var key in obj){
42
- if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
43
- var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
44
- if (desc && (desc.get || desc.set)) {
45
- Object.defineProperty(newObj, key, desc);
46
- } else {
47
- newObj[key] = obj[key];
48
- }
49
- }
50
- }
51
- newObj.default = obj;
52
- if (cache) {
53
- cache.set(obj, newObj);
54
- }
55
- return newObj;
56
- }
57
- function showHelp() {
58
- console.log(`
59
- Seed Data CLI
60
-
61
- Usage:
62
- npm run seed:<command> [options]
63
-
64
- Commands:
65
- run Execute seeders for all entities
66
- run:all Execute seeders for all tenants (multi-tenant mode)
67
- clear Clear all seeded data
68
- status Show seeding status
69
-
70
- Options:
71
- --config=<path> Migration config file (default: src/persistence/migration.config.ts)
72
- --count=<number> Number of records per entity (default: from seed-config)
73
- --clear Clear existing data before seeding
74
- --entity=<name> Seed only specific entity
75
- --hard Hard delete when clearing (ignore soft delete)
76
- --tenant=<id> Target specific tenant (multi-tenant mode)
77
-
78
- Examples:
79
- npm run seed:run
80
- npm run seed:run -- --count=50 --clear
81
- npm run seed:run -- --entity=User --count=100
82
- npm run seed:clear
83
- npm run seed:clear -- --hard
84
- npm run seed:status
85
- npm run seed:run:all
86
- `);
87
- }
88
- function parseArgs() {
89
- const args = process.argv.slice(2);
90
- let command;
91
- let count;
92
- let clear = false;
93
- let entity;
94
- let hard = false;
95
- let tenant;
96
- let configPath;
97
- for (const arg of args){
98
- if (arg.startsWith('--count=')) {
99
- count = parseInt(arg.split('=')[1], 10);
100
- } else if (arg === '--clear') {
101
- clear = true;
102
- } else if (arg.startsWith('--entity=')) {
103
- entity = arg.split('=')[1];
104
- } else if (arg === '--hard') {
105
- hard = true;
106
- } else if (arg.startsWith('--tenant=')) {
107
- tenant = arg.split('=')[1];
108
- } else if (arg.startsWith('--config=')) {
109
- configPath = arg.split('=')[1];
110
- } else if (!arg.startsWith('-') && !command) {
111
- command = arg;
112
- }
113
- }
114
- return {
115
- command,
116
- count,
117
- clear,
118
- entity,
119
- hard,
120
- tenant,
121
- configPath
122
- };
123
- }
124
- let resolvedConfigPath;
125
- async function resolveConfigPath(explicitPath) {
126
- if (resolvedConfigPath) return resolvedConfigPath;
127
- const envConfigPath = _config.envConfig.tryGetValue('MIGRATION_CONFIG');
128
- const defaultPaths = [
129
- explicitPath,
130
- envConfigPath,
131
- 'src/persistence/migration.config.ts',
132
- 'src/persistence/migration.config.js'
133
- ].filter(Boolean);
134
- for (const tryPath of defaultPaths){
135
- const absolutePath = _path.isAbsolute(tryPath) ? tryPath : _path.resolve(process.cwd(), tryPath);
136
- try {
137
- await Promise.resolve(absolutePath).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
138
- resolvedConfigPath = absolutePath;
139
- return absolutePath;
140
- } catch {
141
- // Try next path
142
- }
143
- }
144
- console.error('❌ Config not found. Use --config=<path> or set MIGRATION_CONFIG env var');
145
- process.exit(1);
146
- }
147
- async function loadDataSource(tenantId, configPath) {
148
- const resolvedPath = await resolveConfigPath(configPath);
149
- const module1 = await Promise.resolve(resolvedPath).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
150
- const migrationConfig = module1.migrationConfig || module1.default?.migrationConfig || module1;
151
- const dbConfig = tenantId ? migrationConfig.tenants?.find((t)=>t.tenantId === tenantId || t.id === tenantId) : migrationConfig.defaultDatabaseConfig || migrationConfig.database;
152
- if (!dbConfig) {
153
- throw new Error(`Database configuration not found${tenantId ? ` for tenant: ${tenantId}` : ''}`);
154
- }
155
- const entities = typeof migrationConfig.entities === 'function' ? migrationConfig.entities() : migrationConfig.entities || [];
156
- const dataSource = new _typeorm.DataSource({
157
- type: dbConfig.type || 'mysql',
158
- host: dbConfig.host,
159
- port: dbConfig.port,
160
- username: dbConfig.username,
161
- password: dbConfig.password,
162
- database: dbConfig.database,
163
- entities,
164
- synchronize: false,
165
- logging: false
166
- });
167
- await dataSource.initialize();
168
- return dataSource;
169
- }
170
- async function getAllTenantIds(configPath) {
171
- const resolvedPath = await resolveConfigPath(configPath);
172
- const module1 = await Promise.resolve(resolvedPath).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
173
- const migrationConfig = module1.migrationConfig || module1.default?.migrationConfig || module1;
174
- if (migrationConfig.bootstrapAppConfig?.databaseMode !== 'multi-tenant' || !migrationConfig.tenants) {
175
- throw new Error('Multi-tenant mode is not configured');
176
- }
177
- return migrationConfig.tenants.map((t)=>t.tenantId || t.id);
178
- }
179
- async function runCommand(options, configPath) {
180
- const dataSource = await loadDataSource(undefined, configPath);
181
- try {
182
- const runner = new _seedrunner.SeedRunner(dataSource);
183
- if (options.entity) {
184
- console.log(`🌱 Seeding entity: ${options.entity}\n`);
185
- const result = await runner.runSingle(options.entity, options);
186
- if (result.success) {
187
- console.log(`\n✓ ${result.entity}: ${result.count} records created\n`);
188
- } else {
189
- console.error(`\n✗ ${result.entity}: ${result.error}\n`);
190
- process.exit(1);
191
- }
192
- } else {
193
- const results = await runner.runAll(options);
194
- if (results.some((r)=>!r.success)) process.exit(1);
195
- }
196
- } finally{
197
- await dataSource.destroy();
198
- }
199
- }
200
- async function runAllTenantsCommand(options, configPath) {
201
- console.log('🏢 Running seeds for all tenants...\n');
202
- const tenantIds = await getAllTenantIds(configPath);
203
- let successCount = 0;
204
- let failCount = 0;
205
- for (const tenantId of tenantIds){
206
- console.log(`\n📦 Tenant: ${tenantId}`);
207
- console.log('─'.repeat(50));
208
- const dataSource = await loadDataSource(tenantId, configPath);
209
- try {
210
- const runner = new _seedrunner.SeedRunner(dataSource);
211
- const results = await runner.runAll(options);
212
- results.some((r)=>!r.success) ? failCount++ : successCount++;
213
- } catch (error) {
214
- console.error(`✗ Failed to seed tenant ${tenantId}:`, error);
215
- failCount++;
216
- } finally{
217
- await dataSource.destroy();
218
- }
219
- }
220
- console.log('\n' + '='.repeat(50));
221
- console.log(`\n✓ Completed: ${successCount} tenants seeded`);
222
- if (failCount > 0) {
223
- console.log(`✗ Failed: ${failCount} tenants\n`);
224
- process.exit(1);
225
- }
226
- }
227
- async function clearCommand(hard = false, configPath) {
228
- const dataSource = await loadDataSource(undefined, configPath);
229
- try {
230
- await new _seedrunner.SeedRunner(dataSource).clearAll(hard);
231
- } finally{
232
- await dataSource.destroy();
233
- }
234
- }
235
- async function statusCommand(configPath) {
236
- const dataSource = await loadDataSource(undefined, configPath);
237
- try {
238
- const status = await new _seedrunner.SeedRunner(dataSource).getStatus();
239
- console.log('\nSeed Status:');
240
- console.log('┌────────────────────────────┬───────────┬─────────┐');
241
- console.log('│ Entity │ Records │ Status │');
242
- console.log('├────────────────────────────┼───────────┼─────────┤');
243
- for (const item of status){
244
- console.log(`│ ${item.entity.padEnd(26)} │ ${item.count.toString().padEnd(9)} │ ${item.isEmpty ? '⚠ Empty' : '✓ Ready '} │`);
245
- }
246
- console.log('└────────────────────────────┴───────────┴─────────┘\n');
247
- } finally{
248
- await dataSource.destroy();
249
- }
250
- }
251
- async function main() {
252
- const args = parseArgs();
253
- if (!args.command) {
254
- showHelp();
255
- return;
256
- }
257
- if (args.configPath) {
258
- await resolveConfigPath(args.configPath);
259
- }
260
- switch(args.command){
261
- case 'run':
262
- await runCommand({
263
- count: args.count,
264
- clear: args.clear,
265
- entity: args.entity
266
- }, args.configPath);
267
- break;
268
- case 'run:all':
269
- await runAllTenantsCommand({
270
- count: args.count,
271
- clear: args.clear
272
- }, args.configPath);
273
- break;
274
- case 'clear':
275
- await clearCommand(args.hard, args.configPath);
276
- break;
277
- case 'status':
278
- await statusCommand(args.configPath);
279
- break;
280
- default:
281
- console.error(`❌ Unknown command: ${args.command}`);
282
- showHelp();
283
- process.exit(1);
284
- }
285
- }
286
- if (require.main === module) {
287
- main().catch((error)=>{
288
- console.error('Fatal error:', error);
289
- process.exit(1);
290
- });
291
- }
@@ -1,215 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "DataGenerator", {
6
- enumerable: true,
7
- get: function() {
8
- return DataGenerator;
9
- }
10
- });
11
- const _faker = require("@faker-js/faker");
12
- const _fieldpatterns = require("./field-patterns");
13
- let DataGenerator = class DataGenerator {
14
- /**
15
- * Generate value for a single column based on its metadata
16
- */ generateValue(column) {
17
- if (column.isGenerated) return undefined;
18
- // Handle nullable fields (50% chance of null)
19
- if (column.isNullable && _faker.faker.datatype.boolean()) return null;
20
- // Check for enum
21
- if (column.enum && column.enum.length > 0) {
22
- return _faker.faker.helpers.arrayElement(column.enum);
23
- }
24
- // Generate based on field pattern
25
- const pattern = (0, _fieldpatterns.detectFieldPattern)(column);
26
- if (pattern) {
27
- const value = this.generateByPattern(pattern, column);
28
- if (value !== undefined) return value;
29
- }
30
- // Generate based on column type
31
- return this.generateByType(column);
32
- }
33
- /**
34
- * Generate entity object from column definitions
35
- */ generateEntity(columns) {
36
- const entity = {};
37
- for (const column of columns){
38
- const value = this.generateValue(column);
39
- if (value !== undefined) {
40
- entity[column.propertyName] = value;
41
- }
42
- }
43
- return entity;
44
- }
45
- /**
46
- * Generate value based on detected field pattern
47
- */ generateByPattern(pattern, column) {
48
- switch(pattern){
49
- case 'skip':
50
- return undefined;
51
- case 'null':
52
- return null;
53
- case 'boolean':
54
- return _faker.faker.datatype.boolean();
55
- case 'token':
56
- return _faker.faker.string.alphanumeric((0, _fieldpatterns.getTokenLength)(column));
57
- case 'firstName':
58
- return _faker.faker.person.firstName();
59
- case 'lastName':
60
- return _faker.faker.person.lastName();
61
- case 'fullName':
62
- return _faker.faker.person.fullName();
63
- case 'email':
64
- return _faker.faker.internet.email().toLowerCase();
65
- case 'phone':
66
- return _faker.faker.phone.number();
67
- case 'address':
68
- return _faker.faker.location.streetAddress();
69
- case 'street':
70
- return _faker.faker.location.street();
71
- case 'city':
72
- return _faker.faker.location.city();
73
- case 'state':
74
- return _faker.faker.location.state();
75
- case 'country':
76
- return _faker.faker.location.country();
77
- case 'zipCode':
78
- return _faker.faker.location.zipCode();
79
- case 'url':
80
- return _faker.faker.internet.url();
81
- case 'domain':
82
- return _faker.faker.internet.domainName();
83
- case 'slug':
84
- return _faker.faker.helpers.slugify(_faker.faker.lorem.words(3)).toLowerCase();
85
- case 'description':
86
- return _faker.faker.lorem.paragraph();
87
- case 'summary':
88
- return _faker.faker.lorem.sentence();
89
- case 'content':
90
- return _faker.faker.lorem.paragraphs(3);
91
- case 'title':
92
- return _faker.faker.lorem.sentence();
93
- case 'username':
94
- return _faker.faker.internet.username();
95
- case 'password':
96
- return '$2b$12$dummy.hashed.password.value';
97
- case 'birthdate':
98
- return _faker.faker.date.birthdate({
99
- min: 18,
100
- max: 80,
101
- mode: 'age'
102
- });
103
- case 'futureDate':
104
- return _faker.faker.date.future();
105
- case 'recentDateOrNull':
106
- return _faker.faker.datatype.boolean() ? _faker.faker.date.recent() : null;
107
- case 'serial':
108
- return _faker.faker.number.int({
109
- min: 1,
110
- max: 100
111
- });
112
- case 'company':
113
- return _faker.faker.company.name();
114
- case 'emailProvider':
115
- return _faker.faker.helpers.arrayElement([
116
- 'smtp',
117
- 'sendgrid',
118
- 'mailgun',
119
- 'ses',
120
- 'postmark'
121
- ]);
122
- case 'formAccessType':
123
- return _faker.faker.helpers.arrayElement([
124
- 'public',
125
- 'authenticated',
126
- 'permission'
127
- ]);
128
- default:
129
- return undefined;
130
- }
131
- }
132
- /**
133
- * Generate value based on column type
134
- */ generateByType(column) {
135
- const category = (0, _fieldpatterns.detectTypeCategory)(column.type);
136
- switch(category){
137
- case 'string':
138
- return this.generateString(column);
139
- case 'integer':
140
- return _faker.faker.number.int({
141
- min: 1,
142
- max: 1000
143
- });
144
- case 'decimal':
145
- return _faker.faker.number.float({
146
- min: 0,
147
- max: 10000,
148
- fractionDigits: column.scale || 2
149
- });
150
- case 'boolean':
151
- return _faker.faker.datatype.boolean();
152
- case 'date':
153
- case 'timestamp':
154
- return _faker.faker.date.recent({
155
- days: 30
156
- });
157
- case 'time':
158
- return _faker.faker.date.recent().toTimeString().split(' ')[0];
159
- case 'uuid':
160
- return _faker.faker.string.uuid();
161
- case 'json':
162
- return {
163
- key1: _faker.faker.lorem.word(),
164
- key2: _faker.faker.number.int({
165
- min: 1,
166
- max: 100
167
- })
168
- };
169
- case 'array':
170
- return [
171
- _faker.faker.lorem.word(),
172
- _faker.faker.lorem.word()
173
- ];
174
- default:
175
- return _faker.faker.lorem.word();
176
- }
177
- }
178
- /**
179
- * Generate string value respecting length constraints
180
- */ generateString(column) {
181
- const maxLength = typeof column.length === 'number' ? column.length : 255;
182
- const category = (0, _fieldpatterns.getStringLengthCategory)(column.length);
183
- switch(category){
184
- case 'word':
185
- return _faker.faker.lorem.word().substring(0, maxLength);
186
- case 'sentence':
187
- return _faker.faker.lorem.sentence().substring(0, maxLength);
188
- case 'paragraph':
189
- return _faker.faker.lorem.paragraph().substring(0, maxLength);
190
- }
191
- }
192
- /**
193
- * Generate related entity ID (for foreign keys)
194
- */ generateRelationId(relatedEntities) {
195
- if (relatedEntities.length === 0) return null;
196
- return _faker.faker.helpers.arrayElement(relatedEntities).id;
197
- }
198
- /**
199
- * Generate multiple related entity IDs (for many-to-many)
200
- */ generateRelationIds(relatedEntities, min = 1, max = 3) {
201
- if (relatedEntities.length === 0) return [];
202
- const count = _faker.faker.number.int({
203
- min,
204
- max: Math.min(max, relatedEntities.length)
205
- });
206
- return _faker.faker.helpers.arrayElements(relatedEntities, count).map((e)=>e.id);
207
- }
208
- constructor(locale = 'en'){
209
- _faker.faker.setDefaultRefDate(new Date());
210
- if (locale !== 'en') {
211
- // @ts-expect-error - faker locale property access
212
- _faker.faker.locale = locale;
213
- }
214
- }
215
- };
@@ -1,151 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "EntityReader", {
6
- enumerable: true,
7
- get: function() {
8
- return EntityReader;
9
- }
10
- });
11
- function _define_property(obj, key, value) {
12
- if (key in obj) {
13
- Object.defineProperty(obj, key, {
14
- value: value,
15
- enumerable: true,
16
- configurable: true,
17
- writable: true
18
- });
19
- } else {
20
- obj[key] = value;
21
- }
22
- return obj;
23
- }
24
- let EntityReader = class EntityReader {
25
- /**
26
- * Get all entity metadata from DataSource
27
- */ getAllEntities() {
28
- return this.dataSource.entityMetadatas;
29
- }
30
- /**
31
- * Get detailed entity information
32
- * @param entityName Entity class name or table name
33
- */ getEntityInfo(entityName) {
34
- const metadata = this.findEntityMetadata(entityName);
35
- if (!metadata) {
36
- throw new Error(`Entity ${entityName} not found in DataSource`);
37
- }
38
- return {
39
- name: metadata.name,
40
- tableName: metadata.tableName,
41
- columns: this.extractColumns(metadata),
42
- relations: this.extractRelations(metadata),
43
- hasSoftDelete: metadata.deleteDateColumn !== undefined,
44
- hasCompany: this.hasCompanyColumn(metadata)
45
- };
46
- }
47
- /**
48
- * Get seeding order based on foreign key dependencies
49
- * Returns entity names sorted by dependency (independent entities first)
50
- */ getSeedingOrder(skipEntities = []) {
51
- const entities = this.getAllEntities().filter((e)=>!skipEntities.includes(e.name) && !skipEntities.includes(e.tableName));
52
- const graph = this.buildDependencyGraph(entities);
53
- return this.topologicalSort(graph);
54
- }
55
- /**
56
- * Find entity metadata by name or table name
57
- */ findEntityMetadata(entityName) {
58
- return this.getAllEntities().find((e)=>e.name === entityName || e.tableName === entityName);
59
- }
60
- /**
61
- * Extract column information from entity metadata
62
- */ extractColumns(metadata) {
63
- return metadata.columns.map((col)=>({
64
- name: col.databaseName,
65
- propertyName: col.propertyName,
66
- type: this.getColumnTypeString(col.type),
67
- length: col.length,
68
- isNullable: col.isNullable,
69
- isUnique: col.isUnique === true || false,
70
- isPrimary: col.isPrimary,
71
- isGenerated: col.isGenerated,
72
- default: col.default,
73
- enum: col.enum ? Object.values(col.enum) : undefined,
74
- precision: col.precision ?? undefined,
75
- scale: col.scale ?? undefined
76
- }));
77
- }
78
- /**
79
- * Extract relation information from entity metadata
80
- */ extractRelations(metadata) {
81
- return metadata.relations.map((rel)=>({
82
- propertyName: rel.propertyName,
83
- type: rel.relationType,
84
- targetEntity: rel.inverseEntityMetadata.name,
85
- isNullable: rel.isNullable,
86
- joinColumnName: rel.joinColumns?.[0]?.databaseName
87
- }));
88
- }
89
- /**
90
- * Check if entity has company-related columns
91
- */ hasCompanyColumn(metadata) {
92
- return metadata.columns.some((col)=>col.propertyName === 'companyId' || col.databaseName === 'company_id');
93
- }
94
- /**
95
- * Convert TypeORM column type to string
96
- */ getColumnTypeString(type) {
97
- if (typeof type === 'string') {
98
- return type;
99
- }
100
- if (typeof type === 'function') {
101
- return type.name.toLowerCase();
102
- }
103
- return 'unknown';
104
- }
105
- /**
106
- * Build dependency graph for entities
107
- */ buildDependencyGraph(entities) {
108
- const graph = new Map();
109
- for (const entity of entities){
110
- const dependencies = [];
111
- for (const relation of entity.relations){
112
- if (relation.relationType === 'many-to-one' && !relation.isNullable) {
113
- dependencies.push(relation.inverseEntityMetadata.name);
114
- }
115
- }
116
- graph.set(entity.name, dependencies);
117
- }
118
- return graph;
119
- }
120
- /**
121
- * Topological sort for dependency resolution
122
- */ topologicalSort(graph) {
123
- const sorted = [];
124
- const visited = new Set();
125
- const visiting = new Set();
126
- const visit = (node)=>{
127
- if (visited.has(node)) return;
128
- if (visiting.has(node)) {
129
- throw new Error(`Circular dependency detected involving ${node}`);
130
- }
131
- visiting.add(node);
132
- const dependencies = graph.get(node) || [];
133
- for (const dep of dependencies){
134
- if (graph.has(dep)) {
135
- visit(dep);
136
- }
137
- }
138
- visiting.delete(node);
139
- visited.add(node);
140
- sorted.push(node);
141
- };
142
- for (const node of graph.keys()){
143
- visit(node);
144
- }
145
- return sorted;
146
- }
147
- constructor(dataSource){
148
- _define_property(this, "dataSource", void 0);
149
- this.dataSource = dataSource;
150
- }
151
- };