import { normalizeOptionalString } from "../shared/string-coerce.js"; export type AutoSelectableProvider = { id: string; autoSelectOrder?: number; }; export type ProviderSelection = { configuredProviderId?: string; missingConfiguredProvider: boolean; provider: TProvider | undefined; }; export type ResolvedConfiguredProvider = | { ok: true; configuredProviderId?: string; provider: TProvider; providerConfig: TConfig; } | { ok: false; code: "missing-configured-provider" | "no-registered-provider" | "provider-not-configured"; configuredProviderId?: string; provider?: TProvider; }; export function selectConfiguredOrAutoProvider(params: { configuredProviderId?: string; getConfiguredProvider: (providerId: string | undefined) => TProvider | undefined; listProviders: () => Iterable; }): ProviderSelection { const configuredProviderId = normalizeOptionalString(params.configuredProviderId); const configuredProvider = configuredProviderId ? params.getConfiguredProvider(configuredProviderId) : undefined; if (configuredProviderId && !configuredProvider) { return { configuredProviderId, missingConfiguredProvider: true, provider: undefined, }; } return { configuredProviderId, missingConfiguredProvider: false, provider: configuredProvider ?? selectFirstAutoProvider(params.listProviders()), }; } export function resolveProviderRawConfig(params: { providerId: string; configuredProviderId?: string; providerConfigs?: Record | undefined>; }): Record { const canonicalProviderConfig = readProviderConfig(params.providerConfigs, params.providerId); const selectedProviderConfig = readProviderConfig( params.providerConfigs, params.configuredProviderId, ); return { ...canonicalProviderConfig, ...selectedProviderConfig, }; } export function resolveConfiguredCapabilityProvider< TConfig, TFullConfig, TProvider extends AutoSelectableProvider, >(params: { configuredProviderId?: string; providerConfigs?: Record | undefined>; cfg: TFullConfig | undefined; cfgForResolve: TFullConfig; getConfiguredProvider: (providerId: string | undefined) => TProvider | undefined; listProviders: () => Iterable; resolveProviderConfig: (params: { provider: TProvider; cfg: TFullConfig; rawConfig: Record; }) => TConfig; isProviderConfigured: (params: { provider: TProvider; cfg: TFullConfig | undefined; providerConfig: TConfig; }) => boolean; }): ResolvedConfiguredProvider { const configuredProviderId = normalizeOptionalString(params.configuredProviderId); if (configuredProviderId) { const provider = params.getConfiguredProvider(configuredProviderId); if (!provider) { return { ok: false, code: "missing-configured-provider", configuredProviderId, }; } return resolveProviderCandidate({ ...params, configuredProviderId, provider, }); } const providers = [...params.listProviders()].toSorted(compareProviderAutoSelectOrder); if (providers.length === 0) { return { ok: false, code: "no-registered-provider", }; } let firstUnconfigured: TProvider | undefined; for (const provider of providers) { const resolution = resolveProviderCandidate({ ...params, provider, }); if (resolution.ok) { return resolution; } firstUnconfigured ??= provider; } return { ok: false, code: "provider-not-configured", provider: firstUnconfigured, }; } function compareProviderAutoSelectOrder( left: TProvider, right: TProvider, ): number { return ( (left.autoSelectOrder ?? Number.MAX_SAFE_INTEGER) - (right.autoSelectOrder ?? Number.MAX_SAFE_INTEGER) ); } function selectFirstAutoProvider( providers: Iterable, ): TProvider | undefined { let selected: TProvider | undefined; for (const provider of providers) { if (!selected || compareProviderAutoSelectOrder(provider, selected) < 0) { selected = provider; } } return selected; } function readProviderConfig( providerConfigs: Record | undefined> | undefined, providerId: string | undefined, ): Record | undefined { if (!providerId) { return undefined; } const providerConfig = providerConfigs?.[providerId]; return providerConfig && typeof providerConfig === "object" ? providerConfig : undefined; } function resolveProviderCandidate< TConfig, TFullConfig, TProvider extends AutoSelectableProvider, >(params: { configuredProviderId?: string; providerConfigs?: Record | undefined>; cfg: TFullConfig | undefined; cfgForResolve: TFullConfig; provider: TProvider; resolveProviderConfig: (params: { provider: TProvider; cfg: TFullConfig; rawConfig: Record; }) => TConfig; isProviderConfigured: (params: { provider: TProvider; cfg: TFullConfig | undefined; providerConfig: TConfig; }) => boolean; }): ResolvedConfiguredProvider { const rawProviderConfig = resolveProviderRawConfig({ providerId: params.provider.id, configuredProviderId: params.configuredProviderId, providerConfigs: params.providerConfigs, }); const providerConfig = params.resolveProviderConfig({ provider: params.provider, cfg: params.cfgForResolve, rawConfig: rawProviderConfig, }); if ( !params.isProviderConfigured({ provider: params.provider, cfg: params.cfg, providerConfig }) ) { return { ok: false, code: "provider-not-configured", configuredProviderId: params.configuredProviderId, provider: params.provider, }; } return { ok: true, configuredProviderId: params.configuredProviderId, provider: params.provider, providerConfig, }; }