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:
@@ -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.'
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// node native scope
|
||||
import * as path from 'path';
|
||||
import * as path from 'node:path';
|
||||
|
||||
export {
|
||||
path,
|
||||
|
||||
@@ -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