@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
@@ -1,4 +1,33 @@
1
- import { readDiagnosticEnv } from "./diagnostic-env.js";
1
+ import { readDiagnosticEnv, registerDiagnosticValue } from "./diagnostic-env.js";
2
+ import { AsyncResource } from "node:async_hooks";
3
+ import type {
4
+ BrowserTelemetryPoolAcquireOutcome,
5
+ BrowserTelemetrySink,
6
+ } from "./browser-telemetry.js";
7
+ import { observeTelemetryCallback as observeBrowserTelemetry } from "./http-telemetry-guard.js";
8
+ import { cookieValuesFromHeaders } from "./stealth-cookies.js";
9
+
10
+ function browserTelemetryErrorCode(
11
+ error: unknown,
12
+ ):
13
+ | "BROWSER_CDP_POOL_REQUIRED"
14
+ | "BROWSER_PROXY_INVALID"
15
+ | "BROWSER_RUNTIME_UNSUPPORTED"
16
+ | "BROWSER_CDP_POOL_ERROR"
17
+ | "other" {
18
+ const code =
19
+ error && typeof error === "object" && "code" in error
20
+ ? (error as { code?: unknown }).code
21
+ : undefined;
22
+ if (
23
+ code === "BROWSER_CDP_POOL_REQUIRED" ||
24
+ code === "BROWSER_PROXY_INVALID" ||
25
+ code === "BROWSER_RUNTIME_UNSUPPORTED"
26
+ )
27
+ return code;
28
+ if (typeof code === "number") return "BROWSER_CDP_POOL_ERROR";
29
+ return "other";
30
+ }
2
31
  import { createRequire } from "node:module";
3
32
  import type { CDPSession, Frame, LaunchOptions, Locator, Page, Request, Route } from "playwright";
4
33
 
@@ -416,9 +445,7 @@ function getCdpAuthRequiredRequestId(params: unknown): string | null {
416
445
 
417
446
  function isCdpProxyAuthChallenge(params: unknown): boolean {
418
447
  return (
419
- isRecord(params) &&
420
- isRecord(params.authChallenge) &&
421
- params.authChallenge.source === "Proxy"
448
+ isRecord(params) && isRecord(params.authChallenge) && params.authChallenge.source === "Proxy"
422
449
  );
423
450
  }
424
451
 
@@ -427,6 +454,7 @@ async function handleCdpAuthRequired(
427
454
  proxy: PlaywrightProxy & { username: string; password: string },
428
455
  authAttempts: Set<string>,
429
456
  params: unknown,
457
+ telemetry?: BrowserTelemetrySink,
430
458
  ): Promise<void> {
431
459
  const requestId = getCdpAuthRequiredRequestId(params);
432
460
  if (!requestId) return;
@@ -436,20 +464,21 @@ async function handleCdpAuthRequired(
436
464
  const response =
437
465
  isCdpProxyAuthChallenge(params) && !authAttempts.has(requestId)
438
466
  ? (() => {
439
- authAttempts.add(requestId);
440
- return {
441
- authChallengeResponse: {
442
- password: proxy.password,
443
- response: "ProvideCredentials" as const,
444
- username: proxy.username,
445
- },
467
+ authAttempts.add(requestId);
468
+ observeBrowserTelemetry(telemetry, () => telemetry?.recordProxyAuthChallenge());
469
+ return {
470
+ authChallengeResponse: {
471
+ password: proxy.password,
472
+ response: "ProvideCredentials" as const,
473
+ username: proxy.username,
474
+ },
475
+ requestId,
476
+ };
477
+ })()
478
+ : {
479
+ authChallengeResponse: { response: "CancelAuth" as const },
446
480
  requestId,
447
481
  };
448
- })()
449
- : {
450
- authChallengeResponse: { response: "CancelAuth" as const },
451
- requestId,
452
- };
453
482
 
454
483
  await session.send("Fetch.continueWithAuth", response).catch(() => undefined);
455
484
  }
@@ -485,6 +514,7 @@ export type BrowserClientOptions = BrowserOptions & {
485
514
  executablePath?: string;
486
515
  extraArgs?: string[];
487
516
  serviceWorkers?: "allow" | "block";
517
+ telemetry?: BrowserTelemetrySink;
488
518
  };
489
519
 
490
520
  type SupportedBrowserClient = {
@@ -602,6 +632,8 @@ function toPlaywrightProxy(proxy: string | undefined): LaunchOptions["proxy"] {
602
632
  fix: "Percent-encode the proxy username and password as URL userinfo.",
603
633
  });
604
634
  }
635
+ registerDiagnosticValue(username);
636
+ registerDiagnosticValue(password);
605
637
 
606
638
  return {
607
639
  server: `${parsed.protocol}//${parsed.host}`,
@@ -803,13 +835,21 @@ class PlaywrightBrowserPage implements BrowserPageContract {
803
835
  constructor(
804
836
  private readonly page: Page,
805
837
  private readonly proxy: LaunchOptions["proxy"] = undefined,
838
+ private readonly telemetry?: BrowserTelemetrySink,
806
839
  ) {}
807
840
 
808
841
  async goto(
809
842
  url: string,
810
843
  options?: { readonly timeout?: number; readonly waitUntil?: "load" | "domcontentloaded" },
811
844
  ): Promise<void> {
812
- await this.page.goto(url, options);
845
+ const response = await this.page.goto(url, options);
846
+ const headers = await response?.headersArray().catch(() => []);
847
+ for (const header of headers ?? []) {
848
+ if (header.name.toLowerCase() === "set-cookie") {
849
+ registerDiagnosticValue(header.value);
850
+ for (const value of cookieValuesFromHeaders(header.value)) registerDiagnosticValue(value);
851
+ }
852
+ }
813
853
  }
814
854
 
815
855
  async evaluate<T>(fn: string | (() => T)): Promise<T> {
@@ -870,7 +910,9 @@ class PlaywrightBrowserPage implements BrowserPageContract {
870
910
  }
871
911
 
872
912
  async cookies(): Promise<readonly BrowserCookie[]> {
873
- return (await this.page.context().cookies()).map(toBrowserCookie);
913
+ const cookies = (await this.page.context().cookies()).map(toBrowserCookie);
914
+ for (const cookie of cookies) registerDiagnosticValue(cookie.value);
915
+ return cookies;
874
916
  }
875
917
 
876
918
  async withResourcePolicy<T>(policy: BrowserResourcePolicy, run: () => Promise<T>): Promise<T> {
@@ -914,7 +956,7 @@ class PlaywrightBrowserPage implements BrowserPageContract {
914
956
  const authAttempts = new Set<string>();
915
957
  const onAuthRequired = (params: unknown) => {
916
958
  if (!proxy) return;
917
- void handleCdpAuthRequired(session, proxy, authAttempts, params);
959
+ void handleCdpAuthRequired(session, proxy, authAttempts, params, this.telemetry);
918
960
  };
919
961
  session.on("Fetch.requestPaused", onRequestPaused);
920
962
  if (proxy) {
@@ -990,10 +1032,14 @@ class PlaywrightBrowserClient implements SupportedBrowserClient {
990
1032
  }
991
1033
 
992
1034
  async newPage(): Promise<BrowserPageContract> {
1035
+ if (!this.options.cdpUrl)
1036
+ observeBrowserTelemetry(this.options.telemetry, () =>
1037
+ this.options.telemetry?.recordPoolAcquire("not_configured"),
1038
+ );
993
1039
  const browser = await this.ensureBrowser();
994
1040
  const page = await browser.newPage();
995
1041
 
996
- return new PlaywrightBrowserPage(page, this.parsedProxy);
1042
+ return new PlaywrightBrowserPage(page, this.parsedProxy, this.options.telemetry);
997
1043
  }
998
1044
 
999
1045
  async rawPage(): Promise<BrowserPageContract> {
@@ -1004,10 +1050,14 @@ class PlaywrightBrowserClient implements SupportedBrowserClient {
1004
1050
  }
1005
1051
 
1006
1052
  async withIsolatedContext<T>(handler: (page: BrowserPageContract) => Promise<T>): Promise<T> {
1053
+ if (!this.options.cdpUrl)
1054
+ observeBrowserTelemetry(this.options.telemetry, () =>
1055
+ this.options.telemetry?.recordPoolAcquire("not_configured"),
1056
+ );
1007
1057
  const browser = await this.ensureBrowser();
1008
1058
  const context = await browser.newContext({ serviceWorkers: this.options.serviceWorkers });
1009
1059
  const page = await context.newPage();
1010
- const browserPage = new PlaywrightBrowserPage(page, this.parsedProxy);
1060
+ const browserPage = new PlaywrightBrowserPage(page, this.parsedProxy, this.options.telemetry);
1011
1061
 
1012
1062
  try {
1013
1063
  return await handler(browserPage);
@@ -1061,6 +1111,7 @@ abstract class WebSocketCommandClient {
1061
1111
  >();
1062
1112
  private socket?: WebSocket;
1063
1113
  private socketPromise?: Promise<WebSocket>;
1114
+ private closed = false;
1064
1115
 
1065
1116
  constructor(endpoint: string) {
1066
1117
  this.endpoint = normalizeWebSocketEndpoint(endpoint);
@@ -1084,6 +1135,7 @@ abstract class WebSocketCommandClient {
1084
1135
  params: Record<string, unknown> = {},
1085
1136
  ): Promise<Record<string, unknown>> {
1086
1137
  const socket = await this.getSocket();
1138
+ if (this.closed) throw new Error(`WebSocket closed: ${this.endpoint}`);
1087
1139
  const id = this.nextId++;
1088
1140
 
1089
1141
  return await new Promise<Record<string, unknown>>((resolve, reject) => {
@@ -1094,6 +1146,7 @@ abstract class WebSocketCommandClient {
1094
1146
  }
1095
1147
 
1096
1148
  async close(): Promise<void> {
1149
+ this.closed = true;
1097
1150
  for (const pending of this.pending.values()) {
1098
1151
  pending.reject(new Error(`WebSocket closed: ${this.endpoint}`));
1099
1152
  }
@@ -1112,6 +1165,7 @@ abstract class WebSocketCommandClient {
1112
1165
  ): CdpCommandFrame | JsonRpcCommandFrame;
1113
1166
 
1114
1167
  private async getSocket(): Promise<WebSocket> {
1168
+ if (this.closed) throw new Error(`WebSocket closed: ${this.endpoint}`);
1115
1169
  if (this.socket?.readyState === WebSocket.OPEN) {
1116
1170
  return this.socket;
1117
1171
  }
@@ -1124,6 +1178,11 @@ abstract class WebSocketCommandClient {
1124
1178
  const socket = new WebSocket(this.endpoint);
1125
1179
 
1126
1180
  socket.addEventListener("open", () => {
1181
+ if (this.closed) {
1182
+ socket.close();
1183
+ reject(new Error(`WebSocket closed: ${this.endpoint}`));
1184
+ return;
1185
+ }
1127
1186
  this.socket = socket;
1128
1187
  resolve(socket);
1129
1188
  });
@@ -1318,6 +1377,36 @@ function parsePoolAcquireResponse(value: unknown): PoolAcquireResponse {
1318
1377
  };
1319
1378
  }
1320
1379
 
1380
+ function poolAcquireOutcome(error: unknown): BrowserTelemetryPoolAcquireOutcome {
1381
+ const code =
1382
+ error instanceof Error && typeof (error as Error & { code?: unknown }).code === "number"
1383
+ ? (error as Error & { code: number }).code
1384
+ : undefined;
1385
+ switch (code) {
1386
+ case -32001:
1387
+ return "queue_full";
1388
+ case -32002:
1389
+ return "timed_out";
1390
+ case -32003:
1391
+ return "shutting_down";
1392
+ case -32004:
1393
+ return "unknown_lease";
1394
+ case -32005:
1395
+ return "unknown_method";
1396
+ case -32006:
1397
+ return "missing_allowed_hosts";
1398
+ }
1399
+ if (error instanceof Error && /Unable to connect|WebSocket closed/.test(error.message))
1400
+ return "transport_failure";
1401
+ return "other";
1402
+ }
1403
+
1404
+ function poolAcquireCode(error: unknown): number | undefined {
1405
+ return error instanceof Error && typeof (error as Error & { code?: unknown }).code === "number"
1406
+ ? (error as Error & { code: number }).code
1407
+ : undefined;
1408
+ }
1409
+
1321
1410
  function parseCdpFrameTreeNode(value: unknown): CdpFrameTreeNode | undefined {
1322
1411
  if (!isRecord(value) || !isRecord(value.frame)) {
1323
1412
  return undefined;
@@ -1366,9 +1455,7 @@ function getCdpDestroyedExecutionContextId(params: unknown): number | undefined
1366
1455
  return undefined;
1367
1456
  }
1368
1457
 
1369
- return typeof params.executionContextId === "number"
1370
- ? params.executionContextId
1371
- : undefined;
1458
+ return typeof params.executionContextId === "number" ? params.executionContextId : undefined;
1372
1459
  }
1373
1460
 
1374
1461
  function isMissingExecutionContextError(error: unknown): boolean {
@@ -1481,7 +1568,7 @@ class CdpBrowserFrame implements BrowserFrame {
1481
1568
  }
1482
1569
 
1483
1570
  class CdpPoolBrowserPage implements BrowserPageContract {
1484
- private closed = false;
1571
+ private closePromise?: Promise<void>;
1485
1572
  private initialized = false;
1486
1573
  private readonly frameExecutionContexts = new Map<string, number>();
1487
1574
 
@@ -1518,11 +1605,7 @@ class CdpPoolBrowserPage implements BrowserPageContract {
1518
1605
  if (typeof navigation.errorText === "string" && navigation.errorText.length > 0) {
1519
1606
  throw new Error(`Page.navigate failed: ${navigation.errorText} at ${url}`);
1520
1607
  }
1521
- await this.waitForDocumentReady(
1522
- startedAt + timeout,
1523
- () => expectedEventSeen,
1524
- waitUntil,
1525
- );
1608
+ await this.waitForDocumentReady(startedAt + timeout, () => expectedEventSeen, waitUntil);
1526
1609
  } finally {
1527
1610
  unsubscribe();
1528
1611
  }
@@ -1699,24 +1782,31 @@ class CdpPoolBrowserPage implements BrowserPageContract {
1699
1782
  async cookies(): Promise<readonly BrowserCookie[]> {
1700
1783
  await this.initialize();
1701
1784
  const result = await this.pageClient.send("Network.getCookies");
1702
- return parseCdpCookies(result.cookies);
1785
+ const cookies = parseCdpCookies(result.cookies);
1786
+ for (const cookie of cookies) registerDiagnosticValue(cookie.value);
1787
+ return cookies;
1703
1788
  }
1704
1789
 
1705
1790
  async close(): Promise<void> {
1706
- if (this.closed) {
1791
+ // Only the first caller observes the release outcome; repeat calls stay
1792
+ // idempotent (as they were before the promise was memoized) instead of
1793
+ // replaying a cached rejection, and never re-issue a release against a
1794
+ // target the pool has already disposed.
1795
+ if (this.closePromise) {
1796
+ await this.closePromise.catch(() => undefined);
1707
1797
  return;
1708
1798
  }
1709
-
1710
- this.closed = true;
1711
-
1712
- try {
1713
- await this.release({
1714
- ...(this.browserContextId ? { browserContextId: this.browserContextId } : {}),
1715
- pageId: this.pageId,
1716
- });
1717
- } finally {
1718
- await this.pageClient.close();
1719
- }
1799
+ this.closePromise = (async () => {
1800
+ try {
1801
+ await this.release({
1802
+ ...(this.browserContextId ? { browserContextId: this.browserContextId } : {}),
1803
+ pageId: this.pageId,
1804
+ });
1805
+ } finally {
1806
+ await this.pageClient.close();
1807
+ }
1808
+ })();
1809
+ await this.closePromise;
1720
1810
  }
1721
1811
 
1722
1812
  async withResourcePolicy<T>(policy: BrowserResourcePolicy, run: () => Promise<T>): Promise<T> {
@@ -1746,8 +1836,19 @@ class CdpPoolBrowserPage implements BrowserPageContract {
1746
1836
  try {
1747
1837
  return await run();
1748
1838
  } finally {
1749
- unsubscribe();
1750
- await this.pageClient.send("Fetch.disable");
1839
+ try {
1840
+ if (this.closePromise) {
1841
+ // Keep interception active until release settles. The release failure
1842
+ // stays observable through close() itself; swallowing it here keeps
1843
+ // this finally from replacing run()'s own outcome, and still avoids a
1844
+ // reconnect or a disable against a disposed target.
1845
+ await this.closePromise.catch(() => undefined);
1846
+ } else {
1847
+ await this.pageClient.send("Fetch.disable");
1848
+ }
1849
+ } finally {
1850
+ unsubscribe();
1851
+ }
1751
1852
  }
1752
1853
  }
1753
1854
 
@@ -1859,8 +1960,31 @@ class CdpPoolBrowserPage implements BrowserPageContract {
1859
1960
  this.pageClient.on("Runtime.executionContextsCleared", () => {
1860
1961
  this.frameExecutionContexts.clear();
1861
1962
  });
1963
+ const registerResponseCookies = (params: unknown) => {
1964
+ if (!isRecord(params)) return;
1965
+ const headers = Array.isArray(params.responseHeaders)
1966
+ ? params.responseHeaders
1967
+ : isRecord(params.headers)
1968
+ ? Object.entries(params.headers).map(([name, value]) => ({ name, value }))
1969
+ : [];
1970
+ for (const header of headers) {
1971
+ if (
1972
+ isRecord(header) &&
1973
+ typeof header.name === "string" &&
1974
+ header.name.toLowerCase() === "set-cookie" &&
1975
+ typeof header.value === "string"
1976
+ ) {
1977
+ registerDiagnosticValue(header.value);
1978
+ for (const value of cookieValuesFromHeaders(header.value)) registerDiagnosticValue(value);
1979
+ }
1980
+ }
1981
+ };
1982
+ const scopedCookieRegistration = AsyncResource.bind(registerResponseCookies);
1983
+ this.pageClient.on("Network.responseReceived", scopedCookieRegistration);
1984
+ this.pageClient.on("Network.responseReceivedExtraInfo", scopedCookieRegistration);
1862
1985
  await this.pageClient.send("Page.enable");
1863
1986
  await this.pageClient.send("Runtime.enable");
1987
+ await this.pageClient.send("Network.enable").catch(() => undefined);
1864
1988
  this.initialized = true;
1865
1989
  }
1866
1990
 
@@ -1910,6 +2034,7 @@ class CdpPoolBrowserPage implements BrowserPageContract {
1910
2034
  class CdpPoolBrowserClient implements SupportedBrowserClient {
1911
2035
  private readonly allowedHosts: string[];
1912
2036
  private readonly poolClient: JsonRpcWebSocketClient;
2037
+ private readonly telemetry?: BrowserTelemetrySink;
1913
2038
  readonly engine = "playwright-stealth" satisfies BrowserEngine;
1914
2039
 
1915
2040
  constructor(options: BrowserClientOptions) {
@@ -1920,6 +2045,7 @@ class CdpPoolBrowserClient implements SupportedBrowserClient {
1920
2045
  }
1921
2046
 
1922
2047
  this.allowedHosts = [...new Set(options.allowedHosts ?? [])];
2048
+ this.telemetry = options.telemetry;
1923
2049
  this.poolClient = new JsonRpcWebSocketClient(options.cdpUrl);
1924
2050
  }
1925
2051
 
@@ -1928,12 +2054,29 @@ class CdpPoolBrowserClient implements SupportedBrowserClient {
1928
2054
  }
1929
2055
 
1930
2056
  private async acquirePage(options?: { isolatedContext?: boolean }): Promise<BrowserPageContract> {
1931
- const acquireResult = parsePoolAcquireResponse(
1932
- await this.poolClient.send("acquire", {
1933
- ...(this.allowedHosts.length > 0 ? { allowedHosts: this.allowedHosts } : {}),
1934
- ...(options?.isolatedContext ? { isolationMode: "browserContext" } : {}),
1935
- }),
1936
- );
2057
+ let acquireResult: PoolAcquireResponse;
2058
+ try {
2059
+ acquireResult = parsePoolAcquireResponse(
2060
+ await this.poolClient.send("acquire", {
2061
+ ...(this.allowedHosts.length > 0 ? { allowedHosts: this.allowedHosts } : {}),
2062
+ ...(options?.isolatedContext ? { isolationMode: "browserContext" } : {}),
2063
+ }),
2064
+ );
2065
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordPoolAcquire("ok"));
2066
+ } catch (error) {
2067
+ observeBrowserTelemetry(this.telemetry, () =>
2068
+ this.telemetry?.recordPoolAcquire(poolAcquireOutcome(error)),
2069
+ );
2070
+ observeBrowserTelemetry(this.telemetry, () =>
2071
+ this.telemetry?.recordError("BROWSER_CDP_POOL_ERROR"),
2072
+ );
2073
+ const code = poolAcquireCode(error);
2074
+ if (code !== undefined && ![-32001, -32002, -32003, -32004, -32005, -32006].includes(code))
2075
+ observeBrowserTelemetry(this.telemetry, () =>
2076
+ this.telemetry?.recordPoolAcquireUnknownCode?.(code),
2077
+ );
2078
+ throw error;
2079
+ }
1937
2080
  const pageClient = new CdpWebSocketClient(acquireResult.wsEndpoint);
1938
2081
  const page = new CdpPoolBrowserPage(
1939
2082
  acquireResult.pageId,
@@ -2025,6 +2168,7 @@ export class BrowserClient implements BrowserClientContract {
2025
2168
  private activePage?: BrowserPageContract;
2026
2169
  private readonly activePages = new Set<BrowserPageContract>();
2027
2170
  private readonly _engine: BrowserEngine;
2171
+ private readonly telemetry?: BrowserTelemetrySink;
2028
2172
 
2029
2173
  constructor(options: BrowserClientOptions = {}) {
2030
2174
  const resolvedOptions = {
@@ -2033,6 +2177,7 @@ export class BrowserClient implements BrowserClientContract {
2033
2177
  };
2034
2178
  const engine = resolvedOptions.engine ?? "playwright-stealth";
2035
2179
  this._engine = engine;
2180
+ this.telemetry = resolvedOptions.telemetry;
2036
2181
  this.cdpUrl = resolvedOptions.cdpUrl;
2037
2182
 
2038
2183
  if (resolvedOptions.requireCdpPool && !resolvedOptions.cdpUrl) {
@@ -2061,23 +2206,46 @@ export class BrowserClient implements BrowserClientContract {
2061
2206
  }
2062
2207
 
2063
2208
  async newPage(): Promise<BrowserPageContract> {
2064
- const page = await this.client.newPage();
2065
- return this.activatePage(page);
2209
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
2210
+ try {
2211
+ const page = await this.client.newPage();
2212
+ return this.activatePage(page);
2213
+ } catch (error) {
2214
+ observeBrowserTelemetry(this.telemetry, () =>
2215
+ this.telemetry?.recordError(browserTelemetryErrorCode(error)),
2216
+ );
2217
+ throw error;
2218
+ }
2066
2219
  }
2067
2220
 
2068
2221
  async rawPage(): Promise<BrowserPageContract> {
2222
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
2069
2223
  if (!this.cdpUrl) {
2224
+ observeBrowserTelemetry(this.telemetry, () =>
2225
+ this.telemetry?.recordPoolAcquire("not_configured"),
2226
+ );
2227
+ observeBrowserTelemetry(this.telemetry, () =>
2228
+ this.telemetry?.recordError("BROWSER_RUNTIME_UNSUPPORTED"),
2229
+ );
2070
2230
  throw new ProviderError("ctx.browser.rawPage() requires a CDP pool", {
2071
2231
  code: "BROWSER_RUNTIME_UNSUPPORTED",
2072
2232
  fix: "Set APIFUSE__CDP_POOL__URL. The SDK escape hatch is CDP pool-backed only and never launches local Chromium.",
2073
2233
  });
2074
2234
  }
2075
2235
 
2076
- const page = await this.client.rawPage();
2077
- return this.activatePage(page);
2236
+ try {
2237
+ const page = await this.client.rawPage();
2238
+ return this.activatePage(page);
2239
+ } catch (error) {
2240
+ observeBrowserTelemetry(this.telemetry, () =>
2241
+ this.telemetry?.recordError(browserTelemetryErrorCode(error)),
2242
+ );
2243
+ throw error;
2244
+ }
2078
2245
  }
2079
2246
 
2080
2247
  async withIsolatedContext<T>(handler: (page: BrowserPageContract) => Promise<T>): Promise<T> {
2248
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
2081
2249
  const previousActivePage = this.activePage;
2082
2250
  let trackedPage: BrowserPageContract | undefined;
2083
2251
 
@@ -2095,22 +2263,34 @@ export class BrowserClient implements BrowserClientContract {
2095
2263
  }
2096
2264
 
2097
2265
  private activatePage(page: BrowserPageContract): BrowserPageContract {
2098
- let closed = false;
2266
+ let closePromise: Promise<void> | undefined;
2099
2267
  const originalClose = page.close.bind(page);
2100
2268
  const trackedPage = new Proxy(page, {
2101
2269
  get: (target, property, receiver) => {
2102
2270
  if (property === "close") {
2103
2271
  return async () => {
2104
- if (closed) return;
2105
- closed = true;
2106
- try {
2107
- await originalClose();
2108
- } finally {
2109
- this.activePages.delete(trackedPage);
2110
- if (this.activePage === trackedPage) {
2111
- this.activePage = undefined;
2112
- }
2272
+ // Repeat calls resolve on the first outcome instead of replaying a
2273
+ // cached rejection, so a failed release cannot make close() — or
2274
+ // BrowserClient.close(), which closes every tracked page — throw
2275
+ // forever.
2276
+ if (closePromise) {
2277
+ await closePromise.catch(() => undefined);
2278
+ return;
2113
2279
  }
2280
+ closePromise = (async () => {
2281
+ // Bookkeeping runs even when the release fails: the page is gone
2282
+ // either way, so leaving it in activePages would leak it and let
2283
+ // solveChallenge reuse a disposed target.
2284
+ try {
2285
+ await originalClose();
2286
+ } finally {
2287
+ this.activePages.delete(trackedPage);
2288
+ if (this.activePage === trackedPage) {
2289
+ this.activePage = undefined;
2290
+ }
2291
+ }
2292
+ })();
2293
+ await closePromise;
2114
2294
  };
2115
2295
  }
2116
2296
  const value = Reflect.get(target, property, receiver);
@@ -2123,7 +2303,11 @@ export class BrowserClient implements BrowserClientContract {
2123
2303
  }
2124
2304
 
2125
2305
  async solveChallenge(request: BrowserChallengeRequest): Promise<BrowserChallengeResult> {
2306
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
2126
2307
  if (request.type !== "recaptcha") {
2308
+ observeBrowserTelemetry(this.telemetry, () =>
2309
+ this.telemetry?.recordError("BROWSER_RUNTIME_UNSUPPORTED"),
2310
+ );
2127
2311
  throw new ProviderError(`Unsupported browser challenge: ${request.type}`, {
2128
2312
  code: "BROWSER_RUNTIME_UNSUPPORTED",
2129
2313
  });