@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
@@ -29,7 +29,10 @@ import {
29
29
  import { createBrowserResolverVendorAdapter } from "./resolver-vendors/browser.js";
30
30
  import { createCapsolverResolverVendorAdapter } from "./resolver-vendors/capsolver.js";
31
31
  import { createHypersolutionsResolverVendorAdapter } from "./resolver-vendors/hypersolutions.js";
32
- import { assertResolverHostAllowed } from "./resolver-vendors/hosts.js";
32
+ import {
33
+ assertResolverHostAllowed,
34
+ assertResolverVendorTransportHosts,
35
+ } from "./resolver-vendors/hosts.js";
33
36
  import { createTwoCaptchaResolverVendorAdapter } from "./resolver-vendors/twocaptcha.js";
34
37
  import {
35
38
  RESOLVER_VENDOR_CAPABILITIES,
@@ -63,6 +66,7 @@ import {
63
66
  } from "./resolver-config.js";
64
67
  import { DEFAULT_STEALTH_PROFILE } from "./stealth.js";
65
68
  import type { TraceRecorder } from "./trace.js";
69
+ import type { BrowserTelemetrySink } from "./browser-telemetry.js";
66
70
  import type {
67
71
  ResolverTelemetryErrorClass,
68
72
  ResolverTelemetryPhase,
@@ -127,6 +131,8 @@ export interface ResolverRuntimeOptions {
127
131
  readonly cache?: ProviderCache;
128
132
  /** Optional request-scoped observability sink; never used as resolver identity. */
129
133
  readonly telemetry?: ResolverTelemetrySink;
134
+ /** Request-scoped browser sink used by the resolver-owned browser vendor. */
135
+ readonly browserTelemetry?: BrowserTelemetrySink;
130
136
  /** Inputs for SDK-owned lazy proxy resolution. The SDK never accepts a caller-built identity. */
131
137
  readonly proxyIntent?: {
132
138
  readonly mode: ProviderProxyMode;
@@ -222,6 +228,7 @@ export type ResolverAdapterFactory = (
222
228
  configuration: string | undefined,
223
229
  timeoutMs: number,
224
230
  allowedHosts: readonly string[],
231
+ telemetry?: BrowserTelemetrySink,
225
232
  ) => ResolverVendorAdapter;
226
233
 
227
234
  const resolverAdapterRegistry: Partial<Record<ProviderResolverVendor, ResolverAdapterFactory>> = {
@@ -242,11 +249,12 @@ const resolverAdapterRegistry: Partial<Record<ProviderResolverVendor, ResolverAd
242
249
  timeoutMs,
243
250
  });
244
251
  },
245
- browser(configuration, timeoutMs, allowedHosts) {
252
+ browser(configuration, timeoutMs, allowedHosts, telemetry) {
246
253
  return createBrowserResolverVendorAdapter({
247
254
  allowedHosts,
248
255
  cdpUrl: configuration,
249
256
  timeoutMs,
257
+ telemetry,
250
258
  });
251
259
  },
252
260
  hypersolutions(configuration, timeoutMs, allowedHosts) {
@@ -306,7 +314,7 @@ const KNOWN_UNIMPLEMENTED_RESOLVER_VENDORS: ReadonlySet<ProviderResolverVendor>
306
314
  type ResolverChainEntry = {
307
315
  readonly id: ProviderResolverVendor;
308
316
  supports(kind: ProviderChallengeKind): boolean;
309
- createAdapter(): ResolverVendorAdapter;
317
+ createAdapter(browserTelemetry?: BrowserTelemetrySink): ResolverVendorAdapter;
310
318
  };
311
319
 
312
320
  function normalizedEnvValue(env: EnvLike, key: string): string | undefined {
@@ -365,6 +373,7 @@ function createAdapter(
365
373
  timeoutMs: number,
366
374
  allowedHosts: readonly string[],
367
375
  adapterFactories: Partial<Readonly<Record<ProviderResolverVendor, ResolverAdapterFactory>>>,
376
+ browserTelemetry?: BrowserTelemetrySink,
368
377
  ): ResolverVendorAdapter {
369
378
  const factory = adapterFactories[vendor.vendor];
370
379
  if (!factory && !KNOWN_UNIMPLEMENTED_RESOLVER_VENDORS.has(vendor.vendor)) {
@@ -377,7 +386,7 @@ function createAdapter(
377
386
  }
378
387
 
379
388
  if (factory) {
380
- return factory(vendor.configuration, timeoutMs, allowedHosts);
389
+ return factory(vendor.configuration, timeoutMs, allowedHosts, browserTelemetry);
381
390
  }
382
391
  return createUnavailableAdapter(vendor.vendor, "not_implemented");
383
392
  }
@@ -480,15 +489,26 @@ function sanitizeCauseMessage(message: string): string {
480
489
  .slice(0, 512);
481
490
  }
482
491
 
492
+ function assertResolverTransportLive(revoked: boolean): void {
493
+ if (!revoked) return;
494
+ throw new ProviderError("Resolver transport was revoked when the vendor solve settled", {
495
+ code: "RESOLVER_TRANSPORT_REVOKED",
496
+ fix: "Complete every bound-transport call before solve() settles; the transport is per-attempt and must not be retained.",
497
+ });
498
+ }
499
+
483
500
  function restrictResolverTransport(
484
501
  transport: ResolverVendorTransport,
485
502
  allowedHosts: readonly string[],
486
- ): ResolverVendorTransport {
487
- return {
503
+ ): { readonly transport: ResolverVendorTransport; revoke(): void } {
504
+ // A retained reference must fail closed once the adapter's solve has settled.
505
+ let revoked = false;
506
+ const restricted: ResolverVendorTransport = {
488
507
  sessionHeaders: transport.sessionHeaders,
489
508
  ...(transport.getCookie
490
509
  ? {
491
510
  getCookie(name, url) {
511
+ assertResolverTransportLive(revoked);
492
512
  // Jar reads are gated like fetches: an adapter may only read state for declared hosts.
493
513
  assertResolverHostAllowed(url, allowedHosts);
494
514
  return transport.getCookie?.(name, url);
@@ -496,6 +516,7 @@ function restrictResolverTransport(
496
516
  }
497
517
  : {}),
498
518
  async fetch(url, init) {
519
+ assertResolverTransportLive(revoked);
499
520
  // Empty declarations remain deny-by-default, matching the adapter-factory/browser path.
500
521
  assertResolverHostAllowed(url, allowedHosts);
501
522
  const response = await transport.fetch(url, { ...init, redirect: "manual" });
@@ -511,6 +532,12 @@ function restrictResolverTransport(
511
532
  return response;
512
533
  },
513
534
  };
535
+ return {
536
+ transport: restricted,
537
+ revoke() {
538
+ revoked = true;
539
+ },
540
+ };
514
541
  }
515
542
 
516
543
  function safeCause(error: ResolverVendorUnavailableError): ResolverChainAttempt["cause"] {
@@ -1042,7 +1069,8 @@ async function resolveResolverIdentity(
1042
1069
  }
1043
1070
 
1044
1071
  function createChainTelemetryBinding(options: Parameters<typeof createResolverChainClient>[0]) {
1045
- return (telemetry: ResolverTelemetrySink) => createResolverChainClient({ ...options, telemetry });
1072
+ return (telemetry: ResolverTelemetrySink, browserTelemetry?: BrowserTelemetrySink) =>
1073
+ createResolverChainClient({ ...options, telemetry, browserTelemetry });
1046
1074
  }
1047
1075
 
1048
1076
  function createResolverChainClient(options: {
@@ -1059,6 +1087,7 @@ function createResolverChainClient(options: {
1059
1087
  readonly clientProfile?: string;
1060
1088
  readonly allowedHosts?: readonly string[];
1061
1089
  readonly telemetry?: ResolverTelemetrySink;
1090
+ readonly browserTelemetry?: BrowserTelemetrySink;
1062
1091
  }): ResolverChainClient {
1063
1092
  assertClientProfileTransportContract(options.clientProfile, options.transport);
1064
1093
  const cache =
@@ -1167,7 +1196,7 @@ function createResolverChainClient(options: {
1167
1196
  }
1168
1197
  const attempts: ResolverChainAttempt[] = [];
1169
1198
  for (const [entryIndex, entry] of supportingEntries.entries()) {
1170
- const adapter = entry.createAdapter();
1199
+ const adapter = entry.createAdapter(options.browserTelemetry);
1171
1200
  const attemptStartedAt = Date.now();
1172
1201
  const diagnostics: ResolverAttemptDiagnostics = {};
1173
1202
  const telemetryTraceRecorder = createResolverTelemetryTraceRecorder({
@@ -1179,7 +1208,11 @@ function createResolverChainClient(options: {
1179
1208
  });
1180
1209
  let solution: ChallengeSolution;
1181
1210
  try {
1182
- const solveAttempt = () => {
1211
+ const solveAttempt = async () => {
1212
+ // Boundary first: an overreaching declaration is a caller fault even when no
1213
+ // transport is configured, not a "missing_transport" vendor unavailability.
1214
+ const adapterHosts = adapter.transportAllowedHosts ?? [];
1215
+ assertResolverVendorTransportHosts(adapter.id, adapterHosts);
1183
1216
  const requiresTransport = adapterRequiresTransport(adapter, challenge.kind);
1184
1217
  const unrestrictedTransport =
1185
1218
  options.transport ??
@@ -1192,10 +1225,10 @@ function createResolverChainClient(options: {
1192
1225
  if (requiresTransport && unrestrictedTransport === undefined) {
1193
1226
  throw new ResolverVendorUnavailableError(adapter.id, "missing_transport");
1194
1227
  }
1195
- const transport = unrestrictedTransport
1228
+ const boundTransport = unrestrictedTransport
1196
1229
  ? restrictResolverTransport(unrestrictedTransport, [
1197
1230
  ...(options.allowedHosts ?? []),
1198
- ...(adapter.transportAllowedHosts ?? []),
1231
+ ...adapterHosts,
1199
1232
  ])
1200
1233
  : undefined;
1201
1234
  const usage: ResolverPaidUsageContext = {
@@ -1209,14 +1242,18 @@ function createResolverChainClient(options: {
1209
1242
  }
1210
1243
  : {}),
1211
1244
  };
1212
- return adapter.solve(
1213
- challenge,
1214
- identity,
1215
- signal,
1216
- telemetryTraceRecorder,
1217
- transport,
1218
- usage,
1219
- );
1245
+ try {
1246
+ return await adapter.solve(
1247
+ challenge,
1248
+ identity,
1249
+ signal,
1250
+ telemetryTraceRecorder,
1251
+ boundTransport?.transport,
1252
+ usage,
1253
+ );
1254
+ } finally {
1255
+ boundTransport?.revoke();
1256
+ }
1220
1257
  };
1221
1258
  solution = traceRecorder
1222
1259
  ? await traceRecorder.runSpan("resolver.vendor.attempt", solveAttempt, {
@@ -1334,7 +1371,7 @@ function createResolverChainClient(options: {
1334
1371
  },
1335
1372
  };
1336
1373
  resolverCaches.set(client, cache ?? null);
1337
- const reusableOptions = { ...options, telemetry: undefined };
1374
+ const reusableOptions = { ...options, telemetry: undefined, browserTelemetry: undefined };
1338
1375
  registerResolverTelemetryBinding(client, createChainTelemetryBinding(reusableOptions));
1339
1376
  return client;
1340
1377
  }
@@ -1352,6 +1389,10 @@ export function createResolverClient(options: {
1352
1389
  readonly allowedHosts?: readonly string[];
1353
1390
  readonly telemetry?: ResolverTelemetrySink;
1354
1391
  }): ResolverChainClient {
1392
+ for (const adapter of options.adapters) {
1393
+ // Fail at construction, not on the first solve, when a supplied adapter oversteps its vendor.
1394
+ assertResolverVendorTransportHosts(adapter.id, adapter.transportAllowedHosts ?? []);
1395
+ }
1355
1396
  return createResolverChainClient({
1356
1397
  kinds: options.kinds,
1357
1398
  entries: options.adapters.map((adapter) => ({
@@ -1459,12 +1500,13 @@ function createResolverClientFromEnvInternal(
1459
1500
  return {
1460
1501
  id: vendor,
1461
1502
  supports: (kind: ProviderChallengeKind) => resolverVendorSupports(vendor, kind),
1462
- createAdapter: () =>
1503
+ createAdapter: (browserTelemetry) =>
1463
1504
  createAdapter(
1464
1505
  resolveVendorAvailability(vendor, env),
1465
1506
  timeoutMs,
1466
1507
  allowedHosts,
1467
1508
  adapterFactories,
1509
+ browserTelemetry,
1468
1510
  ),
1469
1511
  };
1470
1512
  }),
@@ -1477,6 +1519,7 @@ function createResolverClientFromEnvInternal(
1477
1519
  clientProfile: config.clientProfile,
1478
1520
  allowedHosts,
1479
1521
  telemetry: options.telemetry,
1522
+ browserTelemetry: options.browserTelemetry,
1480
1523
  });
1481
1524
  }
1482
1525
 
@@ -0,0 +1,298 @@
1
+ import type {
2
+ ProviderRuntimeState,
3
+ ProviderStateNamespace,
4
+ StateNamespaceOptions,
5
+ } from "../types.js";
6
+ import { createTelemetryObserver } from "./http-telemetry-guard.js";
7
+ import {
8
+ type ClosedEnum,
9
+ closedEnum,
10
+ type SpanIndex,
11
+ type TelemetryContributor,
12
+ } from "./request-telemetry.js";
13
+ import { getStateViolation, isStateRedisNoop } from "./state.js";
14
+
15
+ export type StateTelemetryOperation =
16
+ | "list"
17
+ | "get"
18
+ | "set"
19
+ | "patch"
20
+ | "delete"
21
+ | "cas"
22
+ | "increment";
23
+ export type StateTelemetryBackend = "redis" | "memory" | "unsupported";
24
+ export type StateTelemetryViolation = "quota" | "ttl" | "size" | "unsupported" | "error";
25
+
26
+ export type StateTelemetrySample = {
27
+ key: string;
28
+ op: StateTelemetryOperation;
29
+ ms: number;
30
+ backend: StateTelemetryBackend;
31
+ };
32
+ export type StateTelemetryLogPayload = {
33
+ telemetryFailed?: true;
34
+ ops: {
35
+ list: number;
36
+ get: number;
37
+ set: number;
38
+ patch: number;
39
+ delete: number;
40
+ cas: number;
41
+ increment: number;
42
+ };
43
+ casConflicts: number;
44
+ violations: { quota: number; ttl: number; size: number; unsupported: number; error: number };
45
+ backend: ClosedEnum<StateTelemetryBackend>;
46
+ /** State fails closed on Redis errors, so this remains 0 until a fallback exists. */
47
+ redisFallbacks: number;
48
+ redisMode: ClosedEnum<"not_configured" | "configured" | "degraded">;
49
+ /** Successful state operations that touch Redis; each operation counts once, regardless of command count. */
50
+ redisRoundTrips: number;
51
+ ms: number;
52
+ samples: StateTelemetrySample[];
53
+ dropped: number;
54
+ };
55
+ export type StateTelemetryHeaderPayload = Omit<
56
+ StateTelemetryLogPayload,
57
+ "samples" | "telemetryFailed"
58
+ >;
59
+
60
+ export interface StateTelemetrySink {
61
+ markTelemetryFailed?(): void;
62
+ setRedisConfigured?(configured: boolean): void;
63
+ record(event: {
64
+ op: StateTelemetryOperation;
65
+ key?: string;
66
+ ms: number;
67
+ backend: StateTelemetryBackend;
68
+ ok: boolean;
69
+ conflict?: boolean;
70
+ violation?: StateTelemetryViolation;
71
+ /** False for successful Redis-backed operations that execute no Redis commands. */
72
+ redisTouched?: boolean;
73
+ }): void;
74
+ }
75
+
76
+ const MAX_SAMPLES = 24;
77
+ const MAX_TEXT = 300;
78
+ const STATE_TELEMETRY_INSTRUMENTED = Symbol.for(
79
+ "@apifuse/provider-sdk/state-telemetry-instrumented",
80
+ );
81
+ function integer(value: number): number {
82
+ return Number.isFinite(value)
83
+ ? Math.min(Number.MAX_SAFE_INTEGER, Math.max(0, Math.floor(value)))
84
+ : Number.MAX_SAFE_INTEGER;
85
+ }
86
+ function add(a: number, b: number): number {
87
+ return Math.min(Number.MAX_SAFE_INTEGER, integer(a) + integer(b));
88
+ }
89
+ function keyText(value: string | undefined, redact?: (text: string) => string): string | undefined {
90
+ if (value === undefined) return undefined;
91
+ let result: string;
92
+ try {
93
+ result = redact?.(value) ?? value;
94
+ } catch {
95
+ result = "[REDACTION_FAILED]";
96
+ }
97
+ const length = Math.min(result.length, MAX_TEXT);
98
+ const units = new Uint16Array(length);
99
+ for (let index = 0; index < length; index += 1) units[index] = result.charCodeAt(index);
100
+ return String.fromCharCode(...units);
101
+ }
102
+
103
+ export class StateTelemetryCollector
104
+ implements
105
+ TelemetryContributor<StateTelemetryLogPayload, StateTelemetryHeaderPayload>,
106
+ StateTelemetrySink
107
+ {
108
+ readonly key = "state" as const;
109
+ readonly #redact?: (text: string) => string;
110
+ #telemetryFailed = false;
111
+ #ops = { list: 0, get: 0, set: 0, patch: 0, delete: 0, cas: 0, increment: 0 };
112
+ #conflicts = 0;
113
+ #violations = { quota: 0, ttl: 0, size: 0, unsupported: 0, error: 0 };
114
+ #backend: StateTelemetryBackend = "unsupported";
115
+ #redisFallbacks = 0;
116
+ #configured: "not_configured" | "configured" | "degraded" = "not_configured";
117
+ #redisRoundTrips = 0;
118
+ #ms = 0;
119
+ #samples: StateTelemetrySample[] = [];
120
+ #dropped = 0;
121
+ constructor(options: { redact?: (text: string) => string } = {}) {
122
+ this.#redact = options.redact;
123
+ }
124
+ markTelemetryFailed(): void {
125
+ this.#telemetryFailed = true;
126
+ }
127
+ setRedisConfigured(configured: boolean): void {
128
+ if (configured && this.#configured === "not_configured") this.#configured = "configured";
129
+ }
130
+ record(event: {
131
+ op: StateTelemetryOperation;
132
+ key?: string;
133
+ ms: number;
134
+ backend: StateTelemetryBackend;
135
+ ok: boolean;
136
+ conflict?: boolean;
137
+ violation?: StateTelemetryViolation;
138
+ /** False for successful Redis-backed operations that execute no Redis commands. */
139
+ redisTouched?: boolean;
140
+ }): void {
141
+ if (
142
+ !["list", "get", "set", "patch", "delete", "cas", "increment"].includes(event.op) ||
143
+ !["redis", "memory", "unsupported"].includes(event.backend) ||
144
+ (event.violation !== undefined &&
145
+ !["quota", "ttl", "size", "unsupported", "error"].includes(event.violation))
146
+ ) {
147
+ this.#telemetryFailed = true;
148
+ return;
149
+ }
150
+ this.#ops[event.op] = add(this.#ops[event.op], 1);
151
+ this.#backend = event.backend;
152
+ if (event.backend === "redis" && this.#configured === "not_configured")
153
+ this.#configured = "configured";
154
+ if (event.backend === "redis" && event.ok && event.redisTouched !== false)
155
+ this.#redisRoundTrips = add(this.#redisRoundTrips, 1);
156
+ if (event.backend === "redis" && !event.ok) this.#configured = "degraded";
157
+ this.#ms = add(this.#ms, event.ms);
158
+ if (event.conflict) this.#conflicts = add(this.#conflicts, 1);
159
+ if (event.violation)
160
+ this.#violations[event.violation] = add(this.#violations[event.violation], 1);
161
+ if (this.#samples.length < MAX_SAMPLES)
162
+ this.#samples.push({
163
+ key: keyText(event.key, this.#redact) ?? "",
164
+ op: event.op,
165
+ ms: integer(event.ms),
166
+ backend: event.backend,
167
+ });
168
+ else this.#dropped = add(this.#dropped, 1);
169
+ }
170
+ toLogPayload(_spans: SpanIndex): StateTelemetryLogPayload | undefined {
171
+ if (Object.values(this.#ops).every((value) => value === 0) && !this.#telemetryFailed)
172
+ return undefined;
173
+ return {
174
+ ...(this.#telemetryFailed ? { telemetryFailed: true as const } : {}),
175
+ ops: { ...this.#ops },
176
+ casConflicts: this.#conflicts,
177
+ violations: { ...this.#violations },
178
+ backend: closedEnum(this.#backend),
179
+ redisFallbacks: this.#redisFallbacks,
180
+ redisMode: closedEnum(this.#configured),
181
+ redisRoundTrips: this.#redisRoundTrips,
182
+ ms: this.#ms,
183
+ samples: this.#samples.map((sample) => ({ ...sample })),
184
+ dropped: this.#dropped,
185
+ };
186
+ }
187
+ toHeaderPayload(log: StateTelemetryLogPayload): StateTelemetryHeaderPayload {
188
+ return {
189
+ ops: log.ops,
190
+ casConflicts: log.casConflicts,
191
+ violations: log.violations,
192
+ backend: log.backend,
193
+ redisFallbacks: log.redisFallbacks,
194
+ redisMode: log.redisMode,
195
+ redisRoundTrips: log.redisRoundTrips,
196
+ ms: log.ms,
197
+ dropped: log.dropped,
198
+ };
199
+ }
200
+ }
201
+
202
+ function backendOf(state: ProviderRuntimeState): StateTelemetryBackend {
203
+ try {
204
+ const name = state.constructor?.name;
205
+ return name?.includes("Redis") ? "redis" : name?.includes("Memory") ? "memory" : "unsupported";
206
+ } catch {
207
+ return "unsupported";
208
+ }
209
+ }
210
+
211
+ function violationOf(error: unknown): StateTelemetryViolation {
212
+ return getStateViolation(error) ?? "error";
213
+ }
214
+
215
+ function wrapNamespace(
216
+ namespace: ProviderStateNamespace,
217
+ sink: StateTelemetrySink,
218
+ backend: StateTelemetryBackend,
219
+ ): ProviderStateNamespace {
220
+ // Compare-and-set records one event per attempt; a three-attempt loop therefore
221
+ // reports cas: 3 and casConflicts: 2 when the first two attempts conflict.
222
+ const observe = createTelemetryObserver(sink);
223
+ return new Proxy(namespace, {
224
+ get(target, property, receiver) {
225
+ const value = Reflect.get(target, property, receiver);
226
+ if (typeof value !== "function" || property === "constructor") return value;
227
+ const op =
228
+ property === "compareAndSet" ? "cas" : (String(property) as StateTelemetryOperation);
229
+ if (!(op in { list: 1, get: 1, set: 1, patch: 1, delete: 1, cas: 1, increment: 1 }))
230
+ return value;
231
+ return async (...args: unknown[]) => {
232
+ const started = Date.now();
233
+ const key = typeof args[0] === "string" ? args[0] : undefined;
234
+ try {
235
+ const result = await Reflect.apply(value, target, args);
236
+ observe(() =>
237
+ sink.record({
238
+ op,
239
+ key,
240
+ ms: Date.now() - started,
241
+ backend,
242
+ ok: true,
243
+ redisTouched: !isStateRedisNoop(result),
244
+ conflict:
245
+ op === "cas" &&
246
+ !!result &&
247
+ typeof result === "object" &&
248
+ "ok" in result &&
249
+ result.ok === false,
250
+ }),
251
+ );
252
+ return result;
253
+ } catch (error) {
254
+ observe(() =>
255
+ sink.record({
256
+ op,
257
+ key,
258
+ ms: Date.now() - started,
259
+ backend,
260
+ ok: false,
261
+ violation: violationOf(error),
262
+ }),
263
+ );
264
+ throw error;
265
+ }
266
+ };
267
+ },
268
+ }) as ProviderStateNamespace;
269
+ }
270
+
271
+ export function instrumentProviderRuntimeState(
272
+ state: ProviderRuntimeState,
273
+ sink: StateTelemetrySink,
274
+ redact?: (text: string) => string,
275
+ ): ProviderRuntimeState {
276
+ if (
277
+ (state as ProviderRuntimeState & { [STATE_TELEMETRY_INSTRUMENTED]?: boolean })[
278
+ STATE_TELEMETRY_INSTRUMENTED
279
+ ]
280
+ )
281
+ return state;
282
+ const backend = backendOf(state);
283
+ const observe = createTelemetryObserver(sink);
284
+ observe(() => sink.setRedisConfigured?.(backend === "redis"));
285
+ return new Proxy(state, {
286
+ get(target, property, receiver) {
287
+ if (property === STATE_TELEMETRY_INSTRUMENTED) return true;
288
+ const value = Reflect.get(target, property, receiver);
289
+ if (property === "forConnection" && typeof value === "function")
290
+ return (id: string | undefined) =>
291
+ instrumentProviderRuntimeState(value.call(target, id), sink, redact);
292
+ if (property === "namespace" && typeof value === "function")
293
+ return (name: string, options: StateNamespaceOptions) =>
294
+ wrapNamespace(value.call(target, name, options), sink, backend);
295
+ return value;
296
+ },
297
+ }) as ProviderRuntimeState;
298
+ }