/**
* Provider Manager
* Manages object storage provider instances and provides unified access
*/
import { Provider, ProviderRegistry } from "./interface.js";
import type { SourceConfig, ProviderType } from "../types/config.js";
// Singleton instance for global access
let managerInstance: ProviderManager | null = null;
/**
* Manages storage providers and provides a unified interface to work with them
* Supports multiple storage sources with unique IDs
*/
export class ProviderManager {
// Maps for multi-source support
private providers: Map<string, Provider> = new Map();
private sourceConfigs: Map<string, SourceConfig> = new Map();
private sourceIds: string[] = [];
constructor() {
if (!managerInstance) {
managerInstance = this;
}
}
/**
* Initialize and connect to multiple storage sources
* @param sources Array of source configurations
*/
async connectWithSources(sources: SourceConfig[]): Promise<void> {
if (sources.length === 0) {
throw new Error("No sources provided");
}
// Connect to each source
for (const source of sources) {
await this.connectSource(source);
}
}
/**
* Connect to a single source
*/
private async connectSource(source: SourceConfig): Promise<void> {
const sourceId = source.id;
// Find provider prototype for this source type
const providerPrototype = ProviderRegistry.getProvider(source.type);
if (!providerPrototype) {
throw new Error(
`Source '${sourceId}': No provider found for type: ${source.type}. Available providers: ${ProviderRegistry.getAvailableProviders().join(", ")}`
);
}
// Create a new instance of the provider (clone) to avoid sharing state
const provider = providerPrototype.clone();
// Attach source ID to provider instance
(provider as any).sourceId = sourceId;
// Connect to the storage service
await provider.connect(source);
// Store provider
this.providers.set(sourceId, provider);
this.sourceIds.push(sourceId);
// Store source config (for API exposure)
this.sourceConfigs.set(sourceId, source);
}
/**
* Close all storage connections
*/
async disconnect(): Promise<void> {
for (const [sourceId, provider] of this.providers.entries()) {
try {
await provider.disconnect();
console.error(`Disconnected from source '${sourceId}'`);
} catch (error) {
console.error(`Error disconnecting from source '${sourceId}':`, error);
}
}
// Clear state
this.providers.clear();
this.sourceConfigs.clear();
this.sourceIds = [];
}
/**
* Get a provider by source ID
* @param sourceId Source ID (optional, defaults to first source)
*/
getProvider(sourceId?: string): Provider {
const id = sourceId || this.sourceIds[0];
const provider = this.providers.get(id);
if (!provider) {
if (sourceId) {
throw new Error(
`Source '${sourceId}' not found. Available sources: ${this.sourceIds.join(", ")}`
);
} else {
throw new Error("No sources connected. Call connectWithSources() first.");
}
}
return provider;
}
/**
* Get all available source IDs
*/
getSourceIds(): string[] {
return [...this.sourceIds];
}
/**
* Get source configuration by ID
*/
getSourceConfig(sourceId?: string): SourceConfig | null {
if (this.providers.size === 0) {
return null;
}
const id = sourceId || this.sourceIds[0];
return this.sourceConfigs.get(id) || null;
}
/**
* Get all source configurations
*/
getAllSourceConfigs(): SourceConfig[] {
return this.sourceIds.map((id) => this.sourceConfigs.get(id)!);
}
// Static methods for global access
/**
* Get all available provider types
*/
static getAvailableProviders(): ProviderType[] {
return ProviderRegistry.getAvailableProviders();
}
/**
* Get sample endpoints for all providers
*/
static getAllSampleEndpoints(): { [key in ProviderType]?: string } {
return ProviderRegistry.getAllSampleEndpoints();
}
/**
* Get current provider instance
*/
static getCurrentProvider(sourceId?: string): Provider {
if (!managerInstance) {
throw new Error("ProviderManager not initialized");
}
return managerInstance.getProvider(sourceId);
}
/**
* Get all available source IDs
*/
static getAvailableSourceIds(): string[] {
if (!managerInstance) {
throw new Error("ProviderManager not initialized");
}
return managerInstance.getSourceIds();
}
/**
* Get source configuration by ID
*/
static getSourceConfig(sourceId?: string): SourceConfig | null {
if (!managerInstance) {
throw new Error("ProviderManager not initialized");
}
return managerInstance.getSourceConfig(sourceId);
}
/**
* Get all source configurations
*/
static getAllSourceConfigs(): SourceConfig[] {
if (!managerInstance) {
throw new Error("ProviderManager not initialized");
}
return managerInstance.getAllSourceConfigs();
}
}