/**
* Provider Factory
*
* Factory for creating VCS provider instances dynamically.
* Supports GitHub, Gitea, and multi-provider configurations.
*/
import { BaseProvider, MultiProvider } from './base-provider.js';
import { ProviderConfig, ProviderResult } from './types.js';
import { configManager } from '../config.js';
export type ProviderType = 'github' | 'gitea';
export class ProviderFactory {
private config: ProviderConfig;
constructor(config: ProviderConfig) {
this.config = config;
}
/**
* Create a single provider instance
*/
createProvider(type: ProviderType): BaseProvider {
switch (type) {
case 'github':
return this.createGitHubProvider();
case 'gitea':
return this.createGiteaProvider();
default:
throw new Error(`Unsupported provider type: ${type}`);
}
}
/**
* Create a multi-provider instance that handles multiple providers
*/
createMultiProvider(types: ProviderType[]): MultiProvider {
const providers = types.map(type => this.createProvider(type));
return new MultiProviderImpl(providers);
}
/**
* Create provider based on string identifier
*/
createProviderFromString(provider: string): BaseProvider | MultiProvider {
// UNIVERSAL MODE: Auto-aplicar 'both' se modo universal ativo e provider não especificado
if (!provider && configManager.isUniversalMode()) {
provider = 'both';
}
switch (provider) {
case 'github':
return this.createProvider('github');
case 'gitea':
return this.createProvider('gitea');
case 'both':
return this.createMultiProvider(['github', 'gitea']);
default:
// UNIVERSAL MODE: Default para 'both' no modo universal
if (configManager.isUniversalMode()) {
return this.createMultiProvider(['github', 'gitea']);
}
throw new Error(`Invalid provider: ${provider}. Valid options: github, gitea, both`);
}
}
/**
* Get available providers based on configuration
*/
getAvailableProviders(): ProviderType[] {
const available: ProviderType[] = [];
if (this.config.github?.token && this.config.github?.username) {
available.push('github');
}
if (this.config.gitea?.url && this.config.gitea?.token && this.config.gitea?.username) {
available.push('gitea');
}
return available;
}
/**
* Check if a provider type is available
*/
isProviderAvailable(type: ProviderType): boolean {
return this.getAvailableProviders().includes(type);
}
/**
* Validate provider configuration
*/
validateProviderConfig(type: ProviderType): {
valid: boolean;
missingFields: string[];
} {
switch (type) {
case 'github':
return this.validateGitHubConfig();
case 'gitea':
return this.validateGiteaConfig();
default:
return { valid: false, missingFields: ['Invalid provider type'] };
}
}
private createGitHubProvider(): BaseProvider {
if (!this.config.github) {
throw new Error('GitHub configuration is missing');
}
// Import GitHubProvider dynamically to avoid circular dependencies
const { GitHubProvider } = require('./github-provider.js');
return new GitHubProvider(this.config.github);
}
private createGiteaProvider(): BaseProvider {
if (!this.config.gitea) {
throw new Error('Gitea configuration is missing');
}
// Import GiteaProvider dynamically to avoid circular dependencies
const { GiteaProvider } = require('./gitea-provider.js');
return new GiteaProvider(this.config.gitea);
}
private validateGitHubConfig(): { valid: boolean; missingFields: string[] } {
const missing: string[] = [];
if (!this.config.github?.token) {
missing.push('GITHUB_TOKEN');
}
if (!this.config.github?.username) {
missing.push('GITHUB_USERNAME');
}
return {
valid: missing.length === 0,
missingFields: missing
};
}
private validateGiteaConfig(): { valid: boolean; missingFields: string[] } {
const missing: string[] = [];
if (!this.config.gitea?.url) {
missing.push('GITEA_URL');
}
if (!this.config.gitea?.token) {
missing.push('GITEA_TOKEN');
}
if (!this.config.gitea?.username) {
missing.push('GITEA_USERNAME');
}
return {
valid: missing.length === 0,
missingFields: missing
};
}
/**
* Get default provider based on mode
*/
getDefaultProvider(): string {
return configManager.isUniversalMode() ? 'both' : 'github';
}
}
/**
* Multi-provider implementation
*/
class MultiProviderImpl implements MultiProvider {
providers: BaseProvider[];
constructor(providers: BaseProvider[]) {
this.providers = providers;
}
/**
* Execute operation on all configured providers in parallel
*/
async executeOperation(operation: string, params: any): Promise<ProviderResult[]> {
const configuredProviders = this.getConfiguredProviders();
if (configuredProviders.length === 0) {
throw new Error('No providers are configured');
}
// Execute operations in parallel
const promises = configuredProviders.map(async (provider) => {
try {
return await provider.executeOperation(operation, params);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
success: false,
error: {
code: 'PROVIDER_ERROR',
message: `Provider ${provider.getName()} failed: ${errorMessage}`,
details: error
},
provider: provider.getName()
};
}
});
return Promise.all(promises);
}
/**
* Get only the configured providers
*/
getConfiguredProviders(): BaseProvider[] {
return this.providers.filter(provider => provider.isConfigured());
}
/**
* Check if any provider is configured
*/
isAnyProviderConfigured(): boolean {
return this.getConfiguredProviders().length > 0;
}
}