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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/AUTHORING.md +367 -9
  2. package/CHANGELOG.md +8 -0
  3. package/LICENSE +21 -0
  4. package/README.md +1 -0
  5. package/bin/apifuse-check.ts +84 -2
  6. package/bin/apifuse-dev.ts +11 -31
  7. package/bin/apifuse-migrate-deployment.ts +200 -0
  8. package/bin/apifuse-pack-check.ts +11 -4
  9. package/bin/apifuse-pack-types.ts +187 -4
  10. package/bin/apifuse-record.ts +44 -32
  11. package/bin/apifuse-submit-check.ts +9 -5
  12. package/bin/apifuse-test.ts +3 -1
  13. package/dist/cli/commands.d.ts +1 -1
  14. package/dist/cli/commands.js +11 -0
  15. package/dist/cli/deployment-intent.d.ts +178 -0
  16. package/dist/cli/deployment-intent.js +277 -0
  17. package/dist/cli/migrate-deployment-intent.d.ts +132 -0
  18. package/dist/cli/migrate-deployment-intent.js +1552 -0
  19. package/dist/cli/migrate-provider-shape.d.ts +5 -1
  20. package/dist/cli/migrate-provider-shape.js +4 -2
  21. package/dist/cli/resolver-runtime.d.ts +21 -0
  22. package/dist/cli/resolver-runtime.js +31 -0
  23. package/dist/define.d.ts +3 -0
  24. package/dist/define.js +5 -0
  25. package/dist/dev.d.ts +3 -1
  26. package/dist/dev.js +6 -1
  27. package/dist/engine.d.ts +35 -1
  28. package/dist/engine.js +21 -2
  29. package/dist/error-resolution.d.ts +1 -0
  30. package/dist/error-resolution.js +54 -0
  31. package/dist/errors.d.ts +39 -1
  32. package/dist/errors.js +50 -0
  33. package/dist/handle-meta.d.ts +36 -1
  34. package/dist/handle-meta.js +16 -0
  35. package/dist/handle.d.ts +23 -5
  36. package/dist/handle.js +54 -8
  37. package/dist/health-scenario.d.ts +38 -0
  38. package/dist/i18n/error-messages.d.ts +86 -0
  39. package/dist/i18n/error-messages.js +192 -0
  40. package/dist/i18n/index.d.ts +1 -0
  41. package/dist/i18n/index.js +1 -0
  42. package/dist/i18n/keys.d.ts +12 -0
  43. package/dist/i18n/keys.js +76 -5
  44. package/dist/index.d.ts +11 -3
  45. package/dist/index.js +9 -1
  46. package/dist/lint.d.ts +15 -0
  47. package/dist/lint.js +469 -82
  48. package/dist/provider.d.ts +6 -6
  49. package/dist/provider.js +3 -3
  50. package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
  51. package/dist/runtime/browser-telemetry-binding.js +83 -0
  52. package/dist/runtime/browser-telemetry.d.ts +76 -0
  53. package/dist/runtime/browser-telemetry.js +165 -0
  54. package/dist/runtime/browser.d.ts +3 -0
  55. package/dist/runtime/browser.js +208 -47
  56. package/dist/runtime/cache-telemetry.d.ts +71 -0
  57. package/dist/runtime/cache-telemetry.js +177 -0
  58. package/dist/runtime/cache.js +67 -7
  59. package/dist/runtime/capability-response.d.ts +3 -0
  60. package/dist/runtime/capability-response.js +56 -0
  61. package/dist/runtime/capability-telemetry.d.ts +46 -0
  62. package/dist/runtime/capability-telemetry.js +68 -0
  63. package/dist/runtime/diagnostic-env.d.ts +3 -0
  64. package/dist/runtime/diagnostic-env.js +10 -1
  65. package/dist/runtime/engine-mode.d.ts +74 -0
  66. package/dist/runtime/engine-mode.js +152 -0
  67. package/dist/runtime/handle.js +9 -1
  68. package/dist/runtime/http-telemetry-guard.d.ts +10 -0
  69. package/dist/runtime/http-telemetry-guard.js +62 -10
  70. package/dist/runtime/http.js +141 -35
  71. package/dist/runtime/instrumentation.js +92 -13
  72. package/dist/runtime/ocr-telemetry.d.ts +82 -0
  73. package/dist/runtime/ocr-telemetry.js +192 -0
  74. package/dist/runtime/ocr.js +35 -8
  75. package/dist/runtime/request-telemetry.d.ts +2 -0
  76. package/dist/runtime/request-telemetry.js +2 -1
  77. package/dist/runtime/resolver-runtime-options.d.ts +14 -0
  78. package/dist/runtime/resolver-runtime-options.js +33 -0
  79. package/dist/runtime/resolver-shared.d.ts +3 -2
  80. package/dist/runtime/resolver-shared.js +4 -4
  81. package/dist/runtime/resolver-telemetry.d.ts +1 -0
  82. package/dist/runtime/resolver-telemetry.js +1 -1
  83. package/dist/runtime/resolver-vendors/browser.d.ts +2 -0
  84. package/dist/runtime/resolver-vendors/browser.js +46 -0
  85. package/dist/runtime/resolver-vendors/hosts.d.ts +21 -0
  86. package/dist/runtime/resolver-vendors/hosts.js +35 -0
  87. package/dist/runtime/resolver-vendors/hypersolutions.js +2 -2
  88. package/dist/runtime/resolver-vendors/types.d.ts +5 -4
  89. package/dist/runtime/resolver.d.ts +4 -1
  90. package/dist/runtime/resolver.js +46 -13
  91. package/dist/runtime/state-telemetry.d.ts +79 -0
  92. package/dist/runtime/state-telemetry.js +194 -0
  93. package/dist/runtime/state.d.ts +3 -0
  94. package/dist/runtime/state.js +26 -7
  95. package/dist/runtime/stealth-cookies.d.ts +5 -1
  96. package/dist/runtime/stealth-cookies.js +21 -1
  97. package/dist/runtime/stealth-owned-headers.d.ts +26 -0
  98. package/dist/runtime/stealth-owned-headers.js +37 -0
  99. package/dist/runtime/stealth-telemetry.d.ts +92 -0
  100. package/dist/runtime/stealth-telemetry.js +179 -0
  101. package/dist/runtime/stealth.d.ts +4 -1
  102. package/dist/runtime/stealth.js +228 -85
  103. package/dist/runtime/stt-telemetry.d.ts +77 -0
  104. package/dist/runtime/stt-telemetry.js +176 -0
  105. package/dist/runtime/stt.js +15 -6
  106. package/dist/runtime-boundary-lint.d.ts +132 -0
  107. package/dist/runtime-boundary-lint.js +586 -0
  108. package/dist/server/error-stack-frames.d.ts +35 -0
  109. package/dist/server/error-stack-frames.js +219 -0
  110. package/dist/server/index.d.ts +7 -0
  111. package/dist/server/index.js +4 -0
  112. package/dist/server/sdk-version.d.ts +3 -0
  113. package/dist/server/sdk-version.js +19 -0
  114. package/dist/server/self-test.js +1 -18
  115. package/dist/server/sensitive-values.d.ts +1 -1
  116. package/dist/server/sensitive-values.js +9 -0
  117. package/dist/server/serve-implementation.d.ts +51 -2
  118. package/dist/server/serve-implementation.js +337 -90
  119. package/dist/server/types.d.ts +1 -0
  120. package/dist/server/types.js +7 -0
  121. package/dist/stateful/stateful-provider-owner-forwarder.js +22 -0
  122. package/dist/stateful/stateful-provider-runtime-context.d.ts +3 -0
  123. package/dist/stateful/stateful-provider-runtime-context.js +1 -0
  124. package/dist/types.d.ts +123 -1
  125. package/dist/typescript-module.d.ts +39 -0
  126. package/dist/typescript-module.js +72 -0
  127. package/package.json +6 -1
  128. package/src/cli/commands.ts +14 -0
  129. package/src/cli/deployment-intent.ts +364 -0
  130. package/src/cli/migrate-deployment-intent.ts +1908 -0
  131. package/src/cli/migrate-provider-shape.ts +10 -4
  132. package/src/cli/resolver-runtime.ts +72 -0
  133. package/src/define.ts +13 -0
  134. package/src/dev.ts +9 -2
  135. package/src/engine.ts +38 -2
  136. package/src/error-resolution.ts +58 -0
  137. package/src/errors.ts +77 -1
  138. package/src/handle-meta.ts +51 -1
  139. package/src/handle.ts +92 -10
  140. package/src/health-scenario.ts +38 -0
  141. package/src/i18n/error-messages.ts +255 -0
  142. package/src/i18n/index.ts +1 -0
  143. package/src/i18n/keys.ts +80 -6
  144. package/src/index.ts +86 -0
  145. package/src/lint.ts +604 -91
  146. package/src/provider.ts +12 -0
  147. package/src/runtime/browser-telemetry-binding.ts +92 -0
  148. package/src/runtime/browser-telemetry.ts +289 -0
  149. package/src/runtime/browser.ts +249 -65
  150. package/src/runtime/cache-telemetry.ts +247 -0
  151. package/src/runtime/cache.ts +68 -17
  152. package/src/runtime/capability-response.ts +53 -0
  153. package/src/runtime/capability-telemetry.ts +101 -0
  154. package/src/runtime/diagnostic-env.ts +11 -1
  155. package/src/runtime/engine-mode.ts +233 -0
  156. package/src/runtime/handle.ts +9 -1
  157. package/src/runtime/http-telemetry-guard.ts +66 -13
  158. package/src/runtime/http.ts +168 -37
  159. package/src/runtime/instrumentation.ts +105 -16
  160. package/src/runtime/ocr-telemetry.ts +308 -0
  161. package/src/runtime/ocr.ts +114 -74
  162. package/src/runtime/request-telemetry.ts +2 -1
  163. package/src/runtime/resolver-runtime-options.ts +50 -0
  164. package/src/runtime/resolver-shared.ts +9 -4
  165. package/src/runtime/resolver-telemetry.ts +1 -1
  166. package/src/runtime/resolver-vendors/browser.ts +65 -7
  167. package/src/runtime/resolver-vendors/hosts.ts +43 -0
  168. package/src/runtime/resolver-vendors/hypersolutions.ts +2 -2
  169. package/src/runtime/resolver-vendors/types.ts +5 -4
  170. package/src/runtime/resolver.ts +64 -21
  171. package/src/runtime/state-telemetry.ts +298 -0
  172. package/src/runtime/state.ts +46 -45
  173. package/src/runtime/stealth-cookies.ts +24 -1
  174. package/src/runtime/stealth-owned-headers.ts +41 -0
  175. package/src/runtime/stealth-telemetry.ts +304 -0
  176. package/src/runtime/stealth.ts +355 -150
  177. package/src/runtime/stt-telemetry.ts +282 -0
  178. package/src/runtime/stt.ts +93 -71
  179. package/src/runtime-boundary-lint.ts +703 -0
  180. package/src/server/error-stack-frames.ts +214 -0
  181. package/src/server/index.ts +53 -0
  182. package/src/server/sdk-version.ts +22 -0
  183. package/src/server/self-test.ts +1 -21
  184. package/src/server/sensitive-values.ts +14 -0
  185. package/src/server/serve-implementation.ts +569 -108
  186. package/src/server/types.ts +7 -0
  187. package/src/stateful/stateful-provider-owner-forwarder.ts +23 -0
  188. package/src/stateful/stateful-provider-runtime-context.ts +4 -0
  189. package/src/types.ts +128 -1
  190. package/src/typescript-module.ts +100 -0
  191. package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +0 -6
  192. package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +0 -3
  193. package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +0 -10
  194. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +0 -3
  195. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +0 -3
  196. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +0 -2
  197. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +0 -3
  198. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +0 -6
  199. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +0 -1
  200. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +0 -1
  201. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +0 -3
  202. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +0 -7
  203. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +0 -19
  204. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +0 -6
  205. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +0 -3
  206. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +0 -8
  207. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +0 -5
  208. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +0 -4
  209. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +0 -3
  210. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +0 -3
  211. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +0 -10
  212. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +0 -1
  213. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +0 -1
  214. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-index.ts.txt +0 -3
  215. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-one.ts.txt +0 -6
  216. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-two.ts.txt +0 -6
  217. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-mixed-index.ts.txt +0 -14
  218. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-registry.ts.txt +0 -3
  219. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +0 -2
  220. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +0 -3
  221. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +0 -3
  222. package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +0 -8
  223. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-hoisted-array-spread.ts.txt +0 -15
  224. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +0 -9
  225. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-kebab-operation-id.ts.txt +0 -17
  226. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +0 -5
  227. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +0 -24
  228. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +0 -16
  229. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-snake-operation-id.ts.txt +0 -21
  230. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +0 -11
  231. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +0 -7
  232. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +0 -3
  233. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +0 -18
  234. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +0 -13
  235. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +0 -31
  236. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +0 -11
  237. package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +0 -11
  238. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +0 -11
  239. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +0 -21
  240. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +0 -11
  241. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +0 -14
  242. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-en.json +0 -1
  243. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ja.json +0 -1
  244. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ko.json +0 -1
  245. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-existing-operation-namespace-en.json +0 -8
  246. package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +0 -7
  247. package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +0 -6
  248. package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +0 -7
  249. package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +0 -6
  250. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-binding-ambiguous.ts.txt +0 -12
  251. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported-other.ts.txt +0 -7
  252. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported.ts.txt +0 -5
  253. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-key-ambiguous.ts.txt +0 -19
  254. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-shorthand.ts.txt +0 -9
  255. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-typed.ts.txt +0 -11
  256. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-unrelated.ts.txt +0 -13
  257. package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +0 -7
  258. package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +0 -7
  259. package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +0 -15
  260. package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +0 -4
  261. package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +0 -12
@@ -0,0 +1,308 @@
1
+ import type { OcrContext } from "../types.js";
2
+ import {
3
+ add,
4
+ capabilityErrorText,
5
+ capabilityFacts,
6
+ capabilityMetadata,
7
+ capabilityText,
8
+ inputBytes,
9
+ integer,
10
+ observeCapability,
11
+ registerCapabilityInput,
12
+ } from "./capability-telemetry.js";
13
+ import {
14
+ type ClosedEnum,
15
+ closedEnum,
16
+ type SpanIndex,
17
+ type TelemetryContributor,
18
+ } from "./request-telemetry.js";
19
+ export type OcrTelemetryBackend =
20
+ | "cloudflare-workers-ai"
21
+ | "openai-compatible"
22
+ | "custom"
23
+ | "unavailable";
24
+ export type OcrTelemetryEngine = "workers-ai" | "openai-compatible" | "custom";
25
+ export type OcrTelemetryErrorCode =
26
+ | "OCR_UNAVAILABLE"
27
+ | "UNSUPPORTED_OCR_BACKEND"
28
+ | "OCR_UPSTREAM_FAILED"
29
+ | "OCR_INCOMPLETE_RESPONSE"
30
+ | "transport_network_error"
31
+ | "transport_timeout"
32
+ | "other";
33
+ export type OcrTelemetryModel =
34
+ | "gemma-4-26b-a4b-it"
35
+ | "glm-ocr"
36
+ | "moondream3.1-9B-A2B"
37
+ | "kimi-k2.7-code";
38
+ export type OcrTelemetryFinishReason =
39
+ | "stop"
40
+ | "length"
41
+ | "content_filter"
42
+ | "tool_calls"
43
+ | "function_call"
44
+ | "error"
45
+ | "unknown";
46
+ const ERROR_CODES: ReadonlySet<string> = new Set([
47
+ "OCR_UNAVAILABLE",
48
+ "UNSUPPORTED_OCR_BACKEND",
49
+ "OCR_UPSTREAM_FAILED",
50
+ "OCR_INCOMPLETE_RESPONSE",
51
+ "transport_network_error",
52
+ "transport_timeout",
53
+ "other",
54
+ ]);
55
+ const MODELS = new Map<string, OcrTelemetryModel>([
56
+ ["@cf/google/gemma-4-26b-a4b-it", "gemma-4-26b-a4b-it"],
57
+ ["zai-org/GLM-OCR", "glm-ocr"],
58
+ ["@cf/moondream/moondream3.1-9B-A2B", "moondream3.1-9B-A2B"],
59
+ ["@cf/moonshotai/kimi-k2.7-code", "kimi-k2.7-code"],
60
+ ]);
61
+ function errorCode(error: unknown): OcrTelemetryErrorCode {
62
+ const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
63
+ return typeof code === "string" && ERROR_CODES.has(code)
64
+ ? (code as OcrTelemetryErrorCode)
65
+ : "other";
66
+ }
67
+ function finishReason(value: string | undefined, failed: boolean): OcrTelemetryFinishReason {
68
+ switch (value) {
69
+ case "stop":
70
+ case "length":
71
+ case "content_filter":
72
+ case "tool_calls":
73
+ case "function_call":
74
+ return value;
75
+ default:
76
+ return value === undefined ? (failed ? "error" : "stop") : "unknown";
77
+ }
78
+ }
79
+ export type OcrTelemetryEvent = {
80
+ backend: OcrTelemetryBackend;
81
+ engine: OcrTelemetryEngine;
82
+ model?: string;
83
+ ms: number;
84
+ status?: number;
85
+ bytesIn?: number;
86
+ bytesOut?: number;
87
+ candidates?: number;
88
+ warnings?: number;
89
+ errorCode?: OcrTelemetryErrorCode;
90
+ diagnostics?: string;
91
+ finishReason?: OcrTelemetryFinishReason;
92
+ };
93
+ export interface OcrTelemetrySink {
94
+ record(event: OcrTelemetryEvent): void;
95
+ markTelemetryFailed?(): void;
96
+ }
97
+ export type OcrTelemetryLogPayload = {
98
+ telemetryFailed?: true;
99
+ diagnostics?: string[];
100
+ backend: OcrTelemetryBackend;
101
+ engine: OcrTelemetryEngine;
102
+ model?: string;
103
+ ms: number;
104
+ status?: number;
105
+ bytesIn: number;
106
+ bytesOut: number;
107
+ candidates: number;
108
+ warnings: number;
109
+ lastErrorCode?: OcrTelemetryErrorCode;
110
+ finishReason?: OcrTelemetryFinishReason;
111
+ samples?: Array<Omit<OcrTelemetryEvent, "backend" | "engine" | "model" | "diagnostics">>;
112
+ samplesDropped?: number;
113
+ };
114
+ export type OcrTelemetryHeaderPayload = {
115
+ backend: ClosedEnum<OcrTelemetryBackend>;
116
+ engine: ClosedEnum<OcrTelemetryEngine>;
117
+ model?: ClosedEnum<OcrTelemetryModel>;
118
+ ms: number;
119
+ status?: number;
120
+ bytesIn: number;
121
+ bytesOut: number;
122
+ candidates: number;
123
+ warnings: number;
124
+ lastErrorCode?: ClosedEnum<OcrTelemetryErrorCode>;
125
+ finishReason?: ClosedEnum<OcrTelemetryFinishReason>;
126
+ samples?: Array<{
127
+ ms: number;
128
+ status?: number;
129
+ bytesIn?: number;
130
+ bytesOut?: number;
131
+ candidates?: number;
132
+ warnings?: number;
133
+ errorCode?: ClosedEnum<OcrTelemetryErrorCode>;
134
+ finishReason?: ClosedEnum<OcrTelemetryFinishReason>;
135
+ }>;
136
+ samplesDropped?: number;
137
+ };
138
+ export class OcrTelemetryCollector
139
+ implements
140
+ OcrTelemetrySink,
141
+ TelemetryContributor<OcrTelemetryLogPayload, OcrTelemetryHeaderPayload>
142
+ {
143
+ readonly key = "ocr" as const;
144
+ readonly #redact?: (text: string) => string;
145
+ #samples: NonNullable<OcrTelemetryLogPayload["samples"]> = [];
146
+ #summary: Omit<OcrTelemetryLogPayload, "samples" | "samplesDropped"> | undefined;
147
+ #diagnostics: string[] = [];
148
+ #dropped = 0;
149
+ #failed = false;
150
+ constructor(options: { redact?: (text: string) => string } = {}) {
151
+ this.#redact = options.redact;
152
+ }
153
+ markTelemetryFailed(): void {
154
+ this.#failed = true;
155
+ this.#summary ??= {
156
+ backend: "custom",
157
+ engine: "custom",
158
+ ms: 0,
159
+ bytesIn: 0,
160
+ bytesOut: 0,
161
+ candidates: 0,
162
+ warnings: 0,
163
+ };
164
+ }
165
+ record(event: OcrTelemetryEvent): void {
166
+ const sample = {
167
+ ms: integer(event.ms),
168
+ ...(event.status === undefined ? {} : { status: integer(event.status) }),
169
+ ...(event.bytesIn === undefined ? {} : { bytesIn: integer(event.bytesIn) }),
170
+ ...(event.bytesOut === undefined ? {} : { bytesOut: integer(event.bytesOut) }),
171
+ ...(event.candidates === undefined ? {} : { candidates: integer(event.candidates) }),
172
+ ...(event.warnings === undefined ? {} : { warnings: integer(event.warnings) }),
173
+ ...(event.errorCode ? { errorCode: event.errorCode } : {}),
174
+ ...(event.finishReason ? { finishReason: event.finishReason } : {}),
175
+ ...(event.diagnostics === undefined
176
+ ? {}
177
+ : { diagnostics: capabilityText(event.diagnostics, this.#redact, true) }),
178
+ };
179
+ const prior = this.#summary;
180
+ this.#summary = {
181
+ backend: event.backend,
182
+ engine: event.engine,
183
+ ...(event.model === undefined ? {} : { model: capabilityText(event.model, this.#redact) }),
184
+ ms: add(prior?.ms ?? 0, sample.ms),
185
+ ...(sample.status === undefined ? {} : { status: sample.status }),
186
+ bytesIn: add(prior?.bytesIn ?? 0, sample.bytesIn ?? 0),
187
+ bytesOut: add(prior?.bytesOut ?? 0, sample.bytesOut ?? 0),
188
+ candidates: add(prior?.candidates ?? 0, sample.candidates ?? 0),
189
+ warnings: add(prior?.warnings ?? 0, sample.warnings ?? 0),
190
+ ...(event.errorCode ? { lastErrorCode: event.errorCode } : {}),
191
+ ...(event.finishReason ? { finishReason: event.finishReason } : {}),
192
+ };
193
+ if (this.#samples.length < 24) {
194
+ const { diagnostics, ...closedSample } = sample;
195
+ this.#samples.push(closedSample);
196
+ if (diagnostics !== undefined) this.#diagnostics.push(diagnostics);
197
+ } else this.#dropped = add(this.#dropped, 1);
198
+ }
199
+ toLogPayload(_spans: SpanIndex): OcrTelemetryLogPayload | undefined {
200
+ if (!this.#summary) return undefined;
201
+ return {
202
+ ...this.#summary,
203
+ ...(this.#diagnostics.length ? { diagnostics: [...this.#diagnostics] } : {}),
204
+ ...(this.#failed ? { telemetryFailed: true as const } : {}),
205
+ samples: this.#samples.map((s) => ({ ...s })),
206
+ ...(this.#dropped ? { samplesDropped: this.#dropped } : {}),
207
+ };
208
+ }
209
+ toHeaderPayload(log: OcrTelemetryLogPayload): OcrTelemetryHeaderPayload {
210
+ return {
211
+ backend: closedEnum(log.backend),
212
+ engine: closedEnum(log.engine),
213
+ ...(log.model && MODELS.has(log.model) ? { model: closedEnum(MODELS.get(log.model)!) } : {}),
214
+ ms: log.ms,
215
+ ...(log.status === undefined ? {} : { status: log.status }),
216
+ bytesIn: log.bytesIn,
217
+ bytesOut: log.bytesOut,
218
+ candidates: log.candidates,
219
+ warnings: log.warnings,
220
+ ...(log.lastErrorCode ? { lastErrorCode: closedEnum(log.lastErrorCode) } : {}),
221
+ ...(log.finishReason ? { finishReason: closedEnum(log.finishReason) } : {}),
222
+ ...(log.samples
223
+ ? {
224
+ samples: log.samples.map((s) => ({
225
+ ms: s.ms,
226
+ ...(s.status === undefined ? {} : { status: s.status }),
227
+ ...(s.bytesIn === undefined ? {} : { bytesIn: s.bytesIn }),
228
+ ...(s.bytesOut === undefined ? {} : { bytesOut: s.bytesOut }),
229
+ ...(s.candidates === undefined ? {} : { candidates: s.candidates }),
230
+ ...(s.warnings === undefined ? {} : { warnings: s.warnings }),
231
+ ...(s.errorCode ? { errorCode: closedEnum(s.errorCode) } : {}),
232
+ ...(s.finishReason ? { finishReason: closedEnum(s.finishReason) } : {}),
233
+ })),
234
+ }
235
+ : {}),
236
+ ...(log.samplesDropped ? { samplesDropped: log.samplesDropped } : {}),
237
+ };
238
+ }
239
+ }
240
+ export function bindOcrTelemetry(
241
+ context: OcrContext,
242
+ sink: OcrTelemetrySink,
243
+ options: { backend?: OcrTelemetryBackend; engine?: OcrTelemetryEngine; model?: string } = {},
244
+ ): OcrContext {
245
+ const metadata = capabilityMetadata(context);
246
+ const backend =
247
+ options.backend ?? (metadata?.backend as OcrTelemetryBackend | undefined) ?? "custom";
248
+ const engine = options.engine ?? (metadata?.engine as OcrTelemetryEngine | undefined) ?? "custom";
249
+ const model = options.model ?? metadata?.model;
250
+ async function run<
251
+ T extends {
252
+ text: string;
253
+ model: string;
254
+ candidates?: readonly unknown[];
255
+ warnings?: readonly unknown[];
256
+ },
257
+ >(input: { kind: string; data?: string; url?: string }, operation: () => Promise<T>): Promise<T> {
258
+ const started = performance.now();
259
+ const facts: { status?: number; finishReason?: string; diagnostics?: string } = {};
260
+ observeCapability(sink, () => registerCapabilityInput(input));
261
+ let result: T;
262
+ try {
263
+ result = await capabilityFacts.run(facts, operation);
264
+ } catch (error) {
265
+ observeCapability(sink, () => {
266
+ const status =
267
+ error &&
268
+ typeof error === "object" &&
269
+ "status" in error &&
270
+ typeof error.status === "number"
271
+ ? error.status
272
+ : undefined;
273
+ return sink.record({
274
+ backend,
275
+ engine,
276
+ model,
277
+ ms: performance.now() - started,
278
+ status: facts.status ?? status,
279
+ bytesIn: inputBytes(input),
280
+ errorCode: errorCode(error),
281
+ finishReason: finishReason(facts.finishReason, true),
282
+ diagnostics: `${capabilityErrorText(error)} ${facts.diagnostics ?? ""}`,
283
+ });
284
+ });
285
+ throw error;
286
+ }
287
+ observeCapability(sink, () =>
288
+ sink.record({
289
+ backend,
290
+ engine,
291
+ model: result.model,
292
+ ms: performance.now() - started,
293
+ status: facts.status,
294
+ bytesIn: inputBytes(input),
295
+ bytesOut: Buffer.byteLength(result.text),
296
+ candidates: result.candidates?.length ?? 0,
297
+ finishReason: finishReason(facts.finishReason, false),
298
+ warnings: result.warnings?.length ?? 0,
299
+ }),
300
+ );
301
+ return result;
302
+ }
303
+ return {
304
+ ...context,
305
+ recognize: (request) => run(request.image, () => context.recognize(request)),
306
+ extractCaptchaText: (image, opts) => run(image, () => context.extractCaptchaText(image, opts)),
307
+ };
308
+ }
@@ -1,4 +1,3 @@
1
- import { readDiagnosticEnv } from "./diagnostic-env.js";
2
1
  import { ProviderError, TransportError } from "../errors.js";
3
2
  import type {
4
3
  OcrCaptchaCandidate,
@@ -10,6 +9,13 @@ import type {
10
9
  OcrResult,
11
10
  ProviderOcrConfig,
12
11
  } from "../types.js";
12
+ import { readCapabilityErrorBody } from "./capability-response.js";
13
+ import {
14
+ captureCapability,
15
+ registerCapabilityInput,
16
+ tagCapability,
17
+ } from "./capability-telemetry.js";
18
+ import { readDiagnosticEnv, registerDiagnosticValue } from "./diagnostic-env.js";
13
19
  import { CLOUDFLARE_ACCOUNT_ID_ENV } from "./stt.js";
14
20
  import { createTimeoutController, isTimeoutLikeError } from "./timeout.js";
15
21
 
@@ -78,14 +84,21 @@ function createErrorOcrClient(options: ErrorOcrClientOptions): OcrContext {
78
84
  fix: options.fix,
79
85
  });
80
86
  };
81
- return {
82
- async recognize() {
83
- return unavailable();
84
- },
85
- async extractCaptchaText() {
86
- return unavailable();
87
+ return tagCapability(
88
+ {
89
+ // Own data property: `bindOcrTelemetry` re-spreads the context, so a
90
+ // getter or prototype member would silently vanish before reaching
91
+ // provider code.
92
+ available: false,
93
+ async recognize() {
94
+ return unavailable();
95
+ },
96
+ async extractCaptchaText() {
97
+ return unavailable();
98
+ },
87
99
  },
88
- };
100
+ { backend: "unavailable", engine: "custom" },
101
+ );
89
102
  }
90
103
 
91
104
  export function createUnsupportedOcrClient(reason?: string): OcrContext {
@@ -249,6 +262,8 @@ function responseContent(payload: unknown, cloudflare: boolean, model: string):
249
262
  const choices = root?.choices;
250
263
  if (!Array.isArray(choices)) return undefined;
251
264
  const choice = unknownRecord(choices[0]);
265
+ if (typeof choice?.finish_reason === "string")
266
+ captureCapability({ finishReason: choice.finish_reason });
252
267
  if (typeof choice?.finish_reason === "string" && choice.finish_reason !== "stop") {
253
268
  throw incompleteResponseError(model, choice.finish_reason);
254
269
  }
@@ -305,7 +320,11 @@ function moondreamSseContent(body: string, model: string): string | undefined {
305
320
  terminalFinishReason = chunk.finish_reason;
306
321
  }
307
322
  }
308
- if (finalAnswer) return finalAnswer;
323
+ if (finalAnswer) {
324
+ captureCapability({ finishReason: "stop" });
325
+ return finalAnswer;
326
+ }
327
+ if (terminalFinishReason) captureCapability({ finishReason: terminalFinishReason });
309
328
  if (terminalFinishReason) throw incompleteResponseError(model, terminalFinishReason);
310
329
  if (firstDecodingFailure) throw malformedResponseError(model, firstDecodingFailure);
311
330
  return finalAnswer;
@@ -340,6 +359,7 @@ function createOcrClient(
340
359
  recognize: (request: OcrRecognizeRequest) => Promise<OcrResult>,
341
360
  ): OcrContext {
342
361
  return {
362
+ available: true,
343
363
  recognize,
344
364
  async extractCaptchaText(image, options = {}): Promise<OcrCaptchaResult> {
345
365
  const result = await recognize({ image, hint: "captcha" });
@@ -361,48 +381,57 @@ export function createCloudflareWorkersAiOcrClient(
361
381
  ): OcrContext {
362
382
  const model = options.model ?? DEFAULT_CLOUDFLARE_WORKERS_AI_OCR_MODEL;
363
383
  const runFetch = options.fetch ?? fetch;
364
- return createOcrClient(model, async (request) => {
365
- const timeout = createTimeoutController(request.timeoutMs ?? DEFAULT_OCR_TIMEOUT_MS);
366
- try {
367
- let response: Response;
384
+ return tagCapability(
385
+ createOcrClient(model, async (request) => {
386
+ registerCapabilityInput(request.image);
387
+ registerDiagnosticValue(options.apiToken);
388
+ registerDiagnosticValue(`Bearer ${options.apiToken}`);
389
+ const timeout = createTimeoutController(request.timeoutMs ?? DEFAULT_OCR_TIMEOUT_MS);
368
390
  try {
369
- response = await runFetch(
370
- `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(options.accountId)}/ai/run/${model}`,
371
- {
372
- method: "POST",
373
- headers: {
374
- Authorization: `Bearer ${options.apiToken}`,
375
- "Content-Type": "application/json",
391
+ let response: Response;
392
+ try {
393
+ response = await runFetch(
394
+ `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(options.accountId)}/ai/run/${model}`,
395
+ {
396
+ method: "POST",
397
+ headers: {
398
+ Authorization: `Bearer ${options.apiToken}`,
399
+ "Content-Type": "application/json",
400
+ },
401
+ body: JSON.stringify(
402
+ isMoondreamModel(model)
403
+ ? moondreamPayload(request)
404
+ : cloudflareMessagesPayload(request, model),
405
+ ),
406
+ signal: timeout.controller.signal,
376
407
  },
377
- body: JSON.stringify(
378
- isMoondreamModel(model)
379
- ? moondreamPayload(request)
380
- : cloudflareMessagesPayload(request, model),
381
- ),
382
- signal: timeout.controller.signal,
383
- },
384
- );
408
+ );
409
+ } catch (error) {
410
+ throw toOcrTransportError(error);
411
+ }
412
+ captureCapability({ status: response.status });
413
+ if (!response.ok) {
414
+ const echoed = await readCapabilityErrorBody(response, timeout.controller.signal);
415
+ captureCapability({ diagnostics: echoed });
416
+ throw new TransportError("OCR upstream request failed", {
417
+ code: "OCR_UPSTREAM_FAILED",
418
+ status: response.status,
419
+ upstreamStatus: response.status,
420
+ });
421
+ }
422
+ const text = isMoondreamModel(model)
423
+ ? moondreamSseContent(await response.text(), model)
424
+ : responseContent(await responseJson(response, model), true, model);
425
+ if (!text) throw emptyResponseError(model);
426
+ return { text, model };
385
427
  } catch (error) {
386
428
  throw toOcrTransportError(error);
429
+ } finally {
430
+ timeout.clear();
387
431
  }
388
- if (!response.ok) {
389
- throw new TransportError("OCR upstream request failed", {
390
- code: "OCR_UPSTREAM_FAILED",
391
- status: response.status,
392
- upstreamStatus: response.status,
393
- });
394
- }
395
- const text = isMoondreamModel(model)
396
- ? moondreamSseContent(await response.text(), model)
397
- : responseContent(await responseJson(response, model), true, model);
398
- if (!text) throw emptyResponseError(model);
399
- return { text, model };
400
- } catch (error) {
401
- throw toOcrTransportError(error);
402
- } finally {
403
- timeout.clear();
404
- }
405
- });
432
+ }),
433
+ { backend: "cloudflare-workers-ai", engine: "workers-ai", model },
434
+ );
406
435
  }
407
436
 
408
437
  export function createOpenAiCompatibleOcrClient(
@@ -410,38 +439,49 @@ export function createOpenAiCompatibleOcrClient(
410
439
  ): OcrContext {
411
440
  const model = options.model;
412
441
  const runFetch = options.fetch ?? fetch;
413
- return createOcrClient(model, async (request) => {
414
- const headers: Record<string, string> = { "Content-Type": "application/json" };
415
- if (options.apiKey) headers.Authorization = `Bearer ${options.apiKey}`;
416
- const timeout = createTimeoutController(request.timeoutMs ?? DEFAULT_OCR_TIMEOUT_MS);
417
- try {
418
- let response: Response;
442
+ return tagCapability(
443
+ createOcrClient(model, async (request) => {
444
+ registerCapabilityInput(request.image);
445
+ if (options.apiKey) {
446
+ registerDiagnosticValue(options.apiKey);
447
+ registerDiagnosticValue(`Bearer ${options.apiKey}`);
448
+ }
449
+ const headers: Record<string, string> = { "Content-Type": "application/json" };
450
+ if (options.apiKey) headers.Authorization = `Bearer ${options.apiKey}`;
451
+ const timeout = createTimeoutController(request.timeoutMs ?? DEFAULT_OCR_TIMEOUT_MS);
419
452
  try {
420
- response = await runFetch(`${options.baseUrl.replace(/\/+$/u, "")}/chat/completions`, {
421
- method: "POST",
422
- headers,
423
- body: JSON.stringify(messagesPayload(request, model)),
424
- signal: timeout.controller.signal,
425
- });
453
+ let response: Response;
454
+ try {
455
+ response = await runFetch(`${options.baseUrl.replace(/\/+$/u, "")}/chat/completions`, {
456
+ method: "POST",
457
+ headers,
458
+ body: JSON.stringify(messagesPayload(request, model)),
459
+ signal: timeout.controller.signal,
460
+ });
461
+ } catch (error) {
462
+ throw toOcrTransportError(error);
463
+ }
464
+ captureCapability({ status: response.status });
465
+ if (!response.ok) {
466
+ const echoed = await readCapabilityErrorBody(response, timeout.controller.signal);
467
+ captureCapability({ diagnostics: echoed });
468
+ throw new TransportError("OCR upstream request failed", {
469
+ code: "OCR_UPSTREAM_FAILED",
470
+ status: response.status,
471
+ upstreamStatus: response.status,
472
+ });
473
+ }
474
+ const text = responseContent(await responseJson(response, model), false, model);
475
+ if (!text) throw emptyResponseError(model);
476
+ return { text, model };
426
477
  } catch (error) {
427
478
  throw toOcrTransportError(error);
479
+ } finally {
480
+ timeout.clear();
428
481
  }
429
- if (!response.ok) {
430
- throw new TransportError("OCR upstream request failed", {
431
- code: "OCR_UPSTREAM_FAILED",
432
- status: response.status,
433
- upstreamStatus: response.status,
434
- });
435
- }
436
- const text = responseContent(await responseJson(response, model), false, model);
437
- if (!text) throw emptyResponseError(model);
438
- return { text, model };
439
- } catch (error) {
440
- throw toOcrTransportError(error);
441
- } finally {
442
- timeout.clear();
443
- }
444
- });
482
+ }),
483
+ { backend: "openai-compatible", engine: "openai-compatible", model },
484
+ );
445
485
  }
446
486
 
447
487
  function cleanCaptchaText(text: string): string {
@@ -140,7 +140,8 @@ const MAX_INGESTIBLE_ARRAY_LENGTH = 64;
140
140
  const MAX_INGESTIBLE_OBJECT_KEYS = 32;
141
141
  const MAX_INGESTIBLE_DEPTH = 4;
142
142
  const INGESTIBLE_TOKEN = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$/;
143
- const HEADER_PRIORITY = [
143
+ /** @internal */
144
+ export const HEADER_PRIORITY = [
144
145
  "proxy",
145
146
  "resolver",
146
147
  "native",
@@ -0,0 +1,50 @@
1
+ import type { ProviderCache, ProviderDefinition, ProviderProxyPolicy } from "../types.js";
2
+ import type { ResolverRuntimeOptions } from "./resolver.js";
3
+ import type { ResolverTelemetrySink } from "./resolver-telemetry.js";
4
+ import type { BrowserTelemetrySink } from "./browser-telemetry.js";
5
+
6
+ /** Normalize the `proxy` declaration shorthand (`true`/`false`) into the policy object the resolver chain consumes. */
7
+ export function resolveNativeProxyPolicy(
8
+ provider: ProviderDefinition,
9
+ ): ProviderProxyPolicy | undefined {
10
+ if (typeof provider.proxy === "object") return provider.proxy;
11
+ if (provider.proxy === true) return { mode: "optional" };
12
+ if (provider.proxy === false) return { mode: "disabled" };
13
+ return undefined;
14
+ }
15
+
16
+ /**
17
+ * One option set for every SDK-built resolver chain — the server's ctx.resolver and automatic
18
+ * SBSD solve, and the CLI helper contexts — so they share allowed hosts, cache, identity scope,
19
+ * proxy intent, and the telemetry sink. `identityScope` is server-owned; CLI contexts pass none.
20
+ */
21
+ export function createResolverRuntimeOptions(
22
+ provider: ProviderDefinition,
23
+ cache: ProviderCache,
24
+ identityScope: string | undefined,
25
+ proxyPolicy: ProviderProxyPolicy | undefined,
26
+ proxyClientOptions: Omit<
27
+ NonNullable<ResolverRuntimeOptions["proxyIntent"]>,
28
+ "mode" | "userAgent"
29
+ >,
30
+ stealthProfile: { readonly userAgent: string } | undefined,
31
+ telemetry: ResolverTelemetrySink,
32
+ browserTelemetry?: BrowserTelemetrySink,
33
+ ): ResolverRuntimeOptions {
34
+ return {
35
+ allowedHosts: provider.allowedHosts,
36
+ cache,
37
+ telemetry,
38
+ browserTelemetry,
39
+ ...(identityScope === undefined ? {} : { identityScope }),
40
+ ...(proxyPolicy
41
+ ? {
42
+ proxyIntent: {
43
+ mode: proxyPolicy.mode,
44
+ ...proxyClientOptions,
45
+ ...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
46
+ },
47
+ }
48
+ : {}),
49
+ };
50
+ }
@@ -2,8 +2,12 @@ import { ProviderError } from "../errors.js";
2
2
  import type { ChallengeSolution, ProviderChallenge, ResolverContext } from "../types.js";
3
3
  import type { TraceRecorder } from "./trace.js";
4
4
  import type { ResolverTelemetryOutcome, ResolverTelemetrySink } from "./resolver-telemetry.js";
5
+ import type { BrowserTelemetrySink } from "./browser-telemetry.js";
5
6
 
6
- type ResolverTelemetryBinding = (sink: ResolverTelemetrySink) => ResolverContext;
7
+ type ResolverTelemetryBinding = (
8
+ sink: ResolverTelemetrySink,
9
+ browserSink?: BrowserTelemetrySink,
10
+ ) => ResolverContext;
7
11
  const telemetryBindings = new WeakMap<ResolverContext, ResolverTelemetryBinding>();
8
12
 
9
13
  /** Internal contract: reconstruct a chain with a new request sink, without mutating the host chain. */
@@ -27,6 +31,7 @@ export function inheritResolverTelemetryBinding(
27
31
  export function bindResolverTelemetry(
28
32
  resolver: ResolverContext,
29
33
  sink: ResolverTelemetrySink | undefined,
34
+ browserSink?: BrowserTelemetrySink,
30
35
  ): ResolverContext {
31
36
  if (!sink) return resolver;
32
37
  const metadata = (
@@ -34,9 +39,9 @@ export function bindResolverTelemetry(
34
39
  readonly [RESOLVER_INSTRUMENTATION_METADATA]?: { readonly target: ResolverContext };
35
40
  }
36
41
  )[RESOLVER_INSTRUMENTATION_METADATA];
37
- if (metadata) return bindResolverTelemetry(metadata.target, sink);
42
+ if (metadata) return bindResolverTelemetry(metadata.target, sink, browserSink);
38
43
  const bind = telemetryBindings.get(resolver);
39
- if (bind) return bind(sink);
44
+ if (bind) return bind(sink, browserSink);
40
45
  const wrapper: ResolverSolveWithRecorder = {
41
46
  async solve(challenge, signal, recorder) {
42
47
  const startedAt = Date.now();
@@ -74,7 +79,7 @@ export function bindResolverTelemetry(
74
79
  }
75
80
 
76
81
  function createOpaqueResolverTelemetryBinding(resolver: ResolverContext): ResolverTelemetryBinding {
77
- return (sink) => bindResolverTelemetry(resolver, sink);
82
+ return (sink, browserSink) => bindResolverTelemetry(resolver, sink, browserSink);
78
83
  }
79
84
 
80
85
  export const RESOLVER_INSTRUMENTATION_METADATA = Symbol.for(
@@ -147,7 +147,7 @@ function boundedInteger(value: number): number {
147
147
  return Math.max(0, Math.floor(Number.isFinite(value) ? value : 0));
148
148
  }
149
149
 
150
- function boundedText(value: string, redact?: (text: string) => string): string {
150
+ export function boundedText(value: string, redact?: (text: string) => string): string {
151
151
  let result = value;
152
152
  try {
153
153
  result = redact?.(value) ?? value;