@apifuse/provider-sdk 2.2.0-beta.7 → 2.2.0-beta.70

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 (478) hide show
  1. package/AUTHORING.md +943 -51
  2. package/CHANGELOG.md +273 -1
  3. package/LICENSE +21 -0
  4. package/README.md +117 -29
  5. package/SUBMISSION.md +3 -2
  6. package/bin/apifuse-check.ts +293 -13
  7. package/bin/apifuse-dev.ts +42 -19
  8. package/bin/apifuse-migrate-deployment.ts +200 -0
  9. package/bin/apifuse-migrate-operation-declaration.ts +55 -0
  10. package/bin/apifuse-migrate-shape.ts +202 -0
  11. package/bin/apifuse-pack-check.ts +19 -6
  12. package/bin/apifuse-pack-smoke.ts +57 -2
  13. package/bin/apifuse-pack-types.ts +736 -38
  14. package/bin/apifuse-perf.ts +59 -23
  15. package/bin/apifuse-record.ts +923 -94
  16. package/bin/apifuse-submit-check.ts +2313 -281
  17. package/bin/apifuse-test.ts +3 -1
  18. package/bin/submit-check-delimited-text.ts +50 -0
  19. package/dist/auth-turn/index.d.ts +3 -3
  20. package/dist/auth-turn/index.js +1 -1
  21. package/dist/auth.d.ts +14 -0
  22. package/dist/auth.js +67 -0
  23. package/dist/ceremonies/index.d.ts +16 -0
  24. package/dist/ceremonies/index.js +141 -36
  25. package/dist/cli/commands.d.ts +1 -1
  26. package/dist/cli/commands.js +30 -0
  27. package/dist/cli/create.d.ts +1 -1
  28. package/dist/cli/create.js +8 -2
  29. package/dist/cli/deployment-intent.d.ts +178 -0
  30. package/dist/cli/deployment-intent.js +277 -0
  31. package/dist/cli/local-connection.d.ts +111 -0
  32. package/dist/cli/local-connection.js +151 -0
  33. package/dist/cli/migrate-deployment-intent.d.ts +132 -0
  34. package/dist/cli/migrate-deployment-intent.js +1552 -0
  35. package/dist/cli/migrate-operation-declaration.d.ts +70 -0
  36. package/dist/cli/migrate-operation-declaration.js +2206 -0
  37. package/dist/cli/migrate-operation-shape.d.ts +44 -0
  38. package/dist/cli/migrate-operation-shape.js +113 -0
  39. package/dist/cli/migrate-provider-shape.d.ts +56 -0
  40. package/dist/cli/migrate-provider-shape.js +580 -0
  41. package/dist/cli/resolver-runtime.d.ts +21 -0
  42. package/dist/cli/resolver-runtime.js +31 -0
  43. package/dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +141 -23
  44. package/dist/cli/templates/provider/Dockerfile.tpl +1 -1
  45. package/dist/cli/templates/provider/README.md.tpl +41 -19
  46. package/dist/cli/templates/provider/index.ts.tpl +6 -3
  47. package/dist/cli/templates/provider/operations/ping.ts.tpl +4 -1
  48. package/dist/cli/templates/provider/provider.json.tpl +6 -0
  49. package/dist/config/loader.d.ts +202 -16
  50. package/dist/config/loader.js +491 -141
  51. package/dist/contract-serialization.js +4 -8
  52. package/dist/contract-types.d.ts +12 -5
  53. package/dist/contract.js +24 -10
  54. package/dist/declaration-validation.d.ts +34 -0
  55. package/dist/declaration-validation.js +305 -0
  56. package/dist/define.d.ts +71 -29
  57. package/dist/define.js +807 -69
  58. package/dist/dev.d.ts +5 -0
  59. package/dist/dev.js +8 -1
  60. package/dist/engine.d.ts +129 -0
  61. package/dist/engine.js +209 -0
  62. package/dist/error-observability.d.ts +7 -0
  63. package/dist/error-observability.js +61 -0
  64. package/dist/error-resolution.d.ts +18 -0
  65. package/dist/error-resolution.js +243 -0
  66. package/dist/errors.d.ts +74 -0
  67. package/dist/errors.js +99 -0
  68. package/dist/fixture-sanitization.d.ts +28 -0
  69. package/dist/fixture-sanitization.js +371 -0
  70. package/dist/handle-meta.d.ts +90 -0
  71. package/dist/handle-meta.js +73 -0
  72. package/dist/handle.d.ts +226 -0
  73. package/dist/handle.js +345 -0
  74. package/dist/health-scenario.d.ts +1901 -0
  75. package/dist/health-scenario.js +653 -0
  76. package/dist/i18n/catalog.js +21 -1
  77. package/dist/i18n/error-messages.d.ts +86 -0
  78. package/dist/i18n/error-messages.js +192 -0
  79. package/dist/i18n/index.d.ts +1 -0
  80. package/dist/i18n/index.js +1 -0
  81. package/dist/i18n/keys.d.ts +12 -0
  82. package/dist/i18n/keys.js +76 -5
  83. package/dist/i18n/operation-locale-namespace.d.ts +2 -0
  84. package/dist/i18n/operation-locale-namespace.js +7 -0
  85. package/dist/index.d.ts +40 -15
  86. package/dist/index.js +29 -11
  87. package/dist/lint.d.ts +43 -11
  88. package/dist/lint.js +2512 -177
  89. package/dist/native-address.d.ts +43 -0
  90. package/dist/native-address.js +281 -0
  91. package/dist/native-egress-policy.d.ts +31 -0
  92. package/dist/native-egress-policy.js +288 -0
  93. package/dist/observability.d.ts +5 -2
  94. package/dist/observability.js +48 -1
  95. package/dist/provider.d.ts +18 -8
  96. package/dist/provider.js +11 -6
  97. package/dist/runtime/auth-flow.d.ts +6 -1
  98. package/dist/runtime/auth-flow.js +8 -0
  99. package/dist/runtime/bounded-expiring-map.d.ts +21 -0
  100. package/dist/runtime/bounded-expiring-map.js +64 -0
  101. package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
  102. package/dist/runtime/browser-telemetry-binding.js +83 -0
  103. package/dist/runtime/browser-telemetry.d.ts +76 -0
  104. package/dist/runtime/browser-telemetry.js +165 -0
  105. package/dist/runtime/browser.d.ts +4 -0
  106. package/dist/runtime/browser.js +688 -83
  107. package/dist/runtime/cache-telemetry.d.ts +71 -0
  108. package/dist/runtime/cache-telemetry.js +177 -0
  109. package/dist/runtime/cache.d.ts +3 -0
  110. package/dist/runtime/cache.js +245 -23
  111. package/dist/runtime/capability-response.d.ts +3 -0
  112. package/dist/runtime/capability-response.js +56 -0
  113. package/dist/runtime/capability-telemetry.d.ts +46 -0
  114. package/dist/runtime/capability-telemetry.js +68 -0
  115. package/dist/runtime/chrome149-header-order.d.ts +58 -0
  116. package/dist/runtime/chrome149-header-order.js +289 -0
  117. package/dist/runtime/diagnostic-env-values.d.ts +5 -0
  118. package/dist/runtime/diagnostic-env-values.js +57 -0
  119. package/dist/runtime/diagnostic-env.d.ts +14 -0
  120. package/dist/runtime/diagnostic-env.js +44 -0
  121. package/dist/runtime/diagnostic-redactor.d.ts +54 -0
  122. package/dist/runtime/diagnostic-redactor.js +571 -0
  123. package/dist/runtime/egress-lease.d.ts +65 -0
  124. package/dist/runtime/egress-lease.js +236 -0
  125. package/dist/runtime/engine-mode.d.ts +74 -0
  126. package/dist/runtime/engine-mode.js +152 -0
  127. package/dist/runtime/env.js +14 -1
  128. package/dist/runtime/executor.d.ts +3 -2
  129. package/dist/runtime/executor.js +23 -26
  130. package/dist/runtime/handle-wordlist.d.ts +43 -0
  131. package/dist/runtime/handle-wordlist.js +256 -0
  132. package/dist/runtime/handle.d.ts +71 -0
  133. package/dist/runtime/handle.js +908 -0
  134. package/dist/runtime/http-telemetry-binding.d.ts +10 -0
  135. package/dist/runtime/http-telemetry-binding.js +152 -0
  136. package/dist/runtime/http-telemetry-guard.d.ts +16 -0
  137. package/dist/runtime/http-telemetry-guard.js +202 -0
  138. package/dist/runtime/http-telemetry.d.ts +104 -0
  139. package/dist/runtime/http-telemetry.js +190 -0
  140. package/dist/runtime/http.d.ts +34 -0
  141. package/dist/runtime/http.js +680 -121
  142. package/dist/runtime/insights.js +2 -2
  143. package/dist/runtime/instrumentation.d.ts +2 -2
  144. package/dist/runtime/instrumentation.js +462 -19
  145. package/dist/runtime/native-network-errors.d.ts +33 -0
  146. package/dist/runtime/native-network-errors.js +69 -0
  147. package/dist/runtime/native-network.d.ts +105 -0
  148. package/dist/runtime/native-network.js +1433 -0
  149. package/dist/runtime/native-telemetry.d.ts +142 -0
  150. package/dist/runtime/native-telemetry.js +231 -0
  151. package/dist/runtime/ocr-telemetry.d.ts +82 -0
  152. package/dist/runtime/ocr-telemetry.js +192 -0
  153. package/dist/runtime/ocr.d.ts +29 -0
  154. package/dist/runtime/ocr.js +468 -0
  155. package/dist/runtime/otlp.d.ts +71 -2
  156. package/dist/runtime/otlp.js +398 -16
  157. package/dist/runtime/provider.d.ts +7 -0
  158. package/dist/runtime/provider.js +14 -0
  159. package/dist/runtime/proxy-errors.js +6 -2
  160. package/dist/runtime/proxy-nodemaven.d.ts +56 -0
  161. package/dist/runtime/proxy-nodemaven.js +147 -0
  162. package/dist/runtime/proxy-retry-policy.d.ts +4 -1
  163. package/dist/runtime/proxy-retry-policy.js +11 -2
  164. package/dist/runtime/proxy-telemetry.d.ts +133 -3
  165. package/dist/runtime/proxy-telemetry.js +218 -70
  166. package/dist/runtime/redirect-hop-headers.d.ts +19 -0
  167. package/dist/runtime/redirect-hop-headers.js +58 -0
  168. package/dist/runtime/redirects.d.ts +61 -0
  169. package/dist/runtime/redirects.js +149 -0
  170. package/dist/runtime/redis.d.ts +1 -1
  171. package/dist/runtime/redis.js +4 -2
  172. package/dist/runtime/request-options.d.ts +75 -1
  173. package/dist/runtime/request-options.js +588 -0
  174. package/dist/runtime/request-telemetry.d.ts +70 -0
  175. package/dist/runtime/request-telemetry.js +229 -0
  176. package/dist/runtime/resolver-config.d.ts +13 -0
  177. package/dist/runtime/resolver-config.js +14 -0
  178. package/dist/runtime/resolver-public.d.ts +3 -0
  179. package/dist/runtime/resolver-public.js +1 -0
  180. package/dist/runtime/resolver-runtime-options.d.ts +14 -0
  181. package/dist/runtime/resolver-runtime-options.js +33 -0
  182. package/dist/runtime/resolver-shared.d.ts +24 -0
  183. package/dist/runtime/resolver-shared.js +69 -0
  184. package/dist/runtime/resolver-span-attributes.d.ts +34 -0
  185. package/dist/runtime/resolver-span-attributes.js +93 -0
  186. package/dist/runtime/resolver-telemetry.d.ts +135 -0
  187. package/dist/runtime/resolver-telemetry.js +204 -0
  188. package/dist/runtime/resolver-usage.d.ts +32 -0
  189. package/dist/runtime/resolver-usage.js +54 -0
  190. package/dist/runtime/resolver-vendors/bindings.d.ts +53 -0
  191. package/dist/runtime/resolver-vendors/bindings.js +45 -0
  192. package/dist/runtime/resolver-vendors/browser.d.ts +24 -0
  193. package/dist/runtime/resolver-vendors/browser.js +423 -0
  194. package/dist/runtime/resolver-vendors/capsolver.d.ts +22 -0
  195. package/dist/runtime/resolver-vendors/capsolver.js +544 -0
  196. package/dist/runtime/resolver-vendors/hosts.d.ts +23 -0
  197. package/dist/runtime/resolver-vendors/hosts.js +68 -0
  198. package/dist/runtime/resolver-vendors/hypersolutions.d.ts +27 -0
  199. package/dist/runtime/resolver-vendors/hypersolutions.js +369 -0
  200. package/dist/runtime/resolver-vendors/twocaptcha.d.ts +24 -0
  201. package/dist/runtime/resolver-vendors/twocaptcha.js +425 -0
  202. package/dist/runtime/resolver-vendors/types.d.ts +125 -0
  203. package/dist/runtime/resolver-vendors/types.js +106 -0
  204. package/dist/runtime/resolver.d.ts +79 -0
  205. package/dist/runtime/resolver.js +1094 -0
  206. package/dist/runtime/state-telemetry.d.ts +79 -0
  207. package/dist/runtime/state-telemetry.js +194 -0
  208. package/dist/runtime/state.d.ts +10 -1
  209. package/dist/runtime/state.js +322 -82
  210. package/dist/runtime/stealth-akamai-sbsd.d.ts +54 -0
  211. package/dist/runtime/stealth-akamai-sbsd.js +142 -0
  212. package/dist/runtime/stealth-cookies.d.ts +24 -0
  213. package/dist/runtime/stealth-cookies.js +131 -0
  214. package/dist/runtime/stealth-owned-headers.d.ts +39 -0
  215. package/dist/runtime/stealth-owned-headers.js +57 -0
  216. package/dist/runtime/stealth-telemetry.d.ts +92 -0
  217. package/dist/runtime/stealth-telemetry.js +179 -0
  218. package/dist/runtime/stealth.d.ts +45 -6
  219. package/dist/runtime/stealth.js +1775 -320
  220. package/dist/runtime/stt-telemetry.d.ts +77 -0
  221. package/dist/runtime/stt-telemetry.js +176 -0
  222. package/dist/runtime/stt.js +18 -19
  223. package/dist/runtime/timeout.d.ts +5 -0
  224. package/dist/runtime/timeout.js +12 -0
  225. package/dist/runtime/trace-config.d.ts +12 -0
  226. package/dist/runtime/trace-config.js +62 -0
  227. package/dist/runtime/trace.d.ts +17 -0
  228. package/dist/runtime/trace.js +176 -25
  229. package/dist/runtime-boundary-lint.d.ts +137 -0
  230. package/dist/runtime-boundary-lint.js +590 -0
  231. package/dist/schema.d.ts +20 -0
  232. package/dist/schema.js +72 -2
  233. package/dist/serve.d.ts +1 -1
  234. package/dist/serve.js +1 -1
  235. package/dist/server/error-observability.d.ts +1 -0
  236. package/dist/server/error-observability.js +1 -0
  237. package/dist/server/error-stack-frames.d.ts +35 -0
  238. package/dist/server/error-stack-frames.js +219 -0
  239. package/dist/server/index.d.ts +21 -2
  240. package/dist/server/index.js +11 -2
  241. package/dist/server/runtime-diagnostics.d.ts +165 -0
  242. package/dist/server/runtime-diagnostics.js +360 -0
  243. package/dist/server/sdk-version.d.ts +3 -0
  244. package/dist/server/sdk-version.js +19 -0
  245. package/dist/server/self-test-input-tokens.d.ts +2 -1
  246. package/dist/server/self-test-input-tokens.js +18 -14
  247. package/dist/server/self-test-redaction.js +8 -33
  248. package/dist/server/self-test.d.ts +18 -3
  249. package/dist/server/self-test.js +139 -76
  250. package/dist/server/sensitive-values.d.ts +26 -0
  251. package/dist/server/sensitive-values.js +137 -0
  252. package/dist/server/serve-implementation.d.ts +285 -0
  253. package/dist/server/serve-implementation.js +3172 -0
  254. package/dist/server/serve.d.ts +1 -75
  255. package/dist/server/serve.js +1 -1170
  256. package/dist/server/state-backend-report.d.ts +70 -0
  257. package/dist/server/state-backend-report.js +113 -0
  258. package/dist/server/trace-output.d.ts +7 -0
  259. package/dist/server/trace-output.js +111 -0
  260. package/dist/server/types.d.ts +37 -5
  261. package/dist/server/types.js +34 -1
  262. package/dist/stateful/errors.d.ts +19 -0
  263. package/dist/stateful/errors.js +24 -0
  264. package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
  265. package/dist/stateful/http-provider-event-emitter.js +237 -0
  266. package/dist/stateful/http-session-owner-registry.d.ts +44 -0
  267. package/dist/stateful/http-session-owner-registry.js +210 -0
  268. package/dist/stateful/index.d.ts +18 -0
  269. package/dist/stateful/index.js +18 -0
  270. package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
  271. package/dist/stateful/provider-event-delivery-failures.js +43 -0
  272. package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
  273. package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
  274. package/dist/stateful/provider-event-pipeline.d.ts +50 -0
  275. package/dist/stateful/provider-event-pipeline.js +1 -0
  276. package/dist/stateful/provider-events.d.ts +101 -0
  277. package/dist/stateful/provider-events.js +289 -0
  278. package/dist/stateful/session-key.d.ts +15 -0
  279. package/dist/stateful/session-key.js +86 -0
  280. package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
  281. package/dist/stateful/stateful-provider-adapter-context.js +42 -0
  282. package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
  283. package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
  284. package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
  285. package/dist/stateful/stateful-provider-adapter.js +287 -0
  286. package/dist/stateful/stateful-provider-observability.d.ts +62 -0
  287. package/dist/stateful/stateful-provider-observability.js +161 -0
  288. package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
  289. package/dist/stateful/stateful-provider-owner-forwarder.js +237 -0
  290. package/dist/stateful/stateful-provider-runtime-context.d.ts +35 -0
  291. package/dist/stateful/stateful-provider-runtime-context.js +61 -0
  292. package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
  293. package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
  294. package/dist/stateful/stateful-provider-session-routing.d.ts +67 -0
  295. package/dist/stateful/stateful-provider-session-routing.js +345 -0
  296. package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
  297. package/dist/stateful/stateful-provider-session-runtime.js +245 -0
  298. package/dist/stateful-signing.d.ts +18 -0
  299. package/dist/stateful-signing.js +27 -0
  300. package/dist/stealth/profiles.d.ts +15 -4
  301. package/dist/stealth/profiles.js +169 -211
  302. package/dist/stream-evidence.d.ts +74 -0
  303. package/dist/stream-evidence.js +785 -0
  304. package/dist/stream.js +7 -1
  305. package/dist/testing/index.d.ts +2 -1
  306. package/dist/testing/index.js +2 -1
  307. package/dist/testing/run.d.ts +32 -2
  308. package/dist/testing/run.js +509 -25
  309. package/dist/trace-sanitization.d.ts +6 -0
  310. package/dist/trace-sanitization.js +68 -0
  311. package/dist/types.d.ts +899 -210
  312. package/dist/types.js +1 -0
  313. package/dist/typescript-module.d.ts +39 -0
  314. package/dist/typescript-module.js +72 -0
  315. package/package.json +52 -6
  316. package/src/auth-turn/index.ts +1 -1
  317. package/src/auth.ts +118 -0
  318. package/src/ceremonies/index.ts +189 -46
  319. package/src/cli/commands.ts +37 -0
  320. package/src/cli/create.ts +8 -2
  321. package/src/cli/deployment-intent.ts +364 -0
  322. package/src/cli/local-connection.ts +221 -0
  323. package/src/cli/migrate-deployment-intent.ts +1908 -0
  324. package/src/cli/migrate-operation-declaration.ts +3026 -0
  325. package/src/cli/migrate-operation-shape.ts +184 -0
  326. package/src/cli/migrate-provider-shape.ts +778 -0
  327. package/src/cli/resolver-runtime.ts +72 -0
  328. package/src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +141 -23
  329. package/src/cli/templates/provider/Dockerfile.tpl +1 -1
  330. package/src/cli/templates/provider/README.md.tpl +41 -19
  331. package/src/cli/templates/provider/index.ts.tpl +6 -3
  332. package/src/cli/templates/provider/operations/ping.ts.tpl +4 -1
  333. package/src/cli/templates/provider/provider.json.tpl +6 -0
  334. package/src/config/loader.ts +774 -181
  335. package/src/contract-serialization.ts +5 -7
  336. package/src/contract-types.ts +12 -5
  337. package/src/contract.ts +24 -10
  338. package/src/declaration-validation.ts +376 -0
  339. package/src/define.ts +1105 -131
  340. package/src/dev.ts +13 -1
  341. package/src/engine.ts +390 -0
  342. package/src/error-observability.ts +64 -0
  343. package/src/error-resolution.ts +269 -0
  344. package/src/errors.ts +157 -0
  345. package/src/fixture-sanitization.ts +422 -0
  346. package/src/handle-meta.ts +143 -0
  347. package/src/handle.ts +694 -0
  348. package/src/health-scenario.ts +960 -0
  349. package/src/i18n/catalog.ts +23 -1
  350. package/src/i18n/error-messages.ts +255 -0
  351. package/src/i18n/index.ts +1 -0
  352. package/src/i18n/keys.ts +80 -6
  353. package/src/i18n/operation-locale-namespace.ts +10 -0
  354. package/src/index.ts +390 -23
  355. package/src/lint.ts +2975 -234
  356. package/src/native-address.ts +340 -0
  357. package/src/native-egress-policy.ts +358 -0
  358. package/src/observability.ts +51 -1
  359. package/src/provider.ts +207 -27
  360. package/src/runtime/auth-flow.ts +15 -0
  361. package/src/runtime/bounded-expiring-map.ts +67 -0
  362. package/src/runtime/browser-telemetry-binding.ts +92 -0
  363. package/src/runtime/browser-telemetry.ts +289 -0
  364. package/src/runtime/browser.ts +882 -99
  365. package/src/runtime/cache-telemetry.ts +247 -0
  366. package/src/runtime/cache.ts +258 -22
  367. package/src/runtime/capability-response.ts +53 -0
  368. package/src/runtime/capability-telemetry.ts +101 -0
  369. package/src/runtime/chrome149-header-order.ts +330 -0
  370. package/src/runtime/diagnostic-env-values.ts +56 -0
  371. package/src/runtime/diagnostic-env.ts +65 -0
  372. package/src/runtime/diagnostic-redactor.ts +668 -0
  373. package/src/runtime/egress-lease.ts +341 -0
  374. package/src/runtime/engine-mode.ts +233 -0
  375. package/src/runtime/env.ts +15 -1
  376. package/src/runtime/executor.ts +39 -32
  377. package/src/runtime/handle-wordlist.ts +266 -0
  378. package/src/runtime/handle.ts +1180 -0
  379. package/src/runtime/http-telemetry-binding.ts +162 -0
  380. package/src/runtime/http-telemetry-guard.ts +208 -0
  381. package/src/runtime/http-telemetry.ts +292 -0
  382. package/src/runtime/http.ts +896 -150
  383. package/src/runtime/insights.ts +2 -2
  384. package/src/runtime/instrumentation.ts +628 -28
  385. package/src/runtime/native-network-errors.ts +99 -0
  386. package/src/runtime/native-network.ts +1853 -0
  387. package/src/runtime/native-telemetry.ts +366 -0
  388. package/src/runtime/ocr-telemetry.ts +308 -0
  389. package/src/runtime/ocr.ts +564 -0
  390. package/src/runtime/otlp.ts +468 -21
  391. package/src/runtime/provider.ts +15 -0
  392. package/src/runtime/proxy-errors.ts +12 -4
  393. package/src/runtime/proxy-nodemaven.ts +222 -0
  394. package/src/runtime/proxy-retry-policy.ts +12 -1
  395. package/src/runtime/proxy-telemetry.ts +358 -103
  396. package/src/runtime/redirect-hop-headers.ts +71 -0
  397. package/src/runtime/redirects.ts +215 -0
  398. package/src/runtime/redis.ts +7 -2
  399. package/src/runtime/request-options.ts +728 -1
  400. package/src/runtime/request-telemetry.ts +375 -0
  401. package/src/runtime/resolver-config.ts +18 -0
  402. package/src/runtime/resolver-public.ts +44 -0
  403. package/src/runtime/resolver-runtime-options.ts +50 -0
  404. package/src/runtime/resolver-shared.ts +113 -0
  405. package/src/runtime/resolver-span-attributes.ts +139 -0
  406. package/src/runtime/resolver-telemetry.ts +362 -0
  407. package/src/runtime/resolver-usage.ts +99 -0
  408. package/src/runtime/resolver-vendors/bindings.ts +61 -0
  409. package/src/runtime/resolver-vendors/browser.ts +591 -0
  410. package/src/runtime/resolver-vendors/capsolver.ts +727 -0
  411. package/src/runtime/resolver-vendors/hosts.ts +81 -0
  412. package/src/runtime/resolver-vendors/hypersolutions.ts +480 -0
  413. package/src/runtime/resolver-vendors/twocaptcha.ts +555 -0
  414. package/src/runtime/resolver-vendors/types.ts +261 -0
  415. package/src/runtime/resolver.ts +1543 -0
  416. package/src/runtime/state-telemetry.ts +298 -0
  417. package/src/runtime/state.ts +435 -89
  418. package/src/runtime/stealth-akamai-sbsd.ts +207 -0
  419. package/src/runtime/stealth-cookies.ts +155 -0
  420. package/src/runtime/stealth-owned-headers.ts +67 -0
  421. package/src/runtime/stealth-telemetry.ts +309 -0
  422. package/src/runtime/stealth.ts +2275 -372
  423. package/src/runtime/stt-telemetry.ts +282 -0
  424. package/src/runtime/stt.ts +96 -91
  425. package/src/runtime/timeout.ts +18 -0
  426. package/src/runtime/trace-config.ts +78 -0
  427. package/src/runtime/trace.ts +257 -35
  428. package/src/runtime-boundary-lint.ts +707 -0
  429. package/src/schema.ts +80 -2
  430. package/src/serve.ts +6 -1
  431. package/src/server/error-observability.ts +1 -0
  432. package/src/server/error-stack-frames.ts +214 -0
  433. package/src/server/index.ts +184 -2
  434. package/src/server/runtime-diagnostics.ts +522 -0
  435. package/src/server/sdk-version.ts +22 -0
  436. package/src/server/self-test-input-tokens.ts +29 -14
  437. package/src/server/self-test-redaction.ts +10 -40
  438. package/src/server/self-test.ts +205 -89
  439. package/src/server/sensitive-values.ts +211 -0
  440. package/src/server/serve-implementation.ts +4596 -0
  441. package/src/server/serve.ts +1 -1685
  442. package/src/server/state-backend-report.ts +185 -0
  443. package/src/server/trace-output.ts +189 -0
  444. package/src/server/types.ts +34 -1
  445. package/src/stateful/README.md +146 -0
  446. package/src/stateful/errors.ts +35 -0
  447. package/src/stateful/http-provider-event-emitter.ts +314 -0
  448. package/src/stateful/http-session-owner-registry.ts +306 -0
  449. package/src/stateful/index.ts +18 -0
  450. package/src/stateful/provider-event-delivery-failures.ts +80 -0
  451. package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
  452. package/src/stateful/provider-event-pipeline.ts +61 -0
  453. package/src/stateful/provider-events.ts +462 -0
  454. package/src/stateful/session-key.ts +111 -0
  455. package/src/stateful/stateful-provider-adapter-context.ts +59 -0
  456. package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
  457. package/src/stateful/stateful-provider-adapter.ts +562 -0
  458. package/src/stateful/stateful-provider-observability.ts +261 -0
  459. package/src/stateful/stateful-provider-owner-forwarder.ts +310 -0
  460. package/src/stateful/stateful-provider-runtime-context.ts +96 -0
  461. package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
  462. package/src/stateful/stateful-provider-session-routing.ts +546 -0
  463. package/src/stateful/stateful-provider-session-runtime.ts +403 -0
  464. package/src/stateful-signing.ts +46 -0
  465. package/src/stealth/profiles.ts +212 -231
  466. package/src/stream-evidence.ts +988 -0
  467. package/src/stream.ts +8 -1
  468. package/src/testing/index.ts +10 -1
  469. package/src/testing/run.ts +685 -19
  470. package/src/trace-sanitization.ts +110 -0
  471. package/src/types.ts +1014 -273
  472. package/src/typescript-module.ts +100 -0
  473. package/dist/choice-token.d.ts +0 -24
  474. package/dist/choice-token.js +0 -74
  475. package/dist/runtime/choice.d.ts +0 -15
  476. package/dist/runtime/choice.js +0 -413
  477. package/src/choice-token.ts +0 -165
  478. package/src/runtime/choice.ts +0 -686
@@ -1,1685 +1 @@
1
- import { existsSync } from "node:fs";
2
- import { join } from "node:path";
3
-
4
- import { Hono } from "hono";
5
- import { z } from "zod";
6
- import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
7
- import {
8
- AuthError,
9
- isProviderError,
10
- isSessionExpiredError,
11
- isTransportError,
12
- ProviderError,
13
- } from "../errors.js";
14
- import {
15
- loadProviderLocaleCatalogs,
16
- localizeAuthTurn,
17
- type ProviderLocaleCatalogMap,
18
- } from "../i18n/catalog.js";
19
- import type { ProviderLocale } from "../i18n/keys.js";
20
- import {
21
- categoryForStatus,
22
- isRetryableCategory,
23
- PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
24
- type ProviderErrorCategory,
25
- } from "../observability.js";
26
- import { createScratchpad } from "../runtime/auth-flow.js";
27
- import { createBrowserClient } from "../runtime/browser.js";
28
- import { createProviderCache } from "../runtime/cache.js";
29
- import {
30
- createProviderChoiceContext,
31
- PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
32
- } from "../runtime/choice.js";
33
- import { createCredentialContext } from "../runtime/credential.js";
34
- import { createEnvContext } from "../runtime/env.js";
35
- import { executeOperation } from "../runtime/executor.js";
36
- import { createHttpClient } from "../runtime/http.js";
37
- import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
38
- import { getProviderBaseUrl } from "../runtime/provider.js";
39
- import {
40
- PROXY_AUTH_IP_DENIED_CODE,
41
- PROXY_EDGE_AUTH_REJECTED_CODE,
42
- PROXY_POOL_EXHAUSTED_CODE,
43
- } from "../runtime/proxy-errors.js";
44
- import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector } from "../runtime/proxy-telemetry.js";
45
- import {
46
- assertRequiredSecretsPresent,
47
- listMissingRequiredSecrets,
48
- MISSING_SECRET_CODE,
49
- } from "../runtime/secrets.js";
50
- import {
51
- createProviderRuntimeStateFromEnv,
52
- createUnsupportedProviderRuntimeState,
53
- } from "../runtime/state.js";
54
- import { createStealthClient } from "../runtime/stealth.js";
55
- import { createSttClientFromEnv } from "../runtime/stt.js";
56
- import { createTraceContext } from "../runtime/trace.js";
57
- import { parseSchema } from "../schema.js";
58
- import { getStealthProfile } from "../stealth/profiles.js";
59
- import {
60
- APIFUSE_STREAM_DONE_EVENT,
61
- APIFUSE_STREAM_ERROR_EVENT,
62
- encodeSseEvent,
63
- error as streamError,
64
- } from "../stream.js";
65
- import type {
66
- AuthContext,
67
- AuthTurn,
68
- BrowserClient,
69
- FlowContext,
70
- FlowContextStore,
71
- HttpRetrySummary,
72
- OperationDefinition,
73
- OperationHttpStreamTransport,
74
- OperationSseTransport,
75
- ProviderContext,
76
- ProviderDefinition,
77
- ProviderRuntimeState,
78
- ProviderStreamEvent,
79
- StealthClient,
80
- SttContext,
81
- } from "../types.js";
82
- import {
83
- createSelfTestApp,
84
- createSelfTestAuthFlowInvoke,
85
- createSelfTestInvoke,
86
- resolveSelfTestPort,
87
- } from "./self-test.js";
88
- import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
89
- import {
90
- type AuthFlowRequest,
91
- AuthFlowRequestSchema,
92
- type AuthFlowResponse,
93
- type AuthFlowSuccessResponse,
94
- type OperationErrorResponse,
95
- type OperationRequest,
96
- OperationRequestSchema,
97
- type OperationResponse,
98
- type OperationSuccessResponse,
99
- } from "./types.js";
100
-
101
- const DEFAULT_HOST = "0.0.0.0";
102
- const DEFAULT_PORT = 3000;
103
- const AUTH_FLOW_LOCALES = ["en", "ko", "ja"] as const;
104
- const retryResponseMeta = new WeakMap<ProviderContext, HttpRetrySummary>();
105
-
106
- type RequestCleanup = () => void | Promise<void>;
107
-
108
- function createAuthStub(): AuthContext {
109
- return {
110
- async requestField(name) {
111
- throw new ProviderError(`Auth prompt is unavailable for ${name}`, {
112
- code: "AUTH_PROMPT_UNAVAILABLE",
113
- });
114
- },
115
- };
116
- }
117
-
118
- function createBrowserStub(): BrowserClient {
119
- return {
120
- engine: "playwright-stealth",
121
- async close() {},
122
- async newPage() {
123
- throw new ProviderError("Browser runtime is not available", {
124
- code: "BROWSER_RUNTIME_UNSUPPORTED",
125
- });
126
- },
127
- async rawPage() {
128
- throw new ProviderError("Browser runtime is not available", {
129
- code: "BROWSER_RUNTIME_UNSUPPORTED",
130
- });
131
- },
132
- async withIsolatedContext() {
133
- throw new ProviderError("Browser runtime is not available", {
134
- code: "BROWSER_RUNTIME_UNSUPPORTED",
135
- });
136
- },
137
- async solveChallenge() {
138
- throw new ProviderError("Browser runtime is not available", {
139
- code: "BROWSER_RUNTIME_UNSUPPORTED",
140
- });
141
- },
142
- };
143
- }
144
-
145
- function createStealthStub(): StealthClient {
146
- return {
147
- async fetch() {
148
- throw new ProviderError("Stealth runtime is not available", {
149
- code: "STEALTH_RUNTIME_UNSUPPORTED",
150
- });
151
- },
152
- createSession() {
153
- throw new ProviderError("Stealth runtime is not available", {
154
- code: "STEALTH_RUNTIME_UNSUPPORTED",
155
- });
156
- },
157
- close() {
158
- // no-op
159
- },
160
- };
161
- }
162
-
163
- function getProviderStealthBaseUrl(provider: ProviderDefinition): string | undefined {
164
- const baseUrl = getProviderBaseUrl(provider);
165
- if (baseUrl) {
166
- return baseUrl;
167
- }
168
- const firstHost = provider.allowedHosts?.[0];
169
- return firstHost ? `https://${firstHost}` : undefined;
170
- }
171
-
172
- function getProviderStealthProfile(provider: ProviderDefinition) {
173
- return provider.stealth?.profile ? getStealthProfile(provider.stealth.profile) : undefined;
174
- }
175
-
176
- function isProductionProviderBrowserMode(provider: ProviderDefinition, env = process.env): boolean {
177
- if (provider.runtime !== "browser") {
178
- return false;
179
- }
180
-
181
- if (env.APIFUSE__PROVIDER__RUNTIME === "browser") {
182
- return true;
183
- }
184
-
185
- return env.NODE_ENV === "production" && env.APIFUSE__PROVIDER__ID === provider.id;
186
- }
187
-
188
- export function resolveProviderProxyAffinityKey(
189
- provider: ProviderDefinition,
190
- request: OperationRequest,
191
- operationId: string,
192
- ): string {
193
- const connectionKey = resolveOperationConnectionId(request) ?? request.connection?.externalRef;
194
- const affinity =
195
- typeof provider.proxy === "object" ? provider.proxy.session?.affinity : undefined;
196
- if (affinity === "operation") {
197
- return `${provider.id}/${operationId}`;
198
- }
199
- return connectionKey ?? provider.id;
200
- }
201
-
202
- function resolveOperationConnectionId(request: OperationRequest): string | undefined {
203
- return request.connection?.id ?? request.connectionId;
204
- }
205
-
206
- function createProviderContext(
207
- provider: ProviderDefinition,
208
- request: OperationRequest,
209
- operationId: string,
210
- options: ProviderServerOptions = {},
211
- state: ProviderRuntimeState = createUnsupportedProviderRuntimeState(),
212
- proxyTelemetry?: ProxyTelemetryCollector,
213
- ): ProviderContext {
214
- const baseUrl = getProviderBaseUrl(provider);
215
- const stealthBaseUrl = getProviderStealthBaseUrl(provider);
216
- const stealthProfile = getProviderStealthProfile(provider);
217
- const proxyClientOptions = {
218
- upstream: { proxy: provider.proxy },
219
- affinityKey: resolveProviderProxyAffinityKey(provider, request, operationId),
220
- telemetry: proxyTelemetry,
221
- };
222
- let wrappedContext: ProviderContext | undefined;
223
- const stealthClientOptions = {
224
- upstream: proxyClientOptions.upstream,
225
- affinityKey: proxyClientOptions.affinityKey,
226
- telemetry: proxyTelemetry,
227
- };
228
-
229
- const env = createEnvContext([
230
- ...(provider.secrets?.map((secret) => secret.name) ?? []),
231
- PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
232
- ]);
233
- const credential = createCredentialContext({
234
- allowedKeys: provider.credential?.keys,
235
- mode: request.connection?.mode,
236
- scopes: request.connection?.scopes,
237
- values: request.connection?.secrets,
238
- });
239
- const requestContext = {
240
- connectionId: resolveOperationConnectionId(request),
241
- headers: request.headers ?? {},
242
- };
243
- const context = wrapWithInstrumentation({
244
- env,
245
- credential,
246
- request: requestContext,
247
- http: createHttpClient(baseUrl, {
248
- ...proxyClientOptions,
249
- onRetrySummary: (summary) => {
250
- if (summary.attempts <= 1 || !wrappedContext) return;
251
- retryResponseMeta.set(wrappedContext, summary);
252
- },
253
- }),
254
- cache: createProviderCache({ providerId: provider.id }),
255
- state,
256
- stealth: stealthBaseUrl
257
- ? stealthProfile
258
- ? createStealthClient(stealthBaseUrl, stealthProfile.name, stealthClientOptions)
259
- : createStealthClient(stealthBaseUrl, stealthClientOptions)
260
- : createStealthStub(),
261
- browser:
262
- provider.runtime === "browser"
263
- ? createBrowserClient({
264
- allowedHosts: provider.allowedHosts,
265
- cdpUrl: process.env.APIFUSE__CDP_POOL__URL,
266
- headless: true,
267
- requireCdpPool: isProductionProviderBrowserMode(provider),
268
- stealth: true,
269
- engine: provider.browser?.engine,
270
- })
271
- : createBrowserStub(),
272
- trace: createTraceContext(),
273
- auth: createAuthStub(),
274
- stt: options.stt ?? createSttClientFromEnv(provider.stt),
275
- choice: createProviderChoiceContext({
276
- providerId: provider.id,
277
- env,
278
- request: requestContext,
279
- credential,
280
- state,
281
- }),
282
- });
283
- wrappedContext = context;
284
- return context;
285
- }
286
-
287
- function createFlowContextStore(
288
- allowedKeys: string[],
289
- initialContext: Record<string, unknown> = {},
290
- ): {
291
- context: FlowContextStore;
292
- getPatch: () => Record<string, unknown | null> | undefined;
293
- } {
294
- const context = createScratchpad(allowedKeys, initialContext);
295
-
296
- return {
297
- context,
298
- getPatch() {
299
- const next = context.toJSON();
300
- const patch = new Map<string, unknown | null>();
301
-
302
- for (const [key, value] of Object.entries(next)) {
303
- if (initialContext[key] !== value) {
304
- patch.set(key, value);
305
- }
306
- }
307
-
308
- for (const key of Object.keys(initialContext)) {
309
- if (!(key in next)) {
310
- patch.set(key, null);
311
- }
312
- }
313
-
314
- if (patch.size === 0) {
315
- return undefined;
316
- }
317
-
318
- return Object.fromEntries(patch.entries());
319
- },
320
- };
321
- }
322
-
323
- function createAuthFlowContext(
324
- provider: ProviderDefinition,
325
- request: AuthFlowRequest,
326
- options: ProviderServerOptions = {},
327
- signal?: AbortSignal,
328
- ): {
329
- context: FlowContext;
330
- getPatch: () => Record<string, unknown | null> | undefined;
331
- } {
332
- const baseUrl = getProviderBaseUrl(provider);
333
- const stealthBaseUrl = getProviderStealthBaseUrl(provider);
334
- const stealthProfile = getProviderStealthProfile(provider);
335
- const contextData = request.context ?? {};
336
- const flowContextStore = createFlowContextStore(
337
- provider.context?.keys ?? Object.keys(contextData),
338
- contextData,
339
- );
340
- const proxyClientOptions = {
341
- upstream: { proxy: provider.proxy },
342
- affinityKey:
343
- request.connectionId ??
344
- request.externalRef ??
345
- request.tenantId ??
346
- request.providerId ??
347
- provider.id,
348
- };
349
- const stealthClientOptions = {
350
- upstream: proxyClientOptions.upstream,
351
- affinityKey: proxyClientOptions.affinityKey,
352
- };
353
- const credential = request.connection
354
- ? createCredentialContext({
355
- allowedKeys: provider.credential?.keys,
356
- mode: request.connection.mode,
357
- scopes: request.connection.scopes,
358
- values: request.connection.secrets,
359
- })
360
- : undefined;
361
-
362
- return {
363
- context: {
364
- connectionId: request.connectionId,
365
- externalRef: request.externalRef,
366
- tenantId: request.tenantId ?? "",
367
- providerId: request.providerId ?? provider.id,
368
- http: createHttpClient(baseUrl, proxyClientOptions),
369
- stealth: stealthBaseUrl
370
- ? stealthProfile
371
- ? createStealthClient(stealthBaseUrl, stealthProfile.name, stealthClientOptions)
372
- : createStealthClient(stealthBaseUrl, stealthClientOptions)
373
- : createStealthStub(),
374
- env: createEnvContext(provider.secrets?.map((secret) => secret.name)),
375
- credential,
376
- context: flowContextStore.context,
377
- stt: options.stt ?? createSttClientFromEnv(provider.stt),
378
- auth: createAuthFlowHelpers({ signal }),
379
- },
380
- getPatch: flowContextStore.getPatch,
381
- };
382
- }
383
-
384
- type ProviderRequestCost = {
385
- durationMs: number;
386
- cpuUserMicros: number;
387
- cpuSystemMicros: number;
388
- cpuTotalMicros: number;
389
- };
390
-
391
- type ProviderServerLogEventBase = ProviderRequestCost & {
392
- providerId: string;
393
- kind: "operation" | "auth";
394
- route: string;
395
- requestId?: string;
396
- status: number;
397
- };
398
-
399
- export type ProviderServerLogEvent =
400
- | (ProviderServerLogEventBase & {
401
- level: "info";
402
- event: "provider_request_completed";
403
- })
404
- | (ProviderServerLogEventBase & {
405
- level: "warn" | "error";
406
- event: "provider_request_failed";
407
- code: string;
408
- errorClass: string;
409
- message: string;
410
- upstreamStatus?: number;
411
- errorCategory?: ProviderErrorCategory;
412
- taxonomyVersion?: string;
413
- retryable?: boolean;
414
- issues?: Array<{ path: string; code: string; message: string }>;
415
- })
416
- | {
417
- level: "warn";
418
- event: "provider_secrets_missing";
419
- providerId: string;
420
- missingSecrets: string[];
421
- }
422
- | {
423
- level: "warn";
424
- event: "provider_cleanup_failed";
425
- providerId: string;
426
- kind: "operation";
427
- route: string;
428
- requestId?: string;
429
- resource: "browser" | "stealth";
430
- errorClass: string;
431
- message: string;
432
- };
433
-
434
- export type ProviderServerLogger = (event: ProviderServerLogEvent) => void;
435
-
436
- export type ProviderServerOptions = {
437
- logger?: ProviderServerLogger;
438
- /** Optional STT override for tests or custom hosts; local/prod normally resolves from env. */
439
- stt?: SttContext;
440
- /** Optional runtime state override for tests or custom hosts. Production resolves Redis from env and fails closed when unavailable. */
441
- state?: ProviderRuntimeState;
442
- /** Allow process-local runtime state only for local development and tests. */
443
- allowMemoryStateFallback?: boolean;
444
- };
445
-
446
- const defaultProviderServerLogger: ProviderServerLogger = (event) => {
447
- const line = JSON.stringify(event);
448
- if (event.level === "info") {
449
- console.log(line);
450
- return;
451
- }
452
- console.error(line);
453
- };
454
-
455
- function startRequestCost(): {
456
- startedAtMs: number;
457
- cpuStart: NodeJS.CpuUsage;
458
- } {
459
- return {
460
- startedAtMs: performance.now(),
461
- cpuStart: process.cpuUsage(),
462
- };
463
- }
464
-
465
- function finishRequestCost(input: {
466
- startedAtMs: number;
467
- cpuStart: NodeJS.CpuUsage;
468
- }): ProviderRequestCost {
469
- const cpuDelta = process.cpuUsage(input.cpuStart);
470
- return {
471
- durationMs: Math.max(0, Math.round(performance.now() - input.startedAtMs)),
472
- cpuUserMicros: Math.max(0, cpuDelta.user),
473
- cpuSystemMicros: Math.max(0, cpuDelta.system),
474
- cpuTotalMicros: Math.max(0, cpuDelta.user + cpuDelta.system),
475
- };
476
- }
477
-
478
- function zodDetails(error: z.ZodError): Array<{
479
- path: string;
480
- code: string;
481
- message: string;
482
- }> {
483
- return error.issues.map((issue) => ({
484
- path: issue.path.join("."),
485
- code: issue.code,
486
- message: issue.message,
487
- }));
488
- }
489
-
490
- function toErrorResponse(error: unknown, requestId?: string): OperationErrorResponse {
491
- if (isProviderError(error)) {
492
- const details = publicProviderErrorDetails(error);
493
- return {
494
- error: {
495
- code: error.code ?? "provider_error",
496
- message: publicProviderErrorMessage(error),
497
- ...(requestId ? { requestId } : {}),
498
- ...(error.fix ? { fix: error.fix } : {}),
499
- ...(details ? { details } : {}),
500
- },
501
- };
502
- }
503
-
504
- if (error instanceof z.ZodError) {
505
- return {
506
- error: {
507
- code: "invalid_request",
508
- message: "Invalid request body",
509
- ...(requestId ? { requestId } : {}),
510
- details: zodDetails(error),
511
- },
512
- };
513
- }
514
-
515
- return {
516
- error: {
517
- code: "internal_error",
518
- message: "Internal error",
519
- ...(requestId ? { requestId } : {}),
520
- },
521
- };
522
- }
523
-
524
- function publicProviderErrorDetails(error: ProviderError): unknown {
525
- const providerDetails = error.details;
526
- const observabilityDetails = providerObservabilityDetails(error);
527
-
528
- if (providerDetails === undefined) {
529
- return observabilityDetails;
530
- }
531
- if (observabilityDetails === undefined) {
532
- return providerDetails;
533
- }
534
- if (isPlainRecord(providerDetails) && isPlainRecord(observabilityDetails)) {
535
- return { ...providerDetails, ...observabilityDetails };
536
- }
537
- return {
538
- provider: providerDetails,
539
- observability: observabilityDetails,
540
- };
541
- }
542
-
543
- function isPlainRecord(value: unknown): value is Record<string, unknown> {
544
- return value !== null && typeof value === "object" && !Array.isArray(value);
545
- }
546
-
547
- // Accepts `unknown` so the branded guards narrow cleanly from the top: the
548
- // subtype error classes are structurally compatible with ProviderError, so
549
- // narrowing from a ProviderError-typed value would collapse the negative branch
550
- // to `never`. Narrowing from unknown avoids that while still recognizing errors
551
- // from a duplicate SDK module instance.
552
- function providerObservabilityDetails(error: unknown):
553
- | {
554
- category: ProviderErrorCategory;
555
- taxonomyVersion: string;
556
- retryable: boolean;
557
- upstreamStatus?: number;
558
- }
559
- | undefined {
560
- // Session-expiry surfaces the credential_expired category + the opt-in
561
- // retryable signal so Gateway/Credential Service can refresh and re-drive the
562
- // operation (see design.md §4.3 D3). Without this branch the auth error would
563
- // serialize as a bare 401 with no retryable/category, losing the refresh
564
- // signal for exactly the retryOnAuthRefresh operations it is meant to enable.
565
- if (isSessionExpiredError(error)) {
566
- return {
567
- category: error.options?.category ?? "credential_expired",
568
- taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
569
- retryable: error.options?.retryable ?? false,
570
- };
571
- }
572
- // Missing-secret errors carry the canonical credential_unavailable category
573
- // so Gateway/observability can attribute the failure to provisioning, not
574
- // the upstream. Matched by code (not constructor) so both the SDK-owned
575
- // runtime gate and any not-yet-migrated provider-thrown MISSING_SECRET
576
- // serialize identically, including across duplicate SDK module instances.
577
- if (isProviderError(error) && error.code === MISSING_SECRET_CODE) {
578
- return {
579
- category: error.options?.category ?? "credential_unavailable",
580
- taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
581
- retryable: error.options?.retryable ?? false,
582
- };
583
- }
584
- if (!isTransportError(error)) {
585
- return undefined;
586
- }
587
- const isProxyPoolCode =
588
- error.code === PROXY_POOL_EXHAUSTED_CODE ||
589
- error.code === PROXY_EDGE_AUTH_REJECTED_CODE ||
590
- error.code === "PROXY_ALLOCATION_FAILED";
591
- const category =
592
- error.options?.category ??
593
- (isProxyPoolCode
594
- ? "proxy_pool"
595
- : error.code === PROXY_AUTH_IP_DENIED_CODE
596
- ? "anti_bot_blocked"
597
- : error.code === "transport_timeout"
598
- ? "timeout"
599
- : error.code === "transport_network_error"
600
- ? "network"
601
- : error.upstreamStatus
602
- ? categoryForStatus(error.upstreamStatus)
603
- : "upstream_http");
604
- return {
605
- category,
606
- taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
607
- retryable:
608
- error.options?.retryable ??
609
- (category === "upstream_http" && error.upstreamStatus
610
- ? error.upstreamStatus >= 500
611
- : isRetryableCategory(category)),
612
- ...(error.upstreamStatus ? { upstreamStatus: error.upstreamStatus } : {}),
613
- };
614
- }
615
-
616
- function publicProviderErrorMessage(error: ProviderError): string {
617
- if (isTransportError(error)) {
618
- if (error.code === PROXY_AUTH_IP_DENIED_CODE) {
619
- return error.message;
620
- }
621
- if (error.code === PROXY_EDGE_AUTH_REJECTED_CODE) {
622
- return error.message;
623
- }
624
- if (error.code === PROXY_POOL_EXHAUSTED_CODE) {
625
- return error.message;
626
- }
627
- if (error.code === "transport_timeout") return "Request timed out";
628
- if (error.code === "transport_network_error") return "Network error";
629
- if (error.code === "upstream_http_error" && error.status) {
630
- return `Upstream request failed with status ${error.status}`;
631
- }
632
- if (error.status) {
633
- return `Upstream request failed with status ${error.status}`;
634
- }
635
- return "Upstream request failed";
636
- }
637
- return error.message;
638
- }
639
-
640
- function toStatusCode(error: unknown): 400 | 401 | 404 | 429 | 500 | 502 | 503 | 504 {
641
- if (error instanceof z.ZodError) {
642
- return 400;
643
- }
644
-
645
- if (isTransportError(error)) {
646
- return error.code === "transport_timeout" ? 504 : 502;
647
- }
648
-
649
- if (isProviderError(error)) {
650
- switch (error.code) {
651
- case "AUTH_REQUIRED":
652
- case "reauth_required":
653
- return 401;
654
- // Unprovisioned declared secret: a deployment/config defect, never an
655
- // upstream failure — explicit 400 (was only reached via fallthrough).
656
- case MISSING_SECRET_CODE:
657
- return 400;
658
- case "NOT_FOUND":
659
- case "not_found":
660
- case "NO_DATA":
661
- return 404;
662
- case "RATE_LIMITED":
663
- case "UPSTREAM_RATE_LIMIT":
664
- case "LIMITED_NUMBER_OF_SERVICE_REQUESTS_EXCEEDS_ERROR":
665
- return 429;
666
- case "UPSTREAM_ERROR":
667
- case "BLOCKED":
668
- return 502;
669
- case "STT_UNAVAILABLE":
670
- case "UNSUPPORTED_STT_BACKEND":
671
- return 503;
672
- }
673
-
674
- return 400;
675
- }
676
-
677
- return 500;
678
- }
679
-
680
- function extractRequestId(raw: unknown): string | undefined {
681
- if (!raw || typeof raw !== "object") {
682
- return undefined;
683
- }
684
-
685
- const value = Object.getOwnPropertyDescriptor(raw, "requestId")?.value;
686
- return typeof value === "string" ? value : undefined;
687
- }
688
-
689
- function logProviderError(
690
- logger: ProviderServerLogger | unknown,
691
- provider: ProviderDefinition,
692
- kind: "operation" | "auth",
693
- route: string,
694
- requestId: string | undefined,
695
- error: unknown,
696
- status: number,
697
- cost: ProviderRequestCost,
698
- ): void {
699
- const code = isProviderError(error)
700
- ? (error.code ?? "provider_error")
701
- : error instanceof z.ZodError
702
- ? "invalid_request"
703
- : "internal_error";
704
- const errorClass = error instanceof Error ? error.name : typeof error;
705
- const message = error instanceof Error ? error.message : String(error);
706
- const details = isProviderError(error) ? providerObservabilityDetails(error) : undefined;
707
- const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
708
- emit({
709
- level: status >= 500 ? "error" : "warn",
710
- event: "provider_request_failed",
711
- providerId: provider.id,
712
- kind,
713
- route,
714
- ...(requestId ? { requestId } : {}),
715
- status,
716
- ...cost,
717
- code,
718
- errorClass,
719
- message,
720
- ...(isTransportError(error) && error.upstreamStatus
721
- ? { upstreamStatus: error.upstreamStatus }
722
- : {}),
723
- ...(details
724
- ? {
725
- errorCategory: details.category,
726
- taxonomyVersion: details.taxonomyVersion,
727
- retryable: details.retryable,
728
- }
729
- : {}),
730
- ...(error instanceof z.ZodError ? { issues: zodDetails(error) } : {}),
731
- });
732
- }
733
-
734
- function logProviderCleanupError(
735
- logger: ProviderServerLogger | unknown,
736
- provider: ProviderDefinition,
737
- operationId: string,
738
- requestId: string | undefined,
739
- resource: "browser" | "stealth",
740
- error: unknown,
741
- ): void {
742
- const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
743
- const errorClass = error instanceof Error ? error.name : typeof error;
744
- const message = error instanceof Error ? error.message : String(error);
745
- emit({
746
- level: "warn",
747
- event: "provider_cleanup_failed",
748
- providerId: provider.id,
749
- kind: "operation",
750
- route: operationId,
751
- ...(requestId ? { requestId } : {}),
752
- resource,
753
- errorClass,
754
- message,
755
- });
756
- }
757
-
758
- function logProviderSuccess(
759
- logger: ProviderServerLogger | unknown,
760
- provider: ProviderDefinition,
761
- kind: "operation" | "auth",
762
- route: string,
763
- requestId: string | undefined,
764
- status: number,
765
- cost: ProviderRequestCost,
766
- ): void {
767
- const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
768
- emit({
769
- level: "info",
770
- event: "provider_request_completed",
771
- providerId: provider.id,
772
- kind,
773
- route,
774
- ...(requestId ? { requestId } : {}),
775
- status,
776
- ...cost,
777
- });
778
- }
779
-
780
- function toJsonSuccessResponse(
781
- result: unknown,
782
- ctx?: ProviderContext,
783
- ): Response | OperationSuccessResponse {
784
- if (result instanceof Response) {
785
- return result;
786
- }
787
-
788
- if (result instanceof ReadableStream) {
789
- return new Response(result);
790
- }
791
-
792
- const cacheMeta = ctx?.cache.responseMeta();
793
- const retryMeta = ctx ? retryResponseMeta.get(ctx) : undefined;
794
- const meta =
795
- cacheMeta || retryMeta
796
- ? {
797
- ...(cacheMeta
798
- ? {
799
- cached: cacheMeta.hit,
800
- stale: cacheMeta.stale,
801
- cache: cacheMeta,
802
- }
803
- : {}),
804
- ...(retryMeta ? { retry: retryMeta } : {}),
805
- }
806
- : undefined;
807
- return {
808
- data: result,
809
- ...(meta ? { meta } : {}),
810
- };
811
- }
812
-
813
- function isAsyncIterable<T = unknown>(value: unknown): value is AsyncIterable<T> {
814
- if (!value || typeof value !== "object") return false;
815
- const iterator = Reflect.get(value, Symbol.asyncIterator);
816
- return typeof iterator === "function";
817
- }
818
-
819
- function responseWithCleanup(response: Response, cleanup: RequestCleanup): Response {
820
- if (!response.body) {
821
- void cleanup();
822
- return response;
823
- }
824
- const reader = response.body.getReader();
825
- let cleaned = false;
826
- const runCleanup = async () => {
827
- if (cleaned) return;
828
- cleaned = true;
829
- await cleanup();
830
- };
831
- const body = new ReadableStream<Uint8Array>({
832
- async pull(controller) {
833
- try {
834
- const { done, value } = await reader.read();
835
- if (done) {
836
- controller.close();
837
- await runCleanup();
838
- return;
839
- }
840
- if (value) controller.enqueue(value);
841
- } catch (error) {
842
- await runCleanup();
843
- controller.error(error);
844
- }
845
- },
846
- async cancel(reason) {
847
- try {
848
- await reader.cancel(reason);
849
- } finally {
850
- await runCleanup();
851
- }
852
- },
853
- });
854
- return new Response(body, {
855
- headers: response.headers,
856
- status: response.status,
857
- statusText: response.statusText,
858
- });
859
- }
860
-
861
- async function validateSseEvent(
862
- operation: OperationDefinition,
863
- event: ProviderStreamEvent,
864
- ): Promise<ProviderStreamEvent> {
865
- const transport = getSseTransport(operation);
866
- const schema = transport?.events?.[event.event];
867
- if (!schema) {
868
- if (event.event === APIFUSE_STREAM_ERROR_EVENT || event.event === APIFUSE_STREAM_DONE_EVENT) {
869
- return event;
870
- }
871
- throw new ProviderError(
872
- `SSE event "${event.event}" is not declared in operation transport.events.`,
873
- {
874
- code: "SSE_EVENT_UNDECLARED",
875
- category: "output_validation",
876
- retryable: false,
877
- fix: `Add "${event.event}" to transport.events or stop emitting that event.`,
878
- },
879
- );
880
- }
881
- const data = await parseSchema(schema, event.data, `transport.events.${event.event}`);
882
- return { ...event, data };
883
- }
884
-
885
- function byteLength(value: Uint8Array | string): number {
886
- if (typeof value === "string") {
887
- return new TextEncoder().encode(value).byteLength;
888
- }
889
- return value.byteLength;
890
- }
891
-
892
- function assertStreamPayloadWithinLimit(
893
- actualBytes: number,
894
- maxBytes: number | undefined,
895
- kind: "event" | "chunk",
896
- ): void {
897
- if (maxBytes === undefined || actualBytes <= maxBytes) return;
898
- throw new ProviderError(
899
- `Stream ${kind} exceeded declared byte limit (${actualBytes} > ${maxBytes}).`,
900
- {
901
- code: kind === "event" ? "STREAM_EVENT_TOO_LARGE" : "STREAM_CHUNK_TOO_LARGE",
902
- retryable: false,
903
- category: "input_validation",
904
- fix:
905
- kind === "event"
906
- ? "Emit smaller SSE events or increase transport.maxEventBytes."
907
- : "Emit smaller stream chunks or increase transport.maxChunkBytes.",
908
- },
909
- );
910
- }
911
-
912
- function toSseResponse(
913
- operation: OperationDefinition,
914
- result: AsyncIterable<ProviderStreamEvent>,
915
- cleanup: RequestCleanup,
916
- requestId?: string,
917
- ): Response {
918
- const encoder = new TextEncoder();
919
- const iterator = result[Symbol.asyncIterator]();
920
- const transport = getSseTransport(operation);
921
- let done = false;
922
- let cleaned = false;
923
- const runCleanup = async () => {
924
- if (cleaned) return;
925
- cleaned = true;
926
- await cleanup();
927
- };
928
- const body = new ReadableStream<Uint8Array>({
929
- async pull(controller) {
930
- try {
931
- if (done) {
932
- controller.close();
933
- await runCleanup();
934
- return;
935
- }
936
- const next = await iterator.next();
937
- if (next.done) {
938
- done = true;
939
- controller.close();
940
- await runCleanup();
941
- return;
942
- }
943
- const validated = await validateSseEvent(operation, next.value);
944
- const encodedEvent = encodeSseEvent(validated);
945
- const encodedBytes = encoder.encode(encodedEvent);
946
- assertStreamPayloadWithinLimit(encodedBytes.byteLength, transport?.maxEventBytes, "event");
947
- controller.enqueue(encodedBytes);
948
- } catch (error) {
949
- const message = error instanceof Error ? error.message : "Stream failed";
950
- controller.enqueue(
951
- encoder.encode(
952
- encodeSseEvent(
953
- streamError("stream_error", message, {
954
- ...(requestId ? { requestId } : {}),
955
- }),
956
- ),
957
- ),
958
- );
959
- controller.close();
960
- done = true;
961
- await runCleanup();
962
- }
963
- },
964
- async cancel(reason) {
965
- try {
966
- await iterator.return?.(reason);
967
- } finally {
968
- await runCleanup();
969
- }
970
- },
971
- });
972
- return new Response(body, {
973
- headers: {
974
- "Cache-Control": "no-cache, no-transform",
975
- Connection: "keep-alive",
976
- "Content-Type": "text/event-stream; charset=utf-8",
977
- },
978
- });
979
- }
980
-
981
- function enforceStreamChunkLimit(
982
- body: ReadableStream<Uint8Array>,
983
- maxChunkBytes: number | undefined,
984
- ): ReadableStream<Uint8Array> {
985
- if (maxChunkBytes === undefined) return body;
986
- const reader = body.getReader();
987
- return new ReadableStream<Uint8Array>({
988
- async pull(controller) {
989
- try {
990
- const { done, value } = await reader.read();
991
- if (done) {
992
- controller.close();
993
- return;
994
- }
995
- if (value) {
996
- assertStreamPayloadWithinLimit(byteLength(value), maxChunkBytes, "chunk");
997
- controller.enqueue(value);
998
- }
999
- } catch (error) {
1000
- controller.error(error);
1001
- }
1002
- },
1003
- cancel(reason) {
1004
- return reader.cancel(reason);
1005
- },
1006
- });
1007
- }
1008
-
1009
- function toStreamingResponse(
1010
- operation: OperationDefinition,
1011
- result: unknown,
1012
- cleanup: RequestCleanup,
1013
- requestId?: string,
1014
- ): Response {
1015
- const transport = operation.transport?.kind ?? "json";
1016
- if (transport === "sse" && (result instanceof Response || result instanceof ReadableStream)) {
1017
- void cleanup();
1018
- throw new ProviderError(
1019
- "SSE operations must return an AsyncIterable of typed stream.event(...) values.",
1020
- {
1021
- code: "SSE_RESULT_UNSUPPORTED",
1022
- category: "output_validation",
1023
- retryable: false,
1024
- fix: "Return an async generator that yields stream.event(name, data) so APIFuse can validate event schemas and enforce event byte limits.",
1025
- },
1026
- );
1027
- }
1028
- if (result instanceof Response) {
1029
- const httpTransport = getHttpStreamTransport(operation);
1030
- if (httpTransport && result.body && httpTransport?.maxChunkBytes !== undefined) {
1031
- return responseWithCleanup(
1032
- new Response(enforceStreamChunkLimit(result.body, httpTransport.maxChunkBytes), {
1033
- headers: result.headers,
1034
- status: result.status,
1035
- statusText: result.statusText,
1036
- }),
1037
- cleanup,
1038
- );
1039
- }
1040
- return responseWithCleanup(result, cleanup);
1041
- }
1042
- if (result instanceof ReadableStream) {
1043
- const httpTransport = getHttpStreamTransport(operation);
1044
- const stream =
1045
- httpTransport !== undefined
1046
- ? enforceStreamChunkLimit(result, httpTransport.maxChunkBytes)
1047
- : result;
1048
- return responseWithCleanup(
1049
- new Response(stream, {
1050
- headers:
1051
- transport === "sse"
1052
- ? { "Content-Type": "text/event-stream; charset=utf-8" }
1053
- : {
1054
- "Content-Type":
1055
- operation.transport?.kind === "http-stream"
1056
- ? (operation.transport.contentType ?? "application/octet-stream")
1057
- : "application/octet-stream",
1058
- },
1059
- }),
1060
- cleanup,
1061
- );
1062
- }
1063
- if (transport === "sse" && isAsyncIterable<ProviderStreamEvent>(result)) {
1064
- return toSseResponse(operation, result, cleanup, requestId);
1065
- }
1066
- void cleanup();
1067
- throw new ProviderError(
1068
- `Streaming operation returned unsupported result for transport "${transport}"`,
1069
- {
1070
- code: "STREAM_RESULT_UNSUPPORTED",
1071
- fix: "Return an AsyncIterable of stream.event(...) values, a ReadableStream, or a Response from streaming operations.",
1072
- },
1073
- );
1074
- }
1075
-
1076
- function getSseTransport(operation: OperationDefinition): OperationSseTransport | undefined {
1077
- return operation.transport?.kind === "sse" ? operation.transport : undefined;
1078
- }
1079
-
1080
- function getHttpStreamTransport(
1081
- operation: OperationDefinition,
1082
- ): OperationHttpStreamTransport | undefined {
1083
- return operation.transport?.kind === "http-stream" ? operation.transport : undefined;
1084
- }
1085
-
1086
- function toAuthFlowResponse(
1087
- result: unknown,
1088
- contextPatch: Record<string, unknown | null> | undefined,
1089
- ): Response | AuthFlowSuccessResponse {
1090
- if (result instanceof Response) {
1091
- return result;
1092
- }
1093
-
1094
- if (result instanceof ReadableStream) {
1095
- return new Response(result);
1096
- }
1097
-
1098
- return {
1099
- data: result,
1100
- ...(contextPatch ? { contextPatch } : {}),
1101
- };
1102
- }
1103
-
1104
- function authFlowLocaleFromHeaders(headers?: Record<string, string>): ProviderLocale {
1105
- const header = Object.entries(headers ?? {}).find(
1106
- ([key]) => key.toLowerCase() === "accept-language",
1107
- )?.[1];
1108
- for (const token of (header ?? "").split(",")) {
1109
- const language = token.trim().split(";")[0]?.split("-")[0]?.toLowerCase();
1110
- if (isAuthFlowLocale(language)) {
1111
- return language;
1112
- }
1113
- }
1114
- return "en";
1115
- }
1116
-
1117
- function isAuthFlowLocale(value: string | undefined): value is ProviderLocale {
1118
- return value === "en" || value === "ko" || value === "ja";
1119
- }
1120
-
1121
- function isAuthTurn(value: unknown): value is AuthTurn {
1122
- return !!value && typeof value === "object" && "kind" in value && "turnId" in value;
1123
- }
1124
-
1125
- function loadAuthFlowLocaleCatalogs(
1126
- provider: ProviderDefinition,
1127
- ): ProviderLocaleCatalogMap | undefined {
1128
- for (const providerDir of [
1129
- process.cwd(),
1130
- join(process.cwd(), "providers", provider.id),
1131
- join(process.cwd(), "providers-staging", provider.id),
1132
- ]) {
1133
- if (!existsSync(join(providerDir, "locales", "en.json"))) continue;
1134
- try {
1135
- return loadProviderLocaleCatalogs({
1136
- providerDir,
1137
- locales: AUTH_FLOW_LOCALES,
1138
- });
1139
- } catch {
1140
- return undefined;
1141
- }
1142
- }
1143
- return undefined;
1144
- }
1145
-
1146
- function materializeAuthFlowTurn(
1147
- provider: ProviderDefinition,
1148
- request: AuthFlowRequest,
1149
- turn: AuthTurn,
1150
- ): AuthTurn {
1151
- const catalogs = loadAuthFlowLocaleCatalogs(provider);
1152
- if (!catalogs) return turn;
1153
- return localizeAuthTurn(turn, {
1154
- catalogs,
1155
- locale: authFlowLocaleFromHeaders(request.headers),
1156
- });
1157
- }
1158
-
1159
- function withAuthRequestHeaders(request: AuthFlowRequest, headers: Headers): AuthFlowRequest {
1160
- return {
1161
- ...request,
1162
- headers: {
1163
- ...(request.headers ?? {}),
1164
- ...Object.fromEntries(headers.entries()),
1165
- },
1166
- };
1167
- }
1168
-
1169
- async function handleOperation(
1170
- provider: ProviderDefinition,
1171
- request: OperationRequest,
1172
- operationId: string,
1173
- options: ProviderServerOptions = {},
1174
- state: ProviderRuntimeState = createUnsupportedProviderRuntimeState(),
1175
- proxyTelemetry?: ProxyTelemetryCollector,
1176
- ): Promise<Response | OperationResponse> {
1177
- const ctx = createProviderContext(provider, request, operationId, options, state, proxyTelemetry);
1178
- const operation = provider.operations[operationId];
1179
- const streaming = operation?.transport?.kind && operation.transport.kind !== "json";
1180
- let cleanupCalled = false;
1181
- const cleanup = async () => {
1182
- if (cleanupCalled) return;
1183
- cleanupCalled = true;
1184
- try {
1185
- ctx.stealth.close?.();
1186
- } catch (error) {
1187
- logProviderCleanupError(
1188
- options.logger,
1189
- provider,
1190
- operationId,
1191
- request.requestId,
1192
- "stealth",
1193
- error,
1194
- );
1195
- }
1196
- try {
1197
- await ctx.browser.close?.();
1198
- } catch (error) {
1199
- logProviderCleanupError(
1200
- options.logger,
1201
- provider,
1202
- operationId,
1203
- request.requestId,
1204
- "browser",
1205
- error,
1206
- );
1207
- }
1208
- };
1209
- try {
1210
- const result = await executeOperation(provider, operationId, ctx, request.input);
1211
- if (streaming && operation) {
1212
- return toStreamingResponse(operation, result, cleanup, request.requestId);
1213
- }
1214
- return toJsonSuccessResponse(result, ctx);
1215
- } catch (error) {
1216
- await cleanup();
1217
- throw error;
1218
- } finally {
1219
- if (!streaming) await cleanup();
1220
- }
1221
- }
1222
-
1223
- function responseWithProviderTelemetry(
1224
- response: Response,
1225
- proxyTelemetry?: ProxyTelemetryCollector,
1226
- ): Response {
1227
- const headerValue = proxyTelemetry?.toHeaderValue();
1228
- const headers = new Headers(response.headers);
1229
- headers.delete(PROVIDER_TELEMETRY_HEADER);
1230
- if (headerValue) headers.set(PROVIDER_TELEMETRY_HEADER, headerValue);
1231
- return new Response(response.body, {
1232
- headers,
1233
- status: response.status,
1234
- statusText: response.statusText,
1235
- });
1236
- }
1237
-
1238
- type AuthRoute = "start" | "continue" | "poll" | "abort" | "refresh";
1239
-
1240
- async function handleAuthFlow(
1241
- provider: ProviderDefinition,
1242
- request: AuthFlowRequest,
1243
- route: AuthRoute,
1244
- options: ProviderServerOptions = {},
1245
- signal?: AbortSignal,
1246
- ): Promise<Response | AuthFlowResponse> {
1247
- const flow = provider.auth?.flow;
1248
- if (!flow) {
1249
- throw new ProviderError("Auth flow is not configured", {
1250
- code: "AUTH_FLOW_NOT_CONFIGURED",
1251
- });
1252
- }
1253
-
1254
- // Same SDK-owned gate as executeOperation: OAuth/credentials ceremonies
1255
- // depend on declared secrets (client ids/secrets), so fail structured before
1256
- // any flow code runs instead of at whatever point the ceremony first reads
1257
- // the env. `abort` stays exempt: a user must always be able to cancel a
1258
- // stranded flow even when provisioning is broken.
1259
- const { context, getPatch } = createAuthFlowContext(provider, request, options, signal);
1260
- try {
1261
- if (route !== "abort") {
1262
- assertRequiredSecretsPresent(provider, context.env);
1263
- }
1264
- const result =
1265
- route === "start"
1266
- ? await flow.start(context)
1267
- : route === "continue"
1268
- ? await flow.continue(context, request.input ?? {})
1269
- : route === "poll"
1270
- ? flow.poll
1271
- ? await flow.poll(context)
1272
- : null
1273
- : route === "abort"
1274
- ? flow.abort
1275
- ? await flow.abort(context)
1276
- : null
1277
- : flow.refresh
1278
- ? await flow.refresh(context, request.input ?? {})
1279
- : null;
1280
-
1281
- if (route === "refresh" && !flow.refresh) {
1282
- throw new AuthError("Provider auth flow does not support refresh.", {
1283
- code: "refresh_not_supported",
1284
- });
1285
- }
1286
-
1287
- const materializedResult =
1288
- result &&
1289
- !(result instanceof Response) &&
1290
- !(result instanceof ReadableStream) &&
1291
- isAuthTurn(result)
1292
- ? materializeAuthFlowTurn(provider, request, result)
1293
- : result;
1294
- return toAuthFlowResponse(materializedResult, getPatch());
1295
- } catch (error) {
1296
- if (error instanceof AuthAbortError) {
1297
- return toAuthFlowResponse(error.turn, getPatch());
1298
- }
1299
- throw error;
1300
- } finally {
1301
- context.stealth.close?.();
1302
- }
1303
- }
1304
-
1305
- export function createServerApp(
1306
- provider: ProviderDefinition,
1307
- options: ProviderServerOptions = {},
1308
- ): Hono {
1309
- const app = new Hono();
1310
- const logger = options.logger ?? defaultProviderServerLogger;
1311
- const state =
1312
- options.state ??
1313
- createProviderRuntimeStateFromEnv({
1314
- providerId: provider.id,
1315
- allowMemoryFallback: options.allowMemoryStateFallback === true,
1316
- });
1317
-
1318
- // Boot-time visibility for unprovisioned declared secrets: emit a structured
1319
- // warn so deploy tooling/alerting sees the gap the moment the pod boots,
1320
- // instead of discovering it request-by-request. Deliberately log-only — a
1321
- // boot crash would trade a structured MISSING_SECRET signal for
1322
- // CrashLoopBackOff. Requests still fail closed via the executeOperation gate.
1323
- const missingSecretsAtBoot = listMissingRequiredSecrets(
1324
- provider,
1325
- createEnvContext(provider.secrets?.map((secret) => secret.name)),
1326
- );
1327
- if (missingSecretsAtBoot.length > 0) {
1328
- logger({
1329
- level: "warn",
1330
- event: "provider_secrets_missing",
1331
- providerId: provider.id,
1332
- missingSecrets: missingSecretsAtBoot,
1333
- });
1334
- }
1335
-
1336
- app.notFound((c) =>
1337
- c.json(
1338
- {
1339
- error: {
1340
- code: "not_found",
1341
- message: "Not found",
1342
- },
1343
- },
1344
- 404,
1345
- ),
1346
- );
1347
-
1348
- app.get("/health", (c) =>
1349
- c.json({
1350
- status: "ok",
1351
- provider: provider.id,
1352
- version: provider.version,
1353
- }),
1354
- );
1355
-
1356
- app.post("/v1/:operation", async (c) => {
1357
- let rawBody: unknown;
1358
- const operation = c.req.param("operation");
1359
- const proxyTelemetry = new ProxyTelemetryCollector();
1360
- const requestCost = startRequestCost();
1361
- try {
1362
- rawBody = await c.req.raw
1363
- .clone()
1364
- .json()
1365
- .catch(() => undefined);
1366
- const body = OperationRequestSchema.parse(rawBody);
1367
- const requestHeaders = Object.fromEntries(c.req.raw.headers.entries());
1368
- body.headers = { ...requestHeaders, ...body.headers };
1369
- const response = await handleOperation(
1370
- provider,
1371
- body,
1372
- operation,
1373
- options,
1374
- state,
1375
- proxyTelemetry,
1376
- );
1377
- if (response instanceof Response) {
1378
- logProviderSuccess(
1379
- logger,
1380
- provider,
1381
- "operation",
1382
- operation,
1383
- body.requestId,
1384
- response.status,
1385
- finishRequestCost(requestCost),
1386
- );
1387
- return responseWithProviderTelemetry(response, proxyTelemetry);
1388
- }
1389
- const telemetryHeader = proxyTelemetry.toHeaderValue();
1390
- if (telemetryHeader) c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
1391
- logProviderSuccess(
1392
- logger,
1393
- provider,
1394
- "operation",
1395
- operation,
1396
- body.requestId,
1397
- 200,
1398
- finishRequestCost(requestCost),
1399
- );
1400
- return c.json(response);
1401
- } catch (error) {
1402
- const status = toStatusCode(error);
1403
- const requestId = extractRequestId(rawBody);
1404
- logProviderError(
1405
- logger,
1406
- provider,
1407
- "operation",
1408
- operation,
1409
- requestId,
1410
- error,
1411
- status,
1412
- finishRequestCost(requestCost),
1413
- );
1414
- const telemetryHeader = proxyTelemetry.toHeaderValue();
1415
- if (telemetryHeader) c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
1416
- return c.json(toErrorResponse(error, requestId), status);
1417
- }
1418
- });
1419
-
1420
- app.post("/auth/start", async (c) => {
1421
- let rawBody: unknown;
1422
- const requestCost = startRequestCost();
1423
- try {
1424
- rawBody = await c.req.raw
1425
- .clone()
1426
- .json()
1427
- .catch(() => undefined);
1428
- const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
1429
- const response = await handleAuthFlow(provider, body, "start", options, c.req.raw.signal);
1430
- logProviderSuccess(
1431
- logger,
1432
- provider,
1433
- "auth",
1434
- "start",
1435
- body.requestId,
1436
- response instanceof Response ? response.status : 200,
1437
- finishRequestCost(requestCost),
1438
- );
1439
- return response instanceof Response ? response : c.json(response);
1440
- } catch (error) {
1441
- const status = toStatusCode(error);
1442
- const requestId = extractRequestId(rawBody);
1443
- logProviderError(
1444
- logger,
1445
- provider,
1446
- "auth",
1447
- "start",
1448
- requestId,
1449
- error,
1450
- status,
1451
- finishRequestCost(requestCost),
1452
- );
1453
- return c.json(toErrorResponse(error, requestId), status);
1454
- }
1455
- });
1456
-
1457
- app.post("/auth/continue", async (c) => {
1458
- let rawBody: unknown;
1459
- const requestCost = startRequestCost();
1460
- try {
1461
- rawBody = await c.req.raw
1462
- .clone()
1463
- .json()
1464
- .catch(() => undefined);
1465
- const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
1466
- const response = await handleAuthFlow(provider, body, "continue", options, c.req.raw.signal);
1467
- logProviderSuccess(
1468
- logger,
1469
- provider,
1470
- "auth",
1471
- "continue",
1472
- body.requestId,
1473
- response instanceof Response ? response.status : 200,
1474
- finishRequestCost(requestCost),
1475
- );
1476
- return response instanceof Response ? response : c.json(response);
1477
- } catch (error) {
1478
- const status = toStatusCode(error);
1479
- const requestId = extractRequestId(rawBody);
1480
- logProviderError(
1481
- logger,
1482
- provider,
1483
- "auth",
1484
- "continue",
1485
- requestId,
1486
- error,
1487
- status,
1488
- finishRequestCost(requestCost),
1489
- );
1490
- return c.json(toErrorResponse(error, requestId), status);
1491
- }
1492
- });
1493
-
1494
- app.post("/auth/poll", async (c) => {
1495
- let rawBody: unknown;
1496
- const requestCost = startRequestCost();
1497
- try {
1498
- rawBody = await c.req.raw
1499
- .clone()
1500
- .json()
1501
- .catch(() => undefined);
1502
- const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
1503
- const response = await handleAuthFlow(provider, body, "poll", options, c.req.raw.signal);
1504
- logProviderSuccess(
1505
- logger,
1506
- provider,
1507
- "auth",
1508
- "poll",
1509
- body.requestId,
1510
- response instanceof Response ? response.status : 200,
1511
- finishRequestCost(requestCost),
1512
- );
1513
- return response instanceof Response ? response : c.json(response);
1514
- } catch (error) {
1515
- const status = toStatusCode(error);
1516
- const requestId = extractRequestId(rawBody);
1517
- logProviderError(
1518
- logger,
1519
- provider,
1520
- "auth",
1521
- "poll",
1522
- requestId,
1523
- error,
1524
- status,
1525
- finishRequestCost(requestCost),
1526
- );
1527
- return c.json(toErrorResponse(error, requestId), status);
1528
- }
1529
- });
1530
-
1531
- app.post("/auth/refresh", async (c) => {
1532
- let rawBody: unknown;
1533
- const requestCost = startRequestCost();
1534
- try {
1535
- rawBody = await c.req.raw
1536
- .clone()
1537
- .json()
1538
- .catch(() => undefined);
1539
- const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
1540
- const response = await handleAuthFlow(provider, body, "refresh", options, c.req.raw.signal);
1541
- logProviderSuccess(
1542
- logger,
1543
- provider,
1544
- "auth",
1545
- "refresh",
1546
- body.requestId,
1547
- response instanceof Response ? response.status : 200,
1548
- finishRequestCost(requestCost),
1549
- );
1550
- return response instanceof Response ? response : c.json(response);
1551
- } catch (error) {
1552
- const status = toStatusCode(error);
1553
- const requestId = extractRequestId(rawBody);
1554
- logProviderError(
1555
- logger,
1556
- provider,
1557
- "auth",
1558
- "refresh",
1559
- requestId,
1560
- error,
1561
- status,
1562
- finishRequestCost(requestCost),
1563
- );
1564
- return c.json(toErrorResponse(error, requestId), status);
1565
- }
1566
- });
1567
-
1568
- app.post("/auth/disconnect", async (c) => {
1569
- let rawBody: unknown;
1570
- const requestCost = startRequestCost();
1571
- try {
1572
- rawBody = await c.req.raw
1573
- .clone()
1574
- .json()
1575
- .catch(() => undefined);
1576
- const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
1577
- const response = await handleAuthFlow(provider, body, "abort", options, c.req.raw.signal);
1578
- logProviderSuccess(
1579
- logger,
1580
- provider,
1581
- "auth",
1582
- "disconnect",
1583
- body.requestId,
1584
- response instanceof Response ? response.status : 200,
1585
- finishRequestCost(requestCost),
1586
- );
1587
- return response instanceof Response ? response : c.json(response);
1588
- } catch (error) {
1589
- const status = toStatusCode(error);
1590
- const requestId = extractRequestId(rawBody);
1591
- logProviderError(
1592
- logger,
1593
- provider,
1594
- "auth",
1595
- "disconnect",
1596
- requestId,
1597
- error,
1598
- status,
1599
- finishRequestCost(requestCost),
1600
- );
1601
- return c.json(toErrorResponse(error, requestId), status);
1602
- }
1603
- });
1604
-
1605
- return app;
1606
- }
1607
-
1608
- type BunServeRuntime = {
1609
- serve: (options: {
1610
- port: number;
1611
- hostname: string;
1612
- fetch: (request: Request) => Response | Promise<Response>;
1613
- }) => unknown;
1614
- };
1615
-
1616
- function getBunServeRuntime(): BunServeRuntime | undefined {
1617
- const bunValue = Object.getOwnPropertyDescriptor(globalThis, "Bun")?.value;
1618
- if (!bunValue || typeof bunValue !== "object") {
1619
- return undefined;
1620
- }
1621
-
1622
- const serve = Object.getOwnPropertyDescriptor(bunValue, "serve")?.value;
1623
- if (typeof serve !== "function") {
1624
- return undefined;
1625
- }
1626
-
1627
- return {
1628
- serve(options) {
1629
- return serve(options);
1630
- },
1631
- };
1632
- }
1633
-
1634
- export interface ServeOptions extends ProviderServerOptions {
1635
- host?: string;
1636
- port?: number;
1637
- /**
1638
- * Port for the internal self-test listener (default 3001 or
1639
- * APIFUSE__PROVIDER_RUNTIME__SELF_TEST_PORT). The listener only starts
1640
- * when APIFUSE__PROVIDER_RUNTIME__SELF_TEST_MASTER_SECRET is present.
1641
- */
1642
- selfTestPort?: number;
1643
- }
1644
-
1645
- export async function serve(
1646
- provider: ProviderDefinition,
1647
- options: ServeOptions = {},
1648
- ): Promise<void> {
1649
- const bunRuntime = getBunServeRuntime();
1650
-
1651
- if (bunRuntime === undefined) {
1652
- throw new ProviderError("Bun runtime is required to start the provider server", {
1653
- code: "RUNTIME_UNSUPPORTED",
1654
- });
1655
- }
1656
-
1657
- const app = createServerApp(provider, {
1658
- logger: options.logger,
1659
- stt: options.stt,
1660
- });
1661
-
1662
- bunRuntime.serve({
1663
- port: options.port ?? DEFAULT_PORT,
1664
- hostname: options.host ?? DEFAULT_HOST,
1665
- fetch: app.fetch,
1666
- });
1667
-
1668
- // Internal self-test listener (health dependency inversion): a SEPARATE
1669
- // socket the tenant-facing gateway never dials. Off by default — it only
1670
- // starts when the shared self-test master secret env is present.
1671
- const selfTestSecrets = resolveSelfTestMasterSecrets();
1672
- if (selfTestSecrets) {
1673
- const selfTestApp = createSelfTestApp(provider, {
1674
- secrets: selfTestSecrets,
1675
- invoke: createSelfTestInvoke(app),
1676
- authFlow: createSelfTestAuthFlowInvoke(app),
1677
- });
1678
- bunRuntime.serve({
1679
- port: options.selfTestPort ?? resolveSelfTestPort(),
1680
- hostname: options.host ?? DEFAULT_HOST,
1681
- fetch: selfTestApp.fetch,
1682
- });
1683
- }
1684
- await Promise.resolve();
1685
- }
1
+ export * from "./serve-implementation.js";