@flusys/nestjs-core 0.1.0-beta.3 → 1.0.0-rc
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 +45 -8
- package/cjs/config/env-config.service.js +1 -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/seeders/base-seeder.js +6 -25
- package/cjs/seeders/data-generator.js +5 -3
- package/cjs/seeders/entity-reader.js +0 -17
- package/cjs/seeders/index.js +4 -5
- package/cjs/seeders/seed-config.js +9 -48
- package/cjs/utils/datasource-config.builder.js +2 -14
- package/fesm/config/env-config.service.js +1 -1
- 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 +6 -67
- package/fesm/seeders/base-seeder.js +7 -51
- package/fesm/seeders/data-generator.js +5 -3
- package/fesm/seeders/entity-reader.js +0 -17
- package/fesm/seeders/index.js +2 -6
- package/fesm/seeders/seed-config.js +9 -59
- package/fesm/utils/datasource-config.builder.js +2 -13
- package/interfaces/base-entity.interface.d.ts +3 -0
- package/package.json +2 -2
- package/seeders/entity-reader.d.ts +0 -1
- package/seeders/index.d.ts +2 -2
- package/seeders/seed-config.d.ts +1 -0
- package/utils/datasource-config.builder.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Core Package Guide
|
|
2
2
|
|
|
3
3
|
> **Package:** `@flusys/nestjs-core`
|
|
4
|
-
> **Type:**
|
|
4
|
+
> **Type:** Foundation utilities for configuration, database, migrations, and seeders
|
|
5
5
|
|
|
6
6
|
Foundation layer providing type-safe configuration, database utilities, migration CLI, seeders, and Swagger setup.
|
|
7
7
|
|
|
@@ -15,7 +15,7 @@ nestjs-core/src/
|
|
|
15
15
|
│ └── database.constants.ts # MODULE_OPTIONS, DEFAULT_TENANT_HEADER
|
|
16
16
|
├── interfaces/
|
|
17
17
|
│ ├── app-config.interfaces.ts # IBootstrapAppConfig, IDynamicModuleConfig
|
|
18
|
-
│ ├── base-entity.interface.ts # IBaseEntity, ISoftDeletable, ITimestampable
|
|
18
|
+
│ ├── base-entity.interface.ts # IBaseEntity, ISoftDeletable, ITimestampable, IOrderable, IMetadata, ICompanyOwned
|
|
19
19
|
│ ├── database.interface.ts # IDatabaseConfig, ITenantDatabaseConfig
|
|
20
20
|
│ └── migration.interface.ts # IMigrationConfig, IMigrationResult
|
|
21
21
|
├── utils/
|
|
@@ -74,6 +74,14 @@ interface IBootstrapAppConfig {
|
|
|
74
74
|
enableCompanyFeature: boolean;
|
|
75
75
|
permissionMode?: 'FULL' | 'RBAC' | 'DIRECT';
|
|
76
76
|
}
|
|
77
|
+
|
|
78
|
+
interface ICompanyOwned {
|
|
79
|
+
companyId?: string | null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface IMetadata {
|
|
83
|
+
metadata?: Record<string, any> | null;
|
|
84
|
+
}
|
|
77
85
|
```
|
|
78
86
|
|
|
79
87
|
## Migration CLI
|
|
@@ -100,6 +108,23 @@ npm run seed:clear
|
|
|
100
108
|
npm run seed:status
|
|
101
109
|
```
|
|
102
110
|
|
|
111
|
+
### Seed Configuration
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { configureSeedConfig, seedConfig } from '@flusys/nestjs-core/seeders';
|
|
115
|
+
|
|
116
|
+
// Configure before running seeders
|
|
117
|
+
configureSeedConfig({
|
|
118
|
+
counts: { User: 50, Product: 100 },
|
|
119
|
+
order: ['User', 'Product', 'Order'],
|
|
120
|
+
skipEntities: ['migrations', 'typeorm_metadata'],
|
|
121
|
+
locale: 'en',
|
|
122
|
+
respectSoftDelete: true,
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Custom Seeders
|
|
127
|
+
|
|
103
128
|
```typescript
|
|
104
129
|
import { BaseSeeder, SeedRunner } from '@flusys/nestjs-core/seeders';
|
|
105
130
|
|
|
@@ -124,6 +149,10 @@ await runner.runAll({ count: 50, clear: true });
|
|
|
124
149
|
```typescript
|
|
125
150
|
import { setupSwaggerDocs, setupModuleSwaggerDocs } from '@flusys/nestjs-core/docs';
|
|
126
151
|
|
|
152
|
+
// Variadic API for multiple configs
|
|
153
|
+
setupSwaggerDocs(app, authConfig, adminConfig);
|
|
154
|
+
|
|
155
|
+
// Array API for module docs
|
|
127
156
|
setupModuleSwaggerDocs(app, [
|
|
128
157
|
{ title: 'Auth API', path: 'auth/docs', bearerAuth: true },
|
|
129
158
|
{ title: 'Admin API', path: 'admin/docs', excludeTags: ['public'] },
|
|
@@ -150,7 +179,7 @@ import {
|
|
|
150
179
|
IDatabaseConfig, ITenantDatabaseConfig, IBootstrapAppConfig,
|
|
151
180
|
IDynamicModuleConfig, IDataSourceServiceOptions,
|
|
152
181
|
IMigrationConfig, IMigrationResult,
|
|
153
|
-
IBaseEntity, ISoftDeletable, ITimestampable, IOrderable,
|
|
182
|
+
IBaseEntity, ISoftDeletable, ITimestampable, IOrderable, ICompanyOwned, IMetadata,
|
|
154
183
|
} from '@flusys/nestjs-core';
|
|
155
184
|
|
|
156
185
|
// Constants
|
|
@@ -161,26 +190,31 @@ import { envConfig } from '@flusys/nestjs-core/config';
|
|
|
161
190
|
|
|
162
191
|
// Utils
|
|
163
192
|
import {
|
|
164
|
-
buildDataSourceOptions, getDatabaseForTenant,
|
|
165
|
-
|
|
193
|
+
buildDataSourceOptions, getDatabaseForTenant, resolveEntities,
|
|
194
|
+
getActiveTenants, getMigrationsFolderPath, getMigrationsGlobPattern,
|
|
166
195
|
} from '@flusys/nestjs-core/utils';
|
|
167
196
|
|
|
168
197
|
// Migration
|
|
169
198
|
import {
|
|
170
199
|
createMigrationDataSource, initializeDataSource, runMigrationCli,
|
|
171
200
|
generateMigration, runMigrations, revertMigration, migrationStatus,
|
|
201
|
+
runForAllTenants, ensureMigrationsFolder,
|
|
172
202
|
} from '@flusys/nestjs-core/migration';
|
|
173
203
|
|
|
174
204
|
// Seeders
|
|
175
205
|
import {
|
|
176
|
-
BaseSeeder, SeedRunner, EntityReader, DataGenerator,
|
|
177
|
-
seedConfig, runSeedCli,
|
|
206
|
+
BaseSeeder, SeedRunner, EntityReader, DataGenerator, TemplateGenerator,
|
|
207
|
+
seedConfig, ISeedConfig, configureSeedConfig, runSeedCli,
|
|
208
|
+
getEntityCount, shouldSkipEntity, getSeedingOrder,
|
|
209
|
+
ISeedResult, ISeedOptions, ISeederLogger, defaultLogger,
|
|
210
|
+
IEntityInfo, IColumnInfo, IRelationInfo,
|
|
178
211
|
} from '@flusys/nestjs-core/seeders';
|
|
179
212
|
|
|
180
213
|
// Swagger
|
|
181
214
|
import {
|
|
182
215
|
setupSwaggerDocs, setupModuleSwaggerDocs,
|
|
183
216
|
IModuleSwaggerOptions, ISchemaPropertyExclusion,
|
|
217
|
+
IQueryParameterExclusion, ISwaggerGlobalHeader,
|
|
184
218
|
} from '@flusys/nestjs-core/docs';
|
|
185
219
|
```
|
|
186
220
|
|
|
@@ -189,5 +223,8 @@ import {
|
|
|
189
223
|
- `dotenv` - Environment variable loading
|
|
190
224
|
- `typeorm` - Migration/seeder utilities
|
|
191
225
|
- `@faker-js/faker` - Seed data generation
|
|
226
|
+
- `@nestjs/common`, `@nestjs/swagger` - Swagger documentation setup only
|
|
227
|
+
|
|
228
|
+
---
|
|
192
229
|
|
|
193
|
-
**
|
|
230
|
+
**Last Updated:** 2026-02-18
|
|
@@ -85,7 +85,7 @@ let EnvConfigService = class EnvConfigService {
|
|
|
85
85
|
return this.getValue(key, throwOnMissing).toLowerCase() === 'true';
|
|
86
86
|
}
|
|
87
87
|
getPort() {
|
|
88
|
-
return this.getNumber('PORT', false);
|
|
88
|
+
return this.getNumber('PORT', false) || 3000;
|
|
89
89
|
}
|
|
90
90
|
isProduction() {
|
|
91
91
|
return this.getValue('MODE', false).toUpperCase() !== 'DEV';
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
|
|
2
|
+
* Entity Contract Interfaces
|
|
3
|
+
*
|
|
4
|
+
* These interfaces define the standard fields that entities should have.
|
|
5
|
+
* Use them as contracts when implementing new entities.
|
|
6
|
+
*/ /** Base entity with UUID, timestamps, and soft delete */ "use strict";
|
|
5
7
|
Object.defineProperty(exports, "__esModule", {
|
|
6
8
|
value: true
|
|
7
9
|
});
|
|
@@ -22,10 +22,9 @@ const _datasourceconfigbuilder = require("../utils/datasource-config.builder");
|
|
|
22
22
|
function createMigrationDataSource(options) {
|
|
23
23
|
const { config, tenantId } = options;
|
|
24
24
|
const { database, tenant, tenantConfig } = (0, _datasourceconfigbuilder.getDatabaseForTenant)(config, tenantId);
|
|
25
|
-
// Log target info
|
|
26
25
|
console.log(`\n📦 Database: ${database}${tenant ? ` [${tenant.name}]` : ' [default]'}`);
|
|
27
26
|
console.log(`📁 Migrations: migrations/${tenantId || 'default'}/`);
|
|
28
|
-
if (config.bootstrapAppConfig?.databaseMode
|
|
27
|
+
if (config.bootstrapAppConfig?.databaseMode === 'multi-tenant' && !tenantId && config.tenants && config.tenants.length > 0) {
|
|
29
28
|
console.log(`\n💡 Multi-tenant mode. Available tenants:`);
|
|
30
29
|
config.tenants.forEach((t)=>console.log(` - TENANT_ID=${t.id} → ${t.database}`));
|
|
31
30
|
}
|
|
@@ -40,7 +39,6 @@ function createMigrationDataSource(options) {
|
|
|
40
39
|
...config.defaultDatabaseConfig,
|
|
41
40
|
database
|
|
42
41
|
};
|
|
43
|
-
// Resolve entities based on tenant config
|
|
44
42
|
const entities = (0, _datasourceconfigbuilder.resolveEntities)(config, tenantConfig);
|
|
45
43
|
const dsOptions = (0, _datasourceconfigbuilder.buildDataSourceOptions)(dbConfig, entities, (0, _datasourceconfigbuilder.getMigrationsGlobPattern)(config.migrationsPath, tenantId), config.migrationsTableName);
|
|
46
44
|
return new _typeorm.DataSource({
|
package/cjs/migration/index.js
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Migration Module Exports
|
|
3
|
-
*
|
|
4
|
-
* Public API for TypeORM migration management in single and multi-tenant databases.
|
|
5
|
-
*
|
|
6
|
-
* Main exports:
|
|
7
|
-
* - createMigrationDataSource: Create DataSource for migrations
|
|
8
|
-
* - initializeDataSource: Create and initialize DataSource
|
|
9
|
-
* - runMigrationCli: Programmatic CLI execution
|
|
10
|
-
*
|
|
11
|
-
* The CLI entry point is at ./cli.ts (used by npm scripts)
|
|
12
|
-
*/ // DataSource factory for migration config files
|
|
13
1
|
"use strict";
|
|
14
2
|
Object.defineProperty(exports, "__esModule", {
|
|
15
3
|
value: true
|
|
@@ -53,23 +53,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
53
53
|
}
|
|
54
54
|
return newObj;
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
* Migration CLI Service
|
|
58
|
-
*
|
|
59
|
-
* Handles migration command execution for single and multi-tenant databases.
|
|
60
|
-
*
|
|
61
|
-
* Commands:
|
|
62
|
-
* - generate: Create new migration
|
|
63
|
-
* - run: Execute pending migrations
|
|
64
|
-
* - revert: Rollback last migration
|
|
65
|
-
* - status: Check migration status
|
|
66
|
-
* - run:all/revert:all/status:all: Batch operations for all tenants
|
|
67
|
-
*
|
|
68
|
-
* Configuration:
|
|
69
|
-
* - TENANT_ID: Target tenant (optional, defaults to default database)
|
|
70
|
-
* - --config=<path>: Migration config file path
|
|
71
|
-
* - --datasource=<path>: DataSource file path for TypeORM CLI
|
|
72
|
-
*/ function showHelp() {
|
|
56
|
+
function showHelp() {
|
|
73
57
|
console.log(`
|
|
74
58
|
Migration CLI
|
|
75
59
|
|
|
@@ -22,25 +22,15 @@ function _define_property(obj, key, value) {
|
|
|
22
22
|
return obj;
|
|
23
23
|
}
|
|
24
24
|
let BaseSeeder = class BaseSeeder {
|
|
25
|
-
|
|
26
|
-
* Clear all records from the entity table
|
|
27
|
-
* Respects soft delete if entity has deletedAt column
|
|
28
|
-
* @param hard If true, perform hard delete (ignore soft delete)
|
|
29
|
-
*/ async clear(hard = false) {
|
|
25
|
+
async clear(hard = false) {
|
|
30
26
|
const hasSoftDelete = this.metadata.deleteDateColumn !== undefined;
|
|
31
27
|
if (hard || !hasSoftDelete) {
|
|
32
|
-
// Hard delete - remove all records
|
|
33
28
|
await this.repository.clear();
|
|
34
29
|
} else {
|
|
35
|
-
// Soft delete - set deletedAt
|
|
36
30
|
await this.repository.createQueryBuilder().softDelete().where('id IS NOT NULL').execute();
|
|
37
31
|
}
|
|
38
32
|
}
|
|
39
|
-
|
|
40
|
-
* Get count of existing records
|
|
41
|
-
* @param includeDeleted If true, count soft-deleted records
|
|
42
|
-
* @returns Total record count
|
|
43
|
-
*/ async count(includeDeleted = false) {
|
|
33
|
+
async count(includeDeleted = false) {
|
|
44
34
|
const hasSoftDelete = this.metadata.deleteDateColumn !== undefined;
|
|
45
35
|
if (hasSoftDelete && !includeDeleted) {
|
|
46
36
|
return this.repository.count({
|
|
@@ -51,22 +41,13 @@ let BaseSeeder = class BaseSeeder {
|
|
|
51
41
|
}
|
|
52
42
|
return this.repository.count();
|
|
53
43
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
* @returns True if no records exist
|
|
57
|
-
*/ async isEmpty() {
|
|
58
|
-
const total = await this.count();
|
|
59
|
-
return total === 0;
|
|
44
|
+
async isEmpty() {
|
|
45
|
+
return await this.count() === 0;
|
|
60
46
|
}
|
|
61
|
-
|
|
62
|
-
* Get entity name for logging
|
|
63
|
-
*/ getEntityName() {
|
|
47
|
+
getEntityName() {
|
|
64
48
|
return this.metadata.tableName;
|
|
65
49
|
}
|
|
66
|
-
|
|
67
|
-
* Wrap operation in transaction
|
|
68
|
-
* @param operation Function to execute in transaction
|
|
69
|
-
*/ async withTransaction(operation) {
|
|
50
|
+
async withTransaction(operation) {
|
|
70
51
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
71
52
|
await queryRunner.connect();
|
|
72
53
|
await queryRunner.startTransaction();
|
|
@@ -240,8 +240,10 @@ let DataGenerator = class DataGenerator {
|
|
|
240
240
|
return selected.map((e)=>e.id);
|
|
241
241
|
}
|
|
242
242
|
constructor(locale = 'en'){
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
_faker.faker.setDefaultRefDate(new Date());
|
|
244
|
+
if (locale !== 'en') {
|
|
245
|
+
// @ts-expect-error - faker locale property access
|
|
246
|
+
_faker.faker.locale = locale;
|
|
247
|
+
}
|
|
246
248
|
}
|
|
247
249
|
};
|
|
@@ -53,23 +53,6 @@ let EntityReader = class EntityReader {
|
|
|
53
53
|
return this.topologicalSort(graph);
|
|
54
54
|
}
|
|
55
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
56
|
* Find entity metadata by name or table name
|
|
74
57
|
*/ findEntityMetadata(entityName) {
|
|
75
58
|
return this.getAllEntities().find((e)=>e.name === entityName || e.tableName === entityName);
|
package/cjs/seeders/index.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Seed System Exports
|
|
3
|
-
*
|
|
4
|
-
* Public API for seed data generation system.
|
|
5
|
-
*/ "use strict";
|
|
1
|
+
"use strict";
|
|
6
2
|
Object.defineProperty(exports, "__esModule", {
|
|
7
3
|
value: true
|
|
8
4
|
});
|
|
@@ -49,6 +45,9 @@ _export(exports, {
|
|
|
49
45
|
get TemplateGenerator () {
|
|
50
46
|
return _templategenerator.TemplateGenerator;
|
|
51
47
|
},
|
|
48
|
+
get configureSeedConfig () {
|
|
49
|
+
return _seedconfig.configureSeedConfig;
|
|
50
|
+
},
|
|
52
51
|
get defaultLogger () {
|
|
53
52
|
return _seedrunner.defaultLogger;
|
|
54
53
|
},
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Seed Configuration
|
|
3
|
-
*
|
|
4
|
-
* Configuration for seed data generation.
|
|
5
|
-
* Customize record counts, seeding order, and behavior.
|
|
6
|
-
*/ "use strict";
|
|
1
|
+
"use strict";
|
|
7
2
|
Object.defineProperty(exports, "__esModule", {
|
|
8
3
|
value: true
|
|
9
4
|
});
|
|
@@ -14,6 +9,9 @@ function _export(target, all) {
|
|
|
14
9
|
});
|
|
15
10
|
}
|
|
16
11
|
_export(exports, {
|
|
12
|
+
get configureSeedConfig () {
|
|
13
|
+
return configureSeedConfig;
|
|
14
|
+
},
|
|
17
15
|
get getEntityCount () {
|
|
18
16
|
return getEntityCount;
|
|
19
17
|
},
|
|
@@ -28,55 +26,20 @@ _export(exports, {
|
|
|
28
26
|
}
|
|
29
27
|
});
|
|
30
28
|
const seedConfig = {
|
|
31
|
-
|
|
32
|
-
|
|
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)
|
|
29
|
+
counts: {},
|
|
30
|
+
order: [],
|
|
69
31
|
skipEntities: [
|
|
70
32
|
'migrations',
|
|
71
33
|
'typeorm_metadata',
|
|
72
34
|
'Migration',
|
|
73
35
|
'Typeorm_Metadata'
|
|
74
36
|
],
|
|
75
|
-
// Faker locale (en, ar, es, fr, de, etc.)
|
|
76
37
|
locale: 'en',
|
|
77
|
-
// Respect soft delete when clearing data
|
|
78
38
|
respectSoftDelete: true
|
|
79
39
|
};
|
|
40
|
+
function configureSeedConfig(config) {
|
|
41
|
+
Object.assign(seedConfig, config);
|
|
42
|
+
}
|
|
80
43
|
function getEntityCount(entityName, config = seedConfig) {
|
|
81
44
|
return config.counts[entityName] || 10;
|
|
82
45
|
}
|
|
@@ -85,13 +48,11 @@ function shouldSkipEntity(entityName, config = seedConfig) {
|
|
|
85
48
|
}
|
|
86
49
|
function getSeedingOrder(availableEntities, config = seedConfig) {
|
|
87
50
|
const ordered = [];
|
|
88
|
-
// Add entities in configured order
|
|
89
51
|
for (const entityName of config.order){
|
|
90
52
|
if (availableEntities.includes(entityName) && !shouldSkipEntity(entityName, config)) {
|
|
91
53
|
ordered.push(entityName);
|
|
92
54
|
}
|
|
93
55
|
}
|
|
94
|
-
// Add remaining entities not in order configuration
|
|
95
56
|
for (const entityName of availableEntities){
|
|
96
57
|
if (!ordered.includes(entityName) && !shouldSkipEntity(entityName, config)) {
|
|
97
58
|
ordered.push(entityName);
|
|
@@ -12,9 +12,6 @@ _export(exports, {
|
|
|
12
12
|
get buildDataSourceOptions () {
|
|
13
13
|
return buildDataSourceOptions;
|
|
14
14
|
},
|
|
15
|
-
get buildTenantDatabaseConfig () {
|
|
16
|
-
return buildTenantDatabaseConfig;
|
|
17
|
-
},
|
|
18
15
|
get getActiveTenants () {
|
|
19
16
|
return getActiveTenants;
|
|
20
17
|
},
|
|
@@ -70,7 +67,8 @@ function getDatabaseForTenant(config, tenantId) {
|
|
|
70
67
|
const defaultTenantConfig = {
|
|
71
68
|
id: 'default',
|
|
72
69
|
database: config.defaultDatabaseConfig.database || 'default',
|
|
73
|
-
enableCompanyFeature: config.bootstrapAppConfig?.enableCompanyFeature ?? false
|
|
70
|
+
enableCompanyFeature: config.bootstrapAppConfig?.enableCompanyFeature ?? false,
|
|
71
|
+
permissionMode: config.bootstrapAppConfig?.permissionMode ?? 'FULL'
|
|
74
72
|
};
|
|
75
73
|
return {
|
|
76
74
|
database: defaultTenantConfig.database,
|
|
@@ -83,16 +81,6 @@ function resolveEntities(config, tenantConfig) {
|
|
|
83
81
|
}
|
|
84
82
|
return config.entities;
|
|
85
83
|
}
|
|
86
|
-
function buildTenantDatabaseConfig(baseConfig, tenant) {
|
|
87
|
-
return {
|
|
88
|
-
type: baseConfig.type,
|
|
89
|
-
host: tenant.host ?? baseConfig.host,
|
|
90
|
-
port: tenant.port ?? baseConfig.port,
|
|
91
|
-
username: tenant.username ?? baseConfig.username,
|
|
92
|
-
password: tenant.password ?? baseConfig.password,
|
|
93
|
-
database: tenant.database
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
84
|
function getActiveTenants(config) {
|
|
97
85
|
return config.tenants || [];
|
|
98
86
|
}
|
|
@@ -34,7 +34,7 @@ let EnvConfigService = class EnvConfigService {
|
|
|
34
34
|
return this.getValue(key, throwOnMissing).toLowerCase() === 'true';
|
|
35
35
|
}
|
|
36
36
|
getPort() {
|
|
37
|
-
return this.getNumber('PORT', false);
|
|
37
|
+
return this.getNumber('PORT', false) || 3000;
|
|
38
38
|
}
|
|
39
39
|
isProduction() {
|
|
40
40
|
return this.getValue('MODE', false).toUpperCase() !== 'DEV';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
*/ export { };
|
|
2
|
+
* Entity Contract Interfaces
|
|
3
|
+
*
|
|
4
|
+
* These interfaces define the standard fields that entities should have.
|
|
5
|
+
* Use them as contracts when implementing new entities.
|
|
6
|
+
*/ /** Base entity with UUID, timestamps, and soft delete */ /** Entity with company relation (for multi-company support) */ export { };
|
package/fesm/migration/cli.js
CHANGED
|
@@ -1,24 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
* Migration CLI Entry Point
|
|
4
|
-
*
|
|
5
|
-
* Main executable for running TypeORM migrations via npm scripts.
|
|
6
|
-
*
|
|
7
|
-
* Usage (via package.json scripts):
|
|
8
|
-
* - npm run migration generate --name=CreateUsers
|
|
9
|
-
* - npm run migration run
|
|
10
|
-
* - npm run migration revert
|
|
11
|
-
* - npm run migration status
|
|
12
|
-
* - npm run migration run:all (multi-tenant)
|
|
13
|
-
* - npm run migration revert:all (multi-tenant)
|
|
14
|
-
* - npm run migration status:all (multi-tenant)
|
|
15
|
-
*
|
|
16
|
-
* Environment Variables:
|
|
17
|
-
* - TENANT_ID: Target specific tenant (optional)
|
|
18
|
-
*
|
|
19
|
-
* This file is the executable entry point referenced in package.json.
|
|
20
|
-
* The actual CLI logic is in migration.cli.ts (runMigrationCli function).
|
|
21
|
-
*/ import { runMigrationCli } from './migration.cli';
|
|
2
|
+
import { runMigrationCli } from './migration.cli';
|
|
22
3
|
runMigrationCli().catch((err)=>{
|
|
23
4
|
console.error('CLI Error:', err.message);
|
|
24
5
|
process.exit(1);
|
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import { DataSource } from 'typeorm';
|
|
2
2
|
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
|
|
3
3
|
import { buildDataSourceOptions, getDatabaseForTenant, getMigrationsGlobPattern, resolveEntities } from '../utils/datasource-config.builder';
|
|
4
|
-
|
|
5
|
-
* Create a DataSource for migrations
|
|
6
|
-
*
|
|
7
|
-
* @param options - Configuration options
|
|
8
|
-
* @param options.config - Migration configuration
|
|
9
|
-
* @param options.tenantId - Optional tenant ID for multi-tenant mode
|
|
10
|
-
* @returns DataSource instance (not initialized)
|
|
11
|
-
*/ export function createMigrationDataSource(options) {
|
|
4
|
+
export function createMigrationDataSource(options) {
|
|
12
5
|
const { config, tenantId } = options;
|
|
13
6
|
const { database, tenant, tenantConfig } = getDatabaseForTenant(config, tenantId);
|
|
14
|
-
// Log target info
|
|
15
7
|
console.log(`\n📦 Database: ${database}${tenant ? ` [${tenant.name}]` : ' [default]'}`);
|
|
16
8
|
console.log(`📁 Migrations: migrations/${tenantId || 'default'}/`);
|
|
17
|
-
if (config.bootstrapAppConfig?.databaseMode
|
|
9
|
+
if (config.bootstrapAppConfig?.databaseMode === 'multi-tenant' && !tenantId && config.tenants && config.tenants.length > 0) {
|
|
18
10
|
console.log(`\n💡 Multi-tenant mode. Available tenants:`);
|
|
19
11
|
config.tenants.forEach((t)=>console.log(` - TENANT_ID=${t.id} → ${t.database}`));
|
|
20
12
|
}
|
|
@@ -29,7 +21,6 @@ import { buildDataSourceOptions, getDatabaseForTenant, getMigrationsGlobPattern,
|
|
|
29
21
|
...config.defaultDatabaseConfig,
|
|
30
22
|
database
|
|
31
23
|
};
|
|
32
|
-
// Resolve entities based on tenant config
|
|
33
24
|
const entities = resolveEntities(config, tenantConfig);
|
|
34
25
|
const dsOptions = buildDataSourceOptions(dbConfig, entities, getMigrationsGlobPattern(config.migrationsPath, tenantId), config.migrationsTableName);
|
|
35
26
|
return new DataSource({
|
|
@@ -37,15 +28,7 @@ import { buildDataSourceOptions, getDatabaseForTenant, getMigrationsGlobPattern,
|
|
|
37
28
|
namingStrategy: new SnakeNamingStrategy()
|
|
38
29
|
});
|
|
39
30
|
}
|
|
40
|
-
|
|
41
|
-
* Initialize and return a DataSource
|
|
42
|
-
*
|
|
43
|
-
* Creates a DataSource and initializes the connection.
|
|
44
|
-
* The connection should be destroyed after use with `ds.destroy()`.
|
|
45
|
-
*
|
|
46
|
-
* @param options - Configuration options
|
|
47
|
-
* @returns Initialized DataSource instance
|
|
48
|
-
*/ export async function initializeDataSource(options) {
|
|
31
|
+
export async function initializeDataSource(options) {
|
|
49
32
|
const ds = createMigrationDataSource(options);
|
|
50
33
|
await ds.initialize();
|
|
51
34
|
return ds;
|
package/fesm/migration/index.js
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Migration Module Exports
|
|
3
|
-
*
|
|
4
|
-
* Public API for TypeORM migration management in single and multi-tenant databases.
|
|
5
|
-
*
|
|
6
|
-
* Main exports:
|
|
7
|
-
* - createMigrationDataSource: Create DataSource for migrations
|
|
8
|
-
* - initializeDataSource: Create and initialize DataSource
|
|
9
|
-
* - runMigrationCli: Programmatic CLI execution
|
|
10
|
-
*
|
|
11
|
-
* The CLI entry point is at ./cli.ts (used by npm scripts)
|
|
12
|
-
*/ // DataSource factory for migration config files
|
|
13
1
|
export { createMigrationDataSource, initializeDataSource } from './datasource.factory';
|
|
14
|
-
// CLI service for programmatic usage
|
|
15
2
|
export { runMigrationCli } from './migration.cli';
|
|
16
|
-
// Migration runner functions (optional, for advanced usage)
|
|
17
3
|
export { generateMigration, runMigrations, revertMigration, migrationStatus, runForAllTenants, ensureMigrationsFolder } from './migration.runner';
|
|
@@ -2,23 +2,7 @@ import * as path from 'path';
|
|
|
2
2
|
import { envConfig } from '../config';
|
|
3
3
|
import { getDatabaseForTenant, getMigrationsFolderPath } from '../utils';
|
|
4
4
|
import { generateMigration, migrationStatus, revertMigration, runForAllTenants, runMigrations } from './migration.runner';
|
|
5
|
-
|
|
6
|
-
* Migration CLI Service
|
|
7
|
-
*
|
|
8
|
-
* Handles migration command execution for single and multi-tenant databases.
|
|
9
|
-
*
|
|
10
|
-
* Commands:
|
|
11
|
-
* - generate: Create new migration
|
|
12
|
-
* - run: Execute pending migrations
|
|
13
|
-
* - revert: Rollback last migration
|
|
14
|
-
* - status: Check migration status
|
|
15
|
-
* - run:all/revert:all/status:all: Batch operations for all tenants
|
|
16
|
-
*
|
|
17
|
-
* Configuration:
|
|
18
|
-
* - TENANT_ID: Target tenant (optional, defaults to default database)
|
|
19
|
-
* - --config=<path>: Migration config file path
|
|
20
|
-
* - --datasource=<path>: DataSource file path for TypeORM CLI
|
|
21
|
-
*/ function showHelp() {
|
|
5
|
+
function showHelp() {
|
|
22
6
|
console.log(`
|
|
23
7
|
Migration CLI
|
|
24
8
|
|
|
@@ -4,26 +4,7 @@ import * as path from 'path';
|
|
|
4
4
|
import { envConfig } from '../config';
|
|
5
5
|
import { getActiveTenants, getDatabaseForTenant, getMigrationsFolderPath } from '../utils';
|
|
6
6
|
import { initializeDataSource } from './datasource.factory';
|
|
7
|
-
|
|
8
|
-
* Migration Runner Functions
|
|
9
|
-
*
|
|
10
|
-
* Core functions for executing migration operations.
|
|
11
|
-
* Supports both single and multi-tenant database configurations.
|
|
12
|
-
*
|
|
13
|
-
* Functions:
|
|
14
|
-
* - ensureMigrationsFolder: Create migrations directory
|
|
15
|
-
* - generateMigration: Create new migration file
|
|
16
|
-
* - runMigrations: Execute pending migrations
|
|
17
|
-
* - revertMigration: Rollback last migration
|
|
18
|
-
* - migrationStatus: Check migration status
|
|
19
|
-
* - runForAllTenants: Batch operations for all tenants
|
|
20
|
-
*/ /**
|
|
21
|
-
* Ensure migrations folder exists
|
|
22
|
-
*
|
|
23
|
-
* @param basePath - Base migrations path
|
|
24
|
-
* @param tenantId - Optional tenant ID
|
|
25
|
-
* @returns Full path to migrations folder
|
|
26
|
-
*/ export function ensureMigrationsFolder(basePath, tenantId) {
|
|
7
|
+
export function ensureMigrationsFolder(basePath, tenantId) {
|
|
27
8
|
const folder = getMigrationsFolderPath(basePath, tenantId);
|
|
28
9
|
if (!fs.existsSync(folder)) {
|
|
29
10
|
fs.mkdirSync(folder, {
|
|
@@ -33,16 +14,7 @@ import { initializeDataSource } from './datasource.factory';
|
|
|
33
14
|
}
|
|
34
15
|
return folder;
|
|
35
16
|
}
|
|
36
|
-
|
|
37
|
-
* Generate a new migration file
|
|
38
|
-
*
|
|
39
|
-
* Uses TypeORM CLI to generate migration based on entity changes.
|
|
40
|
-
*
|
|
41
|
-
* @param config - Migration configuration
|
|
42
|
-
* @param name - Migration name (e.g., "CreateUsers")
|
|
43
|
-
* @param tenantId - Optional tenant ID for multi-tenant mode
|
|
44
|
-
* @param datasourcePath - Optional path to datasource file
|
|
45
|
-
*/ export async function generateMigration(config, name, tenantId, datasourcePath) {
|
|
17
|
+
export async function generateMigration(config, name, tenantId, datasourcePath) {
|
|
46
18
|
const label = tenantId ? `[${tenantId}]` : '[default]';
|
|
47
19
|
const folder = ensureMigrationsFolder(config.migrationsPath, tenantId);
|
|
48
20
|
console.log(`${label} Generating migration: ${name}`);
|
|
@@ -72,15 +44,7 @@ function findDatasourcePath() {
|
|
|
72
44
|
}
|
|
73
45
|
throw new Error('Could not find datasource file. Specify with --datasource=<path>');
|
|
74
46
|
}
|
|
75
|
-
|
|
76
|
-
* Run pending migrations
|
|
77
|
-
*
|
|
78
|
-
* Executes all pending migrations for the specified tenant or default database.
|
|
79
|
-
*
|
|
80
|
-
* @param config - Migration configuration
|
|
81
|
-
* @param tenantId - Optional tenant ID for multi-tenant mode
|
|
82
|
-
* @returns Migration result with success status
|
|
83
|
-
*/ export async function runMigrations(config, tenantId) {
|
|
47
|
+
export async function runMigrations(config, tenantId) {
|
|
84
48
|
const { database } = getDatabaseForTenant(config, tenantId);
|
|
85
49
|
const label = tenantId ? `[${tenantId}]` : '[default]';
|
|
86
50
|
try {
|
|
@@ -113,15 +77,7 @@ function findDatasourcePath() {
|
|
|
113
77
|
};
|
|
114
78
|
}
|
|
115
79
|
}
|
|
116
|
-
|
|
117
|
-
* Revert last migration
|
|
118
|
-
*
|
|
119
|
-
* Rolls back the most recently executed migration.
|
|
120
|
-
*
|
|
121
|
-
* @param config - Migration configuration
|
|
122
|
-
* @param tenantId - Optional tenant ID for multi-tenant mode
|
|
123
|
-
* @returns Migration result with success status
|
|
124
|
-
*/ export async function revertMigration(config, tenantId) {
|
|
80
|
+
export async function revertMigration(config, tenantId) {
|
|
125
81
|
const { database } = getDatabaseForTenant(config, tenantId);
|
|
126
82
|
const label = tenantId ? `[${tenantId}]` : '[default]';
|
|
127
83
|
try {
|
|
@@ -148,15 +104,7 @@ function findDatasourcePath() {
|
|
|
148
104
|
};
|
|
149
105
|
}
|
|
150
106
|
}
|
|
151
|
-
|
|
152
|
-
* Check migration status
|
|
153
|
-
*
|
|
154
|
-
* Shows whether there are pending migrations to run.
|
|
155
|
-
*
|
|
156
|
-
* @param config - Migration configuration
|
|
157
|
-
* @param tenantId - Optional tenant ID for multi-tenant mode
|
|
158
|
-
* @returns Migration result with status information
|
|
159
|
-
*/ export async function migrationStatus(config, tenantId) {
|
|
107
|
+
export async function migrationStatus(config, tenantId) {
|
|
160
108
|
const { database } = getDatabaseForTenant(config, tenantId);
|
|
161
109
|
const label = tenantId ? `[${tenantId}]` : '[default]';
|
|
162
110
|
try {
|
|
@@ -186,16 +134,7 @@ function findDatasourcePath() {
|
|
|
186
134
|
};
|
|
187
135
|
}
|
|
188
136
|
}
|
|
189
|
-
|
|
190
|
-
* Run migration command for all tenants
|
|
191
|
-
*
|
|
192
|
-
* Executes the specified command for all active tenants in batch mode.
|
|
193
|
-
* Only works in multi-tenant database mode.
|
|
194
|
-
*
|
|
195
|
-
* @param config - Migration configuration
|
|
196
|
-
* @param command - Command to run: 'run', 'revert', or 'status'
|
|
197
|
-
* @returns Array of migration results for each tenant
|
|
198
|
-
*/ export async function runForAllTenants(config, command) {
|
|
137
|
+
export async function runForAllTenants(config, command) {
|
|
199
138
|
const tenants = getActiveTenants(config);
|
|
200
139
|
console.log(`\n🔄 Running '${command}' for ${tenants.length} tenant(s)...\n`);
|
|
201
140
|
const results = [];
|
|
@@ -11,51 +11,16 @@ function _define_property(obj, key, value) {
|
|
|
11
11
|
}
|
|
12
12
|
return obj;
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
* Abstract base class for all entity seeders.
|
|
18
|
-
* Provides common methods for data generation, clearing, and counting.
|
|
19
|
-
*
|
|
20
|
-
* Usage:
|
|
21
|
-
* ```typescript
|
|
22
|
-
* export class UserSeeder extends BaseSeeder<User> {
|
|
23
|
-
* constructor(dataSource: DataSource) {
|
|
24
|
-
* super(dataSource, User);
|
|
25
|
-
* }
|
|
26
|
-
*
|
|
27
|
-
* async generate(count: number): Promise<User[]> {
|
|
28
|
-
* const users: User[] = [];
|
|
29
|
-
* for (let i = 0; i < count; i++) {
|
|
30
|
-
* users.push(this.repository.create({
|
|
31
|
-
* name: faker.person.fullName(),
|
|
32
|
-
* email: faker.internet.email(),
|
|
33
|
-
* }));
|
|
34
|
-
* }
|
|
35
|
-
* return this.repository.save(users);
|
|
36
|
-
* }
|
|
37
|
-
* }
|
|
38
|
-
* ```
|
|
39
|
-
*/ export class BaseSeeder {
|
|
40
|
-
/**
|
|
41
|
-
* Clear all records from the entity table
|
|
42
|
-
* Respects soft delete if entity has deletedAt column
|
|
43
|
-
* @param hard If true, perform hard delete (ignore soft delete)
|
|
44
|
-
*/ async clear(hard = false) {
|
|
14
|
+
export class BaseSeeder {
|
|
15
|
+
async clear(hard = false) {
|
|
45
16
|
const hasSoftDelete = this.metadata.deleteDateColumn !== undefined;
|
|
46
17
|
if (hard || !hasSoftDelete) {
|
|
47
|
-
// Hard delete - remove all records
|
|
48
18
|
await this.repository.clear();
|
|
49
19
|
} else {
|
|
50
|
-
// Soft delete - set deletedAt
|
|
51
20
|
await this.repository.createQueryBuilder().softDelete().where('id IS NOT NULL').execute();
|
|
52
21
|
}
|
|
53
22
|
}
|
|
54
|
-
|
|
55
|
-
* Get count of existing records
|
|
56
|
-
* @param includeDeleted If true, count soft-deleted records
|
|
57
|
-
* @returns Total record count
|
|
58
|
-
*/ async count(includeDeleted = false) {
|
|
23
|
+
async count(includeDeleted = false) {
|
|
59
24
|
const hasSoftDelete = this.metadata.deleteDateColumn !== undefined;
|
|
60
25
|
if (hasSoftDelete && !includeDeleted) {
|
|
61
26
|
return this.repository.count({
|
|
@@ -66,22 +31,13 @@ function _define_property(obj, key, value) {
|
|
|
66
31
|
}
|
|
67
32
|
return this.repository.count();
|
|
68
33
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
* @returns True if no records exist
|
|
72
|
-
*/ async isEmpty() {
|
|
73
|
-
const total = await this.count();
|
|
74
|
-
return total === 0;
|
|
34
|
+
async isEmpty() {
|
|
35
|
+
return await this.count() === 0;
|
|
75
36
|
}
|
|
76
|
-
|
|
77
|
-
* Get entity name for logging
|
|
78
|
-
*/ getEntityName() {
|
|
37
|
+
getEntityName() {
|
|
79
38
|
return this.metadata.tableName;
|
|
80
39
|
}
|
|
81
|
-
|
|
82
|
-
* Wrap operation in transaction
|
|
83
|
-
* @param operation Function to execute in transaction
|
|
84
|
-
*/ async withTransaction(operation) {
|
|
40
|
+
async withTransaction(operation) {
|
|
85
41
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
86
42
|
await queryRunner.connect();
|
|
87
43
|
await queryRunner.startTransaction();
|
|
@@ -242,8 +242,10 @@ import { faker } from '@faker-js/faker';
|
|
|
242
242
|
return selected.map((e)=>e.id);
|
|
243
243
|
}
|
|
244
244
|
constructor(locale = 'en'){
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
245
|
+
faker.setDefaultRefDate(new Date());
|
|
246
|
+
if (locale !== 'en') {
|
|
247
|
+
// @ts-expect-error - faker locale property access
|
|
248
|
+
faker.locale = locale;
|
|
249
|
+
}
|
|
248
250
|
}
|
|
249
251
|
}
|
|
@@ -55,23 +55,6 @@ function _define_property(obj, key, value) {
|
|
|
55
55
|
return this.topologicalSort(graph);
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
* Check if entity has required columns populated
|
|
59
|
-
* Used to determine if entity is ready for seeding
|
|
60
|
-
*/ hasRequiredDependencies(entityName, dataSource) {
|
|
61
|
-
const info = this.getEntityInfo(entityName);
|
|
62
|
-
for (const relation of info.relations){
|
|
63
|
-
if (!relation.isNullable && relation.type === 'many-to-one') {
|
|
64
|
-
const targetRepo = dataSource.getRepository(relation.targetEntity);
|
|
65
|
-
if (targetRepo) {
|
|
66
|
-
// Check if target entity has data
|
|
67
|
-
// Note: This is a simplified check, actual implementation may need async
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
58
|
* Find entity metadata by name or table name
|
|
76
59
|
*/ findEntityMetadata(entityName) {
|
|
77
60
|
return this.getAllEntities().find((e)=>e.name === entityName || e.tableName === entityName);
|
package/fesm/seeders/index.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
* Seed System Exports
|
|
3
|
-
*
|
|
4
|
-
* Public API for seed data generation system.
|
|
5
|
-
*/ export { BaseSeeder } from './base-seeder';
|
|
1
|
+
export { BaseSeeder } from './base-seeder';
|
|
6
2
|
export { EntityReader, IEntityInfo, IColumnInfo, IRelationInfo } from './entity-reader';
|
|
7
3
|
export { DataGenerator } from './data-generator';
|
|
8
4
|
export { TemplateGenerator } from './template-generator';
|
|
9
5
|
export { SeedRunner, ISeedResult, ISeedOptions, ISeederLogger, defaultLogger } from './seed-runner';
|
|
10
|
-
export { seedConfig, ISeedConfig, getEntityCount, shouldSkipEntity, getSeedingOrder } from './seed-config';
|
|
6
|
+
export { seedConfig, ISeedConfig, getEntityCount, shouldSkipEntity, getSeedingOrder, configureSeedConfig } from './seed-config';
|
|
11
7
|
export { runSeedCli } from './cli';
|
|
@@ -1,81 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Configuration for seed data generation.
|
|
5
|
-
* Customize record counts, seeding order, and behavior.
|
|
6
|
-
*/ /**
|
|
7
|
-
* Default seed configuration
|
|
8
|
-
*/ export const seedConfig = {
|
|
9
|
-
// Default record counts per entity
|
|
10
|
-
counts: {
|
|
11
|
-
// Auth entities
|
|
12
|
-
Company: 10,
|
|
13
|
-
CompanyBranch: 25,
|
|
14
|
-
User: 50,
|
|
15
|
-
// IAM entities
|
|
16
|
-
Role: 8,
|
|
17
|
-
Permission: 45,
|
|
18
|
-
Action: 30,
|
|
19
|
-
Menu: 20,
|
|
20
|
-
UserIAMPermission: 50,
|
|
21
|
-
UserCompanyPermission: 100,
|
|
22
|
-
// Storage entities
|
|
23
|
-
StorageConfig: 5,
|
|
24
|
-
Folder: 20,
|
|
25
|
-
FileManager: 100
|
|
26
|
-
},
|
|
27
|
-
// Entity seeding order (respects FK constraints)
|
|
28
|
-
// Parent entities must come before child entities
|
|
29
|
-
order: [
|
|
30
|
-
// Auth - foundational
|
|
31
|
-
'Company',
|
|
32
|
-
'CompanyBranch',
|
|
33
|
-
'User',
|
|
34
|
-
// IAM - permissions system
|
|
35
|
-
'Action',
|
|
36
|
-
'Role',
|
|
37
|
-
'Permission',
|
|
38
|
-
'Menu',
|
|
39
|
-
'UserIAMPermission',
|
|
40
|
-
'UserCompanyPermission',
|
|
41
|
-
// Storage - file system
|
|
42
|
-
'StorageConfig',
|
|
43
|
-
'Folder',
|
|
44
|
-
'FileManager'
|
|
45
|
-
],
|
|
46
|
-
// Skip these entities (system tables, migrations)
|
|
1
|
+
export const seedConfig = {
|
|
2
|
+
counts: {},
|
|
3
|
+
order: [],
|
|
47
4
|
skipEntities: [
|
|
48
5
|
'migrations',
|
|
49
6
|
'typeorm_metadata',
|
|
50
7
|
'Migration',
|
|
51
8
|
'Typeorm_Metadata'
|
|
52
9
|
],
|
|
53
|
-
// Faker locale (en, ar, es, fr, de, etc.)
|
|
54
10
|
locale: 'en',
|
|
55
|
-
// Respect soft delete when clearing data
|
|
56
11
|
respectSoftDelete: true
|
|
57
12
|
};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
13
|
+
export function configureSeedConfig(config) {
|
|
14
|
+
Object.assign(seedConfig, config);
|
|
15
|
+
}
|
|
16
|
+
export function getEntityCount(entityName, config = seedConfig) {
|
|
61
17
|
return config.counts[entityName] || 10;
|
|
62
18
|
}
|
|
63
|
-
|
|
64
|
-
* Check if entity should be skipped
|
|
65
|
-
*/ export function shouldSkipEntity(entityName, config = seedConfig) {
|
|
19
|
+
export function shouldSkipEntity(entityName, config = seedConfig) {
|
|
66
20
|
return config.skipEntities.some((skip)=>skip.toLowerCase() === entityName.toLowerCase());
|
|
67
21
|
}
|
|
68
|
-
|
|
69
|
-
* Get seeding order for entities
|
|
70
|
-
*/ export function getSeedingOrder(availableEntities, config = seedConfig) {
|
|
22
|
+
export function getSeedingOrder(availableEntities, config = seedConfig) {
|
|
71
23
|
const ordered = [];
|
|
72
|
-
// Add entities in configured order
|
|
73
24
|
for (const entityName of config.order){
|
|
74
25
|
if (availableEntities.includes(entityName) && !shouldSkipEntity(entityName, config)) {
|
|
75
26
|
ordered.push(entityName);
|
|
76
27
|
}
|
|
77
28
|
}
|
|
78
|
-
// Add remaining entities not in order configuration
|
|
79
29
|
for (const entityName of availableEntities){
|
|
80
30
|
if (!ordered.includes(entityName) && !shouldSkipEntity(entityName, config)) {
|
|
81
31
|
ordered.push(entityName);
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
const defaultTenantConfig = {
|
|
46
46
|
id: 'default',
|
|
47
47
|
database: config.defaultDatabaseConfig.database || 'default',
|
|
48
|
-
enableCompanyFeature: config.bootstrapAppConfig?.enableCompanyFeature ?? false
|
|
48
|
+
enableCompanyFeature: config.bootstrapAppConfig?.enableCompanyFeature ?? false,
|
|
49
|
+
permissionMode: config.bootstrapAppConfig?.permissionMode ?? 'FULL'
|
|
49
50
|
};
|
|
50
51
|
return {
|
|
51
52
|
database: defaultTenantConfig.database,
|
|
@@ -60,18 +61,6 @@
|
|
|
60
61
|
}
|
|
61
62
|
return config.entities;
|
|
62
63
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Build full database config for a tenant
|
|
65
|
-
*/ export function buildTenantDatabaseConfig(baseConfig, tenant) {
|
|
66
|
-
return {
|
|
67
|
-
type: baseConfig.type,
|
|
68
|
-
host: tenant.host ?? baseConfig.host,
|
|
69
|
-
port: tenant.port ?? baseConfig.port,
|
|
70
|
-
username: tenant.username ?? baseConfig.username,
|
|
71
|
-
password: tenant.password ?? baseConfig.password,
|
|
72
|
-
database: tenant.database
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
64
|
/**
|
|
76
65
|
* Get active tenants from config
|
|
77
66
|
*/ export function getActiveTenants(config) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flusys/nestjs-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0-rc",
|
|
4
4
|
"description": "Core types, interfaces, and constants for Flusys NestJS packages",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "fesm/index.js",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@faker-js/faker": "^
|
|
64
|
+
"@faker-js/faker": "^10.0.0",
|
|
65
65
|
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
|
66
66
|
"@nestjs/config": "^3.0.0 || ^4.0.0",
|
|
67
67
|
"@nestjs/core": "^10.0.0 || ^11.0.0",
|
|
@@ -34,7 +34,6 @@ export declare class EntityReader {
|
|
|
34
34
|
getAllEntities(): EntityMetadata[];
|
|
35
35
|
getEntityInfo(entityName: string): IEntityInfo;
|
|
36
36
|
getSeedingOrder(skipEntities?: string[]): string[];
|
|
37
|
-
hasRequiredDependencies(entityName: string, dataSource: DataSource): boolean;
|
|
38
37
|
private findEntityMetadata;
|
|
39
38
|
private extractColumns;
|
|
40
39
|
private extractRelations;
|
package/seeders/index.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ export { BaseSeeder } from './base-seeder';
|
|
|
2
2
|
export { EntityReader, IEntityInfo, IColumnInfo, IRelationInfo } from './entity-reader';
|
|
3
3
|
export { DataGenerator } from './data-generator';
|
|
4
4
|
export { TemplateGenerator } from './template-generator';
|
|
5
|
-
export { SeedRunner, ISeedResult, ISeedOptions, ISeederLogger, defaultLogger
|
|
6
|
-
export { seedConfig, ISeedConfig, getEntityCount, shouldSkipEntity, getSeedingOrder } from './seed-config';
|
|
5
|
+
export { SeedRunner, ISeedResult, ISeedOptions, ISeederLogger, defaultLogger } from './seed-runner';
|
|
6
|
+
export { seedConfig, ISeedConfig, getEntityCount, shouldSkipEntity, getSeedingOrder, configureSeedConfig } from './seed-config';
|
|
7
7
|
export { runSeedCli } from './cli';
|
package/seeders/seed-config.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface ISeedConfig {
|
|
|
6
6
|
respectSoftDelete: boolean;
|
|
7
7
|
}
|
|
8
8
|
export declare const seedConfig: ISeedConfig;
|
|
9
|
+
export declare function configureSeedConfig(config: Partial<ISeedConfig>): void;
|
|
9
10
|
export declare function getEntityCount(entityName: string, config?: ISeedConfig): number;
|
|
10
11
|
export declare function shouldSkipEntity(entityName: string, config?: ISeedConfig): boolean;
|
|
11
12
|
export declare function getSeedingOrder(availableEntities: string[], config?: ISeedConfig): string[];
|
|
@@ -9,5 +9,4 @@ export declare function getDatabaseForTenant(config: IMigrationConfig, tenantId?
|
|
|
9
9
|
tenantConfig: ITenantDatabaseConfig;
|
|
10
10
|
};
|
|
11
11
|
export declare function resolveEntities(config: IMigrationConfig, tenantConfig?: ITenantDatabaseConfig): any[];
|
|
12
|
-
export declare function buildTenantDatabaseConfig(baseConfig: IDatabaseConfig, tenant: ITenantDatabaseConfig): IDatabaseConfig;
|
|
13
12
|
export declare function getActiveTenants(config: IMigrationConfig): ITenantDatabaseConfig[];
|