@adaptic/utils 0.0.1005 → 0.0.1006

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
@@ -2175,6 +2175,419 @@ function validateAlphaVantageApiKey(apiKey) {
2175
2175
  }
2176
2176
  }
2177
2177
 
2178
+ const DEFAULT_RETRY_CONFIG = {
2179
+ maxRetries: 3,
2180
+ baseDelayMs: 1000,
2181
+ maxDelayMs: 30000,
2182
+ retryableStatusCodes: [429, 500, 502, 503, 504],
2183
+ retryOnNetworkError: true,
2184
+ };
2185
+ /**
2186
+ * Node.js / undici / system error codes that represent transient network
2187
+ * conditions. Present on `error.code` for net/http/dns/undici errors.
2188
+ */
2189
+ const RETRYABLE_ERROR_CODES = new Set([
2190
+ "ETIMEDOUT",
2191
+ "ESOCKETTIMEDOUT",
2192
+ "ECONNRESET",
2193
+ "ECONNREFUSED",
2194
+ "EHOSTUNREACH",
2195
+ "ENETUNREACH",
2196
+ "EAI_AGAIN",
2197
+ "EPIPE",
2198
+ "ECONNABORTED",
2199
+ "ENOTFOUND",
2200
+ "UND_ERR_CONNECT_TIMEOUT",
2201
+ "UND_ERR_HEADERS_TIMEOUT",
2202
+ "UND_ERR_BODY_TIMEOUT",
2203
+ "UND_ERR_SOCKET",
2204
+ "UND_ERR_CLOSED",
2205
+ "UND_ERR_REQ_CONTENT_LENGTH_MISMATCH",
2206
+ ]);
2207
+ /**
2208
+ * Error constructor names / `error.name` values that indicate transient
2209
+ * abort / timeout conditions.
2210
+ */
2211
+ const RETRYABLE_ERROR_NAMES = new Set([
2212
+ "AbortError",
2213
+ "TimeoutError",
2214
+ "FetchError",
2215
+ "RequestTimeoutError",
2216
+ "ConnectTimeoutError",
2217
+ "HeadersTimeoutError",
2218
+ "BodyTimeoutError",
2219
+ ]);
2220
+ /**
2221
+ * Message-pattern fallback for libraries that discard error codes/names but
2222
+ * preserve text (e.g., some Apollo/axios wrappers).
2223
+ */
2224
+ const RETRYABLE_MESSAGE_PATTERNS = [
2225
+ /aborted/i,
2226
+ /timeout/i,
2227
+ /timed out/i,
2228
+ /network error/i,
2229
+ /socket hang up/i,
2230
+ /connection (reset|refused|closed)/i,
2231
+ /ECONNRESET/,
2232
+ /ETIMEDOUT/,
2233
+ /ECONNREFUSED/,
2234
+ /EAI_AGAIN/,
2235
+ /UND_ERR_/,
2236
+ ];
2237
+ /**
2238
+ * Walks the `error.cause` chain (capped to avoid cycles) and tests whether
2239
+ * any link along the chain looks like a transient network error. Modern APIs
2240
+ * (undici, fetch, Apollo Client 3.8+) wrap the root network failure as a
2241
+ * `.cause`, so the surface `Error` may report a generic message while the
2242
+ * actionable signal lives one or more levels deeper.
2243
+ *
2244
+ * Exported for use by downstream consumers (engine services, per-call catch
2245
+ * blocks, application-level loggers) that need to demote recoverable
2246
+ * transient errors from ERROR to WARN. Aligns the whole stack on a single
2247
+ * canonical classifier so MassiveAPI, AlpacaAPI, and application-layer
2248
+ * retry handlers all treat the same network blips identically.
2249
+ */
2250
+ function isTransientNetworkError(error) {
2251
+ const MAX_CAUSE_DEPTH = 6;
2252
+ let current = error;
2253
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH && current; depth++) {
2254
+ if (current instanceof Error || typeof current === "object") {
2255
+ const err = current;
2256
+ if (typeof err.name === "string" && RETRYABLE_ERROR_NAMES.has(err.name)) {
2257
+ return true;
2258
+ }
2259
+ if (typeof err.code === "string" && RETRYABLE_ERROR_CODES.has(err.code)) {
2260
+ return true;
2261
+ }
2262
+ if (typeof err.message === "string") {
2263
+ for (const pattern of RETRYABLE_MESSAGE_PATTERNS) {
2264
+ if (pattern.test(err.message)) {
2265
+ return true;
2266
+ }
2267
+ }
2268
+ }
2269
+ current = err.cause;
2270
+ }
2271
+ else {
2272
+ break;
2273
+ }
2274
+ }
2275
+ return false;
2276
+ }
2277
+ /**
2278
+ * Analyzes an error and determines if it's retryable.
2279
+ * @param error - The error to analyze
2280
+ * @param response - Optional Response object for HTTP errors
2281
+ * @param config - Retry configuration
2282
+ * @returns Structured error details
2283
+ */
2284
+ function analyzeError(error, response, config) {
2285
+ // Handle Response objects with error status codes
2286
+ if (response && !response.ok) {
2287
+ const status = response.status;
2288
+ // Rate limit errors - always retryable
2289
+ if (status === 429) {
2290
+ const retryAfterHeader = response.headers.get("Retry-After");
2291
+ const retryAfter = retryAfterHeader
2292
+ ? parseInt(retryAfterHeader, 10) * 1000
2293
+ : undefined;
2294
+ return {
2295
+ type: "RATE_LIMIT",
2296
+ reason: "Rate limit exceeded",
2297
+ status,
2298
+ retryAfter,
2299
+ isRetryable: true,
2300
+ };
2301
+ }
2302
+ // Authentication errors - never retry
2303
+ if (status === 401 || status === 403) {
2304
+ return {
2305
+ type: "AUTH_ERROR",
2306
+ reason: status === 401
2307
+ ? "Authentication failed - invalid credentials"
2308
+ : "Access forbidden - insufficient permissions",
2309
+ status,
2310
+ isRetryable: false,
2311
+ };
2312
+ }
2313
+ // Server errors - check if in retryable list
2314
+ if (status >= 500 && status < 600) {
2315
+ return {
2316
+ type: "SERVER_ERROR",
2317
+ reason: `Server error (${status})`,
2318
+ status,
2319
+ isRetryable: config.retryableStatusCodes.includes(status),
2320
+ };
2321
+ }
2322
+ // Other client errors - never retry
2323
+ if (status >= 400 && status < 500) {
2324
+ return {
2325
+ type: "CLIENT_ERROR",
2326
+ reason: `Client error (${status})`,
2327
+ status,
2328
+ isRetryable: false,
2329
+ };
2330
+ }
2331
+ }
2332
+ // Handle network errors (TypeError from fetch API)
2333
+ if (error instanceof TypeError && error.message.includes("fetch")) {
2334
+ return {
2335
+ type: "NETWORK_ERROR",
2336
+ reason: "Network connectivity issue",
2337
+ status: null,
2338
+ isRetryable: config.retryOnNetworkError,
2339
+ };
2340
+ }
2341
+ // Handle transient network conditions: AbortError, TimeoutError,
2342
+ // Node/undici error codes (ETIMEDOUT, ECONNRESET, UND_ERR_*), and
2343
+ // wrapped failures exposed via error.cause. This catches the broad class
2344
+ // of infrastructure flakes that the TypeError-only check above misses.
2345
+ if (isTransientNetworkError(error)) {
2346
+ const reason = error instanceof Error ? error.message : "Transient network error";
2347
+ return {
2348
+ type: "NETWORK_ERROR",
2349
+ reason,
2350
+ status: null,
2351
+ isRetryable: config.retryOnNetworkError,
2352
+ };
2353
+ }
2354
+ // Handle error objects with messages
2355
+ if (error instanceof Error) {
2356
+ // Parse error messages that might contain status information
2357
+ if (error.message.includes("429") || error.message.includes("RATE_LIMIT")) {
2358
+ const match = error.message.match(/RATE_LIMIT: 429:(\d+)/);
2359
+ const retryAfter = match ? parseInt(match[1], 10) : undefined;
2360
+ return {
2361
+ type: "RATE_LIMIT",
2362
+ reason: "Rate limit exceeded",
2363
+ status: 429,
2364
+ retryAfter,
2365
+ isRetryable: true,
2366
+ };
2367
+ }
2368
+ if (error.message.includes("401") ||
2369
+ error.message.includes("403") ||
2370
+ error.message.includes("AUTH_ERROR")) {
2371
+ const status = error.message.includes("401") ? 401 : 403;
2372
+ return {
2373
+ type: "AUTH_ERROR",
2374
+ reason: `Authentication error (${status})`,
2375
+ status,
2376
+ isRetryable: false,
2377
+ };
2378
+ }
2379
+ if (error.message.includes("SERVER_ERROR") ||
2380
+ error.message.match(/50[0-9]/)) {
2381
+ const statusMatch = error.message.match(/50[0-9]/);
2382
+ const status = statusMatch ? parseInt(statusMatch[0], 10) : 500;
2383
+ return {
2384
+ type: "SERVER_ERROR",
2385
+ reason: `Server error (${status})`,
2386
+ status,
2387
+ isRetryable: config.retryableStatusCodes.includes(status),
2388
+ };
2389
+ }
2390
+ if (error.message.includes("network") ||
2391
+ error.message.includes("NETWORK_ERROR")) {
2392
+ return {
2393
+ type: "NETWORK_ERROR",
2394
+ reason: error.message,
2395
+ status: null,
2396
+ isRetryable: config.retryOnNetworkError,
2397
+ };
2398
+ }
2399
+ }
2400
+ // Unknown error - not retryable by default for safety
2401
+ return {
2402
+ type: "UNKNOWN",
2403
+ reason: error instanceof Error ? error.message : String(error),
2404
+ status: null,
2405
+ isRetryable: false,
2406
+ };
2407
+ }
2408
+ /**
2409
+ * Calculates the delay before the next retry attempt using exponential backoff with jitter.
2410
+ * @param attempt - Current attempt number (1-indexed)
2411
+ * @param baseDelay - Base delay in milliseconds
2412
+ * @param maxDelay - Maximum delay in milliseconds
2413
+ * @returns Delay in milliseconds
2414
+ */
2415
+ function calculateBackoff(attempt, baseDelay, maxDelay) {
2416
+ // Exponential backoff: baseDelay * 2^(attempt-1)
2417
+ const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
2418
+ // Cap at maxDelay
2419
+ const cappedDelay = Math.min(exponentialDelay, maxDelay);
2420
+ // Add jitter (random value between 0% and 25% of the delay)
2421
+ const jitter = Math.random() * cappedDelay * 0.25;
2422
+ return Math.floor(cappedDelay + jitter);
2423
+ }
2424
+ /**
2425
+ * Wraps an async function with retry logic and exponential backoff.
2426
+ *
2427
+ * This utility handles transient errors in external API calls by automatically retrying
2428
+ * failed requests with intelligent backoff strategies. It respects rate limit headers,
2429
+ * fails fast on non-retryable errors, and provides detailed logging.
2430
+ *
2431
+ * @template T - The return type of the wrapped function
2432
+ * @param fn - The async function to wrap with retry logic
2433
+ * @param config - Retry configuration (merged with defaults)
2434
+ * @param label - A descriptive label for logging (e.g., 'Massive.fetchTickerInfo')
2435
+ * @returns A promise that resolves to the function's return value
2436
+ * @throws The last error encountered if all retries are exhausted
2437
+ *
2438
+ * @example
2439
+ * ```typescript
2440
+ * // Basic usage with defaults
2441
+ * const data = await withRetry(
2442
+ * async () => fetch('https://api.example.com/data'),
2443
+ * {},
2444
+ * 'ExampleAPI.fetchData'
2445
+ * );
2446
+ *
2447
+ * // Custom configuration for rate-limited API
2448
+ * const result = await withRetry(
2449
+ * async () => alphaVantageAPI.getQuote(symbol),
2450
+ * {
2451
+ * maxRetries: 5,
2452
+ * baseDelayMs: 5000,
2453
+ * maxDelayMs: 60000,
2454
+ * onRetry: (attempt, error) => {
2455
+ * getLogger().info(`Retry ${attempt} after error:`, error);
2456
+ * }
2457
+ * },
2458
+ * 'AlphaVantage.getQuote'
2459
+ * );
2460
+ * ```
2461
+ */
2462
+ async function withRetry(fn, config = {}, label = "unknown") {
2463
+ const fullConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
2464
+ let lastError;
2465
+ for (let attempt = 1; attempt <= fullConfig.maxRetries; attempt++) {
2466
+ try {
2467
+ const result = await fn();
2468
+ // If we succeeded after retries, log it
2469
+ if (attempt > 1) {
2470
+ getLogger().info(`[${label}] Succeeded on attempt ${attempt}/${fullConfig.maxRetries}`);
2471
+ }
2472
+ return result;
2473
+ }
2474
+ catch (error) {
2475
+ lastError = error;
2476
+ // If this is the last attempt, throw the error.
2477
+ // Transient network classes (undici/fetch timeouts, ECONNRESET,
2478
+ // AbortError, etc.) are self-healing at the upstream retry layer —
2479
+ // the caller re-invokes on the next refresh/poll tick. Logging them
2480
+ // at ERROR produces alert noise that does not represent actionable
2481
+ // failures. Demote the transient class to WARN with a recovery hint;
2482
+ // reserve ERROR for non-transient final failures (auth, schema,
2483
+ // contract violations, unknown classes).
2484
+ if (attempt === fullConfig.maxRetries) {
2485
+ const isTransient = isTransientNetworkError(error);
2486
+ const logMeta = {
2487
+ error: error instanceof Error ? error.message : String(error),
2488
+ attempts: fullConfig.maxRetries,
2489
+ timestamp: new Date().toISOString(),
2490
+ ...(isTransient
2491
+ ? {
2492
+ transient: true,
2493
+ recoveryHint: "Upstream caller should retry on next cycle",
2494
+ }
2495
+ : {}),
2496
+ };
2497
+ if (isTransient) {
2498
+ getLogger().warn(`[${label}] Failed after ${fullConfig.maxRetries} attempts (transient)`, logMeta);
2499
+ }
2500
+ else {
2501
+ getLogger().error(`[${label}] Failed after ${fullConfig.maxRetries} attempts`, logMeta);
2502
+ }
2503
+ throw error;
2504
+ }
2505
+ // Analyze the error to determine if we should retry
2506
+ const response = error instanceof Response ? error : null;
2507
+ const errorDetails = analyzeError(error, response, fullConfig);
2508
+ // If error is not retryable, fail immediately
2509
+ if (!errorDetails.isRetryable) {
2510
+ getLogger().error(`[${label}] Non-retryable error (${errorDetails.type})`, {
2511
+ reason: errorDetails.reason,
2512
+ status: errorDetails.status,
2513
+ timestamp: new Date().toISOString(),
2514
+ });
2515
+ throw error;
2516
+ }
2517
+ // Calculate delay for next retry
2518
+ let delayMs;
2519
+ if (errorDetails.type === "RATE_LIMIT" && errorDetails.retryAfter) {
2520
+ // Use Retry-After header if available
2521
+ delayMs = errorDetails.retryAfter;
2522
+ }
2523
+ else if (errorDetails.type === "RATE_LIMIT") {
2524
+ // For rate limits without Retry-After, use a longer minimum delay
2525
+ delayMs = Math.max(calculateBackoff(attempt, fullConfig.baseDelayMs, fullConfig.maxDelayMs), 5000);
2526
+ }
2527
+ else {
2528
+ // Standard exponential backoff with jitter
2529
+ delayMs = calculateBackoff(attempt, fullConfig.baseDelayMs, fullConfig.maxDelayMs);
2530
+ }
2531
+ // Log the retry attempt
2532
+ getLogger().warn(`[${label}] Attempt ${attempt}/${fullConfig.maxRetries} failed: ${errorDetails.reason}. Retrying in ${delayMs}ms...`, {
2533
+ attemptNumber: attempt,
2534
+ totalRetries: fullConfig.maxRetries,
2535
+ errorType: errorDetails.type,
2536
+ httpStatus: errorDetails.status,
2537
+ retryDelay: delayMs,
2538
+ timestamp: new Date().toISOString(),
2539
+ });
2540
+ // Call the optional retry callback
2541
+ if (fullConfig.onRetry) {
2542
+ fullConfig.onRetry(attempt, error);
2543
+ }
2544
+ // Wait before retrying
2545
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
2546
+ }
2547
+ }
2548
+ // This should never be reached due to the throw in the last attempt,
2549
+ // but TypeScript needs this to satisfy the return type
2550
+ throw lastError;
2551
+ }
2552
+ /**
2553
+ * API-specific retry configurations for different external services.
2554
+ * These configurations are tuned based on each API's rate limits and characteristics.
2555
+ */
2556
+ const API_RETRY_CONFIGS = {
2557
+ /** Massive.com API - 5 requests/second rate limit */
2558
+ MASSIVE: {
2559
+ maxRetries: 3,
2560
+ baseDelayMs: 1000,
2561
+ maxDelayMs: 30000,
2562
+ retryableStatusCodes: [429, 500, 502, 503, 504],
2563
+ retryOnNetworkError: true,
2564
+ },
2565
+ /** Alpha Vantage API - 5 requests/minute rate limit (more strict) */
2566
+ ALPHA_VANTAGE: {
2567
+ maxRetries: 5,
2568
+ baseDelayMs: 5000,
2569
+ maxDelayMs: 60000,
2570
+ retryableStatusCodes: [429, 500, 502, 503, 504],
2571
+ retryOnNetworkError: true,
2572
+ },
2573
+ /** Alpaca API - generally reliable, shorter retry window */
2574
+ ALPACA: {
2575
+ maxRetries: 3,
2576
+ baseDelayMs: 1000,
2577
+ maxDelayMs: 30000,
2578
+ retryableStatusCodes: [429, 500, 502, 503, 504],
2579
+ retryOnNetworkError: true,
2580
+ },
2581
+ /** Generic crypto API configuration */
2582
+ CRYPTO: {
2583
+ maxRetries: 3,
2584
+ baseDelayMs: 1000,
2585
+ maxDelayMs: 30000,
2586
+ retryableStatusCodes: [429, 500, 502, 503, 504],
2587
+ retryOnNetworkError: true,
2588
+ },
2589
+ };
2590
+
2178
2591
  /**
2179
2592
  * Structured error type hierarchy for all API integrations
2180
2593
  *
@@ -2719,6 +3132,32 @@ const rateLimiters = {
2719
3132
  }),
2720
3133
  };
2721
3134
 
3135
+ /**
3136
+ * Bounded retry for TRANSIENT network faults on Alpaca market-data reads.
3137
+ *
3138
+ * 2026-07-30: the engine logged 637 `TypeError: fetch failed` / `read
3139
+ * ECONNRESET` errors against `/v2/stocks/bars` in a single session (~4 per
3140
+ * minute). Every OTHER Alpaca/Massive client in this package already routes
3141
+ * transient faults through {@link isTransientNetworkError} — this client was
3142
+ * the one that never adopted it, so a single reset socket killed the whole
3143
+ * read. Those failures surface in the engine's market-tape prompt context and
3144
+ * its intraday direction/path resolvers, so the decision model judged with a
3145
+ * blind tape: raw confidence collapsed from a 0.677 median to a degenerate
3146
+ * ~0.504 band, the entire distribution fell below the 0.55 admission floor,
3147
+ * and live participation went to near zero.
3148
+ *
3149
+ * `ECONNRESET` on a keep-alive pool is the classic idle-socket race — the
3150
+ * server closes a pooled connection while the client dispatches onto it. It
3151
+ * is transient by construction and safe to retry on an idempotent GET.
3152
+ */
3153
+ const TRANSIENT_NETWORK_RETRY_ATTEMPTS = 3;
3154
+ /** Base backoff (ms); doubled per attempt with full jitter. */
3155
+ const TRANSIENT_NETWORK_RETRY_BASE_MS = 120;
3156
+ /** Full-jitter backoff so concurrent fan-out retries do not resonate. */
3157
+ function transientRetryDelayMs(attempt) {
3158
+ const ceiling = TRANSIENT_NETWORK_RETRY_BASE_MS * 2 ** attempt;
3159
+ return Math.floor(Math.random() * ceiling);
3160
+ }
2722
3161
  const log$l = (message, options = { type: "info" }) => {
2723
3162
  log$m(message, { ...options, source: "AlpacaMarketDataAPI" });
2724
3163
  };
@@ -3229,11 +3668,42 @@ class AlpacaMarketDataAPI extends EventEmitter {
3229
3668
  // can't overrun Alpaca's server-side rate limit. Prior to this, parallel
3230
3669
  // historical-bar fan-out produced ~125 server-side 429s per minute.
3231
3670
  await rateLimiters.alpaca.acquire();
3232
- const response = await fetch(url.toString(), {
3233
- method,
3234
- headers: this.headers,
3235
- signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
3236
- });
3671
+ // Retry ONLY transient connection faults, and only on GET (every
3672
+ // market-data read here is idempotent). A non-2xx response is a real
3673
+ // answer from Alpaca and is never retried — that path still throws on
3674
+ // the first attempt exactly as before.
3675
+ let response;
3676
+ let lastNetworkError;
3677
+ for (let attempt = 0; attempt < TRANSIENT_NETWORK_RETRY_ATTEMPTS; attempt += 1) {
3678
+ try {
3679
+ response = await fetch(url.toString(), {
3680
+ method,
3681
+ headers: this.headers,
3682
+ signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
3683
+ });
3684
+ break;
3685
+ }
3686
+ catch (networkErr) {
3687
+ lastNetworkError = networkErr;
3688
+ const retryable = method === "GET" &&
3689
+ isTransientNetworkError(networkErr) &&
3690
+ attempt < TRANSIENT_NETWORK_RETRY_ATTEMPTS - 1;
3691
+ if (!retryable) {
3692
+ throw networkErr;
3693
+ }
3694
+ const delayMs = transientRetryDelayMs(attempt);
3695
+ log$l(`Transient network fault on ${endpoint} (attempt ${attempt + 1}/${TRANSIENT_NETWORK_RETRY_ATTEMPTS}); retrying in ${delayMs}ms`, { type: "warn" });
3696
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
3697
+ // Re-acquire the rate-limit token so a retry storm cannot overrun
3698
+ // Alpaca's server-side limit.
3699
+ await rateLimiters.alpaca.acquire();
3700
+ }
3701
+ }
3702
+ if (!response) {
3703
+ throw lastNetworkError instanceof Error
3704
+ ? lastNetworkError
3705
+ : new Error(`Market Data API request failed for ${endpoint}`);
3706
+ }
3237
3707
  if (!response.ok) {
3238
3708
  const errorText = await response.text();
3239
3709
  log$l(`Market Data API error (${response.status}): ${errorText}`, {
@@ -3965,419 +4435,6 @@ class AlpacaMarketDataAPI extends EventEmitter {
3965
4435
  // Export the singleton instance
3966
4436
  const marketDataAPI = AlpacaMarketDataAPI.getInstance();
3967
4437
 
3968
- const DEFAULT_RETRY_CONFIG = {
3969
- maxRetries: 3,
3970
- baseDelayMs: 1000,
3971
- maxDelayMs: 30000,
3972
- retryableStatusCodes: [429, 500, 502, 503, 504],
3973
- retryOnNetworkError: true,
3974
- };
3975
- /**
3976
- * Node.js / undici / system error codes that represent transient network
3977
- * conditions. Present on `error.code` for net/http/dns/undici errors.
3978
- */
3979
- const RETRYABLE_ERROR_CODES = new Set([
3980
- "ETIMEDOUT",
3981
- "ESOCKETTIMEDOUT",
3982
- "ECONNRESET",
3983
- "ECONNREFUSED",
3984
- "EHOSTUNREACH",
3985
- "ENETUNREACH",
3986
- "EAI_AGAIN",
3987
- "EPIPE",
3988
- "ECONNABORTED",
3989
- "ENOTFOUND",
3990
- "UND_ERR_CONNECT_TIMEOUT",
3991
- "UND_ERR_HEADERS_TIMEOUT",
3992
- "UND_ERR_BODY_TIMEOUT",
3993
- "UND_ERR_SOCKET",
3994
- "UND_ERR_CLOSED",
3995
- "UND_ERR_REQ_CONTENT_LENGTH_MISMATCH",
3996
- ]);
3997
- /**
3998
- * Error constructor names / `error.name` values that indicate transient
3999
- * abort / timeout conditions.
4000
- */
4001
- const RETRYABLE_ERROR_NAMES = new Set([
4002
- "AbortError",
4003
- "TimeoutError",
4004
- "FetchError",
4005
- "RequestTimeoutError",
4006
- "ConnectTimeoutError",
4007
- "HeadersTimeoutError",
4008
- "BodyTimeoutError",
4009
- ]);
4010
- /**
4011
- * Message-pattern fallback for libraries that discard error codes/names but
4012
- * preserve text (e.g., some Apollo/axios wrappers).
4013
- */
4014
- const RETRYABLE_MESSAGE_PATTERNS = [
4015
- /aborted/i,
4016
- /timeout/i,
4017
- /timed out/i,
4018
- /network error/i,
4019
- /socket hang up/i,
4020
- /connection (reset|refused|closed)/i,
4021
- /ECONNRESET/,
4022
- /ETIMEDOUT/,
4023
- /ECONNREFUSED/,
4024
- /EAI_AGAIN/,
4025
- /UND_ERR_/,
4026
- ];
4027
- /**
4028
- * Walks the `error.cause` chain (capped to avoid cycles) and tests whether
4029
- * any link along the chain looks like a transient network error. Modern APIs
4030
- * (undici, fetch, Apollo Client 3.8+) wrap the root network failure as a
4031
- * `.cause`, so the surface `Error` may report a generic message while the
4032
- * actionable signal lives one or more levels deeper.
4033
- *
4034
- * Exported for use by downstream consumers (engine services, per-call catch
4035
- * blocks, application-level loggers) that need to demote recoverable
4036
- * transient errors from ERROR to WARN. Aligns the whole stack on a single
4037
- * canonical classifier so MassiveAPI, AlpacaAPI, and application-layer
4038
- * retry handlers all treat the same network blips identically.
4039
- */
4040
- function isTransientNetworkError(error) {
4041
- const MAX_CAUSE_DEPTH = 6;
4042
- let current = error;
4043
- for (let depth = 0; depth < MAX_CAUSE_DEPTH && current; depth++) {
4044
- if (current instanceof Error || typeof current === "object") {
4045
- const err = current;
4046
- if (typeof err.name === "string" && RETRYABLE_ERROR_NAMES.has(err.name)) {
4047
- return true;
4048
- }
4049
- if (typeof err.code === "string" && RETRYABLE_ERROR_CODES.has(err.code)) {
4050
- return true;
4051
- }
4052
- if (typeof err.message === "string") {
4053
- for (const pattern of RETRYABLE_MESSAGE_PATTERNS) {
4054
- if (pattern.test(err.message)) {
4055
- return true;
4056
- }
4057
- }
4058
- }
4059
- current = err.cause;
4060
- }
4061
- else {
4062
- break;
4063
- }
4064
- }
4065
- return false;
4066
- }
4067
- /**
4068
- * Analyzes an error and determines if it's retryable.
4069
- * @param error - The error to analyze
4070
- * @param response - Optional Response object for HTTP errors
4071
- * @param config - Retry configuration
4072
- * @returns Structured error details
4073
- */
4074
- function analyzeError(error, response, config) {
4075
- // Handle Response objects with error status codes
4076
- if (response && !response.ok) {
4077
- const status = response.status;
4078
- // Rate limit errors - always retryable
4079
- if (status === 429) {
4080
- const retryAfterHeader = response.headers.get("Retry-After");
4081
- const retryAfter = retryAfterHeader
4082
- ? parseInt(retryAfterHeader, 10) * 1000
4083
- : undefined;
4084
- return {
4085
- type: "RATE_LIMIT",
4086
- reason: "Rate limit exceeded",
4087
- status,
4088
- retryAfter,
4089
- isRetryable: true,
4090
- };
4091
- }
4092
- // Authentication errors - never retry
4093
- if (status === 401 || status === 403) {
4094
- return {
4095
- type: "AUTH_ERROR",
4096
- reason: status === 401
4097
- ? "Authentication failed - invalid credentials"
4098
- : "Access forbidden - insufficient permissions",
4099
- status,
4100
- isRetryable: false,
4101
- };
4102
- }
4103
- // Server errors - check if in retryable list
4104
- if (status >= 500 && status < 600) {
4105
- return {
4106
- type: "SERVER_ERROR",
4107
- reason: `Server error (${status})`,
4108
- status,
4109
- isRetryable: config.retryableStatusCodes.includes(status),
4110
- };
4111
- }
4112
- // Other client errors - never retry
4113
- if (status >= 400 && status < 500) {
4114
- return {
4115
- type: "CLIENT_ERROR",
4116
- reason: `Client error (${status})`,
4117
- status,
4118
- isRetryable: false,
4119
- };
4120
- }
4121
- }
4122
- // Handle network errors (TypeError from fetch API)
4123
- if (error instanceof TypeError && error.message.includes("fetch")) {
4124
- return {
4125
- type: "NETWORK_ERROR",
4126
- reason: "Network connectivity issue",
4127
- status: null,
4128
- isRetryable: config.retryOnNetworkError,
4129
- };
4130
- }
4131
- // Handle transient network conditions: AbortError, TimeoutError,
4132
- // Node/undici error codes (ETIMEDOUT, ECONNRESET, UND_ERR_*), and
4133
- // wrapped failures exposed via error.cause. This catches the broad class
4134
- // of infrastructure flakes that the TypeError-only check above misses.
4135
- if (isTransientNetworkError(error)) {
4136
- const reason = error instanceof Error ? error.message : "Transient network error";
4137
- return {
4138
- type: "NETWORK_ERROR",
4139
- reason,
4140
- status: null,
4141
- isRetryable: config.retryOnNetworkError,
4142
- };
4143
- }
4144
- // Handle error objects with messages
4145
- if (error instanceof Error) {
4146
- // Parse error messages that might contain status information
4147
- if (error.message.includes("429") || error.message.includes("RATE_LIMIT")) {
4148
- const match = error.message.match(/RATE_LIMIT: 429:(\d+)/);
4149
- const retryAfter = match ? parseInt(match[1], 10) : undefined;
4150
- return {
4151
- type: "RATE_LIMIT",
4152
- reason: "Rate limit exceeded",
4153
- status: 429,
4154
- retryAfter,
4155
- isRetryable: true,
4156
- };
4157
- }
4158
- if (error.message.includes("401") ||
4159
- error.message.includes("403") ||
4160
- error.message.includes("AUTH_ERROR")) {
4161
- const status = error.message.includes("401") ? 401 : 403;
4162
- return {
4163
- type: "AUTH_ERROR",
4164
- reason: `Authentication error (${status})`,
4165
- status,
4166
- isRetryable: false,
4167
- };
4168
- }
4169
- if (error.message.includes("SERVER_ERROR") ||
4170
- error.message.match(/50[0-9]/)) {
4171
- const statusMatch = error.message.match(/50[0-9]/);
4172
- const status = statusMatch ? parseInt(statusMatch[0], 10) : 500;
4173
- return {
4174
- type: "SERVER_ERROR",
4175
- reason: `Server error (${status})`,
4176
- status,
4177
- isRetryable: config.retryableStatusCodes.includes(status),
4178
- };
4179
- }
4180
- if (error.message.includes("network") ||
4181
- error.message.includes("NETWORK_ERROR")) {
4182
- return {
4183
- type: "NETWORK_ERROR",
4184
- reason: error.message,
4185
- status: null,
4186
- isRetryable: config.retryOnNetworkError,
4187
- };
4188
- }
4189
- }
4190
- // Unknown error - not retryable by default for safety
4191
- return {
4192
- type: "UNKNOWN",
4193
- reason: error instanceof Error ? error.message : String(error),
4194
- status: null,
4195
- isRetryable: false,
4196
- };
4197
- }
4198
- /**
4199
- * Calculates the delay before the next retry attempt using exponential backoff with jitter.
4200
- * @param attempt - Current attempt number (1-indexed)
4201
- * @param baseDelay - Base delay in milliseconds
4202
- * @param maxDelay - Maximum delay in milliseconds
4203
- * @returns Delay in milliseconds
4204
- */
4205
- function calculateBackoff(attempt, baseDelay, maxDelay) {
4206
- // Exponential backoff: baseDelay * 2^(attempt-1)
4207
- const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
4208
- // Cap at maxDelay
4209
- const cappedDelay = Math.min(exponentialDelay, maxDelay);
4210
- // Add jitter (random value between 0% and 25% of the delay)
4211
- const jitter = Math.random() * cappedDelay * 0.25;
4212
- return Math.floor(cappedDelay + jitter);
4213
- }
4214
- /**
4215
- * Wraps an async function with retry logic and exponential backoff.
4216
- *
4217
- * This utility handles transient errors in external API calls by automatically retrying
4218
- * failed requests with intelligent backoff strategies. It respects rate limit headers,
4219
- * fails fast on non-retryable errors, and provides detailed logging.
4220
- *
4221
- * @template T - The return type of the wrapped function
4222
- * @param fn - The async function to wrap with retry logic
4223
- * @param config - Retry configuration (merged with defaults)
4224
- * @param label - A descriptive label for logging (e.g., 'Massive.fetchTickerInfo')
4225
- * @returns A promise that resolves to the function's return value
4226
- * @throws The last error encountered if all retries are exhausted
4227
- *
4228
- * @example
4229
- * ```typescript
4230
- * // Basic usage with defaults
4231
- * const data = await withRetry(
4232
- * async () => fetch('https://api.example.com/data'),
4233
- * {},
4234
- * 'ExampleAPI.fetchData'
4235
- * );
4236
- *
4237
- * // Custom configuration for rate-limited API
4238
- * const result = await withRetry(
4239
- * async () => alphaVantageAPI.getQuote(symbol),
4240
- * {
4241
- * maxRetries: 5,
4242
- * baseDelayMs: 5000,
4243
- * maxDelayMs: 60000,
4244
- * onRetry: (attempt, error) => {
4245
- * getLogger().info(`Retry ${attempt} after error:`, error);
4246
- * }
4247
- * },
4248
- * 'AlphaVantage.getQuote'
4249
- * );
4250
- * ```
4251
- */
4252
- async function withRetry(fn, config = {}, label = "unknown") {
4253
- const fullConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
4254
- let lastError;
4255
- for (let attempt = 1; attempt <= fullConfig.maxRetries; attempt++) {
4256
- try {
4257
- const result = await fn();
4258
- // If we succeeded after retries, log it
4259
- if (attempt > 1) {
4260
- getLogger().info(`[${label}] Succeeded on attempt ${attempt}/${fullConfig.maxRetries}`);
4261
- }
4262
- return result;
4263
- }
4264
- catch (error) {
4265
- lastError = error;
4266
- // If this is the last attempt, throw the error.
4267
- // Transient network classes (undici/fetch timeouts, ECONNRESET,
4268
- // AbortError, etc.) are self-healing at the upstream retry layer —
4269
- // the caller re-invokes on the next refresh/poll tick. Logging them
4270
- // at ERROR produces alert noise that does not represent actionable
4271
- // failures. Demote the transient class to WARN with a recovery hint;
4272
- // reserve ERROR for non-transient final failures (auth, schema,
4273
- // contract violations, unknown classes).
4274
- if (attempt === fullConfig.maxRetries) {
4275
- const isTransient = isTransientNetworkError(error);
4276
- const logMeta = {
4277
- error: error instanceof Error ? error.message : String(error),
4278
- attempts: fullConfig.maxRetries,
4279
- timestamp: new Date().toISOString(),
4280
- ...(isTransient
4281
- ? {
4282
- transient: true,
4283
- recoveryHint: "Upstream caller should retry on next cycle",
4284
- }
4285
- : {}),
4286
- };
4287
- if (isTransient) {
4288
- getLogger().warn(`[${label}] Failed after ${fullConfig.maxRetries} attempts (transient)`, logMeta);
4289
- }
4290
- else {
4291
- getLogger().error(`[${label}] Failed after ${fullConfig.maxRetries} attempts`, logMeta);
4292
- }
4293
- throw error;
4294
- }
4295
- // Analyze the error to determine if we should retry
4296
- const response = error instanceof Response ? error : null;
4297
- const errorDetails = analyzeError(error, response, fullConfig);
4298
- // If error is not retryable, fail immediately
4299
- if (!errorDetails.isRetryable) {
4300
- getLogger().error(`[${label}] Non-retryable error (${errorDetails.type})`, {
4301
- reason: errorDetails.reason,
4302
- status: errorDetails.status,
4303
- timestamp: new Date().toISOString(),
4304
- });
4305
- throw error;
4306
- }
4307
- // Calculate delay for next retry
4308
- let delayMs;
4309
- if (errorDetails.type === "RATE_LIMIT" && errorDetails.retryAfter) {
4310
- // Use Retry-After header if available
4311
- delayMs = errorDetails.retryAfter;
4312
- }
4313
- else if (errorDetails.type === "RATE_LIMIT") {
4314
- // For rate limits without Retry-After, use a longer minimum delay
4315
- delayMs = Math.max(calculateBackoff(attempt, fullConfig.baseDelayMs, fullConfig.maxDelayMs), 5000);
4316
- }
4317
- else {
4318
- // Standard exponential backoff with jitter
4319
- delayMs = calculateBackoff(attempt, fullConfig.baseDelayMs, fullConfig.maxDelayMs);
4320
- }
4321
- // Log the retry attempt
4322
- getLogger().warn(`[${label}] Attempt ${attempt}/${fullConfig.maxRetries} failed: ${errorDetails.reason}. Retrying in ${delayMs}ms...`, {
4323
- attemptNumber: attempt,
4324
- totalRetries: fullConfig.maxRetries,
4325
- errorType: errorDetails.type,
4326
- httpStatus: errorDetails.status,
4327
- retryDelay: delayMs,
4328
- timestamp: new Date().toISOString(),
4329
- });
4330
- // Call the optional retry callback
4331
- if (fullConfig.onRetry) {
4332
- fullConfig.onRetry(attempt, error);
4333
- }
4334
- // Wait before retrying
4335
- await new Promise((resolve) => setTimeout(resolve, delayMs));
4336
- }
4337
- }
4338
- // This should never be reached due to the throw in the last attempt,
4339
- // but TypeScript needs this to satisfy the return type
4340
- throw lastError;
4341
- }
4342
- /**
4343
- * API-specific retry configurations for different external services.
4344
- * These configurations are tuned based on each API's rate limits and characteristics.
4345
- */
4346
- const API_RETRY_CONFIGS = {
4347
- /** Massive.com API - 5 requests/second rate limit */
4348
- MASSIVE: {
4349
- maxRetries: 3,
4350
- baseDelayMs: 1000,
4351
- maxDelayMs: 30000,
4352
- retryableStatusCodes: [429, 500, 502, 503, 504],
4353
- retryOnNetworkError: true,
4354
- },
4355
- /** Alpha Vantage API - 5 requests/minute rate limit (more strict) */
4356
- ALPHA_VANTAGE: {
4357
- maxRetries: 5,
4358
- baseDelayMs: 5000,
4359
- maxDelayMs: 60000,
4360
- retryableStatusCodes: [429, 500, 502, 503, 504],
4361
- retryOnNetworkError: true,
4362
- },
4363
- /** Alpaca API - generally reliable, shorter retry window */
4364
- ALPACA: {
4365
- maxRetries: 3,
4366
- baseDelayMs: 1000,
4367
- maxDelayMs: 30000,
4368
- retryableStatusCodes: [429, 500, 502, 503, 504],
4369
- retryOnNetworkError: true,
4370
- },
4371
- /** Generic crypto API configuration */
4372
- CRYPTO: {
4373
- maxRetries: 3,
4374
- baseDelayMs: 1000,
4375
- maxDelayMs: 30000,
4376
- retryableStatusCodes: [429, 500, 502, 503, 504],
4377
- retryOnNetworkError: true,
4378
- },
4379
- };
4380
-
4381
4438
  const limitPriceSlippagePercent100 = 0.1; // 0.1%
4382
4439
  /**
4383
4440
  * Alpaca's maximum page size for GET /orders — also our explicit default.
@@ -14561,17 +14618,17 @@ var hasRequiredBrowser;
14561
14618
  function requireBrowser () {
14562
14619
  if (hasRequiredBrowser) return browser.exports;
14563
14620
  hasRequiredBrowser = 1;
14564
- (function (module, exports) {
14621
+ (function (module, exports$1) {
14565
14622
  /**
14566
14623
  * This is the web browser implementation of `debug()`.
14567
14624
  */
14568
14625
 
14569
- exports.formatArgs = formatArgs;
14570
- exports.save = save;
14571
- exports.load = load;
14572
- exports.useColors = useColors;
14573
- exports.storage = localstorage();
14574
- exports.destroy = (() => {
14626
+ exports$1.formatArgs = formatArgs;
14627
+ exports$1.save = save;
14628
+ exports$1.load = load;
14629
+ exports$1.useColors = useColors;
14630
+ exports$1.storage = localstorage();
14631
+ exports$1.destroy = (() => {
14575
14632
  let warned = false;
14576
14633
 
14577
14634
  return () => {
@@ -14586,7 +14643,7 @@ function requireBrowser () {
14586
14643
  * Colors.
14587
14644
  */
14588
14645
 
14589
- exports.colors = [
14646
+ exports$1.colors = [
14590
14647
  '#0000CC',
14591
14648
  '#0000FF',
14592
14649
  '#0033CC',
@@ -14751,7 +14808,7 @@ function requireBrowser () {
14751
14808
  *
14752
14809
  * @api public
14753
14810
  */
14754
- exports.log = console.debug || console.log || (() => {});
14811
+ exports$1.log = console.debug || console.log || (() => {});
14755
14812
 
14756
14813
  /**
14757
14814
  * Save `namespaces`.
@@ -14762,9 +14819,9 @@ function requireBrowser () {
14762
14819
  function save(namespaces) {
14763
14820
  try {
14764
14821
  if (namespaces) {
14765
- exports.storage.setItem('debug', namespaces);
14822
+ exports$1.storage.setItem('debug', namespaces);
14766
14823
  } else {
14767
- exports.storage.removeItem('debug');
14824
+ exports$1.storage.removeItem('debug');
14768
14825
  }
14769
14826
  } catch (error) {
14770
14827
  // Swallow
@@ -14781,7 +14838,7 @@ function requireBrowser () {
14781
14838
  function load() {
14782
14839
  let r;
14783
14840
  try {
14784
- r = exports.storage.getItem('debug') || exports.storage.getItem('DEBUG') ;
14841
+ r = exports$1.storage.getItem('debug') || exports$1.storage.getItem('DEBUG') ;
14785
14842
  } catch (error) {
14786
14843
  // Swallow
14787
14844
  // XXX (@Qix-) should we be logging these?
@@ -14817,7 +14874,7 @@ function requireBrowser () {
14817
14874
  }
14818
14875
  }
14819
14876
 
14820
- module.exports = requireCommon()(exports);
14877
+ module.exports = requireCommon()(exports$1);
14821
14878
 
14822
14879
  const {formatters} = module.exports;
14823
14880
 
@@ -15006,7 +15063,7 @@ var hasRequiredNode$1;
15006
15063
  function requireNode$1 () {
15007
15064
  if (hasRequiredNode$1) return node$1.exports;
15008
15065
  hasRequiredNode$1 = 1;
15009
- (function (module, exports) {
15066
+ (function (module, exports$1) {
15010
15067
  const tty = require$$1$1;
15011
15068
  const util = require$$1$2;
15012
15069
 
@@ -15014,13 +15071,13 @@ function requireNode$1 () {
15014
15071
  * This is the Node.js implementation of `debug()`.
15015
15072
  */
15016
15073
 
15017
- exports.init = init;
15018
- exports.log = log;
15019
- exports.formatArgs = formatArgs;
15020
- exports.save = save;
15021
- exports.load = load;
15022
- exports.useColors = useColors;
15023
- exports.destroy = util.deprecate(
15074
+ exports$1.init = init;
15075
+ exports$1.log = log;
15076
+ exports$1.formatArgs = formatArgs;
15077
+ exports$1.save = save;
15078
+ exports$1.load = load;
15079
+ exports$1.useColors = useColors;
15080
+ exports$1.destroy = util.deprecate(
15024
15081
  () => {},
15025
15082
  'Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.'
15026
15083
  );
@@ -15029,7 +15086,7 @@ function requireNode$1 () {
15029
15086
  * Colors.
15030
15087
  */
15031
15088
 
15032
- exports.colors = [6, 2, 3, 4, 5, 1];
15089
+ exports$1.colors = [6, 2, 3, 4, 5, 1];
15033
15090
 
15034
15091
  try {
15035
15092
  // Optional dependency (as in, doesn't need to be installed, NOT like optionalDependencies in package.json)
@@ -15037,7 +15094,7 @@ function requireNode$1 () {
15037
15094
  const supportsColor = requireSupportsColor();
15038
15095
 
15039
15096
  if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) {
15040
- exports.colors = [
15097
+ exports$1.colors = [
15041
15098
  20,
15042
15099
  21,
15043
15100
  26,
@@ -15126,7 +15183,7 @@ function requireNode$1 () {
15126
15183
  * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
15127
15184
  */
15128
15185
 
15129
- exports.inspectOpts = Object.keys(process.env).filter(key => {
15186
+ exports$1.inspectOpts = Object.keys(process.env).filter(key => {
15130
15187
  return /^debug_/i.test(key);
15131
15188
  }).reduce((obj, key) => {
15132
15189
  // Camel-case
@@ -15158,8 +15215,8 @@ function requireNode$1 () {
15158
15215
  */
15159
15216
 
15160
15217
  function useColors() {
15161
- return 'colors' in exports.inspectOpts ?
15162
- Boolean(exports.inspectOpts.colors) :
15218
+ return 'colors' in exports$1.inspectOpts ?
15219
+ Boolean(exports$1.inspectOpts.colors) :
15163
15220
  tty.isatty(process.stderr.fd);
15164
15221
  }
15165
15222
 
@@ -15185,7 +15242,7 @@ function requireNode$1 () {
15185
15242
  }
15186
15243
 
15187
15244
  function getDate() {
15188
- if (exports.inspectOpts.hideDate) {
15245
+ if (exports$1.inspectOpts.hideDate) {
15189
15246
  return '';
15190
15247
  }
15191
15248
  return new Date().toISOString() + ' ';
@@ -15196,7 +15253,7 @@ function requireNode$1 () {
15196
15253
  */
15197
15254
 
15198
15255
  function log(...args) {
15199
- return process.stderr.write(util.formatWithOptions(exports.inspectOpts, ...args) + '\n');
15256
+ return process.stderr.write(util.formatWithOptions(exports$1.inspectOpts, ...args) + '\n');
15200
15257
  }
15201
15258
 
15202
15259
  /**
@@ -15236,13 +15293,13 @@ function requireNode$1 () {
15236
15293
  function init(debug) {
15237
15294
  debug.inspectOpts = {};
15238
15295
 
15239
- const keys = Object.keys(exports.inspectOpts);
15296
+ const keys = Object.keys(exports$1.inspectOpts);
15240
15297
  for (let i = 0; i < keys.length; i++) {
15241
- debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
15298
+ debug.inspectOpts[keys[i]] = exports$1.inspectOpts[keys[i]];
15242
15299
  }
15243
15300
  }
15244
15301
 
15245
- module.exports = requireCommon()(exports);
15302
+ module.exports = requireCommon()(exports$1);
15246
15303
 
15247
15304
  const {formatters} = module.exports;
15248
15305
 
@@ -15816,7 +15873,7 @@ function requireFollowRedirects () {
15816
15873
  // Wraps the key/value object of protocols with redirect functionality
15817
15874
  function wrap(protocols) {
15818
15875
  // Default settings
15819
- var exports = {
15876
+ var exports$1 = {
15820
15877
  maxRedirects: 21,
15821
15878
  maxBodyLength: 10 * 1024 * 1024,
15822
15879
  };
@@ -15826,7 +15883,7 @@ function requireFollowRedirects () {
15826
15883
  Object.keys(protocols).forEach(function (scheme) {
15827
15884
  var protocol = scheme + ":";
15828
15885
  var nativeProtocol = nativeProtocols[protocol] = protocols[scheme];
15829
- var wrappedProtocol = exports[scheme] = Object.create(nativeProtocol);
15886
+ var wrappedProtocol = exports$1[scheme] = Object.create(nativeProtocol);
15830
15887
 
15831
15888
  // Executes a request, following redirects
15832
15889
  function request(input, options, callback) {
@@ -15849,8 +15906,8 @@ function requireFollowRedirects () {
15849
15906
 
15850
15907
  // Set defaults
15851
15908
  options = Object.assign({
15852
- maxRedirects: exports.maxRedirects,
15853
- maxBodyLength: exports.maxBodyLength,
15909
+ maxRedirects: exports$1.maxRedirects,
15910
+ maxBodyLength: exports$1.maxBodyLength,
15854
15911
  }, input, options);
15855
15912
  options.nativeProtocols = nativeProtocols;
15856
15913
  if (!isString(options.host) && !isString(options.hostname)) {
@@ -15875,7 +15932,7 @@ function requireFollowRedirects () {
15875
15932
  get: { value: get, configurable: true, enumerable: true, writable: true },
15876
15933
  });
15877
15934
  });
15878
- return exports;
15935
+ return exports$1;
15879
15936
  }
15880
15937
 
15881
15938
  function noop() { /* empty */ }
@@ -17481,7 +17538,7 @@ var hasRequiredLodash;
17481
17538
  function requireLodash () {
17482
17539
  if (hasRequiredLodash) return lodash$1.exports;
17483
17540
  hasRequiredLodash = 1;
17484
- (function (module, exports) {
17541
+ (function (module, exports$1) {
17485
17542
  (function() {
17486
17543
 
17487
17544
  /** Used as a safe reference for `undefined` in pre-ES5 environments. */
@@ -17912,7 +17969,7 @@ function requireLodash () {
17912
17969
  var root = freeGlobal || freeSelf || Function('return this')();
17913
17970
 
17914
17971
  /** Detect free variable `exports`. */
17915
- var freeExports = exports && !exports.nodeType && exports;
17972
+ var freeExports = exports$1 && !exports$1.nodeType && exports$1;
17916
17973
 
17917
17974
  /** Detect free variable `module`. */
17918
17975
  var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module;
@@ -35742,12 +35799,12 @@ var hasRequiredIsBuffer;
35742
35799
  function requireIsBuffer () {
35743
35800
  if (hasRequiredIsBuffer) return isBuffer.exports;
35744
35801
  hasRequiredIsBuffer = 1;
35745
- (function (module, exports) {
35802
+ (function (module, exports$1) {
35746
35803
  var root = require_root(),
35747
35804
  stubFalse = requireStubFalse();
35748
35805
 
35749
35806
  /** Detect free variable `exports`. */
35750
- var freeExports = exports && !exports.nodeType && exports;
35807
+ var freeExports = exports$1 && !exports$1.nodeType && exports$1;
35751
35808
 
35752
35809
  /** Detect free variable `module`. */
35753
35810
  var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module;
@@ -35967,11 +36024,11 @@ var hasRequired_nodeUtil;
35967
36024
  function require_nodeUtil () {
35968
36025
  if (hasRequired_nodeUtil) return _nodeUtil.exports;
35969
36026
  hasRequired_nodeUtil = 1;
35970
- (function (module, exports) {
36027
+ (function (module, exports$1) {
35971
36028
  var freeGlobal = require_freeGlobal();
35972
36029
 
35973
36030
  /** Detect free variable `exports`. */
35974
- var freeExports = exports && !exports.nodeType && exports;
36031
+ var freeExports = exports$1 && !exports$1.nodeType && exports$1;
35975
36032
 
35976
36033
  /** Detect free variable `module`. */
35977
36034
  var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module;
@@ -44893,7 +44950,7 @@ var hasRequiredSafeBuffer;
44893
44950
  function requireSafeBuffer () {
44894
44951
  if (hasRequiredSafeBuffer) return safeBuffer.exports;
44895
44952
  hasRequiredSafeBuffer = 1;
44896
- (function (module, exports) {
44953
+ (function (module, exports$1) {
44897
44954
  /* eslint-disable node/no-deprecated-api */
44898
44955
  var buffer = require$$0$5;
44899
44956
  var Buffer = buffer.Buffer;
@@ -44908,8 +44965,8 @@ function requireSafeBuffer () {
44908
44965
  module.exports = buffer;
44909
44966
  } else {
44910
44967
  // Copy properties from require('buffer')
44911
- copyProps(buffer, exports);
44912
- exports.Buffer = SafeBuffer;
44968
+ copyProps(buffer, exports$1);
44969
+ exports$1.Buffer = SafeBuffer;
44913
44970
  }
44914
44971
 
44915
44972
  function SafeBuffer (arg, encodingOrOffset, length) {
@@ -48107,22 +48164,22 @@ var hasRequiredReadable;
48107
48164
  function requireReadable () {
48108
48165
  if (hasRequiredReadable) return readable.exports;
48109
48166
  hasRequiredReadable = 1;
48110
- (function (module, exports) {
48167
+ (function (module, exports$1) {
48111
48168
  var Stream = require$$0$3;
48112
48169
  if (process.env.READABLE_STREAM === 'disable' && Stream) {
48113
48170
  module.exports = Stream.Readable;
48114
48171
  Object.assign(module.exports, Stream);
48115
48172
  module.exports.Stream = Stream;
48116
48173
  } else {
48117
- exports = module.exports = require_stream_readable();
48118
- exports.Stream = Stream || exports;
48119
- exports.Readable = exports;
48120
- exports.Writable = require_stream_writable();
48121
- exports.Duplex = require_stream_duplex();
48122
- exports.Transform = require_stream_transform();
48123
- exports.PassThrough = require_stream_passthrough();
48124
- exports.finished = requireEndOfStream();
48125
- exports.pipeline = requirePipeline();
48174
+ exports$1 = module.exports = require_stream_readable();
48175
+ exports$1.Stream = Stream || exports$1;
48176
+ exports$1.Readable = exports$1;
48177
+ exports$1.Writable = require_stream_writable();
48178
+ exports$1.Duplex = require_stream_duplex();
48179
+ exports$1.Transform = require_stream_transform();
48180
+ exports$1.PassThrough = require_stream_passthrough();
48181
+ exports$1.finished = requireEndOfStream();
48182
+ exports$1.pipeline = requirePipeline();
48126
48183
  }
48127
48184
  } (readable, readable.exports));
48128
48185
  return readable.exports;
@@ -49557,12 +49614,12 @@ var hasRequiredWebsocket;
49557
49614
  function requireWebsocket () {
49558
49615
  if (hasRequiredWebsocket) return websocket$1;
49559
49616
  hasRequiredWebsocket = 1;
49560
- (function (exports) {
49617
+ (function (exports$1) {
49561
49618
  var __importDefault = (websocket$1 && websocket$1.__importDefault) || function (mod) {
49562
49619
  return (mod && mod.__esModule) ? mod : { "default": mod };
49563
49620
  };
49564
- Object.defineProperty(exports, "__esModule", { value: true });
49565
- exports.AlpacaWebsocket = exports.ERROR = exports.CONN_ERROR = exports.EVENT = exports.STATE = void 0;
49621
+ Object.defineProperty(exports$1, "__esModule", { value: true });
49622
+ exports$1.AlpacaWebsocket = exports$1.ERROR = exports$1.CONN_ERROR = exports$1.EVENT = exports$1.STATE = void 0;
49566
49623
  const events_1 = __importDefault(require$$0$4);
49567
49624
  const ws_1 = __importDefault(requireWs());
49568
49625
  const msgpack5_1 = __importDefault(requireMsgpack5());
@@ -49576,7 +49633,7 @@ function requireWebsocket () {
49576
49633
  STATE["DISCONNECTED"] = "disconnected";
49577
49634
  STATE["WAITING_TO_CONNECT"] = "waiting to connect";
49578
49635
  STATE["WAITING_TO_RECONNECT"] = "waiting to reconnect";
49579
- })(STATE || (exports.STATE = STATE = {}));
49636
+ })(STATE || (exports$1.STATE = STATE = {}));
49580
49637
  // Client events
49581
49638
  var EVENT;
49582
49639
  (function (EVENT) {
@@ -49595,9 +49652,9 @@ function requireWebsocket () {
49595
49652
  EVENT["CORRECTIONS"] = "corrections";
49596
49653
  EVENT["ORDERBOOKS"] = "orderbooks";
49597
49654
  EVENT["NEWS"] = "news";
49598
- })(EVENT || (exports.EVENT = EVENT = {}));
49655
+ })(EVENT || (exports$1.EVENT = EVENT = {}));
49599
49656
  // Connection errors by code
49600
- exports.CONN_ERROR = new Map([
49657
+ exports$1.CONN_ERROR = new Map([
49601
49658
  [400, "invalid syntax"],
49602
49659
  [401, "not authenticated"],
49603
49660
  [402, "auth failed"],
@@ -49616,7 +49673,7 @@ function requireWebsocket () {
49616
49673
  ERROR["MISSING_SECERT_KEY"] = "missing secret key";
49617
49674
  ERROR["MISSING_API_KEY"] = "missing api key";
49618
49675
  ERROR["UNEXPECTED_MESSAGE"] = "unexpected message";
49619
- })(ERROR || (exports.ERROR = ERROR = {}));
49676
+ })(ERROR || (exports$1.ERROR = ERROR = {}));
49620
49677
  class AlpacaWebsocket extends events_1.default.EventEmitter {
49621
49678
  constructor(options) {
49622
49679
  super();
@@ -49773,7 +49830,7 @@ function requireWebsocket () {
49773
49830
  this.updateSubscriptions(data[0]);
49774
49831
  break;
49775
49832
  case "error":
49776
- this.emit(EVENT.CLIENT_ERROR, exports.CONN_ERROR.get(data[0].code));
49833
+ this.emit(EVENT.CLIENT_ERROR, exports$1.CONN_ERROR.get(data[0].code));
49777
49834
  break;
49778
49835
  default:
49779
49836
  this.dataHandler(data);
@@ -49802,7 +49859,7 @@ function requireWebsocket () {
49802
49859
  }
49803
49860
  }
49804
49861
  }
49805
- exports.AlpacaWebsocket = AlpacaWebsocket;
49862
+ exports$1.AlpacaWebsocket = AlpacaWebsocket;
49806
49863
  } (websocket$1));
49807
49864
  return websocket$1;
49808
49865
  }
@@ -50436,8 +50493,8 @@ var hasRequiredWebsockets;
50436
50493
  function requireWebsockets () {
50437
50494
  if (hasRequiredWebsockets) return websockets;
50438
50495
  hasRequiredWebsockets = 1;
50439
- (function (exports) {
50440
- Object.defineProperty(exports, "__esModule", { value: true });
50496
+ (function (exports$1) {
50497
+ Object.defineProperty(exports$1, "__esModule", { value: true });
50441
50498
  const events = require$$0$4;
50442
50499
  const WebSocket = requireWs();
50443
50500
  const entity = requireEntity();
@@ -50452,7 +50509,7 @@ function requireWebsockets () {
50452
50509
  STATE.DISCONNECTED = "disconnected";
50453
50510
  STATE.WAITING_TO_CONNECT = "waiting to connect";
50454
50511
  STATE.WAITING_TO_RECONNECT = "waiting to reconnect";
50455
- })((STATE = exports.STATE || (exports.STATE = {})));
50512
+ })((STATE = exports$1.STATE || (exports$1.STATE = {})));
50456
50513
  // Client events
50457
50514
  var EVENT;
50458
50515
  (function (EVENT) {
@@ -50466,7 +50523,7 @@ function requireWebsockets () {
50466
50523
  EVENT.STOCK_QUOTES = "stock_quotes";
50467
50524
  EVENT.STOCK_AGG_SEC = "stock_agg_sec";
50468
50525
  EVENT.STOCK_AGG_MIN = "stock_agg_min";
50469
- })((EVENT = exports.EVENT || (exports.EVENT = {})));
50526
+ })((EVENT = exports$1.EVENT || (exports$1.EVENT = {})));
50470
50527
  // Connection errors Each of these will also emit EVENT.ERROR
50471
50528
  var ERROR;
50472
50529
  (function (ERROR) {
@@ -50475,7 +50532,7 @@ function requireWebsockets () {
50475
50532
  ERROR.MISSING_API_KEY = "missing api key";
50476
50533
  ERROR.MISSING_SECRET_KEY = "missing secret key";
50477
50534
  ERROR.UNKNOWN = "unknown error";
50478
- })((ERROR = exports.ERROR || (exports.ERROR = {})));
50535
+ })((ERROR = exports$1.ERROR || (exports$1.ERROR = {})));
50479
50536
  /**
50480
50537
  * AlpacaStreamClient manages a connection to Alpaca's websocket api
50481
50538
  */
@@ -50788,7 +50845,7 @@ function requireWebsockets () {
50788
50845
  }
50789
50846
  }
50790
50847
  }
50791
- exports.AlpacaStreamClient = AlpacaStreamClient;
50848
+ exports$1.AlpacaStreamClient = AlpacaStreamClient;
50792
50849
  } (websockets));
50793
50850
  return websockets;
50794
50851
  }
@@ -51040,13 +51097,13 @@ var hasRequiredDist;
51040
51097
  function requireDist () {
51041
51098
  if (hasRequiredDist) return dist$1.exports;
51042
51099
  hasRequiredDist = 1;
51043
- (function (module, exports) {
51100
+ (function (module, exports$1) {
51044
51101
  var __importDefault = (dist && dist.__importDefault) || function (mod) {
51045
51102
  return (mod && mod.__esModule) ? mod : { "default": mod };
51046
51103
  };
51047
- Object.defineProperty(exports, "__esModule", { value: true });
51104
+ Object.defineProperty(exports$1, "__esModule", { value: true });
51048
51105
  const alpaca_trade_api_1 = __importDefault(requireAlpacaTradeApi());
51049
- exports.default = alpaca_trade_api_1.default;
51106
+ exports$1.default = alpaca_trade_api_1.default;
51050
51107
  module.exports = alpaca_trade_api_1.default;
51051
51108
  } (dist$1, dist$1.exports));
51052
51109
  return dist$1.exports;