src / providers / ProviderRegistry.ts
src / providers / ProviderRegistry.ts
import { ProviderRegistrationError } from "../models/errors";
import { ProviderCapability, ProviderSpec } from "../types/provider";
import { BaseProvider } from "./BaseProvider";
export { ProviderSpec };
/**
* Single authoritative declarative registry for search provider instances and specifications.
* Mapped from Python `providers/provider_registry.py` ProviderRegistry.
*/
export class ProviderRegistry {
private static readonly DEFAULT_SPECS: Record<number, ProviderSpec> = {
1: {
id: 1,
name: "duckduckgo",
displayName: "DuckDuckGo",
envVar: null,
requiresKey: false,
capabilities: ["web"],
},
2: {
id: 2,
name: "exa",
displayName: "Exa",
envVar: "EXA_KEYS",
requiresKey: true,
capabilities: ["web"],
},
3: {
id: 3,
name: "tavily",
displayName: "Tavily",
envVar: "TAVILY_KEYS",
requiresKey: true,
capabilities: ["web"],
},
4: {
id: 4,
name: "serper",
displayName: "Serper",
envVar: "SERPER_KEYS",
requiresKey: true,
capabilities: ["web"],
},
5: {
id: 5,
name: "brave",
displayName: "Brave",
envVar: "BRAVE_KEYS",
requiresKey: true,
capabilities: ["web"],
},
6: {
id: 6,
name: "firecrawl",
displayName: "Firecrawl",
envVar: "FIRECRAWL_KEYS",
requiresKey: true,
capabilities: ["web"],
},
};
private providers: Map<string, BaseProvider> = new Map();
private specs: Map<string, ProviderSpec> = new Map();
constructor() {
// Populate instance specs with DEFAULT_SPECS
for (const spec of Object.values(ProviderRegistry.DEFAULT_SPECS)) {
this.specs.set(spec.name.toLowerCase().trim(), spec);
}
}
/**
* Static lookup for all default registered ProviderSpec objects ordered by ID.
*/
public static getAllSpecs(): ProviderSpec[] {
return Object.values(ProviderRegistry.DEFAULT_SPECS).sort((a, b) => a.id - b.id);
}
/**
* Compatibility alias for Python parity.
*/
public static get_all_specs(): ProviderSpec[] {
return ProviderRegistry.getAllSpecs();
}
/**
* Static lookup for ProviderSpec by numeric ID.
*/
public static get_by_id(providerId: number): ProviderSpec | undefined {
return ProviderRegistry.DEFAULT_SPECS[providerId];
}
/**
* Static lookup for ProviderSpec by provider name string.
*/
public static getSpec(providerName: string): ProviderSpec | undefined {
const norm = providerName.toLowerCase().trim();
return Object.values(ProviderRegistry.DEFAULT_SPECS).find((s) => s.name === norm);
}
/**
* Register a search provider instance.
* Validates provider identity, performs structural validation, and enforces uniqueness.
*
* @throws ProviderRegistrationError if provider is invalid or already registered.
*/
public register(provider: BaseProvider): void {
if (!provider) {
throw new ProviderRegistrationError("Cannot register null or undefined provider.");
}
const key = provider.providerName.toLowerCase().trim();
if (!key) {
throw new ProviderRegistrationError("Provider identity validation failed: providerName is required.");
}
if (this.providers.has(key)) {
throw new ProviderRegistrationError(`Provider '${provider.providerName}' is already registered.`);
}
if (!provider.validate()) {
throw new ProviderRegistrationError(`Structural validation failed for provider '${provider.providerName}'.`);
}
this.providers.set(key, provider);
}
/**
* Unregister a search provider by name.
*
* @returns True if provider was removed, False if not found.
*/
public unregister(providerName: string): boolean {
const key = providerName.toLowerCase().trim();
return this.providers.delete(key);
}
/**
* Retrieve a registered search provider instance by name.
*/
public get(providerName: string): BaseProvider | undefined {
const key = providerName.toLowerCase().trim();
return this.providers.get(key);
}
/**
* Check if a provider is registered by name.
*/
public has(providerName: string): boolean {
const key = providerName.toLowerCase().trim();
return this.providers.has(key);
}
/**
* List all currently registered provider instances.
*/
public getAll(): BaseProvider[] {
return Array.from(this.providers.values());
}
/**
* Return array of registered provider name strings in registration order.
*/
public listNames(): string[] {
return Array.from(this.providers.keys());
}
/**
* Filter registered provider instances by capability and enabled state.
*/
public getByCapability(capability: ProviderCapability): BaseProvider[] {
return this.getAll().filter(
(provider) => provider.isEnabled() && provider.hasCapability(capability)
);
}
/**
* Register a ProviderSpec metadata definition on instance.
*/
public registerSpec(spec: ProviderSpec): void {
const key = spec.name.toLowerCase().trim();
this.specs.set(key, spec);
}
/**
* Retrieve ProviderSpec metadata by provider name from instance.
*/
public getSpec(providerName: string): ProviderSpec | undefined {
const key = providerName.toLowerCase().trim();
return this.specs.get(key) ?? ProviderRegistry.getSpec(providerName);
}
/**
* Return all registered ProviderSpec metadata definitions from instance.
*/
public getAllSpecs(): ProviderSpec[] {
return Array.from(this.specs.values()).sort((a, b) => a.id - b.id);
}
/**
* Clear all registered providers and specs.
*/
public clear(): void {
this.providers.clear();
this.specs.clear();
}
}
import { ProviderRegistrationError } from "../models/errors";
import { ProviderCapability, ProviderSpec } from "../types/provider";
import { BaseProvider } from "./BaseProvider";
export { ProviderSpec };
/**
* Single authoritative declarative registry for search provider instances and specifications.
* Mapped from Python `providers/provider_registry.py` ProviderRegistry.
*/
export class ProviderRegistry {
private static readonly DEFAULT_SPECS: Record<number, ProviderSpec> = {
1: {
id: 1,
name: "duckduckgo",
displayName: "DuckDuckGo",
envVar: null,
requiresKey: false,
capabilities: ["web"],
},
2: {
id: 2,
name: "exa",
displayName: "Exa",
envVar: "EXA_KEYS",
requiresKey: true,
capabilities: ["web"],
},
3: {
id: 3,
name: "tavily",
displayName: "Tavily",
envVar: "TAVILY_KEYS",
requiresKey: true,
capabilities: ["web"],
},
4: {
id: 4,
name: "serper",
displayName: "Serper",
envVar: "SERPER_KEYS",
requiresKey: true,
capabilities: ["web"],
},
5: {
id: 5,
name: "brave",
displayName: "Brave",
envVar: "BRAVE_KEYS",
requiresKey: true,
capabilities: ["web"],
},
6: {
id: 6,
name: "firecrawl",
displayName: "Firecrawl",
envVar: "FIRECRAWL_KEYS",
requiresKey: true,
capabilities: ["web"],
},
};
private providers: Map<string, BaseProvider> = new Map();
private specs: Map<string, ProviderSpec> = new Map();
constructor() {
// Populate instance specs with DEFAULT_SPECS
for (const spec of Object.values(ProviderRegistry.DEFAULT_SPECS)) {
this.specs.set(spec.name.toLowerCase().trim(), spec);
}
}
/**
* Static lookup for all default registered ProviderSpec objects ordered by ID.
*/
public static getAllSpecs(): ProviderSpec[] {
return Object.values(ProviderRegistry.DEFAULT_SPECS).sort((a, b) => a.id - b.id);
}
/**
* Compatibility alias for Python parity.
*/
public static get_all_specs(): ProviderSpec[] {
return ProviderRegistry.getAllSpecs();
}
/**
* Static lookup for ProviderSpec by numeric ID.
*/
public static get_by_id(providerId: number): ProviderSpec | undefined {
return ProviderRegistry.DEFAULT_SPECS[providerId];
}
/**
* Static lookup for ProviderSpec by provider name string.
*/
public static getSpec(providerName: string): ProviderSpec | undefined {
const norm = providerName.toLowerCase().trim();
return Object.values(ProviderRegistry.DEFAULT_SPECS).find((s) => s.name === norm);
}
/**
* Register a search provider instance.
* Validates provider identity, performs structural validation, and enforces uniqueness.
*
* @throws ProviderRegistrationError if provider is invalid or already registered.
*/
public register(provider: BaseProvider): void {
if (!provider) {
throw new ProviderRegistrationError("Cannot register null or undefined provider.");
}
const key = provider.providerName.toLowerCase().trim();
if (!key) {
throw new ProviderRegistrationError("Provider identity validation failed: providerName is required.");
}
if (this.providers.has(key)) {
throw new ProviderRegistrationError(`Provider '${provider.providerName}' is already registered.`);
}
if (!provider.validate()) {
throw new ProviderRegistrationError(`Structural validation failed for provider '${provider.providerName}'.`);
}
this.providers.set(key, provider);
}
/**
* Unregister a search provider by name.
*
* @returns True if provider was removed, False if not found.
*/
public unregister(providerName: string): boolean {
const key = providerName.toLowerCase().trim();
return this.providers.delete(key);
}
/**
* Retrieve a registered search provider instance by name.
*/
public get(providerName: string): BaseProvider | undefined {
const key = providerName.toLowerCase().trim();
return this.providers.get(key);
}
/**
* Check if a provider is registered by name.
*/
public has(providerName: string): boolean {
const key = providerName.toLowerCase().trim();
return this.providers.has(key);
}
/**
* List all currently registered provider instances.
*/
public getAll(): BaseProvider[] {
return Array.from(this.providers.values());
}
/**
* Return array of registered provider name strings in registration order.
*/
public listNames(): string[] {
return Array.from(this.providers.keys());
}
/**
* Filter registered provider instances by capability and enabled state.
*/
public getByCapability(capability: ProviderCapability): BaseProvider[] {
return this.getAll().filter(
(provider) => provider.isEnabled() && provider.hasCapability(capability)
);
}
/**
* Register a ProviderSpec metadata definition on instance.
*/
public registerSpec(spec: ProviderSpec): void {
const key = spec.name.toLowerCase().trim();
this.specs.set(key, spec);
}
/**
* Retrieve ProviderSpec metadata by provider name from instance.
*/
public getSpec(providerName: string): ProviderSpec | undefined {
const key = providerName.toLowerCase().trim();
return this.specs.get(key) ?? ProviderRegistry.getSpec(providerName);
}
/**
* Return all registered ProviderSpec metadata definitions from instance.
*/
public getAllSpecs(): ProviderSpec[] {
return Array.from(this.specs.values()).sort((a, b) => a.id - b.id);
}
/**
* Clear all registered providers and specs.
*/
public clear(): void {
this.providers.clear();
this.specs.clear();
}
}