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

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 (243) hide show
  1. package/AUTHORING.md +268 -9
  2. package/CHANGELOG.md +4 -0
  3. package/README.md +1 -0
  4. package/bin/apifuse-check.ts +84 -2
  5. package/bin/apifuse-dev.ts +11 -31
  6. package/bin/apifuse-migrate-deployment.ts +200 -0
  7. package/bin/apifuse-pack-check.ts +11 -4
  8. package/bin/apifuse-pack-types.ts +187 -4
  9. package/bin/apifuse-record.ts +44 -32
  10. package/bin/apifuse-test.ts +3 -1
  11. package/dist/cli/commands.d.ts +1 -1
  12. package/dist/cli/commands.js +11 -0
  13. package/dist/cli/deployment-intent.d.ts +178 -0
  14. package/dist/cli/deployment-intent.js +277 -0
  15. package/dist/cli/migrate-deployment-intent.d.ts +132 -0
  16. package/dist/cli/migrate-deployment-intent.js +1552 -0
  17. package/dist/cli/migrate-provider-shape.d.ts +5 -1
  18. package/dist/cli/migrate-provider-shape.js +4 -2
  19. package/dist/cli/resolver-runtime.d.ts +21 -0
  20. package/dist/cli/resolver-runtime.js +31 -0
  21. package/dist/define.d.ts +3 -0
  22. package/dist/define.js +5 -0
  23. package/dist/dev.d.ts +3 -1
  24. package/dist/dev.js +6 -1
  25. package/dist/engine.d.ts +35 -1
  26. package/dist/engine.js +21 -2
  27. package/dist/error-resolution.d.ts +1 -0
  28. package/dist/error-resolution.js +54 -0
  29. package/dist/errors.d.ts +39 -1
  30. package/dist/errors.js +50 -0
  31. package/dist/i18n/error-messages.d.ts +86 -0
  32. package/dist/i18n/error-messages.js +192 -0
  33. package/dist/i18n/index.d.ts +1 -0
  34. package/dist/i18n/index.js +1 -0
  35. package/dist/i18n/keys.d.ts +12 -0
  36. package/dist/i18n/keys.js +76 -5
  37. package/dist/index.d.ts +7 -1
  38. package/dist/index.js +6 -0
  39. package/dist/lint.d.ts +10 -0
  40. package/dist/lint.js +412 -49
  41. package/dist/provider.d.ts +5 -5
  42. package/dist/provider.js +2 -2
  43. package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
  44. package/dist/runtime/browser-telemetry-binding.js +83 -0
  45. package/dist/runtime/browser-telemetry.d.ts +76 -0
  46. package/dist/runtime/browser-telemetry.js +165 -0
  47. package/dist/runtime/browser.d.ts +3 -0
  48. package/dist/runtime/browser.js +208 -47
  49. package/dist/runtime/cache-telemetry.d.ts +71 -0
  50. package/dist/runtime/cache-telemetry.js +177 -0
  51. package/dist/runtime/cache.js +67 -7
  52. package/dist/runtime/capability-response.d.ts +3 -0
  53. package/dist/runtime/capability-response.js +56 -0
  54. package/dist/runtime/capability-telemetry.d.ts +46 -0
  55. package/dist/runtime/capability-telemetry.js +68 -0
  56. package/dist/runtime/diagnostic-env.d.ts +3 -0
  57. package/dist/runtime/diagnostic-env.js +10 -1
  58. package/dist/runtime/engine-mode.d.ts +74 -0
  59. package/dist/runtime/engine-mode.js +152 -0
  60. package/dist/runtime/http-telemetry-guard.d.ts +10 -0
  61. package/dist/runtime/http-telemetry-guard.js +62 -10
  62. package/dist/runtime/http.js +141 -35
  63. package/dist/runtime/instrumentation.js +92 -13
  64. package/dist/runtime/ocr-telemetry.d.ts +82 -0
  65. package/dist/runtime/ocr-telemetry.js +192 -0
  66. package/dist/runtime/ocr.js +30 -8
  67. package/dist/runtime/request-telemetry.d.ts +2 -0
  68. package/dist/runtime/request-telemetry.js +2 -1
  69. package/dist/runtime/resolver-runtime-options.d.ts +14 -0
  70. package/dist/runtime/resolver-runtime-options.js +33 -0
  71. package/dist/runtime/resolver-shared.d.ts +3 -2
  72. package/dist/runtime/resolver-shared.js +4 -4
  73. package/dist/runtime/resolver-telemetry.d.ts +1 -0
  74. package/dist/runtime/resolver-telemetry.js +1 -1
  75. package/dist/runtime/resolver-vendors/browser.d.ts +2 -0
  76. package/dist/runtime/resolver-vendors/browser.js +46 -0
  77. package/dist/runtime/resolver-vendors/hosts.d.ts +21 -0
  78. package/dist/runtime/resolver-vendors/hosts.js +35 -0
  79. package/dist/runtime/resolver-vendors/hypersolutions.js +2 -2
  80. package/dist/runtime/resolver-vendors/types.d.ts +5 -4
  81. package/dist/runtime/resolver.d.ts +4 -1
  82. package/dist/runtime/resolver.js +46 -13
  83. package/dist/runtime/state-telemetry.d.ts +79 -0
  84. package/dist/runtime/state-telemetry.js +194 -0
  85. package/dist/runtime/state.d.ts +3 -0
  86. package/dist/runtime/state.js +26 -7
  87. package/dist/runtime/stealth-cookies.d.ts +5 -1
  88. package/dist/runtime/stealth-cookies.js +21 -1
  89. package/dist/runtime/stealth-owned-headers.d.ts +26 -0
  90. package/dist/runtime/stealth-owned-headers.js +37 -0
  91. package/dist/runtime/stealth-telemetry.d.ts +92 -0
  92. package/dist/runtime/stealth-telemetry.js +179 -0
  93. package/dist/runtime/stealth.d.ts +4 -1
  94. package/dist/runtime/stealth.js +228 -85
  95. package/dist/runtime/stt-telemetry.d.ts +77 -0
  96. package/dist/runtime/stt-telemetry.js +176 -0
  97. package/dist/runtime/stt.js +15 -6
  98. package/dist/server/error-stack-frames.d.ts +35 -0
  99. package/dist/server/error-stack-frames.js +219 -0
  100. package/dist/server/index.d.ts +7 -0
  101. package/dist/server/index.js +4 -0
  102. package/dist/server/sdk-version.d.ts +3 -0
  103. package/dist/server/sdk-version.js +19 -0
  104. package/dist/server/self-test.js +1 -18
  105. package/dist/server/sensitive-values.d.ts +1 -1
  106. package/dist/server/sensitive-values.js +9 -0
  107. package/dist/server/serve-implementation.d.ts +51 -2
  108. package/dist/server/serve-implementation.js +337 -90
  109. package/dist/server/types.d.ts +1 -0
  110. package/dist/server/types.js +7 -0
  111. package/dist/stateful/stateful-provider-owner-forwarder.js +22 -0
  112. package/dist/stateful/stateful-provider-runtime-context.d.ts +3 -0
  113. package/dist/stateful/stateful-provider-runtime-context.js +1 -0
  114. package/dist/types.d.ts +94 -1
  115. package/package.json +6 -1
  116. package/src/cli/commands.ts +14 -0
  117. package/src/cli/deployment-intent.ts +364 -0
  118. package/src/cli/migrate-deployment-intent.ts +1908 -0
  119. package/src/cli/migrate-provider-shape.ts +10 -4
  120. package/src/cli/resolver-runtime.ts +72 -0
  121. package/src/define.ts +13 -0
  122. package/src/dev.ts +9 -2
  123. package/src/engine.ts +38 -2
  124. package/src/error-resolution.ts +58 -0
  125. package/src/errors.ts +77 -1
  126. package/src/i18n/error-messages.ts +255 -0
  127. package/src/i18n/index.ts +1 -0
  128. package/src/i18n/keys.ts +80 -6
  129. package/src/index.ts +72 -0
  130. package/src/lint.ts +522 -50
  131. package/src/provider.ts +10 -0
  132. package/src/runtime/browser-telemetry-binding.ts +92 -0
  133. package/src/runtime/browser-telemetry.ts +289 -0
  134. package/src/runtime/browser.ts +249 -65
  135. package/src/runtime/cache-telemetry.ts +247 -0
  136. package/src/runtime/cache.ts +68 -17
  137. package/src/runtime/capability-response.ts +53 -0
  138. package/src/runtime/capability-telemetry.ts +101 -0
  139. package/src/runtime/diagnostic-env.ts +11 -1
  140. package/src/runtime/engine-mode.ts +233 -0
  141. package/src/runtime/http-telemetry-guard.ts +66 -13
  142. package/src/runtime/http.ts +168 -37
  143. package/src/runtime/instrumentation.ts +105 -16
  144. package/src/runtime/ocr-telemetry.ts +308 -0
  145. package/src/runtime/ocr.ts +109 -74
  146. package/src/runtime/request-telemetry.ts +2 -1
  147. package/src/runtime/resolver-runtime-options.ts +50 -0
  148. package/src/runtime/resolver-shared.ts +9 -4
  149. package/src/runtime/resolver-telemetry.ts +1 -1
  150. package/src/runtime/resolver-vendors/browser.ts +65 -7
  151. package/src/runtime/resolver-vendors/hosts.ts +43 -0
  152. package/src/runtime/resolver-vendors/hypersolutions.ts +2 -2
  153. package/src/runtime/resolver-vendors/types.ts +5 -4
  154. package/src/runtime/resolver.ts +64 -21
  155. package/src/runtime/state-telemetry.ts +298 -0
  156. package/src/runtime/state.ts +46 -45
  157. package/src/runtime/stealth-cookies.ts +24 -1
  158. package/src/runtime/stealth-owned-headers.ts +41 -0
  159. package/src/runtime/stealth-telemetry.ts +304 -0
  160. package/src/runtime/stealth.ts +355 -150
  161. package/src/runtime/stt-telemetry.ts +282 -0
  162. package/src/runtime/stt.ts +93 -71
  163. package/src/server/error-stack-frames.ts +214 -0
  164. package/src/server/index.ts +53 -0
  165. package/src/server/sdk-version.ts +22 -0
  166. package/src/server/self-test.ts +1 -21
  167. package/src/server/sensitive-values.ts +14 -0
  168. package/src/server/serve-implementation.ts +569 -108
  169. package/src/server/types.ts +7 -0
  170. package/src/stateful/stateful-provider-owner-forwarder.ts +23 -0
  171. package/src/stateful/stateful-provider-runtime-context.ts +4 -0
  172. package/src/types.ts +99 -1
  173. package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +0 -6
  174. package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +0 -3
  175. package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +0 -10
  176. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +0 -3
  177. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +0 -3
  178. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +0 -2
  179. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +0 -3
  180. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +0 -6
  181. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +0 -1
  182. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +0 -1
  183. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +0 -3
  184. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +0 -7
  185. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +0 -19
  186. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +0 -6
  187. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +0 -3
  188. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +0 -8
  189. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +0 -5
  190. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +0 -4
  191. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +0 -3
  192. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +0 -3
  193. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +0 -10
  194. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +0 -1
  195. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +0 -1
  196. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-index.ts.txt +0 -3
  197. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-one.ts.txt +0 -6
  198. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-two.ts.txt +0 -6
  199. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-mixed-index.ts.txt +0 -14
  200. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-registry.ts.txt +0 -3
  201. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +0 -2
  202. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +0 -3
  203. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +0 -3
  204. package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +0 -8
  205. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-hoisted-array-spread.ts.txt +0 -15
  206. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +0 -9
  207. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-kebab-operation-id.ts.txt +0 -17
  208. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +0 -5
  209. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +0 -24
  210. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +0 -16
  211. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-snake-operation-id.ts.txt +0 -21
  212. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +0 -11
  213. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +0 -7
  214. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +0 -3
  215. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +0 -18
  216. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +0 -13
  217. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +0 -31
  218. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +0 -11
  219. package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +0 -11
  220. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +0 -11
  221. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +0 -21
  222. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +0 -11
  223. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +0 -14
  224. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-en.json +0 -1
  225. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ja.json +0 -1
  226. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ko.json +0 -1
  227. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-existing-operation-namespace-en.json +0 -8
  228. package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +0 -7
  229. package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +0 -6
  230. package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +0 -7
  231. package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +0 -6
  232. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-binding-ambiguous.ts.txt +0 -12
  233. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported-other.ts.txt +0 -7
  234. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported.ts.txt +0 -5
  235. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-key-ambiguous.ts.txt +0 -19
  236. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-shorthand.ts.txt +0 -9
  237. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-typed.ts.txt +0 -11
  238. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-unrelated.ts.txt +0 -13
  239. package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +0 -7
  240. package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +0 -7
  241. package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +0 -15
  242. package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +0 -4
  243. package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +0 -12
@@ -1,4 +1,19 @@
1
- import { readDiagnosticEnv } from "./diagnostic-env.js";
1
+ import { readDiagnosticEnv, registerDiagnosticValue } from "./diagnostic-env.js";
2
+ import { AsyncResource } from "node:async_hooks";
3
+ import { observeTelemetryCallback as observeBrowserTelemetry } from "./http-telemetry-guard.js";
4
+ import { cookieValuesFromHeaders } from "./stealth-cookies.js";
5
+ function browserTelemetryErrorCode(error) {
6
+ const code = error && typeof error === "object" && "code" in error
7
+ ? error.code
8
+ : undefined;
9
+ if (code === "BROWSER_CDP_POOL_REQUIRED" ||
10
+ code === "BROWSER_PROXY_INVALID" ||
11
+ code === "BROWSER_RUNTIME_UNSUPPORTED")
12
+ return code;
13
+ if (typeof code === "number")
14
+ return "BROWSER_CDP_POOL_ERROR";
15
+ return "other";
16
+ }
2
17
  import { createRequire } from "node:module";
3
18
  import { ProviderError } from "../errors.js";
4
19
  const require = createRequire(import.meta.url);
@@ -241,11 +256,9 @@ function getCdpAuthRequiredRequestId(params) {
241
256
  return params.requestId;
242
257
  }
243
258
  function isCdpProxyAuthChallenge(params) {
244
- return (isRecord(params) &&
245
- isRecord(params.authChallenge) &&
246
- params.authChallenge.source === "Proxy");
259
+ return (isRecord(params) && isRecord(params.authChallenge) && params.authChallenge.source === "Proxy");
247
260
  }
248
- async function handleCdpAuthRequired(session, proxy, authAttempts, params) {
261
+ async function handleCdpAuthRequired(session, proxy, authAttempts, params, telemetry) {
249
262
  const requestId = getCdpAuthRequiredRequestId(params);
250
263
  if (!requestId)
251
264
  return;
@@ -254,6 +267,7 @@ async function handleCdpAuthRequired(session, proxy, authAttempts, params) {
254
267
  const response = isCdpProxyAuthChallenge(params) && !authAttempts.has(requestId)
255
268
  ? (() => {
256
269
  authAttempts.add(requestId);
270
+ observeBrowserTelemetry(telemetry, () => telemetry?.recordProxyAuthChallenge());
257
271
  return {
258
272
  authChallengeResponse: {
259
273
  password: proxy.password,
@@ -374,6 +388,8 @@ function toPlaywrightProxy(proxy) {
374
388
  fix: "Percent-encode the proxy username and password as URL userinfo.",
375
389
  });
376
390
  }
391
+ registerDiagnosticValue(username);
392
+ registerDiagnosticValue(password);
377
393
  return {
378
394
  server: `${parsed.protocol}//${parsed.host}`,
379
395
  username,
@@ -543,14 +559,24 @@ async function loadSeleniumBase() {
543
559
  class PlaywrightBrowserPage {
544
560
  page;
545
561
  proxy;
562
+ telemetry;
546
563
  id = "main";
547
564
  pageId;
548
- constructor(page, proxy = undefined) {
565
+ constructor(page, proxy = undefined, telemetry) {
549
566
  this.page = page;
550
567
  this.proxy = proxy;
568
+ this.telemetry = telemetry;
551
569
  }
552
570
  async goto(url, options) {
553
- await this.page.goto(url, options);
571
+ const response = await this.page.goto(url, options);
572
+ const headers = await response?.headersArray().catch(() => []);
573
+ for (const header of headers ?? []) {
574
+ if (header.name.toLowerCase() === "set-cookie") {
575
+ registerDiagnosticValue(header.value);
576
+ for (const value of cookieValuesFromHeaders(header.value))
577
+ registerDiagnosticValue(value);
578
+ }
579
+ }
554
580
  }
555
581
  async evaluate(fn) {
556
582
  if (typeof fn === "string") {
@@ -596,7 +622,10 @@ class PlaywrightBrowserPage {
596
622
  await this.page.close();
597
623
  }
598
624
  async cookies() {
599
- return (await this.page.context().cookies()).map(toBrowserCookie);
625
+ const cookies = (await this.page.context().cookies()).map(toBrowserCookie);
626
+ for (const cookie of cookies)
627
+ registerDiagnosticValue(cookie.value);
628
+ return cookies;
600
629
  }
601
630
  async withResourcePolicy(policy, run) {
602
631
  const allowedMethods = new Set(policy.allowedMethods ?? DEFAULT_RESOURCE_METHODS);
@@ -639,7 +668,7 @@ class PlaywrightBrowserPage {
639
668
  const onAuthRequired = (params) => {
640
669
  if (!proxy)
641
670
  return;
642
- void handleCdpAuthRequired(session, proxy, authAttempts, params);
671
+ void handleCdpAuthRequired(session, proxy, authAttempts, params, this.telemetry);
643
672
  };
644
673
  session.on("Fetch.requestPaused", onRequestPaused);
645
674
  if (proxy) {
@@ -713,9 +742,11 @@ class PlaywrightBrowserClient {
713
742
  return this.browser;
714
743
  }
715
744
  async newPage() {
745
+ if (!this.options.cdpUrl)
746
+ observeBrowserTelemetry(this.options.telemetry, () => this.options.telemetry?.recordPoolAcquire("not_configured"));
716
747
  const browser = await this.ensureBrowser();
717
748
  const page = await browser.newPage();
718
- return new PlaywrightBrowserPage(page, this.parsedProxy);
749
+ return new PlaywrightBrowserPage(page, this.parsedProxy, this.options.telemetry);
719
750
  }
720
751
  async rawPage() {
721
752
  throw new ProviderError("ctx.browser.rawPage() requires a CDP pool", {
@@ -724,10 +755,12 @@ class PlaywrightBrowserClient {
724
755
  });
725
756
  }
726
757
  async withIsolatedContext(handler) {
758
+ if (!this.options.cdpUrl)
759
+ observeBrowserTelemetry(this.options.telemetry, () => this.options.telemetry?.recordPoolAcquire("not_configured"));
727
760
  const browser = await this.ensureBrowser();
728
761
  const context = await browser.newContext({ serviceWorkers: this.options.serviceWorkers });
729
762
  const page = await context.newPage();
730
- const browserPage = new PlaywrightBrowserPage(page, this.parsedProxy);
763
+ const browserPage = new PlaywrightBrowserPage(page, this.parsedProxy, this.options.telemetry);
731
764
  try {
732
765
  return await handler(browserPage);
733
766
  }
@@ -771,6 +804,7 @@ class WebSocketCommandClient {
771
804
  pending = new Map();
772
805
  socket;
773
806
  socketPromise;
807
+ closed = false;
774
808
  constructor(endpoint) {
775
809
  this.endpoint = normalizeWebSocketEndpoint(endpoint);
776
810
  }
@@ -787,6 +821,8 @@ class WebSocketCommandClient {
787
821
  }
788
822
  async send(method, params = {}) {
789
823
  const socket = await this.getSocket();
824
+ if (this.closed)
825
+ throw new Error(`WebSocket closed: ${this.endpoint}`);
790
826
  const id = this.nextId++;
791
827
  return await new Promise((resolve, reject) => {
792
828
  this.pending.set(id, { resolve, reject });
@@ -794,6 +830,7 @@ class WebSocketCommandClient {
794
830
  });
795
831
  }
796
832
  async close() {
833
+ this.closed = true;
797
834
  for (const pending of this.pending.values()) {
798
835
  pending.reject(new Error(`WebSocket closed: ${this.endpoint}`));
799
836
  }
@@ -804,6 +841,8 @@ class WebSocketCommandClient {
804
841
  this.socketPromise = undefined;
805
842
  }
806
843
  async getSocket() {
844
+ if (this.closed)
845
+ throw new Error(`WebSocket closed: ${this.endpoint}`);
807
846
  if (this.socket?.readyState === WebSocket.OPEN) {
808
847
  return this.socket;
809
848
  }
@@ -813,6 +852,11 @@ class WebSocketCommandClient {
813
852
  this.socketPromise = new Promise((resolve, reject) => {
814
853
  const socket = new WebSocket(this.endpoint);
815
854
  socket.addEventListener("open", () => {
855
+ if (this.closed) {
856
+ socket.close();
857
+ reject(new Error(`WebSocket closed: ${this.endpoint}`));
858
+ return;
859
+ }
816
860
  this.socket = socket;
817
861
  resolve(socket);
818
862
  });
@@ -954,6 +998,33 @@ function parsePoolAcquireResponse(value) {
954
998
  wsEndpoint: value.wsEndpoint,
955
999
  };
956
1000
  }
1001
+ function poolAcquireOutcome(error) {
1002
+ const code = error instanceof Error && typeof error.code === "number"
1003
+ ? error.code
1004
+ : undefined;
1005
+ switch (code) {
1006
+ case -32001:
1007
+ return "queue_full";
1008
+ case -32002:
1009
+ return "timed_out";
1010
+ case -32003:
1011
+ return "shutting_down";
1012
+ case -32004:
1013
+ return "unknown_lease";
1014
+ case -32005:
1015
+ return "unknown_method";
1016
+ case -32006:
1017
+ return "missing_allowed_hosts";
1018
+ }
1019
+ if (error instanceof Error && /Unable to connect|WebSocket closed/.test(error.message))
1020
+ return "transport_failure";
1021
+ return "other";
1022
+ }
1023
+ function poolAcquireCode(error) {
1024
+ return error instanceof Error && typeof error.code === "number"
1025
+ ? error.code
1026
+ : undefined;
1027
+ }
957
1028
  function parseCdpFrameTreeNode(value) {
958
1029
  if (!isRecord(value) || !isRecord(value.frame)) {
959
1030
  return undefined;
@@ -992,9 +1063,7 @@ function getCdpDestroyedExecutionContextId(params) {
992
1063
  if (!isRecord(params)) {
993
1064
  return undefined;
994
1065
  }
995
- return typeof params.executionContextId === "number"
996
- ? params.executionContextId
997
- : undefined;
1066
+ return typeof params.executionContextId === "number" ? params.executionContextId : undefined;
998
1067
  }
999
1068
  function isMissingExecutionContextError(error) {
1000
1069
  return error instanceof Error && /\b(?:cannot|failed to) find context\b/i.test(error.message);
@@ -1090,7 +1159,7 @@ class CdpPoolBrowserPage {
1090
1159
  browserContextId;
1091
1160
  pageClient;
1092
1161
  release;
1093
- closed = false;
1162
+ closePromise;
1094
1163
  initialized = false;
1095
1164
  frameExecutionContexts = new Map();
1096
1165
  constructor(pageId, browserContextId, pageClient, release) {
@@ -1248,22 +1317,32 @@ class CdpPoolBrowserPage {
1248
1317
  async cookies() {
1249
1318
  await this.initialize();
1250
1319
  const result = await this.pageClient.send("Network.getCookies");
1251
- return parseCdpCookies(result.cookies);
1320
+ const cookies = parseCdpCookies(result.cookies);
1321
+ for (const cookie of cookies)
1322
+ registerDiagnosticValue(cookie.value);
1323
+ return cookies;
1252
1324
  }
1253
1325
  async close() {
1254
- if (this.closed) {
1326
+ // Only the first caller observes the release outcome; repeat calls stay
1327
+ // idempotent (as they were before the promise was memoized) instead of
1328
+ // replaying a cached rejection, and never re-issue a release against a
1329
+ // target the pool has already disposed.
1330
+ if (this.closePromise) {
1331
+ await this.closePromise.catch(() => undefined);
1255
1332
  return;
1256
1333
  }
1257
- this.closed = true;
1258
- try {
1259
- await this.release({
1260
- ...(this.browserContextId ? { browserContextId: this.browserContextId } : {}),
1261
- pageId: this.pageId,
1262
- });
1263
- }
1264
- finally {
1265
- await this.pageClient.close();
1266
- }
1334
+ this.closePromise = (async () => {
1335
+ try {
1336
+ await this.release({
1337
+ ...(this.browserContextId ? { browserContextId: this.browserContextId } : {}),
1338
+ pageId: this.pageId,
1339
+ });
1340
+ }
1341
+ finally {
1342
+ await this.pageClient.close();
1343
+ }
1344
+ })();
1345
+ await this.closePromise;
1267
1346
  }
1268
1347
  async withResourcePolicy(policy, run) {
1269
1348
  const allowedMethods = new Set(policy.allowedMethods ?? DEFAULT_RESOURCE_METHODS);
@@ -1288,8 +1367,21 @@ class CdpPoolBrowserPage {
1288
1367
  return await run();
1289
1368
  }
1290
1369
  finally {
1291
- unsubscribe();
1292
- await this.pageClient.send("Fetch.disable");
1370
+ try {
1371
+ if (this.closePromise) {
1372
+ // Keep interception active until release settles. The release failure
1373
+ // stays observable through close() itself; swallowing it here keeps
1374
+ // this finally from replacing run()'s own outcome, and still avoids a
1375
+ // reconnect or a disable against a disposed target.
1376
+ await this.closePromise.catch(() => undefined);
1377
+ }
1378
+ else {
1379
+ await this.pageClient.send("Fetch.disable");
1380
+ }
1381
+ }
1382
+ finally {
1383
+ unsubscribe();
1384
+ }
1293
1385
  }
1294
1386
  }
1295
1387
  async handleResourcePolicyPausedRequest(params, policy, allowedMethods) {
@@ -1383,8 +1475,31 @@ class CdpPoolBrowserPage {
1383
1475
  this.pageClient.on("Runtime.executionContextsCleared", () => {
1384
1476
  this.frameExecutionContexts.clear();
1385
1477
  });
1478
+ const registerResponseCookies = (params) => {
1479
+ if (!isRecord(params))
1480
+ return;
1481
+ const headers = Array.isArray(params.responseHeaders)
1482
+ ? params.responseHeaders
1483
+ : isRecord(params.headers)
1484
+ ? Object.entries(params.headers).map(([name, value]) => ({ name, value }))
1485
+ : [];
1486
+ for (const header of headers) {
1487
+ if (isRecord(header) &&
1488
+ typeof header.name === "string" &&
1489
+ header.name.toLowerCase() === "set-cookie" &&
1490
+ typeof header.value === "string") {
1491
+ registerDiagnosticValue(header.value);
1492
+ for (const value of cookieValuesFromHeaders(header.value))
1493
+ registerDiagnosticValue(value);
1494
+ }
1495
+ }
1496
+ };
1497
+ const scopedCookieRegistration = AsyncResource.bind(registerResponseCookies);
1498
+ this.pageClient.on("Network.responseReceived", scopedCookieRegistration);
1499
+ this.pageClient.on("Network.responseReceivedExtraInfo", scopedCookieRegistration);
1386
1500
  await this.pageClient.send("Page.enable");
1387
1501
  await this.pageClient.send("Runtime.enable");
1502
+ await this.pageClient.send("Network.enable").catch(() => undefined);
1388
1503
  this.initialized = true;
1389
1504
  }
1390
1505
  async getFrameExecutionContextId(frameId) {
@@ -1422,6 +1537,7 @@ class CdpPoolBrowserPage {
1422
1537
  class CdpPoolBrowserClient {
1423
1538
  allowedHosts;
1424
1539
  poolClient;
1540
+ telemetry;
1425
1541
  engine = "playwright-stealth";
1426
1542
  constructor(options) {
1427
1543
  if (!options.cdpUrl) {
@@ -1430,16 +1546,29 @@ class CdpPoolBrowserClient {
1430
1546
  });
1431
1547
  }
1432
1548
  this.allowedHosts = [...new Set(options.allowedHosts ?? [])];
1549
+ this.telemetry = options.telemetry;
1433
1550
  this.poolClient = new JsonRpcWebSocketClient(options.cdpUrl);
1434
1551
  }
1435
1552
  async newPage() {
1436
1553
  return await this.acquirePage({ isolatedContext: true });
1437
1554
  }
1438
1555
  async acquirePage(options) {
1439
- const acquireResult = parsePoolAcquireResponse(await this.poolClient.send("acquire", {
1440
- ...(this.allowedHosts.length > 0 ? { allowedHosts: this.allowedHosts } : {}),
1441
- ...(options?.isolatedContext ? { isolationMode: "browserContext" } : {}),
1442
- }));
1556
+ let acquireResult;
1557
+ try {
1558
+ acquireResult = parsePoolAcquireResponse(await this.poolClient.send("acquire", {
1559
+ ...(this.allowedHosts.length > 0 ? { allowedHosts: this.allowedHosts } : {}),
1560
+ ...(options?.isolatedContext ? { isolationMode: "browserContext" } : {}),
1561
+ }));
1562
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordPoolAcquire("ok"));
1563
+ }
1564
+ catch (error) {
1565
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordPoolAcquire(poolAcquireOutcome(error)));
1566
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordError("BROWSER_CDP_POOL_ERROR"));
1567
+ const code = poolAcquireCode(error);
1568
+ if (code !== undefined && ![-32001, -32002, -32003, -32004, -32005, -32006].includes(code))
1569
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordPoolAcquireUnknownCode?.(code));
1570
+ throw error;
1571
+ }
1443
1572
  const pageClient = new CdpWebSocketClient(acquireResult.wsEndpoint);
1444
1573
  const page = new CdpPoolBrowserPage(acquireResult.pageId, acquireResult.browserContextId, pageClient, async (request) => {
1445
1574
  await this.poolClient.send("release", request);
@@ -1512,6 +1641,7 @@ export class BrowserClient {
1512
1641
  activePage;
1513
1642
  activePages = new Set();
1514
1643
  _engine;
1644
+ telemetry;
1515
1645
  constructor(options = {}) {
1516
1646
  const resolvedOptions = {
1517
1647
  ...options,
@@ -1519,6 +1649,7 @@ export class BrowserClient {
1519
1649
  };
1520
1650
  const engine = resolvedOptions.engine ?? "playwright-stealth";
1521
1651
  this._engine = engine;
1652
+ this.telemetry = resolvedOptions.telemetry;
1522
1653
  this.cdpUrl = resolvedOptions.cdpUrl;
1523
1654
  if (resolvedOptions.requireCdpPool && !resolvedOptions.cdpUrl) {
1524
1655
  throw new ProviderError("Managed CDP Pool is required for browser providers in production", {
@@ -1543,20 +1674,37 @@ export class BrowserClient {
1543
1674
  return this._engine;
1544
1675
  }
1545
1676
  async newPage() {
1546
- const page = await this.client.newPage();
1547
- return this.activatePage(page);
1677
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
1678
+ try {
1679
+ const page = await this.client.newPage();
1680
+ return this.activatePage(page);
1681
+ }
1682
+ catch (error) {
1683
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordError(browserTelemetryErrorCode(error)));
1684
+ throw error;
1685
+ }
1548
1686
  }
1549
1687
  async rawPage() {
1688
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
1550
1689
  if (!this.cdpUrl) {
1690
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordPoolAcquire("not_configured"));
1691
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordError("BROWSER_RUNTIME_UNSUPPORTED"));
1551
1692
  throw new ProviderError("ctx.browser.rawPage() requires a CDP pool", {
1552
1693
  code: "BROWSER_RUNTIME_UNSUPPORTED",
1553
1694
  fix: "Set APIFUSE__CDP_POOL__URL. The SDK escape hatch is CDP pool-backed only and never launches local Chromium.",
1554
1695
  });
1555
1696
  }
1556
- const page = await this.client.rawPage();
1557
- return this.activatePage(page);
1697
+ try {
1698
+ const page = await this.client.rawPage();
1699
+ return this.activatePage(page);
1700
+ }
1701
+ catch (error) {
1702
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordError(browserTelemetryErrorCode(error)));
1703
+ throw error;
1704
+ }
1558
1705
  }
1559
1706
  async withIsolatedContext(handler) {
1707
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
1560
1708
  const previousActivePage = this.activePage;
1561
1709
  let trackedPage;
1562
1710
  return await this.client.withIsolatedContext(async (page) => {
@@ -1573,24 +1721,35 @@ export class BrowserClient {
1573
1721
  });
1574
1722
  }
1575
1723
  activatePage(page) {
1576
- let closed = false;
1724
+ let closePromise;
1577
1725
  const originalClose = page.close.bind(page);
1578
1726
  const trackedPage = new Proxy(page, {
1579
1727
  get: (target, property, receiver) => {
1580
1728
  if (property === "close") {
1581
1729
  return async () => {
1582
- if (closed)
1730
+ // Repeat calls resolve on the first outcome instead of replaying a
1731
+ // cached rejection, so a failed release cannot make close() — or
1732
+ // BrowserClient.close(), which closes every tracked page — throw
1733
+ // forever.
1734
+ if (closePromise) {
1735
+ await closePromise.catch(() => undefined);
1583
1736
  return;
1584
- closed = true;
1585
- try {
1586
- await originalClose();
1587
1737
  }
1588
- finally {
1589
- this.activePages.delete(trackedPage);
1590
- if (this.activePage === trackedPage) {
1591
- this.activePage = undefined;
1738
+ closePromise = (async () => {
1739
+ // Bookkeeping runs even when the release fails: the page is gone
1740
+ // either way, so leaving it in activePages would leak it and let
1741
+ // solveChallenge reuse a disposed target.
1742
+ try {
1743
+ await originalClose();
1592
1744
  }
1593
- }
1745
+ finally {
1746
+ this.activePages.delete(trackedPage);
1747
+ if (this.activePage === trackedPage) {
1748
+ this.activePage = undefined;
1749
+ }
1750
+ }
1751
+ })();
1752
+ await closePromise;
1594
1753
  };
1595
1754
  }
1596
1755
  const value = Reflect.get(target, property, receiver);
@@ -1602,7 +1761,9 @@ export class BrowserClient {
1602
1761
  return trackedPage;
1603
1762
  }
1604
1763
  async solveChallenge(request) {
1764
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
1605
1765
  if (request.type !== "recaptcha") {
1766
+ observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordError("BROWSER_RUNTIME_UNSUPPORTED"));
1606
1767
  throw new ProviderError(`Unsupported browser challenge: ${request.type}`, {
1607
1768
  code: "BROWSER_RUNTIME_UNSUPPORTED",
1608
1769
  });
@@ -0,0 +1,71 @@
1
+ import type { ProviderCacheLookupMeta } from "../types.js";
2
+ import { type ClosedEnum, type SpanIndex, type TelemetryContributor } from "./request-telemetry.js";
3
+ export type CacheTelemetrySource = "memory" | "redis" | "loader";
4
+ export type CacheTelemetrySample = {
5
+ key: string;
6
+ hit: boolean;
7
+ stale: boolean;
8
+ ageMs?: number;
9
+ source: CacheTelemetrySource;
10
+ };
11
+ export type CacheTelemetryLogPayload = {
12
+ telemetryFailed?: true;
13
+ lookups: number;
14
+ hits: number;
15
+ stale: number;
16
+ misses: number;
17
+ writes: number;
18
+ loaderErrors: number;
19
+ writeErrors: number;
20
+ source: {
21
+ memory: number;
22
+ redis: number;
23
+ loader: number;
24
+ };
25
+ redisFallbacks: number;
26
+ redisMode: ClosedEnum<"not_configured" | "configured" | "degraded">;
27
+ /** Successful Redis commands, including reads, writes, and deletes; memory-only work counts zero. */
28
+ redisRoundTrips: number;
29
+ ageMs?: {
30
+ p50: number;
31
+ max: number;
32
+ };
33
+ samples: CacheTelemetrySample[];
34
+ dropped: number;
35
+ };
36
+ export type CacheTelemetryHeaderPayload = Omit<CacheTelemetryLogPayload, "samples" | "ageMs" | "telemetryFailed"> & {
37
+ ageP50?: number;
38
+ ageMax?: number;
39
+ };
40
+ export interface CacheTelemetrySink {
41
+ markTelemetryFailed?(): void;
42
+ setRedisConfigured?(configured: boolean): void;
43
+ recordLookup(meta: ProviderCacheLookupMeta): void;
44
+ recordWrite(): void;
45
+ recordLoaderError?(): void;
46
+ recordWriteError?(): void;
47
+ recordRedisFallback(): void;
48
+ recordRedisRoundTrip?(): void;
49
+ }
50
+ /** Internal construction binding; keeps sink plumbing off ProviderCacheOptions. */
51
+ export declare function bindCacheTelemetry<T extends object>(options: T, sink: CacheTelemetrySink | undefined): T;
52
+ /** Internal lookup used only by the cache factory. */
53
+ export declare function cacheTelemetryFor(options: object): CacheTelemetrySink | undefined;
54
+ export declare function guardCacheTelemetry(sink: CacheTelemetrySink | undefined): CacheTelemetrySink;
55
+ export declare class CacheTelemetryCollector implements TelemetryContributor<CacheTelemetryLogPayload, CacheTelemetryHeaderPayload>, CacheTelemetrySink {
56
+ #private;
57
+ readonly key: "cache";
58
+ constructor(options?: {
59
+ redact?: (text: string) => string;
60
+ });
61
+ setRedisConfigured(configured: boolean): void;
62
+ markTelemetryFailed(): void;
63
+ recordLookup(meta: ProviderCacheLookupMeta): void;
64
+ recordWrite(): void;
65
+ recordLoaderError(): void;
66
+ recordWriteError(): void;
67
+ recordRedisFallback(): void;
68
+ recordRedisRoundTrip(): void;
69
+ toLogPayload(_spans: SpanIndex): CacheTelemetryLogPayload | undefined;
70
+ toHeaderPayload(log: CacheTelemetryLogPayload): CacheTelemetryHeaderPayload;
71
+ }