import * as plugins from '../plugins.ts'; import { logger } from '../logging.ts'; export interface ICacheDbOptions { storagePath?: string; dbName?: string; debug?: boolean; } /** * Singleton wrapper around LocalTsmDb + SmartdataDb. * Provides a managed MongoDB-compatible cache database. */ export class CacheDb { private static instance: CacheDb | null = null; private smartMongo: InstanceType | null = null; private smartdataDb: InstanceType | null = null; private options: Required; private constructor(options: ICacheDbOptions = {}) { this.options = { storagePath: options.storagePath ?? './.nogit/cachedb', dbName: options.dbName ?? 'gitops_cache', debug: options.debug ?? false, }; } static getInstance(options?: ICacheDbOptions): CacheDb { if (!CacheDb.instance) { CacheDb.instance = new CacheDb(options); } return CacheDb.instance; } static resetInstance(): void { CacheDb.instance = null; } async start(): Promise { logger.info('Starting CacheDb...'); this.smartMongo = await plugins.smartmongo.SmartMongo.createAndStart(); const mongoDescriptor = await this.smartMongo.getMongoDescriptor(); this.smartdataDb = new plugins.smartdata.SmartdataDb({ mongoDbUrl: mongoDescriptor.mongoDbUrl, mongoDbName: this.options.dbName, }); await this.smartdataDb.init(); logger.success(`CacheDb started (db: ${this.options.dbName})`); } async stop(): Promise { logger.info('Stopping CacheDb...'); if (this.smartdataDb) { await this.smartdataDb.close(); this.smartdataDb = null; } if (this.smartMongo) { // smartMongo.stop() may hang under Deno — fire-and-forget with timeout const stopPromise = this.smartMongo.stop().catch(() => {}); await Promise.race([ stopPromise, new Promise((resolve) => { const id = setTimeout(resolve, 3000); Deno.unrefTimer(id); }), ]); this.smartMongo = null; } logger.success('CacheDb stopped'); } getDb(): InstanceType { if (!this.smartdataDb) { throw new Error('CacheDb not started. Call start() first.'); } return this.smartdataDb; } }