@apifuse/provider-sdk 2.2.0-beta.44 → 2.2.0-beta.46

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.
@@ -18,7 +18,7 @@ import {
18
18
  vendorFromResolvedSource,
19
19
  } from "../config/loader.js";
20
20
  import { SDKError, TransportError } from "../errors.js";
21
- import { getStealthProfile } from "../stealth/profiles.js";
21
+ import { getStealthProfile, getStealthProfileIntentAlias } from "../stealth/profiles.js";
22
22
  import type {
23
23
  HttpMethod,
24
24
  StealthClient,
@@ -67,7 +67,7 @@ import {
67
67
  serializeRequestUrl,
68
68
  } from "./request-options.js";
69
69
 
70
- export const DEFAULT_PROFILE = "chrome-146";
70
+ export const DEFAULT_PROFILE = "chrome-desktop";
71
71
 
72
72
  const MISSING_PROXY_WARNING =
73
73
  "[provider-sdk] Provider requested proxy routing, but no proxy URL was configured. Continuing without proxy.";
@@ -101,6 +101,8 @@ function sensitiveQueryParamNames(url: string): string[] {
101
101
 
102
102
  export type StealthClientOptions = ProxyResolutionOptions & {
103
103
  warn?: (message: string) => void;
104
+ /** Abort all requests issued by this client. */
105
+ signal?: AbortSignal;
104
106
  /**
105
107
  * Proxy-only stealth transport overrides. Use only for upstream proxy products
106
108
  * that terminate CONNECT with a private CA instead of tunneling the origin
@@ -244,7 +246,12 @@ function resolveDefaultWreqProfileMapping(): { identifier: string; os: Emulation
244
246
  return { identifier, os: profile.platform };
245
247
  }
246
248
 
247
- const DEFAULT_WREQ_PROFILE_MAPPING = resolveDefaultWreqProfileMapping();
249
+ let defaultWreqProfileMapping: ReturnType<typeof resolveDefaultWreqProfileMapping> | undefined;
250
+
251
+ function getDefaultWreqProfileMapping(): ReturnType<typeof resolveDefaultWreqProfileMapping> {
252
+ defaultWreqProfileMapping ??= resolveDefaultWreqProfileMapping();
253
+ return defaultWreqProfileMapping;
254
+ }
248
255
 
249
256
  export function resolveWreqProfile(
250
257
  profileName: string,
@@ -268,8 +275,9 @@ export function resolveWreqProfile(
268
275
  // profile strings still run with the transport default instead of failing
269
276
  // before the request starts. Removed built-in profile aliases above remain
270
277
  // explicit errors so callers do not accidentally pin retired fingerprints.
271
- identifier = DEFAULT_WREQ_PROFILE_MAPPING.identifier;
272
- os = DEFAULT_WREQ_PROFILE_MAPPING.os;
278
+ const defaultMapping = getDefaultWreqProfileMapping();
279
+ identifier = defaultMapping.identifier;
280
+ os = defaultMapping.os;
273
281
  }
274
282
 
275
283
  const browser = closestWreqProfile(identifier, wreqProfiles);
@@ -354,6 +362,15 @@ export async function normalizeResponse(
354
362
  response: StealthTransportResponse,
355
363
  requestUrl?: string,
356
364
  maxBodyBytes?: number,
365
+ ): Promise<StealthResponse> {
366
+ return normalizeResponseWithSignal(response, requestUrl, maxBodyBytes);
367
+ }
368
+
369
+ async function normalizeResponseWithSignal(
370
+ response: StealthTransportResponse,
371
+ requestUrl?: string,
372
+ maxBodyBytes?: number,
373
+ signal?: AbortSignal,
357
374
  ): Promise<StealthResponse> {
358
375
  const headers = Object.fromEntries(response.headers.entries());
359
376
  const cookies = new StealthCookieJar(
@@ -362,8 +379,8 @@ export async function normalizeResponse(
362
379
  );
363
380
  const bodyBytes =
364
381
  maxBodyBytes === undefined
365
- ? await response.arrayBuffer()
366
- : await readResponseBodyWithLimit(response, maxBodyBytes);
382
+ ? await readResponseArrayBuffer(response, signal)
383
+ : await readResponseBodyWithLimit(response, maxBodyBytes, signal);
367
384
  const body = new TextDecoder().decode(bodyBytes);
368
385
 
369
386
  return {
@@ -391,6 +408,42 @@ export async function normalizeResponse(
391
408
  };
392
409
  }
393
410
 
411
+ async function readResponseArrayBuffer(
412
+ response: StealthTransportResponse,
413
+ signal?: AbortSignal,
414
+ ): Promise<ArrayBuffer> {
415
+ if (!signal) return response.arrayBuffer();
416
+ throwIfAmbientAborted(signal);
417
+ return new Promise((resolve, reject) => {
418
+ let settled = false;
419
+ const settle = (operation: () => void) => {
420
+ if (settled) return;
421
+ settled = true;
422
+ signal.removeEventListener("abort", onAbort);
423
+ operation();
424
+ };
425
+ const onAbort = () => {
426
+ const error = toAmbientCancellationError(signal);
427
+ try {
428
+ void response.body?.cancel().catch(() => undefined);
429
+ } catch {
430
+ // Preserve the cancellation error if accessing or cancelling the body fails.
431
+ }
432
+ settle(() => reject(error));
433
+ };
434
+ signal.addEventListener("abort", onAbort, { once: true });
435
+ try {
436
+ void response.arrayBuffer().then(
437
+ (body) => settle(() => resolve(body)),
438
+ (error) => settle(() => reject(error)),
439
+ );
440
+ } catch (error) {
441
+ settle(() => reject(error));
442
+ }
443
+ if (signal.aborted) onAbort();
444
+ });
445
+ }
446
+
394
447
  function responseTooLargeError(maxBodyBytes: number, observedBytes: number): TransportError {
395
448
  return new TransportError(
396
449
  `Response body exceeded maxBodyBytes limit of ${maxBodyBytes} bytes (observed ${observedBytes} bytes)`,
@@ -413,7 +466,9 @@ function declaredContentLength(headers: StealthTransportHeaders): number | undef
413
466
  async function readResponseBodyWithLimit(
414
467
  response: StealthTransportResponse,
415
468
  maxBodyBytes: number,
469
+ signal?: AbortSignal,
416
470
  ): Promise<ArrayBuffer> {
471
+ throwIfAmbientAborted(signal);
417
472
  const contentLength = declaredContentLength(response.headers);
418
473
  if (contentLength !== undefined && contentLength > maxBodyBytes) {
419
474
  await response.body?.cancel().catch(() => undefined);
@@ -433,7 +488,7 @@ async function readResponseBodyWithLimit(
433
488
  let receivedBytes = 0;
434
489
  try {
435
490
  while (true) {
436
- const { done, value } = await reader.read();
491
+ const { done, value } = await readResponseBodyChunk(reader, signal);
437
492
  if (done) break;
438
493
  if (!value) continue;
439
494
  receivedBytes += value.byteLength;
@@ -447,6 +502,13 @@ async function readResponseBodyWithLimit(
447
502
  reader.releaseLock();
448
503
  }
449
504
 
505
+ return concatenateResponseBodyChunks(chunks, receivedBytes);
506
+ }
507
+
508
+ function concatenateResponseBodyChunks(
509
+ chunks: readonly Uint8Array[],
510
+ receivedBytes: number,
511
+ ): ArrayBuffer {
450
512
  const bodyBytes = new Uint8Array(receivedBytes);
451
513
  let offset = 0;
452
514
  for (const chunk of chunks) {
@@ -456,6 +518,34 @@ async function readResponseBodyWithLimit(
456
518
  return bodyBytes.buffer;
457
519
  }
458
520
 
521
+ function readResponseBodyChunk(
522
+ reader: ReturnType<StealthTransportBody["getReader"]>,
523
+ signal?: AbortSignal,
524
+ ): Promise<{ done: boolean; value?: Uint8Array }> {
525
+ if (!signal) return reader.read();
526
+ throwIfAmbientAborted(signal);
527
+ return new Promise((resolve, reject) => {
528
+ let settled = false;
529
+ const settle = (operation: () => void) => {
530
+ if (settled) return;
531
+ settled = true;
532
+ signal.removeEventListener("abort", onAbort);
533
+ operation();
534
+ };
535
+ const onAbort = () => {
536
+ const error = toAmbientCancellationError(signal);
537
+ void reader.cancel().catch(() => undefined);
538
+ settle(() => reject(error));
539
+ };
540
+ signal.addEventListener("abort", onAbort, { once: true });
541
+ void reader.read().then(
542
+ (chunk) => settle(() => resolve(chunk)),
543
+ (error) => settle(() => reject(error)),
544
+ );
545
+ if (signal.aborted) onAbort();
546
+ });
547
+ }
548
+
459
549
  function normalizeBody(body: StealthFetchOptions["body"]): string {
460
550
  if (body === undefined) {
461
551
  return "";
@@ -621,8 +711,41 @@ function normalizeStealthTransportError(error: unknown): TransportError {
621
711
  });
622
712
  }
623
713
 
624
- function sleep(ms: number): Promise<void> {
625
- return new Promise((resolve) => setTimeout(resolve, ms));
714
+ function toAmbientCancellationError(
715
+ signal: AbortSignal,
716
+ error: unknown = signal.reason,
717
+ ): TransportError {
718
+ if (error instanceof TransportError && error.code === "transport_cancelled") {
719
+ return error;
720
+ }
721
+ return new TransportError("Request cancelled", {
722
+ code: "transport_cancelled",
723
+ status: 0,
724
+ retryable: false,
725
+ ...(error !== undefined
726
+ ? { cause: error instanceof Error ? error : new Error(String(error)) }
727
+ : {}),
728
+ });
729
+ }
730
+
731
+ function throwIfAmbientAborted(signal: AbortSignal | undefined): void {
732
+ if (signal?.aborted) throw toAmbientCancellationError(signal);
733
+ }
734
+
735
+ function sleep(ms: number, signal?: AbortSignal): Promise<void> {
736
+ if (!signal) return new Promise((resolve) => setTimeout(resolve, ms));
737
+ throwIfAmbientAborted(signal);
738
+ return new Promise((resolve, reject) => {
739
+ const onAbort = () => {
740
+ clearTimeout(timer);
741
+ reject(toAmbientCancellationError(signal));
742
+ };
743
+ const timer = setTimeout(() => {
744
+ signal.removeEventListener("abort", onAbort);
745
+ resolve();
746
+ }, ms);
747
+ signal.addEventListener("abort", onAbort, { once: true });
748
+ });
626
749
  }
627
750
 
628
751
  function normalizeMethod(method: HttpMethod | string): StealthMethod {
@@ -687,6 +810,7 @@ async function fetchStealthRedirectChain(
687
810
  requestUrl: string,
688
811
  method: StealthMethod,
689
812
  options: StealthFetchOptions,
813
+ signal?: AbortSignal,
690
814
  ): Promise<{ normalized: StealthResponse; response: StealthTransportResponse }> {
691
815
  let currentUrl = requestUrl;
692
816
  let currentMethod = method;
@@ -697,6 +821,7 @@ async function fetchStealthRedirectChain(
697
821
  const deadline = options.timeout ? performance.now() + options.timeout : undefined;
698
822
 
699
823
  while (true) {
824
+ throwIfAmbientAborted(signal);
700
825
  const headers = { ...currentHeaders };
701
826
  if (!hasHeader(headers, "Cookie")) {
702
827
  const cookieHeader = cookieJar.toHeader(currentUrl);
@@ -706,10 +831,12 @@ async function fetchStealthRedirectChain(
706
831
  headers: normalizeHeaders(headers),
707
832
  method: currentMethod,
708
833
  redirect: "manual",
834
+ ...(signal ? { signal } : {}),
709
835
  };
710
836
  if (currentBody !== undefined) requestInit.body = currentBody;
711
837
 
712
838
  await transport.clearCookies();
839
+ throwIfAmbientAborted(signal);
713
840
  const remainingTimeout =
714
841
  deadline === undefined ? undefined : Math.ceil(deadline - performance.now());
715
842
  if (remainingTimeout !== undefined && remainingTimeout <= 0) {
@@ -720,6 +847,10 @@ async function fetchStealthRedirectChain(
720
847
  }
721
848
  if (remainingTimeout !== undefined) requestInit.timeout = remainingTimeout;
722
849
  response = await transport.fetch(currentUrl, requestInit);
850
+ if (signal?.aborted) {
851
+ discardStealthRedirectBody(response);
852
+ throw toAmbientCancellationError(signal);
853
+ }
723
854
  cookieJar.setFromCookieStrings(
724
855
  setCookieHeadersFromResponse(response.headers),
725
856
  response.url ?? currentUrl,
@@ -758,7 +889,12 @@ async function fetchStealthRedirectChain(
758
889
  followedHops += 1;
759
890
  }
760
891
 
761
- const normalized = await normalizeResponse(response, currentUrl, options.maxBodyBytes);
892
+ const normalized = await normalizeResponseWithSignal(
893
+ response,
894
+ currentUrl,
895
+ options.maxBodyBytes,
896
+ signal,
897
+ );
762
898
  if (followedHops > 0) normalized.redirected = true;
763
899
  return { normalized, response };
764
900
  }
@@ -772,6 +908,12 @@ function createSessionFetcher(
772
908
  let closed = false;
773
909
  let hasWarnedMissingProxy = false;
774
910
  const warn = clientOptions.warn ?? console.warn;
911
+ const intentAlias = getStealthProfileIntentAlias(defaultProfile);
912
+ if (intentAlias) {
913
+ warn(
914
+ `[provider-sdk] Stealth profile "${defaultProfile}" pins a browser version and is deprecated. Use the intent profile "${intentAlias}" so TLS, headers, and User-Agent stay aligned with SDK updates; derive an explicit User-Agent with getStealthProfile("${intentAlias}").userAgent instead of hardcoding one.`,
915
+ );
916
+ }
775
917
  const cookieJar = new StealthCookieJar([], baseUrl);
776
918
 
777
919
  async function getClientEntry(
@@ -807,19 +949,57 @@ function createSessionFetcher(
807
949
  proxyUrl: string | undefined,
808
950
  ignoreTlsErrors: boolean,
809
951
  operation: (client: WreqSession) => Promise<T>,
952
+ signal?: AbortSignal,
810
953
  ): Promise<T> {
954
+ throwIfAmbientAborted(signal);
811
955
  const entry = await getClientEntry(profileName, proxyUrl, ignoreTlsErrors);
956
+ throwIfAmbientAborted(signal);
812
957
  const previous = entry.tail;
813
958
  let release!: () => void;
814
959
  entry.tail = new Promise<void>((resolve) => {
815
960
  release = resolve;
816
961
  });
817
- await previous;
962
+ let acquired = false;
818
963
  try {
819
- return await operation(await entry.session);
964
+ await waitForClientTurn(previous, signal);
965
+ acquired = true;
966
+ throwIfAmbientAborted(signal);
967
+ const client = await entry.session;
968
+ throwIfAmbientAborted(signal);
969
+ const result = await operation(client);
970
+ throwIfAmbientAborted(signal);
971
+ return result;
820
972
  } finally {
821
- release();
973
+ if (acquired) {
974
+ release();
975
+ } else {
976
+ void previous.then(release, release);
977
+ }
978
+ }
979
+ }
980
+
981
+ async function waitForClientTurn(previous: Promise<void>, signal?: AbortSignal): Promise<void> {
982
+ if (!signal) {
983
+ await previous;
984
+ return;
822
985
  }
986
+ throwIfAmbientAborted(signal);
987
+ await new Promise<void>((resolve, reject) => {
988
+ let settled = false;
989
+ const settle = (operation: () => void) => {
990
+ if (settled) return;
991
+ settled = true;
992
+ signal.removeEventListener("abort", onAbort);
993
+ operation();
994
+ };
995
+ const onAbort = () => settle(() => reject(toAmbientCancellationError(signal)));
996
+ signal.addEventListener("abort", onAbort, { once: true });
997
+ void previous.then(
998
+ () => settle(resolve),
999
+ (error) => settle(() => reject(error)),
1000
+ );
1001
+ if (signal.aborted) onAbort();
1002
+ });
823
1003
  }
824
1004
 
825
1005
  async function resolveRequestProxy(
@@ -881,6 +1061,7 @@ function createSessionFetcher(
881
1061
  throw redactSensitiveRequestError(error, url, options.sensitiveParams);
882
1062
  }
883
1063
  })();
1064
+ throwIfAmbientAborted(clientOptions.signal);
884
1065
  const hasPolicyProxy = isPolicyManagedProxy(clientOptions);
885
1066
  const usesPolicyAllocator = hasPolicyProxy && !options.proxy && !clientOptions.proxy;
886
1067
  const retryAttemptCap = Math.max(1, stealthRetryOptions?.attempts ?? 1);
@@ -921,6 +1102,7 @@ function createSessionFetcher(
921
1102
  const attemptedProxies = new Set<string>();
922
1103
 
923
1104
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
1105
+ throwIfAmbientAborted(clientOptions.signal);
924
1106
  let proxy: string | undefined;
925
1107
  let attemptProxy: ResolvedAttemptProxy | undefined;
926
1108
  // Reuse the exact serialization used by this outbound attempt in its catch path.
@@ -951,6 +1133,7 @@ function createSessionFetcher(
951
1133
  });
952
1134
  };
953
1135
  try {
1136
+ throwIfAmbientAborted(clientOptions.signal);
954
1137
  const sensitiveParams = normalizeSensitiveParams(options.sensitiveParams);
955
1138
  const structural = redactUrlQueryParams(url, Object.keys(sensitiveParams ?? {}));
956
1139
  fallbackSensitiveValues = [
@@ -963,6 +1146,7 @@ function createSessionFetcher(
963
1146
  fallbackRedactedUrl = structural.redactedUrl;
964
1147
  assertNoUnsupportedFingerprintOverrides(options);
965
1148
  attemptProxy = await resolveRequestProxy(options, attempt, refreshAttempt);
1149
+ throwIfAmbientAborted(clientOptions.signal);
966
1150
  proxy = attemptProxy.url;
967
1151
  if (proxy && dedupeAllocatorEndpoints) {
968
1152
  // An under-filled allocation repeats endpoints (via the modulo
@@ -991,7 +1175,15 @@ function createSessionFetcher(
991
1175
  proxy,
992
1176
  ignoreTlsErrors,
993
1177
  (transport) =>
994
- fetchStealthRedirectChain(transport, cookieJar, requestUrl, method, options),
1178
+ fetchStealthRedirectChain(
1179
+ transport,
1180
+ cookieJar,
1181
+ requestUrl,
1182
+ method,
1183
+ options,
1184
+ clientOptions.signal,
1185
+ ),
1186
+ clientOptions.signal,
995
1187
  );
996
1188
 
997
1189
  if (proxy && isProxyConnectFailureResponse(response, normalized.body)) {
@@ -1039,14 +1231,26 @@ function createSessionFetcher(
1039
1231
  const sensitiveValues = serializedUrl?.sensitiveValues ?? fallbackSensitiveValues;
1040
1232
  let normalizedError: TransportError;
1041
1233
  try {
1234
+ throwIfAmbientAborted(clientOptions.signal);
1042
1235
  normalizedError = normalizeStealthTransportError(error);
1043
1236
  } catch (normalizationError) {
1044
- throw redactSensitiveError(
1237
+ const redactedNormalizationError = redactSensitiveError(
1045
1238
  normalizationError,
1046
1239
  sensitiveValues,
1047
1240
  serializedUrl?.requestUrl ?? fallbackRequestUrl,
1048
1241
  serializedUrl?.redactedUrl ?? fallbackRedactedUrl,
1049
1242
  );
1243
+ if (
1244
+ normalizationError instanceof TransportError &&
1245
+ normalizationError.code === "transport_cancelled"
1246
+ ) {
1247
+ recordProxyAttempt(
1248
+ "error",
1249
+ proxyAttemptErrorCode(normalizationError),
1250
+ proxyAttemptStatus(normalizationError),
1251
+ );
1252
+ }
1253
+ throw redactedNormalizationError;
1050
1254
  }
1051
1255
  const retryErrorCode = proxyAttemptErrorCode(normalizedError);
1052
1256
  const refreshableProxyError = isProxyPoolRefreshableError(normalizedError);
@@ -1104,8 +1308,12 @@ function createSessionFetcher(
1104
1308
  })
1105
1309
  ) {
1106
1310
  if (stealthRetryOptions) {
1107
- await sleep(computeProxyTransportRetryDelayMs(stealthRetryOptions!, attempt + 1));
1311
+ await sleep(
1312
+ computeProxyTransportRetryDelayMs(stealthRetryOptions, attempt + 1),
1313
+ clientOptions.signal,
1314
+ );
1108
1315
  }
1316
+ throwIfAmbientAborted(clientOptions.signal);
1109
1317
  continue;
1110
1318
  }
1111
1319
  throw normalizedError;
@@ -1117,11 +1325,13 @@ function createSessionFetcher(
1117
1325
  stalePoolError &&
1118
1326
  refreshAttempt < MAX_POLICY_PROXY_POOL_REFRESHES
1119
1327
  ) {
1328
+ throwIfAmbientAborted(clientOptions.signal);
1120
1329
  await invalidateProxyResolutionCacheAsync({
1121
1330
  proxyPolicy: clientOptions.proxyPolicy,
1122
1331
  upstream: clientOptions.upstream,
1123
1332
  affinityKey: clientOptions.affinityKey,
1124
1333
  });
1334
+ throwIfAmbientAborted(clientOptions.signal);
1125
1335
  continue;
1126
1336
  }
1127
1337
 
@@ -1157,6 +1367,7 @@ function createSessionFetcher(
1157
1367
  break;
1158
1368
  }
1159
1369
 
1370
+ throwIfAmbientAborted(clientOptions.signal);
1160
1371
  throw normalizeStealthTransportError(lastError);
1161
1372
  },
1162
1373
  cookies: cookieJar,
@@ -1380,16 +1591,31 @@ function createSessionFetcher(
1380
1591
  proxy: string,
1381
1592
  ): Promise<"source_ip_denied" | "edge_auth_rejected" | undefined> {
1382
1593
  try {
1383
- return await withClient(profileName, proxy, false, async (client) => {
1384
- await client.clearCookies();
1385
- const response = await client.fetch(PROXY_AUTH_DIAGNOSTIC_URL, {
1386
- method: "GET",
1387
- timeout: PROXY_AUTH_DIAGNOSTIC_TIMEOUT_MS,
1388
- });
1389
- const normalized = await normalizeResponse(response);
1390
- return classifyProxyAuthDiagnosticMessage(normalized.body);
1391
- });
1594
+ return await withClient(
1595
+ profileName,
1596
+ proxy,
1597
+ false,
1598
+ async (client) => {
1599
+ throwIfAmbientAborted(clientOptions.signal);
1600
+ await client.clearCookies();
1601
+ throwIfAmbientAborted(clientOptions.signal);
1602
+ const response = await client.fetch(PROXY_AUTH_DIAGNOSTIC_URL, {
1603
+ method: "GET",
1604
+ timeout: PROXY_AUTH_DIAGNOSTIC_TIMEOUT_MS,
1605
+ ...(clientOptions.signal ? { signal: clientOptions.signal } : {}),
1606
+ });
1607
+ const normalized = await normalizeResponseWithSignal(
1608
+ response,
1609
+ undefined,
1610
+ undefined,
1611
+ clientOptions.signal,
1612
+ );
1613
+ return classifyProxyAuthDiagnosticMessage(normalized.body);
1614
+ },
1615
+ clientOptions.signal,
1616
+ );
1392
1617
  } catch (error) {
1618
+ throwIfAmbientAborted(clientOptions.signal);
1393
1619
  const message =
1394
1620
  error instanceof Error
1395
1621
  ? [error.message, error.cause instanceof Error ? error.cause.message : ""]
@@ -667,6 +667,7 @@ function createProviderContext(
667
667
  upstream: proxyClientOptions.upstream,
668
668
  affinityKey: proxyClientOptions.affinityKey,
669
669
  telemetry: proxyTelemetry,
670
+ ...(signal ? { signal } : {}),
670
671
  };
671
672
  const { capabilityModules } = options;
672
673
  const logStealthCleanupError = (error: unknown) =>
@@ -715,7 +716,7 @@ function createProviderContext(
715
716
  ? stealthProfile
716
717
  ? capabilityModules.stealth.createStealthClient(
717
718
  stealthBaseUrl,
718
- stealthProfile.name,
719
+ provider.stealth!.profile,
719
720
  stealthClientOptions,
720
721
  )
721
722
  : capabilityModules.stealth.createStealthClient(stealthBaseUrl, stealthClientOptions)
@@ -723,7 +724,7 @@ function createProviderContext(
723
724
  ? createLazyStealthClient(
724
725
  logStealthCleanupError,
725
726
  stealthBaseUrl,
726
- stealthProfile.name,
727
+ provider.stealth!.profile,
727
728
  stealthClientOptions,
728
729
  )
729
730
  : createLazyStealthClient(logStealthCleanupError, stealthBaseUrl, stealthClientOptions)
@@ -891,6 +892,7 @@ function createAuthFlowContext(
891
892
  upstream: proxyClientOptions.upstream,
892
893
  affinityKey: proxyClientOptions.affinityKey,
893
894
  telemetry: proxyTelemetry,
895
+ ...(signal ? { signal } : {}),
894
896
  };
895
897
  const { capabilityModules } = options;
896
898
  const logStealthCleanupError = (error: unknown) =>
@@ -930,7 +932,7 @@ function createAuthFlowContext(
930
932
  ? stealthProfile
931
933
  ? capabilityModules.stealth.createStealthClient(
932
934
  stealthBaseUrl,
933
- stealthProfile.name,
935
+ provider.stealth!.profile,
934
936
  stealthClientOptions,
935
937
  )
936
938
  : capabilityModules.stealth.createStealthClient(stealthBaseUrl, stealthClientOptions)
@@ -938,7 +940,7 @@ function createAuthFlowContext(
938
940
  ? createLazyStealthClient(
939
941
  logStealthCleanupError,
940
942
  stealthBaseUrl,
941
- stealthProfile.name,
943
+ provider.stealth!.profile,
942
944
  stealthClientOptions,
943
945
  )
944
946
  : createLazyStealthClient(logStealthCleanupError, stealthBaseUrl, stealthClientOptions)