@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,1552 @@
1
+ import path from "node:path";
2
+ import { DEPLOYMENT_INTENT_FIELD_ORDER, deepEqual, deploymentIntentsEquivalent, normalizeLegacyDeployment, parseDeploymentIntent, resolveDeploymentIntent, subtractDeploymentDefaults, } from "./deployment-intent.js";
3
+ export const TYPESCRIPT_REQUIRED_MESSAGE = "apifuse migrate-deployment requires typescript; install it in the workspace running the CLI (bun add -d typescript)";
4
+ const ts = await loadTypeScript();
5
+ /**
6
+ * Throws (rather than exiting) when the compiler is missing so importers can
7
+ * decide: the migrate command reports and exits, `apifuse check` skips its
8
+ * source-shape inspection.
9
+ */
10
+ async function loadTypeScript() {
11
+ try {
12
+ return await import("typescript");
13
+ }
14
+ catch (error) {
15
+ throw new Error(TYPESCRIPT_REQUIRED_MESSAGE, { cause: error });
16
+ }
17
+ }
18
+ const DEPLOYMENT_PROPERTY_NAME = "deployment";
19
+ /** index.ts sits at the repository root; the deploy module is its sibling. */
20
+ const INDEX_VIRTUAL_DIRECTORY = "/";
21
+ const DEPLOY_MODULE_VIRTUAL_PATH = "/deploy";
22
+ const SOURCE_EXTENSION = /\.(?:[cm]?[jt]s|[jt]sx)$/u;
23
+ const LINE_WIDTH = 80;
24
+ /**
25
+ * Emitted above the key only when it carries `runtime`: the deployment
26
+ * runtime axis sits next to the execution `runtime` property and must not
27
+ * be read as the same axis.
28
+ */
29
+ export const DEPLOYMENT_RUNTIME_AXIS_COMMENT = "// Deployment runtime axis (shared | dedicated | browser); not the execution runtime above.";
30
+ /** CODEOWNERS block the platform scaffold emitted for the mirror; removed verbatim only. */
31
+ const CODEOWNERS_MIRROR_COMMENT = [
32
+ "# Platform-generated deployment mirror (TEMPORARY until the SDK deployment",
33
+ "# passthrough lands): deployment intent is platform-owned, so changes to the",
34
+ "# generated deploy.ts require operator review.",
35
+ ];
36
+ const CODEOWNERS_DEPLOY_LINE = /^\/deploy\.ts(?:\s|$)/;
37
+ export function migrateDeploymentIntent(input) {
38
+ const notes = [];
39
+ const fileName = input.indexFileName ?? "index.ts";
40
+ const source = parseSource(fileName, input.indexSource);
41
+ const syntaxError = firstSyntaxError(source);
42
+ if (syntaxError !== undefined) {
43
+ return { status: "refused", reason: `index.ts does not parse: ${syntaxError}` };
44
+ }
45
+ let codeowners;
46
+ if (input.codeownersSource !== undefined) {
47
+ const stripped = stripCodeownersDeployLock(input.codeownersSource);
48
+ if (input.dropCodeownersLock === true) {
49
+ codeowners = stripped;
50
+ }
51
+ else {
52
+ codeowners = { text: input.codeownersSource, changed: false };
53
+ if (stripped.changed) {
54
+ notes.push("Kept the /deploy.ts CODEOWNERS entry: the platform still honors a re-added deploy.ts until its legacy fallback is retired; re-run with --drop-codeowners-lock after that.");
55
+ }
56
+ }
57
+ }
58
+ // ── Legacy file
59
+ let legacy;
60
+ let legacyRaw;
61
+ if (input.deploySource !== undefined) {
62
+ const parsed = readLegacyDeployFile(input.deploySource);
63
+ if (!parsed.ok)
64
+ return { status: "refused", reason: parsed.reason };
65
+ legacy = parsed.value;
66
+ legacyRaw = parsed.raw;
67
+ }
68
+ // ── Declaration literal
69
+ const calls = collectDefineProviderCalls(source);
70
+ if (calls.length === 0) {
71
+ return {
72
+ status: "refused",
73
+ reason: "No defineProvider(...) call found; this file does not declare a provider.",
74
+ };
75
+ }
76
+ if (calls.length > 1) {
77
+ return {
78
+ status: "refused",
79
+ reason: `Found ${calls.length} defineProvider(...) calls; the transform rewrites exactly one declaration.`,
80
+ };
81
+ }
82
+ const call = calls[0];
83
+ if (call === undefined)
84
+ return { status: "refused", reason: "Internal: defineProvider call vanished." };
85
+ if (!isTopLevelNode(call)) {
86
+ return {
87
+ status: "refused",
88
+ reason: "defineProvider(...) is called inside a function or block; the transform only rewrites a top-level declaration, where bindings cannot be shadowed.",
89
+ };
90
+ }
91
+ const located = locateDeclarationLiteral(source, call);
92
+ if (located.status === "refused")
93
+ return located;
94
+ const { literal: declaration, shape } = located;
95
+ // ── Existing key inside the declaration
96
+ const existingProperty = resolveDeclarationDeployment(declaration, source);
97
+ if (existingProperty.status === "refused")
98
+ return existingProperty;
99
+ let existingIntent;
100
+ let existingResolved;
101
+ let existingOpaque = false;
102
+ if (existingProperty.property !== undefined) {
103
+ const evaluated = evaluateStatic(existingProperty.property.initializer, source);
104
+ if (evaluated.ok) {
105
+ const parsedIntent = parseDeploymentIntent(evaluated.value);
106
+ if (!parsedIntent.ok) {
107
+ return {
108
+ status: "refused",
109
+ reason: `The declaration's deployment key is invalid: ${parsedIntent.reason}`,
110
+ };
111
+ }
112
+ existingIntent = parsedIntent.value;
113
+ try {
114
+ existingResolved = resolveDeploymentIntent(existingIntent);
115
+ }
116
+ catch (error) {
117
+ return {
118
+ status: "refused",
119
+ reason: `The declaration's deployment key does not resolve: ${describeError(error)}`,
120
+ };
121
+ }
122
+ }
123
+ else {
124
+ existingOpaque = true;
125
+ }
126
+ }
127
+ // ── Default-export spread carrying `deployment`
128
+ const spread = analyzeSpreadExport(source, call, builderVariableName(call, source));
129
+ if (spread.status === "refused")
130
+ return spread;
131
+ const deployImports = collectLegacyDeployImports(source);
132
+ if (deployImports.status === "refused")
133
+ return deployImports;
134
+ let spreadResolved;
135
+ if (spread.deploymentProperty !== undefined) {
136
+ const valueNode = spread.deploymentProperty.value;
137
+ if (valueNode.kind === "deploy-import") {
138
+ if (legacy === undefined) {
139
+ return {
140
+ status: "refused",
141
+ reason: "index.ts imports the default export of ./deploy but deploy.ts is missing; restore the file or remove the import first.",
142
+ };
143
+ }
144
+ // The platform reads the attached object as the deployment KEY
145
+ // (omitted fields resolve to profile defaults), not as a legacy
146
+ // file (omitted hpa meant disabled). Resolve it the way the platform
147
+ // does and require agreement with the legacy reading before the
148
+ // file that satisfies both is deleted.
149
+ const asKey = parseDeploymentIntent(legacyRaw);
150
+ if (!asKey.ok) {
151
+ return {
152
+ status: "refused",
153
+ reason: `deploy.ts is attached as the default export's deployment key but is not a valid key: ${asKey.reason}`,
154
+ };
155
+ }
156
+ try {
157
+ spreadResolved = resolveDeploymentIntent(asKey.value);
158
+ }
159
+ catch (error) {
160
+ return {
161
+ status: "refused",
162
+ reason: `deploy.ts, attached as the default export's deployment key, does not resolve: ${describeError(error)}`,
163
+ };
164
+ }
165
+ if (!deploymentIntentsEquivalent(spreadResolved, legacy)) {
166
+ return {
167
+ status: "refused",
168
+ reason: "deploy.ts resolves differently as the attached deployment key (omitted fields take profile defaults) than as a legacy file (omitted hpa is disabled); make the file explicit by hand before migrating.",
169
+ };
170
+ }
171
+ }
172
+ else {
173
+ const parsedIntent = parseDeploymentIntent(valueNode.value);
174
+ if (!parsedIntent.ok) {
175
+ return {
176
+ status: "refused",
177
+ reason: `The default export's deployment property is invalid: ${parsedIntent.reason}`,
178
+ };
179
+ }
180
+ try {
181
+ spreadResolved = resolveDeploymentIntent(parsedIntent.value);
182
+ }
183
+ catch (error) {
184
+ return {
185
+ status: "refused",
186
+ reason: `The default export's deployment property does not resolve: ${describeError(error)}`,
187
+ };
188
+ }
189
+ }
190
+ }
191
+ else if (deployImports.defaultImport !== undefined) {
192
+ return {
193
+ status: "refused",
194
+ reason: "index.ts imports ./deploy but does not attach it as `deployment` on the default export; the transform cannot tell how the value is used.",
195
+ };
196
+ }
197
+ // ── Which resolved view is authoritative
198
+ if (existingOpaque && (legacy !== undefined || spreadResolved !== undefined)) {
199
+ return {
200
+ status: "refused",
201
+ reason: "The declaration already carries a deployment key that is not a static literal, so it cannot be compared with the legacy deploy.ts value.",
202
+ };
203
+ }
204
+ if (existingResolved !== undefined &&
205
+ spreadResolved !== undefined &&
206
+ !deploymentIntentsEquivalent(existingResolved, spreadResolved)) {
207
+ return {
208
+ status: "refused",
209
+ reason: "The declaration's deployment key and the default export's deployment property resolve to different deployments; reconcile them by hand.",
210
+ };
211
+ }
212
+ const keyResolved = spreadResolved ?? existingResolved;
213
+ if (legacy !== undefined &&
214
+ keyResolved !== undefined &&
215
+ !deploymentIntentsEquivalent(keyResolved, legacy)) {
216
+ return {
217
+ status: "refused",
218
+ reason: "deploy.ts and the authored deployment key resolve to different deployments (the key is authoritative for the platform); reconcile them by hand before deleting the file.",
219
+ };
220
+ }
221
+ const target = keyResolved ?? legacy;
222
+ if (target === undefined) {
223
+ // Nothing to move: no deploy.ts, no key to hoist. Only the CODEOWNERS
224
+ // lock on a file that no longer exists may still need cleanup.
225
+ if (existingOpaque) {
226
+ notes.push("The declaration's deployment key is not a static literal; it was left untouched.");
227
+ }
228
+ if (codeowners?.changed) {
229
+ return {
230
+ status: "migrated",
231
+ shape,
232
+ indexSource: input.indexSource,
233
+ indexChanged: false,
234
+ codeownersSource: codeowners.text,
235
+ codeownersChanged: true,
236
+ removeDeployFile: false,
237
+ intent: {},
238
+ resolved: resolveDeploymentIntent(),
239
+ notes,
240
+ };
241
+ }
242
+ return { status: "unchanged", notes };
243
+ }
244
+ const intent = subtractDeploymentDefaults(target);
245
+ const resolved = resolveDeploymentIntent(intent);
246
+ if (!deploymentIntentsEquivalent(resolved, target)) {
247
+ return {
248
+ status: "refused",
249
+ reason: "Internal: the minimal deployment key does not resolve back to the declared deployment; refusing to write it.",
250
+ };
251
+ }
252
+ // ── Edits
253
+ const edits = [];
254
+ if (existingProperty.property !== undefined) {
255
+ if (existingIntent === undefined || !deepEqual(existingIntent, intent)) {
256
+ if (Object.keys(intent).length === 0) {
257
+ if (existingProperty.spreadCarriesDeployment === true) {
258
+ return {
259
+ status: "refused",
260
+ reason: "The declaration's deployment key only restates profile defaults, but removing it would expose the deployment carried by a spread in the same literal; reconcile the spread by hand first.",
261
+ };
262
+ }
263
+ edits.push(removeObjectProperty(existingProperty.property, declaration, source));
264
+ notes.push("Removed a deployment key that only restated profile defaults.");
265
+ }
266
+ else {
267
+ const indent = lineIndentation(source, existingProperty.property.getStart(source));
268
+ const unit = indentUnitFor(source, declaration, indent);
269
+ edits.push({
270
+ start: existingProperty.property.initializer.getStart(source),
271
+ end: existingProperty.property.initializer.getEnd(),
272
+ text: formatIntentLiteral(intent, indent, unit),
273
+ });
274
+ notes.push("Rewrote the deployment key to the minimal (profile-subtracted) form.");
275
+ }
276
+ }
277
+ }
278
+ else if (Object.keys(intent).length > 0) {
279
+ edits.push(insertDeploymentProperty(declaration, intent, source));
280
+ }
281
+ if (spread.deploymentProperty !== undefined) {
282
+ edits.push(...removeSpreadDeploymentProperty(spread, declaration, source));
283
+ const valueNode = spread.deploymentProperty.value;
284
+ if (valueNode.kind === "const" &&
285
+ countIdentifierReferences(source, valueNode.name) === 1 &&
286
+ !hasExportModifier(valueNode.statement)) {
287
+ edits.push(removeStatement(valueNode.statement, source));
288
+ }
289
+ }
290
+ for (const statement of deployImports.removable) {
291
+ edits.push(removeStatement(statement, source));
292
+ }
293
+ if (editsOverlap(edits)) {
294
+ return {
295
+ status: "refused",
296
+ reason: "Internal: the planned source edits overlap; refusing to emit a partial migration.",
297
+ };
298
+ }
299
+ const output = applyEdits(input.indexSource, edits);
300
+ const verified = parseSource(fileName, output);
301
+ const outputError = firstSyntaxError(verified);
302
+ if (outputError !== undefined) {
303
+ return {
304
+ status: "refused",
305
+ reason: `Transform produced source that does not parse (${outputError}); refusing to emit a partial migration.`,
306
+ };
307
+ }
308
+ if (deployImports.defaultImport !== undefined) {
309
+ const remaining = countIdentifierReferences(verified, deployImports.defaultImport);
310
+ if (remaining > 0) {
311
+ return {
312
+ status: "refused",
313
+ reason: `\`${deployImports.defaultImport}\` (imported from ./deploy) is still referenced after removing the default-export property; the transform cannot delete deploy.ts.`,
314
+ };
315
+ }
316
+ }
317
+ for (const typeName of deployImports.typeOnlyNames) {
318
+ if (countIdentifierReferences(verified, typeName) > 0) {
319
+ return {
320
+ status: "refused",
321
+ reason: `index.ts still uses \`${typeName}\`, a type imported from ./deploy, after the import is removed; deploy.ts is being deleted, so replace it (for example with \`ProviderDeploymentOverrides\` from @apifuse/provider-sdk) by hand before migrating.`,
322
+ };
323
+ }
324
+ }
325
+ if (referencesLegacyDeployModule(verified)) {
326
+ return {
327
+ status: "refused",
328
+ reason: "index.ts still references the ./deploy module after the transform; remove that usage by hand.",
329
+ };
330
+ }
331
+ const indexChanged = output !== input.indexSource;
332
+ const removeDeployFile = input.deploySource !== undefined;
333
+ if (!indexChanged && !removeDeployFile && !codeowners?.changed) {
334
+ return { status: "unchanged", notes };
335
+ }
336
+ return {
337
+ status: "migrated",
338
+ shape,
339
+ indexSource: output,
340
+ indexChanged,
341
+ ...(codeowners === undefined ? {} : { codeownersSource: codeowners.text }),
342
+ codeownersChanged: codeowners?.changed ?? false,
343
+ removeDeployFile,
344
+ ...(legacy === undefined ? {} : { legacy }),
345
+ intent,
346
+ resolved,
347
+ notes,
348
+ };
349
+ }
350
+ /**
351
+ * Statically read the `deployment` key authored inside the declaration
352
+ * literal of `indexSource` (undefined when absent). Used to prove round
353
+ * trips: the value the transform wrote must resolve to the legacy view.
354
+ */
355
+ export function readDeclaredDeploymentIntent(indexSource, fileName = "index.ts") {
356
+ const source = parseSource(fileName, indexSource);
357
+ const calls = collectDefineProviderCalls(source);
358
+ const call = calls[0];
359
+ if (calls.length !== 1 || call === undefined) {
360
+ return { ok: false, reason: `expected exactly one defineProvider call, found ${calls.length}` };
361
+ }
362
+ const located = locateDeclarationLiteral(source, call);
363
+ if (located.status === "refused")
364
+ return { ok: false, reason: located.reason };
365
+ const property = resolveDeclarationDeployment(located.literal, source);
366
+ if (property.status === "refused")
367
+ return { ok: false, reason: property.reason };
368
+ if (property.property === undefined)
369
+ return { ok: true, value: undefined };
370
+ return evaluateStatic(property.property.initializer, source);
371
+ }
372
+ /**
373
+ * Line (1-based) of a `deployment` property attached to a spread default
374
+ * export (`export default { ...provider, deployment }`), or undefined when
375
+ * the module has no such legacy surface. Used by `apifuse check`: the loaded
376
+ * module cannot tell that extra apart from a declaration-level key, so the
377
+ * source shape is inspected instead.
378
+ */
379
+ export function findSpreadExportDeploymentProperty(indexSource, fileName = "index.ts") {
380
+ const source = parseSource(fileName, indexSource);
381
+ const exportAssignment = findDefaultExport(source);
382
+ if (exportAssignment === undefined)
383
+ return undefined;
384
+ const exported = unwrapExpression(exportAssignment.expression);
385
+ if (!ts.isObjectLiteralExpression(exported))
386
+ return undefined;
387
+ if (!exported.properties.some(ts.isSpreadAssignment))
388
+ return undefined;
389
+ for (const property of exported.properties) {
390
+ if (ts.isSpreadAssignment(property))
391
+ continue;
392
+ const name = property.name;
393
+ if (name === undefined)
394
+ continue;
395
+ let text;
396
+ if (ts.isIdentifier(name) || ts.isStringLiteralLike(name))
397
+ text = name.text;
398
+ else if (ts.isComputedPropertyName(name)) {
399
+ const inner = unwrapExpression(name.expression);
400
+ if (ts.isStringLiteralLike(inner))
401
+ text = inner.text;
402
+ }
403
+ if (text === DEPLOYMENT_PROPERTY_NAME) {
404
+ return { line: source.getLineAndCharacterOfPosition(property.getStart(source)).line + 1 };
405
+ }
406
+ }
407
+ return undefined;
408
+ }
409
+ /** Parse a legacy `deploy.ts` module text into the resolved deployment view. */
410
+ export function readLegacyDeployFile(deploySource) {
411
+ const source = parseSource("deploy.ts", deploySource);
412
+ const syntaxError = firstSyntaxError(source);
413
+ if (syntaxError !== undefined) {
414
+ return { ok: false, reason: `deploy.ts does not parse: ${syntaxError}` };
415
+ }
416
+ const exportAssignment = findDefaultExport(source);
417
+ if (exportAssignment === undefined) {
418
+ return {
419
+ ok: false,
420
+ reason: "deploy.ts has no `export default`; the transform cannot read its deployment config.",
421
+ };
422
+ }
423
+ const evaluated = evaluateStatic(exportAssignment.expression, source);
424
+ if (!evaluated.ok) {
425
+ return {
426
+ ok: false,
427
+ reason: `deploy.ts default export is not a static literal: ${evaluated.reason}`,
428
+ };
429
+ }
430
+ const normalized = normalizeLegacyDeployment(evaluated.value);
431
+ if (!normalized.ok)
432
+ return normalized;
433
+ return { ok: true, value: normalized.value, raw: evaluated.value };
434
+ }
435
+ /**
436
+ * Repository sources (other than index.ts and deploy.ts themselves) that
437
+ * import the deploy module — `import x from "../deploy"`, re-exports, dynamic
438
+ * `import("./deploy")`, `require("./deploy")`. Deleting deploy.ts while one
439
+ * exists breaks that consumer, so the command refuses until it is removed.
440
+ * `files` carry paths relative to or absolute under `providerRoot`.
441
+ */
442
+ export function findDeployModuleConsumers(providerRoot, files) {
443
+ const deployModule = path.resolve(providerRoot, "deploy");
444
+ const consumers = [];
445
+ for (const file of files) {
446
+ const absolute = path.resolve(providerRoot, file.path);
447
+ const source = parseSource(absolute, file.text);
448
+ if (sourceImportsModule(source, path.dirname(absolute), deployModule)) {
449
+ consumers.push(path.relative(providerRoot, absolute));
450
+ }
451
+ }
452
+ return consumers.sort();
453
+ }
454
+ /**
455
+ * Remove the `/deploy.ts` CODEOWNERS line and, when it is preceded by the
456
+ * exact platform-scaffold comment block, that block too. Any other content
457
+ * is preserved byte for byte.
458
+ */
459
+ export function stripCodeownersDeployLock(text) {
460
+ const newline = text.includes("\r\n") ? "\r\n" : "\n";
461
+ const lines = text.split(newline);
462
+ const output = [];
463
+ let changed = false;
464
+ for (let index = 0; index < lines.length; index += 1) {
465
+ const line = lines[index] ?? "";
466
+ if (!CODEOWNERS_DEPLOY_LINE.test(line)) {
467
+ output.push(line);
468
+ continue;
469
+ }
470
+ changed = true;
471
+ const commentStart = output.length - CODEOWNERS_MIRROR_COMMENT.length;
472
+ if (commentStart >= 0 &&
473
+ CODEOWNERS_MIRROR_COMMENT.every((expected, offset) => output[commentStart + offset] === expected)) {
474
+ output.splice(commentStart, CODEOWNERS_MIRROR_COMMENT.length);
475
+ }
476
+ }
477
+ return { text: changed ? output.join(newline) : text, changed };
478
+ }
479
+ function locateDeclarationLiteral(source, call) {
480
+ const argument = call.arguments[0];
481
+ if (argument === undefined) {
482
+ return {
483
+ status: "refused",
484
+ reason: "defineProvider() is called without a declaration argument.",
485
+ };
486
+ }
487
+ const unwrapped = unwrapExpression(argument);
488
+ if (ts.isObjectLiteralExpression(unwrapped)) {
489
+ return { status: "ok", literal: unwrapped, shape: "literal-argument" };
490
+ }
491
+ if (ts.isIdentifier(unwrapped)) {
492
+ const declaration = findTopLevelConst(source, unwrapped.text);
493
+ if (declaration?.initializer !== undefined) {
494
+ const initializer = unwrapExpression(declaration.initializer);
495
+ if (ts.isObjectLiteralExpression(initializer)) {
496
+ if (!identifierUsesAreReadOnly(source, unwrapped.text)) {
497
+ return {
498
+ status: "refused",
499
+ reason: `\`${unwrapped.text}\` is mutated, aliased, or passed to a call after its initializer, so the transform cannot prove the literal is the effective declaration.`,
500
+ };
501
+ }
502
+ return { status: "ok", literal: initializer, shape: "variable-literal-argument" };
503
+ }
504
+ }
505
+ return {
506
+ status: "refused",
507
+ reason: `defineProvider(${unwrapped.text}) is called with a variable whose initializer is not a top-level const object literal, so the transform cannot place the deployment key.`,
508
+ };
509
+ }
510
+ return {
511
+ status: "refused",
512
+ reason: "defineProvider(...) is called with a non-literal argument, so the transform cannot place the deployment key.",
513
+ };
514
+ }
515
+ /**
516
+ * The declaration literal's effective `deployment` property. Object literals
517
+ * are last-wins, so spreads inside the literal are inspected statically: a
518
+ * spread that resolves to a top-level const object literal is scanned
519
+ * (recursively) for a `deployment` key; anything the transform cannot inspect
520
+ * is refused because it cannot prove the spread carries no deployment. A
521
+ * deployment-bearing spread that is the last contributor — or the only one —
522
+ * refuses too, since inserting or rewriting a plain property would not change
523
+ * (or would silently change) the effective deployment.
524
+ */
525
+ function resolveDeclarationDeployment(literal, source) {
526
+ let explicit;
527
+ let lastSource;
528
+ let lastSpreadText = "";
529
+ let anySpreadCarries = false;
530
+ for (const property of literal.properties) {
531
+ if (ts.isSpreadAssignment(property)) {
532
+ const scan = spreadCarriesDeployment(property.expression, source, new Set());
533
+ if (scan.status === "refused") {
534
+ return { status: "refused", reason: `The declaration spreads ${scan.reason}` };
535
+ }
536
+ if (scan.carries) {
537
+ anySpreadCarries = true;
538
+ lastSource = "spread";
539
+ lastSpreadText = property.expression.getText(source);
540
+ }
541
+ continue;
542
+ }
543
+ const named = matchDeploymentProperty(property, "declaration");
544
+ if (named.status === "refused")
545
+ return named;
546
+ if (named.property === undefined)
547
+ continue;
548
+ if (explicit !== undefined) {
549
+ return {
550
+ status: "refused",
551
+ reason: "The declaration declares `deployment` more than once; keep a single property.",
552
+ };
553
+ }
554
+ explicit = named.property;
555
+ lastSource = "explicit";
556
+ }
557
+ if (lastSource === "spread") {
558
+ return {
559
+ status: "refused",
560
+ reason: `The declaration receives its deployment key through \`...${lastSpreadText}\`; hoist that key into a plain \`deployment\` property by hand before migrating.`,
561
+ };
562
+ }
563
+ return { status: "ok", property: explicit, spreadCarriesDeployment: anySpreadCarries };
564
+ }
565
+ /**
566
+ * Match one non-spread object-literal element against the `deployment`
567
+ * name. Only a plain `deployment: <expr>` assignment can be rewritten;
568
+ * shorthand, accessor, method, and computed spellings refuse.
569
+ */
570
+ function matchDeploymentProperty(property, context) {
571
+ const propertyName = property.name;
572
+ if (propertyName === undefined)
573
+ return { status: "ok", property: undefined };
574
+ if (ts.isComputedPropertyName(propertyName)) {
575
+ const inner = unwrapExpression(propertyName.expression);
576
+ if (ts.isStringLiteralLike(inner)) {
577
+ if (inner.text !== DEPLOYMENT_PROPERTY_NAME)
578
+ return { status: "ok", property: undefined };
579
+ return {
580
+ status: "refused",
581
+ reason: `The ${context} spells \`${DEPLOYMENT_PROPERTY_NAME}\` as a computed property name; write it as a plain property first.`,
582
+ };
583
+ }
584
+ return {
585
+ status: "refused",
586
+ reason: `The ${context} carries a computed property whose name is not static; it could evaluate to \`${DEPLOYMENT_PROPERTY_NAME}\`, so the transform refuses.`,
587
+ };
588
+ }
589
+ if (!(ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName))) {
590
+ return { status: "ok", property: undefined };
591
+ }
592
+ if (propertyName.text !== DEPLOYMENT_PROPERTY_NAME)
593
+ return { status: "ok", property: undefined };
594
+ if (ts.isPropertyAssignment(property))
595
+ return { status: "ok", property };
596
+ return {
597
+ status: "refused",
598
+ reason: `The ${context}'s \`${DEPLOYMENT_PROPERTY_NAME}\` property is a ${ts.isShorthandPropertyAssignment(property) ? "shorthand" : "method or accessor"} form the transform cannot rewrite.`,
599
+ };
600
+ }
601
+ /** Whether a spread expression statically resolves to an object that owns a `deployment` key. */
602
+ function spreadCarriesDeployment(expression, source, seen) {
603
+ const expr = unwrapExpression(expression);
604
+ if (ts.isObjectLiteralExpression(expr))
605
+ return objectCarriesDeployment(expr, source, seen);
606
+ if (ts.isIdentifier(expr)) {
607
+ if (seen.has(expr.text)) {
608
+ return {
609
+ status: "refused",
610
+ reason: `\`...${expr.text}\`, which is defined in terms of itself.`,
611
+ };
612
+ }
613
+ const declaration = findTopLevelConst(source, expr.text);
614
+ if (declaration?.initializer !== undefined) {
615
+ const initializer = unwrapExpression(declaration.initializer);
616
+ if (ts.isObjectLiteralExpression(initializer)) {
617
+ if (!identifierUsesAreReadOnly(source, expr.text)) {
618
+ return {
619
+ status: "refused",
620
+ reason: `\`...${expr.text}\`, which is mutated, aliased, or passed to a call after its initializer, so the transform cannot prove what it carries.`,
621
+ };
622
+ }
623
+ const next = new Set(seen);
624
+ next.add(expr.text);
625
+ return objectCarriesDeployment(initializer, source, next);
626
+ }
627
+ }
628
+ return {
629
+ status: "refused",
630
+ reason: `\`...${expr.text}\`, which does not resolve to a top-level const object literal, so the transform cannot prove it carries no \`deployment\` key.`,
631
+ };
632
+ }
633
+ return {
634
+ status: "refused",
635
+ reason: `\`...${expr.getText(source)}\`, which is not a statically inspectable object, so the transform cannot prove it carries no \`deployment\` key.`,
636
+ };
637
+ }
638
+ function objectCarriesDeployment(literal, source, seen) {
639
+ let carries = false;
640
+ for (const property of literal.properties) {
641
+ if (ts.isSpreadAssignment(property)) {
642
+ const nested = spreadCarriesDeployment(property.expression, source, seen);
643
+ if (nested.status === "refused")
644
+ return nested;
645
+ carries = carries || nested.carries;
646
+ continue;
647
+ }
648
+ const name = property.name;
649
+ if (name === undefined)
650
+ continue;
651
+ if (ts.isComputedPropertyName(name)) {
652
+ const inner = unwrapExpression(name.expression);
653
+ if (!ts.isStringLiteralLike(inner)) {
654
+ return {
655
+ status: "refused",
656
+ reason: `an object with a computed property name that is not static, so the transform cannot prove it carries no \`deployment\` key.`,
657
+ };
658
+ }
659
+ if (inner.text === DEPLOYMENT_PROPERTY_NAME)
660
+ carries = true;
661
+ continue;
662
+ }
663
+ if ((ts.isIdentifier(name) || ts.isStringLiteralLike(name)) &&
664
+ name.text === DEPLOYMENT_PROPERTY_NAME) {
665
+ carries = true;
666
+ }
667
+ }
668
+ return { status: "ok", carries };
669
+ }
670
+ /**
671
+ * Name of the variable the `defineProvider(...)` call is bound to — only
672
+ * when that binding is a top-level `const` that is never reassigned, so a
673
+ * later `builder({ operations })` call provably builds THIS declaration.
674
+ */
675
+ function builderVariableName(call, source) {
676
+ const parent = call.parent;
677
+ if (!ts.isVariableDeclaration(parent) || !ts.isIdentifier(parent.name))
678
+ return undefined;
679
+ const list = parent.parent;
680
+ if (!ts.isVariableDeclarationList(list) || (list.flags & ts.NodeFlags.Const) === 0)
681
+ return undefined;
682
+ const statement = list.parent;
683
+ if (!ts.isVariableStatement(statement) || statement.parent !== source)
684
+ return undefined;
685
+ return isReassigned(source, parent.name.text) ? undefined : parent.name.text;
686
+ }
687
+ /** Whether any expression assigns to (or increments) the binding `name`. */
688
+ function isReassigned(source, name) {
689
+ let reassigned = false;
690
+ const visit = (node) => {
691
+ if (reassigned)
692
+ return;
693
+ if (ts.isIdentifier(node) && node.text === name && isIdentifierReference(node)) {
694
+ const parent = node.parent;
695
+ if ((ts.isBinaryExpression(parent) &&
696
+ parent.left === node &&
697
+ parent.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
698
+ parent.operatorToken.kind <= ts.SyntaxKind.LastAssignment) ||
699
+ ts.isPrefixUnaryExpression(parent) ||
700
+ ts.isPostfixUnaryExpression(parent)) {
701
+ reassigned = true;
702
+ return;
703
+ }
704
+ }
705
+ ts.forEachChild(node, visit);
706
+ };
707
+ visit(source);
708
+ return reassigned;
709
+ }
710
+ function analyzeSpreadExport(source, declarationCall, builderName) {
711
+ const exportAssignment = findDefaultExport(source);
712
+ if (exportAssignment === undefined)
713
+ return { status: "ok" };
714
+ const exported = unwrapExpression(exportAssignment.expression);
715
+ if (ts.isIdentifier(exported)) {
716
+ // `export default provider;` — the binding must reach the export
717
+ // untouched (`provider.deployment!.replicas = 2` would make the
718
+ // declaration key the transform rewrites not the effective one).
719
+ if (findTopLevelConst(source, exported.text) !== undefined &&
720
+ !identifierUsesAreReadOnly(source, exported.text, new Set(), "provider")) {
721
+ return {
722
+ status: "refused",
723
+ reason: `The default export \`${exported.text}\` is mutated, aliased, or passed to a call before it is exported, so the declaration's deployment key may not be the effective one; reconcile by hand.`,
724
+ };
725
+ }
726
+ return { status: "ok" };
727
+ }
728
+ if (!ts.isObjectLiteralExpression(exported))
729
+ return { status: "ok" };
730
+ if (!exported.properties.some(ts.isSpreadAssignment))
731
+ return { status: "ok" };
732
+ let deploymentProperty;
733
+ let deploymentIndex = -1;
734
+ let providerSpreadIndex = -1;
735
+ let lastDeploymentSpreadIndex = -1;
736
+ for (const [index, property] of exported.properties.entries()) {
737
+ if (ts.isSpreadAssignment(property)) {
738
+ if (isBuiltProviderSpread(property.expression, source, declarationCall, builderName)) {
739
+ providerSpreadIndex = index;
740
+ continue;
741
+ }
742
+ // Any other spread must be statically inspectable: a spread the
743
+ // transform cannot see into could carry (and, if it comes later,
744
+ // override) the deployment key it is about to hoist.
745
+ const scan = spreadCarriesDeployment(property.expression, source, new Set());
746
+ if (scan.status === "refused") {
747
+ return { status: "refused", reason: `The default export spreads ${scan.reason}` };
748
+ }
749
+ if (scan.carries)
750
+ lastDeploymentSpreadIndex = index;
751
+ continue;
752
+ }
753
+ const named = matchDeploymentProperty(property, "default export");
754
+ if (named.status === "refused") {
755
+ // Shorthand `deployment` on the export is a legitimate legacy shape;
756
+ // resolve the identifier instead of refusing it.
757
+ if (ts.isShorthandPropertyAssignment(property) &&
758
+ property.name.text === DEPLOYMENT_PROPERTY_NAME) {
759
+ if (deploymentProperty !== undefined)
760
+ return duplicateExportDeployment();
761
+ const value = resolveSpreadValueIdentifier(source, property.name);
762
+ if (value.status === "refused")
763
+ return value;
764
+ deploymentProperty = { node: property, value: value.value };
765
+ deploymentIndex = index;
766
+ continue;
767
+ }
768
+ return named;
769
+ }
770
+ if (named.property === undefined)
771
+ continue;
772
+ if (deploymentProperty !== undefined)
773
+ return duplicateExportDeployment();
774
+ const initializer = unwrapExpression(named.property.initializer);
775
+ if (ts.isIdentifier(initializer)) {
776
+ const value = resolveSpreadValueIdentifier(source, initializer);
777
+ if (value.status === "refused")
778
+ return value;
779
+ deploymentProperty = { node: named.property, value: value.value };
780
+ }
781
+ else {
782
+ const evaluated = evaluateStatic(initializer, source);
783
+ if (!evaluated.ok) {
784
+ return {
785
+ status: "refused",
786
+ reason: `The default export's deployment property is not a static literal: ${evaluated.reason}`,
787
+ };
788
+ }
789
+ deploymentProperty = {
790
+ node: named.property,
791
+ value: { kind: "literal", value: evaluated.value },
792
+ };
793
+ }
794
+ deploymentIndex = index;
795
+ }
796
+ if (deploymentProperty === undefined) {
797
+ if (lastDeploymentSpreadIndex >= 0) {
798
+ return {
799
+ status: "refused",
800
+ reason: "The default export receives a deployment key through a spread; hoist that key into the declaration by hand before migrating.",
801
+ };
802
+ }
803
+ return { status: "ok", exportAssignment, literal: exported };
804
+ }
805
+ if (providerSpreadIndex < 0) {
806
+ return {
807
+ status: "refused",
808
+ reason: "The default export carries a deployment property but does not spread the built provider (or that binding is mutated afterwards), so a key hoisted into the declaration would never reach the export; reconcile by hand.",
809
+ };
810
+ }
811
+ if (providerSpreadIndex > deploymentIndex) {
812
+ return {
813
+ status: "refused",
814
+ reason: "The default export spreads the built provider after its deployment property, so the property may not be the effective deployment; move the spread first or reconcile by hand.",
815
+ };
816
+ }
817
+ if (lastDeploymentSpreadIndex >= 0) {
818
+ // After the property: it is not the effective deployment. Before it:
819
+ // removing the property would expose the spread's deployment.
820
+ return {
821
+ status: "refused",
822
+ reason: "The default export also spreads an object that carries a deployment key, so removing its deployment property would change the effective deployment; reconcile by hand.",
823
+ };
824
+ }
825
+ return { status: "ok", exportAssignment, literal: exported, deploymentProperty };
826
+ }
827
+ function duplicateExportDeployment() {
828
+ return {
829
+ status: "refused",
830
+ reason: "The default export declares `deployment` more than once; keep a single property.",
831
+ };
832
+ }
833
+ /**
834
+ * `...provider` where `provider` is the declaration's built provider: bound
835
+ * to a call of the declaration builder (`const provider = buildProvider({
836
+ * operations })`), to the `defineProvider(...)` call itself (single-phase
837
+ * shape), or that call inline. The built provider carries exactly the
838
+ * declaration's deployment key, which the transform already accounts for.
839
+ */
840
+ function isBuiltProviderSpread(expression, source, declarationCall, builderName) {
841
+ let expr = unwrapExpression(expression);
842
+ if (ts.isIdentifier(expr)) {
843
+ const declaration = findTopLevelConst(source, expr.text);
844
+ if (declaration?.initializer === undefined)
845
+ return false;
846
+ // A binding mutated after the builder call (`provider.deployment = …`)
847
+ // no longer carries exactly the declaration's deployment.
848
+ if (!identifierUsesAreReadOnly(source, expr.text, new Set(), "provider"))
849
+ return false;
850
+ expr = unwrapExpression(declaration.initializer);
851
+ }
852
+ if (expr === declarationCall)
853
+ return true;
854
+ return (builderName !== undefined &&
855
+ ts.isCallExpression(expr) &&
856
+ ts.isIdentifier(expr.expression) &&
857
+ expr.expression.text === builderName);
858
+ }
859
+ function resolveSpreadValueIdentifier(source, identifier) {
860
+ const imports = collectLegacyDeployImports(source);
861
+ if (imports.status === "ok" && imports.defaultImport === identifier.text) {
862
+ return { status: "ok", value: { kind: "deploy-import" } };
863
+ }
864
+ const declaration = findTopLevelConst(source, identifier.text);
865
+ if (declaration?.initializer !== undefined) {
866
+ // Evaluate the identifier itself so the binding's other uses are
867
+ // checked for mutation, not just its initializer text.
868
+ const evaluated = evaluateStatic(identifier, source);
869
+ if (!evaluated.ok) {
870
+ return {
871
+ status: "refused",
872
+ reason: `\`${identifier.text}\` (attached as deployment on the default export) is not a static literal: ${evaluated.reason}`,
873
+ };
874
+ }
875
+ const statement = declaration.parent.parent;
876
+ if (!ts.isVariableStatement(statement)) {
877
+ return {
878
+ status: "refused",
879
+ reason: `\`${identifier.text}\` is not declared by a plain variable statement.`,
880
+ };
881
+ }
882
+ return {
883
+ status: "ok",
884
+ value: { kind: "const", value: evaluated.value, name: identifier.text, statement },
885
+ };
886
+ }
887
+ return {
888
+ status: "refused",
889
+ reason: `\`${identifier.text}\` (attached as deployment on the default export) does not resolve to a top-level const or the ./deploy default import.`,
890
+ };
891
+ }
892
+ function removeSpreadDeploymentProperty(spread, declaration, source) {
893
+ const { exportAssignment, literal, deploymentProperty } = spread;
894
+ if (exportAssignment === undefined || literal === undefined || deploymentProperty === undefined)
895
+ return [];
896
+ const remaining = literal.properties.filter((property) => property !== deploymentProperty.node);
897
+ const onlySpread = remaining[0];
898
+ // Folding re-emits the spread's source text; when the declaration literal
899
+ // lives inside it (`{ ...defineProvider({...}), deployment }`) that text
900
+ // would overlap the declaration edits, so only the property is removed.
901
+ const declarationInsideSpread = onlySpread !== undefined &&
902
+ declaration.getStart(source) >= onlySpread.getStart(source) &&
903
+ declaration.getEnd() <= onlySpread.getEnd();
904
+ if (remaining.length === 1 &&
905
+ onlySpread !== undefined &&
906
+ ts.isSpreadAssignment(onlySpread) &&
907
+ !declarationInsideSpread) {
908
+ // `export default { ...provider }` folds to `export default provider;`.
909
+ return [
910
+ {
911
+ start: exportAssignment.expression.getStart(source),
912
+ end: exportAssignment.expression.getEnd(),
913
+ text: onlySpread.expression.getText(source),
914
+ },
915
+ ];
916
+ }
917
+ return [removeObjectProperty(deploymentProperty.node, literal, source)];
918
+ }
919
+ function collectLegacyDeployImports(source) {
920
+ let defaultImport;
921
+ const removable = [];
922
+ const typeOnlyNames = [];
923
+ const collectNamed = (bindings) => {
924
+ if (bindings === undefined)
925
+ return;
926
+ if (ts.isNamedImports(bindings)) {
927
+ for (const element of bindings.elements)
928
+ typeOnlyNames.push(element.name.text);
929
+ }
930
+ else {
931
+ typeOnlyNames.push(bindings.name.text);
932
+ }
933
+ };
934
+ for (const statement of source.statements) {
935
+ if (ts.isExportDeclaration(statement)) {
936
+ const specifier = statement.moduleSpecifier;
937
+ if (specifier !== undefined &&
938
+ ts.isStringLiteralLike(specifier) &&
939
+ isDeployModuleSpecifier(specifier.text, INDEX_VIRTUAL_DIRECTORY, DEPLOY_MODULE_VIRTUAL_PATH)) {
940
+ return {
941
+ status: "refused",
942
+ reason: "index.ts re-exports from ./deploy; remove that re-export by hand before deleting the file.",
943
+ };
944
+ }
945
+ continue;
946
+ }
947
+ if (!ts.isImportDeclaration(statement))
948
+ continue;
949
+ const specifier = statement.moduleSpecifier;
950
+ if (!ts.isStringLiteralLike(specifier) ||
951
+ !isDeployModuleSpecifier(specifier.text, INDEX_VIRTUAL_DIRECTORY, DEPLOY_MODULE_VIRTUAL_PATH)) {
952
+ continue;
953
+ }
954
+ const clause = statement.importClause;
955
+ if (clause === undefined) {
956
+ return {
957
+ status: "refused",
958
+ reason: "index.ts has a side-effect import of ./deploy; remove it by hand.",
959
+ };
960
+ }
961
+ if (clause.isTypeOnly) {
962
+ if (clause.name !== undefined)
963
+ typeOnlyNames.push(clause.name.text);
964
+ collectNamed(clause.namedBindings);
965
+ removable.push(statement);
966
+ continue;
967
+ }
968
+ if (clause.namedBindings !== undefined) {
969
+ if (ts.isNamedImports(clause.namedBindings) &&
970
+ clause.namedBindings.elements.every((element) => element.isTypeOnly)) {
971
+ // `{ type Config }` alongside the default binding, or alone.
972
+ collectNamed(clause.namedBindings);
973
+ if (clause.name === undefined) {
974
+ removable.push(statement);
975
+ continue;
976
+ }
977
+ }
978
+ else {
979
+ return {
980
+ status: "refused",
981
+ reason: "index.ts imports named or namespace bindings from ./deploy; only the default export can be hoisted.",
982
+ };
983
+ }
984
+ }
985
+ if (clause.name !== undefined) {
986
+ if (defaultImport !== undefined) {
987
+ return { status: "refused", reason: "index.ts imports ./deploy more than once." };
988
+ }
989
+ defaultImport = clause.name.text;
990
+ removable.push(statement);
991
+ }
992
+ }
993
+ return {
994
+ status: "ok",
995
+ ...(defaultImport === undefined ? {} : { defaultImport }),
996
+ removable,
997
+ typeOnlyNames,
998
+ };
999
+ }
1000
+ function referencesLegacyDeployModule(source) {
1001
+ return sourceImportsModule(source, INDEX_VIRTUAL_DIRECTORY, DEPLOY_MODULE_VIRTUAL_PATH);
1002
+ }
1003
+ /**
1004
+ * Whether `source` (located in `fromDirectory`) imports the module at
1005
+ * `moduleAbsolutePath` (extension-less) through any specifier position:
1006
+ * static import/re-export, dynamic `import()`, `require()`, `import x =
1007
+ * require()`, or an `import("…")` type. Template-literal specifiers count.
1008
+ */
1009
+ function sourceImportsModule(source, fromDirectory, moduleAbsolutePath) {
1010
+ let found = false;
1011
+ const visit = (node) => {
1012
+ if (found)
1013
+ return;
1014
+ if (ts.isStringLiteralLike(node) &&
1015
+ isModuleSpecifierPosition(node) &&
1016
+ isDeployModuleSpecifier(node.text, fromDirectory, moduleAbsolutePath)) {
1017
+ found = true;
1018
+ return;
1019
+ }
1020
+ ts.forEachChild(node, visit);
1021
+ };
1022
+ visit(source);
1023
+ return found;
1024
+ }
1025
+ function isModuleSpecifierPosition(node) {
1026
+ const parent = node.parent;
1027
+ return (ts.isImportDeclaration(parent) ||
1028
+ ts.isExportDeclaration(parent) ||
1029
+ ts.isExternalModuleReference(parent) ||
1030
+ (ts.isCallExpression(parent) &&
1031
+ parent.arguments[0] === node &&
1032
+ (parent.expression.kind === ts.SyntaxKind.ImportKeyword ||
1033
+ (ts.isIdentifier(parent.expression) && parent.expression.text === "require"))) ||
1034
+ (ts.isLiteralTypeNode(parent) && ts.isImportTypeNode(parent.parent)));
1035
+ }
1036
+ /** A relative specifier that resolves (extension-less) to `moduleAbsolutePath`. */
1037
+ function isDeployModuleSpecifier(specifier, fromDirectory, moduleAbsolutePath) {
1038
+ if (!specifier.startsWith("."))
1039
+ return false;
1040
+ const resolved = path.resolve(fromDirectory, specifier);
1041
+ // Strip the extension from the file name only; a directory segment such
1042
+ // as `/srv/provider.ts/` must survive untouched.
1043
+ const withoutExtension = path.join(path.dirname(resolved), path.basename(resolved).replace(SOURCE_EXTENSION, ""));
1044
+ return withoutExtension === moduleAbsolutePath;
1045
+ }
1046
+ function evaluateStatic(node, source, seen = new Set()) {
1047
+ const expression = unwrapExpression(node);
1048
+ if (ts.isObjectLiteralExpression(expression)) {
1049
+ const value = {};
1050
+ for (const property of expression.properties) {
1051
+ if (ts.isSpreadAssignment(property)) {
1052
+ return { ok: false, reason: "object literal contains a spread" };
1053
+ }
1054
+ const name = property.name;
1055
+ if (name === undefined ||
1056
+ !(ts.isIdentifier(name) || ts.isStringLiteralLike(name) || ts.isNumericLiteral(name))) {
1057
+ return { ok: false, reason: "object literal has a computed or non-static property name" };
1058
+ }
1059
+ if (Object.hasOwn(value, name.text)) {
1060
+ return { ok: false, reason: `object literal repeats property \`${name.text}\`` };
1061
+ }
1062
+ let element;
1063
+ if (ts.isPropertyAssignment(property)) {
1064
+ element = evaluateStatic(property.initializer, source, seen);
1065
+ }
1066
+ else if (ts.isShorthandPropertyAssignment(property)) {
1067
+ element = evaluateStatic(property.name, source, seen);
1068
+ }
1069
+ else {
1070
+ return { ok: false, reason: `property \`${name.text}\` is a method or accessor` };
1071
+ }
1072
+ if (!element.ok)
1073
+ return element;
1074
+ value[name.text] = element.value;
1075
+ }
1076
+ return { ok: true, value };
1077
+ }
1078
+ if (ts.isArrayLiteralExpression(expression)) {
1079
+ const items = [];
1080
+ for (const element of expression.elements) {
1081
+ if (ts.isSpreadElement(element) || ts.isOmittedExpression(element)) {
1082
+ return { ok: false, reason: "array literal contains a spread or hole" };
1083
+ }
1084
+ const item = evaluateStatic(element, source, seen);
1085
+ if (!item.ok)
1086
+ return item;
1087
+ items.push(item.value);
1088
+ }
1089
+ return { ok: true, value: items };
1090
+ }
1091
+ if (ts.isStringLiteralLike(expression))
1092
+ return { ok: true, value: expression.text };
1093
+ if (ts.isNumericLiteral(expression))
1094
+ return { ok: true, value: Number(expression.text) };
1095
+ if (ts.isPrefixUnaryExpression(expression) &&
1096
+ expression.operator === ts.SyntaxKind.MinusToken &&
1097
+ ts.isNumericLiteral(expression.operand)) {
1098
+ return { ok: true, value: -Number(expression.operand.text) };
1099
+ }
1100
+ if (expression.kind === ts.SyntaxKind.TrueKeyword)
1101
+ return { ok: true, value: true };
1102
+ if (expression.kind === ts.SyntaxKind.FalseKeyword)
1103
+ return { ok: true, value: false };
1104
+ if (expression.kind === ts.SyntaxKind.NullKeyword)
1105
+ return { ok: true, value: null };
1106
+ if (ts.isIdentifier(expression)) {
1107
+ if (expression.text === "undefined")
1108
+ return { ok: true, value: undefined };
1109
+ if (seen.has(expression.text)) {
1110
+ return { ok: false, reason: `\`${expression.text}\` is defined in terms of itself` };
1111
+ }
1112
+ if (!isTopLevelNode(expression)) {
1113
+ return {
1114
+ ok: false,
1115
+ reason: `\`${expression.text}\` is referenced inside a nested scope where a local binding could shadow the top-level const`,
1116
+ };
1117
+ }
1118
+ const declaration = findTopLevelConst(source, expression.text);
1119
+ if (declaration?.initializer === undefined) {
1120
+ return {
1121
+ ok: false,
1122
+ reason: `\`${expression.text}\` does not resolve to a top-level const with a literal initializer`,
1123
+ };
1124
+ }
1125
+ if (!identifierUsesAreReadOnly(source, expression.text)) {
1126
+ return {
1127
+ ok: false,
1128
+ reason: `\`${expression.text}\` is mutated, aliased, or passed to a call after its initializer, so the initializer may not be its effective value`,
1129
+ };
1130
+ }
1131
+ const next = new Set(seen);
1132
+ next.add(expression.text);
1133
+ return evaluateStatic(declaration.initializer, source, next);
1134
+ }
1135
+ return { ok: false, reason: `${ts.SyntaxKind[expression.kind]} is not a static literal` };
1136
+ }
1137
+ function unwrapExpression(node) {
1138
+ let current = node;
1139
+ for (;;) {
1140
+ if (ts.isParenthesizedExpression(current))
1141
+ current = current.expression;
1142
+ else if (ts.isAsExpression(current))
1143
+ current = current.expression;
1144
+ else if (ts.isSatisfiesExpression(current))
1145
+ current = current.expression;
1146
+ else if (ts.isTypeAssertionExpression(current))
1147
+ current = current.expression;
1148
+ else if (ts.isNonNullExpression(current))
1149
+ current = current.expression;
1150
+ else
1151
+ return current;
1152
+ }
1153
+ }
1154
+ function findTopLevelConst(source, name) {
1155
+ for (const statement of source.statements) {
1156
+ if (!ts.isVariableStatement(statement))
1157
+ continue;
1158
+ if ((statement.declarationList.flags & ts.NodeFlags.Const) === 0)
1159
+ continue;
1160
+ for (const declaration of statement.declarationList.declarations) {
1161
+ if (ts.isIdentifier(declaration.name) && declaration.name.text === name) {
1162
+ return statement.declarationList.declarations.length === 1 ? declaration : undefined;
1163
+ }
1164
+ }
1165
+ }
1166
+ return undefined;
1167
+ }
1168
+ /** Whether an identifier node is a reference (not a declared or property name). */
1169
+ function isIdentifierReference(node) {
1170
+ const parent = node.parent;
1171
+ const isDeclaredOrPropertyName = (ts.isPropertyAssignment(parent) && parent.name === node) ||
1172
+ (ts.isPropertyAccessExpression(parent) && parent.name === node) ||
1173
+ (ts.isMethodDeclaration(parent) && parent.name === node) ||
1174
+ (ts.isPropertySignature(parent) && parent.name === node) ||
1175
+ (ts.isImportSpecifier(parent) && parent.propertyName === node) ||
1176
+ ts.isImportClause(parent) ||
1177
+ (ts.isVariableDeclaration(parent) && parent.name === node);
1178
+ return !isDeclaredOrPropertyName;
1179
+ }
1180
+ /** Identifier references to `name` in expression position (property names excluded). */
1181
+ function countIdentifierReferences(source, name) {
1182
+ let count = 0;
1183
+ const visit = (node) => {
1184
+ if (ts.isIdentifier(node) && node.text === name && isIdentifierReference(node))
1185
+ count += 1;
1186
+ ts.forEachChild(node, visit);
1187
+ };
1188
+ visit(source);
1189
+ return count;
1190
+ }
1191
+ function identifierUsesAreReadOnly(source, name, seen = new Set(), kind = "value") {
1192
+ if (seen.has(name))
1193
+ return true;
1194
+ const next = new Set(seen);
1195
+ next.add(name);
1196
+ let readOnly = true;
1197
+ const visit = (node) => {
1198
+ if (!readOnly)
1199
+ return;
1200
+ if (ts.isIdentifier(node) && node.text === name && isIdentifierReference(node)) {
1201
+ let use = node;
1202
+ while (ts.isParenthesizedExpression(use.parent) ||
1203
+ ts.isAsExpression(use.parent) ||
1204
+ ts.isSatisfiesExpression(use.parent) ||
1205
+ ts.isNonNullExpression(use.parent)) {
1206
+ use = use.parent;
1207
+ }
1208
+ const parent = use.parent;
1209
+ let plainRead = ts.isExportAssignment(parent) || isDefineProviderArgument(parent, use);
1210
+ if (!plainRead &&
1211
+ kind === "provider" &&
1212
+ (ts.isPropertyAccessExpression(parent) || ts.isElementAccessExpression(parent)) &&
1213
+ parent.expression === use) {
1214
+ // `provider.operations.x.handler(...)` is a read; `provider.x = …`
1215
+ // is not, and reaching `.deployment` directly is refused outright.
1216
+ plainRead = memberAccessIsRead(parent);
1217
+ }
1218
+ if (!plainRead &&
1219
+ (ts.isSpreadAssignment(parent) ||
1220
+ (ts.isPropertyAssignment(parent) && parent.initializer === use) ||
1221
+ ts.isShorthandPropertyAssignment(parent)) &&
1222
+ ts.isObjectLiteralExpression(parent.parent)) {
1223
+ // Placing the object (or a shallow copy sharing its nested
1224
+ // objects) inside another literal is only a read when that
1225
+ // literal itself cannot be reached and mutated afterwards.
1226
+ plainRead = literalIsAnchored(parent.parent, source, next);
1227
+ }
1228
+ if (!plainRead)
1229
+ readOnly = false;
1230
+ return;
1231
+ }
1232
+ ts.forEachChild(node, visit);
1233
+ };
1234
+ visit(source);
1235
+ return readOnly;
1236
+ }
1237
+ /**
1238
+ * A member-access chain rooted at the binding is a read unless it (or the
1239
+ * chain it starts) is assigned, deleted, or updated, or unless its first
1240
+ * member is `deployment` itself (or not statically nameable): anything
1241
+ * touching the deployment object directly is left to a human.
1242
+ */
1243
+ function memberAccessIsRead(access) {
1244
+ let firstMember;
1245
+ if (ts.isPropertyAccessExpression(access))
1246
+ firstMember = access.name.text;
1247
+ else if (ts.isStringLiteralLike(access.argumentExpression)) {
1248
+ firstMember = access.argumentExpression.text;
1249
+ }
1250
+ if (firstMember === undefined || firstMember === DEPLOYMENT_PROPERTY_NAME)
1251
+ return false;
1252
+ let chain = access;
1253
+ for (;;) {
1254
+ const parent = chain.parent;
1255
+ if (parent === undefined)
1256
+ return true;
1257
+ if ((ts.isPropertyAccessExpression(parent) || ts.isElementAccessExpression(parent)) &&
1258
+ parent.expression === chain) {
1259
+ chain = parent;
1260
+ continue;
1261
+ }
1262
+ if (ts.isNonNullExpression(parent) || ts.isParenthesizedExpression(parent)) {
1263
+ chain = parent;
1264
+ continue;
1265
+ }
1266
+ if (ts.isBinaryExpression(parent) &&
1267
+ parent.left === chain &&
1268
+ parent.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
1269
+ parent.operatorToken.kind <= ts.SyntaxKind.LastAssignment) {
1270
+ return false;
1271
+ }
1272
+ return !(ts.isDeleteExpression(parent) ||
1273
+ ts.isPrefixUnaryExpression(parent) ||
1274
+ ts.isPostfixUnaryExpression(parent));
1275
+ }
1276
+ }
1277
+ function isDefineProviderArgument(parent, use) {
1278
+ return (ts.isCallExpression(parent) &&
1279
+ ts.isIdentifier(parent.expression) &&
1280
+ parent.expression.text === "defineProvider" &&
1281
+ parent.arguments.some((argument) => argument === use));
1282
+ }
1283
+ /**
1284
+ * An object literal is anchored when, climbing through enclosing literals
1285
+ * and wrappers, it ends up as the default export, a `defineProvider()`
1286
+ * argument, or the initializer of a top-level const whose own uses are
1287
+ * read-only. Anything else (a `holder` that is later dereferenced, a call
1288
+ * argument, a return value) may alias and mutate the contents.
1289
+ */
1290
+ function literalIsAnchored(literal, source, seen) {
1291
+ let node = literal;
1292
+ for (;;) {
1293
+ const parent = node.parent;
1294
+ if (parent === undefined)
1295
+ return false;
1296
+ if (ts.isParenthesizedExpression(parent) ||
1297
+ ts.isAsExpression(parent) ||
1298
+ ts.isSatisfiesExpression(parent) ||
1299
+ ts.isNonNullExpression(parent) ||
1300
+ ts.isPropertyAssignment(parent) ||
1301
+ ts.isSpreadAssignment(parent) ||
1302
+ ts.isObjectLiteralExpression(parent) ||
1303
+ ts.isArrayLiteralExpression(parent)) {
1304
+ node = parent;
1305
+ continue;
1306
+ }
1307
+ if (ts.isExportAssignment(parent))
1308
+ return true;
1309
+ if (isDefineProviderArgument(parent, node))
1310
+ return true;
1311
+ if (ts.isVariableDeclaration(parent) &&
1312
+ parent.initializer === node &&
1313
+ ts.isIdentifier(parent.name) &&
1314
+ ts.isVariableDeclarationList(parent.parent) &&
1315
+ (parent.parent.flags & ts.NodeFlags.Const) !== 0 &&
1316
+ ts.isVariableStatement(parent.parent.parent) &&
1317
+ parent.parent.parent.parent === source) {
1318
+ return identifierUsesAreReadOnly(source, parent.name.text, seen);
1319
+ }
1320
+ return false;
1321
+ }
1322
+ }
1323
+ function hasExportModifier(statement) {
1324
+ return (ts
1325
+ .getModifiers(statement)
1326
+ ?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) === true);
1327
+ }
1328
+ // ── Emission ──────────────────────────────────────────────────────────────
1329
+ function insertDeploymentProperty(declaration, intent, source) {
1330
+ const anchor = findAnchorProperty(declaration, "runtime") ??
1331
+ findAnchorProperty(declaration, "version") ??
1332
+ findAnchorProperty(declaration, "id") ??
1333
+ declaration.properties[0];
1334
+ const text = source.getFullText();
1335
+ if (anchor === undefined) {
1336
+ // Empty declaration literal: `{}`.
1337
+ const indent = lineIndentation(source, declaration.getStart(source));
1338
+ const unit = indent.startsWith("\t") ? "\t" : " ";
1339
+ const inner = `${indent}${unit}`;
1340
+ const body = renderPropertyLines(intent, inner, unit);
1341
+ return {
1342
+ start: declaration.getStart(source) + 1,
1343
+ end: declaration.getEnd() - 1,
1344
+ text: `\n${body}\n${indent}`,
1345
+ };
1346
+ }
1347
+ const indent = lineIndentation(source, anchor.getStart(source));
1348
+ const unit = indentUnitFor(source, declaration, indent);
1349
+ // Insert after the anchor's trailing comma when it has one; otherwise
1350
+ // give the anchor a comma so the literal stays well-formed.
1351
+ let cursor = anchor.getEnd();
1352
+ while (cursor < text.length && /[ \t]/.test(text.charAt(cursor)))
1353
+ cursor += 1;
1354
+ const hasComma = text.charAt(cursor) === ",";
1355
+ const insertAt = hasComma ? cursor + 1 : anchor.getEnd();
1356
+ const prefix = hasComma ? "" : ",";
1357
+ return {
1358
+ start: insertAt,
1359
+ end: insertAt,
1360
+ text: `${prefix}\n${renderPropertyLines(intent, indent, unit)}`,
1361
+ };
1362
+ }
1363
+ function renderPropertyLines(intent, indent, unit) {
1364
+ const lines = [];
1365
+ if (intent.runtime !== undefined)
1366
+ lines.push(`${indent}${DEPLOYMENT_RUNTIME_AXIS_COMMENT}`);
1367
+ lines.push(`${indent}${DEPLOYMENT_PROPERTY_NAME}: ${formatIntentLiteral(intent, indent, unit)},`);
1368
+ return lines.join("\n");
1369
+ }
1370
+ function findAnchorProperty(declaration, name) {
1371
+ for (const property of declaration.properties) {
1372
+ if (ts.isSpreadAssignment(property))
1373
+ continue;
1374
+ const propertyName = property.name;
1375
+ if (propertyName !== undefined &&
1376
+ (ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName)) &&
1377
+ propertyName.text === name) {
1378
+ return property;
1379
+ }
1380
+ }
1381
+ return undefined;
1382
+ }
1383
+ /**
1384
+ * Format the key's object literal starting at column `indent.length +
1385
+ * "deployment: ".length`: inline when it fits the line width, otherwise
1386
+ * expanded one property per line (recursively), trailing commas included —
1387
+ * the same decisions a formatter would keep.
1388
+ */
1389
+ export function formatIntentLiteral(intent, indent, unit) {
1390
+ const ordered = {};
1391
+ for (const key of DEPLOYMENT_INTENT_FIELD_ORDER) {
1392
+ if (intent[key] !== undefined)
1393
+ ordered[key] = intent[key];
1394
+ }
1395
+ return formatValue(ordered, indent, unit, `${DEPLOYMENT_PROPERTY_NAME}: `.length, 1);
1396
+ }
1397
+ function formatValue(value, indent, unit, prefixWidth, suffixWidth) {
1398
+ if (Array.isArray(value)) {
1399
+ return `[${value.map((item) => formatValue(item, indent, unit, 0, 0)).join(", ")}]`;
1400
+ }
1401
+ if (typeof value === "object" && value !== null) {
1402
+ const entries = Object.entries(value).filter(([, item]) => item !== undefined);
1403
+ if (entries.length === 0)
1404
+ return "{}";
1405
+ const inline = `{ ${entries
1406
+ .map(([key, item]) => `${key}: ${formatValue(item, indent, unit, 0, 0)}`)
1407
+ .join(", ")} }`;
1408
+ if (indent.length + prefixWidth + inline.length + suffixWidth <= LINE_WIDTH)
1409
+ return inline;
1410
+ const inner = `${indent}${unit}`;
1411
+ const lines = entries.map(([key, item]) => `${inner}${key}: ${formatValue(item, inner, unit, `${key}: `.length, 1)},`);
1412
+ return `{\n${lines.join("\n")}\n${indent}}`;
1413
+ }
1414
+ if (typeof value === "string")
1415
+ return JSON.stringify(value);
1416
+ return String(value);
1417
+ }
1418
+ function lineIndentation(source, position) {
1419
+ const text = source.getFullText();
1420
+ const lineStart = text.lastIndexOf("\n", position - 1) + 1;
1421
+ const match = /^[ \t]*/.exec(text.slice(lineStart, position));
1422
+ return match?.[0] ?? "";
1423
+ }
1424
+ function indentUnitFor(source, literal, propertyIndent) {
1425
+ if (propertyIndent.startsWith("\t"))
1426
+ return "\t";
1427
+ const literalIndent = lineIndentation(source, literal.getStart(source));
1428
+ if (propertyIndent.length > literalIndent.length && propertyIndent.startsWith(literalIndent)) {
1429
+ return propertyIndent.slice(literalIndent.length);
1430
+ }
1431
+ return propertyIndent.length > 0 ? propertyIndent : " ";
1432
+ }
1433
+ function parseSource(fileName, text) {
1434
+ return ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest,
1435
+ /* setParentNodes */ true, ts.ScriptKind.TS);
1436
+ }
1437
+ function collectDefineProviderCalls(source) {
1438
+ const calls = [];
1439
+ const visit = (node) => {
1440
+ if (ts.isCallExpression(node) &&
1441
+ ts.isIdentifier(node.expression) &&
1442
+ node.expression.text === "defineProvider") {
1443
+ calls.push(node);
1444
+ }
1445
+ ts.forEachChild(node, visit);
1446
+ };
1447
+ visit(source);
1448
+ return calls;
1449
+ }
1450
+ function findDefaultExport(source) {
1451
+ for (const statement of source.statements) {
1452
+ if (ts.isExportAssignment(statement) && statement.isExportEquals !== true)
1453
+ return statement;
1454
+ }
1455
+ return undefined;
1456
+ }
1457
+ /** Remove one property from an object literal, absorbing a neighboring comma. */
1458
+ function removeObjectProperty(property, literal, source) {
1459
+ const properties = literal.properties;
1460
+ const index = properties.indexOf(property);
1461
+ const text = source.getFullText();
1462
+ const start = property.getFullStart();
1463
+ let end = property.getEnd();
1464
+ let cursor = end;
1465
+ while (cursor < text.length && /\s/.test(text.charAt(cursor)))
1466
+ cursor += 1;
1467
+ if (text.charAt(cursor) === ",") {
1468
+ end = cursor + 1;
1469
+ }
1470
+ else if (index > 0) {
1471
+ const previous = properties[index - 1];
1472
+ if (previous !== undefined) {
1473
+ let back = previous.getEnd();
1474
+ while (back < text.length && /\s/.test(text.charAt(back)))
1475
+ back += 1;
1476
+ if (text.charAt(back) === ",")
1477
+ return { start: back, end, text: "" };
1478
+ }
1479
+ }
1480
+ return { start, end, text: "" };
1481
+ }
1482
+ /**
1483
+ * Remove a statement's own lines (from the start of its first line through
1484
+ * the line break after it), keeping surrounding blank lines and any comment
1485
+ * block above it intact.
1486
+ */
1487
+ function removeStatement(statement, source) {
1488
+ const text = source.getFullText();
1489
+ const statementStart = statement.getStart(source);
1490
+ const lineStart = text.lastIndexOf("\n", statementStart - 1) + 1;
1491
+ // Only take the whole line when the statement owns it; a statement that
1492
+ // shares its line with an earlier one is cut out in place.
1493
+ const ownsLine = /^[ \t]*$/.test(text.slice(lineStart, statementStart));
1494
+ let start = ownsLine ? lineStart : statementStart;
1495
+ let end = statement.getEnd();
1496
+ if (ownsLine) {
1497
+ if (text.charAt(end) === "\r")
1498
+ end += 1;
1499
+ if (text.charAt(end) === "\n")
1500
+ end += 1;
1501
+ }
1502
+ else {
1503
+ // Absorb the separating whitespace before the statement so the
1504
+ // neighbor keeps its own line ending intact.
1505
+ while (start > lineStart && /[ \t]/.test(text.charAt(start - 1)))
1506
+ start -= 1;
1507
+ }
1508
+ return { start, end, text: "" };
1509
+ }
1510
+ function firstSyntaxError(source) {
1511
+ const diagnostics = source
1512
+ .parseDiagnostics;
1513
+ const first = diagnostics?.[0];
1514
+ if (first === undefined)
1515
+ return undefined;
1516
+ const message = ts.flattenDiagnosticMessageText(first.messageText, " ");
1517
+ const { line } = source.getLineAndCharacterOfPosition(first.start);
1518
+ return `${message} (line ${line + 1})`;
1519
+ }
1520
+ function editsOverlap(edits) {
1521
+ const ordered = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
1522
+ for (let index = 1; index < ordered.length; index += 1) {
1523
+ const previous = ordered[index - 1];
1524
+ const current = ordered[index];
1525
+ if (previous !== undefined && current !== undefined && current.start < previous.end)
1526
+ return true;
1527
+ }
1528
+ return false;
1529
+ }
1530
+ /** Whether `node` sits directly in the module scope (no enclosing function, class, or block). */
1531
+ function isTopLevelNode(node) {
1532
+ for (let current = node.parent; current !== undefined; current = current.parent) {
1533
+ if (ts.isFunctionLike(current) ||
1534
+ ts.isClassLike(current) ||
1535
+ ts.isBlock(current) ||
1536
+ ts.isModuleBlock(current)) {
1537
+ return false;
1538
+ }
1539
+ }
1540
+ return true;
1541
+ }
1542
+ function applyEdits(text, edits) {
1543
+ const ordered = [...edits].sort((a, b) => b.start - a.start || b.end - a.end);
1544
+ let output = text;
1545
+ for (const edit of ordered) {
1546
+ output = output.slice(0, edit.start) + edit.text + output.slice(edit.end);
1547
+ }
1548
+ return output;
1549
+ }
1550
+ function describeError(error) {
1551
+ return error instanceof Error ? error.message : String(error);
1552
+ }