@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.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: process.env.ALPACA_API_KEY,
12455
- secret: process.env.ALPACA_SECRET_KEY,
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
- // Optional: implement reconnect logic
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;