BID · Console
Baseline · Intelligence · Decision
src/tools/retrieval/connectors/sec-financials.ts 2,066 bytes · typescript
/**
 * SEC EDGAR XBRL company facts connector.
 *
 * Returns the full XBRL fact set for one company, optionally narrowed
 * to a comma-separated list of concept tags (e.g. "Revenues,Assets").
 * Backed by https://data.sec.gov/api/xbrl/companyfacts/CIK{cik}.json.
 *
 * Implementation reuses the canonical `secFinancials` function from
 * sec-edgar.ts so there is exactly one HTTP / rate-limit / error-
 * translation path against EDGAR.
 */

import {
  RetrievalError,
  type FetchParams,
  type RawPayload,
  type RetrievalConnector,
} from '../interface.js';
import { secFinancials } from './sec-edgar.js';

export class SecFinancialsConnector implements RetrievalConnector {
  readonly name = 'sec-financials';
  readonly authRequired = false;
  readonly rateLimit = { requestsPerSecond: 10, burstSize: 10 };

  async isAvailable(): Promise<boolean> {
    return true;
  }

  async fetch(params: FetchParams): Promise<RawPayload> {
    const cik = params.entity?.id;
    if (typeof cik !== 'string' || !cik.trim()) {
      throw new RetrievalError(
        'invalid-request',
        'sec-financials: entity.id (CIK) is required (any numeric form; will be padded).',
      );
    }
    const concepts =
      typeof params.query?.concepts === 'string'
        ? (params.query.concepts as string)
        : Array.isArray(params.query?.concepts)
          ? (params.query!.concepts as readonly string[]).join(',')
          : undefined;
    const facts = await secFinancials(cik, concepts);
    return {
      source: this.name,
      sourceUrl: facts.sourceUrl,
      capturedAt: facts.capturedAt,
      contentType: 'application/json',
      rawContent: JSON.stringify(facts),
      metadata: {
        cik: facts.cik,
        entity: cik,
        conceptsFilter: facts.conceptsFilter ?? null,
        availableConcepts: facts.availableConcepts ?? null,
      },
    };
  }
}

/** Re-export the underlying standalone fetcher for callers that don't
 *  go through the dispatcher. */
export { secFinancials };
export type { SecCompanyFacts } from './sec-edgar.js';