@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
package/src/provider.ts CHANGED
@@ -115,8 +115,13 @@ export {
115
115
  isProviderError,
116
116
  isSessionExpiredError,
117
117
  isTransportError,
118
+ ProviderEgressDeniedError,
119
+ ProviderEngineAuthenticationError,
120
+ ProviderEngineProtocolVersionError,
121
+ ProviderEngineUnavailableError,
118
122
  ProviderError,
119
123
  type ProviderErrorObservability,
124
+ SDKError,
120
125
  SessionExpiredError,
121
126
  TransportError,
122
127
  ValidationError,
@@ -134,6 +139,7 @@ export {
134
139
  isEngineOwnedRuntimeEnvName,
135
140
  isEngineOwnedTelemetryEnvName,
136
141
  PROVIDER_CAPABILITY_KEYS,
142
+ PROVIDER_ENGINE_MODE_ENV,
137
143
  PROVIDER_ENGINE_PROTOCOL_VERSION,
138
144
  readEngineProxyCredentials,
139
145
  } from "./engine.js";
@@ -143,6 +149,7 @@ export type {
143
149
  ProviderEngineAttachmentInput,
144
150
  ProviderEngineBindingCandidates,
145
151
  ProviderEngineCapabilitySurface,
152
+ ProviderEngineMode,
146
153
  ProviderEngineRequest,
147
154
  ProviderEngineResidentSurface,
148
155
  ProviderEngineSession,
@@ -150,6 +157,7 @@ export type {
150
157
  } from "./engine.js";
151
158
  export {
152
159
  getProviderLocalePath,
160
+ type ProviderErrorMessageParams,
153
161
  providerLocaleKey,
154
162
  qualifyProviderLocaleKey,
155
163
  } from "./i18n/index.js";
@@ -212,6 +220,8 @@ export type {
212
220
  HealthJourneyRunContext,
213
221
  HealthJourneyRunResult,
214
222
  HealthScheduleRandomization,
223
+ HttpAttemptContext,
224
+ HttpHeadersFactory,
215
225
  HttpRedirectFailureReason,
216
226
  HttpRedirectPolicy,
217
227
  HttpRedirectPolicyMode,
@@ -0,0 +1,92 @@
1
+ import type { BrowserClient } from "../types.js";
2
+ import type { BrowserTelemetrySink } from "./browser-telemetry.js";
3
+ import { observeTelemetryCallback } from "./http-telemetry-guard.js";
4
+
5
+ const methods = new Set(["newPage", "rawPage", "withIsolatedContext", "solveChallenge"]);
6
+ type State = { sink: BrowserTelemetrySink; adapter: BrowserClient };
7
+ const adapters = new WeakMap<object, State>();
8
+ let warned = false;
9
+
10
+ export function isSupportedBrowserBinding(value: unknown): value is BrowserClient {
11
+ if ((typeof value !== "object" && typeof value !== "function") || value === null) return false;
12
+ return [...methods].some((method) => {
13
+ try {
14
+ return typeof Reflect.get(value, method) === "function";
15
+ } catch {
16
+ return false;
17
+ }
18
+ });
19
+ }
20
+
21
+ export function warnUnsupportedBrowserBinding(): void {
22
+ if (warned) return;
23
+ warned = true;
24
+ try {
25
+ console.warn("[apifuse] browser telemetry not attached; reason=unsupported_binding_shape");
26
+ } catch {}
27
+ }
28
+
29
+ export function bindBrowserTelemetry(
30
+ client: BrowserClient,
31
+ sink: BrowserTelemetrySink,
32
+ ): BrowserClient {
33
+ const existing = adapters.get(client as object);
34
+ if (existing) {
35
+ existing.sink = sink;
36
+ return existing.adapter;
37
+ }
38
+ const wrappers = new Map<
39
+ PropertyKey,
40
+ { original: unknown; wrapped: (...args: unknown[]) => unknown }
41
+ >();
42
+ const adapter = new Proxy(Object.create(client) as BrowserClient, {
43
+ get(_target, property) {
44
+ const original = Reflect.get(client, property, client);
45
+ if (typeof property !== "string" || !methods.has(property) || typeof original !== "function")
46
+ return original;
47
+ const cached = wrappers.get(property);
48
+ if (cached && cached.original === original) return cached.wrapped;
49
+ const wrapped = function (this: unknown, ...args: unknown[]) {
50
+ const receiver = this === adapter ? client : this;
51
+ observeTelemetryCallback(state.sink, () => {
52
+ const declared = client.engine;
53
+ const engine =
54
+ declared === "playwright-stealth" ||
55
+ declared === "nodriver" ||
56
+ declared === "selenium-uc"
57
+ ? declared
58
+ : "host";
59
+ return state.sink.recordEngine(engine);
60
+ });
61
+ if (property === "newPage" || property === "rawPage" || property === "withIsolatedContext")
62
+ observeTelemetryCallback(state.sink, () =>
63
+ state.sink.recordPoolAcquire("not_configured"),
64
+ );
65
+ let result: unknown;
66
+ try {
67
+ result = Reflect.apply(original, receiver, args);
68
+ } catch (error) {
69
+ observeTelemetryCallback(state.sink, () => state.sink.recordError("other"));
70
+ throw error;
71
+ }
72
+ try {
73
+ if (result instanceof Promise)
74
+ void Promise.prototype.then.call(result, undefined, () => {
75
+ observeTelemetryCallback(state.sink, () => state.sink.recordError("other"));
76
+ });
77
+ } catch (error) {
78
+ observeTelemetryCallback(state.sink, () => {
79
+ throw error;
80
+ });
81
+ }
82
+ return result;
83
+ };
84
+ wrappers.set(property, { original, wrapped });
85
+ return wrapped;
86
+ },
87
+ });
88
+ const state: State = { sink, adapter };
89
+ adapters.set(client as object, state);
90
+ adapters.set(adapter as object, state);
91
+ return adapter;
92
+ }
@@ -0,0 +1,289 @@
1
+ import type { BrowserEngine } from "../types.js";
2
+ import { Buffer } from "node:buffer";
3
+ export type BrowserTelemetryEngine = BrowserEngine | "host";
4
+ import {
5
+ closedEnum,
6
+ type ClosedEnum,
7
+ type SpanIndex,
8
+ type TelemetryContributor,
9
+ } from "./request-telemetry.js";
10
+
11
+ export type BrowserTelemetryPoolAcquireOutcome =
12
+ | "ok"
13
+ | "not_configured"
14
+ | "queue_full"
15
+ | "timed_out"
16
+ | "shutting_down"
17
+ | "unknown_lease"
18
+ | "unknown_method"
19
+ | "missing_allowed_hosts"
20
+ | "transport_failure"
21
+ | "other";
22
+
23
+ export type BrowserTelemetryErrorCode =
24
+ | "BROWSER_CDP_POOL_REQUIRED"
25
+ | "BROWSER_PROXY_INVALID"
26
+ | "BROWSER_RUNTIME_UNSUPPORTED"
27
+ | "BROWSER_CDP_POOL_ERROR"
28
+ | "other";
29
+
30
+ export type BrowserTelemetrySampleName =
31
+ | "browser.newPage"
32
+ | "browser.page.goto"
33
+ | "browser.page.fill"
34
+ | "browser.page.click"
35
+ | "browser.page.type"
36
+ | "browser.page.waitForSelector"
37
+ | "browser.evaluate"
38
+ | "browser.content"
39
+ | "browser.screenshot"
40
+ | "other";
41
+
42
+ export type BrowserTelemetryLogPayload = {
43
+ allocateMs: number;
44
+ pages: number;
45
+ navigations: number;
46
+ actions: number;
47
+ evaluate: number;
48
+ content: number;
49
+ screenshot: number;
50
+ poolAcquireOutcome?: BrowserTelemetryPoolAcquireOutcome;
51
+ poolAcquireAttempts: number;
52
+ poolAcquireFailures: number;
53
+ poolAcquireUnknownCode?: number;
54
+ proxyAuthChallenges: number;
55
+ engine?: BrowserTelemetryEngine;
56
+ lastErrorCode?: BrowserTelemetryErrorCode;
57
+ samples: {
58
+ name: BrowserTelemetrySampleName;
59
+ ms: number;
60
+ status: "ok" | "error";
61
+ diagnostics?: string;
62
+ }[];
63
+ dropped: number;
64
+ telemetryFailed?: true;
65
+ };
66
+
67
+ export type BrowserTelemetryHeaderPayload = {
68
+ allocateMs: number;
69
+ pages: number;
70
+ navigations: number;
71
+ actions: number;
72
+ evaluate: number;
73
+ content: number;
74
+ screenshot: number;
75
+ poolAcquireOutcome?: ClosedEnum<BrowserTelemetryPoolAcquireOutcome>;
76
+ poolAcquireAttempts: number;
77
+ poolAcquireFailures: number;
78
+ proxyAuthChallenges: number;
79
+ engine?: ClosedEnum<BrowserTelemetryEngine>;
80
+ lastErrorCode?: ClosedEnum<BrowserTelemetryErrorCode>;
81
+ samples: {
82
+ name: ClosedEnum<BrowserTelemetrySampleName>;
83
+ ms: number;
84
+ status: ClosedEnum<"ok" | "error">;
85
+ }[];
86
+ dropped: number;
87
+ };
88
+
89
+ export interface BrowserTelemetrySink {
90
+ recordPoolAcquire(outcome: BrowserTelemetryPoolAcquireOutcome): void;
91
+ recordPoolAcquireUnknownCode?(code: number): void;
92
+ recordProxyAuthChallenge(): void;
93
+ recordEngine(engine: BrowserTelemetryEngine): void;
94
+ recordError(code: BrowserTelemetryErrorCode): void;
95
+ markTelemetryFailed?(): void;
96
+ }
97
+
98
+ const MAX_SAMPLES = 24;
99
+ const SAMPLE_NAMES = new Set<BrowserTelemetrySampleName>([
100
+ "browser.newPage",
101
+ "browser.page.goto",
102
+ "browser.page.fill",
103
+ "browser.page.click",
104
+ "browser.page.type",
105
+ "browser.page.waitForSelector",
106
+ "browser.evaluate",
107
+ "browser.content",
108
+ "browser.screenshot",
109
+ ]);
110
+ function sampleName(name: string): BrowserTelemetrySampleName {
111
+ if (name === "browser.page.evaluate") return "browser.evaluate";
112
+ if (name === "browser.page.content") return "browser.content";
113
+ if (name === "browser.page.screenshot") return "browser.screenshot";
114
+ return name === "[REDACTION_FAILED]" || !SAMPLE_NAMES.has(name as BrowserTelemetrySampleName)
115
+ ? "other"
116
+ : (name as BrowserTelemetrySampleName);
117
+ }
118
+
119
+ function boundedDiagnostics(text: string, redact?: (value: string) => string): string {
120
+ let result: string;
121
+ try {
122
+ result = stripBrowserHeaderMaterial(redact?.(text) ?? text);
123
+ } catch {
124
+ return "[REDACTION_FAILED]";
125
+ }
126
+ const length = Math.min(result.length, 300);
127
+ const units = Buffer.alloc(length * 2);
128
+ for (let i = 0; i < length; i += 1) units.writeUInt16LE(result.charCodeAt(i), i * 2);
129
+ return units.toString("utf16le");
130
+ }
131
+
132
+ function integer(value: number): number {
133
+ return Math.min(
134
+ Number.MAX_SAFE_INTEGER,
135
+ Math.max(0, Math.floor(Number.isFinite(value) ? value : 0)),
136
+ );
137
+ }
138
+
139
+ function add(a: number, b: number): number {
140
+ return Math.min(Number.MAX_SAFE_INTEGER, integer(a) + integer(b));
141
+ }
142
+
143
+ export class BrowserTelemetryCollector
144
+ implements
145
+ TelemetryContributor<BrowserTelemetryLogPayload, BrowserTelemetryHeaderPayload>,
146
+ BrowserTelemetrySink
147
+ {
148
+ readonly key = "browser" as const;
149
+ #poolAcquireOutcome: BrowserTelemetryPoolAcquireOutcome | undefined;
150
+ #poolAcquireAttempts = 0;
151
+ #poolAcquireFailures = 0;
152
+ #poolAcquireUnknownCode: number | undefined;
153
+ #telemetryFailed = false;
154
+ #proxyAuthChallenges = 0;
155
+ #engine: BrowserTelemetryEngine | undefined;
156
+ #lastErrorCode: BrowserTelemetryErrorCode | undefined;
157
+ readonly #redact: ((value: string) => string) | undefined;
158
+
159
+ constructor(options: { redact?: (value: string) => string } = {}) {
160
+ this.#redact = options.redact;
161
+ }
162
+
163
+ recordPoolAcquire(outcome: BrowserTelemetryPoolAcquireOutcome): void {
164
+ this.#poolAcquireOutcome = outcome;
165
+ this.#poolAcquireAttempts = add(this.#poolAcquireAttempts, 1);
166
+ if (outcome !== "ok") this.#poolAcquireFailures = add(this.#poolAcquireFailures, 1);
167
+ }
168
+ recordPoolAcquireUnknownCode(code: number): void {
169
+ this.#poolAcquireUnknownCode = code;
170
+ }
171
+ recordProxyAuthChallenge(): void {
172
+ this.#proxyAuthChallenges = add(this.#proxyAuthChallenges, 1);
173
+ }
174
+ recordEngine(engine: BrowserTelemetryEngine): void {
175
+ this.#engine = engine;
176
+ }
177
+ recordError(code: BrowserTelemetryErrorCode): void {
178
+ this.#lastErrorCode = code;
179
+ }
180
+ markTelemetryFailed(): void {
181
+ this.#telemetryFailed = true;
182
+ }
183
+
184
+ toLogPayload(spans: SpanIndex): BrowserTelemetryLogPayload | undefined {
185
+ const browserSpans = spans.spans.filter(
186
+ (span) =>
187
+ span.name === "browser.newPage" ||
188
+ span.name === "browser.rawPage" ||
189
+ span.name === "browser.withIsolatedContext" ||
190
+ span.name.startsWith("browser.page.") ||
191
+ /^browser\.(evaluate|content|screenshot)$/.test(span.name),
192
+ );
193
+ const failedSpan = spans.spans.some((span) => span.name === "[REDACTION_FAILED]");
194
+ if (
195
+ browserSpans.length === 0 &&
196
+ !failedSpan &&
197
+ !this.#poolAcquireOutcome &&
198
+ this.#proxyAuthChallenges === 0 &&
199
+ !this.#engine &&
200
+ !this.#lastErrorCode &&
201
+ !this.#telemetryFailed
202
+ )
203
+ return undefined;
204
+ const count = (name: string) => spans.count(name);
205
+ const sampleSpans = failedSpan
206
+ ? [...browserSpans, ...spans.spans.filter((span) => span.name === "[REDACTION_FAILED]")]
207
+ : browserSpans;
208
+ const samples: BrowserTelemetryLogPayload["samples"] = sampleSpans
209
+ .slice(0, MAX_SAMPLES)
210
+ .map((span) => ({
211
+ name: sampleName(span.name),
212
+ ms: integer(span.duration_ms),
213
+ status: (span.status === "error" ? "error" : "ok") as "ok" | "error",
214
+ ...(span.name === "[REDACTION_FAILED]"
215
+ ? { diagnostics: "[REDACTION_FAILED]" }
216
+ : span.error
217
+ ? { diagnostics: boundedDiagnostics(span.error, this.#redact) }
218
+ : {}),
219
+ }));
220
+ return {
221
+ allocateMs: integer(
222
+ browserSpans
223
+ .filter((span) => span.name === "browser.newPage")
224
+ .reduce((sum, span) => sum + Number(span.attributes.allocate_ms ?? span.duration_ms), 0),
225
+ ),
226
+ pages: add(
227
+ add(count("browser.newPage"), count("browser.rawPage")),
228
+ count("browser.withIsolatedContext"),
229
+ ),
230
+ navigations: integer(count("browser.page.goto")),
231
+ actions: ["fill", "click", "type", "waitForSelector"].reduce(
232
+ (sum, method) => add(sum, count(`browser.page.${method}`)),
233
+ 0,
234
+ ),
235
+ evaluate: add(count("browser.evaluate"), count("browser.page.evaluate")),
236
+ content: add(count("browser.page.content"), count("browser.content")),
237
+ screenshot: add(count("browser.page.screenshot"), count("browser.screenshot")),
238
+ ...(this.#poolAcquireOutcome ? { poolAcquireOutcome: this.#poolAcquireOutcome } : {}),
239
+ poolAcquireAttempts: this.#poolAcquireAttempts,
240
+ poolAcquireFailures: this.#poolAcquireFailures,
241
+ ...(this.#poolAcquireUnknownCode === undefined
242
+ ? {}
243
+ : { poolAcquireUnknownCode: this.#poolAcquireUnknownCode }),
244
+ proxyAuthChallenges: this.#proxyAuthChallenges,
245
+ ...(this.#engine ? { engine: this.#engine } : {}),
246
+ ...(this.#lastErrorCode ? { lastErrorCode: this.#lastErrorCode } : {}),
247
+ samples,
248
+ dropped: Math.max(0, sampleSpans.length - samples.length),
249
+ ...(this.#telemetryFailed ? { telemetryFailed: true as const } : {}),
250
+ };
251
+ }
252
+
253
+ toHeaderPayload(log: BrowserTelemetryLogPayload): BrowserTelemetryHeaderPayload {
254
+ return {
255
+ allocateMs: log.allocateMs,
256
+ pages: log.pages,
257
+ navigations: log.navigations,
258
+ actions: log.actions,
259
+ evaluate: log.evaluate,
260
+ content: log.content,
261
+ screenshot: log.screenshot,
262
+ ...(log.poolAcquireOutcome ? { poolAcquireOutcome: closedEnum(log.poolAcquireOutcome) } : {}),
263
+ poolAcquireAttempts: log.poolAcquireAttempts,
264
+ poolAcquireFailures: log.poolAcquireFailures,
265
+ proxyAuthChallenges: log.proxyAuthChallenges,
266
+ ...(log.engine ? { engine: closedEnum(log.engine) } : {}),
267
+ ...(log.lastErrorCode ? { lastErrorCode: closedEnum(log.lastErrorCode) } : {}),
268
+ samples: log.samples.map((sample) => ({
269
+ name: closedEnum(sample.name),
270
+ ms: sample.ms,
271
+ status: closedEnum(sample.status),
272
+ })),
273
+ dropped: log.dropped,
274
+ };
275
+ }
276
+ }
277
+
278
+ /** Defence in depth for diagnostics that accidentally contain protocol headers. */
279
+ export function stripBrowserHeaderMaterial(value: string): string {
280
+ return value
281
+ .replace(
282
+ /(^|[^\p{L}\p{N}_-])[\t ]*(?:cookie|set-cookie|authorization)[\t ]*(?::|=)[^\r\n]*/giu,
283
+ "$1[REDACTED]",
284
+ )
285
+ .replace(
286
+ /"(?:cookie|set-cookie|authorization)"[\t ]*:[\t ]*"(?:\\.|[^"\\])*"/giu,
287
+ "[REDACTED]",
288
+ );
289
+ }