{"version":3,"file":"registry-C6Dji33A.mjs","names":[],"sources":["../src/core/registry.ts"],"sourcesContent":["import type { ProviderConfig, SearchFilterCapabilities } from \"./types.ts\";\nimport { providerApiKeyEnvVar } from \"./providers.ts\";\nimport {\n  isAvailabilityProvider,\n  isImageSearchProvider,\n  isPaginatedSearchProvider,\n  isReadProvider,\n  isSearchProvider,\n  type ImageSearchProvider,\n  type Provider,\n  type ProviderCapabilities,\n  type ProviderCapability,\n  type ProviderConstructor,\n  type ProviderImageSearchCapabilities,\n  type ProviderReadCapabilities,\n  type ProviderSearchCapabilities,\n  type ReadProvider,\n  type SearchProvider,\n} from \"./provider.ts\";\nimport {\n  ImageSearchNotSupportedError,\n  ReadNotSupportedError,\n  SearchNotSupportedError,\n  UnknownProviderError,\n} from \"./errors.ts\";\nimport { builtins } from \"../providers/index.ts\";\n\n/**\n * One provider the registry knows: the metadata every listing and capability lookup answers from,\n * plus a loader for the class. A built-in entry's `load` is a literal `import()` of its module, so\n * the module runs on the first `create()` for that name and never on package import.\n */\nexport interface ProviderEntry {\n  readonly name: string;\n  /** API key variable; `null` when registering is enough to count as configured; absent derives `<NAME>_API_KEY`. */\n  readonly apiKeyEnvVar?: string | null;\n  /** Local configuration check for providers whose credentials are more than one variable. */\n  readonly isConfigured?: () => boolean;\n  /** Whether instances answer `isAvailable()`, so discovery can probe without loading the rest. */\n  readonly availability?: boolean;\n  /** Search capability as discovery reports it; absent when the provider does not search. */\n  readonly search?: Omit<ProviderSearchCapabilities, \"supported\">;\n  /** Reverse image search capability; absent when the provider does not implement it. */\n  readonly searchImage?: Omit<ProviderImageSearchCapabilities, \"supported\">;\n  /** URL reader capability; absent when the provider does not read. */\n  readonly read?: Omit<ProviderReadCapabilities, \"supported\">;\n  /** Resolves the provider class. */\n  readonly load: () => Promise<ProviderConstructor>;\n}\n\ninterface ProviderRegistration {\n  readonly entry: ProviderEntry;\n  /** Whether the entry came from the manifest, so its class still waits behind an `import()`. */\n  readonly builtin: boolean;\n  readonly previous?: ProviderRegistration;\n}\n\nlet registrations: Map<string, ProviderRegistration> | undefined;\nconst removedRegistrations = new WeakSet<ProviderRegistration>();\n\n/**\n * The registry table, seeded from the built-in manifest on first use rather than at module scope,\n * so a consumer bundle that never touches the registry drops the table with it.\n * @returns {Map<string, ProviderRegistration>} The seeded table.\n */\nfunction table(): Map<string, ProviderRegistration> {\n  registrations ??= new Map(\n    builtins.map((entry): [string, ProviderRegistration] => [entry.name, { entry, builtin: true }]),\n  );\n  return registrations;\n}\n\nfunction entryFor(name: string): ProviderEntry | undefined {\n  return table().get(name)?.entry;\n}\n\n/**\n * Register a provider class living outside the package.\n * The class's static metadata and its prototype decide what discovery reports; registering a name\n * again shadows the previous entry until the returned function removes the registration.\n * @param {ProviderConstructor} provider - Provider class to register.\n * @returns {() => void} A function that removes this registration when it is still current.\n */\nexport function register(provider: ProviderConstructor): () => void {\n  assertProviderName(provider.providerName);\n  const registration: ProviderRegistration = {\n    entry: entryFromClass(provider),\n    builtin: false,\n    previous: table().get(provider.providerName),\n  };\n  table().set(provider.providerName, registration);\n  return () => removeRegistration(provider.providerName, registration);\n}\n\n/**\n * Create a provider instance by name.\n * Imports a built-in provider's module on the first call for its name; the module map shares one\n * import between parallel callers. Resolves apiKey from config or the provider's env var.\n * @param {string} name - Registered provider name.\n * @param {ProviderConfig} config - Provider configuration.\n * @returns {Promise<Provider>} Configured provider instance.\n */\nexport async function create(name: string, config?: Readonly<ProviderConfig>): Promise<Provider> {\n  const entry = entryFor(name);\n  if (!entry) {\n    throw new UnknownProviderError(name);\n  }\n  return instantiate(entry, config);\n}\n\n/**\n * Create a provider that implements text search.\n * @param name - Registered provider name.\n * @param config - Provider configuration.\n * @returns {Promise<Provider & SearchProvider>} A provider with search support.\n */\nexport async function createSearchProvider(\n  name: string,\n  config?: Readonly<ProviderConfig>,\n): Promise<Provider & SearchProvider> {\n  const entry = requireEntry(name);\n  if (entry.search === undefined) throw new SearchNotSupportedError(name);\n  const provider = await instantiate(entry, config);\n  if (!isSearchProvider(provider)) throw new SearchNotSupportedError(name);\n  return provider;\n}\n\n/**\n * Create a provider that implements reverse image search.\n * @param name - Registered provider name.\n * @param config - Provider configuration.\n * @returns {Promise<Provider & ImageSearchProvider>} A provider with reverse image search support.\n */\nexport async function createImageSearchProvider(\n  name: string,\n  config?: Readonly<ProviderConfig>,\n): Promise<Provider & ImageSearchProvider> {\n  const entry = requireEntry(name);\n  if (entry.searchImage === undefined) throw new ImageSearchNotSupportedError(name);\n  const provider = await instantiate(entry, config);\n  if (!isImageSearchProvider(provider)) throw new ImageSearchNotSupportedError(name);\n  return provider;\n}\n\n/**\n * Create a provider that implements URL reading.\n * @param name - Registered provider name.\n * @param config - Provider configuration.\n * @returns {Promise<Provider & ReadProvider>} A provider with read support.\n */\nexport async function createReadProvider(\n  name: string,\n  config?: Readonly<ProviderConfig>,\n): Promise<Provider & ReadProvider> {\n  const entry = requireEntry(name);\n  if (entry.read === undefined) throw new ReadNotSupportedError(name);\n  const provider = await instantiate(entry, config);\n  if (!isReadProvider(provider)) throw new ReadNotSupportedError(name);\n  return provider;\n}\n\n/**\n * Return every registered provider name, built-ins first in manifest order.\n * @returns {string[]} Registered provider names.\n */\nexport function providers(): string[] {\n  return Array.from(table().keys());\n}\n\n/**\n * Return registered providers that implement text search.\n * @returns {string[]} Search provider names.\n */\nexport function searchProviders(): string[] {\n  return providerNamesWithCapability(\"search\");\n}\n\n/**\n * Return registered providers that implement reverse image search.\n * @returns {string[]} Image search provider names.\n */\nexport function searchImageProviders(): string[] {\n  return providerNamesWithCapability(\"searchImage\");\n}\n\n/**\n * Return registered providers that implement URL reading.\n * @returns {string[]} Read provider names.\n */\nexport function readProviders(): string[] {\n  return providerNamesWithCapability(\"read\");\n}\n\n/**\n * Return the API key variable declared by a provider or derived from its name.\n * @param {string} name - Registered or prospective provider name.\n * @returns {string | null} Environment variable name, or null for a keyless provider.\n */\nexport function getProviderApiKeyEnvVar(name: string): string | null {\n  const entry = entryFor(name);\n  return entry === undefined ? providerApiKeyEnvVar(name) : apiKeyEnvVarOf(entry);\n}\n\n/**\n * Check local credentials without a network request or token refresh.\n * @param name - Registered provider name.\n * @returns {boolean} Whether selection from the environment can use this provider.\n */\nexport function isProviderConfigured(name: string): boolean {\n  const entry = entryFor(name);\n  if (entry?.isConfigured) return entry.isConfigured();\n  const envVar = getProviderApiKeyEnvVar(name);\n  return envVar === null || Boolean(process.env[envVar]);\n}\n\n/**\n * Return whether discovery should instantiate a provider to look for its `isAvailable()` probe.\n * A built-in declares the probe in the manifest, so the others stay behind their `import()`; a\n * registered class is already loaded and may carry the probe as an instance field, so it is always\n * worth constructing.\n * @param name - Registered provider name.\n * @returns {boolean} Whether `create(name)` may yield an `AvailabilityProvider`.\n */\nexport function probesAvailability(name: string): boolean {\n  const registration = table().get(name);\n  if (registration === undefined) return false;\n  return registration.entry.availability === true || !registration.builtin;\n}\n\nexport function getSearchFilterCapabilities(name: string): SearchFilterCapabilities | undefined {\n  const search = entryFor(name)?.search;\n  if (search?.filters === undefined) return undefined;\n  return {\n    filters: search.filters,\n    ...(search.categories === undefined ? {} : { categories: search.categories }),\n  };\n}\n\n/**\n * Return the complete operation matrix a registered provider declares.\n * Optional details stay absent for custom providers that only implement methods.\n * @param name - Registered provider name.\n * @returns {ProviderCapabilities | undefined} Capability metadata, or undefined when the provider is not registered.\n */\nexport function getProviderCapabilities(name: string): ProviderCapabilities | undefined {\n  const entry = entryFor(name);\n  if (!entry) return undefined;\n\n  return {\n    search: entry.search ? { supported: true, ...entry.search } : { supported: false },\n    searchImage: entry.searchImage\n      ? { supported: true, ...entry.searchImage }\n      : { supported: false },\n    read: entry.read ? { supported: true, ...entry.read } : { supported: false },\n  };\n}\n\nexport function has(name: string): boolean {\n  return table().has(name);\n}\n\nfunction requireEntry(name: string): ProviderEntry {\n  const entry = entryFor(name);\n  if (!entry) throw new UnknownProviderError(name);\n  return entry;\n}\n\nfunction apiKeyEnvVarOf(entry: Readonly<ProviderEntry>): string | null {\n  return entry.apiKeyEnvVar === undefined ? providerApiKeyEnvVar(entry.name) : entry.apiKeyEnvVar;\n}\n\n/**\n * Construct a provider from its entry, the key variable read from that same entry so a\n * registration that replaces the name during the import cannot hand the class another one.\n * @param entry - Registry entry the caller resolved.\n * @param config - Provider configuration.\n * @returns {Promise<Provider>} Configured provider instance.\n */\nasync function instantiate(\n  entry: Readonly<ProviderEntry>,\n  config?: Readonly<ProviderConfig>,\n): Promise<Provider> {\n  const envVar = apiKeyEnvVarOf(entry);\n  const ProviderClass = await entry.load();\n  const apiKey = config?.apiKey || (envVar === null ? undefined : process.env[envVar]);\n\n  return new ProviderClass({\n    ...config,\n    apiKey,\n    baseURL: config?.baseURL || ProviderClass.defaultBaseURL,\n  });\n}\n\n/**\n * Describe a class the way a manifest entry would: the static metadata it declares plus the\n * operations its prototype implements.\n * @param ProviderClass - Registered provider class.\n * @returns {ProviderEntry} Entry answering from the class without loading anything.\n */\nfunction entryFromClass(ProviderClass: ProviderConstructor): ProviderEntry {\n  return {\n    name: ProviderClass.providerName,\n    ...(ProviderClass.apiKeyEnvVar === undefined\n      ? {}\n      : { apiKeyEnvVar: ProviderClass.apiKeyEnvVar }),\n    ...(ProviderClass.isConfigured\n      ? { isConfigured: ProviderClass.isConfigured.bind(ProviderClass) }\n      : {}),\n    ...(isAvailabilityProvider(ProviderClass.prototype) ? { availability: true } : {}),\n    ...(supportsCapability(ProviderClass, \"search\", isSearchProvider)\n      ? { search: searchDetails(ProviderClass) }\n      : {}),\n    ...(supportsCapability(ProviderClass, \"searchImage\", isImageSearchProvider)\n      ? { searchImage: { ...ProviderClass.capabilityDetails?.searchImage } }\n      : {}),\n    ...(supportsCapability(ProviderClass, \"read\", isReadProvider)\n      ? { read: { ...ProviderClass.capabilityDetails?.read } }\n      : {}),\n    load: () => Promise.resolve(ProviderClass),\n  };\n}\n\nfunction searchDetails(ProviderClass: ProviderConstructor): NonNullable<ProviderEntry[\"search\"]> {\n  const details = ProviderClass.capabilityDetails?.search;\n  return {\n    ...ProviderClass.searchFilterCapabilities,\n    ...(details\n      ? {\n          contentOptions: details.contentOptions,\n          ...(details.resultLimit ? { resultLimit: details.resultLimit } : {}),\n          resultFields: details.resultFields,\n        }\n      : {}),\n    ...(isPaginatedSearchProvider(ProviderClass.prototype) ? { pagination: true } : {}),\n  };\n}\n\nfunction providerNamesWithCapability(capability: ProviderCapability): string[] {\n  return Array.from(table().values())\n    .filter(({ entry }) => entry[capability] !== undefined)\n    .map(({ entry }) => entry.name);\n}\n\nfunction supportsCapability(\n  ProviderClass: ProviderConstructor,\n  capability: ProviderCapability,\n  predicate: (provider: object) => boolean,\n): boolean {\n  return (\n    ProviderClass.capabilities?.includes(capability) === true || predicate(ProviderClass.prototype)\n  );\n}\n\nfunction removeRegistration(name: string, registration: Readonly<ProviderRegistration>): void {\n  if (removedRegistrations.has(registration)) return;\n  removedRegistrations.add(registration);\n  if (table().get(name) !== registration) return;\n\n  let previous = registration.previous;\n  while (previous !== undefined && removedRegistrations.has(previous)) {\n    previous = previous.previous;\n  }\n  if (previous === undefined) {\n    table().delete(name);\n    return;\n  }\n  table().set(name, previous);\n}\n\nfunction assertProviderName(name: string): void {\n  if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(name) || name === \"auto\" || name === \"all\") {\n    throw new TypeError(\n      'providerName must use lowercase ASCII letters, digits, and single internal hyphens, and cannot be \"auto\" or \"all\"',\n    );\n  }\n}\n"],"mappings":"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"}