@flusys/nestjs-core 1.0.0-beta → 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.
Files changed (34) hide show
  1. package/README.md +45 -8
  2. package/cjs/config/env-config.service.js +1 -1
  3. package/cjs/interfaces/base-entity.interface.js +5 -3
  4. package/cjs/interfaces/database.interface.js +1 -3
  5. package/cjs/migration/datasource.factory.js +1 -3
  6. package/cjs/migration/index.js +0 -12
  7. package/cjs/migration/migration.cli.js +1 -17
  8. package/cjs/seeders/base-seeder.js +6 -25
  9. package/cjs/seeders/data-generator.js +5 -3
  10. package/cjs/seeders/entity-reader.js +0 -17
  11. package/cjs/seeders/index.js +4 -5
  12. package/cjs/seeders/seed-config.js +9 -48
  13. package/cjs/utils/datasource-config.builder.js +2 -14
  14. package/fesm/config/env-config.service.js +1 -1
  15. package/fesm/interfaces/app-config.interfaces.js +1 -3
  16. package/fesm/interfaces/base-entity.interface.js +5 -5
  17. package/fesm/interfaces/database.interface.js +1 -5
  18. package/fesm/migration/cli.js +1 -20
  19. package/fesm/migration/datasource.factory.js +3 -20
  20. package/fesm/migration/index.js +0 -14
  21. package/fesm/migration/migration.cli.js +1 -17
  22. package/fesm/migration/migration.runner.js +6 -67
  23. package/fesm/seeders/base-seeder.js +7 -51
  24. package/fesm/seeders/data-generator.js +5 -3
  25. package/fesm/seeders/entity-reader.js +0 -17
  26. package/fesm/seeders/index.js +2 -6
  27. package/fesm/seeders/seed-config.js +9 -59
  28. package/fesm/utils/datasource-config.builder.js +2 -13
  29. package/interfaces/base-entity.interface.d.ts +3 -0
  30. package/package.json +2 -2
  31. package/seeders/entity-reader.d.ts +0 -1
  32. package/seeders/index.d.ts +2 -2
  33. package/seeders/seed-config.d.ts +1 -0
  34. 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:** Pure TypeScript utilities (Zero NestJS dependencies)
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
- resolveEntities, getActiveTenants,
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
- **No NestJS dependencies** - Can be used in any TypeScript project.
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
- * Base Entity Interface
3
- * All entities should implement this for consistency
4
- */ "use strict";
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
  });
@@ -1,6 +1,4 @@
1
- /**
2
- * Supported database types
3
- */ "use strict";
1
+ "use strict";
4
2
  Object.defineProperty(exports, "__esModule", {
5
3
  value: true
6
4
  });
@@ -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 == 'multi-tenant' && !tenantId && config.tenants && config.tenants.length > 0) {
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({
@@ -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
- * Check if entity has any records
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
- // Note: faker v8+ uses faker.locale instead of setLocale
244
- // For v8+: faker.locale = locale;
245
- // Locale is set globally, not needed for basic usage
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);
@@ -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
- // 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)
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,3 +1 @@
1
- /**
2
- * Async module options - common pattern for NestJS async module registration
3
- */ export { };
1
+ /** Async module options pattern */ export { };
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Base Entity Interface
3
- * All entities should implement this for consistency
4
- */ /**
5
- * Entity with metadata
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 { };
@@ -1,5 +1 @@
1
- /**
2
- * Supported database types
3
- */ /**
4
- * DataSource build options for TypeORM
5
- */ export { };
1
+ export { };
@@ -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 == 'multi-tenant' && !tenantId && config.tenants && config.tenants.length > 0) {
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;
@@ -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
- * Base Seeder Class
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
- * Check if entity has any records
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
- // Note: faker v8+ uses faker.locale instead of setLocale
246
- // For v8+: faker.locale = locale;
247
- // Locale is set globally, not needed for basic usage
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);
@@ -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
- * Seed Configuration
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
- * Get count for entity (with default fallback)
60
- */ export function getEntityCount(entityName, config = seedConfig) {
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) {
@@ -17,3 +17,6 @@ export interface IOrderable {
17
17
  export interface IMetadata {
18
18
  metadata?: Record<string, any> | null;
19
19
  }
20
+ export interface ICompanyOwned {
21
+ companyId?: string | null;
22
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/nestjs-core",
3
- "version": "1.0.0-beta",
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": "^9.0.0",
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;
@@ -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, } from './seed-runner';
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';
@@ -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[];