fiberx-backend-toolkit 0.0.26 → 0.0.28

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.
@@ -7,6 +7,6 @@ declare abstract class BaseConnector<TConnection> {
7
7
  readonly env_manager: EnvManagerUtil;
8
8
  readonly logger: LoggerUtil;
9
9
  protected constructor(connector_name: string);
10
- abstract connect(): TConnection;
10
+ abstract connect(): Promise<TConnection>;
11
11
  }
12
12
  export default BaseConnector;
@@ -8,8 +8,8 @@ declare class SequelizeConnector extends BaseConnector<Sequelize> {
8
8
  static getInstance(): SequelizeConnector;
9
9
  private formatSQLQueryLog;
10
10
  private ensureDatabaseExists;
11
- connect(options?: SequelizeConnectionOptions): Sequelize;
12
- connectNamed(name: string, options?: SequelizeConnectionOptions): Sequelize;
11
+ connect(options?: SequelizeConnectionOptions): Promise<Sequelize>;
12
+ connectNamed(name: string, options?: SequelizeConnectionOptions): Promise<Sequelize>;
13
13
  getConnection(name?: string): Sequelize | undefined;
14
14
  hasConnection(name?: string): boolean;
15
15
  closeConnection(name?: string): Promise<void>;
@@ -55,6 +55,7 @@ class SequelizeConnector extends base_connector_1.default {
55
55
  if (dialect === "mssql") {
56
56
  admin_db = "master";
57
57
  }
58
+ this.logger.info(`🔌 Connecting to admin database: ${admin_db}`);
58
59
  const admin = new sequelize_1.Sequelize(admin_db, username, password, {
59
60
  host,
60
61
  port,
@@ -62,6 +63,7 @@ class SequelizeConnector extends base_connector_1.default {
62
63
  logging: false,
63
64
  });
64
65
  await admin.authenticate();
66
+ this.logger.success(`🔌 Connected to admin database: ${admin_db}`);
65
67
  this.logger.info(`🔍 Checking database existence: ${database}`);
66
68
  switch (dialect) {
67
69
  case "postgres":
@@ -95,47 +97,53 @@ class SequelizeConnector extends base_connector_1.default {
95
97
  // ----------------------------------
96
98
  // Single default connection
97
99
  // ----------------------------------
98
- connect(options = {}) {
99
- return this.connectNamed(options.name ?? "default", options);
100
+ async connect(options = {}) {
101
+ return await this.connectNamed(options.name ?? "default", options);
100
102
  }
101
103
  // ----------------------------------
102
104
  // Multiple named connections
103
105
  // ----------------------------------
104
- connectNamed(name, options = {}) {
105
- if (SequelizeConnector.connections.has(name)) {
106
- return SequelizeConnector.connections.get(name);
106
+ async connectNamed(name, options = {}) {
107
+ try {
108
+ if (SequelizeConnector.connections.has(name)) {
109
+ return SequelizeConnector.connections.get(name);
110
+ }
111
+ const dialect = options.dialect ?? this.env_manager.getEnvVar("DB_DIALECT", "postgres");
112
+ const logging_enabled = options.logging ?? this.env_manager.getEnvVar("DB_LOGGING", false);
113
+ const logging = logging_enabled ? this.formatSQLQueryLog : false;
114
+ const database = options.database ?? this.env_manager.getEnvVar("DB_NAME");
115
+ const username = options.username ?? this.env_manager.getEnvVar("DB_USER");
116
+ const password = options.password ?? this.env_manager.getEnvVar("DB_PASSWORD");
117
+ const host = options.host ?? this.env_manager.getEnvVar("DB_HOST");
118
+ const port = options.port ?? this.env_manager.getEnvVar("DB_PORT", 5432);
119
+ // 🔴 IMPORTANT: Attempt to ensure DB exists BEFORE connecting
120
+ await this.ensureDatabaseExists({
121
+ dialect,
122
+ database,
123
+ username,
124
+ password,
125
+ host,
126
+ port,
127
+ logging,
128
+ }).catch(() => { });
129
+ const sequelize = new sequelize_1.Sequelize({
130
+ database,
131
+ username,
132
+ password,
133
+ host,
134
+ port,
135
+ dialect,
136
+ logging,
137
+ benchmark: logging_enabled,
138
+ });
139
+ SequelizeConnector.connections.set(name, sequelize);
140
+ this.logger.success(`✅ Sequelize connected [${name}]`);
141
+ return sequelize;
142
+ }
143
+ catch (error) {
144
+ this.logger.error(`❌ Failed to connect Sequelize [${name}]`, { error });
145
+ throw error;
107
146
  }
108
- const dialect = options.dialect ?? this.env_manager.getEnvVar("DB_DIALECT", "postgres");
109
- const logging_enabled = options.logging ?? this.env_manager.getEnvVar("DB_LOGGING", false);
110
- const logging = logging_enabled ? this.formatSQLQueryLog : false;
111
- const database = options.database ?? this.env_manager.getEnvVar("DB_NAME");
112
- const username = options.username ?? this.env_manager.getEnvVar("DB_USER");
113
- const password = options.password ?? this.env_manager.getEnvVar("DB_PASSWORD");
114
- const host = options.host ?? this.env_manager.getEnvVar("DB_HOST");
115
- const port = options.port ?? this.env_manager.getEnvVar("DB_PORT", 5432);
116
- // 🔴 IMPORTANT: Attempt to ensure DB exists BEFORE connecting
117
- this.ensureDatabaseExists({
118
- dialect,
119
- database,
120
- username,
121
- password,
122
- host,
123
- port,
124
- logging,
125
- }).catch(() => { });
126
- const sequelize = new sequelize_1.Sequelize({
127
- database,
128
- username,
129
- password,
130
- host,
131
- port,
132
- dialect,
133
- logging,
134
- benchmark: logging_enabled,
135
- });
136
- SequelizeConnector.connections.set(name, sequelize);
137
- this.logger.success(`✅ Sequelize connected [${name}]`);
138
- return sequelize;
139
147
  }
140
148
  // ----------------------------------
141
149
  // Utilities
@@ -4,7 +4,7 @@ declare class MigrationRunnerScript {
4
4
  private readonly migrations_dir;
5
5
  private readonly db_connection;
6
6
  private readonly logger;
7
- private readonly sequelize_instance;
7
+ private sequelize_instance;
8
8
  constructor();
9
9
  private getMigrationFiles;
10
10
  private ensureMetaTable;
@@ -14,10 +14,9 @@ class MigrationRunnerScript {
14
14
  migrations_dir = constants_1.MIGRATIONS_DIR;
15
15
  db_connection = main_1.SequelizeConnector?.getInstance?.();
16
16
  logger = new main_2.LoggerUtil(this.module_name);
17
- sequelize_instance;
17
+ sequelize_instance = null;
18
18
  constructor() {
19
19
  main_2.InputValidatorUtil.dirExists(this.migrations_dir, true);
20
- this.sequelize_instance = this.db_connection.connect();
21
20
  }
22
21
  // Fetch all migration files
23
22
  getMigrationFiles(schema_name) {
@@ -28,8 +27,8 @@ class MigrationRunnerScript {
28
27
  return files;
29
28
  }
30
29
  // Ensure SequelizeMeta table exists
31
- async ensureMetaTable() {
32
- const queryInterface = this.sequelize_instance.getQueryInterface();
30
+ async ensureMetaTable(sequelize_instance) {
31
+ const queryInterface = sequelize_instance.getQueryInterface();
33
32
  const table_names = await queryInterface.showAllTables();
34
33
  if (!table_names.includes(constants_1.SEQUELIZE_META_TABLE_NAME)) {
35
34
  this.logger.info("⏳ Creating SequelizeMeta table...");
@@ -44,12 +43,12 @@ class MigrationRunnerScript {
44
43
  }
45
44
  }
46
45
  // Fetch already applied migrations
47
- async getAppliedMigrations() {
48
- const [results] = await this.sequelize_instance.query(`SELECT name FROM \"${constants_1.SEQUELIZE_META_TABLE_NAME}\";`);
46
+ async getAppliedMigrations(sequelize_instance) {
47
+ const [results] = await sequelize_instance.query(`SELECT name FROM \"${constants_1.SEQUELIZE_META_TABLE_NAME}\";`);
49
48
  return Array.isArray(results) ? results.map((r) => r.name) : [];
50
49
  }
51
50
  // Apply a single migration
52
- async applyMigration(file_path, operation) {
51
+ async applyMigration(sequelize_instance, file_path, operation) {
53
52
  const MigrationClass = require(file_path)?.default;
54
53
  if (!MigrationClass) {
55
54
  const log_msg = `Migration file has no default export: ${file_path}`;
@@ -57,22 +56,24 @@ class MigrationRunnerScript {
57
56
  throw new Error(log_msg);
58
57
  }
59
58
  const migration_instance = new MigrationClass();
60
- const queryInterface = this.sequelize_instance.getQueryInterface();
59
+ const queryInterface = sequelize_instance.getQueryInterface();
61
60
  if (operation === "up") {
62
- await migration_instance.up.bind(migration_instance)(queryInterface, this.sequelize_instance);
63
- await this.sequelize_instance.getQueryInterface().bulkInsert(constants_1.SEQUELIZE_META_TABLE_NAME, [{ name: path_1.default.basename(file_path) }]);
61
+ await migration_instance.up.bind(migration_instance)(queryInterface, sequelize_instance);
62
+ await sequelize_instance.getQueryInterface().bulkInsert(constants_1.SEQUELIZE_META_TABLE_NAME, [{ name: path_1.default.basename(file_path) }]);
64
63
  }
65
64
  else {
66
- await migration_instance.down.bind(migration_instance)(queryInterface, this.sequelize_instance);
67
- await this.sequelize_instance.getQueryInterface().bulkDelete(constants_1.SEQUELIZE_META_TABLE_NAME, { name: path_1.default.basename(file_path) });
65
+ await migration_instance.down.bind(migration_instance)(queryInterface, sequelize_instance);
66
+ await sequelize_instance.getQueryInterface().bulkDelete(constants_1.SEQUELIZE_META_TABLE_NAME, { name: path_1.default.basename(file_path) });
68
67
  }
69
68
  }
70
69
  // Main runner
71
70
  async run(operation, schema_name, migration_file) {
72
71
  this.logger.info(`🏃‍♂️ Starting Migration process Operation: ${operation.toUpperCase()}, Schema: ${schema_name}, File: ${migration_file || "all"}`);
72
+ this.logger.info("🔌 Connecting to database...");
73
+ this.sequelize_instance = await this.db_connection.connect();
73
74
  await this.sequelize_instance?.authenticate();
74
75
  this.logger.success("✅ Database connected.");
75
- await this.ensureMetaTable();
76
+ await this.ensureMetaTable(this.sequelize_instance);
76
77
  let files = [];
77
78
  if (migration_file) {
78
79
  const full_path = path_1.default.join(this.migrations_dir, migration_file);
@@ -99,7 +100,7 @@ class MigrationRunnerScript {
99
100
  ? a_priority - b_priority
100
101
  : b_priority - a_priority;
101
102
  });
102
- const applied_migrations = await this.getAppliedMigrations();
103
+ const applied_migrations = await this.getAppliedMigrations(this.sequelize_instance);
103
104
  for (const file of files) {
104
105
  const file_name = path_1.default.basename(file);
105
106
  if (operation === "up" && applied_migrations.includes(file_name)) {
@@ -111,7 +112,7 @@ class MigrationRunnerScript {
111
112
  continue;
112
113
  }
113
114
  this.logger.info(`${operation === "up" ? "Applying" : "Reverting"} migration: ${file_name}`);
114
- await this.applyMigration(file, operation);
115
+ await this.applyMigration(this.sequelize_instance, file, operation);
115
116
  this.logger.success(`${operation === "up" ? "Applied" : "Reverted"} migration: ${file_name}`);
116
117
  }
117
118
  this.logger.success(`🏁 Migration process completed.`);
@@ -4,7 +4,7 @@ declare class SeederRunnerScript {
4
4
  private readonly seeders_dir;
5
5
  private readonly db_connection;
6
6
  private readonly logger;
7
- private readonly sequelize_instance;
7
+ private sequelize_instance;
8
8
  constructor();
9
9
  private getSeederFiles;
10
10
  private ensureMetaTable;
@@ -14,10 +14,9 @@ class SeederRunnerScript {
14
14
  seeders_dir = constants_1.SEEDERS_DIR;
15
15
  db_connection = main_1.SequelizeConnector?.getInstance?.();
16
16
  logger = new main_2.LoggerUtil(this.name);
17
- sequelize_instance;
17
+ sequelize_instance = null;
18
18
  constructor() {
19
19
  main_2.InputValidatorUtil.dirExists(this.seeders_dir, true);
20
- this.sequelize_instance = this.db_connection.connect();
21
20
  }
22
21
  // Get all seeder files
23
22
  getSeederFiles(seeder_name) {
@@ -28,8 +27,8 @@ class SeederRunnerScript {
28
27
  return files;
29
28
  }
30
29
  // Ensure SequelizeSeederMeta table exists
31
- async ensureMetaTable() {
32
- const queryInterface = this.sequelize_instance.getQueryInterface();
30
+ async ensureMetaTable(sequelize_instance) {
31
+ const queryInterface = sequelize_instance.getQueryInterface();
33
32
  const tables = await queryInterface.showAllTables();
34
33
  if (!tables.includes(constants_1.SEQUELIZE_SEEDER_META_TABLE_NAME)) {
35
34
  this.logger.info("⏳ Creating Sequelize seeder meta table table...");
@@ -40,24 +39,24 @@ class SeederRunnerScript {
40
39
  }
41
40
  }
42
41
  // Get already applied seeders
43
- async getAppliedSeeders() {
44
- const [results] = await this.sequelize_instance.query(`SELECT name FROM "${constants_1.SEQUELIZE_SEEDER_META_TABLE_NAME}";`);
42
+ async getAppliedSeeders(sequelize_instance) {
43
+ const [results] = await sequelize_instance.query(`SELECT name FROM "${constants_1.SEQUELIZE_SEEDER_META_TABLE_NAME}";`);
45
44
  return Array.isArray(results) ? results.map((r) => r.name) : [];
46
45
  }
47
46
  // Apply a single seeder
48
- async applySeeder(file_path, operation) {
47
+ async applySeeder(sequelize_instance, file_path, operation) {
49
48
  const SeederClass = require(file_path)?.default;
50
49
  if (!SeederClass) {
51
50
  throw new Error(`Seeder file has no default export: ${file_path}`);
52
51
  }
53
52
  const seeder_instance = new SeederClass();
54
- const queryInterface = this.sequelize_instance.getQueryInterface();
53
+ const queryInterface = sequelize_instance.getQueryInterface();
55
54
  if (operation === "up") {
56
- await seeder_instance.up(queryInterface, this.sequelize_instance);
55
+ await seeder_instance.up(queryInterface, sequelize_instance);
57
56
  await queryInterface.bulkInsert(constants_1.SEQUELIZE_SEEDER_META_TABLE_NAME, [{ name: path_1.default.basename(file_path) }]);
58
57
  }
59
58
  else {
60
- await seeder_instance.down(queryInterface, this.sequelize_instance);
59
+ await seeder_instance.down(queryInterface, sequelize_instance);
61
60
  await queryInterface.bulkDelete(constants_1.SEQUELIZE_SEEDER_META_TABLE_NAME, { name: path_1.default.basename(file_path) });
62
61
  }
63
62
  }
@@ -65,9 +64,11 @@ class SeederRunnerScript {
65
64
  async run(operation, seeder_name, seeder_file) {
66
65
  try {
67
66
  this.logger.info(`🏃‍♂️ Starting seeder process Operation: ${operation.toUpperCase()}, Seeder: ${seeder_name || "all"}, File: ${seeder_file || "none"}`);
67
+ this.logger.info("🔌 Connecting to database...");
68
+ this.sequelize_instance = await this.db_connection.connect();
68
69
  await this.sequelize_instance?.authenticate();
69
70
  this.logger.success("✅ Database connected.");
70
- await this.ensureMetaTable();
71
+ await this.ensureMetaTable(this.sequelize_instance);
71
72
  let files = [];
72
73
  if (seeder_file) {
73
74
  const full_path = path_1.default.join(this.seeders_dir, seeder_file);
@@ -82,7 +83,7 @@ class SeederRunnerScript {
82
83
  }
83
84
  // Sort by filename: ascending for UP, descending for DOWN
84
85
  files.sort((a, b) => (operation === "up" ? a.localeCompare(b) : b.localeCompare(a)));
85
- const applied_seeders = await this.getAppliedSeeders();
86
+ const applied_seeders = await this.getAppliedSeeders(this.sequelize_instance);
86
87
  for (const file of files) {
87
88
  const file_name = path_1.default.basename(file);
88
89
  if (operation === "up" && applied_seeders.includes(file_name)) {
@@ -94,7 +95,7 @@ class SeederRunnerScript {
94
95
  continue;
95
96
  }
96
97
  this.logger.info(`${operation === "up" ? "Applying" : "Reverting"} seeder: ${file_name}`);
97
- await this.applySeeder(file, operation);
98
+ await this.applySeeder(this.sequelize_instance, file, operation);
98
99
  this.logger.success(`${operation === "up" ? "Applied" : "Reverted"} seeder: ${file_name}`);
99
100
  }
100
101
  this.logger.success("🏁 Seeder process completed.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fiberx-backend-toolkit",
3
- "version": "0.0.26",
3
+ "version": "0.0.28",
4
4
  "description": "A TypeScript backend toolkit providing shared domain logic, infrastructure helpers, and utilities for FiberX server-side applications and services.",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",