@apifuse/provider-sdk 2.2.0-beta.61 → 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 (258) hide show
  1. package/AUTHORING.md +268 -9
  2. package/CHANGELOG.md +8 -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 +191 -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/handle-meta.d.ts +18 -2
  32. package/dist/handle-meta.js +30 -2
  33. package/dist/handle.d.ts +33 -12
  34. package/dist/handle.js +69 -24
  35. package/dist/i18n/error-messages.d.ts +86 -0
  36. package/dist/i18n/error-messages.js +192 -0
  37. package/dist/i18n/index.d.ts +1 -0
  38. package/dist/i18n/index.js +1 -0
  39. package/dist/i18n/keys.d.ts +12 -0
  40. package/dist/i18n/keys.js +76 -5
  41. package/dist/index.d.ts +10 -2
  42. package/dist/index.js +9 -1
  43. package/dist/lint.d.ts +10 -0
  44. package/dist/lint.js +440 -57
  45. package/dist/provider.d.ts +7 -5
  46. package/dist/provider.js +4 -2
  47. package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
  48. package/dist/runtime/browser-telemetry-binding.js +83 -0
  49. package/dist/runtime/browser-telemetry.d.ts +76 -0
  50. package/dist/runtime/browser-telemetry.js +165 -0
  51. package/dist/runtime/browser.d.ts +3 -0
  52. package/dist/runtime/browser.js +208 -47
  53. package/dist/runtime/cache-telemetry.d.ts +71 -0
  54. package/dist/runtime/cache-telemetry.js +177 -0
  55. package/dist/runtime/cache.js +67 -7
  56. package/dist/runtime/capability-response.d.ts +3 -0
  57. package/dist/runtime/capability-response.js +56 -0
  58. package/dist/runtime/capability-telemetry.d.ts +46 -0
  59. package/dist/runtime/capability-telemetry.js +68 -0
  60. package/dist/runtime/diagnostic-env.d.ts +3 -0
  61. package/dist/runtime/diagnostic-env.js +10 -1
  62. package/dist/runtime/engine-mode.d.ts +74 -0
  63. package/dist/runtime/engine-mode.js +152 -0
  64. package/dist/runtime/handle.js +15 -10
  65. package/dist/runtime/http-telemetry-binding.d.ts +10 -0
  66. package/dist/runtime/http-telemetry-binding.js +152 -0
  67. package/dist/runtime/http-telemetry-guard.d.ts +16 -0
  68. package/dist/runtime/http-telemetry-guard.js +202 -0
  69. package/dist/runtime/http-telemetry.d.ts +104 -0
  70. package/dist/runtime/http-telemetry.js +190 -0
  71. package/dist/runtime/http.d.ts +31 -0
  72. package/dist/runtime/http.js +356 -174
  73. package/dist/runtime/instrumentation.js +92 -13
  74. package/dist/runtime/ocr-telemetry.d.ts +82 -0
  75. package/dist/runtime/ocr-telemetry.js +192 -0
  76. package/dist/runtime/ocr.js +30 -8
  77. package/dist/runtime/request-telemetry.d.ts +2 -0
  78. package/dist/runtime/request-telemetry.js +2 -1
  79. package/dist/runtime/resolver-runtime-options.d.ts +14 -0
  80. package/dist/runtime/resolver-runtime-options.js +33 -0
  81. package/dist/runtime/resolver-shared.d.ts +3 -2
  82. package/dist/runtime/resolver-shared.js +4 -4
  83. package/dist/runtime/resolver-telemetry.d.ts +1 -0
  84. package/dist/runtime/resolver-telemetry.js +1 -1
  85. package/dist/runtime/resolver-vendors/browser.d.ts +2 -0
  86. package/dist/runtime/resolver-vendors/browser.js +46 -0
  87. package/dist/runtime/resolver-vendors/hosts.d.ts +21 -0
  88. package/dist/runtime/resolver-vendors/hosts.js +35 -0
  89. package/dist/runtime/resolver-vendors/hypersolutions.js +2 -2
  90. package/dist/runtime/resolver-vendors/types.d.ts +5 -4
  91. package/dist/runtime/resolver.d.ts +4 -1
  92. package/dist/runtime/resolver.js +46 -13
  93. package/dist/runtime/state-telemetry.d.ts +79 -0
  94. package/dist/runtime/state-telemetry.js +194 -0
  95. package/dist/runtime/state.d.ts +3 -0
  96. package/dist/runtime/state.js +26 -7
  97. package/dist/runtime/stealth-cookies.d.ts +5 -1
  98. package/dist/runtime/stealth-cookies.js +21 -1
  99. package/dist/runtime/stealth-owned-headers.d.ts +26 -0
  100. package/dist/runtime/stealth-owned-headers.js +37 -0
  101. package/dist/runtime/stealth-telemetry.d.ts +92 -0
  102. package/dist/runtime/stealth-telemetry.js +179 -0
  103. package/dist/runtime/stealth.d.ts +4 -1
  104. package/dist/runtime/stealth.js +228 -85
  105. package/dist/runtime/stt-telemetry.d.ts +77 -0
  106. package/dist/runtime/stt-telemetry.js +176 -0
  107. package/dist/runtime/stt.js +15 -6
  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 +9 -0
  111. package/dist/server/index.js +5 -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 +53 -2
  118. package/dist/server/serve-implementation.js +369 -123
  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 +94 -1
  125. package/package.json +6 -1
  126. package/src/cli/commands.ts +14 -0
  127. package/src/cli/deployment-intent.ts +364 -0
  128. package/src/cli/migrate-deployment-intent.ts +1908 -0
  129. package/src/cli/migrate-provider-shape.ts +10 -4
  130. package/src/cli/resolver-runtime.ts +72 -0
  131. package/src/define.ts +13 -0
  132. package/src/dev.ts +9 -2
  133. package/src/engine.ts +38 -2
  134. package/src/error-resolution.ts +58 -0
  135. package/src/errors.ts +77 -1
  136. package/src/handle-meta.ts +39 -4
  137. package/src/handle.ts +121 -38
  138. package/src/i18n/error-messages.ts +255 -0
  139. package/src/i18n/index.ts +1 -0
  140. package/src/i18n/keys.ts +80 -6
  141. package/src/index.ts +91 -0
  142. package/src/lint.ts +552 -57
  143. package/src/provider.ts +20 -0
  144. package/src/runtime/browser-telemetry-binding.ts +92 -0
  145. package/src/runtime/browser-telemetry.ts +289 -0
  146. package/src/runtime/browser.ts +249 -65
  147. package/src/runtime/cache-telemetry.ts +247 -0
  148. package/src/runtime/cache.ts +68 -17
  149. package/src/runtime/capability-response.ts +53 -0
  150. package/src/runtime/capability-telemetry.ts +101 -0
  151. package/src/runtime/diagnostic-env.ts +11 -1
  152. package/src/runtime/engine-mode.ts +233 -0
  153. package/src/runtime/handle.ts +20 -10
  154. package/src/runtime/http-telemetry-binding.ts +162 -0
  155. package/src/runtime/http-telemetry-guard.ts +208 -0
  156. package/src/runtime/http-telemetry.ts +292 -0
  157. package/src/runtime/http.ts +457 -204
  158. package/src/runtime/instrumentation.ts +105 -16
  159. package/src/runtime/ocr-telemetry.ts +308 -0
  160. package/src/runtime/ocr.ts +109 -74
  161. package/src/runtime/request-telemetry.ts +2 -1
  162. package/src/runtime/resolver-runtime-options.ts +50 -0
  163. package/src/runtime/resolver-shared.ts +9 -4
  164. package/src/runtime/resolver-telemetry.ts +1 -1
  165. package/src/runtime/resolver-vendors/browser.ts +65 -7
  166. package/src/runtime/resolver-vendors/hosts.ts +43 -0
  167. package/src/runtime/resolver-vendors/hypersolutions.ts +2 -2
  168. package/src/runtime/resolver-vendors/types.ts +5 -4
  169. package/src/runtime/resolver.ts +64 -21
  170. package/src/runtime/state-telemetry.ts +298 -0
  171. package/src/runtime/state.ts +46 -45
  172. package/src/runtime/stealth-cookies.ts +24 -1
  173. package/src/runtime/stealth-owned-headers.ts +41 -0
  174. package/src/runtime/stealth-telemetry.ts +304 -0
  175. package/src/runtime/stealth.ts +355 -150
  176. package/src/runtime/stt-telemetry.ts +282 -0
  177. package/src/runtime/stt.ts +93 -71
  178. package/src/server/error-stack-frames.ts +214 -0
  179. package/src/server/index.ts +66 -0
  180. package/src/server/sdk-version.ts +22 -0
  181. package/src/server/self-test.ts +1 -21
  182. package/src/server/sensitive-values.ts +14 -0
  183. package/src/server/serve-implementation.ts +601 -157
  184. package/src/server/types.ts +7 -0
  185. package/src/stateful/stateful-provider-owner-forwarder.ts +23 -0
  186. package/src/stateful/stateful-provider-runtime-context.ts +4 -0
  187. package/src/types.ts +99 -1
  188. package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +0 -6
  189. package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +0 -3
  190. package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +0 -10
  191. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +0 -3
  192. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +0 -3
  193. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +0 -2
  194. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +0 -3
  195. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +0 -6
  196. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +0 -1
  197. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +0 -1
  198. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +0 -3
  199. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +0 -7
  200. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +0 -19
  201. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +0 -6
  202. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +0 -3
  203. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +0 -8
  204. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +0 -5
  205. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +0 -4
  206. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +0 -3
  207. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +0 -3
  208. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +0 -10
  209. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +0 -1
  210. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +0 -1
  211. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-index.ts.txt +0 -3
  212. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-one.ts.txt +0 -6
  213. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-two.ts.txt +0 -6
  214. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-mixed-index.ts.txt +0 -14
  215. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-registry.ts.txt +0 -3
  216. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +0 -2
  217. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +0 -3
  218. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +0 -3
  219. package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +0 -8
  220. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-hoisted-array-spread.ts.txt +0 -15
  221. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +0 -9
  222. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-kebab-operation-id.ts.txt +0 -17
  223. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +0 -5
  224. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +0 -24
  225. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +0 -16
  226. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-snake-operation-id.ts.txt +0 -21
  227. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +0 -11
  228. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +0 -7
  229. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +0 -3
  230. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +0 -18
  231. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +0 -13
  232. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +0 -31
  233. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +0 -11
  234. package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +0 -11
  235. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +0 -11
  236. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +0 -21
  237. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +0 -11
  238. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +0 -14
  239. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-en.json +0 -1
  240. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ja.json +0 -1
  241. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ko.json +0 -1
  242. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-existing-operation-namespace-en.json +0 -8
  243. package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +0 -7
  244. package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +0 -6
  245. package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +0 -7
  246. package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +0 -6
  247. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-binding-ambiguous.ts.txt +0 -12
  248. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported-other.ts.txt +0 -7
  249. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported.ts.txt +0 -5
  250. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-key-ambiguous.ts.txt +0 -19
  251. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-shorthand.ts.txt +0 -9
  252. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-typed.ts.txt +0 -11
  253. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-unrelated.ts.txt +0 -13
  254. package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +0 -7
  255. package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +0 -7
  256. package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +0 -15
  257. package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +0 -4
  258. 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,10 +157,17 @@ 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";
156
164
  export * from "./handle.js";
165
+ export {
166
+ APIFUSE_HANDLE_META_KEY,
167
+ HANDLE_KIND_NAME_PATTERN,
168
+ handleFieldDescription,
169
+ isHandleFieldMeta,
170
+ } from "./handle-meta.js";
157
171
  export {
158
172
  type CreateHandleContextOptions,
159
173
  type CreateTestHandleContextOptions,
@@ -163,6 +177,10 @@ export {
163
177
  type NormalizedHandle,
164
178
  normalizeHandle,
165
179
  } from "./runtime/handle.js";
180
+ export {
181
+ createMemoryProviderRuntimeState,
182
+ createUnsupportedProviderRuntimeState,
183
+ } from "./runtime/state.js";
166
184
  export {
167
185
  APIFUSE_CONTENT_PROVENANCE_META_KEY,
168
186
  APIFUSE_CONTENT_TRUST_META_KEY,
@@ -202,6 +220,8 @@ export type {
202
220
  HealthJourneyRunContext,
203
221
  HealthJourneyRunResult,
204
222
  HealthScheduleRandomization,
223
+ HttpAttemptContext,
224
+ HttpHeadersFactory,
205
225
  HttpRedirectFailureReason,
206
226
  HttpRedirectPolicy,
207
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
+ }