@apifuse/provider-sdk 2.2.0-beta.5 → 2.2.0-beta.50

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 (365) hide show
  1. package/AUTHORING.md +537 -33
  2. package/CHANGELOG.md +201 -1
  3. package/README.md +83 -22
  4. package/SUBMISSION.md +2 -2
  5. package/bin/apifuse-check.ts +165 -11
  6. package/bin/apifuse-dev.ts +57 -13
  7. package/bin/apifuse-migrate-operation-declaration.ts +55 -0
  8. package/bin/apifuse-migrate-shape.ts +202 -0
  9. package/bin/apifuse-pack-check.ts +22 -2
  10. package/bin/apifuse-pack-smoke.ts +57 -2
  11. package/bin/apifuse-pack-types.ts +357 -38
  12. package/bin/apifuse-perf.ts +14 -13
  13. package/bin/apifuse-record.ts +709 -72
  14. package/bin/apifuse-submit-check.ts +2344 -324
  15. package/bin/apifuse-sync-assets.ts +117 -0
  16. package/bin/submit-check-delimited-text.ts +50 -0
  17. package/dist/auth-turn/index.d.ts +3 -3
  18. package/dist/auth-turn/index.js +1 -1
  19. package/dist/auth.d.ts +14 -0
  20. package/dist/auth.js +67 -0
  21. package/dist/ceremonies/index.d.ts +16 -0
  22. package/dist/ceremonies/index.js +141 -36
  23. package/dist/cli/commands.d.ts +1 -1
  24. package/dist/cli/commands.js +27 -0
  25. package/dist/cli/create.d.ts +4 -1
  26. package/dist/cli/create.js +42 -37
  27. package/dist/cli/migrate-operation-declaration.d.ts +59 -0
  28. package/dist/cli/migrate-operation-declaration.js +1178 -0
  29. package/dist/cli/migrate-operation-shape.d.ts +44 -0
  30. package/dist/cli/migrate-operation-shape.js +113 -0
  31. package/dist/cli/migrate-provider-shape.d.ts +52 -0
  32. package/dist/cli/migrate-provider-shape.js +578 -0
  33. package/dist/cli/prompt-assets.d.ts +80 -0
  34. package/dist/cli/prompt-assets.js +743 -0
  35. package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
  36. package/dist/cli/templates/provider/Dockerfile.tpl +1 -1
  37. package/dist/cli/templates/provider/README.md.tpl +5 -5
  38. package/dist/cli/templates/provider/index.ts.tpl +6 -3
  39. package/dist/cli/templates/provider/operations/ping.ts.tpl +4 -1
  40. package/dist/cli/templates/provider/provider.json.tpl +6 -0
  41. package/dist/config/loader.d.ts +179 -16
  42. package/dist/config/loader.js +435 -127
  43. package/dist/contract-serialization.js +4 -8
  44. package/dist/contract-types.d.ts +12 -5
  45. package/dist/contract.js +24 -10
  46. package/dist/declaration-validation.d.ts +33 -0
  47. package/dist/declaration-validation.js +266 -0
  48. package/dist/define.d.ts +66 -29
  49. package/dist/define.js +778 -69
  50. package/dist/dev.d.ts +3 -0
  51. package/dist/dev.js +1 -1
  52. package/dist/engine.d.ts +78 -0
  53. package/dist/engine.js +133 -0
  54. package/dist/error-observability.d.ts +7 -0
  55. package/dist/error-observability.js +61 -0
  56. package/dist/error-resolution.d.ts +4 -0
  57. package/dist/error-resolution.js +122 -0
  58. package/dist/errors.d.ts +33 -0
  59. package/dist/errors.js +40 -0
  60. package/dist/fixture-sanitization.d.ts +28 -0
  61. package/dist/fixture-sanitization.js +227 -0
  62. package/dist/health-scenario.d.ts +1842 -0
  63. package/dist/health-scenario.js +624 -0
  64. package/dist/index.d.ts +21 -10
  65. package/dist/index.js +12 -7
  66. package/dist/lint.d.ts +23 -11
  67. package/dist/lint.js +572 -69
  68. package/dist/native-address.d.ts +43 -0
  69. package/dist/native-address.js +281 -0
  70. package/dist/native-egress-policy.d.ts +31 -0
  71. package/dist/native-egress-policy.js +288 -0
  72. package/dist/observability.d.ts +5 -2
  73. package/dist/observability.js +48 -1
  74. package/dist/provider.d.ts +10 -2
  75. package/dist/provider.js +4 -1
  76. package/dist/runtime/auth-flow.d.ts +5 -1
  77. package/dist/runtime/auth-flow.js +6 -0
  78. package/dist/runtime/browser.d.ts +1 -0
  79. package/dist/runtime/browser.js +492 -49
  80. package/dist/runtime/cache.d.ts +1 -0
  81. package/dist/runtime/cache.js +169 -15
  82. package/dist/runtime/choice-wordlist.d.ts +9 -0
  83. package/dist/runtime/choice-wordlist.js +138 -0
  84. package/dist/runtime/choice.d.ts +13 -1
  85. package/dist/runtime/choice.js +490 -102
  86. package/dist/runtime/chrome149-header-order.d.ts +58 -0
  87. package/dist/runtime/chrome149-header-order.js +289 -0
  88. package/dist/runtime/env.js +12 -0
  89. package/dist/runtime/executor.d.ts +3 -2
  90. package/dist/runtime/executor.js +26 -25
  91. package/dist/runtime/http.d.ts +1 -0
  92. package/dist/runtime/http.js +515 -53
  93. package/dist/runtime/insights.js +2 -2
  94. package/dist/runtime/instrumentation.d.ts +2 -2
  95. package/dist/runtime/instrumentation.js +366 -8
  96. package/dist/runtime/native-network-errors.d.ts +33 -0
  97. package/dist/runtime/native-network-errors.js +69 -0
  98. package/dist/runtime/native-network.d.ts +96 -0
  99. package/dist/runtime/native-network.js +1232 -0
  100. package/dist/runtime/ocr.d.ts +29 -0
  101. package/dist/runtime/ocr.js +440 -0
  102. package/dist/runtime/otlp.d.ts +71 -2
  103. package/dist/runtime/otlp.js +397 -16
  104. package/dist/runtime/proxy-errors.js +6 -2
  105. package/dist/runtime/proxy-nodemaven.d.ts +56 -0
  106. package/dist/runtime/proxy-nodemaven.js +146 -0
  107. package/dist/runtime/proxy-telemetry.d.ts +80 -1
  108. package/dist/runtime/proxy-telemetry.js +154 -47
  109. package/dist/runtime/redirects.d.ts +29 -0
  110. package/dist/runtime/redirects.js +36 -0
  111. package/dist/runtime/redis.d.ts +1 -1
  112. package/dist/runtime/redis.js +4 -2
  113. package/dist/runtime/request-options.d.ts +68 -1
  114. package/dist/runtime/request-options.js +548 -0
  115. package/dist/runtime/resolver-config.d.ts +6 -0
  116. package/dist/runtime/resolver-config.js +6 -0
  117. package/dist/runtime/resolver-public.d.ts +1 -0
  118. package/dist/runtime/resolver-public.js +1 -0
  119. package/dist/runtime/resolver-shared.d.ts +14 -0
  120. package/dist/runtime/resolver-shared.js +12 -0
  121. package/dist/runtime/resolver-vendors/bindings.d.ts +48 -0
  122. package/dist/runtime/resolver-vendors/bindings.js +40 -0
  123. package/dist/runtime/resolver-vendors/browser.d.ts +22 -0
  124. package/dist/runtime/resolver-vendors/browser.js +377 -0
  125. package/dist/runtime/resolver-vendors/capsolver.d.ts +22 -0
  126. package/dist/runtime/resolver-vendors/capsolver.js +531 -0
  127. package/dist/runtime/resolver-vendors/hosts.d.ts +2 -0
  128. package/dist/runtime/resolver-vendors/hosts.js +33 -0
  129. package/dist/runtime/resolver-vendors/twocaptcha.d.ts +24 -0
  130. package/dist/runtime/resolver-vendors/twocaptcha.js +408 -0
  131. package/dist/runtime/resolver-vendors/types.d.ts +94 -0
  132. package/dist/runtime/resolver-vendors/types.js +96 -0
  133. package/dist/runtime/resolver.d.ts +72 -0
  134. package/dist/runtime/resolver.js +771 -0
  135. package/dist/runtime/secrets.d.ts +27 -0
  136. package/dist/runtime/secrets.js +51 -0
  137. package/dist/runtime/state.d.ts +3 -0
  138. package/dist/runtime/state.js +277 -71
  139. package/dist/runtime/stealth-cookies.d.ts +20 -0
  140. package/dist/runtime/stealth-cookies.js +111 -0
  141. package/dist/runtime/stealth.d.ts +41 -5
  142. package/dist/runtime/stealth.js +997 -291
  143. package/dist/runtime/stt.js +1 -12
  144. package/dist/runtime/timeout.d.ts +5 -0
  145. package/dist/runtime/timeout.js +12 -0
  146. package/dist/runtime/trace-config.d.ts +12 -0
  147. package/dist/runtime/trace-config.js +62 -0
  148. package/dist/runtime/trace.d.ts +5 -0
  149. package/dist/runtime/trace.js +43 -10
  150. package/dist/serve.d.ts +1 -1
  151. package/dist/serve.js +1 -1
  152. package/dist/server/error-observability.d.ts +1 -0
  153. package/dist/server/error-observability.js +1 -0
  154. package/dist/server/index.d.ts +5 -2
  155. package/dist/server/index.js +2 -2
  156. package/dist/server/self-test-input-tokens.d.ts +2 -1
  157. package/dist/server/self-test-input-tokens.js +18 -14
  158. package/dist/server/self-test.d.ts +14 -3
  159. package/dist/server/self-test.js +129 -58
  160. package/dist/server/serve-implementation.d.ts +230 -0
  161. package/dist/server/serve-implementation.js +2265 -0
  162. package/dist/server/serve.d.ts +1 -70
  163. package/dist/server/serve.js +1 -1131
  164. package/dist/server/trace-output.d.ts +6 -0
  165. package/dist/server/trace-output.js +79 -0
  166. package/dist/server/types.d.ts +30 -5
  167. package/dist/server/types.js +13 -1
  168. package/dist/stateful/errors.d.ts +19 -0
  169. package/dist/stateful/errors.js +24 -0
  170. package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
  171. package/dist/stateful/http-provider-event-emitter.js +237 -0
  172. package/dist/stateful/http-session-owner-registry.d.ts +44 -0
  173. package/dist/stateful/http-session-owner-registry.js +210 -0
  174. package/dist/stateful/index.d.ts +18 -0
  175. package/dist/stateful/index.js +18 -0
  176. package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
  177. package/dist/stateful/provider-event-delivery-failures.js +43 -0
  178. package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
  179. package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
  180. package/dist/stateful/provider-event-pipeline.d.ts +50 -0
  181. package/dist/stateful/provider-event-pipeline.js +1 -0
  182. package/dist/stateful/provider-events.d.ts +101 -0
  183. package/dist/stateful/provider-events.js +289 -0
  184. package/dist/stateful/session-key.d.ts +15 -0
  185. package/dist/stateful/session-key.js +86 -0
  186. package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
  187. package/dist/stateful/stateful-provider-adapter-context.js +42 -0
  188. package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
  189. package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
  190. package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
  191. package/dist/stateful/stateful-provider-adapter.js +287 -0
  192. package/dist/stateful/stateful-provider-observability.d.ts +62 -0
  193. package/dist/stateful/stateful-provider-observability.js +161 -0
  194. package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
  195. package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
  196. package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
  197. package/dist/stateful/stateful-provider-runtime-context.js +60 -0
  198. package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
  199. package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
  200. package/dist/stateful/stateful-provider-session-routing.d.ts +67 -0
  201. package/dist/stateful/stateful-provider-session-routing.js +345 -0
  202. package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
  203. package/dist/stateful/stateful-provider-session-runtime.js +245 -0
  204. package/dist/stateful-signing.d.ts +18 -0
  205. package/dist/stateful-signing.js +27 -0
  206. package/dist/stealth/profiles.d.ts +9 -4
  207. package/dist/stealth/profiles.js +160 -211
  208. package/dist/stream-evidence.d.ts +74 -0
  209. package/dist/stream-evidence.js +785 -0
  210. package/dist/stream.js +7 -1
  211. package/dist/testing/index.d.ts +2 -1
  212. package/dist/testing/index.js +2 -1
  213. package/dist/testing/run.d.ts +32 -2
  214. package/dist/testing/run.js +489 -21
  215. package/dist/trace-sanitization.d.ts +5 -0
  216. package/dist/trace-sanitization.js +45 -0
  217. package/dist/types.d.ts +673 -131
  218. package/dist/types.js +1 -0
  219. package/package.json +45 -5
  220. package/src/auth-turn/index.ts +1 -1
  221. package/src/auth.ts +118 -0
  222. package/src/ceremonies/index.ts +189 -46
  223. package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +6 -0
  224. package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +3 -0
  225. package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +10 -0
  226. package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +8 -0
  227. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +5 -0
  228. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +16 -0
  229. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +3 -0
  230. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +31 -0
  231. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +11 -0
  232. package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +11 -0
  233. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +11 -0
  234. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +21 -0
  235. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +11 -0
  236. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +14 -0
  237. package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +7 -0
  238. package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +6 -0
  239. package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +7 -0
  240. package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +6 -0
  241. package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +7 -0
  242. package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +7 -0
  243. package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +15 -0
  244. package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +4 -0
  245. package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +12 -0
  246. package/src/cli/commands.ts +33 -0
  247. package/src/cli/create.ts +50 -37
  248. package/src/cli/migrate-operation-declaration.ts +1654 -0
  249. package/src/cli/migrate-operation-shape.ts +184 -0
  250. package/src/cli/migrate-provider-shape.ts +772 -0
  251. package/src/cli/prompt-assets.ts +865 -0
  252. package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
  253. package/src/cli/templates/provider/Dockerfile.tpl +1 -1
  254. package/src/cli/templates/provider/README.md.tpl +5 -5
  255. package/src/cli/templates/provider/index.ts.tpl +6 -3
  256. package/src/cli/templates/provider/operations/ping.ts.tpl +4 -1
  257. package/src/cli/templates/provider/provider.json.tpl +6 -0
  258. package/src/config/loader.ts +690 -163
  259. package/src/contract-serialization.ts +5 -7
  260. package/src/contract-types.ts +12 -5
  261. package/src/contract.ts +24 -10
  262. package/src/declaration-validation.ts +330 -0
  263. package/src/define.ts +1048 -131
  264. package/src/dev.ts +4 -1
  265. package/src/engine.ts +279 -0
  266. package/src/error-observability.ts +64 -0
  267. package/src/error-resolution.ts +127 -0
  268. package/src/errors.ts +68 -0
  269. package/src/fixture-sanitization.ts +264 -0
  270. package/src/health-scenario.ts +875 -0
  271. package/src/index.ts +220 -13
  272. package/src/lint.ts +698 -90
  273. package/src/native-address.ts +340 -0
  274. package/src/native-egress-policy.ts +358 -0
  275. package/src/observability.ts +51 -1
  276. package/src/provider.ts +161 -3
  277. package/src/runtime/auth-flow.ts +12 -0
  278. package/src/runtime/browser.ts +661 -63
  279. package/src/runtime/cache.ts +189 -14
  280. package/src/runtime/choice-wordlist.ts +145 -0
  281. package/src/runtime/choice.ts +631 -120
  282. package/src/runtime/chrome149-header-order.ts +330 -0
  283. package/src/runtime/env.ts +13 -0
  284. package/src/runtime/executor.ts +43 -31
  285. package/src/runtime/http.ts +641 -61
  286. package/src/runtime/insights.ts +2 -2
  287. package/src/runtime/instrumentation.ts +520 -15
  288. package/src/runtime/native-network-errors.ts +99 -0
  289. package/src/runtime/native-network.ts +1605 -0
  290. package/src/runtime/ocr.ts +523 -0
  291. package/src/runtime/otlp.ts +467 -21
  292. package/src/runtime/proxy-errors.ts +12 -4
  293. package/src/runtime/proxy-nodemaven.ts +221 -0
  294. package/src/runtime/proxy-telemetry.ts +244 -75
  295. package/src/runtime/redirects.ts +66 -0
  296. package/src/runtime/redis.ts +7 -2
  297. package/src/runtime/request-options.ts +680 -1
  298. package/src/runtime/resolver-config.ts +6 -0
  299. package/src/runtime/resolver-public.ts +23 -0
  300. package/src/runtime/resolver-shared.ts +33 -0
  301. package/src/runtime/resolver-vendors/bindings.ts +56 -0
  302. package/src/runtime/resolver-vendors/browser.ts +533 -0
  303. package/src/runtime/resolver-vendors/capsolver.ts +707 -0
  304. package/src/runtime/resolver-vendors/hosts.ts +38 -0
  305. package/src/runtime/resolver-vendors/twocaptcha.ts +540 -0
  306. package/src/runtime/resolver-vendors/types.ts +212 -0
  307. package/src/runtime/resolver.ts +1157 -0
  308. package/src/runtime/secrets.ts +64 -0
  309. package/src/runtime/state.ts +394 -77
  310. package/src/runtime/stealth-cookies.ts +132 -0
  311. package/src/runtime/stealth.ts +1280 -336
  312. package/src/runtime/stt.ts +1 -19
  313. package/src/runtime/timeout.ts +18 -0
  314. package/src/runtime/trace-config.ts +78 -0
  315. package/src/runtime/trace.ts +57 -17
  316. package/src/serve.ts +6 -1
  317. package/src/server/error-observability.ts +1 -0
  318. package/src/server/index.ts +34 -2
  319. package/src/server/self-test-input-tokens.ts +29 -14
  320. package/src/server/self-test.ts +191 -68
  321. package/src/server/serve-implementation.ts +3378 -0
  322. package/src/server/serve.ts +1 -1632
  323. package/src/server/trace-output.ts +129 -0
  324. package/src/server/types.ts +13 -1
  325. package/src/stateful/README.md +146 -0
  326. package/src/stateful/errors.ts +35 -0
  327. package/src/stateful/http-provider-event-emitter.ts +314 -0
  328. package/src/stateful/http-session-owner-registry.ts +306 -0
  329. package/src/stateful/index.ts +18 -0
  330. package/src/stateful/provider-event-delivery-failures.ts +80 -0
  331. package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
  332. package/src/stateful/provider-event-pipeline.ts +61 -0
  333. package/src/stateful/provider-events.ts +462 -0
  334. package/src/stateful/session-key.ts +111 -0
  335. package/src/stateful/stateful-provider-adapter-context.ts +59 -0
  336. package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
  337. package/src/stateful/stateful-provider-adapter.ts +562 -0
  338. package/src/stateful/stateful-provider-observability.ts +261 -0
  339. package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
  340. package/src/stateful/stateful-provider-runtime-context.ts +92 -0
  341. package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
  342. package/src/stateful/stateful-provider-session-routing.ts +546 -0
  343. package/src/stateful/stateful-provider-session-runtime.ts +403 -0
  344. package/src/stateful-signing.ts +46 -0
  345. package/src/stealth/profiles.ts +202 -231
  346. package/src/stream-evidence.ts +988 -0
  347. package/src/stream.ts +8 -1
  348. package/src/testing/index.ts +10 -1
  349. package/src/testing/run.ts +658 -15
  350. package/src/trace-sanitization.ts +63 -0
  351. package/src/types.ts +788 -174
  352. package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
  353. package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
  354. /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  355. /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  356. /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  357. /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  358. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  359. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
  360. /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  361. /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  362. /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  363. /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  364. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  365. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
@@ -0,0 +1,27 @@
1
+ import type { EnvContext, ProviderDefinition } from "../types.js";
2
+ /**
3
+ * Canonical error code for a declared-but-unprovisioned provider secret.
4
+ *
5
+ * The SDK is the single source of truth for env/secret presence validation:
6
+ * providers declare secrets in `defineProvider({ secrets: [...] })` and the
7
+ * runtime enforces presence before any handler or auth-flow code runs.
8
+ * Provider-local presence guards (requireServiceKey/requireApiKey style) are a
9
+ * deprecated antipattern — see the `sdk-owned-secret-presence` submit-check
10
+ * rule.
11
+ */
12
+ export declare const MISSING_SECRET_CODE = "MISSING_SECRET";
13
+ /**
14
+ * Names of declared `required: true` secrets whose env values are unset or
15
+ * whitespace-only. Whitespace-only values count as missing for parity with the
16
+ * `.trim()` guards well-built providers used before the SDK owned this check —
17
+ * a blank value provisioned by a broken secret pipeline must not pass the gate.
18
+ */
19
+ export declare function listMissingRequiredSecrets(provider: ProviderDefinition, env: EnvContext): string[];
20
+ /**
21
+ * Throws the canonical structured missing-secret error when any declared
22
+ * `required: true` secret is absent. All missing names are reported in a
23
+ * single error so operators can provision the full set in one pass instead of
24
+ * discovering them one deploy at a time (the 2026-07-22 unprovisioned-secret
25
+ * incident failure mode).
26
+ */
27
+ export declare function assertRequiredSecretsPresent(provider: ProviderDefinition, env: EnvContext): void;
@@ -0,0 +1,51 @@
1
+ import { ProviderSecretError } from "../errors.js";
2
+ /**
3
+ * Canonical error code for a declared-but-unprovisioned provider secret.
4
+ *
5
+ * The SDK is the single source of truth for env/secret presence validation:
6
+ * providers declare secrets in `defineProvider({ secrets: [...] })` and the
7
+ * runtime enforces presence before any handler or auth-flow code runs.
8
+ * Provider-local presence guards (requireServiceKey/requireApiKey style) are a
9
+ * deprecated antipattern — see the `sdk-owned-secret-presence` submit-check
10
+ * rule.
11
+ */
12
+ export const MISSING_SECRET_CODE = "MISSING_SECRET";
13
+ /**
14
+ * Names of declared `required: true` secrets whose env values are unset or
15
+ * whitespace-only. Whitespace-only values count as missing for parity with the
16
+ * `.trim()` guards well-built providers used before the SDK owned this check —
17
+ * a blank value provisioned by a broken secret pipeline must not pass the gate.
18
+ */
19
+ export function listMissingRequiredSecrets(provider, env) {
20
+ const missing = [];
21
+ for (const secret of provider.secrets ?? []) {
22
+ if (secret.required !== true) {
23
+ continue;
24
+ }
25
+ const value = env.get(secret.name);
26
+ if (value === undefined || value.trim() === "") {
27
+ missing.push(secret.name);
28
+ }
29
+ }
30
+ return missing;
31
+ }
32
+ /**
33
+ * Throws the canonical structured missing-secret error when any declared
34
+ * `required: true` secret is absent. All missing names are reported in a
35
+ * single error so operators can provision the full set in one pass instead of
36
+ * discovering them one deploy at a time (the 2026-07-22 unprovisioned-secret
37
+ * incident failure mode).
38
+ */
39
+ export function assertRequiredSecretsPresent(provider, env) {
40
+ const missing = listMissingRequiredSecrets(provider, env);
41
+ if (missing.length === 0) {
42
+ return;
43
+ }
44
+ const names = missing.join(", ");
45
+ throw new ProviderSecretError(`Missing required provider secret${missing.length > 1 ? "s" : ""}: ${names}`, {
46
+ code: MISSING_SECRET_CODE,
47
+ category: "credential_unavailable",
48
+ retryable: false,
49
+ fix: `Provision ${names} in the provider environment (e.g. Doppler). Declared in defineProvider({ secrets: [...] }).`,
50
+ });
51
+ }
@@ -1,8 +1,11 @@
1
1
  import { ProviderError } from "../errors.js";
2
2
  import type { ProviderRuntimeState } from "../types.js";
3
+ import { type ProviderRedisClient } from "./redis.js";
3
4
  type RedisProviderRuntimeStateOptions = {
4
5
  readonly redisUrl: string;
5
6
  readonly providerId?: string;
7
+ /** Test seam; production callers use redisUrl-backed client sharing. */
8
+ readonly __redisClient?: ProviderRedisClient;
6
9
  };
7
10
  export declare class UnsupportedProviderStateError extends ProviderError {
8
11
  constructor(message?: string);
@@ -1,21 +1,93 @@
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
+ local next_decoded = cjson.decode(envelope)
64
+ next_decoded.createdAt = current_decoded.createdAt
65
+ envelope = cjson.encode(next_decoded)
66
+ end
67
+ redis.call("SET", KEYS[1], envelope, "PXAT", expires_at)
68
+ redis.call("ZADD", KEYS[2], expires_at, KEYS[1])
69
+ redis.call("PEXPIRE", KEYS[2], index_ttl)
70
+ return {1, envelope}
71
+ `;
72
+ const DELETE_WITH_INDEX_SCRIPT = `
73
+ redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2])
74
+ redis.call("ZREM", KEYS[2], KEYS[1])
75
+ return 1
76
+ `;
6
77
  const redisBackends = new Map();
7
- function getRedisBackend(redisUrl) {
78
+ function getRedisBackend(redisUrl, injectedRedis) {
8
79
  const existing = redisBackends.get(redisUrl);
9
80
  if (existing)
10
81
  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
- });
82
+ const redis = injectedRedis ??
83
+ createProviderRedisClient({
84
+ redisUrl,
85
+ timeoutMs: DEFAULT_REDIS_TIMEOUT_MS,
86
+ onError: () => {
87
+ // Runtime state operations fail closed at their call sites. Avoid noisy
88
+ // unhandled Redis errors from background reconnect attempts.
89
+ },
90
+ });
19
91
  const backend = { redis };
20
92
  redisBackends.set(redisUrl, backend);
21
93
  return backend;
@@ -36,15 +108,26 @@ async function requireRedisReady(redis) {
36
108
  return;
37
109
  throw new UnsupportedProviderStateError("Provider runtime state Redis is unavailable");
38
110
  }
39
- function providerStatePrefix(providerId, namespace) {
40
- return `${REDIS_STATE_PREFIX}:${providerId ?? "default"}:${namespace}`;
111
+ function connectionScopeDiscriminator(connectionId) {
112
+ if (connectionId === undefined)
113
+ return MISSING_CONNECTION_SCOPE_DISCRIMINATOR;
114
+ const digest = createHash("sha256").update(connectionId, "utf8").digest("hex");
115
+ return `scope:connection:sha256:${digest}`;
116
+ }
117
+ function providerStatePrefix(providerId, namespace, scopeDiscriminator) {
118
+ return `${REDIS_STATE_PREFIX}:${providerId ?? "default"}:${namespace}:${scopeDiscriminator}`;
41
119
  }
42
- function providerStateKey(providerId, namespace, key) {
43
- return `${providerStatePrefix(providerId, namespace)}:${key}`;
120
+ function providerStateKey(providerId, namespace, scopeDiscriminator, key) {
121
+ return `${providerStatePrefix(providerId, namespace, scopeDiscriminator)}:${key}`;
44
122
  }
45
- function publicStateKey(providerId, namespace, redisKey) {
46
- const prefix = `${providerStatePrefix(providerId, namespace)}:`;
47
- return redisKey.startsWith(prefix) ? redisKey.slice(prefix.length) : redisKey;
123
+ function publicStateKey(redisKey, prefixes) {
124
+ for (const prefix of prefixes) {
125
+ const prefixWithSeparator = `${prefix}:`;
126
+ if (redisKey.startsWith(prefixWithSeparator)) {
127
+ return redisKey.slice(prefixWithSeparator.length);
128
+ }
129
+ }
130
+ return redisKey;
48
131
  }
49
132
  function parseStateDurationMs(ttl) {
50
133
  const match = /^(\d+)(ms|s|m|h|d)$/.exec(ttl ?? "1h");
@@ -63,13 +146,21 @@ function parseStateDurationMs(ttl) {
63
146
  : 86_400_000;
64
147
  return Math.max(1, amount * multiplier);
65
148
  }
66
- function resolveExpiresAt(ttl) {
67
- return new Date(Date.now() + parseStateDurationMs(ttl)).toISOString();
68
- }
69
149
  function envelopeFromJson(key, raw) {
70
150
  if (!raw)
71
151
  return null;
72
- const parsed = JSON.parse(raw);
152
+ // A corrupt/undecodable persisted envelope must be treated as absent rather
153
+ // than throwing a raw JSON.parse SyntaxError: an uncaught SyntaxError escapes
154
+ // the provider error taxonomy, is masked as internal_error 500, and is then
155
+ // retried by the hub (2026-07-22 catchtable reserve RCA, candidate A). Returning
156
+ // null also keeps list() from aborting the whole scan on a single bad entry.
157
+ let parsed;
158
+ try {
159
+ parsed = JSON.parse(raw);
160
+ }
161
+ catch {
162
+ return null;
163
+ }
73
164
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
74
165
  return null;
75
166
  }
@@ -90,6 +181,20 @@ function envelopeFromJson(key, raw) {
90
181
  updatedAt: record.updatedAt,
91
182
  };
92
183
  }
184
+ function isStateTombstone(raw) {
185
+ if (!raw)
186
+ return false;
187
+ try {
188
+ const parsed = JSON.parse(raw);
189
+ return (parsed !== null &&
190
+ typeof parsed === "object" &&
191
+ !Array.isArray(parsed) &&
192
+ parsed.deleted === true);
193
+ }
194
+ catch {
195
+ return false;
196
+ }
197
+ }
93
198
  function redisEnvelope(value, version, createdAt, expiresAt) {
94
199
  const updatedAt = new Date().toISOString();
95
200
  return { value, version, expiresAt, createdAt, updatedAt };
@@ -99,21 +204,38 @@ class RedisProviderStateNamespace {
99
204
  providerId;
100
205
  namespaceName;
101
206
  options;
102
- constructor(backend, providerId, namespaceName, options) {
207
+ scopeDiscriminator;
208
+ constructor(backend, providerId, namespaceName, options, scopeDiscriminator) {
103
209
  this.backend = backend;
104
210
  this.providerId = providerId;
105
211
  this.namespaceName = namespaceName;
106
212
  this.options = options;
213
+ this.scopeDiscriminator = scopeDiscriminator;
107
214
  }
108
215
  redisKey(key) {
109
- return providerStateKey(this.providerId, this.namespaceName, key);
216
+ return providerStateKey(this.providerId, this.namespaceName, this.scopeDiscriminator, key);
217
+ }
218
+ statePrefix() {
219
+ return providerStatePrefix(this.providerId, this.namespaceName, this.scopeDiscriminator);
110
220
  }
111
- prefix() {
112
- return `${providerStatePrefix(this.providerId, this.namespaceName)}:`;
221
+ indexKey() {
222
+ // Keep bookkeeping outside the caller-owned keyspace. A provider may use
223
+ // any state key (including "__index"), so a suffix inside the namespace
224
+ // could turn the ZSET into a string and break every subsequent write.
225
+ const namespaceIdentity = Buffer.from(providerStatePrefix(this.providerId, this.namespaceName, this.scopeDiscriminator), "utf8").toString("base64url");
226
+ return `${REDIS_STATE_PREFIX}:index:${namespaceIdentity}`;
113
227
  }
114
- async activeKeys() {
228
+ async indexedKeys(limit) {
115
229
  await requireRedisReady(this.backend.redis);
116
- return await withRequiredRedis(() => this.backend.redis.keys(`${this.prefix()}*`));
230
+ const now = Date.now();
231
+ return await withRequiredRedis(async () => {
232
+ await this.backend.redis.zremrangebyscore(this.indexKey(), "-inf", now);
233
+ return await this.backend.redis.zrangebyscore(this.indexKey(), now + 1, "+inf", "LIMIT", 0, limit);
234
+ });
235
+ }
236
+ async readRaw(key) {
237
+ await requireRedisReady(this.backend.redis);
238
+ return await withRequiredRedis(() => this.backend.redis.get(this.redisKey(key)));
117
239
  }
118
240
  enforceValueSize(value) {
119
241
  const bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
@@ -121,52 +243,68 @@ class RedisProviderStateNamespace {
121
243
  throw new UnsupportedProviderStateError(`Provider runtime state value exceeds maxValueBytes (${bytes} > ${this.options.maxValueBytes})`);
122
244
  }
123
245
  }
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})`);
246
+ quotaExceeded() {
247
+ return new UnsupportedProviderStateError(`Provider runtime state namespace quota exceeded (${this.options.maxEntries + 1} > ${this.options.maxEntries})`);
248
+ }
249
+ writeTiming(ttl) {
250
+ const ttlMs = parseStateDurationMs(ttl ?? this.options.defaultTtl);
251
+ const maxTtlMs = parseStateDurationMs(this.options.maxTtl);
252
+ if (ttlMs > maxTtlMs) {
253
+ throw new UnsupportedProviderStateError(`Provider runtime state ttl exceeds maxTtl (${ttlMs} > ${maxTtlMs})`);
130
254
  }
255
+ const expiresAtMs = Date.now() + ttlMs;
256
+ return {
257
+ expiresAt: new Date(expiresAtMs).toISOString(),
258
+ expiresAtMs,
259
+ indexTtlMs: maxTtlMs,
260
+ };
131
261
  }
132
262
  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)
263
+ const requestedLimit = Math.max(0, options?.limit ?? this.options.maxEntries);
264
+ if (requestedLimit === 0)
139
265
  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
- });
266
+ const keys = await this.indexedKeys(this.options.maxEntries);
267
+ if (keys.length === 0)
268
+ return [];
269
+ const values = await withRequiredRedis(() => this.backend.redis.mget(keys));
270
+ const rows = [];
271
+ for (const [index, raw] of values.entries()) {
272
+ const redisKey = keys[index];
273
+ if (!redisKey)
274
+ continue;
275
+ const publicKey = publicStateKey(redisKey, [this.statePrefix()]);
276
+ if (options?.prefix && !publicKey.startsWith(options.prefix))
277
+ continue;
278
+ const value = envelopeFromJson(publicKey, raw);
279
+ if (!value)
280
+ continue;
281
+ rows.push(value);
282
+ }
283
+ return rows.slice(0, requestedLimit);
148
284
  }
149
285
  async get(key) {
150
- await requireRedisReady(this.backend.redis);
151
- const raw = await withRequiredRedis(() => this.backend.redis.get(this.redisKey(key)));
286
+ const raw = await this.readRaw(key);
287
+ if (isStateTombstone(raw))
288
+ return null;
152
289
  return envelopeFromJson(key, raw);
153
290
  }
154
291
  async set(key, value, options) {
155
292
  this.enforceValueSize(value);
156
- await this.enforceMaxEntries(key);
157
293
  const current = await this.get(key);
158
294
  const createdAt = current?.createdAt ?? new Date().toISOString();
159
295
  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));
296
+ const timing = this.writeTiming(options?.ttl);
297
+ const envelope = redisEnvelope(value, version, createdAt, timing.expiresAt);
298
+ await requireRedisReady(this.backend.redis);
299
+ 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)));
300
+ if (!Array.isArray(result) || Number(result[0]) !== 1) {
301
+ throw this.quotaExceeded();
302
+ }
165
303
  return {
166
304
  key,
167
305
  value,
168
306
  version,
169
- expiresAt,
307
+ expiresAt: timing.expiresAt,
170
308
  createdAt,
171
309
  updatedAt: envelope.updatedAt,
172
310
  };
@@ -179,15 +317,29 @@ class RedisProviderStateNamespace {
179
317
  }
180
318
  async compareAndSet(key, expectedVersion, value, options) {
181
319
  this.enforceValueSize(value);
182
- const current = await this.get(key);
183
- if ((current?.version ?? 0) !== expectedVersion) {
184
- return { ok: false, current };
320
+ const createdAt = new Date().toISOString();
321
+ const timing = this.writeTiming(options?.ttl);
322
+ const envelope = redisEnvelope(value, expectedVersion + 1, createdAt, timing.expiresAt);
323
+ await requireRedisReady(this.backend.redis);
324
+ 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)));
325
+ if (Array.isArray(result) && Number(result[0]) === 0) {
326
+ throw this.quotaExceeded();
327
+ }
328
+ if (!Array.isArray(result) || Number(result[0]) !== 1) {
329
+ const rawCurrent = Array.isArray(result) && typeof result[1] === "string" ? result[1] : null;
330
+ return { ok: false, current: envelopeFromJson(key, rawCurrent) };
331
+ }
332
+ const rawWritten = typeof result[1] === "string" ? result[1] : null;
333
+ const written = envelopeFromJson(key, rawWritten);
334
+ if (!written) {
335
+ throw new UnsupportedProviderStateError("Provider runtime state CAS returned an invalid value");
185
336
  }
186
- return { ok: true, value: await this.set(key, value, options) };
337
+ return { ok: true, value: written };
187
338
  }
188
339
  async delete(key) {
189
340
  await requireRedisReady(this.backend.redis);
190
- await withRequiredRedis(() => this.backend.redis.del(this.redisKey(key)));
341
+ const tombstone = JSON.stringify({ deleted: true });
342
+ await withRequiredRedis(() => this.backend.redis.eval(DELETE_WITH_INDEX_SCRIPT, 2, this.redisKey(key), this.indexKey(), tombstone, String(parseStateDurationMs(this.options.maxTtl))));
191
343
  }
192
344
  async increment(key, field, delta = 1, options) {
193
345
  const current = (await this.get(key))?.value ?? {};
@@ -198,12 +350,19 @@ class RedisProviderStateNamespace {
198
350
  class RedisProviderRuntimeState {
199
351
  backend;
200
352
  providerId;
201
- constructor(options) {
202
- this.backend = getRedisBackend(options.redisUrl);
353
+ redisUrl;
354
+ scopeDiscriminator;
355
+ constructor(options, scopeDiscriminator = MISSING_CONNECTION_SCOPE_DISCRIMINATOR) {
356
+ this.backend = getRedisBackend(options.redisUrl, options.__redisClient);
203
357
  this.providerId = options.providerId;
358
+ this.redisUrl = options.redisUrl;
359
+ this.scopeDiscriminator = scopeDiscriminator;
360
+ }
361
+ forConnection(connectionId) {
362
+ return new RedisProviderRuntimeState({ redisUrl: this.redisUrl, providerId: this.providerId }, connectionScopeDiscriminator(connectionId));
204
363
  }
205
364
  namespace(name, options) {
206
- return new RedisProviderStateNamespace(this.backend, this.providerId, name, options);
365
+ return new RedisProviderStateNamespace(this.backend, this.providerId, name, options, options.scope === "provider" ? PROVIDER_SCOPE_DISCRIMINATOR : this.scopeDiscriminator);
207
366
  }
208
367
  }
209
368
  export class UnsupportedProviderStateError extends ProviderError {
@@ -236,6 +395,9 @@ class UnsupportedProviderStateNamespace {
236
395
  }
237
396
  }
238
397
  class UnsupportedProviderRuntimeState {
398
+ forConnection(_connectionId) {
399
+ return new UnsupportedProviderRuntimeState();
400
+ }
239
401
  namespace(_name, _options) {
240
402
  return new UnsupportedProviderStateNamespace();
241
403
  }
@@ -247,6 +409,23 @@ class MemoryProviderStateNamespace {
247
409
  constructor(options) {
248
410
  this.options = options;
249
411
  }
412
+ enforceValueSize(value) {
413
+ const bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
414
+ if (bytes > this.options.maxValueBytes) {
415
+ throw new UnsupportedProviderStateError(`Provider runtime state value exceeds maxValueBytes (${bytes} > ${this.options.maxValueBytes})`);
416
+ }
417
+ }
418
+ enforceWritePolicy(key, value, ttl) {
419
+ this.enforceValueSize(value);
420
+ const ttlMs = parseStateDurationMs(ttl ?? this.options.defaultTtl);
421
+ const maxTtlMs = parseStateDurationMs(this.options.maxTtl);
422
+ if (ttlMs > maxTtlMs) {
423
+ throw new UnsupportedProviderStateError(`Provider runtime state ttl exceeds maxTtl (${ttlMs} > ${maxTtlMs})`);
424
+ }
425
+ if (!this.values.has(key) && this.values.size >= this.options.maxEntries) {
426
+ throw new UnsupportedProviderStateError(`Provider runtime state namespace quota exceeded (${this.options.maxEntries + 1} > ${this.options.maxEntries})`);
427
+ }
428
+ }
250
429
  pruneExpired(nowMs = Date.now()) {
251
430
  for (const [key, row] of this.values.entries()) {
252
431
  if (row.expiresAt && Date.parse(row.expiresAt) <= nowMs) {
@@ -265,6 +444,7 @@ class MemoryProviderStateNamespace {
265
444
  }
266
445
  async set(key, value, options) {
267
446
  this.pruneExpired();
447
+ this.enforceWritePolicy(key, value, options?.ttl);
268
448
  const now = new Date().toISOString();
269
449
  const current = this.values.get(key);
270
450
  const expiresAt = resolveMemoryStateExpiresAt(options?.ttl ?? this.options.defaultTtl);
@@ -282,8 +462,24 @@ class MemoryProviderStateNamespace {
282
462
  async patch(_key, _partial, _options) {
283
463
  throw new UnsupportedProviderStateError("In-memory provider runtime state does not support patch");
284
464
  }
285
- async compareAndSet(_key, _expectedVersion, _value, _options) {
286
- throw new UnsupportedProviderStateError("In-memory provider runtime state does not support compareAndSet");
465
+ async compareAndSet(key, expectedVersion, value, options) {
466
+ this.pruneExpired();
467
+ const current = this.values.get(key);
468
+ if ((current?.version ?? 0) !== expectedVersion) {
469
+ return { ok: false, current: current ?? null };
470
+ }
471
+ this.enforceWritePolicy(key, value, options?.ttl);
472
+ const now = new Date().toISOString();
473
+ const stored = {
474
+ key,
475
+ value,
476
+ version: expectedVersion + 1,
477
+ expiresAt: resolveMemoryStateExpiresAt(options?.ttl ?? this.options.defaultTtl),
478
+ createdAt: current?.createdAt ?? now,
479
+ updatedAt: now,
480
+ };
481
+ this.values.set(key, stored);
482
+ return { ok: true, value: stored };
287
483
  }
288
484
  async delete(key) {
289
485
  this.values.delete(key);
@@ -293,13 +489,23 @@ class MemoryProviderStateNamespace {
293
489
  }
294
490
  }
295
491
  class MemoryProviderRuntimeState {
296
- namespaces = new Map();
297
- namespace(name, _options) {
298
- const existing = this.namespaces.get(name);
492
+ backend;
493
+ scopeDiscriminator;
494
+ constructor(backend = { namespaces: new Map() }, scopeDiscriminator = MISSING_CONNECTION_SCOPE_DISCRIMINATOR) {
495
+ this.backend = backend;
496
+ this.scopeDiscriminator = scopeDiscriminator;
497
+ }
498
+ forConnection(connectionId) {
499
+ return new MemoryProviderRuntimeState(this.backend, connectionScopeDiscriminator(connectionId));
500
+ }
501
+ namespace(name, options) {
502
+ const scopeDiscriminator = options.scope === "provider" ? PROVIDER_SCOPE_DISCRIMINATOR : this.scopeDiscriminator;
503
+ const namespaceIdentity = `${scopeDiscriminator}\0${name}`;
504
+ const existing = this.backend.namespaces.get(namespaceIdentity);
299
505
  if (existing)
300
506
  return existing;
301
- const created = new MemoryProviderStateNamespace(_options);
302
- this.namespaces.set(name, created);
507
+ const created = new MemoryProviderStateNamespace(options);
508
+ this.backend.namespaces.set(namespaceIdentity, created);
303
509
  return created;
304
510
  }
305
511
  }
@@ -0,0 +1,20 @@
1
+ import type { CookieJar, StealthCookieStore, StealthCookieStoreV1, StealthSessionCookies } from "../types.js";
2
+ export declare class StealthCookieJar implements CookieJar, StealthSessionCookies {
3
+ private cookies;
4
+ private readonly defaultUrl;
5
+ constructor(cookieStrings: readonly string[], defaultUrl?: string);
6
+ setFromCookieStrings(cookieStrings: readonly string[], url?: string): void;
7
+ get(name: string, url?: string): string | undefined;
8
+ getAll(url?: string): Record<string, string>;
9
+ has(name: string, url?: string): boolean;
10
+ toString(url?: string): string;
11
+ toHeader(url?: string): string;
12
+ snapshot(): Record<string, string>;
13
+ restore(cookies: Record<string, string>): void;
14
+ serialize(): StealthCookieStoreV1;
15
+ deserialize(state: StealthCookieStore): void;
16
+ clear(): void;
17
+ find(predicate: (cookie: string) => boolean, url?: string): string | undefined;
18
+ private normalizeUrl;
19
+ private getUniqueCookies;
20
+ }