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

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 (368) hide show
  1. package/AUTHORING.md +537 -33
  2. package/CHANGELOG.md +205 -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 +1249 -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-hoisted-array-spread.ts.txt +15 -0
  228. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +9 -0
  229. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +5 -0
  230. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +24 -0
  231. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +16 -0
  232. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +3 -0
  233. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +31 -0
  234. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +11 -0
  235. package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +11 -0
  236. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +11 -0
  237. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +21 -0
  238. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +11 -0
  239. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +14 -0
  240. package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +7 -0
  241. package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +6 -0
  242. package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +7 -0
  243. package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +6 -0
  244. package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +7 -0
  245. package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +7 -0
  246. package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +15 -0
  247. package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +4 -0
  248. package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +12 -0
  249. package/src/cli/commands.ts +33 -0
  250. package/src/cli/create.ts +50 -37
  251. package/src/cli/migrate-operation-declaration.ts +1751 -0
  252. package/src/cli/migrate-operation-shape.ts +184 -0
  253. package/src/cli/migrate-provider-shape.ts +772 -0
  254. package/src/cli/prompt-assets.ts +865 -0
  255. package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
  256. package/src/cli/templates/provider/Dockerfile.tpl +1 -1
  257. package/src/cli/templates/provider/README.md.tpl +5 -5
  258. package/src/cli/templates/provider/index.ts.tpl +6 -3
  259. package/src/cli/templates/provider/operations/ping.ts.tpl +4 -1
  260. package/src/cli/templates/provider/provider.json.tpl +6 -0
  261. package/src/config/loader.ts +690 -163
  262. package/src/contract-serialization.ts +5 -7
  263. package/src/contract-types.ts +12 -5
  264. package/src/contract.ts +24 -10
  265. package/src/declaration-validation.ts +330 -0
  266. package/src/define.ts +1048 -131
  267. package/src/dev.ts +4 -1
  268. package/src/engine.ts +279 -0
  269. package/src/error-observability.ts +64 -0
  270. package/src/error-resolution.ts +127 -0
  271. package/src/errors.ts +68 -0
  272. package/src/fixture-sanitization.ts +264 -0
  273. package/src/health-scenario.ts +875 -0
  274. package/src/index.ts +220 -13
  275. package/src/lint.ts +698 -90
  276. package/src/native-address.ts +340 -0
  277. package/src/native-egress-policy.ts +358 -0
  278. package/src/observability.ts +51 -1
  279. package/src/provider.ts +161 -3
  280. package/src/runtime/auth-flow.ts +12 -0
  281. package/src/runtime/browser.ts +661 -63
  282. package/src/runtime/cache.ts +189 -14
  283. package/src/runtime/choice-wordlist.ts +145 -0
  284. package/src/runtime/choice.ts +631 -120
  285. package/src/runtime/chrome149-header-order.ts +330 -0
  286. package/src/runtime/env.ts +13 -0
  287. package/src/runtime/executor.ts +43 -31
  288. package/src/runtime/http.ts +641 -61
  289. package/src/runtime/insights.ts +2 -2
  290. package/src/runtime/instrumentation.ts +520 -15
  291. package/src/runtime/native-network-errors.ts +99 -0
  292. package/src/runtime/native-network.ts +1605 -0
  293. package/src/runtime/ocr.ts +523 -0
  294. package/src/runtime/otlp.ts +467 -21
  295. package/src/runtime/proxy-errors.ts +12 -4
  296. package/src/runtime/proxy-nodemaven.ts +221 -0
  297. package/src/runtime/proxy-telemetry.ts +244 -75
  298. package/src/runtime/redirects.ts +66 -0
  299. package/src/runtime/redis.ts +7 -2
  300. package/src/runtime/request-options.ts +680 -1
  301. package/src/runtime/resolver-config.ts +6 -0
  302. package/src/runtime/resolver-public.ts +23 -0
  303. package/src/runtime/resolver-shared.ts +33 -0
  304. package/src/runtime/resolver-vendors/bindings.ts +56 -0
  305. package/src/runtime/resolver-vendors/browser.ts +533 -0
  306. package/src/runtime/resolver-vendors/capsolver.ts +707 -0
  307. package/src/runtime/resolver-vendors/hosts.ts +38 -0
  308. package/src/runtime/resolver-vendors/twocaptcha.ts +540 -0
  309. package/src/runtime/resolver-vendors/types.ts +212 -0
  310. package/src/runtime/resolver.ts +1157 -0
  311. package/src/runtime/secrets.ts +64 -0
  312. package/src/runtime/state.ts +394 -77
  313. package/src/runtime/stealth-cookies.ts +132 -0
  314. package/src/runtime/stealth.ts +1280 -336
  315. package/src/runtime/stt.ts +1 -19
  316. package/src/runtime/timeout.ts +18 -0
  317. package/src/runtime/trace-config.ts +78 -0
  318. package/src/runtime/trace.ts +57 -17
  319. package/src/serve.ts +6 -1
  320. package/src/server/error-observability.ts +1 -0
  321. package/src/server/index.ts +34 -2
  322. package/src/server/self-test-input-tokens.ts +29 -14
  323. package/src/server/self-test.ts +191 -68
  324. package/src/server/serve-implementation.ts +3378 -0
  325. package/src/server/serve.ts +1 -1632
  326. package/src/server/trace-output.ts +129 -0
  327. package/src/server/types.ts +13 -1
  328. package/src/stateful/README.md +146 -0
  329. package/src/stateful/errors.ts +35 -0
  330. package/src/stateful/http-provider-event-emitter.ts +314 -0
  331. package/src/stateful/http-session-owner-registry.ts +306 -0
  332. package/src/stateful/index.ts +18 -0
  333. package/src/stateful/provider-event-delivery-failures.ts +80 -0
  334. package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
  335. package/src/stateful/provider-event-pipeline.ts +61 -0
  336. package/src/stateful/provider-events.ts +462 -0
  337. package/src/stateful/session-key.ts +111 -0
  338. package/src/stateful/stateful-provider-adapter-context.ts +59 -0
  339. package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
  340. package/src/stateful/stateful-provider-adapter.ts +562 -0
  341. package/src/stateful/stateful-provider-observability.ts +261 -0
  342. package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
  343. package/src/stateful/stateful-provider-runtime-context.ts +92 -0
  344. package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
  345. package/src/stateful/stateful-provider-session-routing.ts +546 -0
  346. package/src/stateful/stateful-provider-session-runtime.ts +403 -0
  347. package/src/stateful-signing.ts +46 -0
  348. package/src/stealth/profiles.ts +202 -231
  349. package/src/stream-evidence.ts +988 -0
  350. package/src/stream.ts +8 -1
  351. package/src/testing/index.ts +10 -1
  352. package/src/testing/run.ts +658 -15
  353. package/src/trace-sanitization.ts +63 -0
  354. package/src/types.ts +788 -174
  355. package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
  356. package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
  357. /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  358. /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  359. /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  360. /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  361. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  362. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
  363. /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  364. /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  365. /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  366. /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  367. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  368. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
@@ -0,0 +1,1232 @@
1
+ import { createHash } from "node:crypto";
2
+ import { Socket } from "node:net";
3
+ import { connect as connectTlsSocket } from "node:tls";
4
+ import { SocksClient } from "socks";
5
+ import { assertTunnelingScheme, ProxyResolutionError, SMARTPROXY_APP_KEY_ENV, VENDOR_DEFAULT_PROTOCOL, resolveWithVendor, } from "../config/loader.js";
6
+ import { NativeEgressPolicyValidationError, parseNativeEgressPolicy, } from "../native-egress-policy.js";
7
+ import { canonicalizeEgressHost, classifyEgressHost, embeddedIpv4FromIpv6, ipv4InCidr, ipv6InCidr, parseIpv6, parseStrictIpv4, } from "../native-address.js";
8
+ import { createEnvContext } from "./env.js";
9
+ import { NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, safeDiagnosticEgressHost, } from "./native-network-errors.js";
10
+ import { NODEMAVEN_FILTER_ENV, NODEMAVEN_PASSWORD_ENV, NODEMAVEN_USERNAME_ENV, nodemavenSessionWindow, synthesizeNodemavenProxy, } from "./proxy-nodemaven.js";
11
+ import { redactSensitiveError } from "./request-options.js";
12
+ export { NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, } from "./native-network-errors.js";
13
+ const VENDOR_CREDENTIAL_NAMES = {
14
+ smartproxy: [SMARTPROXY_APP_KEY_ENV],
15
+ nodemaven: [NODEMAVEN_USERNAME_ENV, NODEMAVEN_PASSWORD_ENV],
16
+ };
17
+ /** Build a resolver over the SDK's existing injectable environment context. */
18
+ export function createEnvVendorCredentialResolver(env = createEnvContext()) {
19
+ return (vendor) => {
20
+ const names = VENDOR_CREDENTIAL_NAMES[vendor] ?? [];
21
+ const values = {};
22
+ const missing = [];
23
+ for (const name of names) {
24
+ const value = env.get(name)?.trim();
25
+ if (value)
26
+ values[name] = value;
27
+ else
28
+ missing.push(name);
29
+ }
30
+ if (missing.length > 0 || names.length === 0)
31
+ return { kind: "absent", missing };
32
+ if (vendor === "nodemaven") {
33
+ const filter = env.get(NODEMAVEN_FILTER_ENV)?.trim();
34
+ if (filter)
35
+ values[NODEMAVEN_FILTER_ENV] = filter;
36
+ }
37
+ return { kind: "present", values };
38
+ };
39
+ }
40
+ function skipped(reason) {
41
+ return { kind: "skipped", reason };
42
+ }
43
+ function lookupCredentials(input) {
44
+ try {
45
+ return input.credentials(input.vendor);
46
+ }
47
+ catch (error) {
48
+ return {
49
+ kind: "error",
50
+ cause: error instanceof Error ? error : new Error(String(error)),
51
+ };
52
+ }
53
+ }
54
+ function synthesizeNodemavenGateway(input) {
55
+ if (input.vendor !== "nodemaven")
56
+ return undefined;
57
+ const lookup = lookupCredentials(input);
58
+ if (lookup.kind === "error") {
59
+ return skipped({ kind: "credential_lookup_failed", cause: lookup.cause });
60
+ }
61
+ if (lookup.kind === "absent") {
62
+ return skipped({ kind: "credentials_absent", missing: lookup.missing });
63
+ }
64
+ const sessionWindow = nodemavenSessionWindow(input.policy, input.now);
65
+ const synthesized = synthesizeNodemavenProxy({
66
+ policy: input.policy,
67
+ credentials: {
68
+ username: lookup.values[NODEMAVEN_USERNAME_ENV] ?? "",
69
+ password: lookup.values[NODEMAVEN_PASSWORD_ENV] ?? "",
70
+ ...(lookup.values[NODEMAVEN_FILTER_ENV]
71
+ ? { filter: lookup.values[NODEMAVEN_FILTER_ENV] }
72
+ : {}),
73
+ },
74
+ affinityKey: input.affinityKey,
75
+ protocol: input.protocol,
76
+ poolIndex: 0,
77
+ refreshEpoch: sessionWindow.refreshEpoch,
78
+ now: input.now,
79
+ expiresAt: sessionWindow.expiresAt,
80
+ });
81
+ return {
82
+ url: synthesized.url,
83
+ vendor: "nodemaven",
84
+ sticky: synthesized.sticky,
85
+ sessionId: synthesized.sessionId,
86
+ expiresAt: synthesized.expiresAt,
87
+ };
88
+ }
89
+ async function synthesizeSmartproxyGateway(input) {
90
+ if (input.vendor !== "smartproxy")
91
+ return undefined;
92
+ const lookup = lookupCredentials(input);
93
+ if (lookup.kind === "error") {
94
+ return skipped({ kind: "credential_lookup_failed", cause: lookup.cause });
95
+ }
96
+ if (lookup.kind === "absent") {
97
+ return skipped({ kind: "credentials_absent", missing: lookup.missing });
98
+ }
99
+ try {
100
+ const resolved = await resolveWithVendor("smartproxy", input.policy, {
101
+ proxyPolicy: input.policy,
102
+ affinityKey: input.affinityKey,
103
+ protocol: input.protocol,
104
+ }, {
105
+ protocol: input.protocol,
106
+ poolIndex: 0,
107
+ refreshEpoch: 0,
108
+ credentials: lookup.values,
109
+ ambientDefaults: false,
110
+ sharedCache: false,
111
+ });
112
+ if (!resolved.url) {
113
+ return skipped({ kind: "allocation_failed", cause: new Error("No endpoint returned") });
114
+ }
115
+ return {
116
+ url: resolved.url,
117
+ vendor: "smartproxy",
118
+ sticky: isStickyPolicy(input.policy),
119
+ };
120
+ }
121
+ catch (error) {
122
+ const cause = error instanceof Error ? error : new Error(String(error));
123
+ return skipped({
124
+ kind: "allocation_failed",
125
+ cause: redactSensitiveError(cause, Object.values(lookup.values)),
126
+ });
127
+ }
128
+ }
129
+ const DEFAULT_GATEWAY_SYNTHESIZERS = [
130
+ synthesizeSmartproxyGateway,
131
+ synthesizeNodemavenGateway,
132
+ ];
133
+ /** Domain-separated, process-independent affinity derived from credential identity. */
134
+ export function deriveNativeCredentialAffinityKey(credentialIdentity) {
135
+ return createHash("sha256")
136
+ .update("apifuse-native-proxy-affinity:v1\0")
137
+ .update(credentialIdentity)
138
+ .digest("hex");
139
+ }
140
+ function isStickyPolicy(policy) {
141
+ return (policy.session?.affinity ?? "request") !== "request";
142
+ }
143
+ function resolveNativeVendorChain(policy) {
144
+ const declared = policy.providers?.length
145
+ ? policy.providers
146
+ : policy.provider
147
+ ? [policy.provider]
148
+ : ["nodemaven"];
149
+ const chain = [];
150
+ for (const vendor of declared) {
151
+ if (!chain.includes(vendor))
152
+ chain.push(vendor);
153
+ }
154
+ return chain;
155
+ }
156
+ function isProxyProtocol(value) {
157
+ return value === "http" || value === "socks5";
158
+ }
159
+ function isSkippedSynthesis(result) {
160
+ return "kind" in result && result.kind === "skipped";
161
+ }
162
+ function sanitizeVendorResolutionCause(error, vendor, credentials) {
163
+ const cause = error instanceof Error ? error : new Error(String(error));
164
+ try {
165
+ const lookup = credentials(vendor);
166
+ return lookup.kind === "present"
167
+ ? redactSensitiveError(cause, Object.values(lookup.values))
168
+ : cause;
169
+ }
170
+ catch {
171
+ return cause;
172
+ }
173
+ }
174
+ function sanitizeVendorSkipReason(reason, vendor, credentials) {
175
+ return reason.kind === "allocation_failed" || reason.kind === "credential_lookup_failed"
176
+ ? {
177
+ ...reason,
178
+ cause: sanitizeVendorResolutionCause(reason.cause, vendor, credentials),
179
+ }
180
+ : reason;
181
+ }
182
+ function defaultVendorProtocol(vendor) {
183
+ return vendor === "smartproxy" || vendor === "nodemaven"
184
+ ? VENDOR_DEFAULT_PROTOCOL[vendor]
185
+ : "http";
186
+ }
187
+ async function resolveNativeGatewayProxyDetailed(input) {
188
+ if (input.policy.mode === "disabled")
189
+ return { skips: [] };
190
+ const synthesizers = input.gatewaySynthesizers ?? DEFAULT_GATEWAY_SYNTHESIZERS;
191
+ const credentials = input.credentials ?? createEnvVendorCredentialResolver();
192
+ const now = input.now ?? Date.now();
193
+ const skips = [];
194
+ for (const vendor of resolveNativeVendorChain(input.policy)) {
195
+ const protocol = input.protocol ?? defaultVendorProtocol(vendor);
196
+ if (!isProxyProtocol(protocol)) {
197
+ skips.push({ vendor, reason: { kind: "protocol_unsupported", protocol: String(protocol) } });
198
+ continue;
199
+ }
200
+ let vendorSkip;
201
+ for (const synthesize of synthesizers) {
202
+ try {
203
+ const resolved = await synthesize({
204
+ vendor,
205
+ policy: input.policy,
206
+ affinityKey: input.affinityKey,
207
+ now,
208
+ protocol,
209
+ credentials,
210
+ });
211
+ if (!resolved)
212
+ continue;
213
+ if (isSkippedSynthesis(resolved)) {
214
+ vendorSkip = sanitizeVendorSkipReason(resolved.reason, vendor, credentials);
215
+ continue;
216
+ }
217
+ if (resolved.vendor === vendor) {
218
+ assertTunnelingScheme(resolved.url);
219
+ return { proxy: resolved, skips };
220
+ }
221
+ }
222
+ catch (error) {
223
+ vendorSkip = {
224
+ kind: "allocation_failed",
225
+ cause: sanitizeVendorResolutionCause(error, vendor, credentials),
226
+ };
227
+ }
228
+ }
229
+ skips.push({ vendor, reason: vendorSkip ?? { kind: "adapter_unavailable" } });
230
+ }
231
+ return { skips };
232
+ }
233
+ /** Resolve the first configured native gateway, including allocation vendors. */
234
+ export async function resolveNativeGatewayProxy(input) {
235
+ return (await resolveNativeGatewayProxyDetailed(input)).proxy;
236
+ }
237
+ function formatVendorSkip(skip) {
238
+ switch (skip.reason.kind) {
239
+ case "credentials_absent":
240
+ return `${skip.vendor}: credentials absent (missing ${skip.reason.missing.join(", ") || "unspecified variables"})`;
241
+ case "protocol_unsupported":
242
+ return `${skip.vendor}: protocol ${skip.reason.protocol} is unsupported`;
243
+ case "allocation_failed":
244
+ return `${skip.vendor}: allocation failed (${skip.reason.cause.message})`;
245
+ case "credential_lookup_failed":
246
+ return `${skip.vendor}: credential lookup failed (${skip.reason.cause.message})`;
247
+ case "adapter_unavailable":
248
+ return `${skip.vendor}: no native adapter is registered`;
249
+ }
250
+ }
251
+ function proxyRequiredError(policy, skips) {
252
+ const chain = resolveNativeVendorChain(policy).filter((vendor) => vendor === "smartproxy" || vendor === "nodemaven");
253
+ return new ProxyResolutionError("PROXY_REQUIRED", `Native proxy egress is required but the vendor chain was exhausted: ${skips.map(formatVendorSkip).join("; ") || "no vendors declared"}.`, { vendorChain: chain });
254
+ }
255
+ function deadlineFrom(timeoutMs) {
256
+ if (timeoutMs === undefined)
257
+ return undefined;
258
+ return Date.now() + Math.max(0, timeoutMs);
259
+ }
260
+ function remainingMs(deadline) {
261
+ return deadline === undefined ? undefined : Math.max(0, deadline - Date.now());
262
+ }
263
+ function abortError() {
264
+ return new NativeNetworkError("Native connection was aborted", "native_connection_aborted");
265
+ }
266
+ function timeoutError() {
267
+ return new NativeNetworkError("Native connection timed out", "native_connection_timeout");
268
+ }
269
+ function failedError(cause) {
270
+ return new NativeNetworkError("Native connection failed", "native_connection_failed", cause);
271
+ }
272
+ function assertCanStart(signal, deadline) {
273
+ if (signal?.aborted)
274
+ throw abortError();
275
+ if (deadline !== undefined && deadline <= Date.now())
276
+ throw timeoutError();
277
+ }
278
+ async function waitForSocketEvent(socket, event, signal, deadline) {
279
+ assertCanStart(signal, deadline);
280
+ await new Promise((resolve, reject) => {
281
+ let timer;
282
+ const cleanup = () => {
283
+ if (timer)
284
+ clearTimeout(timer);
285
+ socket.off(event, onReady);
286
+ socket.off("error", onError);
287
+ socket.off("close", onClose);
288
+ signal?.removeEventListener("abort", onAbort);
289
+ };
290
+ const finish = (error) => {
291
+ cleanup();
292
+ if (error) {
293
+ socket.on("error", () => undefined);
294
+ socket.destroy();
295
+ reject(error);
296
+ }
297
+ else
298
+ resolve();
299
+ };
300
+ const onReady = () => finish();
301
+ const onError = (cause) => finish(failedError(cause));
302
+ const onClose = () => finish(failedError());
303
+ const onAbort = () => finish(abortError());
304
+ socket.once(event, onReady);
305
+ socket.once("error", onError);
306
+ socket.once("close", onClose);
307
+ signal?.addEventListener("abort", onAbort, { once: true });
308
+ const remaining = remainingMs(deadline);
309
+ if (remaining !== undefined)
310
+ timer = setTimeout(() => finish(timeoutError()), remaining);
311
+ });
312
+ }
313
+ async function connectPlainSocket(host, port, signal, deadline) {
314
+ assertCanStart(signal, deadline);
315
+ const socket = new Socket();
316
+ const ready = waitForSocketEvent(socket, "connect", signal, deadline);
317
+ socket.connect({ host, port });
318
+ await ready;
319
+ return socket;
320
+ }
321
+ function parseSocks5Proxy(proxyUrl) {
322
+ let parsed;
323
+ try {
324
+ parsed = new URL(proxyUrl);
325
+ }
326
+ catch {
327
+ throw new NativeNetworkError("Native proxy URL is invalid", "native_proxy_invalid");
328
+ }
329
+ const port = Number(parsed.port);
330
+ if (parsed.protocol !== "socks5:" || !parsed.hostname || !Number.isInteger(port) || port <= 0) {
331
+ throw new NativeNetworkError("Native proxy URL is invalid", "native_proxy_invalid");
332
+ }
333
+ try {
334
+ return {
335
+ host: parsed.hostname,
336
+ port,
337
+ ...(parsed.username ? { userId: decodeURIComponent(parsed.username) } : {}),
338
+ ...(parsed.password ? { password: decodeURIComponent(parsed.password) } : {}),
339
+ };
340
+ }
341
+ catch {
342
+ throw new NativeNetworkError("Native proxy URL is invalid", "native_proxy_invalid");
343
+ }
344
+ }
345
+ function parseHttpConnectProxy(proxyUrl) {
346
+ let parsed;
347
+ try {
348
+ parsed = new URL(proxyUrl);
349
+ }
350
+ catch {
351
+ throw new NativeNetworkError("Native proxy URL is invalid", "native_proxy_invalid");
352
+ }
353
+ const port = Number(parsed.port || "80");
354
+ if (parsed.protocol !== "http:" ||
355
+ !parsed.hostname ||
356
+ !Number.isInteger(port) ||
357
+ port <= 0 ||
358
+ parsed.pathname !== "/" ||
359
+ parsed.search ||
360
+ parsed.hash) {
361
+ throw new NativeNetworkError("Native proxy URL is invalid", "native_proxy_invalid");
362
+ }
363
+ try {
364
+ return {
365
+ host: parsed.hostname,
366
+ port,
367
+ ...(parsed.username ? { userId: decodeURIComponent(parsed.username) } : {}),
368
+ ...(parsed.password ? { password: decodeURIComponent(parsed.password) } : {}),
369
+ };
370
+ }
371
+ catch {
372
+ throw new NativeNetworkError("Native proxy URL is invalid", "native_proxy_invalid");
373
+ }
374
+ }
375
+ const SOCKS5_REPLY_CODES = {
376
+ Failure: 0x01,
377
+ NotAllowed: 0x02,
378
+ NetworkUnreachable: 0x03,
379
+ HostUnreachable: 0x04,
380
+ ConnectionRefused: 0x05,
381
+ TTLExpired: 0x06,
382
+ CommandNotSupported: 0x07,
383
+ AddressNotSupported: 0x08,
384
+ };
385
+ function socks5ReplyCode(error) {
386
+ const match = /Socks5 proxy rejected connection - ([A-Za-z]+)/i.exec(error.message);
387
+ if (!match?.[1])
388
+ return undefined;
389
+ const reply = Object.entries(SOCKS5_REPLY_CODES).find(([name]) => name.toLowerCase() === match[1]?.toLowerCase());
390
+ return reply?.[1];
391
+ }
392
+ function sanitizeProxyFailureCause(error, proxyUrl, credentials) {
393
+ // socks' SocksClientError retains the live socket in options. It is neither
394
+ // useful diagnostic payload nor serializable, so preserve the original error
395
+ // while replacing only that options object with a socket-free snapshot.
396
+ const options = Reflect.get(error, "options");
397
+ if (options && typeof options === "object" && !Array.isArray(options)) {
398
+ const snapshot = { ...options };
399
+ delete snapshot.existing_socket;
400
+ try {
401
+ Reflect.set(error, "options", snapshot);
402
+ }
403
+ catch {
404
+ // The recursive redactor below clones readonly diagnostics safely.
405
+ }
406
+ }
407
+ const replyCode = socks5ReplyCode(error);
408
+ if (replyCode !== undefined) {
409
+ try {
410
+ Object.defineProperty(error, "socks5ReplyCode", {
411
+ value: replyCode,
412
+ configurable: true,
413
+ enumerable: true,
414
+ writable: false,
415
+ });
416
+ }
417
+ catch {
418
+ // The reply label remains in message if an exotic error is immutable.
419
+ }
420
+ }
421
+ let redactedProxyUrl = proxyUrl;
422
+ try {
423
+ const parsed = new URL(proxyUrl);
424
+ parsed.username = "[REDACTED]";
425
+ parsed.password = "[REDACTED]";
426
+ redactedProxyUrl = parsed.toString();
427
+ }
428
+ catch {
429
+ // parseSocks5Proxy already validated this URL; keep a defensive fallback.
430
+ }
431
+ return redactSensitiveError(error, [
432
+ credentials.userId,
433
+ credentials.password,
434
+ credentials.userId !== undefined || credentials.password !== undefined
435
+ ? `${credentials.userId ?? ""}:${credentials.password ?? ""}`
436
+ : undefined,
437
+ credentials.userId !== undefined || credentials.password !== undefined
438
+ ? Buffer.from(`${credentials.userId ?? ""}:${credentials.password ?? ""}`).toString("base64")
439
+ : undefined,
440
+ ].filter((value) => typeof value === "string" && value.length > 0), proxyUrl, redactedProxyUrl);
441
+ }
442
+ async function waitForSocksHandshake(proxySocket, promise, signal, deadline, sanitizeFailure) {
443
+ assertCanStart(signal, deadline);
444
+ return await new Promise((resolve, reject) => {
445
+ let settled = false;
446
+ let timer;
447
+ const cleanup = () => {
448
+ if (timer)
449
+ clearTimeout(timer);
450
+ signal?.removeEventListener("abort", onAbort);
451
+ };
452
+ const finish = (error, socket) => {
453
+ if (settled)
454
+ return;
455
+ settled = true;
456
+ cleanup();
457
+ if (error) {
458
+ proxySocket.destroy();
459
+ reject(error);
460
+ }
461
+ else if (socket)
462
+ resolve(socket);
463
+ else
464
+ reject(failedError());
465
+ };
466
+ const onAbort = () => finish(abortError());
467
+ signal?.addEventListener("abort", onAbort, { once: true });
468
+ const remaining = remainingMs(deadline);
469
+ if (remaining !== undefined)
470
+ timer = setTimeout(() => finish(timeoutError()), remaining);
471
+ void promise.then((result) => finish(undefined, result.socket), (error) => finish(error instanceof Error && /\b(?:timed out|timeout)\b/i.test(error.message)
472
+ ? timeoutError()
473
+ : failedError(sanitizeFailure(error instanceof Error ? error : new Error(String(error))))));
474
+ });
475
+ }
476
+ async function connectSocksTunnel(proxy, input, deadline, beforeDestinationConnect) {
477
+ const parsed = parseSocks5Proxy(proxy.url);
478
+ const proxySocket = await connectPlainSocket(parsed.host, parsed.port, input.signal, deadline);
479
+ // `socks` removes its internal listeners as the handshake settles. Keep a
480
+ // credential-free sink on the owned socket so an abort/close race cannot emit
481
+ // an unhandled late network error between library cleanup and our wrapper.
482
+ proxySocket.on("error", () => undefined);
483
+ const remaining = remainingMs(deadline);
484
+ try {
485
+ beforeDestinationConnect();
486
+ }
487
+ catch (error) {
488
+ proxySocket.destroy();
489
+ throw error;
490
+ }
491
+ const handshake = SocksClient.createConnection({
492
+ command: "connect",
493
+ destination: { host: input.host, port: input.port },
494
+ proxy: { type: 5, ...parsed },
495
+ existing_socket: proxySocket,
496
+ ...(remaining === undefined ? {} : { timeout: Math.max(1, remaining) }),
497
+ });
498
+ return await waitForSocksHandshake(proxySocket, handshake, input.signal, deadline, (error) => sanitizeProxyFailureCause(error, proxy.url, parsed));
499
+ }
500
+ function connectAuthority(host, port) {
501
+ return `${host.includes(":") && !host.startsWith("[") ? `[${host}]` : host}:${port}`;
502
+ }
503
+ function connectStatusError(statusLine, statusCode) {
504
+ const error = new Error(`HTTP CONNECT proxy rejected tunnel: ${statusLine || "invalid response"}`);
505
+ Object.defineProperties(error, {
506
+ connectStatusLine: {
507
+ value: statusLine || "invalid response",
508
+ configurable: true,
509
+ enumerable: true,
510
+ },
511
+ ...(statusCode === undefined
512
+ ? {}
513
+ : {
514
+ connectStatusCode: {
515
+ value: statusCode,
516
+ configurable: true,
517
+ enumerable: true,
518
+ },
519
+ }),
520
+ });
521
+ return error;
522
+ }
523
+ async function waitForConnectResponse(proxySocket, signal, deadline, sanitizeFailure) {
524
+ assertCanStart(signal, deadline);
525
+ return await new Promise((resolve, reject) => {
526
+ let settled = false;
527
+ let buffered = Buffer.alloc(0);
528
+ let timer;
529
+ const cleanup = () => {
530
+ if (timer)
531
+ clearTimeout(timer);
532
+ proxySocket.off("data", onData);
533
+ proxySocket.off("error", onError);
534
+ proxySocket.off("close", onClose);
535
+ signal?.removeEventListener("abort", onAbort);
536
+ };
537
+ const finish = (error) => {
538
+ if (settled)
539
+ return;
540
+ settled = true;
541
+ cleanup();
542
+ if (error) {
543
+ proxySocket.on("error", () => undefined);
544
+ proxySocket.destroy();
545
+ reject(error);
546
+ }
547
+ else
548
+ resolve(proxySocket);
549
+ };
550
+ const onError = (cause) => finish(failedError(sanitizeFailure(cause)));
551
+ const onClose = () => finish(failedError(sanitizeFailure(new Error("HTTP CONNECT proxy closed before response"))));
552
+ const onAbort = () => finish(abortError());
553
+ const onData = (chunk) => {
554
+ buffered = Buffer.concat([buffered, chunk]);
555
+ if (buffered.length > 64 * 1024) {
556
+ finish(failedError(sanitizeFailure(connectStatusError("response headers too large"))));
557
+ return;
558
+ }
559
+ const headerEnd = buffered.indexOf("\r\n\r\n");
560
+ if (headerEnd < 0)
561
+ return;
562
+ const header = buffered.subarray(0, headerEnd).toString("latin1");
563
+ const statusLine = header.split("\r\n", 1)[0] ?? "";
564
+ const match = /^HTTP\/1\.[01] ([0-9]{3})(?: |$)/.exec(statusLine);
565
+ const statusCode = match?.[1] ? Number(match[1]) : undefined;
566
+ if (statusCode === undefined || statusCode < 200 || statusCode >= 300) {
567
+ finish(failedError(sanitizeFailure(connectStatusError(statusLine, statusCode))));
568
+ return;
569
+ }
570
+ const remaining = buffered.subarray(headerEnd + 4);
571
+ cleanup();
572
+ if (remaining.length > 0)
573
+ proxySocket.unshift(remaining);
574
+ finish();
575
+ };
576
+ proxySocket.on("data", onData);
577
+ proxySocket.once("error", onError);
578
+ proxySocket.once("close", onClose);
579
+ signal?.addEventListener("abort", onAbort, { once: true });
580
+ const remaining = remainingMs(deadline);
581
+ if (remaining !== undefined)
582
+ timer = setTimeout(() => finish(timeoutError()), remaining);
583
+ });
584
+ }
585
+ async function connectHttpTunnel(proxy, input, deadline, beforeDestinationConnect) {
586
+ const parsed = parseHttpConnectProxy(proxy.url);
587
+ const proxySocket = await connectPlainSocket(parsed.host, parsed.port, input.signal, deadline);
588
+ const sanitizeFailure = (error) => sanitizeProxyFailureCause(error, proxy.url, parsed);
589
+ try {
590
+ beforeDestinationConnect();
591
+ }
592
+ catch (error) {
593
+ proxySocket.destroy();
594
+ throw error;
595
+ }
596
+ const authority = connectAuthority(input.host, input.port);
597
+ const authorization = parsed.userId !== undefined || parsed.password !== undefined
598
+ ? `Proxy-Authorization: Basic ${Buffer.from(`${parsed.userId ?? ""}:${parsed.password ?? ""}`).toString("base64")}\r\n`
599
+ : "";
600
+ const response = waitForConnectResponse(proxySocket, input.signal, deadline, sanitizeFailure);
601
+ proxySocket.write(`CONNECT ${authority} HTTP/1.1\r\nHost: ${authority}\r\n${authorization}Proxy-Connection: Keep-Alive\r\n\r\n`);
602
+ return await response;
603
+ }
604
+ async function connectProxyTunnel(proxy, input, deadline, beforeDestinationConnect) {
605
+ assertTunnelingScheme(proxy.url);
606
+ return new URL(proxy.url).protocol === "http:"
607
+ ? await connectHttpTunnel(proxy, input, deadline, beforeDestinationConnect)
608
+ : await connectSocksTunnel(proxy, input, deadline, beforeDestinationConnect);
609
+ }
610
+ async function upgradeTls(socket, input, deadline) {
611
+ assertCanStart(input.signal, deadline);
612
+ const tlsSocket = socket
613
+ ? connectTlsSocket({
614
+ socket,
615
+ servername: input.serverName ?? input.host,
616
+ rejectUnauthorized: input.rejectUnauthorized,
617
+ })
618
+ : connectTlsSocket({
619
+ host: input.host,
620
+ port: input.port,
621
+ servername: input.serverName ?? input.host,
622
+ rejectUnauthorized: input.rejectUnauthorized,
623
+ });
624
+ await waitForSocketEvent(tlsSocket, "secureConnect", input.signal, deadline);
625
+ return tlsSocket;
626
+ }
627
+ export function createNativeNetworkConnection(socket, proxy, options, idleTimeoutMs) {
628
+ const warn = options.warn ?? console.warn;
629
+ let terminalError;
630
+ let closeReason;
631
+ let drainHandler;
632
+ let idleTimer;
633
+ let expiringTimer;
634
+ let hardExpiryTimer;
635
+ let lifecycleSettled = false;
636
+ let settleLifecycle = () => undefined;
637
+ const lifecycleCutoff = new Promise((resolve) => {
638
+ settleLifecycle = resolve;
639
+ });
640
+ const clearIdleTimer = () => {
641
+ if (idleTimer)
642
+ clearTimeout(idleTimer);
643
+ idleTimer = undefined;
644
+ };
645
+ const resetIdleTimer = () => {
646
+ clearIdleTimer();
647
+ if (idleTimeoutMs === undefined || socket.readableEnded || socket.destroyed)
648
+ return;
649
+ idleTimer = setTimeout(() => {
650
+ idleTimer = undefined;
651
+ if (socket.readableEnded || socket.destroyed)
652
+ return;
653
+ closeReason = new NativeIdleTimeoutError();
654
+ socket.destroy(closeReason);
655
+ }, Math.max(0, idleTimeoutMs));
656
+ idleTimer.unref?.();
657
+ };
658
+ const clearLifecycle = () => {
659
+ clearIdleTimer();
660
+ if (lifecycleSettled)
661
+ return;
662
+ lifecycleSettled = true;
663
+ if (expiringTimer)
664
+ clearTimeout(expiringTimer);
665
+ if (hardExpiryTimer)
666
+ clearTimeout(hardExpiryTimer);
667
+ settleLifecycle();
668
+ };
669
+ socket.on("error", (error) => {
670
+ terminalError = error;
671
+ });
672
+ socket.once("end", clearIdleTimer);
673
+ socket.once("close", clearLifecycle);
674
+ resetIdleTimer();
675
+ const expiresAt = proxy?.sticky ? proxy.expiresAt : undefined;
676
+ const leadSeconds = options.proxyPolicy?.session?.drainLeadSeconds;
677
+ const expiresAtMs = expiresAt ? Date.parse(expiresAt) : Number.NaN;
678
+ if (expiresAt &&
679
+ Number.isFinite(expiresAtMs) &&
680
+ typeof leadSeconds === "number" &&
681
+ Number.isFinite(leadSeconds) &&
682
+ leadSeconds >= 0) {
683
+ const event = {
684
+ expiresAt,
685
+ leadSeconds,
686
+ reason: "sticky_expiry",
687
+ };
688
+ const remaining = Math.max(0, expiresAtMs - Date.now());
689
+ const leadDelay = Math.max(0, remaining - leadSeconds * 1_000);
690
+ expiringTimer = setTimeout(() => {
691
+ expiringTimer = undefined;
692
+ const handler = drainHandler;
693
+ if (!handler) {
694
+ warn("Native sticky proxy is expiring without a drain handler");
695
+ return;
696
+ }
697
+ void (async () => {
698
+ try {
699
+ await Promise.race([Promise.resolve(handler(event)), lifecycleCutoff]);
700
+ }
701
+ catch {
702
+ // Drain failures do not bypass the hard-expiry fail-safe.
703
+ }
704
+ })();
705
+ }, leadDelay);
706
+ expiringTimer.unref?.();
707
+ hardExpiryTimer = setTimeout(() => {
708
+ hardExpiryTimer = undefined;
709
+ if (socket.destroyed)
710
+ return;
711
+ closeReason = new NativeProxyExpiredError(expiresAt);
712
+ settleLifecycle();
713
+ socket.destroy();
714
+ }, remaining);
715
+ hardExpiryTimer.unref?.();
716
+ }
717
+ const read = async () => {
718
+ if (closeReason)
719
+ throw closeReason;
720
+ if (terminalError)
721
+ throw failedError(terminalError);
722
+ const chunk = socket.read();
723
+ if (chunk) {
724
+ resetIdleTimer();
725
+ return new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
726
+ }
727
+ if (socket.readableEnded || socket.destroyed)
728
+ return null;
729
+ await new Promise((resolve) => {
730
+ const cleanup = () => {
731
+ socket.off("readable", onReadable);
732
+ socket.off("end", onDone);
733
+ socket.off("close", onDone);
734
+ socket.off("error", onDone);
735
+ };
736
+ const onReadable = () => {
737
+ cleanup();
738
+ resolve();
739
+ };
740
+ const onDone = () => {
741
+ cleanup();
742
+ resolve();
743
+ };
744
+ socket.once("readable", onReadable);
745
+ socket.once("end", onDone);
746
+ socket.once("close", onDone);
747
+ socket.once("error", onDone);
748
+ });
749
+ return await read();
750
+ };
751
+ return {
752
+ get closeReason() {
753
+ return closeReason;
754
+ },
755
+ ...(proxy
756
+ ? {
757
+ proxy: {
758
+ vendor: proxy.vendor,
759
+ sticky: proxy.sticky,
760
+ sessionId: proxy.sessionId,
761
+ expiresAt: proxy.expiresAt,
762
+ },
763
+ }
764
+ : {}),
765
+ read,
766
+ onExpiring: (handler) => {
767
+ drainHandler = handler;
768
+ },
769
+ write: async (data) => {
770
+ if (closeReason)
771
+ throw closeReason;
772
+ if (socket.destroyed) {
773
+ throw new NativeNetworkError("Native connection is closed", "native_connection_closed");
774
+ }
775
+ await new Promise((resolve, reject) => {
776
+ socket.write(data, (error) => {
777
+ if (error)
778
+ reject(failedError(error));
779
+ else
780
+ resolve();
781
+ });
782
+ });
783
+ },
784
+ close: async () => {
785
+ if (socket.closed || socket.destroyed) {
786
+ clearLifecycle();
787
+ return;
788
+ }
789
+ await new Promise((resolve) => {
790
+ socket.once("close", () => resolve());
791
+ socket.destroy();
792
+ });
793
+ },
794
+ };
795
+ }
796
+ async function resolveConnectionProxy(options, input, deadline) {
797
+ const policy = options.proxyPolicy;
798
+ if (!policy || policy.mode === "disabled")
799
+ return undefined;
800
+ const explicitAffinityKey = input.affinityKey ?? options.affinityKey;
801
+ const affinityKey = explicitAffinityKey ??
802
+ (isStickyPolicy(policy) && options.credentialIdentity !== undefined
803
+ ? deriveNativeCredentialAffinityKey(options.credentialIdentity)
804
+ : undefined);
805
+ const resolution = await waitForProxyResolution(resolveNativeGatewayProxyDetailed({
806
+ policy,
807
+ affinityKey,
808
+ protocol: options.proxyProtocol,
809
+ credentials: options.credentials,
810
+ gatewaySynthesizers: options.gatewaySynthesizers,
811
+ }), input.signal, deadline);
812
+ if (!resolution.proxy && policy.mode === "required") {
813
+ throw proxyRequiredError(policy, resolution.skips);
814
+ }
815
+ return resolution.proxy;
816
+ }
817
+ async function waitForProxyResolution(promise, signal, deadline) {
818
+ assertCanStart(signal, deadline);
819
+ return await new Promise((resolve, reject) => {
820
+ let settled = false;
821
+ let timer;
822
+ const cleanup = () => {
823
+ if (timer)
824
+ clearTimeout(timer);
825
+ signal?.removeEventListener("abort", onAbort);
826
+ };
827
+ const finish = (value, error) => {
828
+ if (settled)
829
+ return;
830
+ settled = true;
831
+ cleanup();
832
+ if (error !== undefined)
833
+ reject(error);
834
+ else
835
+ resolve(value);
836
+ };
837
+ const onAbort = () => finish(undefined, abortError());
838
+ signal?.addEventListener("abort", onAbort, { once: true });
839
+ const remaining = remainingMs(deadline);
840
+ if (remaining !== undefined) {
841
+ timer = setTimeout(() => finish(undefined, timeoutError()), remaining);
842
+ }
843
+ void promise.then((value) => finish(value), (error) => finish(undefined, error));
844
+ });
845
+ }
846
+ export const NATIVE_EGRESS_EXPIRED_EVIDENCE_LIMIT = 256;
847
+ function invalidPolicy(message) {
848
+ return new NativeNetworkError(message, "native_egress_policy_invalid");
849
+ }
850
+ function matchesDnsSuffix(host, suffix) {
851
+ return host === suffix || host.endsWith(`.${suffix}`);
852
+ }
853
+ /** Internal validator-independent source selector matcher. */
854
+ export function matchesSourceHost(rule, host) {
855
+ const kind = classifyEgressHost(host);
856
+ if (kind === "numeric-ambiguous")
857
+ return false;
858
+ const hasSelector = rule.sourceHost !== undefined ||
859
+ rule.sourceHostSuffixes.length > 0 ||
860
+ rule.sourceIpv4Cidrs.length > 0 ||
861
+ rule.sourceIpv6Cidrs.length > 0;
862
+ if (!hasSelector)
863
+ return false;
864
+ if (host === rule.sourceHost)
865
+ return true;
866
+ return matchesHostFamilySelectors(host, rule.sourceHostSuffixes, rule.sourceIpv4Cidrs, rule.sourceIpv6Cidrs);
867
+ }
868
+ function matchesHostFamilySelectors(host, hostSuffixes, ipv4Cidrs, ipv6Cidrs) {
869
+ const kind = classifyEgressHost(host);
870
+ if (kind === "ipv4") {
871
+ const address = parseStrictIpv4(host);
872
+ return address !== undefined && ipv4Cidrs.some((cidr) => ipv4InCidr(address, cidr));
873
+ }
874
+ if (kind === "ipv4-mapped-ipv6") {
875
+ const address = parseIpv6(host);
876
+ const embedded = address && embeddedIpv4FromIpv6(address);
877
+ return embedded !== undefined && ipv4Cidrs.some((cidr) => ipv4InCidr(embedded, cidr));
878
+ }
879
+ if (kind === "ipv6") {
880
+ const address = parseIpv6(host);
881
+ return address !== undefined && ipv6Cidrs.some((cidr) => ipv6InCidr(address, cidr));
882
+ }
883
+ return kind === "dns" && hostSuffixes.some((suffix) => matchesDnsSuffix(host, suffix));
884
+ }
885
+ function matchesPortSelectors(port, ports, ranges) {
886
+ if (ports.length === 0 && ranges.length === 0)
887
+ return false;
888
+ return ports.includes(port) || ranges.some(({ start, end }) => port >= start && port <= end);
889
+ }
890
+ function tlsModeAllows(mode, requested) {
891
+ return mode === "allowed" || mode === requested;
892
+ }
893
+ function grantTlsFitsRule(grant, rule) {
894
+ return (rule === "allowed" ||
895
+ (grant === "required" && rule === "required") ||
896
+ (grant === "disabled" && rule === "disabled"));
897
+ }
898
+ function matchesDynamicRuleSelectors(rule, input) {
899
+ return (matchesSourceHost(rule, input.sourceHost) &&
900
+ matchesPortSelectors(input.sourcePort, rule.sourcePorts, rule.sourcePortRanges) &&
901
+ matchesDynamicTargetHost(rule, input.host) &&
902
+ matchesPortSelectors(input.port, rule.targetPorts, rule.targetPortRanges) &&
903
+ grantTlsFitsRule(input.tls, rule.tls));
904
+ }
905
+ function matchesDynamicTargetHost(rule, targetHost) {
906
+ return matchesHostFamilySelectors(targetHost, rule.targetHostSuffixes, rule.targetIpv4Cidrs, rule.targetIpv6Cidrs);
907
+ }
908
+ function invalidGrant(message) {
909
+ return new NativeNetworkError(message, "native_egress_grant_invalid");
910
+ }
911
+ function canonicalGrantHost(value) {
912
+ if (typeof value === "string" && value.includes("*"))
913
+ throw invalidGrant("Native TCP egress grant hosts must be exact non-empty hostnames");
914
+ const canonical = canonicalizeEgressHost(value);
915
+ if (!canonical.ok)
916
+ throw invalidGrant("Native TCP egress grant hosts must be exact non-empty hostnames");
917
+ return canonical.host;
918
+ }
919
+ function assertValidGrantInput(input) {
920
+ canonicalGrantHost(input.sourceHost);
921
+ canonicalGrantHost(input.host);
922
+ if (!Number.isSafeInteger(input.sourcePort) ||
923
+ input.sourcePort < 1 ||
924
+ input.sourcePort > 65_535 ||
925
+ !Number.isSafeInteger(input.port) ||
926
+ input.port < 1 ||
927
+ input.port > 65_535)
928
+ throw invalidGrant("Native TCP egress grant ports must be integers from 1 to 65535");
929
+ if (input.tls !== "required" && input.tls !== "allowed" && input.tls !== "disabled")
930
+ throw invalidGrant("Native TCP egress grant tls must be required, allowed, or disabled");
931
+ if (input.ttlMs !== undefined && (!Number.isSafeInteger(input.ttlMs) || input.ttlMs <= 0))
932
+ throw invalidGrant("Native TCP egress grant ttlMs must be a positive integer");
933
+ }
934
+ function invalidNativeConnectInput(field, reason) {
935
+ return new NativeNetworkError(`Native connection input rejected: field=${field}; reason=${reason}`, "native_egress_input_invalid");
936
+ }
937
+ function inspectNativeConnectInputField(input, field) {
938
+ try {
939
+ return input[field];
940
+ }
941
+ catch {
942
+ throw invalidNativeConnectInput(field, "inspection-failure");
943
+ }
944
+ }
945
+ /** Internal canonical snapshot shared by production and SDK transport test doubles. */
946
+ export function snapshotNativeConnectInput(input) {
947
+ const host = inspectNativeConnectInputField(input, "host");
948
+ const canonicalHost = canonicalizeEgressHost(host);
949
+ if (!canonicalHost.ok)
950
+ throw invalidNativeConnectInput("host", canonicalHost.reason);
951
+ const port = inspectNativeConnectInputField(input, "port");
952
+ if (!Number.isInteger(port) || port < 1 || port > 65_535)
953
+ throw invalidNativeConnectInput("port", "port-range");
954
+ const serverName = inspectNativeConnectInputField(input, "serverName");
955
+ const rejectUnauthorized = inspectNativeConnectInputField(input, "rejectUnauthorized");
956
+ const idleTimeoutMs = inspectNativeConnectInputField(input, "idleTimeoutMs");
957
+ const timeoutMs = inspectNativeConnectInputField(input, "timeoutMs");
958
+ const signal = inspectNativeConnectInputField(input, "signal");
959
+ const affinityKey = inspectNativeConnectInputField(input, "affinityKey");
960
+ return {
961
+ host: canonicalHost.host,
962
+ port,
963
+ ...(serverName === undefined ? {} : { serverName }),
964
+ ...(rejectUnauthorized === undefined ? {} : { rejectUnauthorized }),
965
+ ...(idleTimeoutMs === undefined ? {} : { idleTimeoutMs }),
966
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
967
+ ...(signal === undefined ? {} : { signal }),
968
+ ...(affinityKey === undefined ? {} : { affinityKey }),
969
+ };
970
+ }
971
+ /** Internal canonical snapshot shared by production and SDK transport test doubles. */
972
+ export function snapshotNativeGrantInput(input) {
973
+ let snapshot;
974
+ try {
975
+ const sourceHost = input.sourceHost;
976
+ const sourcePort = input.sourcePort;
977
+ const host = input.host;
978
+ const port = input.port;
979
+ const tls = input.tls;
980
+ const ttlMs = input.ttlMs;
981
+ snapshot = {
982
+ sourceHost,
983
+ sourcePort,
984
+ host,
985
+ port,
986
+ tls,
987
+ ...(ttlMs === undefined ? {} : { ttlMs }),
988
+ };
989
+ }
990
+ catch {
991
+ throw new NativeNetworkError("Native TCP egress grant input could not be inspected safely", "native_egress_input_invalid");
992
+ }
993
+ assertValidGrantInput(snapshot);
994
+ return {
995
+ ...snapshot,
996
+ sourceHost: canonicalGrantHost(snapshot.sourceHost),
997
+ host: canonicalGrantHost(snapshot.host),
998
+ };
999
+ }
1000
+ /** Internal authorization seam shared by production and SDK transport test doubles. */
1001
+ export function createNativeEgressAuthorization(options) {
1002
+ let declared;
1003
+ let staticRules;
1004
+ let dynamicRules;
1005
+ let delegate;
1006
+ try {
1007
+ const policy = options.egress;
1008
+ delegate = options.grantTcpEgress;
1009
+ if (delegate !== undefined && typeof delegate !== "function")
1010
+ throw invalidPolicy("Native egress delegate must be a function");
1011
+ declared = policy !== undefined;
1012
+ const snapshot = declared
1013
+ ? parseNativeEgressPolicy(policy)
1014
+ : { staticRules: [], dynamicRules: [] };
1015
+ staticRules = snapshot.staticRules;
1016
+ dynamicRules = snapshot.dynamicRules;
1017
+ }
1018
+ catch (error) {
1019
+ if (error instanceof NativeNetworkError)
1020
+ throw error;
1021
+ if (error instanceof NativeEgressPolicyValidationError)
1022
+ throw invalidPolicy(error.message);
1023
+ throw invalidPolicy("Native egress policy could not be inspected safely");
1024
+ }
1025
+ const grants = [];
1026
+ const expiredEvidence = new Map();
1027
+ const grantKey = (grant) => `${grant.host}\0${grant.port}\0${grant.tls}`;
1028
+ const recordExpired = (grant) => {
1029
+ const key = grantKey(grant);
1030
+ expiredEvidence.delete(key);
1031
+ expiredEvidence.set(key, grant);
1032
+ while (expiredEvidence.size > NATIVE_EGRESS_EXPIRED_EVIDENCE_LIMIT) {
1033
+ const oldest = expiredEvidence.keys().next().value;
1034
+ if (typeof oldest !== "string")
1035
+ break;
1036
+ expiredEvidence.delete(oldest);
1037
+ }
1038
+ };
1039
+ const purgeInactive = (now) => {
1040
+ const live = [];
1041
+ for (const grant of grants) {
1042
+ if (grant.revoked)
1043
+ continue;
1044
+ if (grant.expiresAtMs !== undefined && now >= grant.expiresAtMs) {
1045
+ recordExpired(grant);
1046
+ continue;
1047
+ }
1048
+ live.push(grant);
1049
+ }
1050
+ grants.length = 0;
1051
+ grants.push(...live);
1052
+ };
1053
+ const assertConnect = (input, tls) => {
1054
+ if (!declared)
1055
+ return;
1056
+ const now = Date.now();
1057
+ purgeInactive(now);
1058
+ if (staticRules.some((rule) => rule.host === input.host &&
1059
+ rule.ports.includes(input.port) &&
1060
+ tlsModeAllows(rule.tls, tls)))
1061
+ return;
1062
+ const matching = grants.filter((grant) => !grant.revoked &&
1063
+ grant.host === input.host &&
1064
+ grant.port === input.port &&
1065
+ tlsModeAllows(grant.tls, tls));
1066
+ if (matching.length > 0)
1067
+ return;
1068
+ const expired = [...expiredEvidence.values()]
1069
+ .filter((grant) => grant.host === input.host && grant.port === input.port && tlsModeAllows(grant.tls, tls))
1070
+ .sort((left, right) => (right.expiresAtMs ?? 0) - (left.expiresAtMs ?? 0))[0];
1071
+ if (expired?.expiresAtMs !== undefined)
1072
+ throw new NativeEgressGrantExpiredError(input.host, input.port, tls, new Date(expired.expiresAtMs).toISOString());
1073
+ throw new NativeEgressNotDeclaredError(input.host, input.port, tls);
1074
+ };
1075
+ const grantLocal = (input) => {
1076
+ assertValidGrantInput(input);
1077
+ const ruleIndex = dynamicRules.findIndex((rule) => matchesDynamicRuleSelectors(rule, input));
1078
+ if (ruleIndex < 0) {
1079
+ const diagnosticSourceHost = safeDiagnosticEgressHost(input.sourceHost);
1080
+ const diagnosticTargetHost = safeDiagnosticEgressHost(input.host);
1081
+ const sourceMatchingRuleIndices = dynamicRules.flatMap((rule, index) => matchesSourceHost(rule, input.sourceHost) &&
1082
+ matchesPortSelectors(input.sourcePort, rule.sourcePorts, rule.sourcePortRanges)
1083
+ ? [index]
1084
+ : []);
1085
+ const targetKind = classifyEgressHost(diagnosticTargetHost);
1086
+ let selectorDetails;
1087
+ if (sourceMatchingRuleIndices.length > 0) {
1088
+ const failedByRule = [];
1089
+ for (const index of sourceMatchingRuleIndices) {
1090
+ const rule = dynamicRules[index];
1091
+ if (!rule)
1092
+ continue;
1093
+ const failedDimensions = [];
1094
+ if (!matchesDynamicTargetHost(rule, input.host))
1095
+ failedDimensions.push("target-host");
1096
+ if (!matchesPortSelectors(input.port, rule.targetPorts, rule.targetPortRanges))
1097
+ failedDimensions.push("target-port");
1098
+ if (!grantTlsFitsRule(input.tls, rule.tls))
1099
+ failedDimensions.push("tls");
1100
+ failedByRule.push(`rule ${index}: ${failedDimensions.join(", ")}`);
1101
+ }
1102
+ selectorDetails = `source-matching rule indices: [${sourceMatchingRuleIndices.join(", ")}]; failed selector dimensions by rule: ${failedByRule.join("; ")}`;
1103
+ }
1104
+ else {
1105
+ const failedByRule = dynamicRules.map((rule, index) => {
1106
+ const failedDimensions = [];
1107
+ if (!matchesSourceHost(rule, input.sourceHost))
1108
+ failedDimensions.push("source-host");
1109
+ if (!matchesPortSelectors(input.sourcePort, rule.sourcePorts, rule.sourcePortRanges))
1110
+ failedDimensions.push("source-port");
1111
+ return `rule ${index}: ${failedDimensions.join(", ")}`;
1112
+ });
1113
+ selectorDetails = `source-matching rule indices: []; failed selector dimensions by rule: ${failedByRule.join("; ")}`;
1114
+ }
1115
+ throw new NativeNetworkError(`Native TCP egress grant is not declared for source ${diagnosticSourceHost}:${input.sourcePort} to target ${diagnosticTargetHost}:${input.port} (${input.tls}); target kind: ${targetKind}; ${selectorDetails}`, "native_egress_not_declared");
1116
+ }
1117
+ const rule = dynamicRules[ruleIndex];
1118
+ if (!rule)
1119
+ throw invalidGrant("Native TCP egress declaration is missing its matched rule");
1120
+ if (input.ttlMs !== undefined && rule.ttlMs !== undefined && input.ttlMs > rule.ttlMs)
1121
+ throw invalidGrant(`Native TCP egress grant ttlMs ${input.ttlMs} exceeds declared maximum ${rule.ttlMs}`);
1122
+ const now = Date.now();
1123
+ purgeInactive(now);
1124
+ const activeForRule = grants.filter((grant) => grant.ruleIndex === ruleIndex &&
1125
+ !grant.revoked &&
1126
+ (grant.expiresAtMs === undefined || now < grant.expiresAtMs)).length;
1127
+ if (rule.maxGrants !== undefined && activeForRule >= rule.maxGrants)
1128
+ throw new NativeNetworkError(`Native TCP egress grant limit exceeded for declaration ${ruleIndex}`, "native_egress_grant_limit_exceeded");
1129
+ const ttlMs = input.ttlMs ?? rule.ttlMs;
1130
+ const expiresAtMs = ttlMs === undefined ? undefined : now + ttlMs;
1131
+ if (expiresAtMs !== undefined && (!Number.isFinite(expiresAtMs) || expiresAtMs > 8.64e15))
1132
+ throw invalidGrant("Native TCP egress grant expiry exceeds the supported date range");
1133
+ const stored = {
1134
+ ruleIndex,
1135
+ host: input.host,
1136
+ port: input.port,
1137
+ tls: input.tls,
1138
+ ...(expiresAtMs === undefined ? {} : { expiresAtMs }),
1139
+ revoked: false,
1140
+ };
1141
+ expiredEvidence.delete(grantKey(stored));
1142
+ grants.push(stored);
1143
+ return {
1144
+ revoke() {
1145
+ stored.revoked = true;
1146
+ },
1147
+ };
1148
+ };
1149
+ return {
1150
+ assertConnect,
1151
+ grant(input) {
1152
+ if (!declared) {
1153
+ if (delegate) {
1154
+ try {
1155
+ const delegated = delegate(input);
1156
+ if (!delegated || typeof delegated.revoke !== "function")
1157
+ throw new TypeError("Native egress delegate returned an invalid grant");
1158
+ return delegated;
1159
+ }
1160
+ catch (error) {
1161
+ if (error instanceof NativeNetworkError)
1162
+ throw error;
1163
+ throw new NativeNetworkError("Deployment native egress authorization failed", "native_egress_authorization_failed");
1164
+ }
1165
+ }
1166
+ throw new NativeNetworkError("Dynamic native egress authorization is not configured", "native_dynamic_egress_unsupported");
1167
+ }
1168
+ const local = grantLocal(input);
1169
+ let delegated;
1170
+ try {
1171
+ delegated = delegate?.(Object.freeze({ ...input }));
1172
+ if (delegated !== undefined && typeof delegated.revoke !== "function")
1173
+ throw new TypeError("Native egress delegate returned an invalid grant");
1174
+ }
1175
+ catch (error) {
1176
+ local.revoke();
1177
+ if (error instanceof NativeNetworkError)
1178
+ throw error;
1179
+ throw new NativeNetworkError("Deployment native egress authorization failed", "native_egress_authorization_failed");
1180
+ }
1181
+ let revoked = false;
1182
+ return {
1183
+ revoke() {
1184
+ if (revoked)
1185
+ return;
1186
+ revoked = true;
1187
+ local.revoke();
1188
+ try {
1189
+ delegated?.revoke();
1190
+ }
1191
+ catch (error) {
1192
+ if (error instanceof NativeNetworkError)
1193
+ throw error;
1194
+ throw new NativeNetworkError("Deployment native egress grant revocation failed", "native_egress_authorization_failed");
1195
+ }
1196
+ },
1197
+ };
1198
+ },
1199
+ };
1200
+ }
1201
+ /** Create the SDK byte-stream runtime with provider-declared egress enforcement. */
1202
+ export function createNativeNetworkClient(options = {}) {
1203
+ const egress = createNativeEgressAuthorization(options);
1204
+ return {
1205
+ connectTcp: async (input) => {
1206
+ const request = snapshotNativeConnectInput(input);
1207
+ egress.assertConnect(request, "disabled");
1208
+ const deadline = deadlineFrom(request.timeoutMs);
1209
+ assertCanStart(request.signal, deadline);
1210
+ const proxy = await resolveConnectionProxy(options, request, deadline);
1211
+ egress.assertConnect(request, "disabled");
1212
+ const socket = proxy
1213
+ ? await connectProxyTunnel(proxy, request, deadline, () => egress.assertConnect(request, "disabled"))
1214
+ : await connectPlainSocket(request.host, request.port, request.signal, deadline);
1215
+ return createNativeNetworkConnection(socket, proxy, options, request.idleTimeoutMs);
1216
+ },
1217
+ connectTls: async (input) => {
1218
+ const request = snapshotNativeConnectInput(input);
1219
+ egress.assertConnect(request, "required");
1220
+ const deadline = deadlineFrom(request.timeoutMs);
1221
+ assertCanStart(request.signal, deadline);
1222
+ const proxy = await resolveConnectionProxy(options, request, deadline);
1223
+ egress.assertConnect(request, "required");
1224
+ const tunnel = proxy
1225
+ ? await connectProxyTunnel(proxy, request, deadline, () => egress.assertConnect(request, "required"))
1226
+ : undefined;
1227
+ const socket = await upgradeTls(tunnel, request, deadline);
1228
+ return createNativeNetworkConnection(socket, proxy, options, request.idleTimeoutMs);
1229
+ },
1230
+ grantTcpEgress: (input) => egress.grant(snapshotNativeGrantInput(input)),
1231
+ };
1232
+ }