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