@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
@@ -0,0 +1,707 @@
1
+ /**
2
+ * Runtime boundary lint: provider runtime source must reach the outside world
3
+ * through the SDK context (`ctx.http`, `ctx.stealth`, `ctx.env`,
4
+ * `ctx.credential`, `ctx.browser`), not through ambient Node/Bun globals.
5
+ *
6
+ * Three rules, reported at `error` level: a finding fails `apifuse check` and
7
+ * `apifuse submit-check` (official and standalone mode alike). The first
8
+ * release reported them at `warn` as a migration window; the window closed once
9
+ * the fleet's request-path hits were fixed or acknowledged. A deliberate
10
+ * exception is acknowledged in code (see below), never through a hidden
11
+ * allowlist.
12
+ *
13
+ * - `process-env-direct-read`: `process.env.X`, `process.env["X"]`, a bare
14
+ * `process.env` object use, and the `Bun.env` equivalents. Runtime bootstrap
15
+ * keys (`APIFUSE__RUNTIME__*`) are exempt everywhere because `serve()`
16
+ * callers read the port and pod identity before a context exists.
17
+ * - `node-runtime-module-import`: value imports of `fs`, `fs/promises`, `net`,
18
+ * `tls`, `dgram`, `http`, `https`, `http2`, `child_process` (with or without
19
+ * the `node:` prefix) via import/export declarations, `require()`, or dynamic
20
+ * `import()`, plus the Bun equivalents `Bun.spawn`, `Bun.spawnSync`, `Bun.$`,
21
+ * `Bun.file`, `Bun.write`. Type-only imports are not runtime reach and are
22
+ * ignored.
23
+ * - `direct-fetch-call`: a call to the global `fetch` (bare identifier, or
24
+ * `globalThis`/`window`/`self`/`global` member). A bare `fetch` call whose
25
+ * enclosing scope declares a `fetch` binding (parameter, variable, import,
26
+ * function, catch clause) is calling that binding, not the global, and is
27
+ * left alone; the check is lexical, so a wrapper that takes `fetch` as a
28
+ * parameter does not hide a global `fetch()` elsewhere in the same file.
29
+ * `ctx.stealth.fetch()` is a member call and never matches.
30
+ *
31
+ * Files are parsed with the script kind their extension implies (`.ts` as
32
+ * TypeScript, `.tsx` as TSX, …). Parsing everything as TSX would make a
33
+ * generic arrow (`<T>(value: T) => value`) or an angle-bracket assertion in an
34
+ * ordinary `.ts` file read as JSX, and everything after it would silently drop
35
+ * out of the AST.
36
+ *
37
+ * Scope is the provider *runtime* source: JavaScript/TypeScript files under
38
+ * `providerSourceFiles` minus tests, recorded fixtures, the root bootstrap
39
+ * entrypoints (`dev.ts`, `start.ts`, `deploy.ts`) and the operator tooling
40
+ * directories (`scripts/`, `tools/`, `bin/`). Tooling legitimately reads the
41
+ * ambient environment and the filesystem when a human runs it; the rule is
42
+ * about code the pod executes on behalf of a request.
43
+ *
44
+ * A deliberate exception is acknowledged with the same escape hatch the submit
45
+ * check uses: `// @apifuse-allow <rule>: <reason>` on the finding line or the
46
+ * line directly above it. Acknowledged findings are reported as information so
47
+ * `apifuse check` still lists them.
48
+ */
49
+
50
+ import type { LintDiagnostic, ProviderLintInformation, ProviderLintResult } from "./lint.js";
51
+ import {
52
+ getTypeScript,
53
+ isTypeScriptCompilerModule,
54
+ type TypeScriptCompilerModuleLike,
55
+ type TypeScriptModule,
56
+ } from "./typescript-module.js";
57
+
58
+ type TsNode = import("typescript").Node;
59
+ type TsSourceFile = import("typescript").SourceFile;
60
+
61
+ export const PROCESS_ENV_DIRECT_READ_RULE = "process-env-direct-read";
62
+ export const NODE_RUNTIME_MODULE_IMPORT_RULE = "node-runtime-module-import";
63
+ export const DIRECT_FETCH_CALL_RULE = "direct-fetch-call";
64
+
65
+ export const RUNTIME_BOUNDARY_RULES = [
66
+ PROCESS_ENV_DIRECT_READ_RULE,
67
+ NODE_RUNTIME_MODULE_IMPORT_RULE,
68
+ DIRECT_FETCH_CALL_RULE,
69
+ ] as const;
70
+
71
+ export type RuntimeBoundaryRule = (typeof RUNTIME_BOUNDARY_RULES)[number];
72
+
73
+ /**
74
+ * Environment names a provider may read from `process.env` directly. These
75
+ * are consumed before `serve()` builds a context (port, pod identity), so
76
+ * `ctx.env` cannot carry them. The whole `APIFUSE__RUNTIME__` family is the
77
+ * bootstrap contract; nothing else is exempt.
78
+ */
79
+ export const BOOTSTRAP_ENV_NAME_PREFIX = "APIFUSE__RUNTIME__";
80
+
81
+ export function isBootstrapEnvName(name: string): boolean {
82
+ return name.startsWith(BOOTSTRAP_ENV_NAME_PREFIX);
83
+ }
84
+
85
+ /** Root entrypoints `serve()`/`startDevServer()` are called from; never request-path code. */
86
+ const BOOTSTRAP_ENTRYPOINT_FILE_PATTERN = /^(?:dev|start|deploy)\.[cm]?[jt]sx?$/;
87
+ /** Operator tooling directories at the provider root (fixture recorders, smoke scripts). */
88
+ const TOOLING_DIRECTORY_PATTERN = /^(?:scripts|tools|bin)\//;
89
+ export const TEST_SOURCE_FILE_PATTERN =
90
+ /(?:^|\/)(?:__tests__|__mocks__|tests)\/|\.(?:test|spec)\.[cm]?[jt]sx?$/;
91
+ const RECORDED_FIXTURE_SOURCE_FILE_PATTERN =
92
+ /(?:^|\/)__fixtures__(?:\/|$)|(?:^|\/)__tests__\/fixtures(?:\/|$)/;
93
+ const JAVASCRIPT_SOURCE_FILE_PATTERN = /\.[cm]?[jt]sx?$/;
94
+ const DECLARATION_FILE_PATTERN = /\.d\.[cm]?ts$/;
95
+
96
+ /**
97
+ * Node built-ins whose value import means the provider talks to the
98
+ * filesystem, the network, or child processes on its own. Deliberately not the
99
+ * whole `node:` namespace: `node:path`, `node:crypto`, `node:url`,
100
+ * `node:buffer` and friends are pure helpers the SDK itself uses.
101
+ */
102
+ const FORBIDDEN_NODE_MODULES: ReadonlySet<string> = new Set([
103
+ "fs",
104
+ "fs/promises",
105
+ "net",
106
+ "tls",
107
+ "dgram",
108
+ "http",
109
+ "https",
110
+ "http2",
111
+ "child_process",
112
+ ]);
113
+
114
+ const FORBIDDEN_BUN_MEMBERS: ReadonlySet<string> = new Set([
115
+ "spawn",
116
+ "spawnSync",
117
+ "$",
118
+ "file",
119
+ "write",
120
+ ]);
121
+
122
+ const GLOBAL_OBJECT_NAMES: ReadonlySet<string> = new Set([
123
+ "globalThis",
124
+ "window",
125
+ "self",
126
+ "global",
127
+ ]);
128
+
129
+ const COMMENT_ONLY_LINE_PATTERN = /^\s*(?:\/\/|\/\*|\*)/;
130
+ const ALLOW_COMMENT_PATTERN =
131
+ /@apifuse-allow\s+([a-z][a-z0-9-]*)\b(?:\s*:\s*(.*?))?\s*(?:\*\/)?\s*$/;
132
+
133
+ type RuntimeBoundaryFinding = {
134
+ rule: RuntimeBoundaryRule;
135
+ /** 1-based line. */
136
+ line: number;
137
+ /** What was reached: the env name, module specifier, or call text. */
138
+ subject: string;
139
+ };
140
+
141
+ type RuntimeBoundaryFileReport = {
142
+ findings: RuntimeBoundaryFinding[];
143
+ acknowledged: Array<RuntimeBoundaryFinding & { reason: string }>;
144
+ };
145
+
146
+ /** True for the files the runtime boundary rules look at. */
147
+ export function isRuntimeBoundarySourceFile(relativePath: string): boolean {
148
+ const normalized = relativePath.replaceAll("\\", "/").replace(/^\.\//, "");
149
+ return (
150
+ JAVASCRIPT_SOURCE_FILE_PATTERN.test(normalized) &&
151
+ !DECLARATION_FILE_PATTERN.test(normalized) &&
152
+ !TEST_SOURCE_FILE_PATTERN.test(normalized) &&
153
+ !RECORDED_FIXTURE_SOURCE_FILE_PATTERN.test(normalized) &&
154
+ !BOOTSTRAP_ENTRYPOINT_FILE_PATTERN.test(normalized) &&
155
+ !TOOLING_DIRECTORY_PATTERN.test(normalized)
156
+ );
157
+ }
158
+
159
+ function normalizeModuleSpecifier(specifier: string): string {
160
+ return specifier.startsWith("node:") ? specifier.slice("node:".length) : specifier;
161
+ }
162
+
163
+ function isForbiddenNodeModule(specifier: string): boolean {
164
+ return FORBIDDEN_NODE_MODULES.has(normalizeModuleSpecifier(specifier));
165
+ }
166
+
167
+ function staticString(ts: TypeScriptModule, node: TsNode | undefined): string | undefined {
168
+ if (!node) return undefined;
169
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
170
+ return undefined;
171
+ }
172
+
173
+ function unwrapExpression(
174
+ ts: TypeScriptModule,
175
+ expression: import("typescript").Expression,
176
+ ): import("typescript").Expression {
177
+ let current = expression;
178
+ while (
179
+ ts.isParenthesizedExpression(current) ||
180
+ ts.isAsExpression(current) ||
181
+ ts.isSatisfiesExpression(current) ||
182
+ ts.isNonNullExpression(current) ||
183
+ ts.isTypeAssertionExpression(current)
184
+ ) {
185
+ current = current.expression;
186
+ }
187
+ return current;
188
+ }
189
+
190
+ /** `process` / `globalThis.process` / `Bun` / `globalThis.Bun` */
191
+ function isGlobalReference(ts: TypeScriptModule, node: TsNode, name: string): boolean {
192
+ if (ts.isIdentifier(node)) return node.text === name;
193
+ if (ts.isPropertyAccessExpression(node)) {
194
+ return (
195
+ ts.isIdentifier(node.expression) &&
196
+ GLOBAL_OBJECT_NAMES.has(node.expression.text) &&
197
+ node.name.text === name
198
+ );
199
+ }
200
+ if (ts.isElementAccessExpression(node)) {
201
+ return (
202
+ ts.isIdentifier(node.expression) &&
203
+ GLOBAL_OBJECT_NAMES.has(node.expression.text) &&
204
+ staticString(ts, node.argumentExpression) === name
205
+ );
206
+ }
207
+ return false;
208
+ }
209
+
210
+ /** `process.env`, `process["env"]`, `Bun.env`, and their globalThis-prefixed forms. */
211
+ function envObjectOwner(ts: TypeScriptModule, node: TsNode): "process" | "Bun" | undefined {
212
+ let owner: TsNode | undefined;
213
+ let member: string | undefined;
214
+ if (ts.isPropertyAccessExpression(node)) {
215
+ owner = node.expression;
216
+ member = node.name.text;
217
+ } else if (ts.isElementAccessExpression(node)) {
218
+ owner = node.expression;
219
+ member = staticString(ts, node.argumentExpression);
220
+ }
221
+ if (!owner || member !== "env") return undefined;
222
+ const unwrapped = unwrapExpression(ts, owner as import("typescript").Expression);
223
+ if (isGlobalReference(ts, unwrapped, "process")) return "process";
224
+ if (isGlobalReference(ts, unwrapped, "Bun")) return "Bun";
225
+ return undefined;
226
+ }
227
+
228
+ /**
229
+ * For an env-object node, the statically known key read from it
230
+ * (`process.env.KEY`, `process.env["KEY"]`), or `undefined` when the object
231
+ * itself is used (spread, passed along, dynamic key).
232
+ */
233
+ function envKeyReadFrom(ts: TypeScriptModule, envNode: TsNode): string | undefined {
234
+ const parent = envNode.parent;
235
+ if (!parent) return undefined;
236
+ if (ts.isPropertyAccessExpression(parent) && parent.expression === envNode) {
237
+ return parent.name.text;
238
+ }
239
+ if (ts.isElementAccessExpression(parent) && parent.expression === envNode) {
240
+ return staticString(ts, parent.argumentExpression);
241
+ }
242
+ return undefined;
243
+ }
244
+
245
+ /**
246
+ * True when the import statement still loads the module at runtime. A
247
+ * clause-level `import type`, and a named import whose every binding is
248
+ * inline `type`, are erased by Bun and by tsc (without
249
+ * `verbatimModuleSyntax`), so they never reach the module. A side-effect
250
+ * import (`import "node:fs"`), a default or namespace binding, or any
251
+ * value-level named binding does load it.
252
+ */
253
+ function importDeclarationLoadsModule(
254
+ ts: TypeScriptModule,
255
+ node: import("typescript").ImportDeclaration,
256
+ ): boolean {
257
+ const clause = node.importClause;
258
+ if (!clause) return true;
259
+ if (clause.isTypeOnly) return false;
260
+ if (clause.name) return true;
261
+ const bindings = clause.namedBindings;
262
+ if (!bindings) return true;
263
+ if (ts.isNamespaceImport(bindings)) return true;
264
+ return bindings.elements.some((element) => !element.isTypeOnly);
265
+ }
266
+
267
+ /** Same rule for `export … from`: erased when type-only at the clause or on every specifier. */
268
+ function exportDeclarationLoadsModule(
269
+ ts: TypeScriptModule,
270
+ node: import("typescript").ExportDeclaration,
271
+ ): boolean {
272
+ if (node.isTypeOnly) return false;
273
+ const clause = node.exportClause;
274
+ if (!clause) return true;
275
+ if (ts.isNamespaceExport(clause)) return true;
276
+ return clause.elements.some((element) => !element.isTypeOnly);
277
+ }
278
+
279
+ /**
280
+ * Script kind for a provider source file, from its extension. `.tsx`/`.jsx`
281
+ * enable JSX; everything else (`.ts`, `.mts`, `.cts`, `.js`, `.mjs`, `.cjs`,
282
+ * and the synthetic names used for in-memory sources) does not, so a generic
283
+ * arrow or `<T>value` assertion in a `.ts` file parses as TypeScript instead
284
+ * of swallowing the rest of the file as unterminated JSX.
285
+ */
286
+ export function scriptKindForSourceFile(
287
+ ts: TypeScriptModule,
288
+ fileName: string,
289
+ ): import("typescript").ScriptKind {
290
+ const lower = fileName.toLowerCase();
291
+ if (lower.endsWith(".tsx")) return ts.ScriptKind.TSX;
292
+ if (lower.endsWith(".jsx")) return ts.ScriptKind.JSX;
293
+ if (/\.[cm]?js$/.test(lower)) return ts.ScriptKind.JS;
294
+ return ts.ScriptKind.TS;
295
+ }
296
+
297
+ /** Scope node → value bindings declared directly in that scope. */
298
+ type ScopeBindings = Map<TsNode, Set<string>>;
299
+
300
+ /** Containers that own `let`/`const`/`class`/`function` declarations. */
301
+ function isBlockScopeContainer(ts: TypeScriptModule, node: TsNode): boolean {
302
+ return (
303
+ ts.isSourceFile(node) ||
304
+ ts.isBlock(node) ||
305
+ ts.isModuleBlock(node) ||
306
+ ts.isCaseBlock(node) ||
307
+ ts.isForStatement(node) ||
308
+ ts.isForInStatement(node) ||
309
+ ts.isForOfStatement(node)
310
+ );
311
+ }
312
+
313
+ /** Containers that own `var` declarations (hoisted past blocks). */
314
+ function isFunctionScopeContainer(ts: TypeScriptModule, node: TsNode): boolean {
315
+ return (
316
+ ts.isSourceFile(node) ||
317
+ ts.isModuleBlock(node) ||
318
+ ts.isFunctionLike(node) ||
319
+ ts.isClassStaticBlockDeclaration(node)
320
+ );
321
+ }
322
+
323
+ function nearestAncestor(
324
+ node: TsNode,
325
+ sourceFile: TsSourceFile,
326
+ predicate: (candidate: TsNode) => boolean,
327
+ ): TsNode {
328
+ let current: TsNode | undefined = node.parent;
329
+ while (current && !predicate(current)) current = current.parent;
330
+ return current ?? sourceFile;
331
+ }
332
+
333
+ /**
334
+ * Value bindings per lexical scope: parameters on their function, `let`/`const`
335
+ * and named function/class declarations on the enclosing block, `var` on the
336
+ * enclosing function body, imports on the file, a catch variable on its
337
+ * clause, a named function/class expression on itself. Destructuring patterns
338
+ * contribute every leaf name.
339
+ */
340
+ function collectScopeBindings(ts: TypeScriptModule, sourceFile: TsSourceFile): ScopeBindings {
341
+ const bindings: ScopeBindings = new Map();
342
+ const bind = (scope: TsNode, name: string) => {
343
+ const names = bindings.get(scope) ?? new Set<string>();
344
+ names.add(name);
345
+ bindings.set(scope, names);
346
+ };
347
+ const bindPattern = (scope: TsNode, name: import("typescript").BindingName) => {
348
+ if (ts.isIdentifier(name)) {
349
+ bind(scope, name.text);
350
+ return;
351
+ }
352
+ for (const element of name.elements) {
353
+ if (ts.isBindingElement(element)) bindPattern(scope, element.name);
354
+ }
355
+ };
356
+ const blockScopeOf = (node: TsNode) => {
357
+ const container = nearestAncestor(node, sourceFile, (candidate) =>
358
+ isBlockScopeContainer(ts, candidate),
359
+ );
360
+ // A for-in/for-of binding is visible in the loop body only; the iterable
361
+ // expression is evaluated before it exists (`for (const fetch of
362
+ // fetch(url))` reaches past the loop binding).
363
+ if (ts.isForInStatement(container) || ts.isForOfStatement(container)) {
364
+ return container.statement;
365
+ }
366
+ return container;
367
+ };
368
+ const functionScopeOf = (node: TsNode) => {
369
+ const container = nearestAncestor(node, sourceFile, (candidate) =>
370
+ isFunctionScopeContainer(ts, candidate),
371
+ );
372
+ // `var` hoists to the function *body*, which parameter initializers cannot
373
+ // see: `function load(result = fetch(url)) { var fetch = transport; }`
374
+ // still calls the global in the initializer.
375
+ const body = (container as { body?: TsNode }).body;
376
+ if (ts.isFunctionLike(container) && body && ts.isBlock(body)) return body;
377
+ return container;
378
+ };
379
+
380
+ const visit = (node: TsNode) => {
381
+ if (ts.isVariableDeclaration(node)) {
382
+ if (ts.isCatchClause(node.parent)) {
383
+ bindPattern(node.parent, node.name);
384
+ } else if (ts.getCombinedNodeFlags(node) & ts.NodeFlags.BlockScoped) {
385
+ bindPattern(blockScopeOf(node.parent), node.name);
386
+ } else {
387
+ bindPattern(functionScopeOf(node.parent), node.name);
388
+ }
389
+ } else if (ts.isParameter(node)) {
390
+ bindPattern(node.parent, node.name);
391
+ } else if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) {
392
+ if (node.name) bind(blockScopeOf(node), node.name.text);
393
+ } else if (ts.isFunctionExpression(node) || ts.isClassExpression(node)) {
394
+ if (node.name) bind(node, node.name.text);
395
+ } else if (ts.isEnumDeclaration(node)) {
396
+ bind(blockScopeOf(node), node.name.text);
397
+ } else if (ts.isModuleDeclaration(node) && ts.isIdentifier(node.name)) {
398
+ bind(blockScopeOf(node), node.name.text);
399
+ } else if (ts.isImportClause(node) || ts.isImportSpecifier(node)) {
400
+ if (node.name) bind(sourceFile, node.name.text);
401
+ } else if (ts.isNamespaceImport(node) || ts.isImportEqualsDeclaration(node)) {
402
+ bind(sourceFile, node.name.text);
403
+ }
404
+ ts.forEachChild(node, visit);
405
+ };
406
+ visit(sourceFile);
407
+ return bindings;
408
+ }
409
+
410
+ /** True when a scope enclosing `node` (or `node` itself) declares `name`. */
411
+ function isBindingInScope(bindings: ScopeBindings, node: TsNode, name: string): boolean {
412
+ let current: TsNode | undefined = node;
413
+ while (current) {
414
+ if (bindings.get(current)?.has(name)) return true;
415
+ current = current.parent;
416
+ }
417
+ return false;
418
+ }
419
+
420
+ function readAllowComment(
421
+ lines: readonly string[],
422
+ line: number,
423
+ rule: RuntimeBoundaryRule,
424
+ ): string | undefined {
425
+ const current = lines[line - 1];
426
+ const previous = lines[line - 2];
427
+ // A trailing comment on the finding line, or a comment-only line directly
428
+ // above it. A trailing acknowledgement on the previous line belongs to that
429
+ // line's finding and must not leak onto this one.
430
+ const candidates = [current, previous?.match(COMMENT_ONLY_LINE_PATTERN) ? previous : undefined];
431
+ for (const candidate of candidates) {
432
+ if (candidate === undefined) continue;
433
+ const match = ALLOW_COMMENT_PATTERN.exec(candidate);
434
+ if (match && match[1] === rule) {
435
+ return match[2]?.trim() || "(no reason given)";
436
+ }
437
+ }
438
+ return undefined;
439
+ }
440
+
441
+ export function analyzeRuntimeBoundary(
442
+ ts: TypeScriptModule,
443
+ fileName: string,
444
+ source: string,
445
+ ): RuntimeBoundaryFileReport {
446
+ const sourceFile = ts.createSourceFile(
447
+ fileName,
448
+ source,
449
+ ts.ScriptTarget.Latest,
450
+ true,
451
+ scriptKindForSourceFile(ts, fileName),
452
+ );
453
+ const lines = source.split(/\r?\n/);
454
+ const scopeBindings = collectScopeBindings(ts, sourceFile);
455
+ const raw: RuntimeBoundaryFinding[] = [];
456
+ const seen = new Set<string>();
457
+
458
+ const record = (rule: RuntimeBoundaryRule, node: TsNode, subject: string) => {
459
+ const line = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
460
+ const key = `${rule}:${line}:${subject}`;
461
+ if (seen.has(key)) return;
462
+ seen.add(key);
463
+ raw.push({ rule, line, subject });
464
+ };
465
+
466
+ const visit = (node: TsNode) => {
467
+ // --- process.env / Bun.env ---------------------------------------
468
+ const envOwner = envObjectOwner(ts, node);
469
+ if (envOwner) {
470
+ const key = envKeyReadFrom(ts, node);
471
+ if (key === undefined || !isBootstrapEnvName(key)) {
472
+ record(
473
+ PROCESS_ENV_DIRECT_READ_RULE,
474
+ node,
475
+ key === undefined ? `${envOwner}.env` : `${envOwner}.env.${key}`,
476
+ );
477
+ }
478
+ // Do not descend: the owner is a global, nothing below is interesting.
479
+ return;
480
+ }
481
+
482
+ // --- node:fs / node:net / node:child_process … ---------------------
483
+ if (ts.isImportDeclaration(node)) {
484
+ const specifier = staticString(ts, node.moduleSpecifier);
485
+ if (specifier && importDeclarationLoadsModule(ts, node) && isForbiddenNodeModule(specifier)) {
486
+ record(NODE_RUNTIME_MODULE_IMPORT_RULE, node, specifier);
487
+ }
488
+ return;
489
+ }
490
+ if (ts.isExportDeclaration(node)) {
491
+ const specifier = staticString(ts, node.moduleSpecifier);
492
+ if (specifier && exportDeclarationLoadsModule(ts, node) && isForbiddenNodeModule(specifier)) {
493
+ record(NODE_RUNTIME_MODULE_IMPORT_RULE, node, specifier);
494
+ }
495
+ return;
496
+ }
497
+ if (ts.isImportEqualsDeclaration(node) && !node.isTypeOnly) {
498
+ const reference = node.moduleReference;
499
+ if (ts.isExternalModuleReference(reference)) {
500
+ const specifier = staticString(ts, reference.expression);
501
+ if (specifier && isForbiddenNodeModule(specifier)) {
502
+ record(NODE_RUNTIME_MODULE_IMPORT_RULE, node, specifier);
503
+ }
504
+ }
505
+ return;
506
+ }
507
+ if (ts.isCallExpression(node)) {
508
+ const callee = unwrapExpression(ts, node.expression);
509
+ // require("node:fs") / import("node:fs")
510
+ if (
511
+ (ts.isIdentifier(callee) && callee.text === "require") ||
512
+ callee.kind === ts.SyntaxKind.ImportKeyword
513
+ ) {
514
+ const specifier = staticString(ts, node.arguments[0]);
515
+ if (specifier && isForbiddenNodeModule(specifier)) {
516
+ record(NODE_RUNTIME_MODULE_IMPORT_RULE, node, specifier);
517
+ }
518
+ }
519
+ // Bun.spawn(...) / Bun.file(...) / Bun.write(...) / Bun.$(...)
520
+ if (ts.isPropertyAccessExpression(callee) || ts.isElementAccessExpression(callee)) {
521
+ const owner = unwrapExpression(ts, callee.expression);
522
+ const member = ts.isPropertyAccessExpression(callee)
523
+ ? callee.name.text
524
+ : staticString(ts, callee.argumentExpression);
525
+ if (
526
+ member !== undefined &&
527
+ FORBIDDEN_BUN_MEMBERS.has(member) &&
528
+ isGlobalReference(ts, owner, "Bun")
529
+ ) {
530
+ record(NODE_RUNTIME_MODULE_IMPORT_RULE, node, `Bun.${member}()`);
531
+ }
532
+ }
533
+ // fetch(...) / globalThis.fetch(...)
534
+ if (
535
+ ts.isIdentifier(callee) &&
536
+ callee.text === "fetch" &&
537
+ !isBindingInScope(scopeBindings, node, "fetch")
538
+ ) {
539
+ record(DIRECT_FETCH_CALL_RULE, node, "fetch()");
540
+ } else if (
541
+ (ts.isPropertyAccessExpression(callee) || ts.isElementAccessExpression(callee)) &&
542
+ isGlobalReference(ts, callee, "fetch")
543
+ ) {
544
+ record(DIRECT_FETCH_CALL_RULE, node, "globalThis.fetch()");
545
+ }
546
+ }
547
+ // Bun.$`…` tagged template
548
+ if (ts.isTaggedTemplateExpression(node)) {
549
+ const tag = unwrapExpression(ts, node.tag);
550
+ if (
551
+ ts.isPropertyAccessExpression(tag) &&
552
+ tag.name.text === "$" &&
553
+ isGlobalReference(ts, unwrapExpression(ts, tag.expression), "Bun")
554
+ ) {
555
+ record(NODE_RUNTIME_MODULE_IMPORT_RULE, node, "Bun.$``");
556
+ }
557
+ }
558
+ ts.forEachChild(node, visit);
559
+ };
560
+ visit(sourceFile);
561
+
562
+ const findings: RuntimeBoundaryFinding[] = [];
563
+ const acknowledged: Array<RuntimeBoundaryFinding & { reason: string }> = [];
564
+ for (const finding of raw) {
565
+ const reason = readAllowComment(lines, finding.line, finding.rule);
566
+ if (reason === undefined) findings.push(finding);
567
+ else acknowledged.push({ ...finding, reason });
568
+ }
569
+ return { findings, acknowledged };
570
+ }
571
+
572
+ function describeSubjects(findings: readonly RuntimeBoundaryFinding[]): string {
573
+ const bySubject = new Map<string, number[]>();
574
+ for (const finding of findings) {
575
+ const lines = bySubject.get(finding.subject) ?? [];
576
+ lines.push(finding.line);
577
+ bySubject.set(finding.subject, lines);
578
+ }
579
+ return [...bySubject.entries()]
580
+ .map(([subject, lines]) => `${subject} (line ${lines.join(", ")})`)
581
+ .join("; ");
582
+ }
583
+
584
+ function remediation(rule: RuntimeBoundaryRule): string {
585
+ switch (rule) {
586
+ case PROCESS_ENV_DIRECT_READ_RULE:
587
+ return `Provider runtime source reads the ambient environment directly. ctx.env.get(name) sees only the names declared in defineProvider secrets[].name (engine-owned names excluded); declare the value there so the SDK presence gate, redaction, and Doppler projection see it. env: true only enables the ctx.env capability (type and runtime gate) and does not widen the readable names. Test/E2E toggles are not secrets and cannot move to ctx.env: replace them with test doubles (inject ctx.env / ctx.http) or recorded fixtures. Only the ${BOOTSTRAP_ENV_NAME_PREFIX}* bootstrap family may be read from process.env, and only dev.ts/start.ts/deploy.ts and scripts/, tools/, bin/ are outside this rule. Acknowledge a deliberate exception with \`// @apifuse-allow ${PROCESS_ENV_DIRECT_READ_RULE}: <reason>\`.`;
588
+ case NODE_RUNTIME_MODULE_IMPORT_RULE:
589
+ return `Provider runtime source reaches the filesystem, the network, or child processes on its own. Use ctx.http / ctx.stealth for HTTP, ctx.browser for browser work, ctx.files / ctx.cache / ctx.state for data that must persist, and keep recorder or smoke tooling under scripts/. Acknowledge a deliberate exception with \`// @apifuse-allow ${NODE_RUNTIME_MODULE_IMPORT_RULE}: <reason>\`.`;
590
+ case DIRECT_FETCH_CALL_RULE:
591
+ return `Provider runtime source calls the global fetch(), bypassing allowedHosts, proxy policy, retries, redaction, and telemetry. Use ctx.http.get/post/request for ordinary HTTP and ctx.stealth.fetch() for browser-shaped requests. Acknowledge a deliberate exception with \`// @apifuse-allow ${DIRECT_FETCH_CALL_RULE}: <reason>\`.`;
592
+ }
593
+ }
594
+
595
+ export type RuntimeBoundaryLintInput = {
596
+ authFlowSource?: string;
597
+ providerSourceFiles?: Record<string, string>;
598
+ operations?: Record<string, { handler?: unknown; source?: string }>;
599
+ };
600
+
601
+ /**
602
+ * Lint the provider runtime boundary. Returns error-level diagnostics (one per
603
+ * file and rule, listing every line) and information entries for findings
604
+ * acknowledged with `@apifuse-allow`.
605
+ */
606
+ export function lintRuntimeBoundary(
607
+ ts: TypeScriptModule,
608
+ provider: RuntimeBoundaryLintInput,
609
+ readOperationSource: (operation: { handler?: unknown; source?: string }) => string,
610
+ ): { diagnostics: LintDiagnostic[]; information: ProviderLintInformation[] } {
611
+ const sources: Array<{ field: string; fileName: string; source: string }> = [];
612
+ const sourceFiles = Object.entries(provider.providerSourceFiles ?? {}).filter(([filePath]) =>
613
+ isRuntimeBoundarySourceFile(filePath),
614
+ );
615
+ if (Object.keys(provider.providerSourceFiles ?? {}).length > 0) {
616
+ for (const [filePath, source] of sourceFiles) {
617
+ sources.push({ field: `sourceFiles.${filePath}`, fileName: filePath, source });
618
+ }
619
+ } else {
620
+ // In-memory providers (tests, embedded definitions) have no file tree;
621
+ // fall back to the auth flow and operation sources the way the other
622
+ // source rules do.
623
+ if (provider.authFlowSource) {
624
+ sources.push({
625
+ field: "auth.flow",
626
+ fileName: "auth-flow.ts",
627
+ source: provider.authFlowSource,
628
+ });
629
+ }
630
+ for (const [operationKey, operation] of Object.entries(provider.operations ?? {})) {
631
+ const source = readOperationSource(operation);
632
+ if (source) {
633
+ sources.push({
634
+ field: `operations.${operationKey}.handler`,
635
+ fileName: `${operationKey}.ts`,
636
+ source,
637
+ });
638
+ }
639
+ }
640
+ }
641
+
642
+ const diagnostics: LintDiagnostic[] = [];
643
+ const information: ProviderLintInformation[] = [];
644
+ for (const { field, fileName, source } of sources) {
645
+ const report = analyzeRuntimeBoundary(ts, fileName, source);
646
+ for (const rule of RUNTIME_BOUNDARY_RULES) {
647
+ const findings = report.findings.filter((finding) => finding.rule === rule);
648
+ if (findings.length > 0) {
649
+ diagnostics.push({
650
+ rule,
651
+ level: "error",
652
+ field,
653
+ message: `${describeSubjects(findings)}: ${remediation(rule)}`,
654
+ });
655
+ }
656
+ for (const finding of report.acknowledged.filter((entry) => entry.rule === rule)) {
657
+ information.push({
658
+ rule,
659
+ field,
660
+ message: `Acknowledged ${finding.subject} at line ${finding.line}. Reason: ${finding.reason}`,
661
+ });
662
+ }
663
+ }
664
+ }
665
+ return { diagnostics, information };
666
+ }
667
+
668
+ export type LintRuntimeBoundarySourcesOptions = {
669
+ /**
670
+ * A loaded TypeScript compiler API module to parse with (`typescript` 6.x
671
+ * or `@typescript/typescript6`). When omitted the SDK resolves one from its
672
+ * own install location, trying `typescript` first and falling back to
673
+ * `@typescript/typescript6` when `typescript` is the 7.x native shell
674
+ * without `createSourceFile`. A caller whose dependency tree pins its own
675
+ * compiler module (the APIFuse monorepo contract check) passes it here so
676
+ * the parse does not depend on what hoists next to the SDK.
677
+ */
678
+ typescript?: TypeScriptCompilerModuleLike;
679
+ };
680
+
681
+ /**
682
+ * Lint provider runtime source files for the runtime boundary rules without a
683
+ * provider definition: `files` maps provider-root-relative paths to source
684
+ * text (the same shape `apifuse check` collects as `providerSourceFiles`).
685
+ * Out-of-scope paths (tests, recorded fixtures, `.d.ts`, the root
686
+ * `dev.ts`/`start.ts`/`deploy.ts`, `scripts/`, `tools/`, `bin/`) are skipped
687
+ * with `isRuntimeBoundarySourceFile`. Diagnostics carry
688
+ * `field: "sourceFiles.<path>"`; `@apifuse-allow` acknowledgements come back
689
+ * as `information`.
690
+ *
691
+ * Parses with `options.typescript` when given, otherwise with the compiler
692
+ * module resolved from the installed SDK (see `getTypeScript`). This is the
693
+ * entry point the platform contract check uses to apply the rules with the
694
+ * monorepo's SDK pin instead of each provider's own pin.
695
+ */
696
+ export function lintRuntimeBoundarySources(
697
+ files: Record<string, string>,
698
+ options: LintRuntimeBoundarySourcesOptions = {},
699
+ ): ProviderLintResult {
700
+ const ts = options.typescript ?? getTypeScript();
701
+ if (!isTypeScriptCompilerModule(ts)) {
702
+ throw new TypeError(
703
+ "lintRuntimeBoundarySources: options.typescript must be a loaded TypeScript compiler API module (typescript 6.x or @typescript/typescript6) exposing createSourceFile.",
704
+ );
705
+ }
706
+ return lintRuntimeBoundary(ts, { providerSourceFiles: files }, () => "");
707
+ }