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:
10
changelog.md
10
changelog.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-11-01 - 3.2.1 - 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
|
||||||
|
|
||||||
|
- SEC EDGAR provider: switch from SmartRequest to native fetch for ticker list and company facts, add AbortController-based timeouts, handle gzip automatically, improve response validation and error messages, and keep CIK/ticker-list caching
|
||||||
|
- Improve timeout and rate-limit handling in SecEdgarProvider (uses native fetch + explicit timeout clear), plus clearer logging on failures
|
||||||
|
- Update ts/plugins import to use node:path for Node compatibility
|
||||||
|
- Bump devDependencies: @git.zone/tsrun to ^1.6.2 and @git.zone/tstest to ^2.7.0; bump @push.rocks/smartrequest to ^4.3.4
|
||||||
|
- Add and refresh comprehensive test files (node/bun/deno variants) for fundamentals, marketstack, secedgar and stockdata services
|
||||||
|
- Add deno.lock (dependency lock) and a local .claude/settings.local.json for CI/permissions
|
||||||
|
|
||||||
## 2025-11-01 - 3.2.0 - feat(StockDataService)
|
## 2025-11-01 - 3.2.0 - feat(StockDataService)
|
||||||
Add unified StockDataService and BaseProviderService with new stockdata interfaces, provider integrations, tests and README updates
|
Add unified StockDataService and BaseProviderService with new stockdata interfaces, provider integrations, tests and README updates
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsbuild": "^2.6.8",
|
"@git.zone/tsbuild": "^2.6.8",
|
||||||
"@git.zone/tsbundle": "^2.5.1",
|
"@git.zone/tsbundle": "^2.5.1",
|
||||||
"@git.zone/tsrun": "^1.3.3",
|
"@git.zone/tsrun": "^1.6.2",
|
||||||
"@git.zone/tstest": "^2.4.2",
|
"@git.zone/tstest": "^2.7.0",
|
||||||
"@types/node": "^22.14.0"
|
"@types/node": "^22.14.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"@push.rocks/smartlog": "^3.1.10",
|
"@push.rocks/smartlog": "^3.1.10",
|
||||||
"@push.rocks/smartpath": "^6.0.0",
|
"@push.rocks/smartpath": "^6.0.0",
|
||||||
"@push.rocks/smartpromise": "^4.2.3",
|
"@push.rocks/smartpromise": "^4.2.3",
|
||||||
"@push.rocks/smartrequest": "^4.3.1",
|
"@push.rocks/smartrequest": "^4.3.4",
|
||||||
"@push.rocks/smartstream": "^3.2.5",
|
"@push.rocks/smartstream": "^3.2.5",
|
||||||
"@push.rocks/smartunique": "^3.0.9",
|
"@push.rocks/smartunique": "^3.0.9",
|
||||||
"@push.rocks/smartxml": "^1.1.1",
|
"@push.rocks/smartxml": "^1.1.1",
|
||||||
|
|||||||
1054
pnpm-lock.yaml
generated
1054
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@fin.cx/opendata',
|
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.'
|
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
|
// node native scope
|
||||||
import * as path from 'path';
|
import * as path from 'node:path';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
path,
|
path,
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ export class SecEdgarProvider implements IFundamentalsProvider {
|
|||||||
/**
|
/**
|
||||||
* Fetch the SEC ticker-to-CIK mapping list
|
* Fetch the SEC ticker-to-CIK mapping list
|
||||||
* Cached for 24 hours (list updates daily)
|
* Cached for 24 hours (list updates daily)
|
||||||
|
* Uses native fetch for automatic gzip decompression
|
||||||
*/
|
*/
|
||||||
private async fetchTickerList(): Promise<any> {
|
private async fetchTickerList(): Promise<any> {
|
||||||
// Check cache
|
// Check cache
|
||||||
@@ -230,15 +231,25 @@ export class SecEdgarProvider implements IFundamentalsProvider {
|
|||||||
// Wait for rate limit slot
|
// Wait for rate limit slot
|
||||||
await this.rateLimiter.waitForSlot();
|
await this.rateLimiter.waitForSlot();
|
||||||
|
|
||||||
// Fetch from SEC
|
// Fetch from SEC using native fetch (handles gzip automatically)
|
||||||
const response = await plugins.smartrequest.SmartRequest.create()
|
const controller = new AbortController();
|
||||||
.url(this.tickersUrl)
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
||||||
.headers({
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.tickersUrl, {
|
||||||
|
headers: {
|
||||||
'User-Agent': this.userAgent,
|
'User-Agent': this.userAgent,
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
})
|
// Note: Accept-Encoding is set automatically by fetch
|
||||||
.timeout(this.config.timeout)
|
},
|
||||||
.get();
|
signal: controller.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
@@ -249,10 +260,15 @@ export class SecEdgarProvider implements IFundamentalsProvider {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch company facts from SEC EDGAR
|
* Fetch company facts from SEC EDGAR
|
||||||
|
* Uses native fetch for automatic gzip decompression
|
||||||
*/
|
*/
|
||||||
private async fetchCompanyFacts(cik: string): Promise<any> {
|
private async fetchCompanyFacts(cik: string): Promise<any> {
|
||||||
// Pad CIK to 10 digits
|
// Pad CIK to 10 digits
|
||||||
@@ -262,17 +278,26 @@ export class SecEdgarProvider implements IFundamentalsProvider {
|
|||||||
// Wait for rate limit slot
|
// Wait for rate limit slot
|
||||||
await this.rateLimiter.waitForSlot();
|
await this.rateLimiter.waitForSlot();
|
||||||
|
|
||||||
// Fetch from SEC
|
// Fetch from SEC using native fetch (handles gzip automatically)
|
||||||
const response = await plugins.smartrequest.SmartRequest.create()
|
const controller = new AbortController();
|
||||||
.url(url)
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
||||||
.headers({
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
headers: {
|
||||||
'User-Agent': this.userAgent,
|
'User-Agent': this.userAgent,
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
'Accept-Encoding': 'gzip, deflate',
|
|
||||||
'Host': 'data.sec.gov'
|
'Host': 'data.sec.gov'
|
||||||
})
|
// Note: Accept-Encoding is set automatically by fetch and gzip is handled transparently
|
||||||
.timeout(this.config.timeout)
|
},
|
||||||
.get();
|
signal: controller.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
@@ -282,6 +307,10 @@ export class SecEdgarProvider implements IFundamentalsProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -382,20 +411,29 @@ export class SecEdgarProvider implements IFundamentalsProvider {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if SEC EDGAR API is available
|
* Check if SEC EDGAR API is available
|
||||||
|
* Uses native fetch for automatic gzip decompression
|
||||||
*/
|
*/
|
||||||
public async isAvailable(): Promise<boolean> {
|
public async isAvailable(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
// Test with Apple's well-known CIK
|
// Test with Apple's well-known CIK
|
||||||
const url = `${this.baseUrl}/companyfacts/CIK0000320193.json`;
|
const url = `${this.baseUrl}/companyfacts/CIK0000320193.json`;
|
||||||
|
|
||||||
const response = await plugins.smartrequest.SmartRequest.create()
|
const controller = new AbortController();
|
||||||
.url(url)
|
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
||||||
.headers({
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
headers: {
|
||||||
'User-Agent': this.userAgent,
|
'User-Agent': this.userAgent,
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
})
|
},
|
||||||
.timeout(5000)
|
signal: controller.signal
|
||||||
.get();
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data && data.facts !== undefined;
|
return data && data.facts !== undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user