@loomcore/api 0.1.48 → 0.1.50

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.
@@ -4,13 +4,13 @@ export declare class MigrationRunner {
4
4
  private dbUrl;
5
5
  private migrationsDir;
6
6
  private primaryTimezone;
7
- private mongoClient;
7
+ private dbConnection;
8
8
  constructor(config: IBaseApiConfig);
9
9
  private getTimestamp;
10
10
  private parseSql;
11
11
  private getMigrator;
12
12
  private wipeDatabase;
13
- run(command: 'up' | 'down' | 'reset'): Promise<void>;
13
+ run(command: 'up' | 'down' | 'reset', target?: string): Promise<void>;
14
14
  private closeConnection;
15
15
  create(name: string): Promise<void>;
16
16
  }
@@ -10,7 +10,7 @@ export class MigrationRunner {
10
10
  dbUrl;
11
11
  migrationsDir;
12
12
  primaryTimezone;
13
- mongoClient = null;
13
+ dbConnection;
14
14
  constructor(config) {
15
15
  this.dbType = config.app.dbType || 'mongodb';
16
16
  this.dbUrl = this.dbType === 'postgres' ? buildPostgresUrl(config) : buildMongoUrl(config);
@@ -33,27 +33,41 @@ export class MigrationRunner {
33
33
  const getPart = (type) => parts.find(p => p.type === type)?.value || '00';
34
34
  return `${getPart('year')}${getPart('month')}${getPart('day')}${getPart('hour')}${getPart('minute')}${getPart('second')}`;
35
35
  }
36
- parseSql(content) {
37
- const upMatch = content.match(/--\s*up\s([\s\S]+?)(?=--\s*down|$)/i);
38
- const downMatch = content.match(/--\s*down\s([\s\S]+)/i);
39
- return {
40
- up: upMatch ? upMatch[1].trim() : '',
41
- down: downMatch ? downMatch[1].trim() : ''
42
- };
36
+ parseSql(filename, content) {
37
+ const upMatch = content.match(/--\s*up\s+([\s\S]+?)(?=--\s*down|$)/i);
38
+ const downMatch = content.match(/--\s*down\s+([\s\S]+)/i);
39
+ const upSql = upMatch ? upMatch[1].trim() : '';
40
+ const downSql = downMatch ? downMatch[1].trim() : '';
41
+ if (!upSql && this.dbType === 'postgres') {
42
+ throw new Error(`❌ Parsing Error in ${filename}: Could not find '-- up' section or it was empty.`);
43
+ }
44
+ return { up: upSql, down: downSql };
43
45
  }
44
46
  async getMigrator() {
47
+ if (!fs.existsSync(this.migrationsDir)) {
48
+ throw new Error(`❌ Migrations directory not found at: ${this.migrationsDir}`);
49
+ }
45
50
  if (this.dbType === 'postgres') {
46
51
  const pool = new Pool({ connectionString: this.dbUrl });
52
+ this.dbConnection = pool;
53
+ const globPattern = path.join(this.migrationsDir, '*.sql').replace(/\\/g, '/');
54
+ console.log(`🔎 Looking for migrations in: ${globPattern}`);
47
55
  return new Umzug({
48
56
  migrations: {
49
- glob: path.join(this.migrationsDir, '*.sql'),
57
+ glob: globPattern,
50
58
  resolve: ({ name, path: filePath, context }) => {
51
59
  const content = fs.readFileSync(filePath, 'utf8');
52
- const { up, down } = this.parseSql(content);
60
+ const { up, down } = this.parseSql(name, content);
53
61
  return {
54
62
  name,
55
- up: async () => context.query(up),
56
- down: async () => context.query(down)
63
+ up: async () => {
64
+ console.log(` Running SQL for ${name}...`);
65
+ await context.query(up);
66
+ },
67
+ down: async () => {
68
+ console.log(` Running Undo SQL for ${name}...`);
69
+ await context.query(down);
70
+ }
57
71
  };
58
72
  }
59
73
  },
@@ -71,15 +85,17 @@ export class MigrationRunner {
71
85
  await context.query(`DELETE FROM migrations WHERE name = $1`, [name]);
72
86
  }
73
87
  },
74
- logger: console,
88
+ logger: undefined,
75
89
  });
76
90
  }
77
91
  else if (this.dbType === 'mongodb') {
78
92
  const client = await MongoClient.connect(this.dbUrl);
79
- this.mongoClient = client;
93
+ this.dbConnection = client;
80
94
  const db = client.db();
95
+ const globPattern = path.join(this.migrationsDir, '*.ts').replace(/\\/g, '/');
96
+ console.log(`🔎 Looking for migrations in: ${globPattern}`);
81
97
  return new Umzug({
82
- migrations: { glob: path.join(this.migrationsDir, '*.ts') },
98
+ migrations: { glob: globPattern },
83
99
  context: db,
84
100
  storage: new MongoDBStorage({ collection: db.collection('migrations') }),
85
101
  logger: console,
@@ -105,42 +121,62 @@ export class MigrationRunner {
105
121
  }
106
122
  console.log('✅ Database wiped.');
107
123
  }
108
- async run(command) {
124
+ async run(command, target) {
109
125
  try {
110
126
  if (command === 'reset') {
111
127
  await this.wipeDatabase();
112
128
  console.log('🚀 Restarting migrations...');
113
129
  const migrator = await this.getMigrator();
114
- await migrator.up();
115
- await this.closeConnection(migrator);
130
+ await migrator.up(target ? { to: target } : undefined);
131
+ await this.closeConnection();
116
132
  console.log('✅ Reset complete.');
117
133
  return;
118
134
  }
119
135
  const migrator = await this.getMigrator();
136
+ migrator.on('migrating', ({ name }) => console.log(`🚀 Migrating: ${name}`));
137
+ migrator.on('migrated', ({ name }) => console.log(`✅ Completed: ${name}`));
138
+ const pending = await migrator.pending();
139
+ console.log(`ℹ️ Found ${pending.length} pending migrations.`);
140
+ if (pending.length === 0 && command === 'up') {
141
+ console.log('⚠️ No pending migrations. (Check the path/glob if this is unexpected)');
142
+ }
120
143
  switch (command) {
121
144
  case 'up':
122
- await migrator.up();
123
- console.log('✅ Migrations up to date.');
145
+ await migrator.up(target ? { to: target } : undefined);
146
+ console.log(target ? `✅ Migrated up to ${target}` : '✅ Migrations up to date.');
124
147
  break;
125
148
  case 'down':
126
- await migrator.down();
127
- console.log('✅ Reverted last migration.');
149
+ if (target) {
150
+ await migrator.down({ to: target });
151
+ console.log(`✅ Reverted down to ${target}`);
152
+ }
153
+ else {
154
+ await migrator.down();
155
+ console.log('✅ Reverted last migration.');
156
+ }
128
157
  break;
129
158
  }
130
- await this.closeConnection(migrator);
159
+ await this.closeConnection();
131
160
  }
132
161
  catch (err) {
133
162
  console.error('❌ Migration failed:', err);
163
+ await this.closeConnection();
134
164
  process.exit(1);
135
165
  }
136
166
  }
137
- async closeConnection(migrator) {
138
- if (this.dbType === 'postgres') {
139
- await migrator.context.end();
167
+ async closeConnection() {
168
+ if (!this.dbConnection)
169
+ return;
170
+ try {
171
+ if (this.dbType === 'postgres') {
172
+ await this.dbConnection.end();
173
+ }
174
+ else if (this.dbType === 'mongo') {
175
+ await this.dbConnection.close();
176
+ }
140
177
  }
141
- else if (this.dbType === 'mongodb' && this.mongoClient) {
142
- await this.mongoClient.close();
143
- this.mongoClient = null;
178
+ catch (e) {
179
+ console.warn('Warning: Error closing connection', e);
144
180
  }
145
181
  }
146
182
  async create(name) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loomcore/api",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
4
4
  "private": false,
5
5
  "description": "Loom Core Api - An opinionated Node.js api using Typescript, Express, and MongoDb or PostgreSQL",
6
6
  "scripts": {