@fileverse/api 0.0.1 → 0.0.2

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.
@@ -48,6 +48,9 @@ function getRuntimeConfig() {
48
48
  get DB_PATH() {
49
49
  return process.env.DB_PATH;
50
50
  },
51
+ get DATABASE_URL() {
52
+ return process.env.DATABASE_URL;
53
+ },
51
54
  get PORT() {
52
55
  return process.env.PORT || STATIC_CONFIG.DEFAULT_PORT;
53
56
  },
@@ -85,6 +88,9 @@ var config = {
85
88
  get DB_PATH() {
86
89
  return process.env.DB_PATH;
87
90
  },
91
+ get DATABASE_URL() {
92
+ return process.env.DATABASE_URL;
93
+ },
88
94
  get PORT() {
89
95
  return process.env.PORT || STATIC_CONFIG.DEFAULT_PORT;
90
96
  },
@@ -171,9 +177,43 @@ var logger = {
171
177
  child: pinoInstance.child.bind(pinoInstance)
172
178
  };
173
179
 
174
- // src/infra/database/connection.ts
180
+ // src/infra/database/adapters/sqlite-adapter.ts
175
181
  import Database from "better-sqlite3";
176
182
 
183
+ // src/domain/file/constants.ts
184
+ var DEFAULT_LIST_LIMIT = 10;
185
+
186
+ // src/infra/database/query-builder.ts
187
+ var QueryBuilder = class {
188
+ static async select(sql, params = []) {
189
+ return getAdapterSync().select(sql, params);
190
+ }
191
+ static async selectOne(sql, params = []) {
192
+ return getAdapterSync().selectOne(sql, params);
193
+ }
194
+ static async execute(sql, params = []) {
195
+ return getAdapterSync().execute(sql, params);
196
+ }
197
+ static async transaction(callback) {
198
+ return getAdapterSync().transaction(callback);
199
+ }
200
+ static paginate(sql, options = {}) {
201
+ let query = sql;
202
+ if (options.orderBy) {
203
+ query += ` ORDER BY ${options.orderBy} ${options.orderDirection || "ASC"}`;
204
+ }
205
+ const hasOffset = (options.offset ?? 0) > 0;
206
+ const limit = options.limit ?? (hasOffset ? DEFAULT_LIST_LIMIT : void 0);
207
+ if (limit) {
208
+ query += ` LIMIT ${limit}`;
209
+ }
210
+ if (hasOffset) {
211
+ query += ` OFFSET ${options.offset}`;
212
+ }
213
+ return query;
214
+ }
215
+ };
216
+
177
217
  // src/infra/database/models/files.model.ts
178
218
  import { uuidv7 } from "uuidv7";
179
219
  var FilesModel = class {
@@ -207,15 +247,15 @@ var FilesModel = class {
207
247
  link: fileRaw.link
208
248
  };
209
249
  }
210
- static findAll(portalAddress, limit, skip) {
250
+ static async findAll(portalAddress, limit, skip) {
211
251
  const whereClause = "isDeleted = 0 AND portalAddress = ?";
212
252
  const params = [portalAddress];
213
253
  const countSql = `
214
- SELECT COUNT(*) as count
215
- FROM ${this.TABLE}
254
+ SELECT COUNT(*) as count
255
+ FROM ${this.TABLE}
216
256
  WHERE ${whereClause}
217
257
  `;
218
- const totalResult = QueryBuilder.selectOne(countSql, params);
258
+ const totalResult = await QueryBuilder.selectOne(countSql, params);
219
259
  const total = totalResult?.count || 0;
220
260
  const sql = `
221
261
  SELECT *
@@ -228,51 +268,51 @@ var FilesModel = class {
228
268
  orderBy: "createdAt",
229
269
  orderDirection: "DESC"
230
270
  });
231
- const filesRaw = QueryBuilder.select(completeSql, params);
271
+ const filesRaw = await QueryBuilder.select(completeSql, params);
232
272
  const files = filesRaw.map(this.parseFile);
233
273
  const hasNext = skip !== void 0 && limit !== void 0 ? skip + limit < total : false;
234
274
  return { files, total, hasNext };
235
275
  }
236
- static findById(_id, portalAddress) {
276
+ static async findById(_id, portalAddress) {
237
277
  const sql = `
238
278
  SELECT *
239
- FROM ${this.TABLE}
279
+ FROM ${this.TABLE}
240
280
  WHERE _id = ? AND isDeleted = 0 AND portalAddress = ?
241
281
  `;
242
- const result = QueryBuilder.selectOne(sql, [_id, portalAddress]);
282
+ const result = await QueryBuilder.selectOne(sql, [_id, portalAddress]);
243
283
  return result ? this.parseFile(result) : void 0;
244
284
  }
245
- static findByIdIncludingDeleted(_id) {
285
+ static async findByIdIncludingDeleted(_id) {
246
286
  const sql = `
247
287
  SELECT *
248
- FROM ${this.TABLE}
288
+ FROM ${this.TABLE}
249
289
  WHERE _id = ?
250
290
  `;
251
- const result = QueryBuilder.selectOne(sql, [_id]);
291
+ const result = await QueryBuilder.selectOne(sql, [_id]);
252
292
  return result ? this.parseFile(result) : void 0;
253
293
  }
254
- static findByIdExcludingDeleted(_id) {
294
+ static async findByIdExcludingDeleted(_id) {
255
295
  const sql = `
256
296
  SELECT *
257
- FROM ${this.TABLE}
297
+ FROM ${this.TABLE}
258
298
  WHERE _id = ? AND isDeleted = 0
259
299
  `;
260
- const result = QueryBuilder.selectOne(sql, [_id]);
300
+ const result = await QueryBuilder.selectOne(sql, [_id]);
261
301
  return result ? this.parseFile(result) : void 0;
262
302
  }
263
- static findByDDocId(ddocId, portalAddress) {
303
+ static async findByDDocId(ddocId, portalAddress) {
264
304
  const sql = `
265
305
  SELECT *
266
- FROM ${this.TABLE}
306
+ FROM ${this.TABLE}
267
307
  WHERE ddocId = ? AND isDeleted = 0 AND portalAddress = ?
268
308
  `;
269
- const result = QueryBuilder.selectOne(sql, [ddocId, portalAddress]);
309
+ const result = await QueryBuilder.selectOne(sql, [ddocId, portalAddress]);
270
310
  return result ? this.parseFile(result) : void 0;
271
311
  }
272
- static searchByTitle(searchTerm, portalAddress, limit, skip) {
312
+ static async searchByTitle(searchTerm, portalAddress, limit, skip) {
273
313
  const sql = `
274
314
  SELECT *
275
- FROM ${this.TABLE}
315
+ FROM ${this.TABLE}
276
316
  WHERE LOWER(title) LIKE LOWER(?) AND isDeleted = 0 AND portalAddress = ?
277
317
  `;
278
318
  const completeSql = QueryBuilder.paginate(sql, {
@@ -281,24 +321,24 @@ var FilesModel = class {
281
321
  orderBy: "createdAt",
282
322
  orderDirection: "DESC"
283
323
  });
284
- const filesRaw = QueryBuilder.select(completeSql, [`%${searchTerm}%`, portalAddress]);
324
+ const filesRaw = await QueryBuilder.select(completeSql, [`%${searchTerm}%`, portalAddress]);
285
325
  return filesRaw.map(this.parseFile);
286
326
  }
287
- static create(input) {
327
+ static async create(input) {
288
328
  const _id = uuidv7();
289
329
  const sql = `
290
- INSERT INTO ${this.TABLE}
291
- (_id, title, content, ddocId, portalAddress)
330
+ INSERT INTO ${this.TABLE}
331
+ (_id, title, content, ddocId, portalAddress)
292
332
  VALUES (?, ?, ?, ?, ?)
293
333
  `;
294
- QueryBuilder.execute(sql, [_id, input.title, input.content, input.ddocId, input.portalAddress]);
295
- const created = this.findById(_id, input.portalAddress);
334
+ await QueryBuilder.execute(sql, [_id, input.title, input.content, input.ddocId, input.portalAddress]);
335
+ const created = await this.findById(_id, input.portalAddress);
296
336
  if (!created) {
297
337
  throw new Error("Failed to create file");
298
338
  }
299
339
  return created;
300
340
  }
301
- static update(_id, payload, portalAddress) {
341
+ static async update(_id, payload, portalAddress) {
302
342
  const now = (/* @__PURE__ */ new Date()).toISOString();
303
343
  const keys = [];
304
344
  const values = [];
@@ -317,22 +357,22 @@ var FilesModel = class {
317
357
  values.push(now, _id, portalAddress);
318
358
  const updateChain = keys.join(", ");
319
359
  const sql = `UPDATE ${this.TABLE} SET ${updateChain} WHERE _id = ? AND portalAddress = ?`;
320
- QueryBuilder.execute(sql, values);
321
- const updated = this.findById(_id, portalAddress);
360
+ await QueryBuilder.execute(sql, values);
361
+ const updated = await this.findById(_id, portalAddress);
322
362
  if (!updated) {
323
363
  throw new Error("Failed to update file");
324
364
  }
325
365
  return updated;
326
366
  }
327
- static softDelete(_id) {
367
+ static async softDelete(_id) {
328
368
  const now = (/* @__PURE__ */ new Date()).toISOString();
329
369
  const sql = `
330
- UPDATE ${this.TABLE}
370
+ UPDATE ${this.TABLE}
331
371
  SET isDeleted = 1, syncStatus = 'pending', updatedAt = ?
332
372
  WHERE _id = ?
333
373
  `;
334
- QueryBuilder.execute(sql, [now, _id]);
335
- const deleted = this.findByIdIncludingDeleted(_id);
374
+ await QueryBuilder.execute(sql, [now, _id]);
375
+ const deleted = await this.findByIdIncludingDeleted(_id);
336
376
  if (!deleted) {
337
377
  throw new Error("Failed to delete file");
338
378
  }
@@ -347,12 +387,12 @@ import { uuidv7 as uuidv72 } from "uuidv7";
347
387
  import { uuidv7 as uuidv73 } from "uuidv7";
348
388
  var ApiKeysModel = class {
349
389
  static TABLE = "api_keys";
350
- static create(input) {
390
+ static async create(input) {
351
391
  const _id = uuidv73();
352
392
  const now = (/* @__PURE__ */ new Date()).toISOString();
353
- const sql = `INSERT INTO ${this.TABLE} (_id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt)
393
+ const sql = `INSERT INTO ${this.TABLE} (_id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt)
354
394
  VALUES (?, ?, ?, ?, ?, ?)`;
355
- const result = QueryBuilder.execute(sql, [
395
+ const result = await QueryBuilder.execute(sql, [
356
396
  _id,
357
397
  input.apiKeySeed,
358
398
  input.name,
@@ -363,29 +403,29 @@ var ApiKeysModel = class {
363
403
  if (result.changes === 0) {
364
404
  throw new Error("Failed to create API key");
365
405
  }
366
- const created = this.findById(_id);
406
+ const created = await this.findById(_id);
367
407
  if (!created) {
368
408
  throw new Error("Failed to create API key");
369
409
  }
370
410
  return created;
371
411
  }
372
- static findById(_id) {
412
+ static async findById(_id) {
373
413
  const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE _id = ? AND isDeleted = 0`;
374
414
  return QueryBuilder.selectOne(sql, [_id]);
375
415
  }
376
- static findByCollaboratorAddress(collaboratorAddress) {
416
+ static async findByCollaboratorAddress(collaboratorAddress) {
377
417
  const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE collaboratorAddress = ? AND isDeleted = 0 LIMIT 1`;
378
418
  return QueryBuilder.selectOne(sql, [collaboratorAddress]);
379
419
  }
380
- static delete(_id) {
420
+ static async delete(_id) {
381
421
  const sql = `UPDATE ${this.TABLE} SET isDeleted = 1 WHERE _id = ?`;
382
- QueryBuilder.execute(sql, [_id]);
422
+ await QueryBuilder.execute(sql, [_id]);
383
423
  }
384
- static findByPortalAddress(portalAddress) {
424
+ static async findByPortalAddress(portalAddress) {
385
425
  const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE portalAddress = ? AND isDeleted = 0`;
386
426
  return QueryBuilder.selectOne(sql, [portalAddress]);
387
427
  }
388
- static findByApiKey(apiKey) {
428
+ static async findByApiKey(apiKey) {
389
429
  const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE apiKeySeed = ? AND isDeleted = 0`;
390
430
  return QueryBuilder.selectOne(sql, [apiKey]);
391
431
  }
@@ -408,16 +448,16 @@ function notifyNewEvent() {
408
448
  var RETRY_DELAYS_MS = [5e3, 3e4, 12e4];
409
449
  var EventsModel = class {
410
450
  static TABLE = "events";
411
- static create(input) {
451
+ static async create(input) {
412
452
  const _id = uuidv74();
413
453
  const timestamp = Date.now();
414
454
  const status = "pending";
415
455
  const sql = `
416
- INSERT INTO ${this.TABLE}
417
- (_id, type, timestamp, fileId, portalAddress, status, retryCount, lastError, lockedAt, nextRetryAt)
456
+ INSERT INTO ${this.TABLE}
457
+ (_id, type, timestamp, fileId, portalAddress, status, retryCount, lastError, lockedAt, nextRetryAt)
418
458
  VALUES (?, ?, ?, ?, ?, ?, 0, NULL, NULL, NULL)
419
459
  `;
420
- QueryBuilder.execute(sql, [_id, input.type, timestamp, input.fileId, input.portalAddress, status]);
460
+ await QueryBuilder.execute(sql, [_id, input.type, timestamp, input.fileId, input.portalAddress, status]);
421
461
  notifyNewEvent();
422
462
  return {
423
463
  _id,
@@ -432,22 +472,22 @@ var EventsModel = class {
432
472
  nextRetryAt: null
433
473
  };
434
474
  }
435
- static findById(_id) {
475
+ static async findById(_id) {
436
476
  const sql = `SELECT * FROM ${this.TABLE} WHERE _id = ?`;
437
- const row = QueryBuilder.selectOne(sql, [_id]);
477
+ const row = await QueryBuilder.selectOne(sql, [_id]);
438
478
  return row ? this.parseEvent(row) : void 0;
439
479
  }
440
- static findNextPending() {
480
+ static async findNextPending() {
441
481
  const sql = `
442
482
  SELECT * FROM ${this.TABLE}
443
483
  WHERE status = 'pending'
444
484
  ORDER BY timestamp ASC
445
485
  LIMIT 1
446
486
  `;
447
- const row = QueryBuilder.selectOne(sql, []);
487
+ const row = await QueryBuilder.selectOne(sql, []);
448
488
  return row ? this.parseEvent(row) : void 0;
449
489
  }
450
- static findNextEligible(lockedFileIds) {
490
+ static async findNextEligible(lockedFileIds) {
451
491
  const now = Date.now();
452
492
  const exclusionClause = lockedFileIds.length > 0 ? `AND e1.fileId NOT IN (${lockedFileIds.map(() => "?").join(", ")})` : "";
453
493
  const sql = `
@@ -465,29 +505,29 @@ var EventsModel = class {
465
505
  LIMIT 1
466
506
  `;
467
507
  const params = [now, ...lockedFileIds];
468
- const row = QueryBuilder.selectOne(sql, params);
508
+ const row = await QueryBuilder.selectOne(sql, params);
469
509
  return row ? this.parseEvent(row) : void 0;
470
510
  }
471
- static markProcessing(_id) {
511
+ static async markProcessing(_id) {
472
512
  const sql = `
473
513
  UPDATE ${this.TABLE}
474
514
  SET status = 'processing',
475
515
  lockedAt = ?
476
516
  WHERE _id = ?
477
517
  `;
478
- QueryBuilder.execute(sql, [Date.now(), _id]);
518
+ await QueryBuilder.execute(sql, [Date.now(), _id]);
479
519
  }
480
- static markProcessed(_id) {
520
+ static async markProcessed(_id) {
481
521
  const sql = `
482
522
  UPDATE ${this.TABLE}
483
523
  SET status = 'processed',
484
524
  lockedAt = NULL
485
525
  WHERE _id = ?
486
526
  `;
487
- QueryBuilder.execute(sql, [_id]);
527
+ await QueryBuilder.execute(sql, [_id]);
488
528
  }
489
- static scheduleRetry(_id, errorMsg) {
490
- const event = this.findById(_id);
529
+ static async scheduleRetry(_id, errorMsg) {
530
+ const event = await this.findById(_id);
491
531
  if (!event) return;
492
532
  const delay = RETRY_DELAYS_MS[Math.min(event.retryCount, RETRY_DELAYS_MS.length - 1)];
493
533
  const nextRetryAt = Date.now() + delay;
@@ -500,9 +540,9 @@ var EventsModel = class {
500
540
  lockedAt = NULL
501
541
  WHERE _id = ?
502
542
  `;
503
- QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
543
+ await QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
504
544
  }
505
- static scheduleRetryAfter(_id, errorMsg, retryAfterMs) {
545
+ static async scheduleRetryAfter(_id, errorMsg, retryAfterMs) {
506
546
  const nextRetryAt = Date.now() + retryAfterMs;
507
547
  const sql = `
508
548
  UPDATE ${this.TABLE}
@@ -512,9 +552,9 @@ var EventsModel = class {
512
552
  lockedAt = NULL
513
553
  WHERE _id = ?
514
554
  `;
515
- QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
555
+ await QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
516
556
  }
517
- static markFailed(_id, errorMsg) {
557
+ static async markFailed(_id, errorMsg) {
518
558
  const sql = `
519
559
  UPDATE ${this.TABLE}
520
560
  SET status = 'failed',
@@ -522,9 +562,9 @@ var EventsModel = class {
522
562
  lockedAt = NULL
523
563
  WHERE _id = ?
524
564
  `;
525
- QueryBuilder.execute(sql, [errorMsg, _id]);
565
+ await QueryBuilder.execute(sql, [errorMsg, _id]);
526
566
  }
527
- static listFailed(portalAddress) {
567
+ static async listFailed(portalAddress) {
528
568
  const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
529
569
  const sql = `
530
570
  SELECT * FROM ${this.TABLE}
@@ -533,10 +573,10 @@ var EventsModel = class {
533
573
  ORDER BY timestamp ASC
534
574
  `;
535
575
  const params = portalAddress != null ? [portalAddress] : [];
536
- const rows = QueryBuilder.select(sql, params);
576
+ const rows = await QueryBuilder.select(sql, params);
537
577
  return rows.map((row) => this.parseEvent(row));
538
578
  }
539
- static resetFailedToPending(_id, portalAddress) {
579
+ static async resetFailedToPending(_id, portalAddress) {
540
580
  const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
541
581
  const sql = `
542
582
  UPDATE ${this.TABLE}
@@ -550,13 +590,13 @@ var EventsModel = class {
550
590
  ${portalClause}
551
591
  `;
552
592
  const params = portalAddress != null ? [_id, portalAddress] : [_id];
553
- const result = QueryBuilder.execute(sql, params);
593
+ const result = await QueryBuilder.execute(sql, params);
554
594
  if (result.changes > 0) {
555
595
  notifyNewEvent();
556
596
  }
557
597
  return result.changes > 0;
558
598
  }
559
- static resetAllFailedToPending(portalAddress) {
599
+ static async resetAllFailedToPending(portalAddress) {
560
600
  const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
561
601
  const sql = `
562
602
  UPDATE ${this.TABLE}
@@ -569,13 +609,13 @@ var EventsModel = class {
569
609
  ${portalClause}
570
610
  `;
571
611
  const params = portalAddress != null ? [portalAddress] : [];
572
- const result = QueryBuilder.execute(sql, params);
612
+ const result = await QueryBuilder.execute(sql, params);
573
613
  if (result.changes > 0) {
574
614
  notifyNewEvent();
575
615
  }
576
616
  return result.changes;
577
617
  }
578
- static resetStaleEvents(staleThreshold) {
618
+ static async resetStaleEvents(staleThreshold) {
579
619
  const sql = `
580
620
  UPDATE ${this.TABLE}
581
621
  SET status = 'pending',
@@ -586,16 +626,16 @@ var EventsModel = class {
586
626
  AND lockedAt IS NOT NULL
587
627
  AND lockedAt < ?
588
628
  `;
589
- const result = QueryBuilder.execute(sql, [staleThreshold]);
629
+ const result = await QueryBuilder.execute(sql, [staleThreshold]);
590
630
  return result.changes;
591
631
  }
592
- static setEventPendingOp(_id, userOpHash, payload) {
632
+ static async setEventPendingOp(_id, userOpHash, payload) {
593
633
  const sql = `UPDATE ${this.TABLE} SET userOpHash = ?, pendingPayload = ? WHERE _id = ?`;
594
- QueryBuilder.execute(sql, [userOpHash, JSON.stringify(payload), _id]);
634
+ await QueryBuilder.execute(sql, [userOpHash, JSON.stringify(payload), _id]);
595
635
  }
596
- static clearEventPendingOp(_id) {
636
+ static async clearEventPendingOp(_id) {
597
637
  const sql = `UPDATE ${this.TABLE} SET userOpHash = NULL, pendingPayload = NULL WHERE _id = ?`;
598
- QueryBuilder.execute(sql, [_id]);
638
+ await QueryBuilder.execute(sql, [_id]);
599
639
  }
600
640
  static parseEvent(row) {
601
641
  return {
@@ -701,96 +741,219 @@ var Reporter = class {
701
741
  };
702
742
  var reporter_default = new Reporter();
703
743
 
704
- // src/infra/database/connection.ts
705
- var DatabaseConnectionManager = class _DatabaseConnectionManager {
706
- static instance;
707
- db = null;
708
- constructor() {
709
- }
710
- static getInstance() {
711
- if (!_DatabaseConnectionManager.instance) {
712
- _DatabaseConnectionManager.instance = new _DatabaseConnectionManager();
713
- }
714
- return _DatabaseConnectionManager.instance;
715
- }
716
- getConnection() {
717
- if (!this.db) {
718
- const dbPath = config.DB_PATH;
719
- this.db = new Database(dbPath, {
720
- verbose: config.NODE_ENV === "development" ? (msg) => logger.debug(String(msg)) : void 0
721
- });
722
- this.db.pragma("journal_mode = WAL");
723
- this.db.pragma("foreign_keys = ON");
724
- this.db.prepare("SELECT 1").get();
725
- logger.info(`SQLite database connected: ${dbPath}`);
726
- }
727
- return this.db;
728
- }
729
- async close() {
730
- if (this.db) {
731
- this.db.close();
732
- this.db = null;
733
- logger.info("Database connection closed");
734
- }
735
- }
736
- isConnected() {
737
- return this.db !== null && this.db.open;
744
+ // src/infra/database/adapters/sqlite-adapter.ts
745
+ var SqliteAdapter = class {
746
+ db;
747
+ constructor(dbPath) {
748
+ this.db = new Database(dbPath, {
749
+ verbose: config.NODE_ENV === "development" ? (msg) => logger.debug(String(msg)) : void 0
750
+ });
751
+ this.db.pragma("journal_mode = WAL");
752
+ this.db.pragma("foreign_keys = ON");
753
+ this.db.prepare("SELECT 1").get();
754
+ logger.info(`SQLite database connected: ${dbPath}`);
738
755
  }
739
- };
740
- var databaseConnectionManager = DatabaseConnectionManager.getInstance();
741
-
742
- // src/domain/file/constants.ts
743
- var DEFAULT_LIST_LIMIT = 10;
744
-
745
- // src/infra/database/query-builder.ts
746
- function getDb() {
747
- return databaseConnectionManager.getConnection();
748
- }
749
- var QueryBuilder = class {
750
- static select(sql, params = []) {
751
- const stmt = getDb().prepare(sql);
756
+ async select(sql, params = []) {
757
+ const stmt = this.db.prepare(sql);
752
758
  return stmt.all(params);
753
759
  }
754
- static selectOne(sql, params = []) {
755
- const stmt = getDb().prepare(sql);
760
+ async selectOne(sql, params = []) {
761
+ const stmt = this.db.prepare(sql);
756
762
  return stmt.get(params);
757
763
  }
758
- static execute(sql, params = []) {
759
- const stmt = getDb().prepare(sql);
764
+ async execute(sql, params = []) {
765
+ const stmt = this.db.prepare(sql);
760
766
  const result = stmt.run(params);
761
767
  return {
762
768
  changes: result.changes,
763
769
  lastInsertRowid: result.lastInsertRowid
764
770
  };
765
771
  }
766
- static transaction(callback) {
767
- return getDb().transaction(callback)();
768
- }
769
- static paginate(sql, options = {}) {
770
- let query = sql;
771
- if (options.orderBy) {
772
- query += ` ORDER BY ${options.orderBy} ${options.orderDirection || "ASC"}`;
772
+ async transaction(callback) {
773
+ this.db.exec("SAVEPOINT txn");
774
+ try {
775
+ const result = await callback(this);
776
+ this.db.exec("RELEASE txn");
777
+ return result;
778
+ } catch (err) {
779
+ this.db.exec("ROLLBACK TO txn");
780
+ throw err;
773
781
  }
774
- const hasOffset = (options.offset ?? 0) > 0;
775
- const limit = options.limit ?? (hasOffset ? DEFAULT_LIST_LIMIT : void 0);
776
- if (limit) {
777
- query += ` LIMIT ${limit}`;
782
+ }
783
+ async exec(sql) {
784
+ this.db.exec(sql);
785
+ }
786
+ async close() {
787
+ this.db.close();
788
+ logger.info("Database connection closed");
789
+ }
790
+ };
791
+
792
+ // src/infra/database/adapters/pg-adapter.ts
793
+ import pg from "pg";
794
+ var { Pool } = pg;
795
+ var COLUMN_NAME_MAP = {
796
+ ddocid: "ddocId",
797
+ localversion: "localVersion",
798
+ onchainversion: "onchainVersion",
799
+ syncstatus: "syncStatus",
800
+ isdeleted: "isDeleted",
801
+ onchainfileid: "onChainFileId",
802
+ portaladdress: "portalAddress",
803
+ createdat: "createdAt",
804
+ updatedat: "updatedAt",
805
+ linkkey: "linkKey",
806
+ linkkeynonce: "linkKeyNonce",
807
+ commentkey: "commentKey",
808
+ portalseed: "portalSeed",
809
+ owneraddress: "ownerAddress",
810
+ apikeyseed: "apiKeySeed",
811
+ collaboratoraddress: "collaboratorAddress",
812
+ fileid: "fileId",
813
+ retrycount: "retryCount",
814
+ lasterror: "lastError",
815
+ lockedat: "lockedAt",
816
+ nextretryat: "nextRetryAt",
817
+ userophash: "userOpHash",
818
+ pendingpayload: "pendingPayload",
819
+ folderid: "folderId",
820
+ folderref: "folderRef",
821
+ foldername: "folderName",
822
+ metadataipfshash: "metadataIPFSHash",
823
+ contentipfshash: "contentIPFSHash",
824
+ lasttransactionhash: "lastTransactionHash",
825
+ lasttransactionblocknumber: "lastTransactionBlockNumber",
826
+ lasttransactionblocktimestamp: "lastTransactionBlockTimestamp",
827
+ created_at: "created_at",
828
+ updated_at: "updated_at"
829
+ };
830
+ function translateParams(sql) {
831
+ let idx = 0;
832
+ return sql.replace(/\?/g, () => `$${++idx}`);
833
+ }
834
+ function mapRow(row) {
835
+ const mapped = {};
836
+ for (const [key, value] of Object.entries(row)) {
837
+ const mappedKey = COLUMN_NAME_MAP[key] ?? key;
838
+ mapped[mappedKey] = value instanceof Date ? value.toISOString() : value;
839
+ }
840
+ return mapped;
841
+ }
842
+ var PgClientAdapter = class {
843
+ constructor(client) {
844
+ this.client = client;
845
+ }
846
+ async select(sql, params = []) {
847
+ const result = await this.client.query(translateParams(sql), params);
848
+ return result.rows.map((row) => mapRow(row));
849
+ }
850
+ async selectOne(sql, params = []) {
851
+ const result = await this.client.query(translateParams(sql), params);
852
+ return result.rows[0] ? mapRow(result.rows[0]) : void 0;
853
+ }
854
+ async execute(sql, params = []) {
855
+ const result = await this.client.query(translateParams(sql), params);
856
+ return { changes: result.rowCount ?? 0, lastInsertRowid: 0 };
857
+ }
858
+ async transaction(callback) {
859
+ await this.client.query("SAVEPOINT nested_txn");
860
+ try {
861
+ const result = await callback(this);
862
+ await this.client.query("RELEASE SAVEPOINT nested_txn");
863
+ return result;
864
+ } catch (err) {
865
+ await this.client.query("ROLLBACK TO SAVEPOINT nested_txn");
866
+ throw err;
778
867
  }
779
- if (hasOffset) {
780
- query += ` OFFSET ${options.offset}`;
868
+ }
869
+ async exec(sql) {
870
+ await this.client.query(sql);
871
+ }
872
+ async close() {
873
+ }
874
+ };
875
+ var PgAdapter = class {
876
+ pool;
877
+ constructor(connectionString) {
878
+ const url = new URL(connectionString);
879
+ const isLocal = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1";
880
+ const sslDisabled = connectionString.includes("sslmode=disable");
881
+ const needsSsl = !isLocal && !sslDisabled;
882
+ this.pool = new Pool({
883
+ connectionString,
884
+ // pg requires password to be a string; local trust/peer auth uses empty string
885
+ password: url.password || "",
886
+ max: 20,
887
+ idleTimeoutMillis: 3e4,
888
+ ssl: needsSsl ? { rejectUnauthorized: false } : void 0
889
+ });
890
+ logger.info(`PostgreSQL pool created (ssl: ${needsSsl ? "on" : "off"})`);
891
+ }
892
+ async select(sql, params = []) {
893
+ const result = await this.pool.query(translateParams(sql), params);
894
+ return result.rows.map((row) => mapRow(row));
895
+ }
896
+ async selectOne(sql, params = []) {
897
+ const result = await this.pool.query(translateParams(sql), params);
898
+ return result.rows[0] ? mapRow(result.rows[0]) : void 0;
899
+ }
900
+ async execute(sql, params = []) {
901
+ const result = await this.pool.query(translateParams(sql), params);
902
+ return { changes: result.rowCount ?? 0, lastInsertRowid: 0 };
903
+ }
904
+ async transaction(callback) {
905
+ const client = await this.pool.connect();
906
+ try {
907
+ await client.query("BEGIN");
908
+ const clientAdapter = new PgClientAdapter(client);
909
+ const result = await callback(clientAdapter);
910
+ await client.query("COMMIT");
911
+ return result;
912
+ } catch (err) {
913
+ await client.query("ROLLBACK");
914
+ throw err;
915
+ } finally {
916
+ client.release();
781
917
  }
782
- return query;
918
+ }
919
+ async exec(sql) {
920
+ await this.pool.query(sql);
921
+ }
922
+ async close() {
923
+ await this.pool.end();
924
+ logger.info("PostgreSQL pool closed");
783
925
  }
784
926
  };
785
927
 
786
- // src/infra/database/index.ts
787
- function getDb2() {
788
- return databaseConnectionManager.getConnection();
928
+ // src/infra/database/connection.ts
929
+ var adapter = null;
930
+ async function getAdapter() {
931
+ if (adapter) return adapter;
932
+ const databaseUrl = config.DATABASE_URL;
933
+ const dbPath = config.DB_PATH;
934
+ if (databaseUrl) {
935
+ adapter = new PgAdapter(databaseUrl);
936
+ } else if (dbPath) {
937
+ adapter = new SqliteAdapter(dbPath);
938
+ } else {
939
+ throw new Error("Either DATABASE_URL or DB_PATH must be set");
940
+ }
941
+ return adapter;
942
+ }
943
+ function getAdapterSync() {
944
+ if (!adapter) {
945
+ throw new Error(
946
+ "Database adapter not initialized. Call getAdapter() at startup first."
947
+ );
948
+ }
949
+ return adapter;
950
+ }
951
+ async function closeAdapter() {
952
+ if (adapter) {
953
+ await adapter.close();
954
+ adapter = null;
955
+ }
789
956
  }
790
- var closeDatabase = async () => {
791
- await databaseConnectionManager.close();
792
- };
793
- var database_default = getDb2;
794
957
 
795
958
  // src/infra/database/migrations/index.ts
796
959
  var STABLE_SCHEMA = `
@@ -802,8 +965,8 @@ CREATE TABLE IF NOT EXISTS files (
802
965
  localVersion INTEGER NOT NULL DEFAULT 1,
803
966
  onchainVersion INTEGER NOT NULL DEFAULT 0,
804
967
  syncStatus TEXT NOT NULL DEFAULT 'pending',
805
- createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
806
- updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
968
+ createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
969
+ updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
807
970
  isDeleted INTEGER NOT NULL DEFAULT 0,
808
971
  portalAddress TEXT NOT NULL,
809
972
  metadata TEXT DEFAULT '{}',
@@ -823,8 +986,8 @@ CREATE TABLE IF NOT EXISTS portals (
823
986
  portalAddress TEXT NOT NULL UNIQUE,
824
987
  portalSeed TEXT NOT NULL UNIQUE,
825
988
  ownerAddress TEXT NOT NULL,
826
- createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
827
- updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
989
+ createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
990
+ updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
828
991
  );
829
992
 
830
993
  CREATE TABLE IF NOT EXISTS api_keys (
@@ -833,7 +996,7 @@ CREATE TABLE IF NOT EXISTS api_keys (
833
996
  name TEXT NOT NULL,
834
997
  collaboratorAddress TEXT NOT NULL UNIQUE,
835
998
  portalAddress TEXT NOT NULL,
836
- createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
999
+ createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
837
1000
  isDeleted INTEGER NOT NULL DEFAULT 0
838
1001
  );
839
1002
 
@@ -869,16 +1032,16 @@ CREATE TABLE IF NOT EXISTS folders (
869
1032
  lastTransactionHash TEXT,
870
1033
  lastTransactionBlockNumber INTEGER NOT NULL,
871
1034
  lastTransactionBlockTimestamp INTEGER NOT NULL,
872
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
873
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
1035
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
1036
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
874
1037
  );
875
1038
  CREATE INDEX IF NOT EXISTS idx_folders_folderRef_folderId ON folders(folderRef, folderId);
876
1039
  CREATE INDEX IF NOT EXISTS idx_folders_folderRef ON folders(folderRef);
877
1040
  CREATE INDEX IF NOT EXISTS idx_folders_created_at ON folders(created_at);
878
1041
  `;
879
- function runMigrations() {
880
- const db = database_default();
881
- db.exec(STABLE_SCHEMA);
1042
+ async function runMigrations() {
1043
+ const adapter2 = getAdapterSync();
1044
+ await adapter2.exec(STABLE_SCHEMA);
882
1045
  logger.debug("Database schema ready");
883
1046
  }
884
1047
 
@@ -888,10 +1051,10 @@ import Table from "cli-table3";
888
1051
 
889
1052
  // src/domain/file/index.ts
890
1053
  import { generate } from "short-uuid";
891
- function listFiles(params) {
1054
+ async function listFiles(params) {
892
1055
  const { limit, skip, portalAddress } = params;
893
1056
  const effectiveLimit = limit || DEFAULT_LIST_LIMIT;
894
- const result = FilesModel.findAll(portalAddress, effectiveLimit, skip);
1057
+ const result = await FilesModel.findAll(portalAddress, effectiveLimit, skip);
895
1058
  const processedFiles = result.files.map((file) => ({
896
1059
  ddocId: file.ddocId,
897
1060
  link: file.link,
@@ -912,11 +1075,11 @@ function listFiles(params) {
912
1075
  hasNext: result.hasNext
913
1076
  };
914
1077
  }
915
- function getFile(ddocId, portalAddress) {
1078
+ async function getFile(ddocId, portalAddress) {
916
1079
  if (!ddocId) {
917
1080
  throw new Error("ddocId is required");
918
1081
  }
919
- const file = FilesModel.findByDDocId(ddocId, portalAddress);
1082
+ const file = await FilesModel.findByDDocId(ddocId, portalAddress);
920
1083
  if (!file) {
921
1084
  return null;
922
1085
  }
@@ -940,13 +1103,13 @@ var createFile = async (input) => {
940
1103
  throw new Error("title, content, and portalAddress are required");
941
1104
  }
942
1105
  const ddocId = generate();
943
- const file = FilesModel.create({
1106
+ const file = await FilesModel.create({
944
1107
  title: input.title,
945
1108
  content: input.content,
946
1109
  ddocId,
947
1110
  portalAddress: input.portalAddress
948
1111
  });
949
- EventsModel.create({ type: "create", fileId: file._id, portalAddress: file.portalAddress });
1112
+ await EventsModel.create({ type: "create", fileId: file._id, portalAddress: file.portalAddress });
950
1113
  return file;
951
1114
  };
952
1115
  var updateFile = async (ddocId, payload, portalAddress) => {
@@ -956,7 +1119,7 @@ var updateFile = async (ddocId, payload, portalAddress) => {
956
1119
  if (!payload.title && !payload.content) {
957
1120
  throw new Error("At least one field is required: Either provide title, content, or both");
958
1121
  }
959
- const existingFile = FilesModel.findByDDocId(ddocId, portalAddress);
1122
+ const existingFile = await FilesModel.findByDDocId(ddocId, portalAddress);
960
1123
  if (!existingFile) {
961
1124
  throw new Error(`File with ddocId ${ddocId} not found`);
962
1125
  }
@@ -966,8 +1129,8 @@ var updateFile = async (ddocId, payload, portalAddress) => {
966
1129
  syncStatus: "pending"
967
1130
  // since the update is done in local db, it's not on the chain yet. hence pending
968
1131
  };
969
- const updatedFile = FilesModel.update(existingFile._id, updatePayload, portalAddress);
970
- EventsModel.create({ type: "update", fileId: updatedFile._id, portalAddress: updatedFile.portalAddress });
1132
+ const updatedFile = await FilesModel.update(existingFile._id, updatePayload, portalAddress);
1133
+ await EventsModel.create({ type: "update", fileId: updatedFile._id, portalAddress: updatedFile.portalAddress });
971
1134
  return {
972
1135
  ddocId: updatedFile.ddocId,
973
1136
  link: updatedFile.link,
@@ -987,12 +1150,12 @@ var deleteFile = async (ddocId, portalAddress) => {
987
1150
  if (!ddocId) {
988
1151
  throw new Error("ddocId is required");
989
1152
  }
990
- const existingFile = FilesModel.findByDDocId(ddocId, portalAddress);
1153
+ const existingFile = await FilesModel.findByDDocId(ddocId, portalAddress);
991
1154
  if (!existingFile) {
992
1155
  throw new Error(`File with ddocId ${ddocId} not found`);
993
1156
  }
994
- const deletedFile = FilesModel.softDelete(existingFile._id);
995
- EventsModel.create({ type: "delete", fileId: deletedFile._id, portalAddress: deletedFile.portalAddress });
1157
+ const deletedFile = await FilesModel.softDelete(existingFile._id);
1158
+ await EventsModel.create({ type: "delete", fileId: deletedFile._id, portalAddress: deletedFile.portalAddress });
996
1159
  return deletedFile;
997
1160
  };
998
1161
 
@@ -1078,14 +1241,14 @@ var listCommand = new Command().name("list").description("List all ddocs").optio
1078
1241
  const runtimeConfig = getRuntimeConfig();
1079
1242
  const apiKey = runtimeConfig.API_KEY;
1080
1243
  validateApiKey(apiKey);
1081
- const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
1244
+ const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
1082
1245
  if (!portalAddress) throw new Error("Portal address is required");
1083
1246
  const params = {
1084
1247
  limit: options.limit,
1085
1248
  skip: options.skip,
1086
1249
  portalAddress
1087
1250
  };
1088
- const result = listFiles(params);
1251
+ const result = await listFiles(params);
1089
1252
  if (result.ddocs.length === 0) {
1090
1253
  console.log("No ddocs found.");
1091
1254
  return;
@@ -1147,9 +1310,9 @@ var getCommand = new Command2().name("get").description("Get a ddoc by its ID").
1147
1310
  const runtimeConfig = getRuntimeConfig();
1148
1311
  const apiKey = runtimeConfig.API_KEY;
1149
1312
  validateApiKey(apiKey);
1150
- const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
1313
+ const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
1151
1314
  if (!portalAddress) throw new Error("Portal address is required");
1152
- const file = getFile(ddocId, portalAddress);
1315
+ const file = await getFile(ddocId, portalAddress);
1153
1316
  if (!file) {
1154
1317
  console.error(`Ddoc with ID "${ddocId}" not found.`);
1155
1318
  return;
@@ -1214,7 +1377,7 @@ var createCommand = new Command3().name("create").description("Create a new ddoc
1214
1377
  const runtimeConfig = getRuntimeConfig();
1215
1378
  const apiKey = runtimeConfig.API_KEY;
1216
1379
  validateApiKey(apiKey);
1217
- const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
1380
+ const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
1218
1381
  if (!portalAddress) throw new Error("Portal address is required");
1219
1382
  const content = fs2.readFileSync(filepath, "utf-8");
1220
1383
  if (!content || content.trim().length === 0) {
@@ -1310,9 +1473,9 @@ var updateCommand = new Command4().name("update").description("Update an existin
1310
1473
  const runtimeConfig = getRuntimeConfig();
1311
1474
  const apiKey = runtimeConfig.API_KEY;
1312
1475
  validateApiKey(apiKey);
1313
- const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
1476
+ const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
1314
1477
  if (!portalAddress) throw new Error("Portal address is required");
1315
- const file = getFile(ddocId, portalAddress);
1478
+ const file = await getFile(ddocId, portalAddress);
1316
1479
  if (!file) {
1317
1480
  throw new Error(`ddoc with ${ddocId} not found.`);
1318
1481
  }
@@ -1366,7 +1529,7 @@ var deleteCommand = new Command5().name("delete").description("Delete one or mor
1366
1529
  const runtimeConfig = getRuntimeConfig();
1367
1530
  const apiKey = runtimeConfig.API_KEY;
1368
1531
  validateApiKey(apiKey);
1369
- const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
1532
+ const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
1370
1533
  if (!portalAddress) throw new Error("Portal address is required");
1371
1534
  for (const ddocId of ddocIds) {
1372
1535
  try {
@@ -1390,9 +1553,9 @@ var downloadCommand = new Command6().name("download").description("Download a dd
1390
1553
  const runtimeConfig = getRuntimeConfig();
1391
1554
  const apiKey = runtimeConfig.API_KEY;
1392
1555
  validateApiKey(apiKey);
1393
- const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
1556
+ const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
1394
1557
  if (!portalAddress) throw new Error("Portal address is required");
1395
- const file = getFile(ddocId, portalAddress);
1558
+ const file = await getFile(ddocId, portalAddress);
1396
1559
  if (!file) {
1397
1560
  console.error(`Ddoc with ID "${ddocId}" not found.`);
1398
1561
  return;
@@ -1418,9 +1581,9 @@ var viewCommand = new Command7().name("view").description("View content preview
1418
1581
  const runtimeConfig = getRuntimeConfig();
1419
1582
  const apiKey = runtimeConfig.API_KEY;
1420
1583
  validateApiKey(apiKey);
1421
- const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
1584
+ const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
1422
1585
  if (!portalAddress) throw new Error("Portal address is required");
1423
- const file = getFile(ddocId, portalAddress);
1586
+ const file = await getFile(ddocId, portalAddress);
1424
1587
  if (!file) {
1425
1588
  console.error(`Ddoc with ID "${ddocId}" not found.`);
1426
1589
  return;
@@ -1462,7 +1625,7 @@ var MAX_ERROR_LEN = 60;
1462
1625
  var eventsCommand = new Command8().name("events").description("Worker event operations (list failed, retry)");
1463
1626
  eventsCommand.command("list-failed").description("List all failed events").action(async () => {
1464
1627
  try {
1465
- const events = EventsModel.listFailed();
1628
+ const events = await EventsModel.listFailed();
1466
1629
  if (events.length === 0) {
1467
1630
  console.log("No failed events.");
1468
1631
  return;
@@ -1495,7 +1658,7 @@ Failed events (${events.length}):
1495
1658
  });
1496
1659
  eventsCommand.command("retry <eventId>").description("Retry a single failed event by ID").action(async (eventId) => {
1497
1660
  try {
1498
- const updated = EventsModel.resetFailedToPending(eventId);
1661
+ const updated = await EventsModel.resetFailedToPending(eventId);
1499
1662
  if (updated) {
1500
1663
  console.log(`Event ${eventId} reset to pending. Worker will pick it up.`);
1501
1664
  } else {
@@ -1510,7 +1673,7 @@ eventsCommand.command("retry <eventId>").description("Retry a single failed even
1510
1673
  });
1511
1674
  eventsCommand.command("retry-all").description("Retry all failed events").action(async () => {
1512
1675
  try {
1513
- const count = EventsModel.resetAllFailedToPending();
1676
+ const count = await EventsModel.resetAllFailedToPending();
1514
1677
  console.log(`Reset ${count} failed event(s) to pending. Worker will pick them up.`);
1515
1678
  } catch (error) {
1516
1679
  const msg = error instanceof Error ? error.message : String(error);
@@ -1521,8 +1684,7 @@ eventsCommand.command("retry-all").description("Retry all failed events").action
1521
1684
 
1522
1685
  // src/commands/index.ts
1523
1686
  logger.level = "error";
1524
- runMigrations();
1525
- var program = new Command9().name("ddctl").description("CLI tool to manage your ddocs").version("0.0.1").addHelpText("beforeAll", "\n").addHelpText("afterAll", "\n");
1687
+ var program = new Command9().name("ddctl").description("CLI tool to manage your ddocs").version("0.0.2").addHelpText("beforeAll", "\n").addHelpText("afterAll", "\n");
1526
1688
  program.addCommand(listCommand);
1527
1689
  program.addCommand(getCommand);
1528
1690
  program.addCommand(createCommand);
@@ -1531,10 +1693,14 @@ program.addCommand(deleteCommand);
1531
1693
  program.addCommand(downloadCommand);
1532
1694
  program.addCommand(viewCommand);
1533
1695
  program.addCommand(eventsCommand);
1534
- program.parseAsync().then(async () => {
1696
+ (async () => {
1697
+ await getAdapter();
1698
+ await runMigrations();
1699
+ await program.parseAsync();
1700
+ })().then(async () => {
1535
1701
  try {
1536
1702
  await closeWorker();
1537
- await closeDatabase();
1703
+ await closeAdapter();
1538
1704
  } catch (error) {
1539
1705
  }
1540
1706
  process.exit(0);