@adaptic/utils 0.0.905 → 0.0.907

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
@@ -1315,7 +1315,7 @@ async function fetchWithRetry(url, options = {}, retries = 3, initialBackoff = 1
1315
1315
  */
1316
1316
  async function validatePolygonApiKey(apiKey) {
1317
1317
  try {
1318
- const response = await fetch(`https://api.polygon.io/v1/meta/symbols?apikey=${apiKey}&limit=1`);
1318
+ const response = await fetch(`https://api.massive.com/v1/meta/symbols?apikey=${apiKey}&limit=1`);
1319
1319
  if (response.status === 401) {
1320
1320
  throw new Error("Invalid or expired Polygon.io API key");
1321
1321
  }
@@ -5187,7 +5187,7 @@ const fetchTickerInfo = async (symbol, options) => {
5187
5187
  }
5188
5188
  const apiKey = options?.apiKey || POLYGON_API_KEY;
5189
5189
  validatePolygonApiKey$1(apiKey);
5190
- const baseUrl = `https://api.polygon.io/v3/reference/tickers/${encodeURIComponent(symbol)}`;
5190
+ const baseUrl = `https://api.massive.com/v3/reference/tickers/${encodeURIComponent(symbol)}`;
5191
5191
  const params = new URLSearchParams({
5192
5192
  apiKey,
5193
5193
  });
@@ -5274,7 +5274,7 @@ const fetchLastTrade = async (symbol, options) => {
5274
5274
  }
5275
5275
  const apiKey = options?.apiKey || POLYGON_API_KEY;
5276
5276
  validatePolygonApiKey$1(apiKey);
5277
- const baseUrl = `https://api.polygon.io/v2/last/trade/${encodeURIComponent(symbol)}`;
5277
+ const baseUrl = `https://api.massive.com/v2/last/trade/${encodeURIComponent(symbol)}`;
5278
5278
  const params = new URLSearchParams({
5279
5279
  apiKey,
5280
5280
  });
@@ -5339,7 +5339,7 @@ const fetchPrices = async (params, options) => {
5339
5339
  const apiKey = options?.apiKey || POLYGON_API_KEY;
5340
5340
  validatePolygonApiKey$1(apiKey);
5341
5341
  const { ticker, start, end = Date.now().valueOf(), multiplier, timespan, limit = 1000, } = params;
5342
- const baseUrl = `https://api.polygon.io/v2/aggs/ticker/${encodeURIComponent(ticker)}/range/${multiplier}/${timespan}/${start}/${end}`;
5342
+ const baseUrl = `https://api.massive.com/v2/aggs/ticker/${encodeURIComponent(ticker)}/range/${multiplier}/${timespan}/${start}/${end}`;
5343
5343
  const urlParams = new URLSearchParams({
5344
5344
  apiKey,
5345
5345
  adjusted: "true",
@@ -5461,7 +5461,7 @@ const fetchGroupedDaily = async (date, options) => {
5461
5461
  if (!options?.apiKey && !POLYGON_API_KEY) {
5462
5462
  throw new Error("Polygon API key is missing");
5463
5463
  }
5464
- const baseUrl = `https://api.polygon.io/v2/aggs/grouped/locale/us/market/stocks/${date}`;
5464
+ const baseUrl = `https://api.massive.com/v2/aggs/grouped/locale/us/market/stocks/${date}`;
5465
5465
  const params = new URLSearchParams({
5466
5466
  apiKey: options?.apiKey || POLYGON_API_KEY,
5467
5467
  adjusted: options?.adjusted !== false ? "true" : "false",
@@ -5554,7 +5554,7 @@ symbol, date = new Date(), options) => {
5554
5554
  throw new Error("Polygon API key is missing");
5555
5555
  }
5556
5556
  const formattedDate = date.toISOString().split("T")[0]; // Format as YYYY-MM-DD
5557
- const baseUrl = `https://api.polygon.io/v1/open-close/${encodeURIComponent(symbol)}/${formattedDate}`;
5557
+ const baseUrl = `https://api.massive.com/v1/open-close/${encodeURIComponent(symbol)}/${formattedDate}`;
5558
5558
  const params = new URLSearchParams({
5559
5559
  apiKey: options?.apiKey || POLYGON_API_KEY,
5560
5560
  adjusted: (options?.adjusted ?? true).toString(),
@@ -5604,7 +5604,7 @@ const fetchTrades = async (symbol, options) => {
5604
5604
  if (!options?.apiKey && !POLYGON_API_KEY) {
5605
5605
  throw new Error("Polygon API key is missing");
5606
5606
  }
5607
- const baseUrl = `https://api.polygon.io/v3/trades/${encodeURIComponent(symbol)}`;
5607
+ const baseUrl = `https://api.massive.com/v3/trades/${encodeURIComponent(symbol)}`;
5608
5608
  const params = new URLSearchParams({
5609
5609
  apiKey: options?.apiKey || POLYGON_API_KEY,
5610
5610
  });
@@ -5670,7 +5670,7 @@ const { ALPACA_INDICES_API_KEY } = process.env;
5670
5670
  const POLYGON_INDICES_CONCURRENCY_LIMIT = 5;
5671
5671
  const polygonIndicesLimit = pLimit(POLYGON_INDICES_CONCURRENCY_LIMIT);
5672
5672
  // Base URL for Polygon API
5673
- const POLYGON_API_BASE_URL = "https://api.polygon.io";
5673
+ const POLYGON_API_BASE_URL = "https://api.massive.com";
5674
5674
  /**
5675
5675
  * Validates that an API key is available
5676
5676
  * @param {string | undefined} apiKey - Optional API key to use
@@ -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;
@@ -15202,7 +15234,7 @@ const rateLimiters = {
15202
15234
  *
15203
15235
  * Configured for 5 requests per second (basic plan).
15204
15236
  * Adjust if you have a different subscription tier.
15205
- * See: https://polygon.io/pricing
15237
+ * See: https://massive.com/pricing
15206
15238
  */
15207
15239
  polygon: new TokenBucketRateLimiter({
15208
15240
  maxTokens: 5,