import * as plugins from './plugins.js'; import * as paths from './paths.js'; import type * as interfaces from './interfaces/index.js'; import { TsViewConfig } from './config/index.js'; import { ViewServer } from './server/index.js'; /** * Main TsView class. * Provides both CLI and programmatic access to S3 and MongoDB viewing. */ export class TsView { public config: TsViewConfig; public server: ViewServer | null = null; private smartbucketInstance: plugins.smartbucket.SmartBucket | null = null; private mongoDbConnection: plugins.smartdata.SmartdataDb | null = null; constructor() { this.config = new TsViewConfig(); } /** * Load configuration from .nogit/env.json */ public async loadConfigFromEnv(cwd?: string): Promise { await this.config.loadFromEnv(cwd); } /** * Set S3 configuration programmatically */ public setS3Config(config: interfaces.IS3Config): void { this.config.setS3Config(config); } /** * Set MongoDB configuration programmatically */ public setMongoConfig(config: interfaces.IMongoConfig): void { this.config.setMongoConfig(config); } /** * Get the SmartBucket instance (lazy initialization) */ public async getSmartBucket(): Promise { if (this.smartbucketInstance) { return this.smartbucketInstance; } const s3Config = this.config.getS3Config(); if (!s3Config) { return null; } this.smartbucketInstance = new plugins.smartbucket.SmartBucket({ endpoint: s3Config.endpoint, port: s3Config.port, accessKey: s3Config.accessKey, accessSecret: s3Config.accessSecret, useSsl: s3Config.useSsl ?? true, }); return this.smartbucketInstance; } /** * Get the MongoDB connection (lazy initialization) */ public async getMongoDb(): Promise { if (this.mongoDbConnection) { return this.mongoDbConnection; } const mongoConfig = this.config.getMongoConfig(); if (!mongoConfig) { return null; } this.mongoDbConnection = new plugins.smartdata.SmartdataDb({ mongoDbUrl: mongoConfig.mongoDbUrl, mongoDbName: mongoConfig.mongoDbName, }); await this.mongoDbConnection.init(); return this.mongoDbConnection; } /** * Find a free port starting from the given port */ private async findFreePort(startPort: number = 3010): Promise { const network = new plugins.smartnetwork.SmartNetwork(); const freePort = await network.findFreePort(startPort, startPort + 100); if (freePort === null) { throw new Error(`No free port found between ${startPort} and ${startPort + 100}`); } return freePort; } /** * Start the viewer server * @param port - Optional port number (if not provided, finds a free port from 3010+) */ public async start(port?: number): Promise { const actualPort = port ?? await this.findFreePort(3010); this.server = new ViewServer(this, actualPort); await this.server.start(); console.log(`TsView server started on http://localhost:${actualPort}`); // Open browser try { await plugins.smartopen.openUrl(`http://localhost:${actualPort}`); } catch (err) { // Ignore browser open errors } return actualPort; } /** * Stop the viewer server */ public async stop(): Promise { if (this.server) { await this.server.stop(); this.server = null; } if (this.mongoDbConnection) { await this.mongoDbConnection.close(); this.mongoDbConnection = null; } } }