@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,7 +1,14 @@
1
+ import { readDiagnosticEnv } from "../runtime/diagnostic-env.js";
1
2
  import { createHash, randomUUID } from "node:crypto";
2
3
  import { existsSync } from "node:fs";
4
+ import { createRequire } from "node:module";
3
5
  import path from "node:path";
4
- import { Redis } from "ioredis";
6
+ import { BoundedExpiringMap } from "../runtime/bounded-expiring-map.js";
7
+ import { NODEMAVEN_DEFAULT_PROTOCOL, NODEMAVEN_FILTER_ENV, NODEMAVEN_MAX_POOL_SIZE, NODEMAVEN_PASSWORD_ENV, NODEMAVEN_USERNAME_ENV, nodemavenPoolSize, synthesizeNodemavenProxy, } from "../runtime/proxy-nodemaven.js";
8
+ // "smartproxy" here is api.smartproxy.org — a residential proxy with an IP
9
+ // extraction API (app_key → raw ip:port pool). It is NOT the company formerly
10
+ // named Smartproxy (smartproxy.com), which rebranded to Decodo in 2025 and is
11
+ // modelled separately as the `decodo` gateway vendor. Do not conflate them.
5
12
  export const SMARTPROXY_APP_KEY_ENV = "APIFUSE__PROXY__SMARTPROXY_APP_KEY";
6
13
  export const SMARTPROXY_MAX_LIFETIME_MINUTES = 2000;
7
14
  export const DEFAULT_SMARTPROXY_POOL_SIZE = 20;
@@ -15,14 +22,27 @@ export const REDIS_URL_ENV = "APIFUSE__REDIS__URL";
15
22
  export class ProxyResolutionError extends Error {
16
23
  code;
17
24
  telemetry;
25
+ vendor;
26
+ vendorChain;
27
+ protocol;
18
28
  constructor(code, message, options) {
19
29
  super(message, options);
20
30
  this.name = "ProxyResolutionError";
21
31
  this.code = code;
22
32
  this.telemetry = options?.telemetry;
33
+ this.vendor = options?.vendor;
34
+ this.vendorChain = options?.vendorChain;
35
+ this.protocol = options?.protocol;
23
36
  }
24
37
  }
25
- const proxyCache = new Map();
38
+ // Keyed per (policy, affinity). Connection affinities are minted by callers, so
39
+ // without a cap a long-lived process accumulates one pool per affinity it ever
40
+ // saw. Evicting a still-fresh pool forces a fresh allocation (a different
41
+ // egress endpoint) for that affinity, so the cap is generous and every
42
+ // capacity eviction is reported as `diagnostics.poolCacheEvictions`.
43
+ const SMARTPROXY_POOL_CACHE_MAX_ENTRIES = 10_000;
44
+ let proxyCache = createSmartproxyPoolCache(SMARTPROXY_POOL_CACHE_MAX_ENTRIES);
45
+ const require = createRequire(import.meta.url);
26
46
  const proxyInflight = new Map();
27
47
  const invalidatedProxyKeys = new Map();
28
48
  const redisClients = new Map();
@@ -43,19 +63,36 @@ const SMARTPROXY_ALLOCATOR_RETRY_BASE_MS = 25;
43
63
  // reusing stale raw CONNECT endpoints as if they were valid for `life` minutes.
44
64
  const SMARTPROXY_EXTRACTION_CACHE_TTL_MS = 15_000;
45
65
  const SMARTPROXY_EXTRACTION_SOFT_REFRESH_MS = 10_000;
66
+ // Precedence is declared once here so the URL accessors, the boot report and
67
+ // any future consumer can never disagree about which env wins.
68
+ const CACHE_REDIS_URL_PRECEDENCE = [PROVIDER_CACHE_REDIS_URL_ENV, REDIS_URL_ENV];
69
+ const STATE_REDIS_URL_PRECEDENCE = [
70
+ PROVIDER_STATE_REDIS_URL_ENV,
71
+ PROVIDER_CACHE_REDIS_URL_ENV,
72
+ REDIS_URL_ENV,
73
+ ];
74
+ function resolveRedisUrlFromEnv(precedence) {
75
+ for (const [index, envName] of precedence.entries()) {
76
+ const url = readDiagnosticEnv(envName)?.trim();
77
+ if (url)
78
+ return { url, envName, fallback: index > 0 };
79
+ }
80
+ return undefined;
81
+ }
46
82
  function redisUrlFromEnv() {
47
- return (process.env.APIFUSE__PROVIDER__CACHE_REDIS_URL?.trim() ||
48
- process.env[REDIS_URL_ENV]?.trim() ||
49
- undefined);
83
+ return providerCacheRedisResolutionFromEnv()?.url;
84
+ }
85
+ export function providerCacheRedisResolutionFromEnv() {
86
+ return resolveRedisUrlFromEnv(CACHE_REDIS_URL_PRECEDENCE);
87
+ }
88
+ export function providerStateRedisResolutionFromEnv() {
89
+ return resolveRedisUrlFromEnv(STATE_REDIS_URL_PRECEDENCE);
50
90
  }
51
91
  export function providerCacheRedisUrlFromEnv() {
52
- return redisUrlFromEnv();
92
+ return providerCacheRedisResolutionFromEnv()?.url;
53
93
  }
54
94
  export function providerStateRedisUrlFromEnv() {
55
- return (process.env[PROVIDER_STATE_REDIS_URL_ENV]?.trim() ||
56
- process.env[PROVIDER_CACHE_REDIS_URL_ENV]?.trim() ||
57
- process.env[REDIS_URL_ENV]?.trim() ||
58
- undefined);
95
+ return providerStateRedisResolutionFromEnv()?.url;
59
96
  }
60
97
  /** @internal Test-only hook for exercising shared proxy-cache behavior. */
61
98
  export function __setProxyRedisForTests(redis) {
@@ -64,6 +101,17 @@ export function __setProxyRedisForTests(redis) {
64
101
  export function __setSmartproxyAllocatorDeadlineMsForTests(deadlineMs) {
65
102
  smartproxyAllocatorDeadlineMsForTests = deadlineMs;
66
103
  }
104
+ /** @internal Test-only hook for exercising the pool-cache capacity bound at a small size. */
105
+ export function __setSmartproxyPoolCacheMaxEntriesForTests(maxEntries) {
106
+ proxyCache = createSmartproxyPoolCache(maxEntries ?? SMARTPROXY_POOL_CACHE_MAX_ENTRIES);
107
+ }
108
+ /** @internal Test-only view of the in-memory Smartproxy cache sizes. */
109
+ export function __getSmartproxyCacheSizesForTests() {
110
+ return { pools: proxyCache.size, tombstones: invalidatedProxyKeys.size };
111
+ }
112
+ function createSmartproxyPoolCache(maxEntries) {
113
+ return new BoundedExpiringMap(maxEntries, (pool) => pool.expiresAt);
114
+ }
67
115
  function getProxyRedis() {
68
116
  if (proxyRedisForTests)
69
117
  return proxyRedisForTests;
@@ -73,7 +121,8 @@ function getProxyRedis() {
73
121
  const existing = redisClients.get(redisUrl);
74
122
  if (existing)
75
123
  return existing;
76
- const redis = new Redis(redisUrl, {
124
+ const { Redis: RedisClient } = require("ioredis");
125
+ const redis = new RedisClient(redisUrl, {
77
126
  connectTimeout: REDIS_TIMEOUT_MS,
78
127
  enableOfflineQueue: false,
79
128
  lazyConnect: true,
@@ -145,6 +194,7 @@ function telemetryForPool(pool, cacheStatus, startedAt, extra = {}) {
145
194
  function telemetryForFailure(cacheStatus, startedAt, extra = {}) {
146
195
  return {
147
196
  provider: "smartproxy",
197
+ outcome: "error",
148
198
  cacheStatus,
149
199
  cacheHit: false,
150
200
  resolutionMs: Math.max(0, Date.now() - startedAt),
@@ -210,59 +260,7 @@ function serializeSmartproxyPool(pool) {
210
260
  }
211
261
  function normalizeProxyUrl(url) {
212
262
  const normalized = url?.trim();
213
- return normalized ? applyStickyProxySession(normalized) : undefined;
214
- }
215
- function readPositiveIntegerEnv(name) {
216
- const raw = process.env[name]?.trim();
217
- if (!raw)
218
- return undefined;
219
- if (!/^[1-9]\d*$/.test(raw)) {
220
- throw new Error(`${name} must be a positive integer`);
221
- }
222
- return raw;
223
- }
224
- function applyStickyProxySession(proxyUrl) {
225
- let parsed;
226
- try {
227
- parsed = new URL(proxyUrl);
228
- }
229
- catch {
230
- return proxyUrl;
231
- }
232
- if (!parsed.hostname || !parsed.username || !parsed.password) {
233
- return proxyUrl;
234
- }
235
- const host = parsed.hostname.toLowerCase();
236
- if (!host.includes("smartproxy") && !host.includes("decodo")) {
237
- return proxyUrl;
238
- }
239
- const username = decodeURIComponent(parsed.username);
240
- const sessionId = process.env.APIFUSE__PROXY__SESSION_ID?.trim() || "apifuse-shared";
241
- const sessionDuration = readPositiveIntegerEnv("APIFUSE__PROXY__SESSION_DURATION");
242
- const stickyUsername = host.includes("smartproxy")
243
- ? buildSmartproxyUsername(username, sessionId, sessionDuration)
244
- : buildDecodoUsername(username, sessionId, sessionDuration ?? "60");
245
- parsed.username = stickyUsername;
246
- return parsed.toString();
247
- }
248
- function buildSmartproxyUsername(username, sessionId, sessionDuration) {
249
- const parts = username.split("_");
250
- const configuredLife = parts.find((part) => part.startsWith("life-"))?.slice("life-".length);
251
- const baseUsername = parts
252
- .filter((part) => !part.startsWith("session-") && !part.startsWith("life-"))
253
- .join("_");
254
- return `${baseUsername}_session-${sessionId}_life-${sessionDuration ?? configuredLife ?? "60"}`;
255
- }
256
- function buildDecodoUsername(username, sessionId, sessionDuration) {
257
- const withoutSticky = username.replace(/-session-.+-sessionduration-\d+$/, "");
258
- const baseUsername = withoutSticky.startsWith("user-") ? withoutSticky : `user-${withoutSticky}`;
259
- return `${baseUsername}-session-${sessionId}-sessionduration-${sessionDuration}`;
260
- }
261
- function syncProxyEnv(config) {
262
- const configProxyUrl = normalizeProxyUrl(config.proxy?.url);
263
- if (!process.env.APIFUSE__PROXY__URL && configProxyUrl) {
264
- process.env.APIFUSE__PROXY__URL = configProxyUrl;
265
- }
263
+ return normalized || undefined;
266
264
  }
267
265
  export function resolveProxyConfig(options = {}) {
268
266
  const explicitProxyUrl = normalizeProxyUrl(options.proxy);
@@ -277,14 +275,6 @@ export function resolveProxyConfig(options = {}) {
277
275
  if (!legacyProxyRequested) {
278
276
  return { shouldWarn: false };
279
277
  }
280
- const envProxyUrl = normalizeProxyUrl(process.env.APIFUSE__PROXY__URL);
281
- if (envProxyUrl) {
282
- return { shouldWarn: false, url: envProxyUrl };
283
- }
284
- const configuredProxyUrl = normalizeProxyUrl(options.apifuseConfig?.proxy?.url);
285
- if (configuredProxyUrl) {
286
- return { shouldWarn: false, url: configuredProxyUrl };
287
- }
288
278
  return { shouldWarn: true };
289
279
  }
290
280
  export async function resolveProxyConfigAsync(options = {}) {
@@ -299,47 +289,213 @@ export async function resolveProxyConfigAsync(options = {}) {
299
289
  if (policy.mode === "disabled") {
300
290
  return { shouldWarn: false };
301
291
  }
302
- const provider = resolveProxyProvider(policy);
303
- if (provider !== "smartproxy") {
292
+ const chain = resolveVendorChain(policy);
293
+ if (chain.length === 0) {
294
+ const declared = declaredVendorChain(policy);
295
+ const deprecated = declared.filter((vendor) => vendor === "decodo" || vendor === "custom");
296
+ if (policy.mode === "required") {
297
+ const providerIds = declared.length > 0 ? declared.map((vendor) => `"${vendor}"`).join(", ") : "none";
298
+ const deprecatedDetail = deprecated.length > 0
299
+ ? ` Deprecated vendor(s): ${deprecated.map((vendor) => `"${vendor}"`).join(", ")}.`
300
+ : "";
301
+ throw new ProxyResolutionError("PROXY_REQUIRED", `Required proxy policy has no SDK-managed adapter for provider id(s): ${providerIds}.${deprecatedDetail} Use "smartproxy" or "nodemaven".`);
302
+ }
303
+ // Deprecated decodo/custom providers have no SDK-managed adapter. Optional
304
+ // policies preserve the warning-only behavior and may continue directly.
304
305
  return resolveProxyConfig({
305
306
  ...options,
306
307
  upstream: { proxy: true },
307
308
  });
308
309
  }
309
- const appKey = process.env[SMARTPROXY_APP_KEY_ENV]?.trim();
310
- if (!appKey) {
311
- if (policy.mode === "required") {
312
- throw new ProxyResolutionError("PROXY_REQUIRED", `Smartproxy egress is required but ${SMARTPROXY_APP_KEY_ENV} is not configured.`);
310
+ // Protocol is chosen per vendor (each vendor's benchmarked-best), with an
311
+ // optional explicit override for the harness/tests. Both are tunnelling
312
+ // schemes. transportProtocols is what the calling transport can actually use.
313
+ const transportProtocols = options.transportProtocols ?? ["http", "socks5"];
314
+ const sizes = chain.map((vendor) => vendorPoolSize(vendor, policy));
315
+ const total = sizes.reduce((sum, size) => sum + size, 0);
316
+ const normalizedAttempt = normalizeAttemptIndex(options.proxyAttempt);
317
+ const { vendorIndex: startVendorIndex, poolIndex: startPoolIndex } = mapFlatAttempt(total > 0 ? normalizedAttempt % total : 0, sizes);
318
+ const refreshEpoch = normalizeAttemptIndex(options.proxyRefreshEpoch);
319
+ let lastError;
320
+ let blockedProtocol;
321
+ for (let vendorIndex = startVendorIndex; vendorIndex < chain.length; vendorIndex++) {
322
+ const vendor = chain[vendorIndex];
323
+ const nextVendor = chain[vendorIndex + 1];
324
+ const poolIndex = vendorIndex === startVendorIndex ? startPoolIndex : 0;
325
+ const protocol = options.protocol ?? VENDOR_DEFAULT_PROTOCOL[vendor];
326
+ if (!vendorHasCredentials(vendor, options.engineCredentials)) {
327
+ options.telemetry?.recordProxyVendorFailover?.({
328
+ vendor,
329
+ nextVendor,
330
+ phase: "resolution",
331
+ reason: "no_credentials",
332
+ });
333
+ continue;
334
+ }
335
+ // The calling transport must be able to use this vendor's protocol; if not,
336
+ // fail over to the next vendor rather than silently downgrading.
337
+ if (!transportProtocols.includes(protocol)) {
338
+ blockedProtocol = protocol;
339
+ options.telemetry?.recordProxyVendorFailover?.({
340
+ vendor,
341
+ nextVendor,
342
+ phase: "resolution",
343
+ reason: "protocol_unsupported",
344
+ });
345
+ continue;
346
+ }
347
+ try {
348
+ return await resolveWithVendor(vendor, policy, options, {
349
+ protocol,
350
+ poolIndex,
351
+ refreshEpoch,
352
+ credentials: options.engineCredentials,
353
+ });
354
+ }
355
+ catch (error) {
356
+ // Config/programming errors (invalid filter, etc.) are not vendor
357
+ // outages — propagate them rather than failing over.
358
+ if (!(error instanceof ProxyResolutionError)) {
359
+ throw error;
360
+ }
361
+ if (error.telemetry) {
362
+ options.telemetry?.recordProxyResolution(error.telemetry);
363
+ }
364
+ lastError = error;
365
+ options.telemetry?.recordProxyVendorFailover?.({
366
+ vendor,
367
+ nextVendor,
368
+ phase: "resolution",
369
+ reason: "allocation_failed",
370
+ });
313
371
  }
314
- return { shouldWarn: true };
315
372
  }
316
- const lifetimeMinutes = resolveSmartproxyLifetime(policy);
373
+ if (policy.mode === "required") {
374
+ if (lastError) {
375
+ throw lastError instanceof ProxyResolutionError
376
+ ? lastError
377
+ : new ProxyResolutionError("PROXY_ALLOCATION_FAILED", `All proxy vendors [${chain.join(", ")}] failed for required proxy egress.`, { cause: lastError, vendorChain: chain });
378
+ }
379
+ if (blockedProtocol) {
380
+ throw new ProxyResolutionError("PROXY_PROTOCOL_UNSUPPORTED", `No proxy vendor in [${chain.join(", ")}] could serve a protocol supported by this transport (supports: ${transportProtocols.join(", ")}; vendor wanted "${blockedProtocol}"). Route this provider through the stealth transport.`, { protocol: blockedProtocol, vendorChain: chain });
381
+ }
382
+ throw new ProxyResolutionError("PROXY_REQUIRED", `Proxy egress is required but no vendor credentials are configured. Missing: ${chain
383
+ .map((vendor) => `${missingCredentialEnv(vendor)} (${vendor})`)
384
+ .join(", ")}.`, { vendorChain: chain });
385
+ }
386
+ return { shouldWarn: true };
387
+ }
388
+ /**
389
+ * Resolve the proxy URL for a provider-owned consumer such as a CAPTCHA solver.
390
+ * Vendor allocation and failover remain owned by the SDK.
391
+ */
392
+ export async function resolveProxy(options = {}) {
393
+ const resolved = await resolveProxyConfigAsync(options);
394
+ const vendor = vendorFromResolvedSource(resolved.source);
395
+ return vendor ? { ...resolved, vendor } : resolved;
396
+ }
397
+ /**
398
+ * Each vendor's default egress protocol, chosen from live KR benchmarks. HTTP
399
+ * CONNECT wins for nodemaven (socks5 adds ~500ms through the gateway) and ties
400
+ * for smartproxy, and is the only protocol ctx.http (Bun native fetch) supports.
401
+ * Override per call via ProxyResolutionOptions.protocol (harness/tests).
402
+ */
403
+ export const VENDOR_DEFAULT_PROTOCOL = {
404
+ smartproxy: "http",
405
+ nodemaven: NODEMAVEN_DEFAULT_PROTOCOL,
406
+ };
407
+ /**
408
+ * Guard the No-MITM invariant: a resolved proxy URL must use a tunnelling scheme
409
+ * (http CONNECT or socks5) so the client TLS handshake reaches the origin
410
+ * end-to-end. Anything else would intercept TLS and break fingerprinting.
411
+ */
412
+ export function assertTunnelingScheme(url) {
413
+ let scheme;
317
414
  try {
318
- const allocated = await allocateSmartproxy(policy, appKey, lifetimeMinutes, options.affinityKey);
319
- options.telemetry?.recordProxyResolution(allocated.telemetry);
320
- const poolIndex = selectProxyPoolIndex(allocated.pool.urls.length, options.proxyAttempt);
415
+ scheme = new URL(url).protocol.replace(/:$/, "").toLowerCase();
416
+ }
417
+ catch {
418
+ throw new ProxyResolutionError("PROXY_ALLOCATION_FAILED", `Malformed proxy URL: ${url}`);
419
+ }
420
+ if (scheme !== "http" && scheme !== "socks5") {
421
+ throw new ProxyResolutionError("PROXY_ALLOCATION_FAILED", `Resolved proxy scheme "${scheme}" is not a tunnelling scheme (expected http or socks5). Refusing to route TLS through a non-tunnelling proxy.`);
422
+ }
423
+ }
424
+ export async function resolveWithVendor(vendor, policy, options, context) {
425
+ if (vendor === "nodemaven") {
426
+ const startedAt = Date.now();
427
+ const username = (context.credentials === undefined
428
+ ? readDiagnosticEnv(NODEMAVEN_USERNAME_ENV)
429
+ : context.credentials[NODEMAVEN_USERNAME_ENV])?.trim();
430
+ const password = (context.credentials === undefined
431
+ ? readDiagnosticEnv(NODEMAVEN_PASSWORD_ENV)
432
+ : context.credentials[NODEMAVEN_PASSWORD_ENV])?.trim();
433
+ const filter = context.credentials === undefined
434
+ ? process.env[NODEMAVEN_FILTER_ENV]
435
+ : context.credentials[NODEMAVEN_FILTER_ENV];
436
+ if (!username || !password) {
437
+ throw new ProxyResolutionError("PROXY_ALLOCATION_FAILED", `NodeMaven credentials missing: set ${NODEMAVEN_USERNAME_ENV} and ${NODEMAVEN_PASSWORD_ENV}.`, { vendor: "nodemaven" });
438
+ }
439
+ const synthesized = synthesizeNodemavenProxy({
440
+ policy,
441
+ credentials: {
442
+ username,
443
+ password,
444
+ ...(filter ? { filter } : {}),
445
+ },
446
+ affinityKey: options.affinityKey,
447
+ protocol: context.protocol,
448
+ poolIndex: context.poolIndex,
449
+ refreshEpoch: context.refreshEpoch,
450
+ country: resolveSmartproxyCountry(policy, context.ambientDefaults !== false),
451
+ });
452
+ options.telemetry?.recordProxyResolution({
453
+ provider: "nodemaven",
454
+ protocol: synthesized.protocol,
455
+ cacheStatus: "disabled",
456
+ cacheHit: false,
457
+ resolutionMs: Math.max(0, Date.now() - startedAt),
458
+ attempts: 1,
459
+ });
460
+ assertTunnelingScheme(synthesized.url);
321
461
  return {
322
462
  shouldWarn: false,
323
- url: allocated.pool.urls[poolIndex],
324
- source: "smartproxy-allocator",
463
+ url: synthesized.url,
464
+ source: "nodemaven-gateway",
465
+ protocol: synthesized.protocol,
325
466
  diagnostics: {
326
- ...allocated.pool.diagnostics,
327
- poolSize: allocated.pool.urls.length,
328
- poolIndex,
467
+ ...synthesized.diagnostics,
468
+ poolIndex: context.poolIndex,
329
469
  },
330
470
  };
331
471
  }
332
- catch (error) {
333
- if (error instanceof ProxyResolutionError && error.telemetry) {
334
- options.telemetry?.recordProxyResolution(error.telemetry);
335
- }
336
- if (policy.mode === "required") {
337
- throw error instanceof ProxyResolutionError
338
- ? error
339
- : new ProxyResolutionError("PROXY_ALLOCATION_FAILED", "Smartproxy allocator failed for required proxy egress.", { cause: error });
340
- }
341
- return { shouldWarn: true };
342
- }
472
+ // smartproxy allocation-style vendor.
473
+ const appKey = (context.credentials === undefined
474
+ ? readDiagnosticEnv(SMARTPROXY_APP_KEY_ENV)
475
+ : context.credentials[SMARTPROXY_APP_KEY_ENV])?.trim();
476
+ if (!appKey) {
477
+ // Guarded by vendorHasCredentials; treated as a vendor-internal failure.
478
+ throw new ProxyResolutionError("PROXY_ALLOCATION_FAILED", `${SMARTPROXY_APP_KEY_ENV} is not configured.`, { vendor: "smartproxy" });
479
+ }
480
+ const lifetimeMinutes = resolveSmartproxyLifetime(policy, context.ambientDefaults !== false);
481
+ const allocated = await allocateSmartproxy(policy, appKey, lifetimeMinutes, options.affinityKey, context.protocol, context.ambientDefaults !== false, context.sharedCache !== false);
482
+ options.telemetry?.recordProxyResolution({ ...allocated.telemetry, protocol: context.protocol });
483
+ const poolIndex = selectProxyPoolIndex(allocated.pool.urls.length, context.poolIndex);
484
+ const url = allocated.pool.urls[poolIndex];
485
+ if (url)
486
+ assertTunnelingScheme(url);
487
+ return {
488
+ shouldWarn: false,
489
+ url,
490
+ source: "smartproxy-allocator",
491
+ protocol: context.protocol,
492
+ diagnostics: {
493
+ ...allocated.pool.diagnostics,
494
+ poolSize: allocated.pool.urls.length,
495
+ poolIndex,
496
+ poolCacheEvictions: proxyCache.capacityEvictions,
497
+ },
498
+ };
343
499
  }
344
500
  function resolvePolicy(options) {
345
501
  if (options.proxyPolicy) {
@@ -351,14 +507,187 @@ function resolvePolicy(options) {
351
507
  }
352
508
  return undefined;
353
509
  }
354
- function resolveProxyProvider(policy) {
355
- return (policy.provider ?? process.env[DEFAULT_PROXY_PROVIDER_ENV]?.trim().toLowerCase() ?? "custom");
510
+ function isRegistryVendor(name) {
511
+ return name === "smartproxy" || name === "nodemaven";
512
+ }
513
+ function declaredVendorChain(policy) {
514
+ const declared = policy.providers?.length
515
+ ? policy.providers
516
+ : [policy.provider ?? envDefaultProvider()];
517
+ return declared.filter((vendor) => vendor !== undefined);
518
+ }
519
+ /**
520
+ * Ordered list of SDK-native proxy vendors declared by the policy. `providers`
521
+ * takes precedence over the legacy singular `provider`; the platform default
522
+ * env is the final fallback. Non-registry names (decodo/custom) are dropped so
523
+ * an all-deprecated chain has no managed adapter.
524
+ */
525
+ export function resolveVendorChain(policy) {
526
+ const chain = [];
527
+ for (const name of declaredVendorChain(policy)) {
528
+ if (isRegistryVendor(name) && !chain.includes(name)) {
529
+ chain.push(name);
530
+ }
531
+ }
532
+ return chain;
533
+ }
534
+ function envDefaultProvider() {
535
+ const raw = process.env[DEFAULT_PROXY_PROVIDER_ENV]?.trim().toLowerCase();
536
+ return raw ?? undefined;
537
+ }
538
+ function vendorHasCredentials(vendor, credentials) {
539
+ if (vendor === "nodemaven") {
540
+ return Boolean((credentials === undefined
541
+ ? readDiagnosticEnv(NODEMAVEN_USERNAME_ENV)
542
+ : credentials[NODEMAVEN_USERNAME_ENV])?.trim() &&
543
+ (credentials === undefined
544
+ ? readDiagnosticEnv(NODEMAVEN_PASSWORD_ENV)
545
+ : credentials[NODEMAVEN_PASSWORD_ENV])?.trim());
546
+ }
547
+ return Boolean((credentials === undefined
548
+ ? readDiagnosticEnv(SMARTPROXY_APP_KEY_ENV)
549
+ : credentials[SMARTPROXY_APP_KEY_ENV])?.trim());
550
+ }
551
+ function missingCredentialEnv(vendor) {
552
+ return vendor === "nodemaven" ? "APIFUSE__PROXY__NODEMAVEN_USERNAME" : SMARTPROXY_APP_KEY_ENV;
553
+ }
554
+ function vendorPoolSize(vendor, policy) {
555
+ return vendor === "nodemaven" ? nodemavenPoolSize(policy) : resolveSmartproxyPoolSize(policy);
556
+ }
557
+ /**
558
+ * Total attempt span across a policy's vendor chain — the sum of each vendor's
559
+ * pool size. Transports use this so successive attempts rotate a vendor's pool
560
+ * and then fail over to the next vendor via the flat attempt index. With one
561
+ * vendor this equals that vendor's pool size (today's behaviour).
562
+ */
563
+ export function resolvePolicyProxyPoolSpan(policy) {
564
+ const chain = resolveVendorChain(policy);
565
+ if (chain.length === 0)
566
+ return resolveSmartproxyPoolSize(policy);
567
+ return chain.reduce((sum, vendor) => sum + vendorPoolSize(vendor, policy), 0);
568
+ }
569
+ function vendorMaxPoolSize(vendor) {
570
+ return vendor === "nodemaven" ? NODEMAVEN_MAX_POOL_SIZE : SMARTPROXY_MAX_POOL_SIZE;
571
+ }
572
+ /**
573
+ * Absolute upper bound on a chain's attempt span — the sum of each vendor's
574
+ * *maximum* pool size. Unlike `resolvePolicyProxyPoolSpan` (the configured
575
+ * span), this backstop is independent of `session.poolSize`, so it never
576
+ * truncates a legitimately large pool below the point where the flat attempt
577
+ * index would cross into the next vendor (e.g. a 50-slot NodeMaven pool).
578
+ */
579
+ export function maxPolicyProxyPoolSpan(policy) {
580
+ const chain = resolveVendorChain(policy);
581
+ if (chain.length === 0)
582
+ return SMARTPROXY_MAX_POOL_SIZE;
583
+ return chain.reduce((sum, vendor) => sum + vendorMaxPoolSize(vendor), 0);
584
+ }
585
+ const UNSAFE_TRANSPORT_RETRY_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE", "TRACE"]);
586
+ /**
587
+ * Transport-retry attempt cap for a policy-managed request. A transport failure
588
+ * rotates the flat attempt index onto the *next* endpoint (and, once the index
589
+ * passes the primary vendor's pool span, the *next vendor*), so the cap must be
590
+ * the chain's full pool span for failover to reach the fallback vendor — the
591
+ * per-endpoint retry budget (default 3) never gets there.
592
+ *
593
+ * The span only widens beyond the caller's retry budget when ALL hold:
594
+ * - the request is policy-allocator managed (not a caller-supplied proxy URL);
595
+ * - the caller did NOT pin an explicit retry policy — `HttpRetryOptions.attempts`
596
+ * is the documented total-attempt ceiling and must be honoured verbatim;
597
+ * - the method is safe/idempotent — an unsafe request must never be duplicated
598
+ * across the pool even if some framework default would allow it;
599
+ * - the policy resolves a non-empty *registry* vendor chain (smartproxy /
600
+ * nodemaven). Deprecated vendors (custom / decodo) resolve no managed pool,
601
+ * so there is no possible endpoint crossover — they keep the retry budget.
602
+ *
603
+ * The widened cap is bounded by the chain's true maximum span (sum of each
604
+ * vendor's max pool size), so a large NodeMaven pool (≤50) stays reachable and
605
+ * a pathological chain can never spin unbounded.
606
+ */
607
+ /**
608
+ * True when a policy request is in *implicit chain-rotation* mode: successive
609
+ * transport attempts rotate the flat index across the concatenated vendor pool
610
+ * spans (and, past the primary vendor's span, into the fallback vendor). This is
611
+ * the ONLY mode in which the transport loop widens its attempt cap AND
612
+ * de-duplicates repeated endpoints — the two behaviours must share one predicate
613
+ * so they never diverge. It holds when ALL of the widening conditions hold:
614
+ * - the request is policy-allocator managed (not a caller-supplied proxy URL);
615
+ * - the caller did NOT pin an explicit retry policy — its `attempts` ceiling is
616
+ * the documented contract and must be honoured verbatim against whatever
617
+ * endpoint each attempt resolves (even a repeated one), so no de-duplication;
618
+ * - the method is safe/idempotent — an unsafe request is never duplicated;
619
+ * - the policy resolves a non-empty registry vendor chain (smartproxy /
620
+ * nodemaven). Deprecated vendors (custom / decodo) resolve no managed
621
+ * endpoint, so there is nothing to rotate or de-duplicate.
622
+ */
623
+ export function policyRotatesTransportVendorChain(input) {
624
+ if (!input.usesPolicyAllocator || !input.policy || input.explicitRetry) {
625
+ return false;
626
+ }
627
+ if (UNSAFE_TRANSPORT_RETRY_METHODS.has(input.method.toUpperCase())) {
628
+ return false;
629
+ }
630
+ return resolveVendorChain(input.policy).length > 0;
631
+ }
632
+ export function resolvePolicyTransportAttemptCap(input) {
633
+ const budget = Math.max(1, Math.floor(input.retryAttempts));
634
+ if (!policyRotatesTransportVendorChain({
635
+ policy: input.policy,
636
+ usesPolicyAllocator: input.usesPolicyAllocator,
637
+ explicitRetry: input.explicitRetry,
638
+ method: input.method,
639
+ })) {
640
+ return budget;
641
+ }
642
+ const span = Math.min(maxPolicyProxyPoolSpan(input.policy), resolvePolicyProxyPoolSpan(input.policy));
643
+ return Math.max(budget, span);
644
+ }
645
+ /**
646
+ * A registry vendor chain (smartproxy/nodemaven) resolves a potentially
647
+ * *different* endpoint per flat attempt index, so a transport retry should
648
+ * advance across endpoints and de-duplicate once the chain stops yielding new
649
+ * ones. Deprecated custom/decodo policies have an empty registry chain and no
650
+ * managed endpoint, so the transport loop has nothing to rotate or de-duplicate.
651
+ */
652
+ export function policyResolvesRegistryVendorChain(policy) {
653
+ return Boolean(policy) && resolveVendorChain(policy).length > 0;
654
+ }
655
+ /** Map a resolved proxy source label to the vendor that served it. */
656
+ export function vendorFromResolvedSource(source) {
657
+ if (source === "nodemaven-gateway")
658
+ return "nodemaven";
659
+ if (source === "smartproxy-allocator")
660
+ return "smartproxy";
661
+ return undefined;
662
+ }
663
+ function normalizeAttemptIndex(attempt) {
664
+ return Number.isFinite(attempt) ? Math.max(0, Math.floor(attempt)) : 0;
356
665
  }
357
- function resolveSmartproxyCountry(policy) {
358
- return (policy.geo?.country ?? process.env[DEFAULT_PROXY_COUNTRY_ENV]?.trim().toUpperCase() ?? undefined);
666
+ /**
667
+ * Map a flat attempt index into (vendorIndex, poolIndex) by concatenating each
668
+ * vendor's pool space in chain order. With a single vendor this reduces to
669
+ * `attempt % poolSize`, preserving today's behaviour exactly.
670
+ */
671
+ export function mapFlatAttempt(flat, sizes) {
672
+ let cursor = flat;
673
+ for (let vendorIndex = 0; vendorIndex < sizes.length; vendorIndex++) {
674
+ const size = Math.max(1, sizes[vendorIndex] ?? 1);
675
+ if (cursor < size) {
676
+ return { vendorIndex, poolIndex: cursor };
677
+ }
678
+ cursor -= size;
679
+ }
680
+ return { vendorIndex: 0, poolIndex: 0 };
359
681
  }
360
- function resolveSmartproxyLifetime(policy) {
361
- const configuredLifetime = policy.session?.lifetimeMinutes ?? readPositiveNumberEnv(DEFAULT_PROXY_LIFETIME_ENV, 30);
682
+ function resolveSmartproxyCountry(policy, ambientDefaults = true) {
683
+ return (policy.geo?.country ??
684
+ (ambientDefaults
685
+ ? process.env[DEFAULT_PROXY_COUNTRY_ENV]?.trim().toUpperCase() || undefined
686
+ : undefined));
687
+ }
688
+ function resolveSmartproxyLifetime(policy, ambientDefaults = true) {
689
+ const configuredLifetime = policy.session?.lifetimeMinutes ??
690
+ (ambientDefaults ? readPositiveNumberEnv(DEFAULT_PROXY_LIFETIME_ENV, 30) : 30);
362
691
  return Math.min(SMARTPROXY_MAX_LIFETIME_MINUTES, Math.max(1, Math.floor(configuredLifetime)));
363
692
  }
364
693
  function readPositiveNumberEnv(name, fallback) {
@@ -381,27 +710,32 @@ function selectProxyPoolIndex(poolSize, attempt = 0) {
381
710
  const normalizedAttempt = Number.isFinite(attempt) ? Math.max(0, Math.floor(attempt)) : 0;
382
711
  return normalizedAttempt % poolSize;
383
712
  }
384
- function buildSmartproxyCacheKey(policy, affinityKey, lifetimeMinutes) {
713
+ function buildSmartproxyCacheKey(policy, appKey, affinityKey, lifetimeMinutes, protocol, ambientDefaults = true) {
385
714
  const poolSize = resolveSmartproxyPoolSize(policy);
386
715
  return JSON.stringify({
387
716
  provider: "smartproxy",
388
- country: resolveSmartproxyCountry(policy),
717
+ credentialHash: createHash("sha256")
718
+ .update("apifuse-smartproxy-credential:v1\0")
719
+ .update(appKey)
720
+ .digest("hex"),
721
+ protocol,
722
+ country: resolveSmartproxyCountry(policy, ambientDefaults),
389
723
  affinity: policy.session?.affinity ?? "request",
390
724
  affinityKey: (policy.session?.affinity ?? "request") === "request" ? undefined : affinityKey,
391
725
  lifetimeMinutes,
392
726
  poolSize,
393
727
  });
394
728
  }
395
- async function allocateSmartproxy(policy, appKey, lifetimeMinutes, affinityKey) {
396
- const cacheKey = buildSmartproxyCacheKey(policy, affinityKey, lifetimeMinutes);
729
+ async function allocateSmartproxy(policy, appKey, lifetimeMinutes, affinityKey, protocol, ambientDefaults = true, sharedCache = true) {
730
+ const cacheKey = buildSmartproxyCacheKey(policy, appKey, affinityKey, lifetimeMinutes, protocol, ambientDefaults);
397
731
  const startedAt = Date.now();
398
732
  const now = startedAt;
399
733
  const invalidatedUntil = invalidatedProxyKeys.get(cacheKey) ?? 0;
400
734
  const skipCached = invalidatedUntil > now;
401
- const cached = proxyCache.get(cacheKey);
402
- if (!skipCached && cached && isFresh(cached, now)) {
735
+ const cached = skipCached ? undefined : proxyCache.get(cacheKey, now);
736
+ if (cached) {
403
737
  if (shouldSoftRefresh(cached, now)) {
404
- void refreshSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes);
738
+ void refreshSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, protocol, ambientDefaults, sharedCache);
405
739
  return {
406
740
  pool: cached,
407
741
  telemetry: telemetryForPool(cached, "soft_stale_refresh", startedAt, {
@@ -414,7 +748,7 @@ async function allocateSmartproxy(policy, appKey, lifetimeMinutes, affinityKey)
414
748
  telemetry: telemetryForPool(cached, "memory_hit", startedAt),
415
749
  };
416
750
  }
417
- if (!skipCached) {
751
+ if (!skipCached && sharedCache) {
418
752
  const redisResult = await readSmartproxyRedisPool(cacheKey, startedAt);
419
753
  if (redisResult)
420
754
  return redisResult;
@@ -429,7 +763,7 @@ async function allocateSmartproxy(policy, appKey, lifetimeMinutes, affinityKey)
429
763
  }),
430
764
  };
431
765
  }
432
- const promise = allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, startedAt).finally(() => {
766
+ const promise = allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, startedAt, protocol, ambientDefaults, sharedCache).finally(() => {
433
767
  proxyInflight.delete(cacheKey);
434
768
  });
435
769
  proxyInflight.set(cacheKey, promise);
@@ -454,15 +788,15 @@ async function readSmartproxyRedisPool(cacheKey, startedAt) {
454
788
  const now = Date.now();
455
789
  if (!isFresh(pool, now))
456
790
  return null;
457
- proxyCache.set(cacheKey, pool);
791
+ proxyCache.set(cacheKey, pool, now);
458
792
  return {
459
793
  pool,
460
794
  telemetry: telemetryForPool(pool, "redis_hit", startedAt, { redisReadMs }),
461
795
  };
462
796
  }
463
- async function refreshSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes) {
797
+ async function refreshSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, protocol, ambientDefaults, sharedCache) {
464
798
  try {
465
- await allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, Date.now(), {
799
+ await allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, Date.now(), protocol, ambientDefaults, sharedCache, {
466
800
  background: true,
467
801
  });
468
802
  }
@@ -470,10 +804,10 @@ async function refreshSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes)
470
804
  // Soft refresh is opportunistic; current fresh pool remains usable.
471
805
  }
472
806
  }
473
- async function allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, startedAt, options = {}) {
474
- const redis = getProxyRedis();
807
+ async function allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, startedAt, protocol, ambientDefaults, sharedCache, options = {}) {
808
+ const redis = sharedCache ? getProxyRedis() : undefined;
475
809
  if (!redis || !(await ensureRedisReady(redis))) {
476
- return await allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, startedAt, { cacheStatus: "allocator" });
810
+ return await allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, startedAt, { cacheStatus: "allocator", protocol, ambientDefaults });
477
811
  }
478
812
  const poolKey = smartproxyRedisPoolKey(cacheKey);
479
813
  const lockKey = smartproxyRedisLockKey(cacheKey);
@@ -487,6 +821,8 @@ async function allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinute
487
821
  cacheStatus: options.background ? "soft_stale_refresh" : "allocator",
488
822
  redis,
489
823
  poolKey,
824
+ protocol,
825
+ ambientDefaults,
490
826
  });
491
827
  }
492
828
  finally {
@@ -599,7 +935,7 @@ async function readSmartproxyAllocatorBodyWithDeadline(response, signal) {
599
935
  }
600
936
  async function allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, startedAt, options) {
601
937
  const poolSize = resolveSmartproxyPoolSize(policy);
602
- const allocatorUrl = buildSmartproxyAllocatorUrl(policy, appKey, lifetimeMinutes, poolSize);
938
+ const allocatorUrl = buildSmartproxyAllocatorUrl(policy, appKey, lifetimeMinutes, poolSize, options.protocol, options.ambientDefaults);
603
939
  const allocatorStartedAt = Date.now();
604
940
  const allocatorDeadlineAt = allocatorStartedAt + smartproxyAllocatorDeadlineMs();
605
941
  let allocation;
@@ -609,7 +945,7 @@ async function allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetime
609
945
  lastFailure = smartproxyAllocatorDeadlineFailure(attempt);
610
946
  break;
611
947
  }
612
- const attemptResult = await fetchSmartproxyAllocatorAttempt(allocatorUrl, attempt, allocatorDeadlineAt);
948
+ const attemptResult = await fetchSmartproxyAllocatorAttempt(allocatorUrl, attempt, allocatorDeadlineAt, options.protocol);
613
949
  if (attemptResult.ok) {
614
950
  allocation = attemptResult;
615
951
  break;
@@ -657,13 +993,13 @@ async function allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetime
657
993
  expiresAt: allocatedAt + ttlMs,
658
994
  diagnostics: {
659
995
  provider: "smartproxy",
660
- country: resolveSmartproxyCountry(policy) ?? "default",
996
+ country: resolveSmartproxyCountry(policy, options.ambientDefaults) ?? "default",
661
997
  lifetimeMinutes,
662
998
  affinity: policy.session?.affinity ?? "request",
663
999
  rawConnect: true,
664
1000
  },
665
1001
  };
666
- proxyCache.set(cacheKey, result);
1002
+ proxyCache.set(cacheKey, result, allocatedAt);
667
1003
  if (options.redis && options.poolKey) {
668
1004
  const redis = options.redis;
669
1005
  const poolKey = options.poolKey;
@@ -692,7 +1028,7 @@ async function allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetime
692
1028
  }),
693
1029
  };
694
1030
  }
695
- async function fetchSmartproxyAllocatorAttempt(allocatorUrl, attempt, deadlineAt) {
1031
+ async function fetchSmartproxyAllocatorAttempt(allocatorUrl, attempt, deadlineAt, protocol) {
696
1032
  const { controller, dispose } = createDeadlineAbortController(deadlineAt);
697
1033
  let response;
698
1034
  try {
@@ -735,7 +1071,7 @@ async function fetchSmartproxyAllocatorAttempt(allocatorUrl, attempt, deadlineAt
735
1071
  bodyClass: "http_error",
736
1072
  };
737
1073
  }
738
- const urls = parseSmartproxyAllocatorProxies(body);
1074
+ const urls = parseSmartproxyAllocatorProxies(body, protocol);
739
1075
  const bodyClass = classifySmartproxyAllocatorBody(body, urls);
740
1076
  if (urls.length === 0) {
741
1077
  return {
@@ -770,17 +1106,24 @@ function smartproxyAllocatorFailureMessage(failure) {
770
1106
  }
771
1107
  return "Smartproxy allocator response did not contain a usable proxy endpoint.";
772
1108
  }
773
- function buildSmartproxyAllocatorUrl(policy, appKey, lifetimeMinutes, poolSize) {
1109
+ // Smartproxy get-ip-v3 `protocol` param: 1 = HTTP. The SOCKS5 value ("2") is a
1110
+ // best-effort mapping pending live vendor confirmation; http is the default and
1111
+ // the only value exercised in production today.
1112
+ const SMARTPROXY_PROTOCOL_PARAM = {
1113
+ http: "1",
1114
+ socks5: "2",
1115
+ };
1116
+ function buildSmartproxyAllocatorUrl(policy, appKey, lifetimeMinutes, poolSize, protocol, ambientDefaults = true) {
774
1117
  const params = new URLSearchParams({
775
1118
  app_key: appKey,
776
1119
  pt: "9",
777
1120
  num: String(poolSize),
778
1121
  life: String(lifetimeMinutes),
779
- protocol: "1",
1122
+ protocol: SMARTPROXY_PROTOCOL_PARAM[protocol],
780
1123
  format: "txt",
781
1124
  lb: "\\n",
782
1125
  });
783
- const country = resolveSmartproxyCountry(policy);
1126
+ const country = resolveSmartproxyCountry(policy, ambientDefaults);
784
1127
  if (country) {
785
1128
  params.set("cc", country);
786
1129
  }
@@ -788,7 +1131,8 @@ function buildSmartproxyAllocatorUrl(policy, appKey, lifetimeMinutes, poolSize)
788
1131
  // old path 404s into the marketing site); the API lives on the api host.
789
1132
  return `https://api.smartproxy.org/web_v1/ip/get-ip-v3?${params.toString()}`;
790
1133
  }
791
- function parseSmartproxyAllocatorProxies(body) {
1134
+ function parseSmartproxyAllocatorProxies(body, protocol) {
1135
+ const scheme = protocol === "socks5" ? "socks5" : "http";
792
1136
  const trimmed = body.trim();
793
1137
  if (!trimmed) {
794
1138
  return [];
@@ -807,7 +1151,7 @@ function parseSmartproxyAllocatorProxies(body) {
807
1151
  const port = "port" in item && (typeof item.port === "string" || typeof item.port === "number")
808
1152
  ? item.port
809
1153
  : "";
810
- return ip && port ? `http://${ip}:${port}` : null;
1154
+ return ip && port ? `${scheme}://${ip}:${port}` : null;
811
1155
  })
812
1156
  .filter((url) => url !== null);
813
1157
  }
@@ -819,7 +1163,7 @@ function parseSmartproxyAllocatorProxies(body) {
819
1163
  .split(/\r?\n/)
820
1164
  .map((item) => item.trim())
821
1165
  .filter((item) => /^\d{1,3}(?:\.\d{1,3}){3}:\d{2,5}$/.test(item))
822
- .map((line) => `http://${line}`);
1166
+ .map((line) => `${scheme}://${line}`);
823
1167
  }
824
1168
  function classifySmartproxyAllocatorBody(body, urls) {
825
1169
  if (urls.length > 0) {
@@ -847,12 +1191,22 @@ function markSmartproxyCacheInvalidated(options = {}) {
847
1191
  if (!policy || policy.mode === "disabled") {
848
1192
  return undefined;
849
1193
  }
850
- if (resolveProxyProvider(policy) !== "smartproxy") {
1194
+ if (!resolveVendorChain(policy).includes("smartproxy")) {
851
1195
  return undefined;
852
1196
  }
853
1197
  const lifetimeMinutes = resolveSmartproxyLifetime(policy);
854
- const cacheKey = buildSmartproxyCacheKey(policy, options.affinityKey, lifetimeMinutes);
855
- invalidatedProxyKeys.set(cacheKey, Date.now() + SMARTPROXY_INVALIDATION_SKIP_REDIS_MS);
1198
+ const appKey = (options.engineCredentials === undefined
1199
+ ? readDiagnosticEnv(SMARTPROXY_APP_KEY_ENV)
1200
+ : options.engineCredentials[SMARTPROXY_APP_KEY_ENV])?.trim();
1201
+ if (!appKey)
1202
+ return undefined;
1203
+ const cacheKey = buildSmartproxyCacheKey(policy, appKey, options.affinityKey, lifetimeMinutes, options.protocol ?? VENDOR_DEFAULT_PROTOCOL.smartproxy);
1204
+ const now = Date.now();
1205
+ for (const [key, invalidatedUntil] of invalidatedProxyKeys) {
1206
+ if (invalidatedUntil <= now)
1207
+ invalidatedProxyKeys.delete(key);
1208
+ }
1209
+ invalidatedProxyKeys.set(cacheKey, now + SMARTPROXY_INVALIDATION_SKIP_REDIS_MS);
856
1210
  proxyCache.delete(cacheKey);
857
1211
  proxyInflight.delete(cacheKey);
858
1212
  return cacheKey;
@@ -897,16 +1251,12 @@ export async function loadApiFuseConfig(dir = process.cwd()) {
897
1251
  const tsPath = path.resolve(dir, "apifuse.config.ts");
898
1252
  if (existsSync(tsPath)) {
899
1253
  const config = await importConfig(tsPath);
900
- const resolvedConfig = config ?? {};
901
- syncProxyEnv(resolvedConfig);
902
- return resolvedConfig;
1254
+ return config ?? {};
903
1255
  }
904
1256
  const jsPath = path.resolve(dir, "apifuse.config.js");
905
1257
  if (existsSync(jsPath)) {
906
1258
  const config = await importConfig(jsPath);
907
- const resolvedConfig = config ?? {};
908
- syncProxyEnv(resolvedConfig);
909
- return resolvedConfig;
1259
+ return config ?? {};
910
1260
  }
911
1261
  return {};
912
1262
  }