@apifuse/provider-sdk 2.1.0-beta.9 → 2.2.0-beta.10

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.
Files changed (263) hide show
  1. package/AUTHORING.md +240 -0
  2. package/CHANGELOG.md +93 -0
  3. package/README.md +26 -10
  4. package/SUBMISSION.md +11 -12
  5. package/bin/apifuse-check.ts +44 -59
  6. package/bin/apifuse-create.ts +1 -1
  7. package/bin/apifuse-dev.ts +27 -52
  8. package/bin/apifuse-pack-check.ts +36 -0
  9. package/bin/apifuse-pack-smoke.ts +22 -81
  10. package/bin/apifuse-pack-types.ts +266 -0
  11. package/bin/apifuse-perf.ts +45 -127
  12. package/bin/apifuse-record.ts +53 -70
  13. package/bin/apifuse-submit-check.ts +2177 -353
  14. package/bin/apifuse-sync-assets.ts +117 -0
  15. package/bin/apifuse.ts +1 -1
  16. package/bin/submit-check-delimited-text.ts +50 -0
  17. package/bin/submit-check-xml-semantics.ts +204 -0
  18. package/bin/submit-check-xml.ts +134 -0
  19. package/dist/auth-turn/auth-turn.v1.schema.json +89 -0
  20. package/dist/auth-turn/fixtures/invalid/empty-kind.json +4 -0
  21. package/dist/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
  22. package/dist/auth-turn/fixtures/invalid/missing-kind.json +3 -0
  23. package/dist/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
  24. package/dist/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
  25. package/dist/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
  26. package/dist/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
  27. package/dist/auth-turn/fixtures/valid/abort.json +8 -0
  28. package/dist/auth-turn/fixtures/valid/challenge.json +17 -0
  29. package/dist/auth-turn/fixtures/valid/complete.json +13 -0
  30. package/dist/auth-turn/fixtures/valid/form.json +14 -0
  31. package/dist/auth-turn/fixtures/valid/message.json +13 -0
  32. package/dist/auth-turn/fixtures/valid/multi_choice.json +15 -0
  33. package/dist/auth-turn/fixtures/valid/pending.json +5 -0
  34. package/dist/auth-turn/fixtures/valid/poll.json +9 -0
  35. package/dist/auth-turn/fixtures/valid/redirect.json +16 -0
  36. package/dist/auth-turn/fixtures/valid/retry.json +8 -0
  37. package/dist/auth-turn/fixtures/valid/unknown-kind.json +7 -0
  38. package/dist/auth-turn/index.d.ts +195 -0
  39. package/dist/auth-turn/index.js +133 -0
  40. package/dist/auth.d.ts +76 -0
  41. package/dist/auth.js +427 -0
  42. package/dist/ceremonies/index.d.ts +1 -1
  43. package/dist/ceremonies/index.js +14 -48
  44. package/dist/cli/commands.d.ts +1 -1
  45. package/dist/cli/commands.js +8 -0
  46. package/dist/cli/create.d.ts +3 -0
  47. package/dist/cli/create.js +47 -33
  48. package/dist/cli/prompt-assets.d.ts +80 -0
  49. package/dist/cli/prompt-assets.js +743 -0
  50. package/dist/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
  51. package/dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
  52. package/dist/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
  53. package/dist/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
  54. package/dist/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
  55. package/dist/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
  56. package/dist/cli/templates/provider/.dockerignore.tpl +22 -0
  57. package/dist/cli/templates/provider/.gitignore.tpl +22 -0
  58. package/dist/cli/templates/provider/AGENTS.md.tpl +96 -0
  59. package/dist/cli/templates/provider/Dockerfile.tpl +7 -0
  60. package/dist/cli/templates/provider/README.md.tpl +163 -0
  61. package/dist/cli/templates/provider/dev.ts.tpl +5 -0
  62. package/dist/cli/templates/provider/domain/README.md.tpl +3 -0
  63. package/dist/cli/templates/provider/index.test.ts.tpl +13 -0
  64. package/dist/cli/templates/provider/index.ts.tpl +15 -0
  65. package/dist/cli/templates/provider/mappers/README.md.tpl +3 -0
  66. package/dist/cli/templates/provider/meta.ts.tpl +7 -0
  67. package/dist/cli/templates/provider/operations/index.ts.tpl +5 -0
  68. package/dist/cli/templates/provider/operations/ping.ts.tpl +24 -0
  69. package/dist/cli/templates/provider/schemas/ping.ts.tpl +24 -0
  70. package/dist/cli/templates/provider/start.ts.tpl +5 -0
  71. package/dist/cli/templates/provider/upstream/README.md.tpl +3 -0
  72. package/dist/config/loader.d.ts +149 -8
  73. package/dist/config/loader.js +378 -89
  74. package/dist/contract-serialization.d.ts +2 -2
  75. package/dist/contract-serialization.js +3 -6
  76. package/dist/contract-types.d.ts +2 -2
  77. package/dist/contract.d.ts +3 -3
  78. package/dist/contract.js +5 -6
  79. package/dist/define.d.ts +13 -1
  80. package/dist/define.js +245 -178
  81. package/dist/dev.d.ts +1 -1
  82. package/dist/dev.js +1 -1
  83. package/dist/errors.d.ts +4 -1
  84. package/dist/errors.js +48 -0
  85. package/dist/i18n/catalog.d.ts +2 -2
  86. package/dist/i18n/catalog.js +4 -10
  87. package/dist/i18n/index.d.ts +2 -2
  88. package/dist/i18n/index.js +2 -2
  89. package/dist/i18n/keys.d.ts +2 -2
  90. package/dist/index.d.ts +44 -41
  91. package/dist/index.js +39 -36
  92. package/dist/lint.d.ts +1 -0
  93. package/dist/lint.js +35 -15
  94. package/dist/provider.d.ts +11 -9
  95. package/dist/provider.js +9 -8
  96. package/dist/public-schema-field-lint.d.ts +1 -1
  97. package/dist/recipes/gov-api.js +1 -1
  98. package/dist/runtime/auth-flow.d.ts +1 -1
  99. package/dist/runtime/auth-flow.js +4 -2
  100. package/dist/runtime/browser.d.ts +1 -1
  101. package/dist/runtime/browser.js +214 -25
  102. package/dist/runtime/cache.d.ts +1 -1
  103. package/dist/runtime/cache.js +4 -8
  104. package/dist/runtime/choice.d.ts +1 -1
  105. package/dist/runtime/choice.js +31 -35
  106. package/dist/runtime/credential.d.ts +1 -1
  107. package/dist/runtime/credential.js +1 -1
  108. package/dist/runtime/env.d.ts +1 -1
  109. package/dist/runtime/executor.d.ts +1 -1
  110. package/dist/runtime/executor.js +15 -3
  111. package/dist/runtime/http.d.ts +2 -2
  112. package/dist/runtime/http.js +160 -344
  113. package/dist/runtime/insights.d.ts +1 -1
  114. package/dist/runtime/insights.js +6 -13
  115. package/dist/runtime/instrumentation.d.ts +2 -2
  116. package/dist/runtime/instrumentation.js +56 -19
  117. package/dist/runtime/keyring.js +1 -1
  118. package/dist/runtime/namespace.js +1 -1
  119. package/dist/runtime/otlp.d.ts +1 -1
  120. package/dist/runtime/perf.d.ts +1 -1
  121. package/dist/runtime/provider.d.ts +1 -1
  122. package/dist/runtime/provider.js +1 -2
  123. package/dist/runtime/proxy-errors.d.ts +1 -1
  124. package/dist/runtime/proxy-errors.js +9 -7
  125. package/dist/runtime/proxy-nodemaven.d.ts +35 -0
  126. package/dist/runtime/proxy-nodemaven.js +128 -0
  127. package/dist/runtime/proxy-retry-policy.d.ts +40 -0
  128. package/dist/runtime/proxy-retry-policy.js +326 -0
  129. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  130. package/dist/runtime/proxy-telemetry.js +55 -52
  131. package/dist/runtime/redis.d.ts +1 -1
  132. package/dist/runtime/redis.js +2 -4
  133. package/dist/runtime/request-options.d.ts +1 -1
  134. package/dist/runtime/secrets.d.ts +27 -0
  135. package/dist/runtime/secrets.js +51 -0
  136. package/dist/runtime/state.d.ts +2 -2
  137. package/dist/runtime/state.js +15 -4
  138. package/dist/runtime/stealth.d.ts +7 -4
  139. package/dist/runtime/stealth.js +257 -215
  140. package/dist/runtime/stt.d.ts +1 -1
  141. package/dist/runtime/stt.js +11 -15
  142. package/dist/runtime/trace.d.ts +2 -2
  143. package/dist/runtime/trace.js +2 -4
  144. package/dist/runtime/waterfall.d.ts +1 -1
  145. package/dist/schema.d.ts +1 -1
  146. package/dist/schema.js +7 -15
  147. package/dist/serve.d.ts +1 -1
  148. package/dist/serve.js +1 -1
  149. package/dist/server/index.d.ts +7 -3
  150. package/dist/server/index.js +6 -2
  151. package/dist/server/self-test-input-tokens.d.ts +1 -0
  152. package/dist/server/self-test-input-tokens.js +37 -0
  153. package/dist/server/self-test-redaction.d.ts +20 -0
  154. package/dist/server/self-test-redaction.js +70 -0
  155. package/dist/server/self-test-token.d.ts +30 -0
  156. package/dist/server/self-test-token.js +50 -0
  157. package/dist/server/self-test.d.ts +199 -0
  158. package/dist/server/self-test.js +1113 -0
  159. package/dist/server/serve.d.ts +14 -3
  160. package/dist/server/serve.js +135 -64
  161. package/dist/server/types.d.ts +10 -9
  162. package/dist/server/types.js +3 -7
  163. package/dist/stealth/profiles.d.ts +1 -1
  164. package/dist/stealth/profiles.js +5 -14
  165. package/dist/stream.d.ts +1 -1
  166. package/dist/testing/index.d.ts +2 -2
  167. package/dist/testing/index.js +2 -2
  168. package/dist/testing/run.d.ts +1 -1
  169. package/dist/testing/run.js +12 -15
  170. package/dist/types.d.ts +237 -1
  171. package/dist/user-input.d.ts +30 -0
  172. package/dist/user-input.js +66 -0
  173. package/package.json +16 -5
  174. package/src/auth-turn/auth-turn.v1.schema.json +89 -0
  175. package/src/auth-turn/fixtures/invalid/empty-kind.json +4 -0
  176. package/src/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
  177. package/src/auth-turn/fixtures/invalid/missing-kind.json +3 -0
  178. package/src/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
  179. package/src/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
  180. package/src/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
  181. package/src/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
  182. package/src/auth-turn/fixtures/valid/abort.json +8 -0
  183. package/src/auth-turn/fixtures/valid/challenge.json +17 -0
  184. package/src/auth-turn/fixtures/valid/complete.json +13 -0
  185. package/src/auth-turn/fixtures/valid/form.json +14 -0
  186. package/src/auth-turn/fixtures/valid/message.json +13 -0
  187. package/src/auth-turn/fixtures/valid/multi_choice.json +15 -0
  188. package/src/auth-turn/fixtures/valid/pending.json +5 -0
  189. package/src/auth-turn/fixtures/valid/poll.json +9 -0
  190. package/src/auth-turn/fixtures/valid/redirect.json +16 -0
  191. package/src/auth-turn/fixtures/valid/retry.json +8 -0
  192. package/src/auth-turn/fixtures/valid/unknown-kind.json +7 -0
  193. package/src/auth-turn/index.ts +177 -0
  194. package/src/auth.ts +728 -0
  195. package/src/ceremonies/index.ts +33 -121
  196. package/src/cli/commands.ts +10 -0
  197. package/src/cli/create.ts +69 -99
  198. package/src/cli/prompt-assets.ts +865 -0
  199. package/src/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
  200. package/src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
  201. package/src/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
  202. package/src/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
  203. package/src/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
  204. package/src/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
  205. package/src/cli/templates/provider/AGENTS.md.tpl +96 -0
  206. package/src/cli/templates/provider/README.md.tpl +7 -4
  207. package/src/config/loader.ts +543 -208
  208. package/src/contract-serialization.ts +5 -11
  209. package/src/contract-types.ts +2 -2
  210. package/src/contract.ts +13 -28
  211. package/src/define.ts +397 -528
  212. package/src/dev.ts +4 -9
  213. package/src/errors.ts +58 -5
  214. package/src/i18n/catalog.ts +10 -32
  215. package/src/i18n/index.ts +2 -2
  216. package/src/i18n/keys.ts +5 -11
  217. package/src/index.ts +64 -41
  218. package/src/lint.ts +122 -159
  219. package/src/provider.ts +40 -9
  220. package/src/public-schema-field-lint.ts +7 -33
  221. package/src/recipes/gov-api.ts +2 -5
  222. package/src/runtime/auth-flow.ts +6 -6
  223. package/src/runtime/browser.ts +320 -151
  224. package/src/runtime/cache.ts +20 -67
  225. package/src/runtime/choice.ts +79 -132
  226. package/src/runtime/credential.ts +2 -2
  227. package/src/runtime/env.ts +1 -1
  228. package/src/runtime/executor.ts +23 -20
  229. package/src/runtime/http.ts +216 -539
  230. package/src/runtime/insights.ts +15 -53
  231. package/src/runtime/instrumentation.ts +78 -65
  232. package/src/runtime/keyring.ts +7 -19
  233. package/src/runtime/namespace.ts +2 -7
  234. package/src/runtime/otlp.ts +12 -23
  235. package/src/runtime/perf.ts +1 -1
  236. package/src/runtime/provider.ts +4 -9
  237. package/src/runtime/proxy-errors.ts +29 -42
  238. package/src/runtime/proxy-nodemaven.ts +178 -0
  239. package/src/runtime/proxy-retry-policy.ts +469 -0
  240. package/src/runtime/proxy-telemetry.ts +79 -77
  241. package/src/runtime/redis.ts +4 -12
  242. package/src/runtime/request-options.ts +4 -13
  243. package/src/runtime/secrets.ts +64 -0
  244. package/src/runtime/state.ts +41 -110
  245. package/src/runtime/stealth.ts +331 -369
  246. package/src/runtime/stt.ts +38 -94
  247. package/src/runtime/trace.ts +14 -44
  248. package/src/runtime/waterfall.ts +5 -18
  249. package/src/schema.ts +23 -84
  250. package/src/serve.ts +1 -1
  251. package/src/server/index.ts +44 -3
  252. package/src/server/self-test-input-tokens.ts +46 -0
  253. package/src/server/self-test-redaction.ts +97 -0
  254. package/src/server/self-test-token.ts +70 -0
  255. package/src/server/self-test.ts +1450 -0
  256. package/src/server/serve.ts +206 -216
  257. package/src/server/types.ts +7 -19
  258. package/src/stealth/profiles.ts +10 -26
  259. package/src/stream.ts +8 -19
  260. package/src/testing/index.ts +2 -2
  261. package/src/testing/run.ts +24 -64
  262. package/src/types.ts +274 -1
  263. package/src/user-input.ts +118 -0
@@ -1,44 +1,19 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { Impit } from "impit";
3
- import { DEFAULT_SMARTPROXY_POOL_SIZE, invalidateProxyResolutionCacheAsync, ProxyResolutionError, resolveProxyConfigAsync, SMARTPROXY_MAX_POOL_SIZE, } from "../config/loader";
4
- import { ProviderError, SDKError, TransportError } from "../errors";
5
- import { getStealthProfile } from "../stealth/profiles";
6
- import { HttpRetryPreset, HttpRetryUnsafeMethodPolicy } from "../types";
7
- import { createProxyAuthIpDeniedError, createProxyEdgeAuthRejectedError, createProxyEdgeTlsRejectedError, createProxyPoolExhaustedError, createProxyPoolStaleError, isProxyAuthIpDeniedMessage, isProxyEdgeAuthRejectedMessage, isProxyEdgeTlsRejectedResponse, isProxyPoolRefreshableError, isProxyPoolStaleMessage, isProxyPoolStaleStatus, PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_STALE_CODE, } from "./proxy-errors";
8
- import { appendQueryParams } from "./request-options";
3
+ import { DEFAULT_SMARTPROXY_POOL_SIZE, invalidateProxyResolutionCacheAsync, policyResolvesRegistryVendorChain, ProxyResolutionError, resolvePolicyProxyPoolSpan, resolvePolicyTransportAttemptCap, resolveProxyConfigAsync, vendorFromResolvedSource, } from "../config/loader.js";
4
+ import { SDKError, TransportError } from "../errors.js";
5
+ import { getStealthProfile } from "../stealth/profiles.js";
6
+ import { createProxyAuthIpDeniedError, createProxyEdgeAuthRejectedError, createProxyEdgeTlsRejectedError, createProxyPoolExhaustedError, createProxyPoolStaleError, isProxyAuthIpDeniedMessage, isProxyEdgeAuthRejectedMessage, isProxyEdgeTlsRejectedResponse, isProxyPoolRefreshableError, isProxyPoolStaleMessage, isProxyPoolStaleStatus, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_STALE_CODE, } from "./proxy-errors.js";
7
+ import { computeProxyAttemptIndex, computeProxyTransportRetryDelayMs, createDefaultProxyTransportRetryOptions, normalizeProxyTransportRetryOptions, shouldRetryProxyTransportAttempt, validateUnsafeProxyTransportRetryMethods, } from "./proxy-retry-policy.js";
8
+ import { appendQueryParams } from "./request-options.js";
9
9
  const DEFAULT_PROFILE = "chrome-146";
10
10
  const MISSING_PROXY_WARNING = "[provider-sdk] Provider requested proxy routing, but no proxy URL was configured. Continuing without proxy.";
11
- const MAX_POLICY_PROXY_RETRY_ATTEMPTS = SMARTPROXY_MAX_POOL_SIZE;
12
11
  const MAX_POLICY_PROXY_POOL_REFRESHES = 1;
13
12
  const PROXY_CONNECT_FAILURE_CODE = "proxy_connect_failed";
14
13
  const PROXY_CONNECT_FAILURE_BODY_PATTERN = /\bproxy\b.*\b(non[\s-]?200|connect|tunnel)|\bconnect\b.*\bproxy\b|\btunnel\b/i;
15
14
  const PROXY_AUTH_DIAGNOSTIC_URL = "http://example.com/";
16
15
  const PROXY_AUTH_DIAGNOSTIC_TIMEOUT_MS = 5_000;
17
- const DEFAULT_STEALTH_RETRY_METHODS = ["GET", "HEAD", "OPTIONS"];
18
- const DEFAULT_STEALTH_RETRY_ERROR_CODES = [
19
- PROXY_CONNECT_FAILURE_CODE,
20
- "transport_network_error",
21
- "transport_timeout",
22
- ];
23
- const RATE_LIMIT_STEALTH_RETRY_ERROR_CODES = ["transport_timeout"];
24
- const KNOWN_STEALTH_RETRY_METHODS = new Set([
25
- "GET",
26
- "HEAD",
27
- "POST",
28
- "PUT",
29
- "DELETE",
30
- "OPTIONS",
31
- "TRACE",
32
- "PATCH",
33
- ]);
34
- const UNSAFE_STEALTH_RETRY_METHODS = new Set([
35
- "POST",
36
- "PUT",
37
- "PATCH",
38
- "DELETE",
39
- "TRACE",
40
- ]);
41
- const MAX_STEALTH_RETRY_ATTEMPTS = 8;
16
+ const STEALTH_PROXY_TRANSPORT_RETRY_ERROR_CODES = [PROXY_CONNECT_FAILURE_CODE];
42
17
  const REMOVED_CHROME_PROFILE_NAMES = new Set([
43
18
  "chrome-120",
44
19
  "chrome-124",
@@ -102,11 +77,32 @@ class CookieJarImpl {
102
77
  getAll() {
103
78
  return { ...this.cookies };
104
79
  }
80
+ has(name) {
81
+ return Object.hasOwn(this.cookies, name);
82
+ }
105
83
  toString() {
106
84
  return Object.entries(this.cookies)
107
85
  .map(([name, value]) => `${name}=${value}`)
108
86
  .join("; ");
109
87
  }
88
+ toHeader() {
89
+ return this.toString();
90
+ }
91
+ snapshot() {
92
+ return this.getAll();
93
+ }
94
+ restore(cookies) {
95
+ this.clear();
96
+ for (const [name, value] of Object.entries(cookies)) {
97
+ if (name)
98
+ this.cookies[name] = value;
99
+ }
100
+ }
101
+ clear() {
102
+ for (const name of Object.keys(this.cookies)) {
103
+ delete this.cookies[name];
104
+ }
105
+ }
110
106
  find(predicate) {
111
107
  for (const [name, value] of Object.entries(this.cookies)) {
112
108
  const cookie = `${name}=${value}`;
@@ -237,7 +233,7 @@ function splitCombinedSetCookieHeader(headerValue) {
237
233
  cookieStrings.push(finalCookie);
238
234
  return cookieStrings;
239
235
  }
240
- export async function normalizeResponse(response) {
236
+ export async function normalizeResponse(response, requestUrl) {
241
237
  const headers = Object.fromEntries(response.headers.entries());
242
238
  const cookies = new CookieJarImpl(setCookieHeadersFromResponse(response.headers));
243
239
  const bodyBytes = await response.arrayBuffer();
@@ -245,6 +241,12 @@ export async function normalizeResponse(response) {
245
241
  return {
246
242
  status: response.status,
247
243
  ok: response.status >= 200 && response.status < 300,
244
+ ...(response.url ? { url: response.url } : {}),
245
+ ...(response.redirected !== undefined
246
+ ? { redirected: response.redirected }
247
+ : requestUrl && response.url
248
+ ? { redirected: response.url !== requestUrl }
249
+ : {}),
248
250
  headers,
249
251
  rawHeaders: headerEntriesFromHeaders(response.headers),
250
252
  body,
@@ -276,130 +278,8 @@ function isPolicyManagedProxy(options) {
276
278
  const policy = options.proxyPolicy ?? options.upstream?.proxy;
277
279
  return Boolean(policy && typeof policy === "object");
278
280
  }
279
- function isRetrySafeStealthMethod(method) {
280
- return method === "GET" || method === "HEAD" || method === "OPTIONS";
281
- }
282
- function createStealthRetryOptions(preset) {
283
- switch (preset) {
284
- case HttpRetryPreset.Off:
285
- return {
286
- attempts: 1,
287
- methods: DEFAULT_STEALTH_RETRY_METHODS,
288
- errorCodes: DEFAULT_STEALTH_RETRY_ERROR_CODES,
289
- unsafeMethodPolicy: HttpRetryUnsafeMethodPolicy.Reject,
290
- };
291
- case HttpRetryPreset.AggressiveRead:
292
- return {
293
- attempts: 4,
294
- methods: DEFAULT_STEALTH_RETRY_METHODS,
295
- errorCodes: DEFAULT_STEALTH_RETRY_ERROR_CODES,
296
- unsafeMethodPolicy: HttpRetryUnsafeMethodPolicy.Reject,
297
- };
298
- case HttpRetryPreset.RateLimitAware:
299
- return {
300
- attempts: 3,
301
- methods: DEFAULT_STEALTH_RETRY_METHODS,
302
- errorCodes: RATE_LIMIT_STEALTH_RETRY_ERROR_CODES,
303
- unsafeMethodPolicy: HttpRetryUnsafeMethodPolicy.Reject,
304
- };
305
- case HttpRetryPreset.SafeRead:
306
- case HttpRetryPreset.TransportTransient:
307
- return {
308
- attempts: 3,
309
- methods: DEFAULT_STEALTH_RETRY_METHODS,
310
- errorCodes: DEFAULT_STEALTH_RETRY_ERROR_CODES,
311
- unsafeMethodPolicy: HttpRetryUnsafeMethodPolicy.Reject,
312
- };
313
- }
314
- throw new ProviderError(`Unknown stealth retry preset: ${preset}`, {
315
- code: "retry_invalid_policy",
316
- });
317
- }
318
- function normalizeStealthRetryOptions(retry) {
319
- if (retry === undefined)
320
- return undefined;
321
- if (retry === false)
322
- return createStealthRetryOptions(HttpRetryPreset.Off);
323
- if (retry === true)
324
- return createStealthRetryOptions(HttpRetryPreset.TransportTransient);
325
- if (typeof retry === "string") {
326
- if (!Object.values(HttpRetryPreset).includes(retry)) {
327
- throw new ProviderError(`Unknown stealth retry preset: ${retry}`, {
328
- code: "retry_invalid_policy",
329
- });
330
- }
331
- return createStealthRetryOptions(retry);
332
- }
333
- if (typeof retry !== "object" || retry === null || Array.isArray(retry)) {
334
- throw new ProviderError("Stealth retry policy must be a plain object", {
335
- code: "retry_invalid_policy",
336
- });
337
- }
338
- if (retry.unsafeMethodPolicy !== undefined &&
339
- !Object.values(HttpRetryUnsafeMethodPolicy).includes(retry.unsafeMethodPolicy)) {
340
- throw new ProviderError(`Unknown stealth retry unsafe method policy: ${String(retry.unsafeMethodPolicy)}`, { code: "retry_invalid_policy" });
341
- }
342
- if (retry.methods !== undefined) {
343
- if (!Array.isArray(retry.methods)) {
344
- throw new ProviderError("Stealth retry methods must be an array", {
345
- code: "retry_invalid_policy",
346
- });
347
- }
348
- const unknownMethods = retry.methods
349
- .map((method) => (typeof method === "string" ? method.toUpperCase() : ""))
350
- .filter((method) => !KNOWN_STEALTH_RETRY_METHODS.has(method));
351
- if (unknownMethods.length > 0) {
352
- throw new ProviderError(`Unknown stealth retry method(s): ${unknownMethods.join(", ")}`, { code: "retry_invalid_policy" });
353
- }
354
- }
355
- if (retry.errorCodes !== undefined) {
356
- if (!Array.isArray(retry.errorCodes) ||
357
- retry.errorCodes.some((errorCode) => typeof errorCode !== "string")) {
358
- throw new ProviderError("Stealth retry errorCodes must contain only strings", { code: "retry_invalid_policy" });
359
- }
360
- }
361
- const base = createStealthRetryOptions(retry.preset ?? HttpRetryPreset.TransportTransient);
362
- const attempts = retry.attempts === undefined || !Number.isFinite(retry.attempts)
363
- ? base.attempts
364
- : Math.max(1, Math.min(MAX_STEALTH_RETRY_ATTEMPTS, Math.floor(retry.attempts)));
365
- const normalized = {
366
- attempts,
367
- methods: retry.methods?.map((method) => method.toUpperCase()) ?? base.methods,
368
- errorCodes: retry.errorCodes ?? base.errorCodes,
369
- unsafeMethodPolicy: retry.unsafeMethodPolicy ?? base.unsafeMethodPolicy,
370
- };
371
- if (normalized.unsafeMethodPolicy !==
372
- HttpRetryUnsafeMethodPolicy.AllowExplicitUnsafe) {
373
- const unsafeMethods = normalized.methods.filter((method) => UNSAFE_STEALTH_RETRY_METHODS.has(method.toUpperCase()));
374
- if (unsafeMethods.length > 0) {
375
- throw new ProviderError(`Stealth retry methods include unsafe method(s): ${unsafeMethods.join(", ")}`, { code: "retry_unsafe_method" });
376
- }
377
- }
378
- return normalized;
379
- }
380
- function isExplicitStealthRetryAllowed(method, error, retryOptions) {
381
- if (!retryOptions || retryOptions.attempts <= 1)
382
- return false;
383
- return (retryOptions.methods.includes(method.toUpperCase()) &&
384
- retryOptions.errorCodes.includes(proxyAttemptErrorCode(error)));
385
- }
386
- function isRetryableProxyTransportError(error) {
387
- if (error instanceof TransportError) {
388
- if (error.code === PROXY_AUTH_IP_DENIED_CODE) {
389
- return false;
390
- }
391
- return (error.code === PROXY_CONNECT_FAILURE_CODE ||
392
- error.code === "transport_network_error" ||
393
- error.code === "transport_timeout");
394
- }
395
- if (error instanceof SDKError) {
396
- return false;
397
- }
398
- const message = error instanceof Error ? error.message : String(error);
399
- return /\bproxy\b|\bnon[\s-]?200\b|\bconnect\b|\btunnel\b/i.test(message);
400
- }
401
281
  function isProxyConnectFailureResponse(response, body) {
402
- return (response.status === 0 && PROXY_CONNECT_FAILURE_BODY_PATTERN.test(body ?? ""));
282
+ return response.status === 0 && PROXY_CONNECT_FAILURE_BODY_PATTERN.test(body ?? "");
403
283
  }
404
284
  function createProxyConnectFailureError(body, cause) {
405
285
  const bodyExcerpt = (body ?? "").trim().slice(0, 1_000);
@@ -502,8 +382,7 @@ function normalizeStealthTransportError(error) {
502
382
  return createProxyEdgeAuthRejectedError(error instanceof Error ? error : undefined);
503
383
  }
504
384
  const proxyTunnelStatus = getProxyTunnelStatus(error);
505
- if (proxyTunnelStatus !== undefined &&
506
- isProxyPoolStaleStatus(proxyTunnelStatus)) {
385
+ if (proxyTunnelStatus !== undefined && isProxyPoolStaleStatus(proxyTunnelStatus)) {
507
386
  return createProxyPoolStaleError(proxyTunnelStatus, error instanceof Error ? error : undefined);
508
387
  }
509
388
  if (PROXY_CONNECT_FAILURE_BODY_PATTERN.test(message)) {
@@ -515,6 +394,9 @@ function normalizeStealthTransportError(error) {
515
394
  cause: error instanceof Error ? error : undefined,
516
395
  });
517
396
  }
397
+ function sleep(ms) {
398
+ return new Promise((resolve) => setTimeout(resolve, ms));
399
+ }
518
400
  function normalizeMethod(method) {
519
401
  switch (method.toUpperCase()) {
520
402
  case "HEAD":
@@ -537,6 +419,23 @@ function normalizeMethod(method) {
537
419
  throw new SDKError(`Unsupported stealth method: ${method}`);
538
420
  }
539
421
  }
422
+ function isRedirectStatus(status) {
423
+ return [301, 302, 303, 307, 308].includes(status);
424
+ }
425
+ function nextRedirectMethod(status, method) {
426
+ if (status === 303 && method !== "HEAD")
427
+ return "GET";
428
+ if ((status === 301 || status === 302) && method === "POST")
429
+ return "GET";
430
+ return method;
431
+ }
432
+ function locationHeader(headers) {
433
+ for (const [name, value] of Object.entries(headers)) {
434
+ if (name.toLowerCase() === "location")
435
+ return value;
436
+ }
437
+ return undefined;
438
+ }
540
439
  function createSessionFetcher(baseUrl, defaultProfile, clientOptions) {
541
440
  const clients = new Map();
542
441
  let closed = false;
@@ -563,19 +462,21 @@ function createSessionFetcher(baseUrl, defaultProfile, clientOptions) {
563
462
  }
564
463
  return client;
565
464
  }
566
- async function resolveRequestProxy(options, proxyAttempt) {
567
- const rawProxyAttemptOffset = options?.proxyAttemptOffset ?? 0;
568
- const proxyAttemptOffset = Number.isFinite(rawProxyAttemptOffset)
569
- ? Math.max(0, Math.floor(rawProxyAttemptOffset))
570
- : 0;
465
+ async function resolveRequestProxy(options, proxyAttempt, refreshEpoch) {
571
466
  const resolvedProxy = await resolveProxyConfigAsync({
572
467
  proxy: options?.proxy ?? clientOptions.proxy,
573
468
  upstream: clientOptions.upstream,
574
469
  apifuseConfig: clientOptions.apifuseConfig,
575
470
  affinityKey: clientOptions.affinityKey,
576
- proxyAttempt: proxyAttempt === undefined
577
- ? proxyAttemptOffset
578
- : proxyAttemptOffset + proxyAttempt,
471
+ proxyAttempt: computeProxyAttemptIndex({
472
+ baseProxyAttempt: clientOptions.proxyAttempt,
473
+ proxyAttemptOffset: options?.proxyAttemptOffset,
474
+ retryAttemptOffset: proxyAttempt,
475
+ }),
476
+ // The impit stealth transport tunnels both HTTP CONNECT and SOCKS5,
477
+ // preserving the client TLS fingerprint end-to-end.
478
+ transportProtocols: ["http", "socks5"],
479
+ ...(refreshEpoch === undefined ? {} : { proxyRefreshEpoch: refreshEpoch }),
579
480
  telemetry: clientOptions.telemetry,
580
481
  });
581
482
  if (resolvedProxy.shouldWarn && !hasWarnedMissingProxy) {
@@ -586,22 +487,51 @@ function createSessionFetcher(baseUrl, defaultProfile, clientOptions) {
586
487
  url: resolvedProxy.url,
587
488
  poolIndex: proxyPoolIndexFromDiagnostics(resolvedProxy.diagnostics),
588
489
  proxyHash: proxyEndpointHash(resolvedProxy.url),
490
+ vendor: vendorFromResolvedSource(resolvedProxy.source),
589
491
  };
590
492
  }
591
- return {
493
+ const session = {
592
494
  async fetch(url, options = {}) {
593
495
  const method = normalizeMethod(options.method ?? "GET");
594
496
  const hasExplicitRetryPolicy = options.retry !== undefined;
595
- const stealthRetryOptions = normalizeStealthRetryOptions(options.retry);
497
+ const stealthRetryOptions = normalizeProxyTransportRetryOptions(options.retry, {
498
+ extraErrorCodes: STEALTH_PROXY_TRANSPORT_RETRY_ERROR_CODES,
499
+ label: "Stealth",
500
+ }) ??
501
+ (hasExplicitRetryPolicy
502
+ ? undefined
503
+ : createDefaultProxyTransportRetryOptions({
504
+ extraErrorCodes: STEALTH_PROXY_TRANSPORT_RETRY_ERROR_CODES,
505
+ label: "Stealth",
506
+ }));
507
+ if (stealthRetryOptions) {
508
+ validateUnsafeProxyTransportRetryMethods(stealthRetryOptions, "Stealth");
509
+ }
596
510
  const hasPolicyProxy = isPolicyManagedProxy(clientOptions);
597
511
  const usesPolicyAllocator = hasPolicyProxy && !options.proxy && !clientOptions.proxy;
598
- const maxAttempts = usesPolicyAllocator
599
- ? Math.max(1, Math.min(MAX_POLICY_PROXY_RETRY_ATTEMPTS, clientOptions.proxyPolicy?.session?.poolSize ??
600
- (typeof clientOptions.upstream?.proxy === "object"
601
- ? clientOptions.upstream.proxy.session?.poolSize
602
- : undefined) ??
603
- DEFAULT_SMARTPROXY_POOL_SIZE))
604
- : 1;
512
+ const retryAttemptCap = Math.max(1, stealthRetryOptions?.attempts ?? 1);
513
+ // Span the whole vendor chain: successive attempts rotate one vendor's
514
+ // pool, then fail over to the next vendor via the flat attempt index.
515
+ const policyProxy = clientOptions.proxyPolicy ??
516
+ (typeof clientOptions.upstream?.proxy === "object"
517
+ ? clientOptions.upstream.proxy
518
+ : undefined);
519
+ // The pool span is already bounded by each vendor's max pool size
520
+ // (smartproxy ≤20, nodemaven ≤50), so the configured span never exceeds
521
+ // the chain's true maximum — a large NodeMaven pool stays fully
522
+ // reachable rather than being truncated at an arbitrary ceiling.
523
+ const policyProxyAttemptCap = Math.max(1, policyProxy ? resolvePolicyProxyPoolSpan(policyProxy) : DEFAULT_SMARTPROXY_POOL_SIZE);
524
+ // A registry vendor chain (smartproxy/nodemaven) is the only policy whose
525
+ // successive attempts resolve a *different* endpoint, so it is the only one
526
+ // that may widen the attempt cap to the pool span, de-duplicate endpoints,
527
+ // and drive allocator stale-pool refresh. A static custom/decodo policy
528
+ // resolves the same URL every attempt: widening/refreshing it would resend
529
+ // the request dozens of times (up to maxAttempts × refreshes) and bypass
530
+ // retry:false and unsafe-method controls. Static policies therefore follow
531
+ // the ordinary transport-retry budget instead.
532
+ const rotatesRegistryChain = usesPolicyAllocator && policyResolvesRegistryVendorChain(policyProxy);
533
+ const maxAttempts = rotatesRegistryChain ? policyProxyAttemptCap : retryAttemptCap;
534
+ const dedupeAllocatorEndpoints = rotatesRegistryChain;
605
535
  let lastError;
606
536
  for (let refreshAttempt = 0; refreshAttempt <= MAX_POLICY_PROXY_POOL_REFRESHES; refreshAttempt += 1) {
607
537
  let stalePoolError;
@@ -617,14 +547,12 @@ function createSessionFetcher(baseUrl, defaultProfile, clientOptions) {
617
547
  return;
618
548
  attemptRecorded = true;
619
549
  clientOptions.telemetry?.recordProxyAttempt?.({
620
- provider: "smartproxy",
550
+ provider: attemptProxy?.vendor ?? "smartproxy",
621
551
  attempt: attempt + 1,
622
552
  ...(attemptProxy?.poolIndex === undefined
623
553
  ? {}
624
554
  : { poolIndex: attemptProxy.poolIndex }),
625
- ...(attemptProxy?.proxyHash
626
- ? { proxyHash: attemptProxy.proxyHash }
627
- : {}),
555
+ ...(attemptProxy?.proxyHash ? { proxyHash: attemptProxy.proxyHash } : {}),
628
556
  outcome,
629
557
  ...(errorCode ? { errorCode } : {}),
630
558
  ...(status === undefined ? {} : { status }),
@@ -633,18 +561,21 @@ function createSessionFetcher(baseUrl, defaultProfile, clientOptions) {
633
561
  };
634
562
  try {
635
563
  assertNoUnsupportedFingerprintOverrides(options);
636
- attemptProxy = await resolveRequestProxy(options, attempt);
564
+ attemptProxy = await resolveRequestProxy(options, attempt, refreshAttempt);
637
565
  proxy = attemptProxy.url;
638
- if (proxy) {
566
+ if (proxy && dedupeAllocatorEndpoints) {
567
+ // An under-filled allocation repeats endpoints (via the modulo
568
+ // pool mapping) before the flat offset crosses into the next
569
+ // vendor. Skip an already-tried endpoint and advance the offset
570
+ // rather than breaking — breaking here would strand the request on
571
+ // the primary vendor and never reach the fallback leg.
639
572
  if (attemptedProxies.has(proxy)) {
640
- break;
573
+ continue;
641
574
  }
642
575
  attemptedProxies.add(proxy);
643
576
  }
644
577
  const ignoreTlsErrors = Boolean(options.stealth?.insecureSkipVerify ??
645
- (!hasPolicyProxy &&
646
- proxy &&
647
- clientOptions.proxyStealth?.insecureSkipVerify));
578
+ (!hasPolicyProxy && proxy && clientOptions.proxyStealth?.insecureSkipVerify));
648
579
  const profileName = options.profile ?? defaultProfile;
649
580
  const requestUrl = appendQueryParams(resolveUrl(baseUrl, url), options.params);
650
581
  const headers = { ...(options.headers ?? {}) };
@@ -656,25 +587,22 @@ function createSessionFetcher(baseUrl, defaultProfile, clientOptions) {
656
587
  const requestInit = {
657
588
  headers: normalizeHeaders(headers),
658
589
  method,
590
+ ...(options.redirect ? { redirect: options.redirect } : {}),
659
591
  ...(options.timeout ? { timeout: options.timeout } : {}),
660
592
  };
661
593
  if (options.body !== undefined) {
662
594
  requestInit.body = normalizeBody(options.body);
663
595
  }
664
596
  const response = await getClient(profileName, proxy, ignoreTlsErrors).fetch(requestUrl, requestInit);
665
- const normalized = await normalizeResponse(response);
597
+ const normalized = await normalizeResponse(response, requestUrl);
666
598
  cookieJar.setFromCookieStrings(setCookieHeadersFromResponse(response.headers));
667
- if (proxy &&
668
- isProxyConnectFailureResponse(response, normalized.body)) {
599
+ if (proxy && isProxyConnectFailureResponse(response, normalized.body)) {
669
600
  throw createProxyConnectFailureError(normalized.body);
670
601
  }
671
602
  if (response.status >= 400) {
672
603
  if (proxy &&
673
604
  usesPolicyAllocator &&
674
- isProxyEdgeTlsRejectedResponse(response.status, [
675
- JSON.stringify(responseHeadersToRecord(response.headers)),
676
- normalized.body,
677
- ].join("\n"))) {
605
+ isProxyEdgeTlsRejectedResponse(response.status, [JSON.stringify(responseHeadersToRecord(response.headers)), normalized.body].join("\n"))) {
678
606
  throw createProxyEdgeTlsRejectedError(response.status);
679
607
  }
680
608
  if (proxy && isProxyAuthIpDeniedMessage(normalized.body)) {
@@ -702,9 +630,7 @@ function createSessionFetcher(baseUrl, defaultProfile, clientOptions) {
702
630
  const normalizedError = normalizeStealthTransportError(error);
703
631
  recordProxyAttempt("error", proxyAttemptErrorCode(normalizedError), proxyAttemptStatus(normalizedError));
704
632
  lastError = normalizedError;
705
- if (proxy &&
706
- usesPolicyAllocator &&
707
- isProxyPoolRefreshableError(normalizedError)) {
633
+ if (proxy && rotatesRegistryChain && isProxyPoolRefreshableError(normalizedError)) {
708
634
  stalePoolError = normalizedError;
709
635
  if (shouldRunProxyAuthDiagnostic(normalizedError)) {
710
636
  stalePoolDiagnosticProxy = proxy;
@@ -714,21 +640,43 @@ function createSessionFetcher(baseUrl, defaultProfile, clientOptions) {
714
640
  }
715
641
  break;
716
642
  }
717
- if (proxy &&
718
- attempt + 1 <
719
- (stealthRetryOptions
720
- ? Math.min(maxAttempts, stealthRetryOptions.attempts)
721
- : maxAttempts) &&
722
- (!hasExplicitRetryPolicy
723
- ? isRetrySafeStealthMethod(method)
724
- : isExplicitStealthRetryAllowed(method, normalizedError, stealthRetryOptions)) &&
725
- isRetryableProxyTransportError(normalizedError)) {
643
+ // Cap the number of transport retries. For a policy-allocator chain,
644
+ // every attempt resolves a *different* endpoint/vendor (poolIndex
645
+ // rotates across the concatenated vendor pool spans), so a transport
646
+ // failure is a signal to advance to the next endpoint — potentially
647
+ // crossing into the fallback vendor — not to retry the same endpoint.
648
+ // Truncating that rotation at the per-endpoint retry budget would
649
+ // strand the request on the primary vendor and never reach the
650
+ // fallback, since the crossover only happens once the flat attempt
651
+ // index exceeds the primary vendor's pool size (~10-20).
652
+ // resolvePolicyTransportAttemptCap widens to the full chain span only
653
+ // for implicit, safe-method allocator requests; explicit retry
654
+ // policies (their documented `attempts` ceiling), unsafe methods, and
655
+ // static/non-registry vendors keep the per-endpoint retry budget.
656
+ const transportRetryCap = resolvePolicyTransportAttemptCap({
657
+ policy: policyProxy,
658
+ usesPolicyAllocator,
659
+ retryAttempts: stealthRetryOptions?.attempts ?? 1,
660
+ explicitRetry: hasExplicitRetryPolicy,
661
+ method,
662
+ });
663
+ if (attempt + 1 < transportRetryCap &&
664
+ shouldRetryProxyTransportAttempt({
665
+ error: normalizedError,
666
+ explicitRetry: hasExplicitRetryPolicy,
667
+ method,
668
+ options: stealthRetryOptions,
669
+ proxyUsed: Boolean(proxy),
670
+ })) {
671
+ if (stealthRetryOptions) {
672
+ await sleep(computeProxyTransportRetryDelayMs(stealthRetryOptions, attempt + 1));
673
+ }
726
674
  continue;
727
675
  }
728
676
  throw normalizedError;
729
677
  }
730
678
  }
731
- if (usesPolicyAllocator &&
679
+ if (rotatesRegistryChain &&
732
680
  stalePoolError &&
733
681
  refreshAttempt < MAX_POLICY_PROXY_POOL_REFRESHES) {
734
682
  await invalidateProxyResolutionCacheAsync({
@@ -758,11 +706,112 @@ function createSessionFetcher(baseUrl, defaultProfile, clientOptions) {
758
706
  }
759
707
  throw normalizeStealthTransportError(lastError);
760
708
  },
709
+ cookies: cookieJar,
710
+ redirects: {
711
+ async run(options) {
712
+ const maxHops = options.maxHops === undefined || !Number.isFinite(options.maxHops)
713
+ ? 10
714
+ : Math.max(0, Math.floor(options.maxHops));
715
+ const hops = [];
716
+ let currentUrl = resolveUrl(baseUrl, options.url);
717
+ let method = normalizeMethod(options.method ?? "GET");
718
+ let body = options.body;
719
+ let response;
720
+ const visitedRequests = new Set();
721
+ const { url: _url, maxHops: _maxHops, stopWhen, params, ...fetchOptions } = options;
722
+ for (let hopIndex = 0; hopIndex <= maxHops; hopIndex += 1) {
723
+ visitedRequests.add(`${method} ${currentUrl}`);
724
+ response = await session.fetch(currentUrl, {
725
+ ...fetchOptions,
726
+ body,
727
+ method,
728
+ ...(hopIndex === 0 && params ? { params } : {}),
729
+ redirect: "manual",
730
+ throwOnHttpError: false,
731
+ });
732
+ if (!isRedirectStatus(response.status)) {
733
+ return {
734
+ final: response,
735
+ hops,
736
+ reason: "completed",
737
+ cookies: cookieJar.snapshot(),
738
+ };
739
+ }
740
+ const location = locationHeader(response.headers);
741
+ const nextUrl = location
742
+ ? new URL(location, response.url ?? currentUrl).toString()
743
+ : undefined;
744
+ const hop = {
745
+ url: response.url ?? currentUrl,
746
+ status: response.status,
747
+ method,
748
+ ...(location ? { location } : {}),
749
+ ...(nextUrl ? { nextUrl } : {}),
750
+ };
751
+ hops.push(hop);
752
+ if (stopWhen && (await stopWhen(hop))) {
753
+ return {
754
+ final: response,
755
+ hops,
756
+ reason: "stopped",
757
+ cookies: cookieJar.snapshot(),
758
+ };
759
+ }
760
+ if (!nextUrl) {
761
+ return {
762
+ final: response,
763
+ hops,
764
+ reason: "missing_location",
765
+ cookies: cookieJar.snapshot(),
766
+ };
767
+ }
768
+ if (hops.length > maxHops) {
769
+ return {
770
+ final: response,
771
+ hops,
772
+ reason: "max_hops",
773
+ cookies: cookieJar.snapshot(),
774
+ };
775
+ }
776
+ const nextMethod = nextRedirectMethod(response.status, method);
777
+ if (nextMethod !== method) {
778
+ body = undefined;
779
+ }
780
+ if (visitedRequests.has(`${nextMethod} ${nextUrl}`)) {
781
+ return {
782
+ final: response,
783
+ hops,
784
+ reason: "loop",
785
+ cookies: cookieJar.snapshot(),
786
+ };
787
+ }
788
+ method = nextMethod;
789
+ currentUrl = nextUrl;
790
+ }
791
+ if (!response) {
792
+ response = await session.fetch(currentUrl, {
793
+ ...fetchOptions,
794
+ body,
795
+ method,
796
+ ...(params ? { params } : {}),
797
+ redirect: "manual",
798
+ throwOnHttpError: false,
799
+ });
800
+ }
801
+ return {
802
+ final: response,
803
+ hops,
804
+ reason: "max_hops",
805
+ cookies: cookieJar.snapshot(),
806
+ };
807
+ },
808
+ },
761
809
  close() {
762
810
  closed = true;
763
811
  clients.clear();
764
812
  },
765
813
  };
814
+ return session;
766
815
  async function classifyProxyAuthDiagnostic(profileName, proxy) {
767
816
  try {
768
817
  const response = await getClient(profileName, proxy, false).fetch(PROXY_AUTH_DIAGNOSTIC_URL, {
@@ -774,10 +823,7 @@ function createSessionFetcher(baseUrl, defaultProfile, clientOptions) {
774
823
  }
775
824
  catch (error) {
776
825
  const message = error instanceof Error
777
- ? [
778
- error.message,
779
- error.cause instanceof Error ? error.cause.message : "",
780
- ]
826
+ ? [error.message, error.cause instanceof Error ? error.cause.message : ""]
781
827
  .filter(Boolean)
782
828
  .join(" ")
783
829
  : String(error);
@@ -805,12 +851,8 @@ function hasHeader(headers, name) {
805
851
  return Object.keys(headers).some((key) => key.toLowerCase() === needle);
806
852
  }
807
853
  export function createStealthClient(baseUrl, defaultProfileOrOptions = DEFAULT_PROFILE, clientOptions = {}) {
808
- const defaultProfile = typeof defaultProfileOrOptions === "string"
809
- ? defaultProfileOrOptions
810
- : DEFAULT_PROFILE;
811
- const resolvedClientOptions = typeof defaultProfileOrOptions === "string"
812
- ? clientOptions
813
- : defaultProfileOrOptions;
854
+ const defaultProfile = typeof defaultProfileOrOptions === "string" ? defaultProfileOrOptions : DEFAULT_PROFILE;
855
+ const resolvedClientOptions = typeof defaultProfileOrOptions === "string" ? clientOptions : defaultProfileOrOptions;
814
856
  let sharedSession = null;
815
857
  function getSharedSession() {
816
858
  if (!sharedSession) {
@@ -1,4 +1,4 @@
1
- import type { ProviderSttConfig, SttContext, SttTranscribeRequest, SttVerificationCodeOptions, VerificationCodeExtractionResult } from "../types";
1
+ import type { ProviderSttConfig, SttContext, SttTranscribeRequest, SttVerificationCodeOptions, VerificationCodeExtractionResult } from "../types.js";
2
2
  export declare const APIFUSE__STT__BACKEND_ENV = "APIFUSE__STT__BACKEND";
3
3
  export declare const APIFUSE__STT__MODEL_ENV = "APIFUSE__STT__MODEL";
4
4
  export declare const CLOUDFLARE_ACCOUNT_ID_ENV = "APIFUSE__CLOUDFLARE__ACCOUNT_ID";