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:
2025-11-01 14:54:04 +00:00
parent d49a738880
commit 54818293a1
11 changed files with 7812 additions and 587 deletions

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@fin.cx/opendata',
version: '3.2.0',
version: '3.2.1',
description: 'A comprehensive TypeScript library for accessing business data and real-time financial information. Features include German company data management with MongoDB integration, JSONL bulk processing, automated Handelsregister interactions, and real-time stock market data from multiple providers.'
}

View File

@@ -1,5 +1,5 @@
// node native scope
import * as path from 'path';
import * as path from 'node:path';
export {
path,

View File

@@ -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;