@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
|
@@ -1,168 +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
|
-
* Check if entity has required columns populated
|
|
57
|
-
* Used to determine if entity is ready for seeding
|
|
58
|
-
*/ hasRequiredDependencies(entityName, dataSource) {
|
|
59
|
-
const info = this.getEntityInfo(entityName);
|
|
60
|
-
for (const relation of info.relations){
|
|
61
|
-
if (!relation.isNullable && relation.type === 'many-to-one') {
|
|
62
|
-
const targetRepo = dataSource.getRepository(relation.targetEntity);
|
|
63
|
-
if (targetRepo) {
|
|
64
|
-
// Check if target entity has data
|
|
65
|
-
// Note: This is a simplified check, actual implementation may need async
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Find entity metadata by name or table name
|
|
74
|
-
*/ findEntityMetadata(entityName) {
|
|
75
|
-
return this.getAllEntities().find((e)=>e.name === entityName || e.tableName === entityName);
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Extract column information from entity metadata
|
|
79
|
-
*/ extractColumns(metadata) {
|
|
80
|
-
return metadata.columns.map((col)=>({
|
|
81
|
-
name: col.databaseName,
|
|
82
|
-
propertyName: col.propertyName,
|
|
83
|
-
type: this.getColumnTypeString(col.type),
|
|
84
|
-
length: col.length,
|
|
85
|
-
isNullable: col.isNullable,
|
|
86
|
-
isUnique: col.isUnique === true || false,
|
|
87
|
-
isPrimary: col.isPrimary,
|
|
88
|
-
isGenerated: col.isGenerated,
|
|
89
|
-
default: col.default,
|
|
90
|
-
enum: col.enum ? Object.values(col.enum) : undefined,
|
|
91
|
-
precision: col.precision ?? undefined,
|
|
92
|
-
scale: col.scale ?? undefined
|
|
93
|
-
}));
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Extract relation information from entity metadata
|
|
97
|
-
*/ extractRelations(metadata) {
|
|
98
|
-
return metadata.relations.map((rel)=>({
|
|
99
|
-
propertyName: rel.propertyName,
|
|
100
|
-
type: rel.relationType,
|
|
101
|
-
targetEntity: rel.inverseEntityMetadata.name,
|
|
102
|
-
isNullable: rel.isNullable,
|
|
103
|
-
joinColumnName: rel.joinColumns?.[0]?.databaseName
|
|
104
|
-
}));
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Check if entity has company-related columns
|
|
108
|
-
*/ hasCompanyColumn(metadata) {
|
|
109
|
-
return metadata.columns.some((col)=>col.propertyName === 'companyId' || col.databaseName === 'company_id');
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Convert TypeORM column type to string
|
|
113
|
-
*/ getColumnTypeString(type) {
|
|
114
|
-
if (typeof type === 'string') {
|
|
115
|
-
return type;
|
|
116
|
-
}
|
|
117
|
-
if (typeof type === 'function') {
|
|
118
|
-
return type.name.toLowerCase();
|
|
119
|
-
}
|
|
120
|
-
return 'unknown';
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Build dependency graph for entities
|
|
124
|
-
*/ buildDependencyGraph(entities) {
|
|
125
|
-
const graph = new Map();
|
|
126
|
-
for (const entity of entities){
|
|
127
|
-
const dependencies = [];
|
|
128
|
-
for (const relation of entity.relations){
|
|
129
|
-
if (relation.relationType === 'many-to-one' && !relation.isNullable) {
|
|
130
|
-
dependencies.push(relation.inverseEntityMetadata.name);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
graph.set(entity.name, dependencies);
|
|
134
|
-
}
|
|
135
|
-
return graph;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Topological sort for dependency resolution
|
|
139
|
-
*/ topologicalSort(graph) {
|
|
140
|
-
const sorted = [];
|
|
141
|
-
const visited = new Set();
|
|
142
|
-
const visiting = new Set();
|
|
143
|
-
const visit = (node)=>{
|
|
144
|
-
if (visited.has(node)) return;
|
|
145
|
-
if (visiting.has(node)) {
|
|
146
|
-
throw new Error(`Circular dependency detected involving ${node}`);
|
|
147
|
-
}
|
|
148
|
-
visiting.add(node);
|
|
149
|
-
const dependencies = graph.get(node) || [];
|
|
150
|
-
for (const dep of dependencies){
|
|
151
|
-
if (graph.has(dep)) {
|
|
152
|
-
visit(dep);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
visiting.delete(node);
|
|
156
|
-
visited.add(node);
|
|
157
|
-
sorted.push(node);
|
|
158
|
-
};
|
|
159
|
-
for (const node of graph.keys()){
|
|
160
|
-
visit(node);
|
|
161
|
-
}
|
|
162
|
-
return sorted;
|
|
163
|
-
}
|
|
164
|
-
constructor(dataSource){
|
|
165
|
-
_define_property(this, "dataSource", void 0);
|
|
166
|
-
this.dataSource = dataSource;
|
|
167
|
-
}
|
|
168
|
-
};
|
package/cjs/seeders/index.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Seed System Exports
|
|
3
|
-
*
|
|
4
|
-
* Public API for seed data generation system.
|
|
5
|
-
*/ "use strict";
|
|
6
|
-
Object.defineProperty(exports, "__esModule", {
|
|
7
|
-
value: true
|
|
8
|
-
});
|
|
9
|
-
function _export(target, all) {
|
|
10
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
11
|
-
enumerable: true,
|
|
12
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
_export(exports, {
|
|
16
|
-
get BaseSeeder () {
|
|
17
|
-
return _baseseeder.BaseSeeder;
|
|
18
|
-
},
|
|
19
|
-
get DataGenerator () {
|
|
20
|
-
return _datagenerator.DataGenerator;
|
|
21
|
-
},
|
|
22
|
-
get EntityReader () {
|
|
23
|
-
return _entityreader.EntityReader;
|
|
24
|
-
},
|
|
25
|
-
get IColumnInfo () {
|
|
26
|
-
return _entityreader.IColumnInfo;
|
|
27
|
-
},
|
|
28
|
-
get IEntityInfo () {
|
|
29
|
-
return _entityreader.IEntityInfo;
|
|
30
|
-
},
|
|
31
|
-
get IRelationInfo () {
|
|
32
|
-
return _entityreader.IRelationInfo;
|
|
33
|
-
},
|
|
34
|
-
get ISeedConfig () {
|
|
35
|
-
return _seedconfig.ISeedConfig;
|
|
36
|
-
},
|
|
37
|
-
get ISeedOptions () {
|
|
38
|
-
return _seedrunner.ISeedOptions;
|
|
39
|
-
},
|
|
40
|
-
get ISeedResult () {
|
|
41
|
-
return _seedrunner.ISeedResult;
|
|
42
|
-
},
|
|
43
|
-
get ISeederLogger () {
|
|
44
|
-
return _seedrunner.ISeederLogger;
|
|
45
|
-
},
|
|
46
|
-
get SeedRunner () {
|
|
47
|
-
return _seedrunner.SeedRunner;
|
|
48
|
-
},
|
|
49
|
-
get TemplateGenerator () {
|
|
50
|
-
return _templategenerator.TemplateGenerator;
|
|
51
|
-
},
|
|
52
|
-
get defaultLogger () {
|
|
53
|
-
return _seedrunner.defaultLogger;
|
|
54
|
-
},
|
|
55
|
-
get getEntityCount () {
|
|
56
|
-
return _seedconfig.getEntityCount;
|
|
57
|
-
},
|
|
58
|
-
get getSeedingOrder () {
|
|
59
|
-
return _seedconfig.getSeedingOrder;
|
|
60
|
-
},
|
|
61
|
-
get runSeedCli () {
|
|
62
|
-
return _cli.runSeedCli;
|
|
63
|
-
},
|
|
64
|
-
get seedConfig () {
|
|
65
|
-
return _seedconfig.seedConfig;
|
|
66
|
-
},
|
|
67
|
-
get shouldSkipEntity () {
|
|
68
|
-
return _seedconfig.shouldSkipEntity;
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
const _baseseeder = require("./base-seeder");
|
|
72
|
-
const _entityreader = require("./entity-reader");
|
|
73
|
-
const _datagenerator = require("./data-generator");
|
|
74
|
-
const _templategenerator = require("./template-generator");
|
|
75
|
-
const _seedrunner = require("./seed-runner");
|
|
76
|
-
const _seedconfig = require("./seed-config");
|
|
77
|
-
const _cli = require("./cli");
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Seed Configuration
|
|
3
|
-
*
|
|
4
|
-
* Configuration for seed data generation.
|
|
5
|
-
* Customize record counts, seeding order, and behavior.
|
|
6
|
-
*/ "use strict";
|
|
7
|
-
Object.defineProperty(exports, "__esModule", {
|
|
8
|
-
value: true
|
|
9
|
-
});
|
|
10
|
-
function _export(target, all) {
|
|
11
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
12
|
-
enumerable: true,
|
|
13
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
_export(exports, {
|
|
17
|
-
get getEntityCount () {
|
|
18
|
-
return getEntityCount;
|
|
19
|
-
},
|
|
20
|
-
get getSeedingOrder () {
|
|
21
|
-
return getSeedingOrder;
|
|
22
|
-
},
|
|
23
|
-
get seedConfig () {
|
|
24
|
-
return seedConfig;
|
|
25
|
-
},
|
|
26
|
-
get shouldSkipEntity () {
|
|
27
|
-
return shouldSkipEntity;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
const seedConfig = {
|
|
31
|
-
// Default record counts per entity
|
|
32
|
-
counts: {
|
|
33
|
-
// Auth entities
|
|
34
|
-
Company: 10,
|
|
35
|
-
CompanyBranch: 25,
|
|
36
|
-
User: 50,
|
|
37
|
-
// IAM entities
|
|
38
|
-
Role: 8,
|
|
39
|
-
Permission: 45,
|
|
40
|
-
Action: 30,
|
|
41
|
-
Menu: 20,
|
|
42
|
-
UserIAMPermission: 50,
|
|
43
|
-
UserCompanyPermission: 100,
|
|
44
|
-
// Storage entities
|
|
45
|
-
StorageConfig: 5,
|
|
46
|
-
Folder: 20,
|
|
47
|
-
FileManager: 100
|
|
48
|
-
},
|
|
49
|
-
// Entity seeding order (respects FK constraints)
|
|
50
|
-
// Parent entities must come before child entities
|
|
51
|
-
order: [
|
|
52
|
-
// Auth - foundational
|
|
53
|
-
'Company',
|
|
54
|
-
'CompanyBranch',
|
|
55
|
-
'User',
|
|
56
|
-
// IAM - permissions system
|
|
57
|
-
'Action',
|
|
58
|
-
'Role',
|
|
59
|
-
'Permission',
|
|
60
|
-
'Menu',
|
|
61
|
-
'UserIAMPermission',
|
|
62
|
-
'UserCompanyPermission',
|
|
63
|
-
// Storage - file system
|
|
64
|
-
'StorageConfig',
|
|
65
|
-
'Folder',
|
|
66
|
-
'FileManager'
|
|
67
|
-
],
|
|
68
|
-
// Skip these entities (system tables, migrations)
|
|
69
|
-
skipEntities: [
|
|
70
|
-
'migrations',
|
|
71
|
-
'typeorm_metadata',
|
|
72
|
-
'Migration',
|
|
73
|
-
'Typeorm_Metadata'
|
|
74
|
-
],
|
|
75
|
-
// Faker locale (en, ar, es, fr, de, etc.)
|
|
76
|
-
locale: 'en',
|
|
77
|
-
// Respect soft delete when clearing data
|
|
78
|
-
respectSoftDelete: true
|
|
79
|
-
};
|
|
80
|
-
function getEntityCount(entityName, config = seedConfig) {
|
|
81
|
-
return config.counts[entityName] || 10;
|
|
82
|
-
}
|
|
83
|
-
function shouldSkipEntity(entityName, config = seedConfig) {
|
|
84
|
-
return config.skipEntities.some((skip)=>skip.toLowerCase() === entityName.toLowerCase());
|
|
85
|
-
}
|
|
86
|
-
function getSeedingOrder(availableEntities, config = seedConfig) {
|
|
87
|
-
const ordered = [];
|
|
88
|
-
// Add entities in configured order
|
|
89
|
-
for (const entityName of config.order){
|
|
90
|
-
if (availableEntities.includes(entityName) && !shouldSkipEntity(entityName, config)) {
|
|
91
|
-
ordered.push(entityName);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
// Add remaining entities not in order configuration
|
|
95
|
-
for (const entityName of availableEntities){
|
|
96
|
-
if (!ordered.includes(entityName) && !shouldSkipEntity(entityName, config)) {
|
|
97
|
-
ordered.push(entityName);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return ordered;
|
|
101
|
-
}
|
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
get SeedRunner () {
|
|
13
|
-
return SeedRunner;
|
|
14
|
-
},
|
|
15
|
-
get defaultLogger () {
|
|
16
|
-
return defaultLogger;
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
const _entityreader = require("./entity-reader");
|
|
20
|
-
const _datagenerator = require("./data-generator");
|
|
21
|
-
const _baseseeder = require("./base-seeder");
|
|
22
|
-
const _seedconfig = require("./seed-config");
|
|
23
|
-
function _define_property(obj, key, value) {
|
|
24
|
-
if (key in obj) {
|
|
25
|
-
Object.defineProperty(obj, key, {
|
|
26
|
-
value: value,
|
|
27
|
-
enumerable: true,
|
|
28
|
-
configurable: true,
|
|
29
|
-
writable: true
|
|
30
|
-
});
|
|
31
|
-
} else {
|
|
32
|
-
obj[key] = value;
|
|
33
|
-
}
|
|
34
|
-
return obj;
|
|
35
|
-
}
|
|
36
|
-
const defaultLogger = {
|
|
37
|
-
log: (message)=>console.log(message),
|
|
38
|
-
error: (message)=>console.error(message)
|
|
39
|
-
};
|
|
40
|
-
let SeedRunner = class SeedRunner {
|
|
41
|
-
/**
|
|
42
|
-
* Register a custom seeder for specific entity
|
|
43
|
-
* Custom seeders take precedence over generic seeders
|
|
44
|
-
* @param entityName Entity name
|
|
45
|
-
* @param seeder Custom seeder instance
|
|
46
|
-
*/ registerCustomSeeder(entityName, seeder) {
|
|
47
|
-
this.customSeeders.set(entityName, seeder);
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Unregister a custom seeder
|
|
51
|
-
* @param entityName Entity name
|
|
52
|
-
*/ unregisterCustomSeeder(entityName) {
|
|
53
|
-
this.customSeeders.delete(entityName);
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Check if entity has custom seeder registered
|
|
57
|
-
* @param entityName Entity name
|
|
58
|
-
*/ hasCustomSeeder(entityName) {
|
|
59
|
-
return this.customSeeders.has(entityName);
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Run seeds for all entities
|
|
63
|
-
* @param options Seeding options
|
|
64
|
-
* @returns Array of seed results
|
|
65
|
-
*/ async runAll(options = {}) {
|
|
66
|
-
const results = [];
|
|
67
|
-
const continueOnError = options.continueOnError ?? true;
|
|
68
|
-
// Get all entities
|
|
69
|
-
const allEntities = this.entityReader.getAllEntities();
|
|
70
|
-
const entityNames = allEntities.map((e)=>e.name).filter((name)=>!(0, _seedconfig.shouldSkipEntity)(name));
|
|
71
|
-
// Get seeding order
|
|
72
|
-
const orderedNames = (0, _seedconfig.getSeedingOrder)(entityNames);
|
|
73
|
-
const mode = options.dryRun ? 'Previewing' : 'Seeding';
|
|
74
|
-
this.logger.log(`\nš± ${mode} ${orderedNames.length} entities...\n`);
|
|
75
|
-
for(let i = 0; i < orderedNames.length; i++){
|
|
76
|
-
const entityName = orderedNames[i];
|
|
77
|
-
// Call progress callback
|
|
78
|
-
options.onProgress?.(i + 1, orderedNames.length, entityName);
|
|
79
|
-
const result = await this.runSingle(entityName, options);
|
|
80
|
-
results.push(result);
|
|
81
|
-
// Log result
|
|
82
|
-
if (result.success) {
|
|
83
|
-
if (result.dryRun) {
|
|
84
|
-
this.logger.log(`ā ${entityName}: ${result.count} records (dry run)`);
|
|
85
|
-
} else if (result.skipped) {
|
|
86
|
-
this.logger.log(`ā ${entityName}: skipped (already has data)`);
|
|
87
|
-
} else {
|
|
88
|
-
this.logger.log(`ā ${entityName}: ${result.count} records`);
|
|
89
|
-
}
|
|
90
|
-
} else {
|
|
91
|
-
this.logger.error(`ā ${entityName}: ${result.error}`);
|
|
92
|
-
// Fail-fast if continueOnError is false
|
|
93
|
-
if (!continueOnError) {
|
|
94
|
-
this.logger.error(`\nā Seeding failed at ${entityName}, stopping...\n`);
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
const successCount = results.filter((r)=>r.success).length;
|
|
100
|
-
const failedCount = results.length - successCount;
|
|
101
|
-
if (failedCount > 0) {
|
|
102
|
-
this.logger.log(`\nā ļø Completed with errors: ${successCount}/${results.length} entities seeded (${failedCount} failed)\n`);
|
|
103
|
-
} else {
|
|
104
|
-
this.logger.log(`\nā Completed: ${successCount}/${results.length} entities seeded\n`);
|
|
105
|
-
}
|
|
106
|
-
return results;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Run seed for a single entity
|
|
110
|
-
* @param entityName Entity name to seed
|
|
111
|
-
* @param options Seeding options
|
|
112
|
-
* @returns Seed result
|
|
113
|
-
*/ async runSingle(entityName, options = {}) {
|
|
114
|
-
const queryRunner = this.dataSource.createQueryRunner();
|
|
115
|
-
await queryRunner.connect();
|
|
116
|
-
await queryRunner.startTransaction();
|
|
117
|
-
try {
|
|
118
|
-
// Get entity info
|
|
119
|
-
const entityInfo = this.entityReader.getEntityInfo(entityName);
|
|
120
|
-
// Find entity target
|
|
121
|
-
const entity = this.dataSource.entityMetadatas.find((e)=>e.name === entityName)?.target;
|
|
122
|
-
if (!entity) {
|
|
123
|
-
throw new Error(`Entity ${entityName} not found`);
|
|
124
|
-
}
|
|
125
|
-
// Get seeder (custom or generic)
|
|
126
|
-
let seeder;
|
|
127
|
-
if (this.customSeeders.has(entityName)) {
|
|
128
|
-
seeder = this.customSeeders.get(entityName);
|
|
129
|
-
} else {
|
|
130
|
-
seeder = new GenericSeeder(queryRunner.manager.connection, entity, this.dataGenerator, entityInfo, options.batchSize);
|
|
131
|
-
}
|
|
132
|
-
// Dry run mode - preview without executing
|
|
133
|
-
if (options.dryRun) {
|
|
134
|
-
const count = options.count ?? (0, _seedconfig.getEntityCount)(entityName);
|
|
135
|
-
await queryRunner.rollbackTransaction();
|
|
136
|
-
return {
|
|
137
|
-
entity: entityName,
|
|
138
|
-
success: true,
|
|
139
|
-
count,
|
|
140
|
-
dryRun: true
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
// Skip if exists
|
|
144
|
-
if (options.skipIfExists) {
|
|
145
|
-
const existingCount = await seeder.count();
|
|
146
|
-
if (existingCount > 0) {
|
|
147
|
-
await queryRunner.rollbackTransaction();
|
|
148
|
-
return {
|
|
149
|
-
entity: entityName,
|
|
150
|
-
success: true,
|
|
151
|
-
count: 0,
|
|
152
|
-
skipped: true
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
// Clear if requested
|
|
157
|
-
if (options.clear) {
|
|
158
|
-
await seeder.clear(options.respectSoftDelete ?? _seedconfig.seedConfig.respectSoftDelete);
|
|
159
|
-
}
|
|
160
|
-
// Generate data
|
|
161
|
-
const count = options.count ?? (0, _seedconfig.getEntityCount)(entityName);
|
|
162
|
-
const entities = await seeder.generate(count);
|
|
163
|
-
// Commit transaction
|
|
164
|
-
await queryRunner.commitTransaction();
|
|
165
|
-
return {
|
|
166
|
-
entity: entityName,
|
|
167
|
-
success: true,
|
|
168
|
-
count: entities.length
|
|
169
|
-
};
|
|
170
|
-
} catch (error) {
|
|
171
|
-
// Rollback on error
|
|
172
|
-
await queryRunner.rollbackTransaction();
|
|
173
|
-
return {
|
|
174
|
-
entity: entityName,
|
|
175
|
-
success: false,
|
|
176
|
-
count: 0,
|
|
177
|
-
error: error instanceof Error ? error.message : String(error)
|
|
178
|
-
};
|
|
179
|
-
} finally{
|
|
180
|
-
await queryRunner.release();
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Clear all seeded data
|
|
185
|
-
* @param hard If true, hard delete (ignore soft delete)
|
|
186
|
-
* @param continueOnError If true, continue even if an entity fails (default: true)
|
|
187
|
-
* @returns Array of clear results
|
|
188
|
-
*/ async clearAll(hard = false, continueOnError = true) {
|
|
189
|
-
const results = [];
|
|
190
|
-
// Get all entities
|
|
191
|
-
const allEntities = this.entityReader.getAllEntities();
|
|
192
|
-
const entityNames = allEntities.map((e)=>e.name).filter((name)=>!(0, _seedconfig.shouldSkipEntity)(name));
|
|
193
|
-
// Reverse order for clearing (delete children before parents)
|
|
194
|
-
const orderedNames = (0, _seedconfig.getSeedingOrder)(entityNames).reverse();
|
|
195
|
-
const mode = hard ? 'Hard deleting' : 'Clearing';
|
|
196
|
-
this.logger.log(`\nšļø ${mode} ${orderedNames.length} entities...\n`);
|
|
197
|
-
for (const entityName of orderedNames){
|
|
198
|
-
try {
|
|
199
|
-
const entity = this.dataSource.entityMetadatas.find((e)=>e.name === entityName)?.target;
|
|
200
|
-
if (!entity) {
|
|
201
|
-
throw new Error(`Entity ${entityName} not found`);
|
|
202
|
-
}
|
|
203
|
-
// Use custom seeder if available, otherwise generic
|
|
204
|
-
let seeder;
|
|
205
|
-
if (this.customSeeders.has(entityName)) {
|
|
206
|
-
seeder = this.customSeeders.get(entityName);
|
|
207
|
-
} else {
|
|
208
|
-
seeder = new GenericSeeder(this.dataSource, entity, this.dataGenerator, this.entityReader.getEntityInfo(entityName));
|
|
209
|
-
}
|
|
210
|
-
const countBefore = await seeder.count(true);
|
|
211
|
-
// Skip if already empty
|
|
212
|
-
if (countBefore === 0) {
|
|
213
|
-
results.push({
|
|
214
|
-
entity: entityName,
|
|
215
|
-
success: true,
|
|
216
|
-
count: 0
|
|
217
|
-
});
|
|
218
|
-
this.logger.log(`ā ${entityName}: already empty`);
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
await seeder.clear(hard);
|
|
222
|
-
const countAfter = await seeder.count(true);
|
|
223
|
-
results.push({
|
|
224
|
-
entity: entityName,
|
|
225
|
-
success: true,
|
|
226
|
-
count: countBefore - countAfter
|
|
227
|
-
});
|
|
228
|
-
this.logger.log(`ā ${entityName}: ${countBefore - countAfter} records cleared`);
|
|
229
|
-
} catch (error) {
|
|
230
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
231
|
-
results.push({
|
|
232
|
-
entity: entityName,
|
|
233
|
-
success: false,
|
|
234
|
-
count: 0,
|
|
235
|
-
error: errorMessage
|
|
236
|
-
});
|
|
237
|
-
this.logger.error(`ā ${entityName}: ${errorMessage}`);
|
|
238
|
-
// Fail-fast if continueOnError is false
|
|
239
|
-
if (!continueOnError) {
|
|
240
|
-
this.logger.error(`\nā Clearing failed at ${entityName}, stopping...\n`);
|
|
241
|
-
break;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
const successCount = results.filter((r)=>r.success).length;
|
|
246
|
-
const failedCount = results.length - successCount;
|
|
247
|
-
if (failedCount > 0) {
|
|
248
|
-
this.logger.log(`\nā ļø Completed with errors: ${successCount}/${results.length} entities cleared (${failedCount} failed)\n`);
|
|
249
|
-
} else {
|
|
250
|
-
this.logger.log(`\nā Completed: ${successCount}/${results.length} entities cleared\n`);
|
|
251
|
-
}
|
|
252
|
-
return results;
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Get status for all entities
|
|
256
|
-
* @returns Array of entity status
|
|
257
|
-
*/ async getStatus() {
|
|
258
|
-
const allEntities = this.entityReader.getAllEntities();
|
|
259
|
-
const status = [];
|
|
260
|
-
for (const metadata of allEntities){
|
|
261
|
-
if ((0, _seedconfig.shouldSkipEntity)(metadata.name)) {
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
const repo = this.dataSource.getRepository(metadata.target);
|
|
265
|
-
const count = await repo.count();
|
|
266
|
-
const isEmpty = count === 0;
|
|
267
|
-
const hasSoftDelete = metadata.deleteDateColumn !== undefined;
|
|
268
|
-
status.push({
|
|
269
|
-
entity: metadata.name,
|
|
270
|
-
tableName: metadata.tableName,
|
|
271
|
-
count,
|
|
272
|
-
isEmpty,
|
|
273
|
-
hasSoftDelete
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
return status;
|
|
277
|
-
}
|
|
278
|
-
constructor(dataSource, logger){
|
|
279
|
-
_define_property(this, "dataSource", void 0);
|
|
280
|
-
_define_property(this, "entityReader", void 0);
|
|
281
|
-
_define_property(this, "dataGenerator", void 0);
|
|
282
|
-
_define_property(this, "logger", void 0);
|
|
283
|
-
_define_property(this, "customSeeders", void 0);
|
|
284
|
-
this.dataSource = dataSource;
|
|
285
|
-
this.customSeeders = new Map();
|
|
286
|
-
this.entityReader = new _entityreader.EntityReader(dataSource);
|
|
287
|
-
this.dataGenerator = new _datagenerator.DataGenerator(_seedconfig.seedConfig.locale);
|
|
288
|
-
this.logger = logger ?? defaultLogger;
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
/**
|
|
292
|
-
* Generic Seeder Implementation
|
|
293
|
-
*
|
|
294
|
-
* Used by SeedRunner for entities without custom seeders.
|
|
295
|
-
* Generates data based on entity metadata with batch processing support.
|
|
296
|
-
*/ let GenericSeeder = class GenericSeeder extends _baseseeder.BaseSeeder {
|
|
297
|
-
async generate(count) {
|
|
298
|
-
const allSaved = [];
|
|
299
|
-
// Process in batches to optimize memory usage for large datasets
|
|
300
|
-
for(let offset = 0; offset < count; offset += this.batchSize){
|
|
301
|
-
const currentBatchSize = Math.min(this.batchSize, count - offset);
|
|
302
|
-
const batch = [];
|
|
303
|
-
for(let i = 0; i < currentBatchSize; i++){
|
|
304
|
-
const data = this.generator.generateEntity(this.entityInfo.columns);
|
|
305
|
-
// TypeORM's create method returns T, but TypeScript needs explicit type assertion
|
|
306
|
-
const entity = this.repository.create(data);
|
|
307
|
-
batch.push(entity);
|
|
308
|
-
}
|
|
309
|
-
// TypeORM's save method has overloads for T and T[]
|
|
310
|
-
// We need to explicitly cast to handle the generic type resolution
|
|
311
|
-
const saved = await this.repository.save(batch);
|
|
312
|
-
allSaved.push(...saved);
|
|
313
|
-
}
|
|
314
|
-
return allSaved;
|
|
315
|
-
}
|
|
316
|
-
constructor(dataSource, entity, generator, entityInfo, batchSize){
|
|
317
|
-
super(dataSource, entity), _define_property(this, "generator", void 0), _define_property(this, "entityInfo", void 0), _define_property(this, "batchSize", void 0), this.generator = generator, this.entityInfo = entityInfo;
|
|
318
|
-
this.batchSize = batchSize ?? 1000; // Default batch size
|
|
319
|
-
}
|
|
320
|
-
};
|