/** Injection token carrying the resolved {@link ExchangeOptions}. */ export const EXCHANGE_OPTIONS = Symbol("EXCHANGE_OPTIONS"); /** Configuration for the {@link ExchangeService} and its CBE provider. */ export interface ExchangeOptions { /** * CBE daily-exchange-rates JSON endpoint. Returns an array of daily records; * `_limit=1&_sort=Date%3ADESC` narrows it to the most recent day. * @default 'https://combanketh.et/cbeapi/daily-exchange-rates/?_limit=1&_sort=Date%3ADESC' */ scrapeUrl?: string; /** * Last-resort USD→ETB rate, used only when the fetch fails, no cached rate * exists, and {@link loadFallbackRate} supplies nothing. The ETB→USD * direction is derived as its inverse. * @default 162 */ fallbackRate?: number; /** * Reads the persisted fallback rate — the last known good CBE rate, or one * set by an operator. Consulted only when the live fetch fails and no cached * rate is available; a `null` result falls through to {@link fallbackRate}. * * Optional: omit it and the provider uses the static `fallbackRate` alone. */ loadFallbackRate?: () => Promise; /** * Persists a freshly fetched live rate as the new fallback, so the stored * value is never more than one successful fetch stale. Called after every * successful fetch that produced a changed rate. * * Failures here are logged and swallowed — persisting the fallback must * never break the pricing call that triggered it. */ saveFallbackRate?: (rate: number) => Promise; /** * How long a successfully fetched rate is cached, in milliseconds. * @default 3_600_000 (1 hour) */ cacheTtlMs?: number; /** * Timeout for the rate HTTP request, in milliseconds. * @default 8_000 */ requestTimeoutMs?: number; } /** The scalar options, all resolved — the callbacks stay genuinely optional. */ export type ResolvedExchangeOptions = Required< Omit > & Pick; /** Defaults applied to any unset scalar {@link ExchangeOptions} field. */ export const EXCHANGE_DEFAULTS: ResolvedExchangeOptions = { scrapeUrl: "https://combanketh.et/cbeapi/daily-exchange-rates/?_limit=1&_sort=Date%3ADESC", fallbackRate: 162, cacheTtlMs: 3_600_000, requestTimeoutMs: 8_000, }; /** Factory contract for {@link ExchangeModule.forRootAsync}. */ export interface ExchangeAsyncOptions { /** Providers to inject into {@link useFactory} (e.g. `[ConfigService]`). */ inject?: unknown[]; /** Returns the options, possibly async. */ useFactory: (...args: never[]) => ExchangeOptions | Promise; }