@flusys/nestjs-core 9.1.1 → 9.2.0

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/fesm/229.js CHANGED
@@ -1,14 +1,14 @@
1
+ import * as __rspack_external_fs from "fs";
1
2
  import * as __rspack_external_path from "path";
2
3
  import * as __rspack_external_typeorm from "typeorm";
3
4
  import * as __rspack_external_typeorm_naming_strategies_48a68f8e from "typeorm-naming-strategies";
4
5
  import * as __rspack_external_url from "url";
5
6
  import * as __rspack_external_dotenv from "dotenv";
6
7
  import * as __rspack_external_child_process from "child_process";
7
- import * as __rspack_external_fs from "fs";
8
8
  export const __rspack_esm_id = 229;
9
9
  export const __rspack_esm_ids = [229];
10
10
  export const __webpack_modules__ = {
11
- 398(module) {
11
+ 631(module) {
12
12
  function __rspack_empty_async_context(req) {
13
13
  // Here Promise.resolve().then() is used instead of new Promise() to prevent
14
14
  // uncaught exception popping up in devtools
@@ -20,7 +20,7 @@ function __rspack_empty_async_context(req) {
20
20
  }
21
21
  __rspack_empty_async_context.keys = () => ([]);
22
22
  __rspack_empty_async_context.resolve = __rspack_empty_async_context;
23
- __rspack_empty_async_context.id = 398;
23
+ __rspack_empty_async_context.id = 631;
24
24
  module.exports = __rspack_empty_async_context;
25
25
 
26
26
 
@@ -37,10 +37,20 @@ __webpack_require__.d(__webpack_exports__, {
37
37
  ;// CONCATENATED MODULE: ./projects/nestjs-core/src/config/env-config.service.ts
38
38
 
39
39
  __rspack_external_dotenv.config();
40
+ const DATABASE_TYPES = [
41
+ 'mysql',
42
+ 'postgres',
43
+ 'mariadb',
44
+ 'sqlite',
45
+ 'mssql'
46
+ ];
47
+ const DEFAULT_DB_PORTS = {
48
+ postgres: 5432,
49
+ mssql: 1433
50
+ };
40
51
  let env_config_service_EnvConfigService = class EnvConfigService {
41
52
  env;
42
53
  restrictedKeys = {
43
- FRONTEND_URL: 'getFrontendUrl',
44
54
  ALLOW_ORIGINS: 'getOrigins',
45
55
  PORT: 'getPort',
46
56
  MODE: 'isProduction',
@@ -56,10 +66,13 @@ let env_config_service_EnvConfigService = class EnvConfigService {
56
66
  }
57
67
  getValue(key, throwOnMissing = true) {
58
68
  const value = this.env[key];
59
- if (!value && throwOnMissing) {
60
- throw new Error(`Config error - missing env.${key}`);
69
+ if (!value?.trim()) {
70
+ if (throwOnMissing) {
71
+ throw new Error(`Config error - missing env.${key}`);
72
+ }
73
+ return '';
61
74
  }
62
- return value ?? '';
75
+ return value;
63
76
  }
64
77
  tryGetValue(key, throwOnMissing = false) {
65
78
  if (this.restrictedKeys[key]) {
@@ -68,30 +81,39 @@ let env_config_service_EnvConfigService = class EnvConfigService {
68
81
  return this.getValue(key, throwOnMissing);
69
82
  }
70
83
  getNumber(key, throwOnMissing = true) {
71
- return Number(this.getValue(key, throwOnMissing));
84
+ const raw = this.getValue(key, throwOnMissing).trim();
85
+ if (!raw) return undefined;
86
+ const value = Number(raw);
87
+ if (Number.isFinite(value)) return value;
88
+ if (throwOnMissing) {
89
+ throw new Error(`Config error - env.${key} must be a number, got "${raw}"`);
90
+ }
91
+ return undefined;
72
92
  }
73
93
  getBoolean(key, throwOnMissing = true) {
74
- return this.getValue(key, throwOnMissing).toLowerCase() === 'true';
94
+ return this.getValue(key, throwOnMissing).trim().toLowerCase() === 'true';
75
95
  }
76
96
  getPort() {
77
97
  return this.getNumber('PORT', false) || 3000;
78
98
  }
79
99
  isProduction() {
80
- return this.getValue('MODE', false).toUpperCase() !== 'DEV';
100
+ return this.getValue('MODE', false).trim().toUpperCase() !== 'DEV';
81
101
  }
82
102
  getOrigins() {
83
- const origins = this.getValue('ALLOW_ORIGINS');
84
- return origins ? origins.split(',').map((o)=>o.trim()) : [];
103
+ return this.getValue('ALLOW_ORIGINS').split(',').map((o)=>o.trim()).filter(Boolean);
85
104
  }
86
105
  getTypeOrmConfig() {
87
- const type = this.getValue('DB_TYPE', false) || 'mysql';
88
- const defaultPort = type === 'postgres' ? 5432 : 3306;
106
+ const rawType = this.getValue('DB_TYPE', false).trim().toLowerCase() || 'mysql';
107
+ if (!DATABASE_TYPES.includes(rawType)) {
108
+ throw new Error(`Config error - unsupported env.DB_TYPE "${rawType}". Expected one of: ${DATABASE_TYPES.join(', ')}`);
109
+ }
110
+ const type = rawType;
89
111
  return {
90
112
  type,
91
113
  host: this.getValue('DB_HOST', false) || 'localhost',
92
- port: this.getNumber('DB_PORT', false) || defaultPort,
114
+ port: this.getNumber('DB_PORT', false) || DEFAULT_DB_PORTS[type] || 3306,
93
115
  username: this.getValue('DB_USER', false) || 'root',
94
- password: this.getValue('DB_PASSWORD', false) || '',
116
+ password: this.getValue('DB_PASSWORD', false),
95
117
  database: this.getValue('DB_NAME', false) || 'flusys_main'
96
118
  };
97
119
  }
@@ -108,15 +130,15 @@ let env_config_service_EnvConfigService = class EnvConfigService {
108
130
  }
109
131
  getMailConfig() {
110
132
  return {
111
- MAIL_FROM: this.getValue('MAIL_FROM', false) || '',
112
- MAIL_APP_PASSWORD: this.getValue('MAIL_APP_PASSWORD', false) || ''
133
+ MAIL_FROM: this.getValue('MAIL_FROM', false),
134
+ MAIL_APP_PASSWORD: this.getValue('MAIL_APP_PASSWORD', false)
113
135
  };
114
136
  }
115
137
  getTenantId() {
116
- return this.getValue('TENANT_ID', false) || null;
138
+ return this.getValue('TENANT_ID', false).trim() || null;
117
139
  }
118
140
  useTenantMode() {
119
- return this.getValue('USE_TENANT_MODE', false).toLowerCase() === 'true';
141
+ return this.getBoolean('USE_TENANT_MODE', false);
120
142
  }
121
143
  getLogConfig() {
122
144
  return {
@@ -124,11 +146,11 @@ let env_config_service_EnvConfigService = class EnvConfigService {
124
146
  level: this.getValue('LOG_LEVEL', false) || (this.isProduction() ? 'info' : 'debug'),
125
147
  maxSize: this.getValue('LOG_MAX_SIZE', false) || '20m',
126
148
  maxFiles: this.getValue('LOG_MAX_FILES', false) || '14d',
127
- disableHttpLogging: this.getValue('DISABLE_HTTP_LOGGING', false).toLowerCase() === 'true'
149
+ disableHttpLogging: this.getBoolean('DISABLE_HTTP_LOGGING', false)
128
150
  };
129
151
  }
130
152
  getEnv() {
131
- return process.env;
153
+ return this.env;
132
154
  }
133
155
  };
134
156
  const envConfig = new env_config_service_EnvConfigService(process.env);
@@ -160,9 +182,9 @@ var datasource_config_builder = __webpack_require__(722);
160
182
  function createMigrationDataSource(options) {
161
183
  const { config, tenantId } = options;
162
184
  const { database, tenant, tenantConfig } = (0,datasource_config_builder/* .getDatabaseForTenant */.LH)(config, tenantId);
163
- console.log(`\nšŸ“¦ Database: ${database}${tenant ? ` [${tenant.name}]` : ' [default]'}`);
164
- console.log(`šŸ“ Migrations: migrations/${tenantId || 'default'}/`);
165
- if (config.bootstrapAppConfig?.databaseMode === 'multi-tenant' && !tenantId && config.tenants && config.tenants.length > 0) {
185
+ console.log(`\nšŸ“¦ Database: ${database} [${tenant ? tenant.name ?? tenant.id : 'default'}]`);
186
+ console.log(`šŸ“ Migrations: ${(0,datasource_config_builder/* .getMigrationsFolderPath */.jr)(config.migrationsPath, tenantId)}/`);
187
+ if (config.bootstrapAppConfig?.databaseMode === 'multi-tenant' && !tenantId && config.tenants?.length) {
166
188
  console.log(`\nšŸ’” Multi-tenant mode. Available tenants:`);
167
189
  config.tenants.forEach((t)=>console.log(` - TENANT_ID=${t.id} → ${t.database}`));
168
190
  }
@@ -189,6 +211,8 @@ __webpack_require__.d(__webpack_exports__, {
189
211
  w: () => (/* binding */ runMigrationCli)
190
212
  });
191
213
 
214
+ // EXTERNAL MODULE: external "fs"
215
+ var external_fs_ = __webpack_require__(421);
192
216
  // EXTERNAL MODULE: external "path"
193
217
  var external_path_ = __webpack_require__(521);
194
218
  ;// CONCATENATED MODULE: external "url"
@@ -197,14 +221,15 @@ var external_path_ = __webpack_require__(521);
197
221
  var src_config = __webpack_require__(892);
198
222
  // EXTERNAL MODULE: ./projects/nestjs-core/src/utils/datasource-config.builder.ts
199
223
  var datasource_config_builder = __webpack_require__(722);
200
- // EXTERNAL MODULE: ./projects/nestjs-core/src/migration/migration.runner.ts + 2 modules
201
- var migration_runner = __webpack_require__(654);
224
+ // EXTERNAL MODULE: ./projects/nestjs-core/src/migration/migration.runner.ts + 1 modules
225
+ var migration_runner = __webpack_require__(989);
202
226
  ;// CONCATENATED MODULE: ./projects/nestjs-core/src/migration/migration.cli.ts
203
227
 
204
228
 
205
229
 
206
230
 
207
231
 
232
+
208
233
  function showHelp() {
209
234
  console.log(`
210
235
  Migration CLI
@@ -239,21 +264,23 @@ Examples:
239
264
  npm run migration reset
240
265
  `);
241
266
  }
267
+ function readFlag(arg, flag) {
268
+ const prefix = `--${flag}=`;
269
+ return arg.startsWith(prefix) ? arg.slice(prefix.length).trim() || undefined : undefined;
270
+ }
242
271
  function parseArgs() {
243
- const args = process.argv.slice(2);
244
272
  let command;
245
273
  let name;
246
274
  let configPath;
247
275
  let datasourcePath;
248
276
  let force = false;
249
- for(let i = 0; i < args.length; i++){
250
- const arg = args[i];
277
+ for (const arg of process.argv.slice(2)){
251
278
  if (arg.startsWith('--name=')) {
252
- name = arg.split('=')[1];
279
+ name = readFlag(arg, 'name');
253
280
  } else if (arg.startsWith('--config=')) {
254
- configPath = arg.split('=')[1];
281
+ configPath = readFlag(arg, 'config');
255
282
  } else if (arg.startsWith('--datasource=')) {
256
- datasourcePath = arg.split('=')[1];
283
+ datasourcePath = readFlag(arg, 'datasource');
257
284
  } else if (arg === '--force') {
258
285
  force = true;
259
286
  } else if (!arg.startsWith('-') && !command) {
@@ -268,17 +295,21 @@ function parseArgs() {
268
295
  force
269
296
  };
270
297
  }
298
+ function resolveConfigPath(configPath) {
299
+ return external_path_.isAbsolute(configPath) ? configPath : external_path_.resolve(process.cwd(), configPath);
300
+ }
271
301
  async function loadConfig(configPath) {
272
- const absolutePath = external_path_.isAbsolute(configPath) ? configPath : external_path_.resolve(process.cwd(), configPath);
273
- const module = await __webpack_require__(398)((0,__rspack_external_url.pathToFileURL)(absolutePath).href);
274
- return module.migrationConfig || module.default?.migrationConfig || module;
302
+ const module = await __webpack_require__(631)((0,__rspack_external_url.pathToFileURL)(resolveConfigPath(configPath)).href);
303
+ const config = module.migrationConfig ?? module.default?.migrationConfig ?? module.default;
304
+ if (!config?.defaultDatabaseConfig || !config.migrationsPath) {
305
+ throw new Error(`Migration config '${configPath}' must export a 'migrationConfig' with defaultDatabaseConfig and migrationsPath`);
306
+ }
307
+ return config;
275
308
  }
276
309
  async function runMigrationCli(configOrPath) {
277
310
  const { command, name, configPath: argConfigPath, datasourcePath, force } = parseArgs();
278
311
  const tenantId = src_config/* .envConfig.getTenantId */.K.getTenantId() ?? undefined;
279
- // npm passes --name=X as npm_config_name
280
312
  const migrationName = src_config/* .envConfig.tryGetValue */.K.tryGetValue('npm_config_name') || src_config/* .envConfig.tryGetValue */.K.tryGetValue('MIGRATION_NAME') || name;
281
- // Resolve config
282
313
  let config;
283
314
  let dsPath = datasourcePath;
284
315
  if (typeof configOrPath === 'string') {
@@ -290,7 +321,6 @@ async function runMigrationCli(configOrPath) {
290
321
  config = await loadConfig(argConfigPath);
291
322
  dsPath = dsPath || argConfigPath;
292
323
  } else {
293
- // Try paths in order: env var, then common defaults
294
324
  const envConfigPath = src_config/* .envConfig.tryGetValue */.K.tryGetValue('MIGRATION_CONFIG');
295
325
  const defaultPaths = [
296
326
  envConfigPath,
@@ -299,18 +329,11 @@ async function runMigrationCli(configOrPath) {
299
329
  'persistence/migration.config.ts',
300
330
  'persistence/migration.config.js'
301
331
  ].filter(Boolean);
302
- let loaded = false;
303
- for (const tryPath of defaultPaths){
304
- try {
305
- config = await loadConfig(tryPath);
306
- dsPath = dsPath || tryPath;
307
- loaded = true;
308
- break;
309
- } catch {
310
- // Try next path
311
- }
312
- }
313
- if (!loaded) {
332
+ const foundPath = defaultPaths.find((p)=>external_fs_.existsSync(resolveConfigPath(p)));
333
+ if (foundPath) {
334
+ config = await loadConfig(foundPath);
335
+ dsPath = dsPath || foundPath;
336
+ } else {
314
337
  console.error('āŒ Config not found. Use --config=<path> or set MIGRATION_CONFIG env var');
315
338
  console.error(' Tried paths:', defaultPaths.join(', '));
316
339
  showHelp();
@@ -321,18 +344,21 @@ async function runMigrationCli(configOrPath) {
321
344
  showHelp();
322
345
  return;
323
346
  }
324
- // Multi-tenant batch commands
325
347
  if (command.endsWith(':all')) {
348
+ const action = command.slice(0, -':all'.length);
349
+ if (!(0,migration_runner/* .isTenantMigrationAction */.x_)(action)) {
350
+ console.error(`āŒ Unknown command: ${command}`);
351
+ showHelp();
352
+ process.exit(1);
353
+ }
326
354
  if (config.bootstrapAppConfig?.databaseMode === 'single') {
327
355
  console.error('āŒ Multi-tenant mode not enabled');
328
356
  process.exit(1);
329
357
  }
330
- const action = command.split(':')[0];
331
358
  const results = await (0,migration_runner/* .runForAllTenants */.XR)(config, action);
332
359
  if (results.some((r)=>!r.success)) process.exit(1);
333
360
  return;
334
361
  }
335
- // Show target info
336
362
  const { database } = (0,datasource_config_builder/* .getDatabaseForTenant */.LH)(config, tenantId);
337
363
  const folder = (0,datasource_config_builder/* .getMigrationsFolderPath */.jr)(config.migrationsPath, tenantId);
338
364
  console.log(`\nšŸ“¦ Database: ${database}`);
@@ -363,16 +389,21 @@ async function runMigrationCli(configOrPath) {
363
389
  console.error('āŒ Refusing to reset database outside dev mode. Pass --force to override.');
364
390
  process.exit(1);
365
391
  }
392
+ const initName = migrationName || 'Init';
393
+ (0,migration_runner/* .assertValidMigrationName */.On)(initName);
366
394
  const result = await (0,migration_runner/* .resetDatabase */.$7)(config, tenantId);
367
395
  if (!result.success) process.exit(1);
368
- await (0,migration_runner/* .generateMigration */.iL)(config, migrationName || 'Init', tenantId, dsPath);
396
+ await (0,migration_runner/* .generateMigration */.iL)(config, initName, tenantId, dsPath);
369
397
  const runResult = await (0,migration_runner/* .runMigrations */.vI)(config, tenantId);
370
398
  if (!runResult.success) process.exit(1);
371
399
  break;
372
400
  }
373
401
  case 'status':
374
- await (0,migration_runner/* .migrationStatus */.vd)(config, tenantId);
375
- break;
402
+ {
403
+ const result = await (0,migration_runner/* .migrationStatus */.vd)(config, tenantId);
404
+ if (!result.success) process.exit(1);
405
+ break;
406
+ }
376
407
  default:
377
408
  console.error(`āŒ Unknown command: ${command}`);
378
409
  showHelp();
@@ -382,12 +413,14 @@ async function runMigrationCli(configOrPath) {
382
413
 
383
414
 
384
415
  },
385
- 654(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
416
+ 989(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
386
417
 
387
418
  // EXPORTS
388
419
  __webpack_require__.d(__webpack_exports__, {
420
+ On: () => (/* binding */ assertValidMigrationName),
389
421
  Mg: () => (/* binding */ ensureMigrationsFolder),
390
422
  iL: () => (/* binding */ generateMigration),
423
+ x_: () => (/* binding */ isTenantMigrationAction),
391
424
  vd: () => (/* binding */ migrationStatus),
392
425
  $7: () => (/* binding */ resetDatabase),
393
426
  $N: () => (/* binding */ revertMigration),
@@ -399,8 +432,8 @@ __webpack_require__.d(__webpack_exports__, {
399
432
 
400
433
  ;// CONCATENATED MODULE: external "child_process"
401
434
 
402
- ;// CONCATENATED MODULE: external "fs"
403
-
435
+ // EXTERNAL MODULE: external "fs"
436
+ var external_fs_ = __webpack_require__(421);
404
437
  // EXTERNAL MODULE: external "path"
405
438
  var external_path_ = __webpack_require__(521);
406
439
  // EXTERNAL MODULE: external "typeorm"
@@ -419,19 +452,38 @@ var datasource_factory = __webpack_require__(193);
419
452
 
420
453
 
421
454
 
455
+ const MIGRATION_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;
456
+ function assertValidMigrationName(name) {
457
+ if (!MIGRATION_NAME_PATTERN.test(name)) {
458
+ throw new Error(`Invalid migration name '${name}'. Use letters, digits, "_" or "-", starting with a letter.`);
459
+ }
460
+ }
422
461
  function getLabel(tenantId) {
423
462
  return tenantId ? `[${tenantId}]` : '[default]';
424
463
  }
464
+ function toErrorMessage(error) {
465
+ return error instanceof Error ? error.message : String(error);
466
+ }
467
+ function failure(label, tenantId, database, error) {
468
+ const errorMessage = toErrorMessage(error);
469
+ console.error(`${label} āœ— Failed: ${errorMessage}`);
470
+ return {
471
+ tenantId,
472
+ database,
473
+ success: false,
474
+ error: errorMessage
475
+ };
476
+ }
425
477
  async function withDataSource(config, tenantId, operation) {
426
478
  const { database } = (0,datasource_config_builder/* .getDatabaseForTenant */.LH)(config, tenantId);
427
479
  const label = getLabel(tenantId);
480
+ let ds;
428
481
  try {
429
- const ds = await (0,datasource_factory/* .initializeDataSource */.w)({
482
+ ds = await (0,datasource_factory/* .initializeDataSource */.w)({
430
483
  config,
431
484
  tenantId
432
485
  });
433
486
  const partial = await operation(ds, label);
434
- await ds.destroy();
435
487
  return {
436
488
  tenantId,
437
489
  database,
@@ -439,20 +491,21 @@ async function withDataSource(config, tenantId, operation) {
439
491
  ...partial
440
492
  };
441
493
  } catch (error) {
442
- const errorMessage = error instanceof Error ? error.message : String(error);
443
- console.error(`${label} āœ— Failed: ${errorMessage}`);
444
- return {
445
- tenantId,
446
- database,
447
- success: false,
448
- error: errorMessage
449
- };
494
+ return failure(label, tenantId, database, error);
495
+ } finally{
496
+ if (ds) {
497
+ try {
498
+ await ds.destroy();
499
+ } catch (error) {
500
+ console.error(`${label} āœ— Failed to close connection: ${toErrorMessage(error)}`);
501
+ }
502
+ }
450
503
  }
451
504
  }
452
505
  function ensureMigrationsFolder(basePath, tenantId) {
453
506
  const folder = (0,datasource_config_builder/* .getMigrationsFolderPath */.jr)(basePath, tenantId);
454
- if (!__rspack_external_fs.existsSync(folder)) {
455
- __rspack_external_fs.mkdirSync(folder, {
507
+ if (!external_fs_.existsSync(folder)) {
508
+ external_fs_.mkdirSync(folder, {
456
509
  recursive: true
457
510
  });
458
511
  console.log(`šŸ“ Created: ${folder}`);
@@ -460,7 +513,8 @@ function ensureMigrationsFolder(basePath, tenantId) {
460
513
  return folder;
461
514
  }
462
515
  async function generateMigration(config, name, tenantId, datasourcePath) {
463
- const label = tenantId ? `[${tenantId}]` : '[default]';
516
+ assertValidMigrationName(name);
517
+ const label = getLabel(tenantId);
464
518
  const folder = ensureMigrationsFolder(config.migrationsPath, tenantId);
465
519
  console.log(`${label} Generating migration: ${name}`);
466
520
  console.log(`${label} Target folder: ${folder}`);
@@ -483,14 +537,11 @@ async function generateMigration(config, name, tenantId, datasourcePath) {
483
537
  }
484
538
  console.log(`${label} āœ“ Generated!`);
485
539
  }
486
- /**
487
- * Removes every `queryRunner.query(...)` line of the just-generated migration whose SQL
488
- * mentions an ignored table or index name. Returns how many statements were removed.
489
- */ function stripIgnoredTableStatements(folder, name, ignoreTable) {
490
- const file = __rspack_external_fs.readdirSync(folder).filter((f)=>f.endsWith(`-${name}.ts`)).sort().pop();
540
+ function stripIgnoredTableStatements(folder, name, ignoreTable) {
541
+ const file = external_fs_.readdirSync(folder).filter((f)=>f.endsWith(`-${name}.ts`)).toSorted().at(-1);
491
542
  if (!file) return 0;
492
543
  const filePath = external_path_.join(folder, file);
493
- const lines = __rspack_external_fs.readFileSync(filePath, 'utf8').split('\n');
544
+ const lines = external_fs_.readFileSync(filePath, 'utf8').split('\n');
494
545
  let removed = 0;
495
546
  const kept = lines.filter((line)=>{
496
547
  if (!line.includes('queryRunner.query(')) return true;
@@ -501,7 +552,7 @@ async function generateMigration(config, name, tenantId, datasourcePath) {
501
552
  if (touches) removed++;
502
553
  return !touches;
503
554
  });
504
- if (removed > 0) __rspack_external_fs.writeFileSync(filePath, kept.join('\n'));
555
+ if (removed > 0) external_fs_.writeFileSync(filePath, kept.join('\n'));
505
556
  return removed;
506
557
  }
507
558
  function findDatasourcePath() {
@@ -510,7 +561,7 @@ function findDatasourcePath() {
510
561
  'projects/flusysnest/src/persistence/migration.config.ts'
511
562
  ];
512
563
  for (const p of possiblePaths){
513
- if (__rspack_external_fs.existsSync(external_path_.join(process.cwd(), p))) {
564
+ if (external_fs_.existsSync(external_path_.join(process.cwd(), p))) {
514
565
  return p;
515
566
  }
516
567
  }
@@ -539,21 +590,30 @@ async function revertMigration(config, tenantId) {
539
590
  }
540
591
  function clearMigrationFiles(basePath, tenantId, label) {
541
592
  const folder = (0,datasource_config_builder/* .getMigrationsFolderPath */.jr)(basePath, tenantId);
542
- if (!__rspack_external_fs.existsSync(folder)) return;
543
- const files = __rspack_external_fs.readdirSync(folder).filter((f)=>f.endsWith('.ts') || f.endsWith('.js'));
544
- files.forEach((file)=>__rspack_external_fs.rmSync(external_path_.join(folder, file)));
593
+ if (!external_fs_.existsSync(folder)) return;
594
+ const files = external_fs_.readdirSync(folder).filter((f)=>f.endsWith('.ts') || f.endsWith('.js'));
595
+ files.forEach((file)=>external_fs_.rmSync(external_path_.join(folder, file)));
545
596
  console.log(`${label} āœ“ Removed ${files.length} migration file(s) from ${folder}`);
546
597
  }
598
+ function quoteIdentifier(name, quote) {
599
+ return `${quote}${name.split(quote).join(quote + quote)}${quote}`;
600
+ }
547
601
  async function dropAndRecreateDatabase(dbConfig, label) {
548
602
  const { type, database } = dbConfig;
603
+ if (!database) {
604
+ throw new Error('Database reset requires a database name');
605
+ }
549
606
  if (type === 'sqlite') {
550
- if (database && __rspack_external_fs.existsSync(database)) __rspack_external_fs.rmSync(database);
607
+ if (external_fs_.existsSync(database)) external_fs_.rmSync(database);
551
608
  console.log(`${label} āœ“ Deleted sqlite file`);
552
609
  return;
553
610
  }
554
611
  if (type !== 'mysql' && type !== 'mariadb' && type !== 'postgres') {
555
612
  throw new Error(`Database reset is not supported for type '${type}'`);
556
613
  }
614
+ if (type === 'postgres' && database === 'postgres') {
615
+ throw new Error("Refusing to reset the 'postgres' maintenance database");
616
+ }
557
617
  const adminDs = new external_typeorm_.DataSource({
558
618
  type,
559
619
  host: dbConfig.host,
@@ -568,11 +628,13 @@ async function dropAndRecreateDatabase(dbConfig, label) {
568
628
  await adminDs.query(`SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1 AND pid <> pg_backend_pid()`, [
569
629
  database
570
630
  ]);
571
- await adminDs.query(`DROP DATABASE IF EXISTS "${database}"`);
572
- await adminDs.query(`CREATE DATABASE "${database}"`);
631
+ const quoted = quoteIdentifier(database, '"');
632
+ await adminDs.query(`DROP DATABASE IF EXISTS ${quoted}`);
633
+ await adminDs.query(`CREATE DATABASE ${quoted}`);
573
634
  } else {
574
- await adminDs.query(`DROP DATABASE IF EXISTS \`${database}\``);
575
- await adminDs.query(`CREATE DATABASE \`${database}\` CHARACTER SET utf8mb4`);
635
+ const quoted = quoteIdentifier(database, '`');
636
+ await adminDs.query(`DROP DATABASE IF EXISTS ${quoted}`);
637
+ await adminDs.query(`CREATE DATABASE ${quoted} CHARACTER SET utf8mb4`);
576
638
  }
577
639
  console.log(`${label} āœ“ Database '${database}' dropped and recreated`);
578
640
  } finally{
@@ -592,14 +654,7 @@ async function resetDatabase(config, tenantId) {
592
654
  success: true
593
655
  };
594
656
  } catch (error) {
595
- const errorMessage = error instanceof Error ? error.message : String(error);
596
- console.error(`${label} āœ— Failed: ${errorMessage}`);
597
- return {
598
- tenantId,
599
- database,
600
- success: false,
601
- error: errorMessage
602
- };
657
+ return failure(label, tenantId, database, error);
603
658
  }
604
659
  }
605
660
  async function migrationStatus(config, tenantId) {
@@ -613,24 +668,28 @@ async function migrationStatus(config, tenantId) {
613
668
  };
614
669
  });
615
670
  }
671
+ const TENANT_ACTIONS = {
672
+ run: runMigrations,
673
+ revert: revertMigration,
674
+ status: migrationStatus
675
+ };
676
+ function isTenantMigrationAction(action) {
677
+ return Object.hasOwn(TENANT_ACTIONS, action);
678
+ }
616
679
  async function runForAllTenants(config, command) {
680
+ if (!isTenantMigrationAction(command)) {
681
+ throw new Error(`Unsupported multi-tenant command '${command}'. Use run, revert or status.`);
682
+ }
683
+ const action = TENANT_ACTIONS[command];
617
684
  const tenants = (0,datasource_config_builder/* .getActiveTenants */.SP)(config);
618
685
  console.log(`\nšŸ”„ Running '${command}' for ${tenants.length} tenant(s)...\n`);
619
686
  const results = [];
620
687
  for (const tenant of tenants){
621
- let result;
622
- switch(command){
623
- case 'run':
624
- result = await runMigrations(config, tenant.id);
625
- break;
626
- case 'revert':
627
- result = await revertMigration(config, tenant.id);
628
- break;
629
- case 'status':
630
- result = await migrationStatus(config, tenant.id);
631
- break;
688
+ try {
689
+ results.push(await action(config, tenant.id));
690
+ } catch (error) {
691
+ results.push(failure(getLabel(tenant.id), tenant.id, tenant.database, error));
632
692
  }
633
- results.push(result);
634
693
  console.log('');
635
694
  }
636
695
  console.log('\n=== Summary ===');
@@ -645,9 +704,7 @@ async function runForAllTenants(config, command) {
645
704
 
646
705
  },
647
706
  722(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
648
- /**
649
- * Build DataSource options from database config
650
- */ function buildDataSourceOptions(config, entities, migrationsPath, migrationsTableName = 'migrations') {
707
+ function buildDataSourceOptions(config, entities, migrationsPath, migrationsTableName = 'migrations') {
651
708
  return {
652
709
  type: config.type,
653
710
  host: config.host,
@@ -663,24 +720,20 @@ async function runForAllTenants(config, command) {
663
720
  synchronize: false
664
721
  };
665
722
  }
666
- /**
667
- * Get migration folder path for a tenant
668
- */ function getMigrationsFolderPath(basePath, tenantId) {
723
+ function getMigrationsFolderPath(basePath, tenantId) {
669
724
  const folder = tenantId || 'default';
670
725
  return `${basePath}/${folder}`;
671
726
  }
672
- /**
673
- * Get migrations glob pattern for a tenant
674
- */ function getMigrationsGlobPattern(basePath, tenantId) {
727
+ function getMigrationsGlobPattern(basePath, tenantId) {
675
728
  return `${getMigrationsFolderPath(basePath, tenantId)}/*{.ts,.js}`;
676
729
  }
677
- /**
678
- * Get database and config for a tenant (or default)
679
- */ function getDatabaseForTenant(config, tenantId) {
680
- if (tenantId && config.tenants) {
681
- const tenant = config.tenants.find((t)=>t.id === tenantId);
730
+ function getDatabaseForTenant(config, tenantId) {
731
+ if (tenantId) {
732
+ const tenants = config.tenants ?? [];
733
+ const tenant = tenants.find((t)=>t.id === tenantId);
682
734
  if (!tenant) {
683
- throw new Error(`Tenant '${tenantId}' not found. Available: ${config.tenants.map((t)=>t.id).join(', ')}`);
735
+ const available = tenants.map((t)=>t.id).join(', ') || 'none';
736
+ throw new Error(`Tenant '${tenantId}' not found. Available: ${available}`);
684
737
  }
685
738
  return {
686
739
  database: tenant.database,
@@ -688,21 +741,20 @@ async function runForAllTenants(config, command) {
688
741
  tenantConfig: tenant
689
742
  };
690
743
  }
691
- // Default config - use bootstrapAppConfig for feature flags
692
744
  const defaultTenantConfig = {
693
745
  id: 'default',
694
746
  database: config.defaultDatabaseConfig.database || 'default',
695
747
  enableCompanyFeature: config.bootstrapAppConfig?.enableCompanyFeature ?? false,
696
- permissionMode: config.bootstrapAppConfig?.permissionMode ?? 'FULL'
748
+ permissionMode: config.bootstrapAppConfig?.permissionMode ?? 'FULL',
749
+ enableEmailVerification: config.bootstrapAppConfig?.enableEmailVerification,
750
+ enableSignUp: config.bootstrapAppConfig?.enableSignUp
697
751
  };
698
752
  return {
699
753
  database: defaultTenantConfig.database,
700
754
  tenantConfig: defaultTenantConfig
701
755
  };
702
756
  }
703
- /**
704
- * Resolve full connection details for a tenant.
705
- */ function resolveDatabaseConnection(config, tenantId) {
757
+ function resolveDatabaseConnection(config, tenantId) {
706
758
  const { database, tenant } = getDatabaseForTenant(config, tenantId);
707
759
  return tenant ? {
708
760
  ...config.defaultDatabaseConfig,
@@ -716,17 +768,13 @@ async function runForAllTenants(config, command) {
716
768
  database
717
769
  };
718
770
  }
719
- /**
720
- * Resolve entities based on tenant config
721
- */ function resolveEntities(config, tenantConfig) {
771
+ function resolveEntities(config, tenantConfig) {
722
772
  if (typeof config.entities === 'function') {
723
773
  return config.entities(tenantConfig);
724
774
  }
725
775
  return config.entities;
726
776
  }
727
- /**
728
- * Get active tenants from config
729
- */ function getActiveTenants(config) {
777
+ function getActiveTenants(config) {
730
778
  return config.tenants || [];
731
779
  }
732
780
 
@@ -741,6 +789,12 @@ __webpack_require__.d(__webpack_exports__, {
741
789
  });
742
790
 
743
791
 
792
+ },
793
+ 421(module) {
794
+
795
+ module.exports = __rspack_external_fs;
796
+
797
+
744
798
  },
745
799
  521(module) {
746
800