@apifuse/provider-sdk 2.2.0-beta.62 → 2.2.0-beta.64

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 (261) hide show
  1. package/AUTHORING.md +367 -9
  2. package/CHANGELOG.md +8 -0
  3. package/LICENSE +21 -0
  4. package/README.md +1 -0
  5. package/bin/apifuse-check.ts +84 -2
  6. package/bin/apifuse-dev.ts +11 -31
  7. package/bin/apifuse-migrate-deployment.ts +200 -0
  8. package/bin/apifuse-pack-check.ts +11 -4
  9. package/bin/apifuse-pack-types.ts +187 -4
  10. package/bin/apifuse-record.ts +44 -32
  11. package/bin/apifuse-submit-check.ts +9 -5
  12. package/bin/apifuse-test.ts +3 -1
  13. package/dist/cli/commands.d.ts +1 -1
  14. package/dist/cli/commands.js +11 -0
  15. package/dist/cli/deployment-intent.d.ts +178 -0
  16. package/dist/cli/deployment-intent.js +277 -0
  17. package/dist/cli/migrate-deployment-intent.d.ts +132 -0
  18. package/dist/cli/migrate-deployment-intent.js +1552 -0
  19. package/dist/cli/migrate-provider-shape.d.ts +5 -1
  20. package/dist/cli/migrate-provider-shape.js +4 -2
  21. package/dist/cli/resolver-runtime.d.ts +21 -0
  22. package/dist/cli/resolver-runtime.js +31 -0
  23. package/dist/define.d.ts +3 -0
  24. package/dist/define.js +5 -0
  25. package/dist/dev.d.ts +3 -1
  26. package/dist/dev.js +6 -1
  27. package/dist/engine.d.ts +35 -1
  28. package/dist/engine.js +21 -2
  29. package/dist/error-resolution.d.ts +1 -0
  30. package/dist/error-resolution.js +54 -0
  31. package/dist/errors.d.ts +39 -1
  32. package/dist/errors.js +50 -0
  33. package/dist/handle-meta.d.ts +36 -1
  34. package/dist/handle-meta.js +16 -0
  35. package/dist/handle.d.ts +23 -5
  36. package/dist/handle.js +54 -8
  37. package/dist/health-scenario.d.ts +38 -0
  38. package/dist/i18n/error-messages.d.ts +86 -0
  39. package/dist/i18n/error-messages.js +192 -0
  40. package/dist/i18n/index.d.ts +1 -0
  41. package/dist/i18n/index.js +1 -0
  42. package/dist/i18n/keys.d.ts +12 -0
  43. package/dist/i18n/keys.js +76 -5
  44. package/dist/index.d.ts +11 -3
  45. package/dist/index.js +9 -1
  46. package/dist/lint.d.ts +15 -0
  47. package/dist/lint.js +469 -82
  48. package/dist/provider.d.ts +6 -6
  49. package/dist/provider.js +3 -3
  50. package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
  51. package/dist/runtime/browser-telemetry-binding.js +83 -0
  52. package/dist/runtime/browser-telemetry.d.ts +76 -0
  53. package/dist/runtime/browser-telemetry.js +165 -0
  54. package/dist/runtime/browser.d.ts +3 -0
  55. package/dist/runtime/browser.js +208 -47
  56. package/dist/runtime/cache-telemetry.d.ts +71 -0
  57. package/dist/runtime/cache-telemetry.js +177 -0
  58. package/dist/runtime/cache.js +67 -7
  59. package/dist/runtime/capability-response.d.ts +3 -0
  60. package/dist/runtime/capability-response.js +56 -0
  61. package/dist/runtime/capability-telemetry.d.ts +46 -0
  62. package/dist/runtime/capability-telemetry.js +68 -0
  63. package/dist/runtime/diagnostic-env.d.ts +3 -0
  64. package/dist/runtime/diagnostic-env.js +10 -1
  65. package/dist/runtime/engine-mode.d.ts +74 -0
  66. package/dist/runtime/engine-mode.js +152 -0
  67. package/dist/runtime/handle.js +9 -1
  68. package/dist/runtime/http-telemetry-guard.d.ts +10 -0
  69. package/dist/runtime/http-telemetry-guard.js +62 -10
  70. package/dist/runtime/http.js +141 -35
  71. package/dist/runtime/instrumentation.js +92 -13
  72. package/dist/runtime/ocr-telemetry.d.ts +82 -0
  73. package/dist/runtime/ocr-telemetry.js +192 -0
  74. package/dist/runtime/ocr.js +35 -8
  75. package/dist/runtime/request-telemetry.d.ts +2 -0
  76. package/dist/runtime/request-telemetry.js +2 -1
  77. package/dist/runtime/resolver-runtime-options.d.ts +14 -0
  78. package/dist/runtime/resolver-runtime-options.js +33 -0
  79. package/dist/runtime/resolver-shared.d.ts +3 -2
  80. package/dist/runtime/resolver-shared.js +4 -4
  81. package/dist/runtime/resolver-telemetry.d.ts +1 -0
  82. package/dist/runtime/resolver-telemetry.js +1 -1
  83. package/dist/runtime/resolver-vendors/browser.d.ts +2 -0
  84. package/dist/runtime/resolver-vendors/browser.js +46 -0
  85. package/dist/runtime/resolver-vendors/hosts.d.ts +21 -0
  86. package/dist/runtime/resolver-vendors/hosts.js +35 -0
  87. package/dist/runtime/resolver-vendors/hypersolutions.js +2 -2
  88. package/dist/runtime/resolver-vendors/types.d.ts +5 -4
  89. package/dist/runtime/resolver.d.ts +4 -1
  90. package/dist/runtime/resolver.js +46 -13
  91. package/dist/runtime/state-telemetry.d.ts +79 -0
  92. package/dist/runtime/state-telemetry.js +194 -0
  93. package/dist/runtime/state.d.ts +3 -0
  94. package/dist/runtime/state.js +26 -7
  95. package/dist/runtime/stealth-cookies.d.ts +5 -1
  96. package/dist/runtime/stealth-cookies.js +21 -1
  97. package/dist/runtime/stealth-owned-headers.d.ts +26 -0
  98. package/dist/runtime/stealth-owned-headers.js +37 -0
  99. package/dist/runtime/stealth-telemetry.d.ts +92 -0
  100. package/dist/runtime/stealth-telemetry.js +179 -0
  101. package/dist/runtime/stealth.d.ts +4 -1
  102. package/dist/runtime/stealth.js +228 -85
  103. package/dist/runtime/stt-telemetry.d.ts +77 -0
  104. package/dist/runtime/stt-telemetry.js +176 -0
  105. package/dist/runtime/stt.js +15 -6
  106. package/dist/runtime-boundary-lint.d.ts +132 -0
  107. package/dist/runtime-boundary-lint.js +586 -0
  108. package/dist/server/error-stack-frames.d.ts +35 -0
  109. package/dist/server/error-stack-frames.js +219 -0
  110. package/dist/server/index.d.ts +7 -0
  111. package/dist/server/index.js +4 -0
  112. package/dist/server/sdk-version.d.ts +3 -0
  113. package/dist/server/sdk-version.js +19 -0
  114. package/dist/server/self-test.js +1 -18
  115. package/dist/server/sensitive-values.d.ts +1 -1
  116. package/dist/server/sensitive-values.js +9 -0
  117. package/dist/server/serve-implementation.d.ts +51 -2
  118. package/dist/server/serve-implementation.js +337 -90
  119. package/dist/server/types.d.ts +1 -0
  120. package/dist/server/types.js +7 -0
  121. package/dist/stateful/stateful-provider-owner-forwarder.js +22 -0
  122. package/dist/stateful/stateful-provider-runtime-context.d.ts +3 -0
  123. package/dist/stateful/stateful-provider-runtime-context.js +1 -0
  124. package/dist/types.d.ts +123 -1
  125. package/dist/typescript-module.d.ts +39 -0
  126. package/dist/typescript-module.js +72 -0
  127. package/package.json +6 -1
  128. package/src/cli/commands.ts +14 -0
  129. package/src/cli/deployment-intent.ts +364 -0
  130. package/src/cli/migrate-deployment-intent.ts +1908 -0
  131. package/src/cli/migrate-provider-shape.ts +10 -4
  132. package/src/cli/resolver-runtime.ts +72 -0
  133. package/src/define.ts +13 -0
  134. package/src/dev.ts +9 -2
  135. package/src/engine.ts +38 -2
  136. package/src/error-resolution.ts +58 -0
  137. package/src/errors.ts +77 -1
  138. package/src/handle-meta.ts +51 -1
  139. package/src/handle.ts +92 -10
  140. package/src/health-scenario.ts +38 -0
  141. package/src/i18n/error-messages.ts +255 -0
  142. package/src/i18n/index.ts +1 -0
  143. package/src/i18n/keys.ts +80 -6
  144. package/src/index.ts +86 -0
  145. package/src/lint.ts +604 -91
  146. package/src/provider.ts +12 -0
  147. package/src/runtime/browser-telemetry-binding.ts +92 -0
  148. package/src/runtime/browser-telemetry.ts +289 -0
  149. package/src/runtime/browser.ts +249 -65
  150. package/src/runtime/cache-telemetry.ts +247 -0
  151. package/src/runtime/cache.ts +68 -17
  152. package/src/runtime/capability-response.ts +53 -0
  153. package/src/runtime/capability-telemetry.ts +101 -0
  154. package/src/runtime/diagnostic-env.ts +11 -1
  155. package/src/runtime/engine-mode.ts +233 -0
  156. package/src/runtime/handle.ts +9 -1
  157. package/src/runtime/http-telemetry-guard.ts +66 -13
  158. package/src/runtime/http.ts +168 -37
  159. package/src/runtime/instrumentation.ts +105 -16
  160. package/src/runtime/ocr-telemetry.ts +308 -0
  161. package/src/runtime/ocr.ts +114 -74
  162. package/src/runtime/request-telemetry.ts +2 -1
  163. package/src/runtime/resolver-runtime-options.ts +50 -0
  164. package/src/runtime/resolver-shared.ts +9 -4
  165. package/src/runtime/resolver-telemetry.ts +1 -1
  166. package/src/runtime/resolver-vendors/browser.ts +65 -7
  167. package/src/runtime/resolver-vendors/hosts.ts +43 -0
  168. package/src/runtime/resolver-vendors/hypersolutions.ts +2 -2
  169. package/src/runtime/resolver-vendors/types.ts +5 -4
  170. package/src/runtime/resolver.ts +64 -21
  171. package/src/runtime/state-telemetry.ts +298 -0
  172. package/src/runtime/state.ts +46 -45
  173. package/src/runtime/stealth-cookies.ts +24 -1
  174. package/src/runtime/stealth-owned-headers.ts +41 -0
  175. package/src/runtime/stealth-telemetry.ts +304 -0
  176. package/src/runtime/stealth.ts +355 -150
  177. package/src/runtime/stt-telemetry.ts +282 -0
  178. package/src/runtime/stt.ts +93 -71
  179. package/src/runtime-boundary-lint.ts +703 -0
  180. package/src/server/error-stack-frames.ts +214 -0
  181. package/src/server/index.ts +53 -0
  182. package/src/server/sdk-version.ts +22 -0
  183. package/src/server/self-test.ts +1 -21
  184. package/src/server/sensitive-values.ts +14 -0
  185. package/src/server/serve-implementation.ts +569 -108
  186. package/src/server/types.ts +7 -0
  187. package/src/stateful/stateful-provider-owner-forwarder.ts +23 -0
  188. package/src/stateful/stateful-provider-runtime-context.ts +4 -0
  189. package/src/types.ts +128 -1
  190. package/src/typescript-module.ts +100 -0
  191. package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +0 -6
  192. package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +0 -3
  193. package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +0 -10
  194. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +0 -3
  195. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +0 -3
  196. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +0 -2
  197. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +0 -3
  198. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +0 -6
  199. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +0 -1
  200. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +0 -1
  201. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +0 -3
  202. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +0 -7
  203. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +0 -19
  204. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +0 -6
  205. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +0 -3
  206. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +0 -8
  207. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +0 -5
  208. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +0 -4
  209. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +0 -3
  210. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +0 -3
  211. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +0 -10
  212. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +0 -1
  213. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +0 -1
  214. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-index.ts.txt +0 -3
  215. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-one.ts.txt +0 -6
  216. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-two.ts.txt +0 -6
  217. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-mixed-index.ts.txt +0 -14
  218. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-registry.ts.txt +0 -3
  219. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +0 -2
  220. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +0 -3
  221. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +0 -3
  222. package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +0 -8
  223. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-hoisted-array-spread.ts.txt +0 -15
  224. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +0 -9
  225. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-kebab-operation-id.ts.txt +0 -17
  226. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +0 -5
  227. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +0 -24
  228. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +0 -16
  229. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-snake-operation-id.ts.txt +0 -21
  230. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +0 -11
  231. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +0 -7
  232. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +0 -3
  233. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +0 -18
  234. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +0 -13
  235. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +0 -31
  236. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +0 -11
  237. package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +0 -11
  238. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +0 -11
  239. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +0 -21
  240. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +0 -11
  241. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +0 -14
  242. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-en.json +0 -1
  243. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ja.json +0 -1
  244. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ko.json +0 -1
  245. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-existing-operation-namespace-en.json +0 -8
  246. package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +0 -7
  247. package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +0 -6
  248. package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +0 -7
  249. package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +0 -6
  250. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-binding-ambiguous.ts.txt +0 -12
  251. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported-other.ts.txt +0 -7
  252. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported.ts.txt +0 -5
  253. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-key-ambiguous.ts.txt +0 -19
  254. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-shorthand.ts.txt +0 -9
  255. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-typed.ts.txt +0 -11
  256. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-unrelated.ts.txt +0 -13
  257. package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +0 -7
  258. package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +0 -7
  259. package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +0 -15
  260. package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +0 -4
  261. package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +0 -12
@@ -37,6 +37,8 @@ import type {
37
37
  StealthSession,
38
38
  } from "../types.js";
39
39
  import { chrome149HeaderOrder } from "./chrome149-header-order.js";
40
+ import { registerDiagnosticValue } from "./diagnostic-env.js";
41
+ import { isStealthOwnedHeaderName } from "./stealth-owned-headers.js";
40
42
  import {
41
43
  ENGINE_CEREMONY_EGRESS_LEASE,
42
44
  type CeremonyEgressBinding,
@@ -90,6 +92,22 @@ import {
90
92
  } from "./stealth-akamai-sbsd.js";
91
93
  import { StealthCookieJar } from "./stealth-cookies.js";
92
94
 
95
+ export type {
96
+ StealthTelemetryAttemptEvent,
97
+ StealthTelemetryAttemptKind,
98
+ StealthTelemetryDiagnostics,
99
+ StealthTelemetryErrorCode,
100
+ StealthTelemetryRequestClass,
101
+ StealthTelemetrySbsdEvent,
102
+ StealthTelemetrySbsdOutcome,
103
+ StealthTelemetrySink,
104
+ } from "./stealth-telemetry.js";
105
+ import type {
106
+ StealthTelemetryAttemptKind,
107
+ StealthTelemetryErrorCode,
108
+ StealthTelemetrySink,
109
+ } from "./stealth-telemetry.js";
110
+
93
111
  export const DEFAULT_STEALTH_PROFILE: StealthProfileDescriptor = Object.freeze({
94
112
  browser: DEFAULT_STEALTH_BROWSER,
95
113
  os: DEFAULT_STEALTH_OS,
@@ -141,6 +159,8 @@ function sensitiveQueryParamNames(url: string): string[] {
141
159
  }
142
160
 
143
161
  export type StealthClientOptions = ProxyResolutionOptions & {
162
+ /** Request-scoped stealth transport observations. */
163
+ stealthTelemetry?: StealthTelemetrySink;
144
164
  warn?: (message: string) => void;
145
165
  /** Abort all requests issued by this client. */
146
166
  signal?: AbortSignal;
@@ -373,16 +393,6 @@ function normalizedHeaderEntries(
373
393
  return entries;
374
394
  }
375
395
 
376
- const SDK_OWNED_EXACT_CHROME_HEADERS = new Set([
377
- "host",
378
- "connection",
379
- "user-agent",
380
- "sec-ch-ua",
381
- "sec-ch-ua-mobile",
382
- "sec-ch-ua-platform",
383
- "accept-encoding",
384
- ]);
385
-
386
396
  // Non-pseudo-header order of real Chrome 149. The fixture captures
387
397
  // (chrome-ground-truth-capture.json, chrome-extended-capture.json,
388
398
  // h1-casing-capture.json) were taken through Playwright's `locale` option, which
@@ -558,7 +568,7 @@ function normalizedCallerHeaderEntriesFromRecord(headers: Record<string, string>
558
568
 
559
569
  function assertCallerHeadersSupported(entries: readonly HeaderTuple[]): void {
560
570
  for (const [name] of entries) {
561
- if (SDK_OWNED_EXACT_CHROME_HEADERS.has(name) || name.startsWith("sec-fetch-")) {
571
+ if (isStealthOwnedHeaderName(name)) {
562
572
  throw new SDKError(`Stealth transport owns the "${name}" header; remove it from headers.`, {
563
573
  code: "STEALTH_HEADER_OVERRIDE_UNSUPPORTED",
564
574
  });
@@ -1210,6 +1220,123 @@ function discardStealthRedirectBody(response: StealthTransportResponse): void {
1210
1220
  }
1211
1221
  }
1212
1222
 
1223
+ /** Telemetry must never replace a transport result, including with caller-provided sinks. */
1224
+ function emitStealthTelemetry(
1225
+ sink: StealthTelemetrySink | undefined,
1226
+ record: (sink: StealthTelemetrySink) => void,
1227
+ ): void {
1228
+ if (!sink) return;
1229
+ try {
1230
+ record(sink);
1231
+ } catch {
1232
+ /* The request owns its result. */
1233
+ }
1234
+ }
1235
+
1236
+ type StealthExchangeTelemetry = {
1237
+ sink: StealthTelemetrySink;
1238
+ profileId: StealthProfileDescriptor;
1239
+ proxyUsed: boolean;
1240
+ kind: StealthTelemetryAttemptKind;
1241
+ cookieJar?: StealthCookieJar;
1242
+ };
1243
+
1244
+ function stripStealthCookieMaterial(text: string, cookieNames: readonly string[]): string {
1245
+ // Header-shaped Cookie/Set-Cookie material is redacted wholesale, including
1246
+ // foreign names. Bare pairs outside a header are limited to jar-known names:
1247
+ // a foreign bare name is indistinguishable from benign key=value prose and is
1248
+ // therefore covered only when layer 1 registered its value.
1249
+ let result = text.replace(
1250
+ /(^|[^\p{L}\p{N}_-])[\t ]*(?:cookie|set-cookie)[\t ]*(?::|=)[^\r\n]*/giu,
1251
+ "$1[REDACTED]",
1252
+ );
1253
+ result = result.replace(
1254
+ /"(?:cookie|set-cookie)"[\t ]*:[\t ]*"(?:\\.|[^"\\])*"/giu,
1255
+ "[REDACTED]",
1256
+ );
1257
+ for (const name of cookieNames) {
1258
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1259
+ result = result.replace(
1260
+ new RegExp(`(^|[;,&?\\s])${escaped}\\s*=\\s*[^;,\\s&\\r\\n]+`, "giu"),
1261
+ "$1[REDACTED]",
1262
+ );
1263
+ }
1264
+ return result;
1265
+ }
1266
+
1267
+ function startStealthExchange(
1268
+ telemetry: StealthExchangeTelemetry | undefined,
1269
+ method: StealthMethod,
1270
+ options: StealthFetchOptions,
1271
+ signal?: AbortSignal,
1272
+ ): (status?: number, error?: unknown) => void {
1273
+ if (!telemetry) return () => {};
1274
+ const startedAt = Date.now();
1275
+ const requestClass = chromeRequestClass(method, options.stealth?.requestClass);
1276
+ let recorded = false;
1277
+ return (status, error) => {
1278
+ if (recorded) return;
1279
+ recorded = true;
1280
+ emitStealthTelemetry(telemetry?.sink, (sink) => {
1281
+ let code: string | undefined;
1282
+ if (error !== undefined) {
1283
+ try {
1284
+ code = signal?.aborted
1285
+ ? "transport_cancelled"
1286
+ : normalizeStealthTransportError(error).code;
1287
+ } catch {
1288
+ code = "other";
1289
+ }
1290
+ }
1291
+ const knownCodes: readonly string[] = [
1292
+ "transport_network_error",
1293
+ "transport_timeout",
1294
+ "transport_cancelled",
1295
+ "upstream_http_error",
1296
+ "response_too_large",
1297
+ "proxy_connect_failed",
1298
+ "PROXY_POOL_STALE",
1299
+ "PROXY_EDGE_AUTH_REJECTED",
1300
+ "PROXY_AUTH_IP_DENIED",
1301
+ "PROXY_EDGE_TLS_REJECTED",
1302
+ "PROXY_REQUIRED",
1303
+ ];
1304
+ const errorCode = code
1305
+ ? knownCodes.includes(code)
1306
+ ? (code as StealthTelemetryErrorCode)
1307
+ : "other"
1308
+ : status !== undefined && status >= 400
1309
+ ? "upstream_http_error"
1310
+ : undefined;
1311
+ const strip = (text: string) =>
1312
+ stripStealthCookieMaterial(text, telemetry.cookieJar?.names() ?? []);
1313
+ sink.recordAttempt({
1314
+ ms: Date.now() - startedAt,
1315
+ profileId: telemetry.profileId,
1316
+ proxyUsed: telemetry.proxyUsed,
1317
+ kind: telemetry.kind,
1318
+ requestClass:
1319
+ requestClass === "navigation" && options.stealth?.userActivation === false
1320
+ ? "script_navigation"
1321
+ : requestClass,
1322
+ ...(status === undefined ? {} : { status }),
1323
+ ...(errorCode ? { errorCode } : {}),
1324
+ ...(error === undefined
1325
+ ? {}
1326
+ : {
1327
+ diagnostics: {
1328
+ name: strip(error instanceof Error ? error.name : "Error"),
1329
+ message: strip(error instanceof Error ? error.message : String(error)),
1330
+ ...(error instanceof Error && "code" in error && typeof error.code === "string"
1331
+ ? { code: strip(error.code) }
1332
+ : {}),
1333
+ },
1334
+ }),
1335
+ });
1336
+ });
1337
+ };
1338
+ }
1339
+
1213
1340
  async function fetchStealthRedirectChain(
1214
1341
  transport: WreqSession,
1215
1342
  cookieJar: StealthCookieJar,
@@ -1223,6 +1350,7 @@ async function fetchStealthRedirectChain(
1223
1350
  body: string | Buffer | undefined,
1224
1351
  headers: Record<string, string>,
1225
1352
  ) => HeaderTuple[],
1353
+ telemetry?: StealthExchangeTelemetry,
1226
1354
  ): Promise<{ normalized: StealthResponse; response: StealthTransportResponse }> {
1227
1355
  let currentUrl = requestUrl;
1228
1356
  let currentMethod = method;
@@ -1232,86 +1360,102 @@ async function fetchStealthRedirectChain(
1232
1360
  let response: StealthTransportResponse;
1233
1361
  const deadline = options.timeout ? performance.now() + options.timeout : undefined;
1234
1362
 
1235
- while (true) {
1236
- throwIfAmbientAborted(signal);
1237
- const headers = { ...currentHeaders };
1238
- if (!hasHeader(headers, "cookie")) {
1239
- const cookieHeader = cookieJar.toHeader(currentUrl);
1240
- if (cookieHeader) headers.cookie = cookieHeader;
1241
- }
1242
- const requestInit: StealthRequestInit = {
1243
- headers: buildHeaders
1244
- ? buildHeaders(currentUrl, currentMethod, currentBody, headers)
1245
- : headers,
1246
- method: currentMethod,
1247
- redirect: "manual",
1248
- ...(new URL(currentUrl).protocol === "http:" ? { disableDefaultHeaders: true } : {}),
1249
- ...(signal ? { signal } : {}),
1250
- };
1251
- if (currentBody !== undefined) requestInit.body = currentBody;
1363
+ let finishExchange: ReturnType<typeof startStealthExchange> | undefined;
1364
+ let status: number | undefined;
1365
+ let exchangeError: unknown;
1366
+ try {
1367
+ while (true) {
1368
+ throwIfAmbientAborted(signal);
1369
+ const headers = { ...currentHeaders };
1370
+ if (!hasHeader(headers, "cookie")) {
1371
+ const cookieHeader = cookieJar.toHeader(currentUrl);
1372
+ if (cookieHeader) headers.cookie = cookieHeader;
1373
+ }
1374
+ const requestInit: StealthRequestInit = {
1375
+ headers: buildHeaders
1376
+ ? buildHeaders(currentUrl, currentMethod, currentBody, headers)
1377
+ : headers,
1378
+ method: currentMethod,
1379
+ redirect: "manual",
1380
+ ...(new URL(currentUrl).protocol === "http:" ? { disableDefaultHeaders: true } : {}),
1381
+ ...(signal ? { signal } : {}),
1382
+ };
1383
+ if (currentBody !== undefined) requestInit.body = currentBody;
1252
1384
 
1253
- await transport.clearCookies();
1254
- throwIfAmbientAborted(signal);
1255
- const remainingTimeout =
1256
- deadline === undefined ? undefined : Math.ceil(deadline - performance.now());
1257
- if (remainingTimeout !== undefined && remainingTimeout <= 0) {
1258
- throw new TransportError("Request timed out", {
1259
- code: "transport_timeout",
1260
- status: 0,
1261
- });
1262
- }
1263
- if (remainingTimeout !== undefined) requestInit.timeout = remainingTimeout;
1264
- response = await transport.fetch(currentUrl, requestInit);
1265
- if (signal?.aborted) {
1266
- discardStealthRedirectBody(response);
1267
- throw toAmbientCancellationError(signal);
1268
- }
1269
- cookieJar.setFromCookieStrings(
1270
- setCookieHeadersFromResponse(response.headers),
1271
- response.url ?? currentUrl,
1272
- );
1385
+ await transport.clearCookies();
1386
+ throwIfAmbientAborted(signal);
1387
+ const remainingTimeout =
1388
+ deadline === undefined ? undefined : Math.ceil(deadline - performance.now());
1389
+ if (remainingTimeout !== undefined && remainingTimeout <= 0) {
1390
+ throw new TransportError("Request timed out", {
1391
+ code: "transport_timeout",
1392
+ status: 0,
1393
+ });
1394
+ }
1395
+ if (remainingTimeout !== undefined) requestInit.timeout = remainingTimeout;
1396
+ status = undefined;
1397
+ finishExchange = startStealthExchange(telemetry, currentMethod, options, signal);
1398
+ response = await transport.fetch(currentUrl, requestInit);
1399
+ status = response.status;
1400
+ if (signal?.aborted) {
1401
+ discardStealthRedirectBody(response);
1402
+ throw toAmbientCancellationError(signal);
1403
+ }
1404
+ cookieJar.setFromCookieStrings(
1405
+ setCookieHeadersFromResponse(response.headers),
1406
+ response.url ?? currentUrl,
1407
+ );
1408
+
1409
+ if (!isRedirectStatus(response.status) || options.redirect === "manual") break;
1410
+ if (options.redirect === "error") {
1411
+ discardStealthRedirectBody(response);
1412
+ throw new TransportError("Stealth request encountered a redirect", {
1413
+ code: "transport_network_error",
1414
+ status: 0,
1415
+ });
1416
+ }
1273
1417
 
1274
- if (!isRedirectStatus(response.status) || options.redirect === "manual") break;
1275
- if (options.redirect === "error") {
1418
+ const nextUrl = resolveRedirectUrl(
1419
+ response.headers.get("location") ?? undefined,
1420
+ response.url ?? currentUrl,
1421
+ );
1422
+ if (!nextUrl) break;
1423
+ if (followedHops >= MAX_STEALTH_REDIRECT_HOPS) {
1424
+ discardStealthRedirectBody(response);
1425
+ throw new TransportError(
1426
+ `Stealth request exceeded the ${MAX_STEALTH_REDIRECT_HOPS}-redirect limit`,
1427
+ { code: "transport_network_error", status: 0 },
1428
+ );
1429
+ }
1430
+ assertStealthRedirectUrl(nextUrl);
1276
1431
  discardStealthRedirectBody(response);
1277
- throw new TransportError("Stealth request encountered a redirect", {
1278
- code: "transport_network_error",
1279
- status: 0,
1280
- });
1432
+ const nextMethod = nextRedirectMethod(response.status, currentMethod);
1433
+ if (nextMethod !== currentMethod) {
1434
+ currentBody = undefined;
1435
+ currentHeaders = withoutRedirectBodyHeaders(currentHeaders);
1436
+ }
1437
+ currentMethod = nextMethod;
1438
+ currentUrl = nextUrl;
1439
+ followedHops += 1;
1440
+ emitStealthTelemetry(telemetry?.sink, (sink) => sink.recordRedirectHop());
1441
+ finishExchange(status);
1442
+ finishExchange = undefined;
1281
1443
  }
1282
1444
 
1283
- const nextUrl = resolveRedirectUrl(
1284
- response.headers.get("location") ?? undefined,
1285
- response.url ?? currentUrl,
1445
+ const normalized = await normalizeResponseWithSignal(
1446
+ response,
1447
+ currentUrl,
1448
+ options.maxBodyBytes,
1449
+ signal,
1286
1450
  );
1287
- if (!nextUrl) break;
1288
- if (followedHops >= MAX_STEALTH_REDIRECT_HOPS) {
1289
- discardStealthRedirectBody(response);
1290
- throw new TransportError(
1291
- `Stealth request exceeded the ${MAX_STEALTH_REDIRECT_HOPS}-redirect limit`,
1292
- { code: "transport_network_error", status: 0 },
1293
- );
1294
- }
1295
- assertStealthRedirectUrl(nextUrl);
1296
- discardStealthRedirectBody(response);
1297
- const nextMethod = nextRedirectMethod(response.status, currentMethod);
1298
- if (nextMethod !== currentMethod) {
1299
- currentBody = undefined;
1300
- currentHeaders = withoutRedirectBodyHeaders(currentHeaders);
1301
- }
1302
- currentMethod = nextMethod;
1303
- currentUrl = nextUrl;
1304
- followedHops += 1;
1451
+ if (followedHops > 0) normalized.redirected = true;
1452
+ return { normalized, response };
1453
+ } catch (error) {
1454
+ exchangeError = error;
1455
+ throw error;
1456
+ } finally {
1457
+ finishExchange?.(status, exchangeError);
1305
1458
  }
1306
-
1307
- const normalized = await normalizeResponseWithSignal(
1308
- response,
1309
- currentUrl,
1310
- options.maxBodyBytes,
1311
- signal,
1312
- );
1313
- if (followedHops > 0) normalized.redirected = true;
1314
- return { normalized, response };
1315
1459
  }
1316
1460
 
1317
1461
  function createSessionFetcher(
@@ -1323,7 +1467,7 @@ function createSessionFetcher(
1323
1467
  let closed = false;
1324
1468
  let hasWarnedMissingProxy = false;
1325
1469
  const warn = clientOptions.warn ?? console.warn;
1326
- const cookieJar = new StealthCookieJar([], baseUrl);
1470
+ const cookieJar = new StealthCookieJar([], baseUrl, registerDiagnosticValue);
1327
1471
  const akamaiSbsdState: AkamaiSbsdSessionState = { transactions: new Map() };
1328
1472
  const challengedReplays = new WeakMap<StealthResponse, ChallengedReplayRecord>();
1329
1473
  const ceremonyEgressLease = clientOptions[ENGINE_CEREMONY_EGRESS_LEASE];
@@ -1558,9 +1702,12 @@ function createSessionFetcher(
1558
1702
  // rotation after a transport failure). Its response reached the origin from an
1559
1703
  // egress the handle does not name; accepting it would split the ceremony identity.
1560
1704
  if (attemptProxy.url !== lease.binding.proxyUrl) {
1561
- throw new SDKError("The response arrived on an egress other than the ceremony's bound endpoint", {
1562
- code: "EGRESS_LEASE_BINDING_INVALID",
1563
- });
1705
+ throw new SDKError(
1706
+ "The response arrived on an egress other than the ceremony's bound endpoint",
1707
+ {
1708
+ code: "EGRESS_LEASE_BINDING_INVALID",
1709
+ },
1710
+ );
1564
1711
  }
1565
1712
  return;
1566
1713
  }
@@ -1718,6 +1865,10 @@ function createSessionFetcher(
1718
1865
  attemptResponse: StealthTransportResponse,
1719
1866
  attemptNormalized: StealthResponse,
1720
1867
  ): StealthResponse => {
1868
+ if (attemptNormalized.challenge)
1869
+ emitStealthTelemetry(clientOptions.stealthTelemetry, (sink) =>
1870
+ sink.recordSbsd(attemptNormalized.challenge!.outcome),
1871
+ );
1721
1872
  if (
1722
1873
  !attemptNormalized.challenge &&
1723
1874
  attemptResponse.status >= 400 &&
@@ -1854,6 +2005,7 @@ function createSessionFetcher(
1854
2005
  fetchSignal: AbortSignal | undefined,
1855
2006
  orderedHeaders = buildOrderedHeaders,
1856
2007
  sessionDefaultHeaders = defaultHeaders,
2008
+ kind: StealthTelemetryAttemptKind = "request",
1857
2009
  ) =>
1858
2010
  withClient(
1859
2011
  requestProfile,
@@ -1868,6 +2020,15 @@ function createSessionFetcher(
1868
2020
  fetchOptions,
1869
2021
  fetchSignal,
1870
2022
  orderedHeaders,
2023
+ clientOptions.stealthTelemetry
2024
+ ? {
2025
+ sink: clientOptions.stealthTelemetry,
2026
+ profileId: requestProfile,
2027
+ proxyUsed: Boolean(proxy),
2028
+ kind,
2029
+ cookieJar,
2030
+ }
2031
+ : undefined,
1871
2032
  ),
1872
2033
  fetchSignal,
1873
2034
  sessionDefaultHeaders,
@@ -1928,7 +2089,14 @@ function createSessionFetcher(
1928
2089
  akamaiSbsdState,
1929
2090
  )
1930
2091
  : undefined;
2092
+ if (akamaiSbsd && !detected)
2093
+ emitStealthTelemetry(clientOptions.stealthTelemetry, (sink) =>
2094
+ sink.recordSbsd({ detected: false }),
2095
+ );
1931
2096
  if (detected && akamaiSbsd) {
2097
+ emitStealthTelemetry(clientOptions.stealthTelemetry, (sink) =>
2098
+ sink.recordSbsd("detected"),
2099
+ );
1932
2100
  if (challengeSolveAttempted || challengeRefetchAttempted) {
1933
2101
  normalized.challenge = {
1934
2102
  challenge: detected,
@@ -2020,6 +2188,7 @@ function createSessionFetcher(
2020
2188
  init.body,
2021
2189
  normalizeHeaders(transportHeaders),
2022
2190
  ),
2191
+ "resolver",
2023
2192
  );
2024
2193
  return {
2025
2194
  status: result.normalized.status,
@@ -2057,54 +2226,57 @@ function createSessionFetcher(
2057
2226
  const reuseCompletedSuccess =
2058
2227
  explicitReplay && akamaiSbsdState.completedSuccessKey === transactionKey;
2059
2228
  if (reuseCompletedSuccess) akamaiSbsdState.completedSuccessKey = undefined;
2060
- let ownsTransaction = false;
2229
+ let ownsTransaction = false;
2061
2230
  if (!reuseCompletedSuccess) {
2062
2231
  let transaction = akamaiSbsdState.transactions.get(transactionKey);
2063
- if (!transaction) {
2232
+ if (!transaction) {
2064
2233
  akamaiSbsdState.completedSuccessKey = undefined;
2065
- challengeSolveAttempted = true;
2066
- ownsTransaction = true;
2067
- // The solve spans several round trips, so it runs under the client's ambient
2068
- // signal and the resolver's own timeouts, not this fetch's per-request `timeout`.
2069
- transaction = {
2070
- result: akamaiSbsd
2071
- .solve(
2072
- detected,
2073
- resolverTransport,
2074
- clientOptions.signal ?? new AbortController().signal,
2075
- )
2076
- .then(
2077
- () => ({ solved: true }) as const,
2078
- (error: unknown) => ({ solved: false, error }) as const,
2079
- )
2080
- .finally(() => {
2081
- akamaiSbsdState.transactions.delete(transactionKey);
2082
- }),
2083
- };
2084
- akamaiSbsdState.transactions.set(transactionKey, transaction);
2085
- }
2086
- const transactionResult = await transaction.result;
2087
- if (!transactionResult.solved) {
2088
- if (ownsTransaction) {
2089
- // The proxy delivered the challenged response; the resolver failed.
2090
- // Surface that failure as-is: it is not a transport fault to normalize
2091
- // or retry.
2234
+ challengeSolveAttempted = true;
2235
+ ownsTransaction = true;
2236
+ // The solve spans several round trips, so it runs under the client's ambient
2237
+ // signal and the resolver's own timeouts, not this fetch's per-request `timeout`.
2238
+ transaction = {
2239
+ result: akamaiSbsd
2240
+ .solve(
2241
+ detected,
2242
+ resolverTransport,
2243
+ clientOptions.signal ?? new AbortController().signal,
2244
+ )
2245
+ .then(
2246
+ () => ({ solved: true }) as const,
2247
+ (error: unknown) => ({ solved: false, error }) as const,
2248
+ )
2249
+ .finally(() => {
2250
+ akamaiSbsdState.transactions.delete(transactionKey);
2251
+ }),
2252
+ };
2253
+ akamaiSbsdState.transactions.set(transactionKey, transaction);
2254
+ }
2255
+ const transactionResult = await transaction.result;
2256
+ if (!transactionResult.solved) {
2257
+ emitStealthTelemetry(clientOptions.stealthTelemetry, (sink) =>
2258
+ sink.recordSbsd("solve_failed"),
2259
+ );
2260
+ if (ownsTransaction) {
2261
+ // The proxy delivered the challenged response; the resolver failed.
2262
+ // Surface that failure as-is: it is not a transport fault to normalize
2263
+ // or retry.
2092
2264
  if (!explicitReplay) {
2093
2265
  recordProxyAttempt("ok", undefined, challenged.response.status);
2094
2266
  }
2095
- challengeSolveFailure = { error: transactionResult.error };
2096
- throw challengeSolveFailure;
2097
- }
2267
+ challengeSolveFailure = { error: transactionResult.error };
2268
+ throw challengeSolveFailure;
2269
+ }
2098
2270
  return {
2099
2271
  normalized: {
2100
2272
  ...challenged.normalized,
2101
2273
  challenge: { challenge: detected, outcome: "solve_failed" },
2102
2274
  },
2103
2275
  response: challenged.response,
2104
- };
2276
+ };
2105
2277
  }
2106
- }
2107
- challengeRefetchAttempted = true;
2278
+ }
2279
+ challengeRefetchAttempted = true;
2108
2280
  // An explicit replay is a new transport exchange on the bound endpoint and
2109
2281
  // gets its own proxy-attempt record timed from here, not from the initiating
2110
2282
  // fetch (the caller's think time in between is not transport duration); the
@@ -2122,28 +2294,35 @@ function createSessionFetcher(
2122
2294
  attemptRecorded = false;
2123
2295
  attemptStartedAt = Date.now();
2124
2296
  }
2297
+ if (!explicitReplay)
2298
+ emitStealthTelemetry(clientOptions.stealthTelemetry, (sink) =>
2299
+ sink.recordSafeRefetch(),
2300
+ );
2125
2301
  const replayed = await fetchOnBoundSession(
2126
- requestUrl,
2127
- method,
2302
+ requestUrl,
2303
+ method,
2128
2304
  replayOptions,
2129
- clientOptions.signal,
2305
+ clientOptions.signal,
2130
2306
  );
2131
2307
  throwProxyTransportFault(replayed.response, replayed.normalized.body);
2132
- const persisted = detectAkamaiSbsdChallenge(
2308
+ const persisted = detectAkamaiSbsdChallenge(
2133
2309
  replayed.normalized,
2134
- requestUrl,
2135
- cookieJar,
2136
- akamaiSbsd.allowedHosts,
2137
- akamaiSbsdState,
2138
- );
2139
- if (persisted) {
2310
+ requestUrl,
2311
+ cookieJar,
2312
+ akamaiSbsd.allowedHosts,
2313
+ akamaiSbsdState,
2314
+ );
2315
+ emitStealthTelemetry(clientOptions.stealthTelemetry, (sink) =>
2316
+ sink.recordSbsd(persisted ? "challenge_persisted" : "refetch_clear"),
2317
+ );
2318
+ if (persisted) {
2140
2319
  replayed.normalized.challenge = {
2141
- challenge: persisted,
2142
- outcome: "challenge_persisted",
2143
- };
2320
+ challenge: persisted,
2321
+ outcome: "challenge_persisted",
2322
+ };
2144
2323
  } else if (ownsTransaction && !explicitReplay) {
2145
2324
  akamaiSbsdState.completedSuccessKey = transactionKey;
2146
- }
2325
+ }
2147
2326
  return replayed;
2148
2327
  };
2149
2328
 
@@ -2156,18 +2335,18 @@ function createSessionFetcher(
2156
2335
  consumed: false,
2157
2336
  generation: attemptEgressGeneration,
2158
2337
  async replay() {
2159
- try {
2338
+ try {
2160
2339
  const replayed = await solveAndReplay(true);
2161
2340
  return finalizeAttempt(replayed.response, replayed.normalized);
2162
2341
  } catch (error) {
2163
2342
  const normalizedError = normalizeAttemptError(error);
2164
2343
  releaseFailedCeremonyEgress(proxy, normalizedError);
2165
2344
  throw recordAttemptFailure(normalizedError);
2166
- }
2345
+ }
2167
2346
  },
2168
2347
  });
2169
2348
  return finalizeAttempt(response, normalized);
2170
- }
2349
+ }
2171
2350
  ({ normalized, response } = await solveAndReplay(false));
2172
2351
  }
2173
2352
 
@@ -2264,6 +2443,7 @@ function createSessionFetcher(
2264
2443
  refreshAttempt < MAX_POLICY_PROXY_POOL_REFRESHES
2265
2444
  ) {
2266
2445
  throwIfAmbientAborted(clientOptions.signal);
2446
+ emitStealthTelemetry(clientOptions.stealthTelemetry, (sink) => sink.recordPoolRefresh());
2267
2447
  await invalidateProxyResolutionCacheAsync({
2268
2448
  proxyPolicy: clientOptions.proxyPolicy,
2269
2449
  upstream: clientOptions.upstream,
@@ -2512,6 +2692,7 @@ function createSessionFetcher(
2512
2692
  if (decision.nextMethod !== method) {
2513
2693
  body = undefined;
2514
2694
  }
2695
+ emitStealthTelemetry(clientOptions.stealthTelemetry, (sink) => sink.recordRedirectHop());
2515
2696
  method = decision.nextMethod;
2516
2697
  currentUrl = decision.nextUrl;
2517
2698
  }
@@ -2564,18 +2745,42 @@ function createSessionFetcher(
2564
2745
  throwIfAmbientAborted(clientOptions.signal);
2565
2746
  await client.clearCookies();
2566
2747
  throwIfAmbientAborted(clientOptions.signal);
2567
- const response = await client.fetch(PROXY_AUTH_DIAGNOSTIC_URL, {
2568
- method: "GET",
2569
- timeout: PROXY_AUTH_DIAGNOSTIC_TIMEOUT_MS,
2570
- ...(clientOptions.signal ? { signal: clientOptions.signal } : {}),
2571
- });
2572
- const normalized = await normalizeResponseWithSignal(
2573
- response,
2574
- undefined,
2575
- undefined,
2748
+ const finishExchange = startStealthExchange(
2749
+ clientOptions.stealthTelemetry
2750
+ ? {
2751
+ sink: clientOptions.stealthTelemetry,
2752
+ profileId: profile,
2753
+ proxyUsed: true,
2754
+ kind: "proxy_diagnostic",
2755
+ cookieJar,
2756
+ }
2757
+ : undefined,
2758
+ "GET",
2759
+ {},
2576
2760
  clientOptions.signal,
2577
2761
  );
2578
- return classifyProxyAuthDiagnosticMessage(normalized.body);
2762
+ let status: number | undefined;
2763
+ let exchangeError: unknown;
2764
+ try {
2765
+ const response = await client.fetch(PROXY_AUTH_DIAGNOSTIC_URL, {
2766
+ method: "GET",
2767
+ timeout: PROXY_AUTH_DIAGNOSTIC_TIMEOUT_MS,
2768
+ ...(clientOptions.signal ? { signal: clientOptions.signal } : {}),
2769
+ });
2770
+ status = response.status;
2771
+ const normalized = await normalizeResponseWithSignal(
2772
+ response,
2773
+ undefined,
2774
+ undefined,
2775
+ clientOptions.signal,
2776
+ );
2777
+ return classifyProxyAuthDiagnosticMessage(normalized.body);
2778
+ } catch (error) {
2779
+ exchangeError = error;
2780
+ throw error;
2781
+ } finally {
2782
+ finishExchange(status, exchangeError);
2783
+ }
2579
2784
  },
2580
2785
  clientOptions.signal,
2581
2786
  );