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 localTsmDb: 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.localTsmDb = new plugins.smartmongo.LocalTsmDb({ folderPath: this.options.storagePath, }); const { connectionUri } = await this.localTsmDb.start(); this.smartdataDb = new plugins.smartdata.SmartdataDb({ mongoDbUrl: connectionUri, 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.localTsmDb) { // localDb.stop() may hang under Deno — fire-and-forget with timeout const stopPromise = this.localTsmDb.stop().catch(() => {}); await Promise.race([ stopPromise, new Promise((resolve) => { const id = setTimeout(resolve, 3000); Deno.unrefTimer(id); }), ]); this.localTsmDb = null; } logger.success('CacheDb stopped'); } getDb(): InstanceType { if (!this.smartdataDb) { throw new Error('CacheDb not started. Call start() first.'); } return this.smartdataDb; } }