@adaptic/utils 0.0.905 → 0.0.906
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +38 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +38 -6
- package/dist/index.mjs.map +1 -1
- package/dist/test.js +38 -6
- package/dist/test.js.map +1 -1
- package/dist/types/alpaca-market-data-api.d.ts +3 -0
- package/dist/types/alpaca-market-data-api.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -12357,6 +12357,8 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12357
12357
|
quotes: [],
|
|
12358
12358
|
bars: [],
|
|
12359
12359
|
};
|
|
12360
|
+
reconnectAttempts = {};
|
|
12361
|
+
reconnectTimers = {};
|
|
12360
12362
|
setMode(mode = "production") {
|
|
12361
12363
|
if (mode === "sandbox") {
|
|
12362
12364
|
// sandbox mode
|
|
@@ -12395,7 +12397,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12395
12397
|
// when env vars are not available. Features will be unavailable until
|
|
12396
12398
|
// credentials are provided at runtime.
|
|
12397
12399
|
const apiKey = process.env.ALPACA_API_KEY || "";
|
|
12398
|
-
const apiSecret = process.env.ALPACA_SECRET_KEY || "";
|
|
12400
|
+
const apiSecret = process.env.ALPACA_API_SECRET || process.env.ALPACA_SECRET_KEY || "";
|
|
12399
12401
|
this.credentialsValid = validateAlpacaCredentials({
|
|
12400
12402
|
apiKey,
|
|
12401
12403
|
apiSecret,
|
|
@@ -12437,6 +12439,12 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12437
12439
|
else {
|
|
12438
12440
|
url = this.cryptoStreamUrl;
|
|
12439
12441
|
}
|
|
12442
|
+
const apiKey = process.env.ALPACA_API_KEY || "";
|
|
12443
|
+
const apiSecret = process.env.ALPACA_API_SECRET || process.env.ALPACA_SECRET_KEY || "";
|
|
12444
|
+
if (!apiKey || !apiSecret) {
|
|
12445
|
+
log$l(`Cannot connect ${streamType} stream: missing Alpaca credentials (ALPACA_API_KEY=${apiKey ? "set" : "MISSING"}, ALPACA_API_SECRET/ALPACA_SECRET_KEY=${apiSecret ? "set" : "MISSING"})`, { type: "error" });
|
|
12446
|
+
return;
|
|
12447
|
+
}
|
|
12440
12448
|
const ws = new WebSocket(url);
|
|
12441
12449
|
if (streamType === "stock") {
|
|
12442
12450
|
this.stockWs = ws;
|
|
@@ -12451,8 +12459,8 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12451
12459
|
log$l(`${streamType} stream connected`, { type: "info" });
|
|
12452
12460
|
const authMessage = {
|
|
12453
12461
|
action: "auth",
|
|
12454
|
-
key:
|
|
12455
|
-
secret:
|
|
12462
|
+
key: apiKey,
|
|
12463
|
+
secret: apiSecret,
|
|
12456
12464
|
};
|
|
12457
12465
|
ws.send(JSON.stringify(authMessage));
|
|
12458
12466
|
});
|
|
@@ -12469,6 +12477,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12469
12477
|
for (const message of messages) {
|
|
12470
12478
|
if (message.T === "success" && message.msg === "authenticated") {
|
|
12471
12479
|
log$l(`${streamType} stream authenticated`, { type: "info" });
|
|
12480
|
+
this.reconnectAttempts[streamType] = 0;
|
|
12472
12481
|
this.sendSubscription(streamType);
|
|
12473
12482
|
}
|
|
12474
12483
|
else if (message.T === "success" && message.msg === "connected") {
|
|
@@ -12491,8 +12500,8 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12491
12500
|
}
|
|
12492
12501
|
}
|
|
12493
12502
|
});
|
|
12494
|
-
ws.on("close", () => {
|
|
12495
|
-
log$l(`${streamType} stream disconnected`, { type: "warn" });
|
|
12503
|
+
ws.on("close", (code) => {
|
|
12504
|
+
log$l(`${streamType} stream disconnected (code: ${code})`, { type: "warn" });
|
|
12496
12505
|
if (streamType === "stock") {
|
|
12497
12506
|
this.stockWs = null;
|
|
12498
12507
|
}
|
|
@@ -12502,12 +12511,35 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12502
12511
|
else {
|
|
12503
12512
|
this.cryptoWs = null;
|
|
12504
12513
|
}
|
|
12505
|
-
//
|
|
12514
|
+
// Reconnect with exponential backoff (unless intentionally closed with code 1000)
|
|
12515
|
+
if (code !== 1000) {
|
|
12516
|
+
this.scheduleReconnect(streamType);
|
|
12517
|
+
}
|
|
12506
12518
|
});
|
|
12507
12519
|
ws.on("error", (error) => {
|
|
12508
12520
|
log$l(`${streamType} stream error: ${error.message}`, { type: "error" });
|
|
12509
12521
|
});
|
|
12510
12522
|
}
|
|
12523
|
+
scheduleReconnect(streamType) {
|
|
12524
|
+
const attempts = this.reconnectAttempts[streamType] ?? 0;
|
|
12525
|
+
const maxAttempts = 10;
|
|
12526
|
+
if (attempts >= maxAttempts) {
|
|
12527
|
+
log$l(`${streamType} stream: max reconnect attempts (${maxAttempts}) reached, giving up`, { type: "error" });
|
|
12528
|
+
return;
|
|
12529
|
+
}
|
|
12530
|
+
// Exponential backoff: 1s, 2s, 4s, 8s, 16s, 30s (capped)
|
|
12531
|
+
const delayMs = Math.min(1000 * Math.pow(2, attempts), 30000);
|
|
12532
|
+
this.reconnectAttempts[streamType] = attempts + 1;
|
|
12533
|
+
log$l(`${streamType} stream: scheduling reconnect attempt ${attempts + 1}/${maxAttempts} in ${delayMs}ms`, { type: "info" });
|
|
12534
|
+
// Clear any existing reconnect timer for this stream
|
|
12535
|
+
if (this.reconnectTimers[streamType]) {
|
|
12536
|
+
clearTimeout(this.reconnectTimers[streamType]);
|
|
12537
|
+
}
|
|
12538
|
+
this.reconnectTimers[streamType] = setTimeout(() => {
|
|
12539
|
+
log$l(`${streamType} stream: reconnecting (attempt ${attempts + 1}/${maxAttempts})`, { type: "info" });
|
|
12540
|
+
this.connect(streamType);
|
|
12541
|
+
}, delayMs);
|
|
12542
|
+
}
|
|
12511
12543
|
sendSubscription(streamType) {
|
|
12512
12544
|
let ws;
|
|
12513
12545
|
let subscriptions;
|