@flusys/nestjs-core 7.1.0 → 8.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +2 -2
  2. package/config/index.d.ts +1 -1
  3. package/constants/index.d.ts +1 -1
  4. package/docs/docs.config.d.ts +1 -1
  5. package/docs/index.d.ts +1 -1
  6. package/fesm/229.js +729 -0
  7. package/fesm/config/index.js +126 -1
  8. package/fesm/constants/index.js +29 -1
  9. package/fesm/docs/index.js +288 -1
  10. package/fesm/index.js +124 -5
  11. package/fesm/interfaces/index.js +18 -4
  12. package/fesm/migration/cli.js +19 -2
  13. package/fesm/migration/index.js +45 -3
  14. package/fesm/runtime.js +85 -0
  15. package/fesm/utils/index.js +131 -1
  16. package/index.d.ts +5 -5
  17. package/interfaces/app-config.interfaces.d.ts +1 -1
  18. package/interfaces/index.d.ts +4 -4
  19. package/interfaces/migration.interface.d.ts +2 -2
  20. package/migration/cli.d.ts +0 -1
  21. package/migration/datasource.factory.d.ts +1 -1
  22. package/migration/index.d.ts +3 -3
  23. package/migration/migration.cli.d.ts +1 -1
  24. package/migration/migration.runner.d.ts +1 -1
  25. package/package.json +24 -32
  26. package/utils/datasource-config.builder.d.ts +2 -2
  27. package/utils/index.d.ts +1 -1
  28. package/cjs/config/env-config.service.js +0 -162
  29. package/cjs/config/env-config.service.spec.js +0 -325
  30. package/cjs/config/index.js +0 -18
  31. package/cjs/constants/database.constants.js +0 -20
  32. package/cjs/constants/index.js +0 -18
  33. package/cjs/docs/docs.config.js +0 -276
  34. package/cjs/docs/docs.config.spec.js +0 -923
  35. package/cjs/docs/index.js +0 -18
  36. package/cjs/index.js +0 -23
  37. package/cjs/interfaces/app-config.interfaces.js +0 -4
  38. package/cjs/interfaces/base-entity.interface.js +0 -9
  39. package/cjs/interfaces/database.interface.js +0 -4
  40. package/cjs/interfaces/index.js +0 -21
  41. package/cjs/interfaces/migration.interface.js +0 -4
  42. package/cjs/migration/cli.js +0 -10
  43. package/cjs/migration/datasource.factory.js +0 -43
  44. package/cjs/migration/datasource.factory.spec.js +0 -113
  45. package/cjs/migration/index.js +0 -45
  46. package/cjs/migration/migration.cli.js +0 -231
  47. package/cjs/migration/migration.cli.spec.js +0 -293
  48. package/cjs/migration/migration.runner.js +0 -276
  49. package/cjs/migration/migration.runner.spec.js +0 -268
  50. package/cjs/utils/datasource-config.builder.js +0 -103
  51. package/cjs/utils/datasource-config.builder.spec.js +0 -232
  52. package/cjs/utils/index.js +0 -18
  53. package/fesm/config/env-config.service.js +0 -112
  54. package/fesm/config/env-config.service.spec.js +0 -321
  55. package/fesm/constants/database.constants.js +0 -2
  56. package/fesm/docs/docs.config.js +0 -258
  57. package/fesm/docs/docs.config.spec.js +0 -919
  58. package/fesm/interfaces/app-config.interfaces.js +0 -1
  59. package/fesm/interfaces/base-entity.interface.js +0 -6
  60. package/fesm/interfaces/database.interface.js +0 -1
  61. package/fesm/interfaces/migration.interface.js +0 -1
  62. package/fesm/migration/datasource.factory.js +0 -25
  63. package/fesm/migration/datasource.factory.spec.js +0 -109
  64. package/fesm/migration/migration.cli.js +0 -180
  65. package/fesm/migration/migration.cli.spec.js +0 -289
  66. package/fesm/migration/migration.runner.js +0 -202
  67. package/fesm/migration/migration.runner.spec.js +0 -264
  68. package/fesm/utils/datasource-config.builder.js +0 -84
  69. package/fesm/utils/datasource-config.builder.spec.js +0 -228
@@ -1,202 +0,0 @@
1
- import { execSync } from 'child_process';
2
- import * as fs from 'fs';
3
- import * as path from 'path';
4
- import { DataSource } from 'typeorm';
5
- import { envConfig } from '../config';
6
- import { getActiveTenants, getDatabaseForTenant, getMigrationsFolderPath, resolveDatabaseConnection } from '../utils';
7
- import { initializeDataSource } from './datasource.factory';
8
- function getLabel(tenantId) {
9
- return tenantId ? `[${tenantId}]` : '[default]';
10
- }
11
- async function withDataSource(config, tenantId, operation) {
12
- const { database } = getDatabaseForTenant(config, tenantId);
13
- const label = getLabel(tenantId);
14
- try {
15
- const ds = await initializeDataSource({
16
- config,
17
- tenantId
18
- });
19
- const partial = await operation(ds, label);
20
- await ds.destroy();
21
- return {
22
- tenantId,
23
- database,
24
- success: true,
25
- ...partial
26
- };
27
- } catch (error) {
28
- const errorMessage = error instanceof Error ? error.message : String(error);
29
- console.error(`${label} ✗ Failed: ${errorMessage}`);
30
- return {
31
- tenantId,
32
- database,
33
- success: false,
34
- error: errorMessage
35
- };
36
- }
37
- }
38
- export function ensureMigrationsFolder(basePath, tenantId) {
39
- const folder = getMigrationsFolderPath(basePath, tenantId);
40
- if (!fs.existsSync(folder)) {
41
- fs.mkdirSync(folder, {
42
- recursive: true
43
- });
44
- console.log(`📁 Created: ${folder}`);
45
- }
46
- return folder;
47
- }
48
- export async function generateMigration(config, name, tenantId, datasourcePath) {
49
- const label = tenantId ? `[${tenantId}]` : '[default]';
50
- const folder = ensureMigrationsFolder(config.migrationsPath, tenantId);
51
- console.log(`${label} Generating migration: ${name}`);
52
- console.log(`${label} Target folder: ${folder}`);
53
- const dsPath = datasourcePath || findDatasourcePath();
54
- const migrationFile = path.join(folder, name);
55
- const command = `ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli migration:generate "${migrationFile}" -d "${dsPath}"`;
56
- execSync(command, {
57
- stdio: 'inherit',
58
- env: {
59
- ...envConfig.getEnv(),
60
- TENANT_ID: tenantId || ''
61
- },
62
- cwd: process.cwd()
63
- });
64
- console.log(`${label} ✓ Generated!`);
65
- }
66
- function findDatasourcePath() {
67
- const possiblePaths = [
68
- 'src/persistence/migration.config.ts',
69
- 'projects/flusysnest/src/persistence/migration.config.ts'
70
- ];
71
- for (const p of possiblePaths){
72
- if (fs.existsSync(path.join(process.cwd(), p))) {
73
- return p;
74
- }
75
- }
76
- throw new Error('Could not find datasource file. Specify with --datasource=<path>');
77
- }
78
- export async function runMigrations(config, tenantId) {
79
- return withDataSource(config, tenantId, async (ds, label)=>{
80
- const migrations = await ds.runMigrations();
81
- if (migrations.length === 0) {
82
- console.log(`${label} No pending migrations.`);
83
- } else {
84
- console.log(`${label} ✓ Executed ${migrations.length} migration(s):`);
85
- migrations.forEach((m)=>console.log(` - ${m.name}`));
86
- }
87
- return {
88
- migrationsRun: migrations.map((m)=>m.name)
89
- };
90
- });
91
- }
92
- export async function revertMigration(config, tenantId) {
93
- return withDataSource(config, tenantId, async (ds, label)=>{
94
- await ds.undoLastMigration();
95
- console.log(`${label} ✓ Reverted!`);
96
- return {};
97
- });
98
- }
99
- function clearMigrationFiles(basePath, tenantId, label) {
100
- const folder = getMigrationsFolderPath(basePath, tenantId);
101
- if (!fs.existsSync(folder)) return;
102
- const files = fs.readdirSync(folder).filter((f)=>f.endsWith('.ts') || f.endsWith('.js'));
103
- files.forEach((file)=>fs.rmSync(path.join(folder, file)));
104
- console.log(`${label} ✓ Removed ${files.length} migration file(s) from ${folder}`);
105
- }
106
- async function dropAndRecreateDatabase(dbConfig, label) {
107
- const { type, database } = dbConfig;
108
- if (type === 'sqlite') {
109
- if (database && fs.existsSync(database)) fs.rmSync(database);
110
- console.log(`${label} ✓ Deleted sqlite file`);
111
- return;
112
- }
113
- if (type !== 'mysql' && type !== 'mariadb' && type !== 'postgres') {
114
- throw new Error(`Database reset is not supported for type '${type}'`);
115
- }
116
- const adminDs = new DataSource({
117
- type,
118
- host: dbConfig.host,
119
- port: dbConfig.port,
120
- username: dbConfig.username,
121
- password: dbConfig.password,
122
- database: type === 'postgres' ? 'postgres' : undefined
123
- });
124
- await adminDs.initialize();
125
- try {
126
- if (type === 'postgres') {
127
- await adminDs.query(`SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1 AND pid <> pg_backend_pid()`, [
128
- database
129
- ]);
130
- await adminDs.query(`DROP DATABASE IF EXISTS "${database}"`);
131
- await adminDs.query(`CREATE DATABASE "${database}"`);
132
- } else {
133
- await adminDs.query(`DROP DATABASE IF EXISTS \`${database}\``);
134
- await adminDs.query(`CREATE DATABASE \`${database}\` CHARACTER SET utf8mb4`);
135
- }
136
- console.log(`${label} ✓ Database '${database}' dropped and recreated`);
137
- } finally{
138
- await adminDs.destroy();
139
- }
140
- }
141
- export async function resetDatabase(config, tenantId) {
142
- const { database } = getDatabaseForTenant(config, tenantId);
143
- const label = getLabel(tenantId);
144
- try {
145
- clearMigrationFiles(config.migrationsPath, tenantId, label);
146
- const dbConfig = resolveDatabaseConnection(config, tenantId);
147
- await dropAndRecreateDatabase(dbConfig, label);
148
- return {
149
- tenantId,
150
- database,
151
- success: true
152
- };
153
- } catch (error) {
154
- const errorMessage = error instanceof Error ? error.message : String(error);
155
- console.error(`${label} ✗ Failed: ${errorMessage}`);
156
- return {
157
- tenantId,
158
- database,
159
- success: false,
160
- error: errorMessage
161
- };
162
- }
163
- }
164
- export async function migrationStatus(config, tenantId) {
165
- return withDataSource(config, tenantId, async (ds, label)=>{
166
- const hasPending = await ds.showMigrations();
167
- console.log(`${label} Pending: ${hasPending ? 'Yes' : 'No'}`);
168
- return {
169
- migrationsRun: [
170
- hasPending ? 'Has pending' : 'Up to date'
171
- ]
172
- };
173
- });
174
- }
175
- export async function runForAllTenants(config, command) {
176
- const tenants = getActiveTenants(config);
177
- console.log(`\n🔄 Running '${command}' for ${tenants.length} tenant(s)...\n`);
178
- const results = [];
179
- for (const tenant of tenants){
180
- let result;
181
- switch(command){
182
- case 'run':
183
- result = await runMigrations(config, tenant.id);
184
- break;
185
- case 'revert':
186
- result = await revertMigration(config, tenant.id);
187
- break;
188
- case 'status':
189
- result = await migrationStatus(config, tenant.id);
190
- break;
191
- }
192
- results.push(result);
193
- console.log('');
194
- }
195
- console.log('\n=== Summary ===');
196
- results.forEach((r)=>{
197
- const icon = r.success ? '✓' : '✗';
198
- const msg = r.error ? ` - ${r.error}` : '';
199
- console.log(`${icon} ${r.tenantId || 'default'}${msg}`);
200
- });
201
- return results;
202
- }
@@ -1,264 +0,0 @@
1
- const mockExistsSync = jest.fn();
2
- const mockMkdirSync = jest.fn();
3
- const mockReaddirSync = jest.fn();
4
- const mockRmSync = jest.fn();
5
- const mockExecSync = jest.fn();
6
- jest.mock('fs', ()=>({
7
- existsSync: (...args)=>mockExistsSync(...args),
8
- mkdirSync: (...args)=>mockMkdirSync(...args),
9
- readdirSync: (...args)=>mockReaddirSync(...args),
10
- rmSync: (...args)=>mockRmSync(...args)
11
- }));
12
- jest.mock('child_process', ()=>({
13
- execSync: (...args)=>mockExecSync(...args)
14
- }));
15
- jest.mock('../config', ()=>({
16
- envConfig: {
17
- getEnv: jest.fn().mockReturnValue({
18
- NODE_ENV: 'test'
19
- })
20
- }
21
- }));
22
- const mockRunMigrations = jest.fn();
23
- const mockUndoLastMigration = jest.fn();
24
- const mockShowMigrations = jest.fn();
25
- const mockDestroy = jest.fn();
26
- const mockDsInitialize = jest.fn();
27
- const mockAdminInitialize = jest.fn();
28
- const mockAdminQuery = jest.fn();
29
- const mockAdminDestroy = jest.fn();
30
- jest.mock('./datasource.factory', ()=>({
31
- initializeDataSource: jest.fn()
32
- }));
33
- jest.mock('typeorm', ()=>({
34
- DataSource: jest.fn().mockImplementation(()=>({
35
- initialize: mockAdminInitialize,
36
- query: mockAdminQuery,
37
- destroy: mockAdminDestroy
38
- }))
39
- }));
40
- import { ensureMigrationsFolder, generateMigration, runMigrations, revertMigration, resetDatabase, migrationStatus, runForAllTenants } from './migration.runner';
41
- import { initializeDataSource } from './datasource.factory';
42
- describe('migration.runner', ()=>{
43
- const baseConfig = {
44
- defaultDatabaseConfig: {
45
- type: 'postgres',
46
- database: 'flusys_default'
47
- },
48
- migrationsPath: 'migrations',
49
- entities: []
50
- };
51
- let logSpy;
52
- let errorSpy;
53
- beforeEach(()=>{
54
- jest.clearAllMocks();
55
- logSpy = jest.spyOn(console, 'log').mockImplementation(()=>undefined);
56
- errorSpy = jest.spyOn(console, 'error').mockImplementation(()=>undefined);
57
- initializeDataSource.mockResolvedValue({
58
- runMigrations: mockRunMigrations,
59
- undoLastMigration: mockUndoLastMigration,
60
- showMigrations: mockShowMigrations,
61
- destroy: mockDestroy,
62
- initialize: mockDsInitialize
63
- });
64
- });
65
- afterEach(()=>{
66
- logSpy.mockRestore();
67
- errorSpy.mockRestore();
68
- });
69
- describe('ensureMigrationsFolder', ()=>{
70
- it('should create the folder when it does not exist', ()=>{
71
- mockExistsSync.mockReturnValue(false);
72
- const folder = ensureMigrationsFolder('migrations', 'tenant-a');
73
- expect(folder).toBe('migrations/tenant-a');
74
- expect(mockMkdirSync).toHaveBeenCalledWith('migrations/tenant-a', {
75
- recursive: true
76
- });
77
- });
78
- it('should not create the folder when it already exists', ()=>{
79
- mockExistsSync.mockReturnValue(true);
80
- ensureMigrationsFolder('migrations');
81
- expect(mockMkdirSync).not.toHaveBeenCalled();
82
- });
83
- });
84
- describe('generateMigration', ()=>{
85
- it('should shell out to the typeorm CLI with the resolved datasource path', async ()=>{
86
- mockExistsSync.mockImplementation((p)=>p.includes('flusysnest'));
87
- await generateMigration(baseConfig, 'AddProductTable', 'tenant-a');
88
- expect(mockExecSync).toHaveBeenCalledTimes(1);
89
- const [command, options] = mockExecSync.mock.calls[0];
90
- expect(command).toContain('migration:generate');
91
- expect(command).toContain('migrations/tenant-a/AddProductTable');
92
- expect(command).toContain('projects/flusysnest/src/persistence/migration.config.ts');
93
- expect(options.env.TENANT_ID).toBe('tenant-a');
94
- });
95
- it('should use an explicit datasource path when provided', async ()=>{
96
- mockExistsSync.mockReturnValue(false);
97
- await generateMigration(baseConfig, 'AddProductTable', undefined, 'custom/datasource.ts');
98
- const [command] = mockExecSync.mock.calls[0];
99
- expect(command).toContain('-d "custom/datasource.ts"');
100
- });
101
- it('should throw when no datasource file can be found and none is specified', async ()=>{
102
- mockExistsSync.mockReturnValue(false);
103
- await expect(generateMigration(baseConfig, 'AddProductTable')).rejects.toThrow('Could not find datasource file. Specify with --datasource=<path>');
104
- });
105
- });
106
- describe('runMigrations', ()=>{
107
- it('should report success with the list of executed migrations', async ()=>{
108
- mockRunMigrations.mockResolvedValue([
109
- {
110
- name: 'M1'
111
- },
112
- {
113
- name: 'M2'
114
- }
115
- ]);
116
- const result = await runMigrations(baseConfig);
117
- expect(result).toEqual({
118
- tenantId: undefined,
119
- database: 'flusys_default',
120
- success: true,
121
- migrationsRun: [
122
- 'M1',
123
- 'M2'
124
- ]
125
- });
126
- expect(mockDestroy).toHaveBeenCalled();
127
- });
128
- it('should report no pending migrations when none run', async ()=>{
129
- mockRunMigrations.mockResolvedValue([]);
130
- const result = await runMigrations(baseConfig);
131
- expect(result.migrationsRun).toEqual([]);
132
- expect(result.success).toBe(true);
133
- });
134
- it('should capture errors and mark the result as failed', async ()=>{
135
- initializeDataSource.mockRejectedValue(new Error('connection refused'));
136
- const result = await runMigrations(baseConfig, 'tenant-a');
137
- expect(result).toEqual({
138
- tenantId: 'tenant-a',
139
- database: 'flusys_default',
140
- success: false,
141
- error: 'connection refused'
142
- });
143
- expect(errorSpy).toHaveBeenCalled();
144
- });
145
- });
146
- describe('revertMigration', ()=>{
147
- it('should undo the last migration and close the connection', async ()=>{
148
- const result = await revertMigration(baseConfig);
149
- expect(mockUndoLastMigration).toHaveBeenCalled();
150
- expect(mockDestroy).toHaveBeenCalled();
151
- expect(result.success).toBe(true);
152
- });
153
- it('should capture revert errors', async ()=>{
154
- mockUndoLastMigration.mockRejectedValue(new Error('no migrations to revert'));
155
- const result = await revertMigration(baseConfig);
156
- expect(result.success).toBe(false);
157
- expect(result.error).toBe('no migrations to revert');
158
- });
159
- });
160
- describe('migrationStatus', ()=>{
161
- it('should report pending status', async ()=>{
162
- mockShowMigrations.mockResolvedValue(true);
163
- const result = await migrationStatus(baseConfig);
164
- expect(result.migrationsRun).toEqual([
165
- 'Has pending'
166
- ]);
167
- });
168
- it('should report up-to-date status', async ()=>{
169
- mockShowMigrations.mockResolvedValue(false);
170
- const result = await migrationStatus(baseConfig);
171
- expect(result.migrationsRun).toEqual([
172
- 'Up to date'
173
- ]);
174
- });
175
- });
176
- describe('resetDatabase', ()=>{
177
- it('should remove migration files then drop/recreate a postgres database', async ()=>{
178
- mockExistsSync.mockReturnValue(true);
179
- mockReaddirSync.mockReturnValue([
180
- '1-init.ts',
181
- '2-add.js',
182
- 'readme.md'
183
- ]);
184
- mockAdminQuery.mockResolvedValue(undefined);
185
- const config = {
186
- defaultDatabaseConfig: {
187
- type: 'postgres',
188
- host: 'localhost',
189
- port: 5432,
190
- username: 'root',
191
- password: 'pw',
192
- database: 'flusys_default'
193
- },
194
- migrationsPath: 'migrations',
195
- entities: []
196
- };
197
- const result = await resetDatabase(config);
198
- expect(mockRmSync).toHaveBeenCalledTimes(2);
199
- expect(mockAdminQuery).toHaveBeenCalledWith(expect.stringContaining('DROP DATABASE IF EXISTS "flusys_default"'));
200
- expect(mockAdminDestroy).toHaveBeenCalled();
201
- expect(result.success).toBe(true);
202
- });
203
- it('should delete the sqlite file directly for sqlite databases', async ()=>{
204
- mockExistsSync.mockReturnValue(true);
205
- mockReaddirSync.mockReturnValue([]);
206
- const config = {
207
- defaultDatabaseConfig: {
208
- type: 'sqlite',
209
- database: 'flusys.sqlite'
210
- },
211
- migrationsPath: 'migrations',
212
- entities: []
213
- };
214
- const result = await resetDatabase(config);
215
- expect(mockRmSync).toHaveBeenCalledWith('flusys.sqlite');
216
- expect(mockAdminInitialize).not.toHaveBeenCalled();
217
- expect(result.success).toBe(true);
218
- });
219
- it('should fail for unsupported database types', async ()=>{
220
- mockExistsSync.mockReturnValue(false);
221
- const config = {
222
- defaultDatabaseConfig: {
223
- type: 'mssql',
224
- database: 'flusys'
225
- },
226
- migrationsPath: 'migrations',
227
- entities: []
228
- };
229
- const result = await resetDatabase(config);
230
- expect(result.success).toBe(false);
231
- expect(result.error).toContain("Database reset is not supported for type 'mssql'");
232
- });
233
- });
234
- describe('runForAllTenants', ()=>{
235
- it('should run the given command across all configured tenants and summarize results', async ()=>{
236
- mockRunMigrations.mockResolvedValue([
237
- {
238
- name: 'M1'
239
- }
240
- ]);
241
- const config = {
242
- ...baseConfig,
243
- tenants: [
244
- {
245
- id: 'tenant-a',
246
- database: 'db_a'
247
- },
248
- {
249
- id: 'tenant-b',
250
- database: 'db_b'
251
- }
252
- ]
253
- };
254
- const results = await runForAllTenants(config, 'run');
255
- expect(results).toHaveLength(2);
256
- expect(results.every((r)=>r.success)).toBe(true);
257
- expect(initializeDataSource).toHaveBeenCalledTimes(2);
258
- });
259
- it('should return an empty result set when no tenants are configured', async ()=>{
260
- const results = await runForAllTenants(baseConfig, 'status');
261
- expect(results).toEqual([]);
262
- });
263
- });
264
- });
@@ -1,84 +0,0 @@
1
- /**
2
- * Build DataSource options from database config
3
- */ export function buildDataSourceOptions(config, entities, migrationsPath, migrationsTableName = 'migrations') {
4
- return {
5
- type: config.type,
6
- host: config.host,
7
- port: config.port,
8
- username: config.username,
9
- password: config.password,
10
- database: config.database || '',
11
- entities,
12
- migrations: [
13
- migrationsPath
14
- ],
15
- migrationsTableName,
16
- synchronize: false
17
- };
18
- }
19
- /**
20
- * Get migration folder path for a tenant
21
- */ export function getMigrationsFolderPath(basePath, tenantId) {
22
- const folder = tenantId || 'default';
23
- return `${basePath}/${folder}`;
24
- }
25
- /**
26
- * Get migrations glob pattern for a tenant
27
- */ export function getMigrationsGlobPattern(basePath, tenantId) {
28
- return `${getMigrationsFolderPath(basePath, tenantId)}/*{.ts,.js}`;
29
- }
30
- /**
31
- * Get database and config for a tenant (or default)
32
- */ export function getDatabaseForTenant(config, tenantId) {
33
- if (tenantId && config.tenants) {
34
- const tenant = config.tenants.find((t)=>t.id === tenantId);
35
- if (!tenant) {
36
- throw new Error(`Tenant '${tenantId}' not found. Available: ${config.tenants.map((t)=>t.id).join(', ')}`);
37
- }
38
- return {
39
- database: tenant.database,
40
- tenant,
41
- tenantConfig: tenant
42
- };
43
- }
44
- // Default config - use bootstrapAppConfig for feature flags
45
- const defaultTenantConfig = {
46
- id: 'default',
47
- database: config.defaultDatabaseConfig.database || 'default',
48
- enableCompanyFeature: config.bootstrapAppConfig?.enableCompanyFeature ?? false,
49
- permissionMode: config.bootstrapAppConfig?.permissionMode ?? 'FULL'
50
- };
51
- return {
52
- database: defaultTenantConfig.database,
53
- tenantConfig: defaultTenantConfig
54
- };
55
- }
56
- /**
57
- * Resolve full connection details for a tenant.
58
- */ export function resolveDatabaseConnection(config, tenantId) {
59
- const { database, tenant } = getDatabaseForTenant(config, tenantId);
60
- return tenant ? {
61
- ...config.defaultDatabaseConfig,
62
- host: tenant.host ?? config.defaultDatabaseConfig.host,
63
- port: tenant.port ?? config.defaultDatabaseConfig.port,
64
- username: tenant.username ?? config.defaultDatabaseConfig.username,
65
- password: tenant.password ?? config.defaultDatabaseConfig.password,
66
- database: tenant.database
67
- } : {
68
- ...config.defaultDatabaseConfig,
69
- database
70
- };
71
- }
72
- /**
73
- * Resolve entities based on tenant config
74
- */ export function resolveEntities(config, tenantConfig) {
75
- if (typeof config.entities === 'function') {
76
- return config.entities(tenantConfig);
77
- }
78
- return config.entities;
79
- }
80
- /**
81
- * Get active tenants from config
82
- */ export function getActiveTenants(config) {
83
- return config.tenants || [];
84
- }