@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,21 +1,96 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { providerStateRedisUrlFromEnv } from "../config/loader.js";
2
3
  import { ProviderError } from "../errors.js";
3
4
  import { createProviderRedisClient, ensureRedisReady, withRedisTimeout, } from "./redis.js";
4
5
  const DEFAULT_REDIS_TIMEOUT_MS = 250;
5
- const REDIS_STATE_PREFIX = "apifuse:provider-state:v1";
6
+ const REDIS_STATE_PREFIX = "apifuse:provider-state:v2";
7
+ const PROVIDER_SCOPE_DISCRIMINATOR = "scope:provider";
8
+ const MISSING_CONNECTION_SCOPE_DISCRIMINATOR = "scope:connection:missing";
9
+ const SET_WITH_QUOTA_SCRIPT = `
10
+ local now = tonumber(ARGV[1])
11
+ local max_entries = tonumber(ARGV[2])
12
+ local expires_at = tonumber(ARGV[3])
13
+ local index_ttl = tonumber(ARGV[4])
14
+ local envelope = ARGV[5]
15
+
16
+ redis.call("ZREMRANGEBYSCORE", KEYS[2], "-inf", now)
17
+ local indexed = redis.call("ZSCORE", KEYS[2], KEYS[1])
18
+ if not indexed and redis.call("ZCARD", KEYS[2]) >= max_entries then
19
+ return {0, false}
20
+ end
21
+
22
+ redis.call("SET", KEYS[1], envelope, "PXAT", expires_at)
23
+ redis.call("ZADD", KEYS[2], expires_at, KEYS[1])
24
+ redis.call("PEXPIRE", KEYS[2], index_ttl)
25
+ return {1, envelope}
26
+ `;
27
+ const COMPARE_AND_SET_WITH_QUOTA_SCRIPT = `
28
+ local current = redis.call("GET", KEYS[1])
29
+ if current then
30
+ local ok, decoded = pcall(cjson.decode, current)
31
+ if ok and type(decoded) == "table" and decoded.deleted == true then
32
+ current = false
33
+ end
34
+ end
35
+
36
+ local now = tonumber(ARGV[2])
37
+ local max_entries = tonumber(ARGV[3])
38
+ local expires_at = tonumber(ARGV[4])
39
+ local index_ttl = tonumber(ARGV[5])
40
+ local envelope = ARGV[6]
41
+
42
+ local current_version = 0
43
+ local current_decoded = false
44
+ if current then
45
+ local ok, decoded = pcall(cjson.decode, current)
46
+ if not ok or type(decoded) ~= "table" or type(decoded.version) ~= "number" then
47
+ return {-2, current}
48
+ end
49
+ current_version = decoded.version
50
+ current_decoded = decoded
51
+ end
52
+ if current_version ~= tonumber(ARGV[1]) then
53
+ return {-1, current or false}
54
+ end
55
+
56
+ redis.call("ZREMRANGEBYSCORE", KEYS[2], "-inf", now)
57
+ local indexed = redis.call("ZSCORE", KEYS[2], KEYS[1])
58
+ if not indexed and redis.call("ZCARD", KEYS[2]) >= max_entries then
59
+ return {0, false}
60
+ end
61
+
62
+ if current_decoded and type(current_decoded.createdAt) == "string" then
63
+ -- Preserve the original createdAt by patching the encoded envelope in place.
64
+ -- The envelope puts its metadata before "value", so the first match is the
65
+ -- envelope's own key, never a provider value; re-encoding with cjson would
66
+ -- silently turn [] inside the value into {}.
67
+ local preserved = string.gsub(current_decoded.createdAt, "%%", "%%%%")
68
+ envelope = (string.gsub(envelope, '"createdAt":"[^"]*"', '"createdAt":"' .. preserved .. '"', 1))
69
+ end
70
+ redis.call("SET", KEYS[1], envelope, "PXAT", expires_at)
71
+ redis.call("ZADD", KEYS[2], expires_at, KEYS[1])
72
+ redis.call("PEXPIRE", KEYS[2], index_ttl)
73
+ return {1, envelope}
74
+ `;
75
+ const DELETE_WITH_INDEX_SCRIPT = `
76
+ redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2])
77
+ redis.call("ZREM", KEYS[2], KEYS[1])
78
+ return 1
79
+ `;
6
80
  const redisBackends = new Map();
7
- function getRedisBackend(redisUrl) {
81
+ function getRedisBackend(redisUrl, injectedRedis) {
8
82
  const existing = redisBackends.get(redisUrl);
9
83
  if (existing)
10
84
  return existing;
11
- const redis = createProviderRedisClient({
12
- redisUrl,
13
- timeoutMs: DEFAULT_REDIS_TIMEOUT_MS,
14
- onError: () => {
15
- // Runtime state operations fail closed at their call sites. Avoid noisy
16
- // unhandled Redis errors from background reconnect attempts.
17
- },
18
- });
85
+ const redis = injectedRedis ??
86
+ createProviderRedisClient({
87
+ redisUrl,
88
+ timeoutMs: DEFAULT_REDIS_TIMEOUT_MS,
89
+ onError: () => {
90
+ // Runtime state operations fail closed at their call sites. Avoid noisy
91
+ // unhandled Redis errors from background reconnect attempts.
92
+ },
93
+ });
19
94
  const backend = { redis };
20
95
  redisBackends.set(redisUrl, backend);
21
96
  return backend;
@@ -36,15 +111,26 @@ async function requireRedisReady(redis) {
36
111
  return;
37
112
  throw new UnsupportedProviderStateError("Provider runtime state Redis is unavailable");
38
113
  }
39
- function providerStatePrefix(providerId, namespace) {
40
- return `${REDIS_STATE_PREFIX}:${providerId ?? "default"}:${namespace}`;
114
+ function connectionScopeDiscriminator(connectionId) {
115
+ if (connectionId === undefined)
116
+ return MISSING_CONNECTION_SCOPE_DISCRIMINATOR;
117
+ const digest = createHash("sha256").update(connectionId, "utf8").digest("hex");
118
+ return `scope:connection:sha256:${digest}`;
119
+ }
120
+ function providerStatePrefix(providerId, namespace, scopeDiscriminator) {
121
+ return `${REDIS_STATE_PREFIX}:${providerId ?? "default"}:${namespace}:${scopeDiscriminator}`;
41
122
  }
42
- function providerStateKey(providerId, namespace, key) {
43
- return `${providerStatePrefix(providerId, namespace)}:${key}`;
123
+ function providerStateKey(providerId, namespace, scopeDiscriminator, key) {
124
+ return `${providerStatePrefix(providerId, namespace, scopeDiscriminator)}:${key}`;
44
125
  }
45
- function publicStateKey(providerId, namespace, redisKey) {
46
- const prefix = `${providerStatePrefix(providerId, namespace)}:`;
47
- return redisKey.startsWith(prefix) ? redisKey.slice(prefix.length) : redisKey;
126
+ function publicStateKey(redisKey, prefixes) {
127
+ for (const prefix of prefixes) {
128
+ const prefixWithSeparator = `${prefix}:`;
129
+ if (redisKey.startsWith(prefixWithSeparator)) {
130
+ return redisKey.slice(prefixWithSeparator.length);
131
+ }
132
+ }
133
+ return redisKey;
48
134
  }
49
135
  function parseStateDurationMs(ttl) {
50
136
  const match = /^(\d+)(ms|s|m|h|d)$/.exec(ttl ?? "1h");
@@ -63,13 +149,21 @@ function parseStateDurationMs(ttl) {
63
149
  : 86_400_000;
64
150
  return Math.max(1, amount * multiplier);
65
151
  }
66
- function resolveExpiresAt(ttl) {
67
- return new Date(Date.now() + parseStateDurationMs(ttl)).toISOString();
68
- }
69
152
  function envelopeFromJson(key, raw) {
70
153
  if (!raw)
71
154
  return null;
72
- const parsed = JSON.parse(raw);
155
+ // A corrupt/undecodable persisted envelope must be treated as absent rather
156
+ // than throwing a raw JSON.parse SyntaxError: an uncaught SyntaxError escapes
157
+ // the provider error taxonomy, is masked as internal_error 500, and is then
158
+ // retried by the hub (2026-07-22 catchtable reserve RCA, candidate A). Returning
159
+ // null also keeps list() from aborting the whole scan on a single bad entry.
160
+ let parsed;
161
+ try {
162
+ parsed = JSON.parse(raw);
163
+ }
164
+ catch {
165
+ return null;
166
+ }
73
167
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
74
168
  return null;
75
169
  }
@@ -90,83 +184,134 @@ function envelopeFromJson(key, raw) {
90
184
  updatedAt: record.updatedAt,
91
185
  };
92
186
  }
187
+ function isStateTombstone(raw) {
188
+ if (!raw)
189
+ return false;
190
+ try {
191
+ const parsed = JSON.parse(raw);
192
+ return (parsed !== null &&
193
+ typeof parsed === "object" &&
194
+ !Array.isArray(parsed) &&
195
+ parsed.deleted === true);
196
+ }
197
+ catch {
198
+ return false;
199
+ }
200
+ }
93
201
  function redisEnvelope(value, version, createdAt, expiresAt) {
94
202
  const updatedAt = new Date().toISOString();
95
- return { value, version, expiresAt, createdAt, updatedAt };
203
+ // Metadata precedes `value` on purpose: the compare-and-set Lua script patches
204
+ // `createdAt` in the encoded string (first occurrence) instead of decoding and
205
+ // re-encoding with cjson, which would turn empty arrays inside `value` into
206
+ // objects. Keep the key order stable.
207
+ return { version, expiresAt, createdAt, updatedAt, value };
96
208
  }
97
209
  class RedisProviderStateNamespace {
98
210
  backend;
99
211
  providerId;
100
212
  namespaceName;
101
213
  options;
102
- constructor(backend, providerId, namespaceName, options) {
214
+ scopeDiscriminator;
215
+ constructor(backend, providerId, namespaceName, options, scopeDiscriminator) {
103
216
  this.backend = backend;
104
217
  this.providerId = providerId;
105
218
  this.namespaceName = namespaceName;
106
219
  this.options = options;
220
+ this.scopeDiscriminator = scopeDiscriminator;
107
221
  }
108
222
  redisKey(key) {
109
- return providerStateKey(this.providerId, this.namespaceName, key);
223
+ return providerStateKey(this.providerId, this.namespaceName, this.scopeDiscriminator, key);
224
+ }
225
+ statePrefix() {
226
+ return providerStatePrefix(this.providerId, this.namespaceName, this.scopeDiscriminator);
110
227
  }
111
- prefix() {
112
- return `${providerStatePrefix(this.providerId, this.namespaceName)}:`;
228
+ indexKey() {
229
+ // Keep bookkeeping outside the caller-owned keyspace. A provider may use
230
+ // any state key (including "__index"), so a suffix inside the namespace
231
+ // could turn the ZSET into a string and break every subsequent write.
232
+ const namespaceIdentity = Buffer.from(providerStatePrefix(this.providerId, this.namespaceName, this.scopeDiscriminator), "utf8").toString("base64url");
233
+ return `${REDIS_STATE_PREFIX}:index:${namespaceIdentity}`;
113
234
  }
114
- async activeKeys() {
235
+ async indexedKeys(limit) {
115
236
  await requireRedisReady(this.backend.redis);
116
- return await withRequiredRedis(() => this.backend.redis.keys(`${this.prefix()}*`));
237
+ const now = Date.now();
238
+ return await withRequiredRedis(async () => {
239
+ await this.backend.redis.zremrangebyscore(this.indexKey(), "-inf", now);
240
+ return await this.backend.redis.zrangebyscore(this.indexKey(), now + 1, "+inf", "LIMIT", 0, limit);
241
+ });
242
+ }
243
+ async readRaw(key) {
244
+ await requireRedisReady(this.backend.redis);
245
+ return await withRequiredRedis(() => this.backend.redis.get(this.redisKey(key)));
117
246
  }
118
247
  enforceValueSize(value) {
119
248
  const bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
120
249
  if (bytes > this.options.maxValueBytes) {
121
- throw new UnsupportedProviderStateError(`Provider runtime state value exceeds maxValueBytes (${bytes} > ${this.options.maxValueBytes})`);
250
+ throw stateViolationError(`Provider runtime state value exceeds maxValueBytes (${bytes} > ${this.options.maxValueBytes})`, "size");
122
251
  }
123
252
  }
124
- async enforceMaxEntries(key) {
125
- const keys = await this.activeKeys();
126
- const redisKey = this.redisKey(key);
127
- const otherKeys = keys.filter((candidate) => candidate !== redisKey);
128
- if (otherKeys.length >= this.options.maxEntries) {
129
- throw new UnsupportedProviderStateError(`Provider runtime state namespace quota exceeded (${otherKeys.length + 1} > ${this.options.maxEntries})`);
253
+ quotaExceeded() {
254
+ return stateViolationError(`Provider runtime state namespace quota exceeded (${this.options.maxEntries + 1} > ${this.options.maxEntries})`, "quota");
255
+ }
256
+ writeTiming(ttl) {
257
+ const ttlMs = parseStateDurationMs(ttl ?? this.options.defaultTtl);
258
+ const maxTtlMs = parseStateDurationMs(this.options.maxTtl);
259
+ if (ttlMs > maxTtlMs) {
260
+ throw stateViolationError(`Provider runtime state ttl exceeds maxTtl (${ttlMs} > ${maxTtlMs})`, "ttl");
130
261
  }
262
+ const expiresAtMs = Date.now() + ttlMs;
263
+ return {
264
+ expiresAt: new Date(expiresAtMs).toISOString(),
265
+ expiresAtMs,
266
+ indexTtlMs: maxTtlMs,
267
+ };
131
268
  }
132
269
  async list(options) {
133
- const keys = (await this.activeKeys()).filter((key) => {
134
- const publicKey = publicStateKey(this.providerId, this.namespaceName, key);
135
- return options?.prefix ? publicKey.startsWith(options.prefix) : true;
136
- });
137
- const limited = keys.slice(0, Math.max(0, options?.limit ?? keys.length));
138
- if (limited.length === 0)
270
+ const requestedLimit = Math.max(0, options?.limit ?? this.options.maxEntries);
271
+ if (requestedLimit === 0)
272
+ return recordStateRedisNoop([]);
273
+ const keys = await this.indexedKeys(this.options.maxEntries);
274
+ if (keys.length === 0)
139
275
  return [];
140
- const values = await withRequiredRedis(() => this.backend.redis.mget(limited));
141
- return values.flatMap((raw, index) => {
142
- const key = limited[index];
143
- if (!key)
144
- return [];
145
- const value = envelopeFromJson(publicStateKey(this.providerId, this.namespaceName, key), raw);
146
- return value ? [value] : [];
147
- });
276
+ const values = await withRequiredRedis(() => this.backend.redis.mget(keys));
277
+ const rows = [];
278
+ for (const [index, raw] of values.entries()) {
279
+ const redisKey = keys[index];
280
+ if (!redisKey)
281
+ continue;
282
+ const publicKey = publicStateKey(redisKey, [this.statePrefix()]);
283
+ if (options?.prefix && !publicKey.startsWith(options.prefix))
284
+ continue;
285
+ const value = envelopeFromJson(publicKey, raw);
286
+ if (!value)
287
+ continue;
288
+ rows.push(value);
289
+ }
290
+ return rows.slice(0, requestedLimit);
148
291
  }
149
292
  async get(key) {
150
- await requireRedisReady(this.backend.redis);
151
- const raw = await withRequiredRedis(() => this.backend.redis.get(this.redisKey(key)));
293
+ const raw = await this.readRaw(key);
294
+ if (isStateTombstone(raw))
295
+ return null;
152
296
  return envelopeFromJson(key, raw);
153
297
  }
154
298
  async set(key, value, options) {
155
299
  this.enforceValueSize(value);
156
- await this.enforceMaxEntries(key);
157
300
  const current = await this.get(key);
158
301
  const createdAt = current?.createdAt ?? new Date().toISOString();
159
302
  const version = (current?.version ?? 0) + 1;
160
- const ttl = options?.ttl ?? this.options.defaultTtl;
161
- const ttlMs = parseStateDurationMs(ttl);
162
- const expiresAt = resolveExpiresAt(ttl);
163
- const envelope = redisEnvelope(value, version, createdAt, expiresAt);
164
- await withRequiredRedis(() => this.backend.redis.set(this.redisKey(key), JSON.stringify(envelope), "PX", ttlMs));
303
+ const timing = this.writeTiming(options?.ttl);
304
+ const envelope = redisEnvelope(value, version, createdAt, timing.expiresAt);
305
+ await requireRedisReady(this.backend.redis);
306
+ const result = await withRequiredRedis(() => this.backend.redis.eval(SET_WITH_QUOTA_SCRIPT, 2, this.redisKey(key), this.indexKey(), String(Date.now()), String(this.options.maxEntries), String(timing.expiresAtMs), String(timing.indexTtlMs), JSON.stringify(envelope)));
307
+ if (!Array.isArray(result) || Number(result[0]) !== 1) {
308
+ throw this.quotaExceeded();
309
+ }
165
310
  return {
166
311
  key,
167
312
  value,
168
313
  version,
169
- expiresAt,
314
+ expiresAt: timing.expiresAt,
170
315
  createdAt,
171
316
  updatedAt: envelope.updatedAt,
172
317
  };
@@ -179,15 +324,29 @@ class RedisProviderStateNamespace {
179
324
  }
180
325
  async compareAndSet(key, expectedVersion, value, options) {
181
326
  this.enforceValueSize(value);
182
- const current = await this.get(key);
183
- if ((current?.version ?? 0) !== expectedVersion) {
184
- return { ok: false, current };
327
+ const createdAt = new Date().toISOString();
328
+ const timing = this.writeTiming(options?.ttl);
329
+ const envelope = redisEnvelope(value, expectedVersion + 1, createdAt, timing.expiresAt);
330
+ await requireRedisReady(this.backend.redis);
331
+ const result = await withRequiredRedis(() => this.backend.redis.eval(COMPARE_AND_SET_WITH_QUOTA_SCRIPT, 2, this.redisKey(key), this.indexKey(), String(expectedVersion), String(Date.now()), String(this.options.maxEntries), String(timing.expiresAtMs), String(timing.indexTtlMs), JSON.stringify(envelope)));
332
+ if (Array.isArray(result) && Number(result[0]) === 0) {
333
+ throw this.quotaExceeded();
334
+ }
335
+ if (!Array.isArray(result) || Number(result[0]) !== 1) {
336
+ const rawCurrent = Array.isArray(result) && typeof result[1] === "string" ? result[1] : null;
337
+ return { ok: false, current: envelopeFromJson(key, rawCurrent) };
185
338
  }
186
- return { ok: true, value: await this.set(key, value, options) };
339
+ const rawWritten = typeof result[1] === "string" ? result[1] : null;
340
+ const written = envelopeFromJson(key, rawWritten);
341
+ if (!written) {
342
+ throw new UnsupportedProviderStateError("Provider runtime state CAS returned an invalid value");
343
+ }
344
+ return { ok: true, value: written };
187
345
  }
188
346
  async delete(key) {
189
347
  await requireRedisReady(this.backend.redis);
190
- await withRequiredRedis(() => this.backend.redis.del(this.redisKey(key)));
348
+ const tombstone = JSON.stringify({ deleted: true });
349
+ await withRequiredRedis(() => this.backend.redis.eval(DELETE_WITH_INDEX_SCRIPT, 2, this.redisKey(key), this.indexKey(), tombstone, String(parseStateDurationMs(this.options.maxTtl))));
191
350
  }
192
351
  async increment(key, field, delta = 1, options) {
193
352
  const current = (await this.get(key))?.value ?? {};
@@ -198,20 +357,46 @@ class RedisProviderStateNamespace {
198
357
  class RedisProviderRuntimeState {
199
358
  backend;
200
359
  providerId;
201
- constructor(options) {
202
- this.backend = getRedisBackend(options.redisUrl);
360
+ redisUrl;
361
+ scopeDiscriminator;
362
+ constructor(options, scopeDiscriminator = MISSING_CONNECTION_SCOPE_DISCRIMINATOR) {
363
+ this.backend = getRedisBackend(options.redisUrl, options.__redisClient);
203
364
  this.providerId = options.providerId;
365
+ this.redisUrl = options.redisUrl;
366
+ this.scopeDiscriminator = scopeDiscriminator;
367
+ }
368
+ forConnection(connectionId) {
369
+ return new RedisProviderRuntimeState({ redisUrl: this.redisUrl, providerId: this.providerId }, connectionScopeDiscriminator(connectionId));
204
370
  }
205
371
  namespace(name, options) {
206
- return new RedisProviderStateNamespace(this.backend, this.providerId, name, options);
372
+ return new RedisProviderStateNamespace(this.backend, this.providerId, name, options, options.scope === "provider" ? PROVIDER_SCOPE_DISCRIMINATOR : this.scopeDiscriminator);
207
373
  }
208
374
  }
209
375
  export class UnsupportedProviderStateError extends ProviderError {
210
376
  constructor(message = "Provider runtime state is not available in this runtime") {
211
377
  super(message, { code: "PROVIDER_STATE_UNSUPPORTED" });
212
378
  this.name = "UnsupportedProviderStateError";
379
+ stateViolationKinds.set(this, "unsupported");
213
380
  }
214
381
  }
382
+ // Keep the exact no-command result out of band, without inspecting caller options twice.
383
+ const stateRedisNoopResults = new WeakSet();
384
+ function recordStateRedisNoop(result) {
385
+ stateRedisNoopResults.add(result);
386
+ return result;
387
+ }
388
+ export function isStateRedisNoop(result) {
389
+ return result !== null && typeof result === "object" && stateRedisNoopResults.has(result);
390
+ }
391
+ const stateViolationKinds = new WeakMap();
392
+ function stateViolationError(message, kind) {
393
+ const error = new UnsupportedProviderStateError(message);
394
+ stateViolationKinds.set(error, kind);
395
+ return error;
396
+ }
397
+ export function getStateViolation(error) {
398
+ return error !== null && typeof error === "object" ? stateViolationKinds.get(error) : undefined;
399
+ }
215
400
  class UnsupportedProviderStateNamespace {
216
401
  async list(_options) {
217
402
  throw new UnsupportedProviderStateError();
@@ -236,18 +421,40 @@ class UnsupportedProviderStateNamespace {
236
421
  }
237
422
  }
238
423
  class UnsupportedProviderRuntimeState {
424
+ forConnection(_connectionId) {
425
+ return new UnsupportedProviderRuntimeState();
426
+ }
239
427
  namespace(_name, _options) {
240
428
  return new UnsupportedProviderStateNamespace();
241
429
  }
242
430
  }
243
431
  class MemoryProviderStateNamespace {
244
432
  options;
433
+ now;
245
434
  // biome-ignore lint/suspicious/noExplicitAny: in-memory state stores heterogeneous generic values by key.
246
435
  values = new Map();
247
- constructor(options) {
436
+ constructor(options, now = Date.now) {
248
437
  this.options = options;
438
+ this.now = now;
249
439
  }
250
- pruneExpired(nowMs = Date.now()) {
440
+ enforceValueSize(value) {
441
+ const bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
442
+ if (bytes > this.options.maxValueBytes) {
443
+ throw stateViolationError(`Provider runtime state value exceeds maxValueBytes (${bytes} > ${this.options.maxValueBytes})`, "size");
444
+ }
445
+ }
446
+ enforceWritePolicy(key, value, ttl) {
447
+ this.enforceValueSize(value);
448
+ const ttlMs = parseStateDurationMs(ttl ?? this.options.defaultTtl);
449
+ const maxTtlMs = parseStateDurationMs(this.options.maxTtl);
450
+ if (ttlMs > maxTtlMs) {
451
+ throw stateViolationError(`Provider runtime state ttl exceeds maxTtl (${ttlMs} > ${maxTtlMs})`, "ttl");
452
+ }
453
+ if (!this.values.has(key) && this.values.size >= this.options.maxEntries) {
454
+ throw stateViolationError(`Provider runtime state namespace quota exceeded (${this.options.maxEntries + 1} > ${this.options.maxEntries})`, "quota");
455
+ }
456
+ }
457
+ pruneExpired(nowMs = this.now()) {
251
458
  for (const [key, row] of this.values.entries()) {
252
459
  if (row.expiresAt && Date.parse(row.expiresAt) <= nowMs) {
253
460
  this.values.delete(key);
@@ -265,9 +472,10 @@ class MemoryProviderStateNamespace {
265
472
  }
266
473
  async set(key, value, options) {
267
474
  this.pruneExpired();
268
- const now = new Date().toISOString();
475
+ this.enforceWritePolicy(key, value, options?.ttl);
476
+ const now = new Date(this.now()).toISOString();
269
477
  const current = this.values.get(key);
270
- const expiresAt = resolveMemoryStateExpiresAt(options?.ttl ?? this.options.defaultTtl);
478
+ const expiresAt = resolveMemoryStateExpiresAt(options?.ttl ?? this.options.defaultTtl, this.now());
271
479
  const row = {
272
480
  key,
273
481
  value,
@@ -282,8 +490,24 @@ class MemoryProviderStateNamespace {
282
490
  async patch(_key, _partial, _options) {
283
491
  throw new UnsupportedProviderStateError("In-memory provider runtime state does not support patch");
284
492
  }
285
- async compareAndSet(_key, _expectedVersion, _value, _options) {
286
- throw new UnsupportedProviderStateError("In-memory provider runtime state does not support compareAndSet");
493
+ async compareAndSet(key, expectedVersion, value, options) {
494
+ this.pruneExpired();
495
+ const current = this.values.get(key);
496
+ if ((current?.version ?? 0) !== expectedVersion) {
497
+ return { ok: false, current: current ?? null };
498
+ }
499
+ this.enforceWritePolicy(key, value, options?.ttl);
500
+ const now = new Date(this.now()).toISOString();
501
+ const stored = {
502
+ key,
503
+ value,
504
+ version: expectedVersion + 1,
505
+ expiresAt: resolveMemoryStateExpiresAt(options?.ttl ?? this.options.defaultTtl, this.now()),
506
+ createdAt: current?.createdAt ?? now,
507
+ updatedAt: now,
508
+ };
509
+ this.values.set(key, stored);
510
+ return { ok: true, value: stored };
287
511
  }
288
512
  async delete(key) {
289
513
  this.values.delete(key);
@@ -293,20 +517,33 @@ class MemoryProviderStateNamespace {
293
517
  }
294
518
  }
295
519
  class MemoryProviderRuntimeState {
296
- namespaces = new Map();
297
- namespace(name, _options) {
298
- const existing = this.namespaces.get(name);
520
+ backend;
521
+ scopeDiscriminator;
522
+ constructor(backend = {
523
+ namespaces: new Map(),
524
+ now: Date.now,
525
+ }, scopeDiscriminator = MISSING_CONNECTION_SCOPE_DISCRIMINATOR) {
526
+ this.backend = backend;
527
+ this.scopeDiscriminator = scopeDiscriminator;
528
+ }
529
+ forConnection(connectionId) {
530
+ return new MemoryProviderRuntimeState(this.backend, connectionScopeDiscriminator(connectionId));
531
+ }
532
+ namespace(name, options) {
533
+ const scopeDiscriminator = options.scope === "provider" ? PROVIDER_SCOPE_DISCRIMINATOR : this.scopeDiscriminator;
534
+ const namespaceIdentity = `${scopeDiscriminator}\0${name}`;
535
+ const existing = this.backend.namespaces.get(namespaceIdentity);
299
536
  if (existing)
300
537
  return existing;
301
- const created = new MemoryProviderStateNamespace(_options);
302
- this.namespaces.set(name, created);
538
+ const created = new MemoryProviderStateNamespace(options, this.backend.now);
539
+ this.backend.namespaces.set(namespaceIdentity, created);
303
540
  return created;
304
541
  }
305
542
  }
306
- function resolveMemoryStateExpiresAt(ttl) {
543
+ function resolveMemoryStateExpiresAt(ttl, nowMs = Date.now()) {
307
544
  const match = /^(\d+)(ms|s|m|h|d)$/.exec(ttl ?? "1h");
308
545
  if (!match)
309
- return new Date(Date.now() + 3_600_000).toISOString();
546
+ return new Date(nowMs + 3_600_000).toISOString();
310
547
  const amount = Number(match[1]);
311
548
  const unit = match[2];
312
549
  const multiplier = unit === "ms"
@@ -318,7 +555,7 @@ function resolveMemoryStateExpiresAt(ttl) {
318
555
  : unit === "h"
319
556
  ? 3_600_000
320
557
  : 86_400_000;
321
- return new Date(Date.now() + amount * multiplier).toISOString();
558
+ return new Date(nowMs + amount * multiplier).toISOString();
322
559
  }
323
560
  export function createRedisProviderRuntimeState(options) {
324
561
  return new RedisProviderRuntimeState(options);
@@ -336,8 +573,11 @@ export function createProviderRuntimeStateFromEnv(options = {}) {
336
573
  }
337
574
  return createUnsupportedProviderRuntimeState();
338
575
  }
339
- export function createMemoryProviderRuntimeState() {
340
- return new MemoryProviderRuntimeState();
576
+ export function createMemoryProviderRuntimeState(options) {
577
+ return new MemoryProviderRuntimeState({
578
+ namespaces: new Map(),
579
+ now: options?.now ?? Date.now,
580
+ });
341
581
  }
342
582
  export function createUnsupportedProviderRuntimeState() {
343
583
  return new UnsupportedProviderRuntimeState();
@@ -0,0 +1,54 @@
1
+ import type { ChallengeSolution, ProviderChallenge, StealthResponse } from "../types.js";
2
+ import type { ResolverVendorTransport } from "./resolver-vendors/types.js";
3
+ import type { StealthCookieJar } from "./stealth-cookies.js";
4
+ export type AkamaiSbsdChallenge = Extract<ProviderChallenge, {
5
+ readonly kind: "akamai_sbsd";
6
+ }>;
7
+ /**
8
+ * SDK-owned response detection and solve wiring derived from the provider declaration.
9
+ * The server attaches it to the stealth client options it builds; it is deliberately not
10
+ * part of the public `StealthClientOptions` type.
11
+ */
12
+ export type StealthChallengeRuntime = {
13
+ readonly akamaiSbsd?: {
14
+ readonly allowedHosts: readonly string[];
15
+ /** Resolver-declared client profile; must name the initiating session's browser family. */
16
+ readonly clientProfile?: string;
17
+ readonly solve?: (challenge: AkamaiSbsdChallenge, transport: ResolverVendorTransport, signal: AbortSignal) => Promise<ChallengeSolution>;
18
+ };
19
+ };
20
+ export type AkamaiSbsdSessionState = {
21
+ /**
22
+ * Latest v-only script for this session; Phase 2 deliberately has no wall-clock TTL.
23
+ * Challenge-state expiry belongs to the Phase 3 ceremony/solve lease handle
24
+ * (ADR-0010 v1.1), not to the stealth session.
25
+ */
26
+ rememberedScript?: URL;
27
+ /** In-flight solves keyed by akamaiSbsdChallengeKey; an entry leaves only when it settles. */
28
+ readonly transactions: Map<string, {
29
+ readonly result: Promise<{
30
+ readonly solved: true;
31
+ } | {
32
+ readonly solved: false;
33
+ error: unknown;
34
+ }>;
35
+ }>;
36
+ /**
37
+ * Key of the last automatic (safe-method) solve whose refetch was not challenged again.
38
+ * An explicit replay for the same key reuses the solved cookies once instead of solving
39
+ * again; any newer solve or lease expiry clears it.
40
+ */
41
+ completedSuccessKey?: string;
42
+ };
43
+ export declare function findAkamaiSbsdScript(body: string, page: URL): URL | undefined;
44
+ export declare function detectAkamaiSbsdChallenge(response: Pick<StealthResponse, "url" | "body">, pageUrl: string, jar: Pick<StealthCookieJar, "has">, allowedHosts: readonly string[], state: AkamaiSbsdSessionState): AkamaiSbsdChallenge | undefined;
45
+ /**
46
+ * One in-flight solve per session, challenge, and bound identity: requests on one session
47
+ * may still pick a different wreq browser/OS or proxy, and a solve is only valid for the
48
+ * identity that ran it.
49
+ */
50
+ export declare function akamaiSbsdChallengeKey(challenge: AkamaiSbsdChallenge, identity: {
51
+ readonly wreqBrowser: string;
52
+ readonly wreqOs: string;
53
+ readonly proxyUrl: string | undefined;
54
+ }): string;