fix(stocks/providers/provider.secedgar): Improve SEC EDGAR provider networking and error handling, update plugin path import, bump dev deps and add/refresh tests and lockfile
This commit is contained in:
		@@ -217,6 +217,7 @@ export class SecEdgarProvider implements IFundamentalsProvider {
 | 
			
		||||
  /**
 | 
			
		||||
   * Fetch the SEC ticker-to-CIK mapping list
 | 
			
		||||
   * Cached for 24 hours (list updates daily)
 | 
			
		||||
   * Uses native fetch for automatic gzip decompression
 | 
			
		||||
   */
 | 
			
		||||
  private async fetchTickerList(): Promise<any> {
 | 
			
		||||
    // Check cache
 | 
			
		||||
@@ -230,29 +231,44 @@ export class SecEdgarProvider implements IFundamentalsProvider {
 | 
			
		||||
    // Wait for rate limit slot
 | 
			
		||||
    await this.rateLimiter.waitForSlot();
 | 
			
		||||
 | 
			
		||||
    // Fetch from SEC
 | 
			
		||||
    const response = await plugins.smartrequest.SmartRequest.create()
 | 
			
		||||
      .url(this.tickersUrl)
 | 
			
		||||
      .headers({
 | 
			
		||||
        'User-Agent': this.userAgent,
 | 
			
		||||
        'Accept': 'application/json'
 | 
			
		||||
      })
 | 
			
		||||
      .timeout(this.config.timeout)
 | 
			
		||||
      .get();
 | 
			
		||||
    // Fetch from SEC using native fetch (handles gzip automatically)
 | 
			
		||||
    const controller = new AbortController();
 | 
			
		||||
    const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
 | 
			
		||||
 | 
			
		||||
    const data = await response.json();
 | 
			
		||||
    try {
 | 
			
		||||
      const response = await fetch(this.tickersUrl, {
 | 
			
		||||
        headers: {
 | 
			
		||||
          'User-Agent': this.userAgent,
 | 
			
		||||
          'Accept': 'application/json'
 | 
			
		||||
          // Note: Accept-Encoding is set automatically by fetch
 | 
			
		||||
        },
 | 
			
		||||
        signal: controller.signal
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    // Cache the list
 | 
			
		||||
    this.tickerListCache = {
 | 
			
		||||
      data,
 | 
			
		||||
      timestamp: new Date()
 | 
			
		||||
    };
 | 
			
		||||
      clearTimeout(timeoutId);
 | 
			
		||||
 | 
			
		||||
    return data;
 | 
			
		||||
      if (!response.ok) {
 | 
			
		||||
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const data = await response.json();
 | 
			
		||||
 | 
			
		||||
      // Cache the list
 | 
			
		||||
      this.tickerListCache = {
 | 
			
		||||
        data,
 | 
			
		||||
        timestamp: new Date()
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      return data;
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      clearTimeout(timeoutId);
 | 
			
		||||
      throw error;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Fetch company facts from SEC EDGAR
 | 
			
		||||
   * Uses native fetch for automatic gzip decompression
 | 
			
		||||
   */
 | 
			
		||||
  private async fetchCompanyFacts(cik: string): Promise<any> {
 | 
			
		||||
    // Pad CIK to 10 digits
 | 
			
		||||
@@ -262,26 +278,39 @@ export class SecEdgarProvider implements IFundamentalsProvider {
 | 
			
		||||
    // Wait for rate limit slot
 | 
			
		||||
    await this.rateLimiter.waitForSlot();
 | 
			
		||||
 | 
			
		||||
    // Fetch from SEC
 | 
			
		||||
    const response = await plugins.smartrequest.SmartRequest.create()
 | 
			
		||||
      .url(url)
 | 
			
		||||
      .headers({
 | 
			
		||||
        'User-Agent': this.userAgent,
 | 
			
		||||
        'Accept': 'application/json',
 | 
			
		||||
        'Accept-Encoding': 'gzip, deflate',
 | 
			
		||||
        'Host': 'data.sec.gov'
 | 
			
		||||
      })
 | 
			
		||||
      .timeout(this.config.timeout)
 | 
			
		||||
      .get();
 | 
			
		||||
    // Fetch from SEC using native fetch (handles gzip automatically)
 | 
			
		||||
    const controller = new AbortController();
 | 
			
		||||
    const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
 | 
			
		||||
 | 
			
		||||
    const data = await response.json();
 | 
			
		||||
    try {
 | 
			
		||||
      const response = await fetch(url, {
 | 
			
		||||
        headers: {
 | 
			
		||||
          'User-Agent': this.userAgent,
 | 
			
		||||
          'Accept': 'application/json',
 | 
			
		||||
          'Host': 'data.sec.gov'
 | 
			
		||||
          // Note: Accept-Encoding is set automatically by fetch and gzip is handled transparently
 | 
			
		||||
        },
 | 
			
		||||
        signal: controller.signal
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    // Validate response
 | 
			
		||||
    if (!data || !data.facts) {
 | 
			
		||||
      throw new Error('Invalid response from SEC EDGAR API');
 | 
			
		||||
      clearTimeout(timeoutId);
 | 
			
		||||
 | 
			
		||||
      if (!response.ok) {
 | 
			
		||||
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const data = await response.json();
 | 
			
		||||
 | 
			
		||||
      // Validate response
 | 
			
		||||
      if (!data || !data.facts) {
 | 
			
		||||
        throw new Error('Invalid response from SEC EDGAR API');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return data;
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      clearTimeout(timeoutId);
 | 
			
		||||
      throw error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -382,20 +411,29 @@ export class SecEdgarProvider implements IFundamentalsProvider {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Check if SEC EDGAR API is available
 | 
			
		||||
   * Uses native fetch for automatic gzip decompression
 | 
			
		||||
   */
 | 
			
		||||
  public async isAvailable(): Promise<boolean> {
 | 
			
		||||
    try {
 | 
			
		||||
      // Test with Apple's well-known CIK
 | 
			
		||||
      const url = `${this.baseUrl}/companyfacts/CIK0000320193.json`;
 | 
			
		||||
 | 
			
		||||
      const response = await plugins.smartrequest.SmartRequest.create()
 | 
			
		||||
        .url(url)
 | 
			
		||||
        .headers({
 | 
			
		||||
      const controller = new AbortController();
 | 
			
		||||
      const timeoutId = setTimeout(() => controller.abort(), 5000);
 | 
			
		||||
 | 
			
		||||
      const response = await fetch(url, {
 | 
			
		||||
        headers: {
 | 
			
		||||
          'User-Agent': this.userAgent,
 | 
			
		||||
          'Accept': 'application/json'
 | 
			
		||||
        })
 | 
			
		||||
        .timeout(5000)
 | 
			
		||||
        .get();
 | 
			
		||||
        },
 | 
			
		||||
        signal: controller.signal
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      clearTimeout(timeoutId);
 | 
			
		||||
 | 
			
		||||
      if (!response.ok) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const data = await response.json();
 | 
			
		||||
      return data && data.facts !== undefined;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user