@mysten-incubation/devstack 0.0.1 → 0.1.1

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 (838) hide show
  1. package/README.md +8 -4
  2. package/dashboard-ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  3. package/dashboard-ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  4. package/dashboard-ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  5. package/dashboard-ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  6. package/dashboard-ui/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
  7. package/dashboard-ui/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
  8. package/dashboard-ui/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
  9. package/dashboard-ui/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
  10. package/dashboard-ui/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
  11. package/dashboard-ui/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
  12. package/dashboard-ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  13. package/dashboard-ui/assets/index-Bmi1UtAg.js +1356 -0
  14. package/dashboard-ui/assets/index-D5EShVt4.js +3 -0
  15. package/dashboard-ui/assets/index-Deml9drg.css +1 -0
  16. package/dashboard-ui/index.html +13 -0
  17. package/dist/api/define-capabilities.d.mts +14 -3
  18. package/dist/api/define-capabilities.mjs +18 -1
  19. package/dist/api/define-capabilities.mjs.map +1 -1
  20. package/dist/api/define-devstack.d.mts +1 -1
  21. package/dist/api/define-devstack.mjs +6 -26
  22. package/dist/api/define-devstack.mjs.map +1 -1
  23. package/dist/api/define-plugin.d.mts +1 -0
  24. package/dist/api/define-plugin.mjs +2 -0
  25. package/dist/api/inference-network.d.mts +132 -0
  26. package/dist/api/inference-network.mjs +105 -16
  27. package/dist/api/inference-network.mjs.map +1 -1
  28. package/dist/api/plugin-errors.d.mts +7 -0
  29. package/dist/api/run-stack.d.mts +20 -9
  30. package/dist/api/run-stack.mjs +56 -29
  31. package/dist/api/run-stack.mjs.map +1 -1
  32. package/dist/build-integrations/playwright/config.d.mts +6 -4
  33. package/dist/build-integrations/playwright/config.mjs +5 -3
  34. package/dist/build-integrations/playwright/config.mjs.map +1 -1
  35. package/dist/build-integrations/playwright/errors.d.mts +1 -1
  36. package/dist/build-integrations/playwright/global-setup.d.mts +43 -17
  37. package/dist/build-integrations/playwright/global-setup.mjs +250 -13
  38. package/dist/build-integrations/playwright/global-setup.mjs.map +1 -1
  39. package/dist/build-integrations/playwright/index.d.mts +5 -3
  40. package/dist/build-integrations/playwright/index.mjs +4 -2
  41. package/dist/build-integrations/playwright/stack-context.d.mts +0 -1
  42. package/dist/build-integrations/playwright/stack-context.mjs +24 -63
  43. package/dist/build-integrations/playwright/stack-context.mjs.map +1 -1
  44. package/dist/build-integrations/playwright/wallet-context.d.mts +7 -10
  45. package/dist/build-integrations/playwright/wallet-context.mjs +8 -13
  46. package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
  47. package/dist/build-integrations/runtime/cold-start-url.d.mts +5 -0
  48. package/dist/build-integrations/runtime/cold-start-url.mjs +4 -3
  49. package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -1
  50. package/dist/build-integrations/runtime/conventional-routes.d.mts +51 -0
  51. package/dist/build-integrations/runtime/conventional-routes.mjs +92 -0
  52. package/dist/build-integrations/runtime/conventional-routes.mjs.map +1 -0
  53. package/dist/build-integrations/runtime/discover.d.mts +38 -30
  54. package/dist/build-integrations/runtime/discover.mjs +73 -70
  55. package/dist/build-integrations/runtime/discover.mjs.map +1 -1
  56. package/dist/build-integrations/runtime/endpoint-registry.d.mts +3 -1
  57. package/dist/build-integrations/runtime/endpoint-registry.mjs +3 -1
  58. package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -1
  59. package/dist/build-integrations/runtime/index.d.mts +7 -2
  60. package/dist/build-integrations/runtime/index.mjs +9 -2
  61. package/dist/build-integrations/runtime/manifest-types.d.mts +2 -0
  62. package/dist/build-integrations/runtime/manifest-types.mjs +3 -0
  63. package/dist/build-integrations/runtime/playwright-stack-context-slot.d.mts +29 -0
  64. package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs +10 -0
  65. package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs.map +1 -0
  66. package/dist/build-integrations/runtime/read-stack-context.d.mts +1 -1
  67. package/dist/build-integrations/runtime/read-stack-context.mjs +10 -4
  68. package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -1
  69. package/dist/build-integrations/runtime/resolve-discovery-env.d.mts +48 -0
  70. package/dist/build-integrations/runtime/resolve-discovery-env.mjs +42 -0
  71. package/dist/build-integrations/runtime/resolve-discovery-env.mjs.map +1 -0
  72. package/dist/build-integrations/runtime/stack-context.d.mts +1 -1
  73. package/dist/build-integrations/runtime/wallet-paths.mjs +2 -0
  74. package/dist/build-integrations/vite/index.d.mts +51 -0
  75. package/dist/build-integrations/vite/index.mjs +73 -0
  76. package/dist/build-integrations/vite/index.mjs.map +1 -0
  77. package/dist/build-integrations/vitest/env.d.mts +4 -3
  78. package/dist/build-integrations/vitest/env.mjs +6 -4
  79. package/dist/build-integrations/vitest/env.mjs.map +1 -1
  80. package/dist/build-integrations/vitest/errors.d.mts +25 -4
  81. package/dist/build-integrations/vitest/errors.mjs +15 -2
  82. package/dist/build-integrations/vitest/errors.mjs.map +1 -1
  83. package/dist/build-integrations/vitest/index.d.mts +2 -2
  84. package/dist/build-integrations/vitest/index.mjs +3 -3
  85. package/dist/build-integrations/vitest/setup.d.mts +4 -2
  86. package/dist/build-integrations/vitest/setup.mjs +2 -2
  87. package/dist/build-integrations/vitest/setup.mjs.map +1 -1
  88. package/dist/build-integrations/vitest/stack-context.d.mts +0 -1
  89. package/dist/build-integrations/vitest/stack-context.mjs +33 -13
  90. package/dist/build-integrations/vitest/stack-context.mjs.map +1 -1
  91. package/dist/cli/bail.mjs +23 -0
  92. package/dist/cli/bail.mjs.map +1 -0
  93. package/dist/{surfaces/cli/commands → cli}/doctor-probes.mjs +26 -16
  94. package/dist/cli/doctor-probes.mjs.map +1 -0
  95. package/dist/cli/main.d.mts +15 -1
  96. package/dist/cli/main.mjs +166 -482
  97. package/dist/cli/main.mjs.map +1 -1
  98. package/dist/cli/prune-direct.mjs +31 -201
  99. package/dist/cli/prune-direct.mjs.map +1 -1
  100. package/dist/cli/snapshot-reader.mjs +14 -11
  101. package/dist/cli/snapshot-reader.mjs.map +1 -1
  102. package/dist/cli/up-lifecycle.mjs +4 -1
  103. package/dist/cli/up-lifecycle.mjs.map +1 -1
  104. package/dist/cli/wirings/apply.mjs +95 -0
  105. package/dist/cli/wirings/apply.mjs.map +1 -0
  106. package/dist/cli/wirings/build-verb-layers.mjs +42 -0
  107. package/dist/cli/wirings/build-verb-layers.mjs.map +1 -0
  108. package/dist/cli/wirings/config-loader.mjs +69 -0
  109. package/dist/cli/wirings/config-loader.mjs.map +1 -0
  110. package/dist/cli/wirings/engine-command.mjs +30 -0
  111. package/dist/cli/wirings/engine-command.mjs.map +1 -0
  112. package/dist/cli/wirings/identity.mjs +72 -0
  113. package/dist/cli/wirings/identity.mjs.map +1 -0
  114. package/dist/cli/wirings/provide-file-system.mjs +7 -0
  115. package/dist/cli/wirings/provide-file-system.mjs.map +1 -0
  116. package/dist/cli/wirings/snapshot.mjs +177 -0
  117. package/dist/cli/wirings/snapshot.mjs.map +1 -0
  118. package/dist/cli/wirings/up.mjs +287 -0
  119. package/dist/cli/wirings/up.mjs.map +1 -0
  120. package/dist/cli/wirings/wipe.mjs +40 -0
  121. package/dist/cli/wirings/wipe.mjs.map +1 -0
  122. package/dist/contracts/capability-decl.d.mts +0 -2
  123. package/dist/contracts/codegenable.d.mts +43 -0
  124. package/dist/contracts/container-runtime.d.mts +26 -1
  125. package/dist/contracts/faucet-strategy.d.mts +25 -0
  126. package/dist/contracts/funding-strategy.d.mts +42 -0
  127. package/dist/contracts/plugin-expander.mjs +60 -0
  128. package/dist/contracts/plugin-expander.mjs.map +1 -0
  129. package/dist/contracts/projection.d.mts +5 -3
  130. package/dist/contracts/renderer.d.mts +23 -0
  131. package/dist/contracts/routable.d.mts +14 -1
  132. package/dist/contracts/snapshotable.d.mts +25 -3
  133. package/dist/contracts/wallet-protocol.d.mts +52 -0
  134. package/dist/contracts/wallet-protocol.mjs +52 -0
  135. package/dist/contracts/wallet-protocol.mjs.map +1 -0
  136. package/dist/index.d.mts +26 -25
  137. package/dist/index.mjs +20 -13
  138. package/dist/{runtime → orchestrators}/built-in-plugin-layers.mjs +10 -5
  139. package/dist/orchestrators/built-in-plugin-layers.mjs.map +1 -0
  140. package/dist/orchestrators/codegen/bindings.d.mts +1 -2
  141. package/dist/orchestrators/codegen/bindings.mjs +52 -186
  142. package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
  143. package/dist/orchestrators/codegen/emit.mjs +19 -1
  144. package/dist/orchestrators/codegen/emit.mjs.map +1 -1
  145. package/dist/orchestrators/codegen/errors.mjs +8 -3
  146. package/dist/orchestrators/codegen/errors.mjs.map +1 -1
  147. package/dist/orchestrators/codegen/format.mjs +47 -39
  148. package/dist/orchestrators/codegen/format.mjs.map +1 -1
  149. package/dist/orchestrators/codegen/gitignore.mjs +3 -1
  150. package/dist/orchestrators/codegen/gitignore.mjs.map +1 -1
  151. package/dist/orchestrators/codegen/output-location.mjs +41 -0
  152. package/dist/orchestrators/codegen/output-location.mjs.map +1 -0
  153. package/dist/orchestrators/codegen/paths.d.mts +1 -1
  154. package/dist/orchestrators/codegen/paths.mjs +40 -14
  155. package/dist/orchestrators/codegen/paths.mjs.map +1 -1
  156. package/dist/orchestrators/codegen/permissions.mjs +3 -1
  157. package/dist/orchestrators/codegen/permissions.mjs.map +1 -1
  158. package/dist/orchestrators/codegen/service.mjs +157 -105
  159. package/dist/orchestrators/codegen/service.mjs.map +1 -1
  160. package/dist/orchestrators/lifecycle-prune/errors.mjs +31 -0
  161. package/dist/orchestrators/lifecycle-prune/errors.mjs.map +1 -0
  162. package/dist/orchestrators/lifecycle-prune/index.mjs +350 -0
  163. package/dist/orchestrators/lifecycle-prune/index.mjs.map +1 -0
  164. package/dist/orchestrators/router/cleanup.mjs +26 -27
  165. package/dist/orchestrators/router/cleanup.mjs.map +1 -1
  166. package/dist/orchestrators/router/entrypoints.mjs +2 -2
  167. package/dist/orchestrators/router/entrypoints.mjs.map +1 -1
  168. package/dist/orchestrators/router/file-provider.mjs +14 -5
  169. package/dist/orchestrators/router/file-provider.mjs.map +1 -1
  170. package/dist/orchestrators/router/hostname.mjs +19 -55
  171. package/dist/orchestrators/router/hostname.mjs.map +1 -1
  172. package/dist/orchestrators/router/index.mjs +11 -0
  173. package/dist/orchestrators/router/profile.mjs +2 -2
  174. package/dist/orchestrators/router/sentinels.mjs +26 -0
  175. package/dist/orchestrators/router/sentinels.mjs.map +1 -0
  176. package/dist/orchestrators/router/service.mjs +40 -37
  177. package/dist/orchestrators/router/service.mjs.map +1 -1
  178. package/dist/orchestrators/router/traefik-container.mjs +27 -16
  179. package/dist/orchestrators/router/traefik-container.mjs.map +1 -1
  180. package/dist/{substrate/runtime → orchestrators}/run.mjs +26 -21
  181. package/dist/orchestrators/run.mjs.map +1 -0
  182. package/dist/orchestrators/runtime-composition.d.mts +1 -1
  183. package/dist/orchestrators/runtime-composition.mjs +123 -39
  184. package/dist/orchestrators/runtime-composition.mjs.map +1 -1
  185. package/dist/orchestrators/snapshot/capture-command.d.mts +1 -0
  186. package/dist/orchestrators/snapshot/capture-command.mjs +25 -0
  187. package/dist/orchestrators/snapshot/capture-command.mjs.map +1 -0
  188. package/dist/orchestrators/snapshot/capture.mjs +42 -28
  189. package/dist/orchestrators/snapshot/capture.mjs.map +1 -1
  190. package/dist/orchestrators/snapshot/descriptor.mjs +35 -10
  191. package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -1
  192. package/dist/orchestrators/snapshot/identity-guard.mjs +1 -1
  193. package/dist/orchestrators/snapshot/index.mjs +13 -0
  194. package/dist/orchestrators/snapshot/integrity.d.mts +1 -1
  195. package/dist/orchestrators/snapshot/integrity.mjs +73 -17
  196. package/dist/orchestrators/snapshot/integrity.mjs.map +1 -1
  197. package/dist/orchestrators/snapshot/pending-marker.d.mts +1 -0
  198. package/dist/orchestrators/snapshot/pending-marker.mjs +99 -0
  199. package/dist/orchestrators/snapshot/pending-marker.mjs.map +1 -0
  200. package/dist/orchestrators/snapshot/phase-error.mjs +23 -0
  201. package/dist/orchestrators/snapshot/phase-error.mjs.map +1 -0
  202. package/dist/orchestrators/snapshot/prune.mjs +20 -55
  203. package/dist/orchestrators/snapshot/prune.mjs.map +1 -1
  204. package/dist/orchestrators/snapshot/recover-pending.d.mts +1 -0
  205. package/dist/orchestrators/snapshot/recover-pending.mjs +223 -0
  206. package/dist/orchestrators/snapshot/recover-pending.mjs.map +1 -0
  207. package/dist/orchestrators/snapshot/restore.mjs +118 -84
  208. package/dist/orchestrators/snapshot/restore.mjs.map +1 -1
  209. package/dist/orchestrators/snapshot/service.mjs +53 -56
  210. package/dist/orchestrators/snapshot/service.mjs.map +1 -1
  211. package/dist/orchestrators/snapshot/state-document.d.mts +1 -0
  212. package/dist/orchestrators/snapshot/state-document.mjs +47 -7
  213. package/dist/orchestrators/snapshot/state-document.mjs.map +1 -1
  214. package/dist/orchestrators/snapshot/wipe.mjs +67 -10
  215. package/dist/orchestrators/snapshot/wipe.mjs.map +1 -1
  216. package/dist/plugins/account/codegen.mjs +9 -8
  217. package/dist/plugins/account/codegen.mjs.map +1 -1
  218. package/dist/plugins/account/errors.d.mts +33 -6
  219. package/dist/plugins/account/errors.mjs.map +1 -1
  220. package/dist/plugins/account/funding.d.mts +29 -19
  221. package/dist/plugins/account/funding.mjs +84 -66
  222. package/dist/plugins/account/funding.mjs.map +1 -1
  223. package/dist/plugins/account/index.d.mts +2 -4
  224. package/dist/plugins/account/index.mjs +64 -31
  225. package/dist/plugins/account/index.mjs.map +1 -1
  226. package/dist/plugins/account/keypair.mjs +6 -9
  227. package/dist/plugins/account/keypair.mjs.map +1 -1
  228. package/dist/plugins/account/lease.mjs +1 -0
  229. package/dist/plugins/account/lease.mjs.map +1 -1
  230. package/dist/plugins/account/registry.d.mts +14 -1
  231. package/dist/plugins/account/registry.mjs +18 -18
  232. package/dist/plugins/account/registry.mjs.map +1 -1
  233. package/dist/plugins/account/service.d.mts +23 -4
  234. package/dist/plugins/account/service.mjs +83 -45
  235. package/dist/plugins/account/service.mjs.map +1 -1
  236. package/dist/plugins/account/snapshot.mjs +2 -5
  237. package/dist/plugins/account/snapshot.mjs.map +1 -1
  238. package/dist/plugins/account/spans.mjs +16 -0
  239. package/dist/plugins/account/spans.mjs.map +1 -0
  240. package/dist/plugins/account/variants/keystore.mjs +6 -8
  241. package/dist/plugins/account/variants/keystore.mjs.map +1 -1
  242. package/dist/plugins/action/build-context.d.mts +3 -2
  243. package/dist/plugins/action/errors.d.mts +16 -12
  244. package/dist/plugins/action/errors.mjs.map +1 -1
  245. package/dist/plugins/action/execute.mjs +65 -103
  246. package/dist/plugins/action/execute.mjs.map +1 -1
  247. package/dist/plugins/action/index.d.mts +0 -2
  248. package/dist/plugins/action/index.mjs +17 -13
  249. package/dist/plugins/action/index.mjs.map +1 -1
  250. package/dist/plugins/action/service.mjs +38 -35
  251. package/dist/plugins/action/service.mjs.map +1 -1
  252. package/dist/plugins/action/spans.mjs +11 -0
  253. package/dist/plugins/action/spans.mjs.map +1 -0
  254. package/dist/plugins/coin/codegen.mjs +9 -8
  255. package/dist/plugins/coin/codegen.mjs.map +1 -1
  256. package/dist/plugins/coin/discovery.mjs +8 -4
  257. package/dist/plugins/coin/discovery.mjs.map +1 -1
  258. package/dist/plugins/coin/index.d.mts +11 -29
  259. package/dist/plugins/coin/index.mjs +56 -27
  260. package/dist/plugins/coin/index.mjs.map +1 -1
  261. package/dist/plugins/coin/metadata.mjs +8 -15
  262. package/dist/plugins/coin/metadata.mjs.map +1 -1
  263. package/dist/plugins/coin/mint.d.mts +30 -11
  264. package/dist/plugins/coin/mint.mjs +78 -74
  265. package/dist/plugins/coin/mint.mjs.map +1 -1
  266. package/dist/plugins/coin/registry.mjs +4 -7
  267. package/dist/plugins/coin/registry.mjs.map +1 -1
  268. package/dist/plugins/coin/service.d.mts +39 -1
  269. package/dist/plugins/coin/service.mjs +11 -4
  270. package/dist/plugins/coin/service.mjs.map +1 -1
  271. package/dist/plugins/coin/spans.mjs +20 -0
  272. package/dist/plugins/coin/spans.mjs.map +1 -0
  273. package/dist/plugins/dashboard/domain.mjs +402 -0
  274. package/dist/plugins/dashboard/domain.mjs.map +1 -0
  275. package/dist/plugins/dashboard/index.d.mts +29 -0
  276. package/dist/plugins/dashboard/index.mjs +91 -0
  277. package/dist/plugins/dashboard/index.mjs.map +1 -0
  278. package/dist/plugins/dashboard/origin-policy.mjs +36 -0
  279. package/dist/plugins/dashboard/origin-policy.mjs.map +1 -0
  280. package/dist/plugins/dashboard/routable.mjs +27 -0
  281. package/dist/plugins/dashboard/routable.mjs.map +1 -0
  282. package/dist/plugins/dashboard/schema/builder.mjs +21 -0
  283. package/dist/plugins/dashboard/schema/builder.mjs.map +1 -0
  284. package/dist/plugins/dashboard/schema/enums.mjs +72 -0
  285. package/dist/plugins/dashboard/schema/enums.mjs.map +1 -0
  286. package/dist/plugins/dashboard/schema/root.mjs +351 -0
  287. package/dist/plugins/dashboard/schema/root.mjs.map +1 -0
  288. package/dist/plugins/dashboard/schema/types.mjs +508 -0
  289. package/dist/plugins/dashboard/schema/types.mjs.map +1 -0
  290. package/dist/plugins/dashboard/schema.mjs +11 -0
  291. package/dist/plugins/dashboard/schema.mjs.map +1 -0
  292. package/dist/plugins/dashboard/server.mjs +166 -0
  293. package/dist/plugins/dashboard/server.mjs.map +1 -0
  294. package/dist/plugins/deepbook/deploy.mjs +53 -115
  295. package/dist/plugins/deepbook/deploy.mjs.map +1 -1
  296. package/dist/plugins/deepbook/errors.mjs +1 -0
  297. package/dist/plugins/deepbook/errors.mjs.map +1 -1
  298. package/dist/plugins/deepbook/faucet-strategy.mjs +47 -34
  299. package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -1
  300. package/dist/plugins/deepbook/hash.mjs +9 -0
  301. package/dist/plugins/deepbook/hash.mjs.map +1 -0
  302. package/dist/plugins/deepbook/index.d.mts +1 -10
  303. package/dist/plugins/deepbook/index.mjs +25 -22
  304. package/dist/plugins/deepbook/index.mjs.map +1 -1
  305. package/dist/plugins/deepbook/pyth/index.mjs +26 -55
  306. package/dist/plugins/deepbook/pyth/index.mjs.map +1 -1
  307. package/dist/plugins/deepbook/snapshot.mjs +17 -29
  308. package/dist/plugins/deepbook/snapshot.mjs.map +1 -1
  309. package/dist/plugins/deepbook/spans.mjs +18 -0
  310. package/dist/plugins/deepbook/spans.mjs.map +1 -0
  311. package/dist/plugins/deepbook/type-strings.mjs +65 -0
  312. package/dist/plugins/deepbook/type-strings.mjs.map +1 -0
  313. package/dist/plugins/deepbook/types.d.mts +2 -3
  314. package/dist/plugins/deepbook/types.mjs.map +1 -1
  315. package/dist/plugins/faucet/dispatcher.d.mts +21 -2
  316. package/dist/plugins/faucet/dispatcher.mjs +1 -2
  317. package/dist/plugins/faucet/dispatcher.mjs.map +1 -1
  318. package/dist/plugins/faucet/errors.d.mts +11 -24
  319. package/dist/plugins/faucet/errors.mjs.map +1 -1
  320. package/dist/plugins/faucet/http.mjs +33 -14
  321. package/dist/plugins/faucet/http.mjs.map +1 -1
  322. package/dist/plugins/faucet/index.d.mts +15 -5
  323. package/dist/plugins/faucet/index.mjs +3 -0
  324. package/dist/plugins/faucet/index.mjs.map +1 -1
  325. package/dist/plugins/faucet/spans.mjs +12 -0
  326. package/dist/plugins/faucet/spans.mjs.map +1 -0
  327. package/dist/plugins/host-service/errors.d.mts +1 -1
  328. package/dist/plugins/host-service/index.mjs +34 -8
  329. package/dist/plugins/host-service/index.mjs.map +1 -1
  330. package/dist/plugins/host-service/routable.mjs +2 -1
  331. package/dist/plugins/host-service/routable.mjs.map +1 -1
  332. package/dist/plugins/host-service/service.d.mts +21 -0
  333. package/dist/plugins/host-service/service.mjs +136 -81
  334. package/dist/plugins/host-service/service.mjs.map +1 -1
  335. package/dist/plugins/internal/acquire-on-chain-artifact.mjs +23 -0
  336. package/dist/plugins/internal/acquire-on-chain-artifact.mjs.map +1 -0
  337. package/dist/plugins/internal/codegen-helpers.mjs +18 -0
  338. package/dist/plugins/internal/codegen-helpers.mjs.map +1 -0
  339. package/dist/plugins/internal/funding-failure-error.mjs +32 -0
  340. package/dist/plugins/internal/funding-failure-error.mjs.map +1 -0
  341. package/dist/plugins/package/build.mjs +1 -1
  342. package/dist/plugins/package/build.mjs.map +1 -1
  343. package/dist/plugins/package/codegen.mjs +26 -0
  344. package/dist/plugins/package/codegen.mjs.map +1 -1
  345. package/dist/plugins/package/errors.d.mts +16 -6
  346. package/dist/plugins/package/errors.mjs.map +1 -1
  347. package/dist/plugins/package/index.d.mts +0 -4
  348. package/dist/plugins/package/index.mjs +27 -21
  349. package/dist/plugins/package/index.mjs.map +1 -1
  350. package/dist/plugins/package/mode-known.mjs +26 -12
  351. package/dist/plugins/package/mode-known.mjs.map +1 -1
  352. package/dist/plugins/package/mode-local.mjs +47 -41
  353. package/dist/plugins/package/mode-local.mjs.map +1 -1
  354. package/dist/plugins/package/publish-executor.mjs +55 -114
  355. package/dist/plugins/package/publish-executor.mjs.map +1 -1
  356. package/dist/plugins/package/registry.mjs +2 -1
  357. package/dist/plugins/package/registry.mjs.map +1 -1
  358. package/dist/plugins/package/service.mjs.map +1 -1
  359. package/dist/plugins/package/spans.mjs +14 -0
  360. package/dist/plugins/package/spans.mjs.map +1 -0
  361. package/dist/plugins/postgres/db-ensure.mjs +19 -12
  362. package/dist/plugins/postgres/db-ensure.mjs.map +1 -1
  363. package/dist/plugins/postgres/errors.d.mts +5 -0
  364. package/dist/plugins/postgres/errors.mjs.map +1 -1
  365. package/dist/plugins/postgres/index.mjs +10 -17
  366. package/dist/plugins/postgres/index.mjs.map +1 -1
  367. package/dist/plugins/postgres/service.mjs +58 -24
  368. package/dist/plugins/postgres/service.mjs.map +1 -1
  369. package/dist/plugins/postgres/snapshot.mjs.map +1 -1
  370. package/dist/plugins/postgres/spans.mjs +11 -0
  371. package/dist/plugins/postgres/spans.mjs.map +1 -0
  372. package/dist/plugins/router-entrypoints.mjs +4 -4
  373. package/dist/plugins/router-entrypoints.mjs.map +1 -1
  374. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +15 -13
  375. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -1
  376. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +15 -19
  377. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -1
  378. package/dist/plugins/seal/codegen.d.mts +2 -3
  379. package/dist/plugins/seal/codegen.mjs.map +1 -1
  380. package/dist/plugins/seal/config-render.mjs +35 -8
  381. package/dist/plugins/seal/config-render.mjs.map +1 -1
  382. package/dist/plugins/seal/deploy.mjs +31 -39
  383. package/dist/plugins/seal/deploy.mjs.map +1 -1
  384. package/dist/plugins/seal/errors.d.mts +1 -3
  385. package/dist/plugins/seal/errors.mjs +7 -1
  386. package/dist/plugins/seal/errors.mjs.map +1 -1
  387. package/dist/plugins/seal/index.d.mts +1 -6
  388. package/dist/plugins/seal/index.mjs +40 -14
  389. package/dist/plugins/seal/index.mjs.map +1 -1
  390. package/dist/plugins/seal/key-manager.d.mts +1 -9
  391. package/dist/plugins/seal/key-manager.mjs +2 -18
  392. package/dist/plugins/seal/key-manager.mjs.map +1 -1
  393. package/dist/plugins/seal/key-server.mjs +37 -36
  394. package/dist/plugins/seal/key-server.mjs.map +1 -1
  395. package/dist/plugins/seal/keygen.mjs +16 -17
  396. package/dist/plugins/seal/keygen.mjs.map +1 -1
  397. package/dist/plugins/seal/mode/fork-known.mjs +36 -14
  398. package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
  399. package/dist/plugins/seal/mode/live.d.mts +6 -4
  400. package/dist/plugins/seal/mode/live.mjs +14 -21
  401. package/dist/plugins/seal/mode/live.mjs.map +1 -1
  402. package/dist/plugins/seal/mode/local-keygen.mjs +57 -21
  403. package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
  404. package/dist/plugins/seal/registry-publish.mjs +1 -0
  405. package/dist/plugins/seal/registry-publish.mjs.map +1 -1
  406. package/dist/plugins/seal/routable.mjs.map +1 -1
  407. package/dist/plugins/seal/service.mjs.map +1 -1
  408. package/dist/plugins/seal/spans.mjs +18 -0
  409. package/dist/plugins/seal/spans.mjs.map +1 -0
  410. package/dist/plugins/sui/auto-tick.mjs +17 -23
  411. package/dist/plugins/sui/auto-tick.mjs.map +1 -1
  412. package/dist/plugins/sui/chain-build-container.mjs +18 -0
  413. package/dist/plugins/sui/chain-build-container.mjs.map +1 -0
  414. package/dist/plugins/sui/chain-probe.d.mts +5 -5
  415. package/dist/plugins/sui/chain-probe.mjs +28 -58
  416. package/dist/plugins/sui/chain-probe.mjs.map +1 -1
  417. package/dist/plugins/sui/codegen.mjs +26 -0
  418. package/dist/plugins/sui/codegen.mjs.map +1 -1
  419. package/dist/plugins/sui/errors.d.mts +2 -29
  420. package/dist/plugins/sui/errors.mjs +1 -3
  421. package/dist/plugins/sui/errors.mjs.map +1 -1
  422. package/dist/plugins/sui/fork-faucet-strategy.d.mts +1 -0
  423. package/dist/plugins/sui/fork-faucet-strategy.mjs +55 -0
  424. package/dist/plugins/sui/fork-faucet-strategy.mjs.map +1 -0
  425. package/dist/plugins/sui/fork-orchestration.d.mts +1 -0
  426. package/dist/plugins/sui/fork-orchestration.mjs +170 -2
  427. package/dist/plugins/sui/fork-orchestration.mjs.map +1 -1
  428. package/dist/plugins/sui/fork-transaction.d.mts +2 -0
  429. package/dist/plugins/sui/fork-transaction.mjs +92 -28
  430. package/dist/plugins/sui/fork-transaction.mjs.map +1 -1
  431. package/dist/plugins/sui/index.d.mts +33 -70
  432. package/dist/plugins/sui/index.mjs +58 -35
  433. package/dist/plugins/sui/index.mjs.map +1 -1
  434. package/dist/plugins/{faucet/strategies/sui-local.mjs → sui/local-faucet-strategy.mjs} +10 -7
  435. package/dist/plugins/sui/local-faucet-strategy.mjs.map +1 -0
  436. package/dist/plugins/sui/mode/external.mjs +7 -13
  437. package/dist/plugins/sui/mode/external.mjs.map +1 -1
  438. package/dist/plugins/sui/mode/fork.mjs +136 -59
  439. package/dist/plugins/sui/mode/fork.mjs.map +1 -1
  440. package/dist/plugins/sui/mode/live.mjs +10 -16
  441. package/dist/plugins/sui/mode/live.mjs.map +1 -1
  442. package/dist/plugins/sui/mode/local.mjs +37 -36
  443. package/dist/plugins/sui/mode/local.mjs.map +1 -1
  444. package/dist/plugins/sui/mode/shared-boot.mjs +38 -115
  445. package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -1
  446. package/dist/plugins/sui/mode/shared.d.mts +1 -5
  447. package/dist/plugins/sui/mode/shared.mjs.map +1 -1
  448. package/dist/plugins/sui/mode/spec.d.mts +18 -0
  449. package/dist/plugins/sui/move-summary-runner.mjs +207 -0
  450. package/dist/plugins/sui/move-summary-runner.mjs.map +1 -0
  451. package/dist/plugins/sui/network-resolver.d.mts +4 -3
  452. package/dist/plugins/sui/service.mjs.map +1 -1
  453. package/dist/plugins/sui/snapshot.mjs +3 -0
  454. package/dist/plugins/sui/snapshot.mjs.map +1 -1
  455. package/dist/plugins/sui/spans.mjs +17 -0
  456. package/dist/plugins/sui/spans.mjs.map +1 -0
  457. package/dist/plugins/wallet/codegen.d.mts +0 -1
  458. package/dist/plugins/wallet/codegen.mjs +8 -9
  459. package/dist/plugins/wallet/codegen.mjs.map +1 -1
  460. package/dist/plugins/wallet/index.d.mts +6 -8
  461. package/dist/plugins/wallet/index.mjs +46 -35
  462. package/dist/plugins/wallet/index.mjs.map +1 -1
  463. package/dist/plugins/wallet/origin-policy.mjs +12 -28
  464. package/dist/plugins/wallet/origin-policy.mjs.map +1 -1
  465. package/dist/plugins/wallet/pairing.mjs +6 -4
  466. package/dist/plugins/wallet/pairing.mjs.map +1 -1
  467. package/dist/plugins/wallet/protocol.d.mts +1 -0
  468. package/dist/plugins/wallet/protocol.mjs +5 -44
  469. package/dist/plugins/wallet/protocol.mjs.map +1 -1
  470. package/dist/plugins/wallet/routable.mjs +2 -8
  471. package/dist/plugins/wallet/routable.mjs.map +1 -1
  472. package/dist/plugins/wallet/server.mjs +36 -44
  473. package/dist/plugins/wallet/server.mjs.map +1 -1
  474. package/dist/plugins/wallet/service.d.mts +1 -5
  475. package/dist/plugins/wallet/service.mjs +22 -18
  476. package/dist/plugins/wallet/service.mjs.map +1 -1
  477. package/dist/plugins/wallet/spans.mjs +22 -0
  478. package/dist/plugins/wallet/spans.mjs.map +1 -0
  479. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +33 -8
  480. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -1
  481. package/dist/plugins/walrus/codegen.d.mts +3 -3
  482. package/dist/plugins/walrus/codegen.mjs.map +1 -1
  483. package/dist/plugins/walrus/deploy-paths.mjs +21 -5
  484. package/dist/plugins/walrus/deploy-paths.mjs.map +1 -1
  485. package/dist/plugins/walrus/deploy.mjs +67 -69
  486. package/dist/plugins/walrus/deploy.mjs.map +1 -1
  487. package/dist/plugins/walrus/errors.mjs +1 -0
  488. package/dist/plugins/walrus/errors.mjs.map +1 -1
  489. package/dist/plugins/walrus/faucet-strategy.mjs +12 -7
  490. package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -1
  491. package/dist/plugins/walrus/index.d.mts +2 -13
  492. package/dist/plugins/walrus/index.mjs +18 -23
  493. package/dist/plugins/walrus/index.mjs.map +1 -1
  494. package/dist/plugins/walrus/mode/known-deploy.mjs +7 -5
  495. package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
  496. package/dist/plugins/walrus/mode/local-cluster.mjs +26 -7
  497. package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
  498. package/dist/plugins/walrus/registry-publish.mjs.map +1 -1
  499. package/dist/plugins/walrus/routable.mjs +7 -23
  500. package/dist/plugins/walrus/routable.mjs.map +1 -1
  501. package/dist/plugins/walrus/service.mjs.map +1 -1
  502. package/dist/plugins/walrus/spans.mjs +18 -0
  503. package/dist/plugins/walrus/spans.mjs.map +1 -0
  504. package/dist/plugins/walrus/storage-nodes.d.mts +6 -2
  505. package/dist/plugins/walrus/storage-nodes.mjs +15 -8
  506. package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
  507. package/dist/plugins/walrus/wal-swap.mjs +58 -15
  508. package/dist/plugins/walrus/wal-swap.mjs.map +1 -1
  509. package/dist/primitives/artifact-publisher.d.mts +1 -1
  510. package/dist/primitives/artifact-publisher.mjs +15 -0
  511. package/dist/primitives/artifact-publisher.mjs.map +1 -0
  512. package/dist/runtime/docker/client.mjs +17 -4
  513. package/dist/runtime/docker/client.mjs.map +1 -1
  514. package/dist/runtime/docker/container.mjs +219 -132
  515. package/dist/runtime/docker/container.mjs.map +1 -1
  516. package/dist/runtime/docker/errors.mjs +0 -6
  517. package/dist/runtime/docker/errors.mjs.map +1 -1
  518. package/dist/runtime/docker/exec.mjs +21 -18
  519. package/dist/runtime/docker/exec.mjs.map +1 -1
  520. package/dist/runtime/docker/image.mjs +36 -13
  521. package/dist/runtime/docker/image.mjs.map +1 -1
  522. package/dist/runtime/docker/index.mjs +14 -0
  523. package/dist/runtime/docker/inspect-and-decode.mjs +51 -0
  524. package/dist/runtime/docker/inspect-and-decode.mjs.map +1 -0
  525. package/dist/runtime/docker/inventory.mjs +14 -11
  526. package/dist/runtime/docker/inventory.mjs.map +1 -1
  527. package/dist/runtime/docker/labels.mjs +38 -5
  528. package/dist/runtime/docker/labels.mjs.map +1 -1
  529. package/dist/runtime/docker/logs.d.mts +1 -1
  530. package/dist/runtime/docker/logs.mjs.map +1 -1
  531. package/dist/runtime/docker/network.mjs +83 -47
  532. package/dist/runtime/docker/network.mjs.map +1 -1
  533. package/dist/runtime/docker/render-run-args.mjs +82 -0
  534. package/dist/runtime/docker/render-run-args.mjs.map +1 -0
  535. package/dist/runtime/docker/service.d.mts +1 -1
  536. package/dist/runtime/docker/service.mjs +12 -7
  537. package/dist/runtime/docker/service.mjs.map +1 -1
  538. package/dist/runtime/docker/sweep.mjs +118 -43
  539. package/dist/runtime/docker/sweep.mjs.map +1 -1
  540. package/dist/runtime/docker/volume.mjs +2 -0
  541. package/dist/runtime/docker/volume.mjs.map +1 -1
  542. package/dist/runtime/docker/wrap.mjs +30 -1
  543. package/dist/runtime/docker/wrap.mjs.map +1 -1
  544. package/dist/substrate/cross-process.mjs +14 -8
  545. package/dist/substrate/cross-process.mjs.map +1 -1
  546. package/dist/substrate/event-time.mjs +33 -0
  547. package/dist/substrate/event-time.mjs.map +1 -0
  548. package/dist/substrate/events.d.mts +28 -8
  549. package/dist/substrate/identity.d.mts +2 -1
  550. package/dist/substrate/manifest.d.mts +68 -5
  551. package/dist/substrate/manifest.mjs +34 -5
  552. package/dist/substrate/manifest.mjs.map +1 -1
  553. package/dist/substrate/network.d.mts +3 -5
  554. package/dist/substrate/options.d.mts +0 -2
  555. package/dist/substrate/plugin.d.mts +13 -0
  556. package/dist/substrate/plugin.mjs +4 -2
  557. package/dist/substrate/plugin.mjs.map +1 -1
  558. package/dist/substrate/projection.d.mts +35 -3
  559. package/dist/substrate/runtime/artifact-publisher/index.mjs +19 -9
  560. package/dist/substrate/runtime/artifact-publisher/index.mjs.map +1 -1
  561. package/dist/substrate/runtime/atomic-write.mjs +97 -32
  562. package/dist/substrate/runtime/atomic-write.mjs.map +1 -1
  563. package/dist/substrate/runtime/cache/index.mjs +3 -0
  564. package/dist/substrate/runtime/cache/schema.mjs +2 -2
  565. package/dist/substrate/runtime/cache/schema.mjs.map +1 -1
  566. package/dist/substrate/runtime/cache/service.mjs +9 -7
  567. package/dist/substrate/runtime/cache/service.mjs.map +1 -1
  568. package/dist/substrate/runtime/capability-sinks/index.d.mts +1 -1
  569. package/dist/substrate/runtime/capability-sinks/index.mjs +3 -0
  570. package/dist/substrate/runtime/capability-sinks/layer.mjs +1 -1
  571. package/dist/substrate/runtime/capability-sinks/service.d.mts +61 -2
  572. package/dist/substrate/runtime/capability-sinks/service.mjs +21 -26
  573. package/dist/substrate/runtime/capability-sinks/service.mjs.map +1 -1
  574. package/dist/substrate/runtime/config-validation.d.mts +1 -13
  575. package/dist/substrate/runtime/config-validation.mjs +2 -91
  576. package/dist/substrate/runtime/config-validation.mjs.map +1 -1
  577. package/dist/substrate/runtime/container-runtime.d.mts +1 -0
  578. package/dist/substrate/runtime/container-runtime.mjs +2 -0
  579. package/dist/substrate/runtime/control-plane/domain.mjs +101 -0
  580. package/dist/substrate/runtime/control-plane/domain.mjs.map +1 -0
  581. package/dist/substrate/runtime/control-plane/service.mjs +7 -0
  582. package/dist/substrate/runtime/control-plane/service.mjs.map +1 -0
  583. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +46 -19
  584. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -1
  585. package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs +30 -12
  586. package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -1
  587. package/dist/substrate/runtime/cross-process/command-channel/index.mjs +5 -0
  588. package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs +11 -2
  589. package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs.map +1 -1
  590. package/dist/substrate/runtime/cross-process/command-channel/runtime-control-lock.mjs +1 -1
  591. package/dist/substrate/runtime/cross-process/index.mjs +11 -0
  592. package/dist/substrate/runtime/cross-process/live-clock.mjs +34 -0
  593. package/dist/substrate/runtime/cross-process/live-clock.mjs.map +1 -0
  594. package/dist/substrate/runtime/cross-process/liveness.mjs +52 -10
  595. package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
  596. package/dist/substrate/runtime/cross-process/lock.d.mts +1 -0
  597. package/dist/substrate/runtime/cross-process/lock.mjs +23 -0
  598. package/dist/substrate/runtime/cross-process/lock.mjs.map +1 -0
  599. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs +64 -0
  600. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs.map +1 -0
  601. package/dist/substrate/runtime/cross-process/roster.mjs +97 -83
  602. package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -1
  603. package/dist/substrate/runtime/cross-process/self-pid.mjs +8 -0
  604. package/dist/substrate/runtime/cross-process/self-pid.mjs.map +1 -0
  605. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +24 -28
  606. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +1 -1
  607. package/dist/substrate/runtime/cross-process/stack-lock.mjs +41 -20
  608. package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -1
  609. package/dist/substrate/runtime/current-plugin.mjs +2 -2
  610. package/dist/substrate/runtime/current-plugin.mjs.map +1 -1
  611. package/dist/substrate/runtime/errors.mjs +12 -6
  612. package/dist/substrate/runtime/errors.mjs.map +1 -1
  613. package/dist/substrate/runtime/format-unknown-error.mjs +30 -0
  614. package/dist/substrate/runtime/format-unknown-error.mjs.map +1 -0
  615. package/dist/substrate/runtime/host-bind-mount-owner.mjs +10 -0
  616. package/dist/substrate/runtime/host-bind-mount-owner.mjs.map +1 -0
  617. package/dist/substrate/runtime/host-gateway.mjs +20 -0
  618. package/dist/substrate/runtime/host-gateway.mjs.map +1 -0
  619. package/dist/substrate/runtime/host-tree-tar/index.mjs +61 -50
  620. package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -1
  621. package/dist/substrate/runtime/http-probe.d.mts +1 -1
  622. package/dist/substrate/runtime/index.mjs +14 -0
  623. package/dist/substrate/runtime/lease-broker/index.d.mts +1 -1
  624. package/dist/substrate/runtime/lease-broker/index.mjs +2 -0
  625. package/dist/substrate/runtime/lease-broker/service.d.mts +1 -64
  626. package/dist/substrate/runtime/lease-broker/service.mjs +1 -1
  627. package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -1
  628. package/dist/substrate/runtime/lifecycle/dep-graph.mjs +22 -3
  629. package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -1
  630. package/dist/substrate/runtime/lifecycle/index.mjs +9 -0
  631. package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +1 -1
  632. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +20 -3
  633. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -1
  634. package/dist/substrate/runtime/lifecycle/selective-restart.mjs +1 -1
  635. package/dist/substrate/runtime/lifecycle/signals.mjs +5 -5
  636. package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -1
  637. package/dist/substrate/runtime/lifecycle/state-machine.mjs +1 -1
  638. package/dist/substrate/runtime/managed-container.d.mts +19 -1
  639. package/dist/substrate/runtime/managed-container.mjs +20 -1
  640. package/dist/substrate/runtime/managed-container.mjs.map +1 -1
  641. package/dist/substrate/runtime/manifest/index.mjs +3 -0
  642. package/dist/substrate/runtime/manifest/manifest.mjs +15 -11
  643. package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
  644. package/dist/substrate/runtime/mode-errors.mjs +20 -0
  645. package/dist/substrate/runtime/mode-errors.mjs.map +1 -0
  646. package/dist/substrate/runtime/observability/cascade-formatter.mjs +9 -13
  647. package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -1
  648. package/dist/substrate/runtime/observability/formatter-registry.mjs +19 -36
  649. package/dist/substrate/runtime/observability/formatter-registry.mjs.map +1 -1
  650. package/dist/substrate/runtime/observability/ignore-with-log.d.mts +1 -0
  651. package/dist/substrate/runtime/observability/ignore-with-log.mjs +17 -0
  652. package/dist/substrate/runtime/observability/ignore-with-log.mjs.map +1 -0
  653. package/dist/substrate/runtime/observability/index.d.mts +1 -1
  654. package/dist/substrate/runtime/observability/index.mjs +13 -0
  655. package/dist/substrate/runtime/observability/log-store.d.mts +1 -0
  656. package/dist/substrate/runtime/observability/log-store.mjs +175 -0
  657. package/dist/substrate/runtime/observability/log-store.mjs.map +1 -0
  658. package/dist/substrate/runtime/observability/logger.d.mts +2 -1
  659. package/dist/substrate/runtime/observability/logger.mjs +5 -9
  660. package/dist/substrate/runtime/observability/logger.mjs.map +1 -1
  661. package/dist/substrate/runtime/observability/output-truncate.mjs +35 -0
  662. package/dist/substrate/runtime/observability/output-truncate.mjs.map +1 -0
  663. package/dist/substrate/runtime/observability/pretty-error.mjs +9 -12
  664. package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -1
  665. package/dist/substrate/runtime/observability/redaction.d.mts +2 -13
  666. package/dist/substrate/runtime/observability/redaction.mjs +8 -14
  667. package/dist/substrate/runtime/observability/redaction.mjs.map +1 -1
  668. package/dist/substrate/runtime/observability/span-store.d.mts +1 -0
  669. package/dist/substrate/runtime/observability/span-store.mjs +110 -0
  670. package/dist/substrate/runtime/observability/span-store.mjs.map +1 -0
  671. package/dist/substrate/runtime/observability/spans.mjs +14 -14
  672. package/dist/substrate/runtime/observability/spans.mjs.map +1 -1
  673. package/dist/substrate/runtime/observability/subprocess-capture.mjs +1 -1
  674. package/dist/substrate/runtime/passthrough-or-wrap.mjs +33 -0
  675. package/dist/substrate/runtime/passthrough-or-wrap.mjs.map +1 -0
  676. package/dist/substrate/runtime/paths.d.mts +1 -1
  677. package/dist/substrate/runtime/paths.mjs +4 -3
  678. package/dist/substrate/runtime/paths.mjs.map +1 -1
  679. package/dist/substrate/runtime/phase-preserving-produce.mjs +40 -0
  680. package/dist/substrate/runtime/phase-preserving-produce.mjs.map +1 -0
  681. package/dist/substrate/runtime/port-broker/index.mjs +3 -0
  682. package/dist/substrate/runtime/port-broker/service.mjs +170 -126
  683. package/dist/substrate/runtime/port-broker/service.mjs.map +1 -1
  684. package/dist/substrate/runtime/post-acquire-tasks.mjs +1 -1
  685. package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -1
  686. package/dist/substrate/runtime/probes.d.mts +7 -5
  687. package/dist/substrate/runtime/probes.mjs +3 -1
  688. package/dist/substrate/runtime/probes.mjs.map +1 -1
  689. package/dist/substrate/runtime/projection/index.mjs +4 -0
  690. package/dist/substrate/runtime/projection/operational-endpoints.mjs +1 -2
  691. package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -1
  692. package/dist/substrate/runtime/projection/persisted.mjs +33 -15
  693. package/dist/substrate/runtime/projection/persisted.mjs.map +1 -1
  694. package/dist/substrate/runtime/projection/state-ref.mjs +26 -2
  695. package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -1
  696. package/dist/substrate/runtime/projection/update.mjs +88 -24
  697. package/dist/substrate/runtime/projection/update.mjs.map +1 -1
  698. package/dist/substrate/runtime/random-suffix.mjs +11 -0
  699. package/dist/substrate/runtime/random-suffix.mjs.map +1 -0
  700. package/dist/substrate/runtime/retry-policy.d.mts +59 -1
  701. package/dist/substrate/runtime/retry-policy.mjs +66 -1
  702. package/dist/substrate/runtime/retry-policy.mjs.map +1 -1
  703. package/dist/substrate/runtime/routed-url.d.mts +1 -0
  704. package/dist/substrate/runtime/routed-url.mjs +79 -0
  705. package/dist/substrate/runtime/routed-url.mjs.map +1 -0
  706. package/dist/substrate/runtime/runtime-decode.d.mts +1 -1
  707. package/dist/substrate/runtime/runtime-decode.mjs.map +1 -1
  708. package/dist/substrate/runtime/scoped-http-server.mjs +2 -3
  709. package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -1
  710. package/dist/substrate/runtime/scoped-multimap/index.mjs +2 -0
  711. package/dist/substrate/runtime/scoped-multimap/service.mjs +52 -0
  712. package/dist/substrate/runtime/scoped-multimap/service.mjs.map +1 -0
  713. package/dist/substrate/runtime/scoped-ref-map/index.mjs +2 -0
  714. package/dist/substrate/runtime/scoped-ref-map/service.mjs +2 -2
  715. package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +1 -1
  716. package/dist/substrate/runtime/stage-and-swap/index.mjs +72 -4
  717. package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -1
  718. package/dist/substrate/runtime/state-store/index.d.mts +1 -0
  719. package/dist/substrate/runtime/state-store/index.mjs +3 -0
  720. package/dist/substrate/runtime/state-store/schema.d.mts +1 -0
  721. package/dist/substrate/runtime/state-store/schema.mjs +19 -14
  722. package/dist/substrate/runtime/state-store/schema.mjs.map +1 -1
  723. package/dist/substrate/runtime/state-store/service.d.mts +1 -0
  724. package/dist/substrate/runtime/state-store/service.mjs +145 -0
  725. package/dist/substrate/runtime/state-store/service.mjs.map +1 -0
  726. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.d.mts +1 -0
  727. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs +33 -0
  728. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs.map +1 -0
  729. package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs +1 -1
  730. package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -1
  731. package/dist/substrate/runtime/strategy-registry/index.mjs +4 -0
  732. package/dist/substrate/runtime/strategy-registry/service.mjs +19 -33
  733. package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -1
  734. package/dist/substrate/runtime/subnet-broker.mjs +60 -0
  735. package/dist/substrate/runtime/subnet-broker.mjs.map +1 -0
  736. package/dist/substrate/runtime/sui-execute/index.d.mts +21 -1
  737. package/dist/substrate/runtime/sui-execute/index.mjs +84 -20
  738. package/dist/substrate/runtime/sui-execute/index.mjs.map +1 -1
  739. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.d.mts +1 -0
  740. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs +32 -0
  741. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs.map +1 -0
  742. package/dist/substrate/runtime/sui-ledger/object-ref.mjs +32 -0
  743. package/dist/substrate/runtime/sui-ledger/object-ref.mjs.map +1 -0
  744. package/dist/substrate/runtime/sui-move-build/index.mjs +93 -19
  745. package/dist/substrate/runtime/sui-move-build/index.mjs.map +1 -1
  746. package/dist/substrate/runtime/supervisor/acquire-node.mjs +181 -0
  747. package/dist/substrate/runtime/supervisor/acquire-node.mjs.map +1 -0
  748. package/dist/substrate/runtime/supervisor/background-tasks.mjs +144 -0
  749. package/dist/substrate/runtime/supervisor/background-tasks.mjs.map +1 -0
  750. package/dist/substrate/runtime/supervisor/command-loop.mjs +68 -0
  751. package/dist/substrate/runtime/supervisor/command-loop.mjs.map +1 -0
  752. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs +151 -0
  753. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs.map +1 -0
  754. package/dist/substrate/runtime/supervisor/errors.d.mts +1 -0
  755. package/dist/substrate/runtime/supervisor/errors.mjs +9 -0
  756. package/dist/substrate/runtime/supervisor/errors.mjs.map +1 -0
  757. package/dist/substrate/runtime/supervisor/index.d.mts +1 -0
  758. package/dist/substrate/runtime/supervisor/index.mjs +3 -0
  759. package/dist/substrate/runtime/supervisor/shutdown.mjs +51 -0
  760. package/dist/substrate/runtime/supervisor/shutdown.mjs.map +1 -0
  761. package/dist/substrate/runtime/supervisor/start-supervisor.d.mts +1 -0
  762. package/dist/substrate/runtime/supervisor/start-supervisor.mjs +250 -0
  763. package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -0
  764. package/dist/substrate/runtime/supervisor/state.d.mts +1 -0
  765. package/dist/substrate/runtime/supervisor/state.mjs +22 -0
  766. package/dist/substrate/runtime/supervisor/state.mjs.map +1 -0
  767. package/dist/substrate/runtime/supervisor/teardown.mjs +57 -0
  768. package/dist/substrate/runtime/supervisor/teardown.mjs.map +1 -0
  769. package/dist/substrate/runtime/supervisor/types.d.mts +1 -0
  770. package/dist/substrate/runtime/supervisor/wiring.mjs +133 -0
  771. package/dist/substrate/runtime/supervisor/wiring.mjs.map +1 -0
  772. package/dist/substrate/runtime/typed-env.mjs +8 -0
  773. package/dist/substrate/runtime/typed-env.mjs.map +1 -0
  774. package/dist/substrate/state-store.d.mts +1 -0
  775. package/dist/substrate/versioned-doc-schema.mjs +17 -0
  776. package/dist/substrate/versioned-doc-schema.mjs.map +1 -0
  777. package/dist/substrate/versioned-doc-sync.mjs +94 -0
  778. package/dist/substrate/versioned-doc-sync.mjs.map +1 -0
  779. package/dist/surfaces/cli/command-tree.mjs.map +1 -1
  780. package/dist/surfaces/cli/commands/index.mjs +9 -0
  781. package/dist/surfaces/cli/commands/prune-picker-entry.mjs +13 -0
  782. package/dist/surfaces/cli/commands/prune-picker-entry.mjs.map +1 -0
  783. package/dist/surfaces/cli/commands/prune-picker.mjs +3 -2
  784. package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -1
  785. package/dist/surfaces/cli/commands/prune.mjs +34 -17
  786. package/dist/surfaces/cli/commands/prune.mjs.map +1 -1
  787. package/dist/surfaces/cli/commands/status.mjs +2 -2
  788. package/dist/surfaces/cli/commands/status.mjs.map +1 -1
  789. package/dist/surfaces/cli/commands/supervisor-presence.mjs +5 -3
  790. package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -1
  791. package/dist/surfaces/cli/commands/wipe.mjs +38 -4
  792. package/dist/surfaces/cli/commands/wipe.mjs.map +1 -1
  793. package/dist/surfaces/cli/errors.mjs +1 -1
  794. package/dist/surfaces/cli/errors.mjs.map +1 -1
  795. package/dist/surfaces/cli/flags.mjs +0 -1
  796. package/dist/surfaces/cli/flags.mjs.map +1 -1
  797. package/dist/surfaces/cli/index.mjs +98 -18
  798. package/dist/surfaces/cli/index.mjs.map +1 -1
  799. package/dist/surfaces/cli/output.mjs +1 -0
  800. package/dist/surfaces/cli/output.mjs.map +1 -1
  801. package/dist/surfaces/cli/sysexits.mjs +2 -1
  802. package/dist/surfaces/cli/sysexits.mjs.map +1 -1
  803. package/dist/surfaces/tui/app.mjs +25 -3
  804. package/dist/surfaces/tui/app.mjs.map +1 -1
  805. package/dist/surfaces/tui/display-derivation.mjs +23 -74
  806. package/dist/surfaces/tui/display-derivation.mjs.map +1 -1
  807. package/dist/surfaces/tui/event-log.mjs +35 -23
  808. package/dist/surfaces/tui/event-log.mjs.map +1 -1
  809. package/dist/surfaces/tui/input.mjs +5 -1
  810. package/dist/surfaces/tui/input.mjs.map +1 -1
  811. package/dist/surfaces/tui/plain-renderer.mjs +70 -34
  812. package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
  813. package/package.json +24 -4
  814. package/dist/contracts/liveness-classifier.d.mts +0 -19
  815. package/dist/contracts/network-resolver.d.mts +0 -15
  816. package/dist/contracts/network-resolver.mjs +0 -7
  817. package/dist/contracts/network-resolver.mjs.map +0 -1
  818. package/dist/orchestrators/codegen/extras.mjs +0 -16
  819. package/dist/orchestrators/codegen/extras.mjs.map +0 -1
  820. package/dist/plugins/deepbook/routable.mjs +0 -21
  821. package/dist/plugins/deepbook/routable.mjs.map +0 -1
  822. package/dist/plugins/faucet/service.d.mts +0 -20
  823. package/dist/plugins/faucet/strategies/sui-local.d.mts +0 -23
  824. package/dist/plugins/faucet/strategies/sui-local.mjs.map +0 -1
  825. package/dist/plugins/sui/seed-objects.d.mts +0 -18
  826. package/dist/plugins/sui/seed-objects.mjs +0 -25
  827. package/dist/plugins/sui/seed-objects.mjs.map +0 -1
  828. package/dist/runtime/built-in-plugin-layers.mjs.map +0 -1
  829. package/dist/substrate/runtime/context-helpers.mjs +0 -32
  830. package/dist/substrate/runtime/context-helpers.mjs.map +0 -1
  831. package/dist/substrate/runtime/run.mjs.map +0 -1
  832. package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs +0 -30
  833. package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs.map +0 -1
  834. package/dist/substrate/runtime/supervisor.d.mts +0 -1
  835. package/dist/substrate/runtime/supervisor.mjs +0 -831
  836. package/dist/substrate/runtime/supervisor.mjs.map +0 -1
  837. package/dist/surfaces/cli/commands/doctor-probes.mjs.map +0 -1
  838. /package/dist/{substrate/runtime/strategy-registry/faucet-capability-for.d.mts → plugins/faucet/http.d.mts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"file-provider.mjs","names":[],"sources":["../../../src/orchestrators/router/file-provider.ts"],"sourcesContent":["// File-provider config generator.\n//\n// Architecture invariant #1: file-provider ONLY, never docker-provider.\n// We materialize per-backend dispatch entries as YAML files in a\n// watched directory; Traefik's file-provider polls + reloads on\n// change. The docker-provider was rejected because the container IP\n// we need is the *shared-network* IP that lands AFTER `network connect`,\n// and the docker-provider captures the per-stack IP at first event\n// and never refreshes.\n//\n// One file per canonical route identity. Each file carries exactly\n// one router + one service (and references the shared CORS middleware\n// when `cors: true`). This layout means atomic add/remove of a route\n// is one file create/unlink — no merge step, no race.\n//\n// Two YAML shapes live under this renderer, discriminated by the\n// resolved route's `wireProtocol`:\n//\n// HTTP / h2c (Host-header dispatch on a shared port):\n//\n// http:\n// routers:\n// <id>-router:\n// rule: \"Host(`<hostname>`)\"\n// entryPoints: [\"<entrypointName>\"]\n// service: \"<id>-svc\"\n// middlewares: [\"devstack-cors\"] # when cors: true\n// services:\n// <id>-svc:\n// loadBalancer:\n// servers:\n// - url: \"http://<upstream-host>:<upstream-port>\"\n//\n// TCP (per-entrypoint-port dispatch; ONE backend per entrypoint):\n//\n// tcp:\n// routers:\n// <id>-router:\n// rule: \"HostSNI(`*`)\"\n// entryPoints: [\"<entrypointName>\"]\n// service: \"<id>-svc\"\n// services:\n// <id>-svc:\n// loadBalancer:\n// servers:\n// - address: \"<upstream-host>:<upstream-port>\"\n//\n// HostSNI(`*`) matches any incoming TCP connection on the entrypoint\n// (Traefik requires every TCP router to have a rule; `*` is the wildcard\n// that means \"any client\"). No CORS section — TCP isn't HTTP and the\n// shared CORS middleware lives under `http.middlewares`.\n\nimport { Effect } from 'effect';\n\nimport type { RoutableDecl } from '../../contracts/routable.ts';\nimport type { RosterHolder } from '../../substrate/cross-process.ts';\nimport type { Identity } from '../../substrate/identity.ts';\nimport { CORS_MIDDLEWARE_NAME } from './cors.ts';\nimport type { EntrypointRegistryShape } from './entrypoints.ts';\nimport { RouteCollision, RouterValidationError, type UnknownEntrypoint } from './errors.ts';\nimport { dispatchFileId, renderUrl, routerHostname } from './hostname.ts';\n\nexport const ROUTE_READINESS_HEADER = 'X-Devstack-Route-Id';\n\n// ---------------------------------------------------------------------------\n// Resolved-route data structure\n// ---------------------------------------------------------------------------\n\n/** Resolved wire-protocol. `'tcp'` carries no hostname matcher and no\n * CORS. The router orchestrator's renderer + collision-detector branch\n * on this. */\nexport type ResolvedWireProtocol = 'http' | 'h2c' | 'tcp';\n\n/** A `RoutableDecl` resolved by the orchestrator: hostname minted,\n * dispatch-id stringified, entrypoint port resolved, upstream URL\n * resolved. This is the shape the file-provider renderer consumes —\n * decoupled from the resolution path so testing the renderer is\n * pure. */\nexport interface ResolvedRoute {\n\treadonly dispatchFileId: string;\n\t/** Minted hostname for HTTP routes. For TCP routes this is still\n\t * computed (it's the host string the URL surfaces to consumers),\n\t * but the Traefik router rule is `HostSNI(\\`*\\`)` — TCP dispatches\n\t * by entrypoint port, not Host. */\n\treadonly hostname: string;\n\treadonly entrypointName: string;\n\treadonly entrypointPort: number;\n\t/** Upstream URL string. For HTTP this is `http://<host>:<port>`; for\n\t * TCP it's still rendered as `tcp://<host>:<port>` so the renderer\n\t * can pull host+port back out for the `address:` field. */\n\treadonly upstreamUrl: string;\n\treadonly cors: boolean;\n\treadonly wireProtocol: ResolvedWireProtocol;\n}\n\nexport interface RouteCollisionMetadata {\n\treadonly dispatchFileId: string;\n\treadonly hostname: string;\n\treadonly entrypointName: string;\n\treadonly entrypointPort: number | null;\n\treadonly wireProtocol: ResolvedWireProtocol;\n}\n\nexport const ROUTER_ROUTE_LEASE_VERSION = 1;\n\nexport interface RouteLeaseMetadata {\n\treadonly version: typeof ROUTER_ROUTE_LEASE_VERSION;\n\treadonly routerProfileId: string;\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly owner: RosterHolder;\n}\n\nexport interface DispatchRouteMetadata extends RouteCollisionMetadata {\n\treadonly lease: RouteLeaseMetadata | null;\n}\n\nexport type DispatchRouteDecodeReason =\n\t| 'missing-required-route-metadata'\n\t| 'unknown-route-lease-version'\n\t| 'invalid-route-lease-metadata';\n\nexport interface DispatchRouteDecodeDiagnostic {\n\treadonly _tag: 'DispatchRouteDecodeDiagnostic';\n\treadonly dispatchFileId: string;\n\treadonly reason: DispatchRouteDecodeReason;\n\treadonly detail: string;\n}\n\nexport type DispatchRouteParseResult =\n\t| {\n\t\t\treadonly _tag: 'valid';\n\t\t\treadonly route: DispatchRouteMetadata;\n\t\t\treadonly diagnostics: ReadonlyArray<DispatchRouteDecodeDiagnostic>;\n\t }\n\t| {\n\t\t\treadonly _tag: 'invalid';\n\t\t\treadonly dispatchFileId: string;\n\t\t\treadonly diagnostics: ReadonlyArray<DispatchRouteDecodeDiagnostic>;\n\t };\n\n/** Filename within the dispatch directory for a given file-id. The\n * `10-` prefix sorts behind the shared CORS middleware (`00-`) so\n * Traefik picks up the middleware before any router referencing it. */\nexport const dispatchFilename = (fileId: string): string => `10-${fileId}.yml`;\n\n// ---------------------------------------------------------------------------\n// Resolution — Routable + Identity + EntrypointRegistry + upstream → ResolvedRoute\n// ---------------------------------------------------------------------------\n\n/** Upstream-resolution interface the file-provider needs. The\n * orchestrator threads in concrete resolvers (`runtime/docker` for\n * containers, the port broker for host-loopback) without leaking\n * service knowledge. Architecture: \"router has no compiled-in\n * awareness of which services exist.\" */\nexport interface UpstreamResolver {\n\t/** For a container-kind upstream, return `(host, port)` where host\n\t * is the container's shared-network IP and port is the container\n\t * port from the plugin. Bounded retry lives inside the resolver\n\t * (architecture invariant #3). */\n\treadonly resolveContainer: (target: {\n\t\treadonly containerName: string;\n\t\treadonly containerPort: number;\n\t}) => Effect.Effect<{ readonly host: string; readonly port: number }, RouterValidationError>;\n\t/** For a host-loopback-kind upstream, return the bound loopback\n\t * port. The plugin owns port allocation and stamps the resolved\n\t * port into its Routable decl after acquire. */\n\treadonly resolveHostLoopback: (target: {\n\t\treadonly port: number;\n\t}) => Effect.Effect<{ readonly host: string; readonly port: number }, RouterValidationError>;\n}\n\n/** Resolve a single Routable into a ResolvedRoute. Pure-ish: the only\n * effectful bit is the upstream resolver (which talks to docker /\n * the port broker) and the entrypoint lookup.\n *\n * Takes the registry as a parameter rather than yielding the service\n * tag so the calling orchestrator can pre-bind it once at layer-\n * construction time, keeping the per-Routable hot path free of\n * Context lookups. */\nexport const resolveRoute = (\n\tidentity: Identity,\n\tdecl: RoutableDecl,\n\tregistry: EntrypointRegistryShape,\n\tupstreams: UpstreamResolver,\n): Effect.Effect<ResolvedRoute, RouterValidationError | UnknownEntrypoint> =>\n\tEffect.gen(function* () {\n\t\tconst entrypoint = yield* registry.byName(decl.endpointName);\n\t\tconst hostname = yield* routerHostname(identity, decl.dispatchId.role);\n\t\tconst fileId = yield* dispatchFileId({ identity, dispatch: decl.dispatchId });\n\t\tconst upstream =\n\t\t\tdecl.upstream.type === 'container'\n\t\t\t\t? yield* upstreams.resolveContainer({\n\t\t\t\t\t\tcontainerName: decl.upstream.containerName,\n\t\t\t\t\t\tcontainerPort: decl.upstream.containerPort,\n\t\t\t\t\t})\n\t\t\t\t: yield* upstreams.resolveHostLoopback({ port: decl.upstream.port });\n\t\tconst wireProtocol: ResolvedWireProtocol =\n\t\t\tdecl.wireProtocol === 'tcp' ? 'tcp' : decl.wireProtocol === 'h2c' ? 'h2c' : 'http';\n\t\t// The router contract says: a `wireProtocol: 'tcp'` decl MUST\n\t\t// reference an entrypoint whose protocol is also `'tcp'`. The\n\t\t// converse holds — HTTP/h2c decls must reference HTTP-family\n\t\t// entrypoints. Mismatch is a programming error in the plugin\n\t\t// author (caught here once, not at every render).\n\t\tconst expectFamily: 'tcp' | 'http' = wireProtocol === 'tcp' ? 'tcp' : 'http';\n\t\tconst entrypointFamily: 'tcp' | 'http' = entrypoint.protocol === 'tcp' ? 'tcp' : 'http';\n\t\tif (expectFamily !== entrypointFamily) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew RouterValidationError({\n\t\t\t\t\tfield: 'entrypointName',\n\t\t\t\t\tvalue: entrypoint.name,\n\t\t\t\t\tdetail:\n\t\t\t\t\t\t`wireProtocol family mismatch: decl is '${wireProtocol}' but ` +\n\t\t\t\t\t\t`entrypoint '${entrypoint.name}' is '${entrypoint.protocol}'`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\t// Validate the upstream URL — defense in depth (the resolvers\n\t\t// already produce safe values, but this stops a future resolver\n\t\t// that returns junk from corrupting the YAML). TCP carries the\n\t\t// `tcp://` scheme so we differentiate from the HTTP path; the\n\t\t// renderer pulls host+port back out either way.\n\t\tconst upstreamUrl = renderUrl({\n\t\t\tprotocol: wireProtocol === 'tcp' ? 'tcp' : 'http',\n\t\t\thostname: upstream.host,\n\t\t\tport: upstream.port,\n\t\t});\n\t\tif (!/^(?:http|tcp):\\/\\/[A-Za-z0-9_.:-]+:\\d+$/.test(upstreamUrl)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew RouterValidationError({\n\t\t\t\t\tfield: 'upstreamUrl',\n\t\t\t\t\tvalue: upstreamUrl,\n\t\t\t\t\tdetail: 'expected http://<host>:<port> or tcp://<host>:<port>',\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\t// CORS is HTTP-only — TCP decls don't carry the field. Render\n\t\t// uses this; the resolved shape carries `false` for TCP.\n\t\tconst cors = decl.wireProtocol === 'tcp' ? false : decl.cors;\n\t\treturn {\n\t\t\tdispatchFileId: fileId,\n\t\t\thostname,\n\t\t\tentrypointName: entrypoint.name,\n\t\t\tentrypointPort: entrypoint.port,\n\t\t\tupstreamUrl,\n\t\t\tcors,\n\t\t\twireProtocol,\n\t\t};\n\t});\n\n// ---------------------------------------------------------------------------\n// Render — ResolvedRoute → YAML body\n// ---------------------------------------------------------------------------\n\n/** Pull host+port out of an `<scheme>://<host>:<port>` URL. Used by the\n * TCP renderer to write the `address:` field. Safe because\n * `resolveRoute` validates the URL shape before we get here. */\nconst splitUpstream = (url: string): { host: string; port: string } => {\n\tconst stripped = url.replace(/^(?:http|tcp):\\/\\//, '');\n\tconst lastColon = stripped.lastIndexOf(':');\n\treturn {\n\t\thost: stripped.slice(0, lastColon),\n\t\tport: stripped.slice(lastColon + 1),\n\t};\n};\n\nconst renderLeaseHeader = (lease: RouteLeaseMetadata): ReadonlyArray<string> => [\n\t`# routeLeaseVersion: ${lease.version}`,\n\t`# routerProfileId: ${lease.routerProfileId}`,\n\t`# ownerApp: ${lease.app}`,\n\t`# ownerStack: ${lease.stack}`,\n\t`# ownerPid: ${lease.owner.pid}`,\n\t`# ownerStartTime: ${lease.owner.startTime}`,\n\t`# ownerHostname: ${lease.owner.hostname}`,\n\t`# ownerClaimedAt: ${lease.owner.claimedAt}`,\n\t`# ownerHeartbeatAt: ${lease.owner.heartbeatAt}`,\n\t`# ownerIntent: ${lease.owner.intent}`,\n];\n\n/** Render the YAML body for a single resolved route. Hand-rolled\n * (same rationale as `cors.ts`): static, controlled, byte-stable so\n * no-op rewrites don't wake the watcher. */\nexport const renderRouteYaml = (route: ResolvedRoute, lease: RouteLeaseMetadata): string => {\n\tif (route.wireProtocol === 'tcp') return renderTcpRouteYaml(route, lease);\n\treturn renderHttpRouteYaml(route, lease);\n};\n\nconst routeReadinessMiddlewareName = (route: ResolvedRoute): string =>\n\t`${route.dispatchFileId}-route-ready`;\n\nconst renderHttpRouteYaml = (route: ResolvedRoute, lease: RouteLeaseMetadata): string => {\n\tconst middlewares = [\n\t\trouteReadinessMiddlewareName(route),\n\t\t...(route.cors ? [CORS_MIDDLEWARE_NAME] : []),\n\t];\n\tconst schemeHint =\n\t\troute.wireProtocol === 'h2c'\n\t\t\t? ` # h2c upstream — gRPC-friendly cleartext HTTP/2.\\n`\n\t\t\t: '';\n\treturn [\n\t\t`# Auto-generated by devstack router orchestrator. Do not edit by hand.`,\n\t\t`# dispatchFileId: ${route.dispatchFileId}`,\n\t\t`# wireProtocol: ${route.wireProtocol}`,\n\t\t`# entrypointName: ${route.entrypointName}`,\n\t\t`# entrypointPort: ${route.entrypointPort}`,\n\t\t`# hostname: ${route.hostname}`,\n\t\t...renderLeaseHeader(lease),\n\t\t`http:`,\n\t\t` routers:`,\n\t\t` ${route.dispatchFileId}-router:`,\n\t\t` rule: \"Host(\\`${route.hostname}\\`)\"`,\n\t\t` entryPoints: [\"${route.entrypointName}\"]`,\n\t\t` service: \"${route.dispatchFileId}-svc\"`,\n\t\t` middlewares: [${middlewares.map((name) => `\"${name}\"`).join(', ')}]`,\n\t\t` middlewares:`,\n\t\t` ${routeReadinessMiddlewareName(route)}:`,\n\t\t` headers:`,\n\t\t` customResponseHeaders:`,\n\t\t` ${ROUTE_READINESS_HEADER}: \"${route.dispatchFileId}\"`,\n\t\t` services:`,\n\t\t` ${route.dispatchFileId}-svc:`,\n\t\t` loadBalancer:`,\n\t\tschemeHint.length > 0 ? schemeHint.trimEnd() : null,\n\t\t` servers:`,\n\t\t` - url: \"${route.upstreamUrl}\"`,\n\t\t``,\n\t]\n\t\t.filter((line): line is string => line !== null)\n\t\t.join('\\n');\n};\n\nconst renderTcpRouteYaml = (route: ResolvedRoute, lease: RouteLeaseMetadata): string => {\n\tconst { host, port } = splitUpstream(route.upstreamUrl);\n\treturn [\n\t\t`# Auto-generated by devstack router orchestrator. Do not edit by hand.`,\n\t\t`# dispatchFileId: ${route.dispatchFileId}`,\n\t\t`# wireProtocol: tcp`,\n\t\t`# entrypointName: ${route.entrypointName}`,\n\t\t`# entrypointPort: ${route.entrypointPort}`,\n\t\t`# hostname: ${route.hostname}`,\n\t\t`# tcpDispatch: entrypoint-port dispatch; HostSNI wildcard`,\n\t\t...renderLeaseHeader(lease),\n\t\t`tcp:`,\n\t\t` routers:`,\n\t\t` ${route.dispatchFileId}-router:`,\n\t\t` rule: \"HostSNI(\\`*\\`)\"`,\n\t\t` entryPoints: [\"${route.entrypointName}\"]`,\n\t\t` service: \"${route.dispatchFileId}-svc\"`,\n\t\t` services:`,\n\t\t` ${route.dispatchFileId}-svc:`,\n\t\t` loadBalancer:`,\n\t\t` servers:`,\n\t\t` - address: \"${host}:${port}\"`,\n\t\t``,\n\t].join('\\n');\n};\n\nexport const dispatchFileIdFromFilename = (filename: string): string | null => {\n\tconst match = /^10-(.+)\\.yml$/.exec(filename);\n\treturn match?.[1] ?? null;\n};\n\nconst commentValue = (body: string, key: string): string | null => {\n\tconst escaped = key.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\tconst match = new RegExp(`^# ${escaped}: (.*)$`, 'm').exec(body);\n\treturn match?.[1]?.trim() ?? null;\n};\n\nconst matchValue = (body: string, pattern: RegExp): string | null =>\n\tpattern.exec(body)?.[1]?.trim() ?? null;\n\nconst commentNumber = (body: string, key: string): number | null => {\n\tconst raw = commentValue(body, key);\n\tif (raw === null || !/^\\d+$/.test(raw)) return null;\n\treturn Number.parseInt(raw, 10);\n};\n\nconst parseWireProtocol = (body: string): ResolvedWireProtocol | null => {\n\tconst fromComment = commentValue(body, 'wireProtocol');\n\tif (fromComment === 'http' || fromComment === 'h2c' || fromComment === 'tcp') {\n\t\treturn fromComment;\n\t}\n\tif (/^tcp:/m.test(body)) return 'tcp';\n\tif (/^http:/m.test(body)) return 'http';\n\treturn null;\n};\n\nconst hasRouteLeaseMetadata = (body: string): boolean =>\n\t[\n\t\t'routeLeaseVersion',\n\t\t'routerProfileId',\n\t\t'ownerApp',\n\t\t'ownerStack',\n\t\t'ownerPid',\n\t\t'ownerStartTime',\n\t\t'ownerHostname',\n\t\t'ownerClaimedAt',\n\t\t'ownerHeartbeatAt',\n\t\t'ownerIntent',\n\t].some((key) => commentValue(body, key) !== null);\n\nconst parseRouteLeaseMetadata = (\n\tbody: string,\n\tdispatchFileId: string,\n): {\n\treadonly lease: RouteLeaseMetadata | null;\n\treadonly diagnostic: DispatchRouteDecodeDiagnostic | null;\n} => {\n\tif (!hasRouteLeaseMetadata(body)) return { lease: null, diagnostic: null };\n\tconst rawVersion = commentValue(body, 'routeLeaseVersion');\n\tconst version = commentNumber(body, 'routeLeaseVersion');\n\tconst routerProfileId = commentValue(body, 'routerProfileId');\n\tconst app = commentValue(body, 'ownerApp');\n\tconst stack = commentValue(body, 'ownerStack');\n\tconst pid = commentNumber(body, 'ownerPid');\n\tconst startTime = commentNumber(body, 'ownerStartTime');\n\tconst hostname = commentValue(body, 'ownerHostname');\n\tconst claimedAt = commentNumber(body, 'ownerClaimedAt');\n\tconst heartbeatAt = commentNumber(body, 'ownerHeartbeatAt');\n\tconst intent = commentValue(body, 'ownerIntent');\n\tif (version !== ROUTER_ROUTE_LEASE_VERSION) {\n\t\treturn {\n\t\t\tlease: null,\n\t\t\tdiagnostic: {\n\t\t\t\t_tag: 'DispatchRouteDecodeDiagnostic',\n\t\t\t\tdispatchFileId,\n\t\t\t\treason: 'unknown-route-lease-version',\n\t\t\t\tdetail: `expected routeLeaseVersion ${ROUTER_ROUTE_LEASE_VERSION}, got ${rawVersion ?? '<missing>'}`,\n\t\t\t},\n\t\t};\n\t}\n\tif (\n\t\trouterProfileId === null ||\n\t\tapp === null ||\n\t\tstack === null ||\n\t\tpid === null ||\n\t\tstartTime === null ||\n\t\thostname === null ||\n\t\tclaimedAt === null ||\n\t\theartbeatAt === null ||\n\t\t(intent !== 'normal' && intent !== 'snapshot')\n\t) {\n\t\treturn {\n\t\t\tlease: null,\n\t\t\tdiagnostic: {\n\t\t\t\t_tag: 'DispatchRouteDecodeDiagnostic',\n\t\t\t\tdispatchFileId,\n\t\t\t\treason: 'invalid-route-lease-metadata',\n\t\t\t\tdetail: 'route lease metadata is incomplete or malformed',\n\t\t\t},\n\t\t};\n\t}\n\treturn {\n\t\tlease: {\n\t\t\tversion: ROUTER_ROUTE_LEASE_VERSION,\n\t\t\trouterProfileId,\n\t\t\tapp,\n\t\t\tstack,\n\t\t\towner: {\n\t\t\t\tpid,\n\t\t\t\tstartTime,\n\t\t\t\thostname,\n\t\t\t\tclaimedAt,\n\t\t\t\theartbeatAt,\n\t\t\t\tintent,\n\t\t\t},\n\t\t},\n\t\tdiagnostic: null,\n\t};\n};\n\nexport const parseDispatchRouteFile = (\n\tbody: string,\n\tfallbackDispatchFileId: string | null = null,\n): DispatchRouteParseResult => {\n\tconst dispatchFileId = commentValue(body, 'dispatchFileId') ?? fallbackDispatchFileId;\n\tconst wireProtocol = parseWireProtocol(body);\n\tconst entrypointName =\n\t\tcommentValue(body, 'entrypointName') ?? matchValue(body, /entryPoints: \\[\"([^\"]+)\"\\]/);\n\tconst hostname =\n\t\tcommentValue(body, 'hostname') ??\n\t\tmatchValue(body, /Host\\(`([^`]+)`\\)/) ??\n\t\t(wireProtocol === 'tcp' ? '' : null);\n\tconst portRaw = commentValue(body, 'entrypointPort');\n\tconst entrypointPort =\n\t\tportRaw === null || !/^\\d+$/.test(portRaw) ? null : Number.parseInt(portRaw, 10);\n\n\tif (\n\t\tdispatchFileId === null ||\n\t\twireProtocol === null ||\n\t\tentrypointName === null ||\n\t\thostname === null\n\t) {\n\t\tconst protectedDispatchFileId = dispatchFileId ?? '<unknown>';\n\t\treturn {\n\t\t\t_tag: 'invalid',\n\t\t\tdispatchFileId: protectedDispatchFileId,\n\t\t\tdiagnostics: [\n\t\t\t\t{\n\t\t\t\t\t_tag: 'DispatchRouteDecodeDiagnostic',\n\t\t\t\t\tdispatchFileId: protectedDispatchFileId,\n\t\t\t\t\treason: 'missing-required-route-metadata',\n\t\t\t\t\tdetail:\n\t\t\t\t\t\t'route file is missing dispatchFileId, wireProtocol/http|tcp block, entrypointName, or hostname metadata',\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\tconst leaseResult = parseRouteLeaseMetadata(body, dispatchFileId);\n\n\treturn {\n\t\t_tag: 'valid',\n\t\troute: {\n\t\t\tdispatchFileId,\n\t\t\thostname,\n\t\t\tentrypointName,\n\t\t\tentrypointPort,\n\t\t\twireProtocol,\n\t\t\tlease: leaseResult.lease,\n\t\t},\n\t\tdiagnostics: leaseResult.diagnostic === null ? [] : [leaseResult.diagnostic],\n\t};\n};\n\nexport const parseDispatchRouteMetadata = (\n\tbody: string,\n\tfallbackDispatchFileId: string | null = null,\n): DispatchRouteMetadata | null => {\n\tconst parsed = parseDispatchRouteFile(body, fallbackDispatchFileId);\n\treturn parsed._tag === 'valid' ? parsed.route : null;\n};\n\n// ---------------------------------------------------------------------------\n// Collision detection\n// ---------------------------------------------------------------------------\n\n/** Assert no two resolved routes share dispatch keys.\n *\n * - HTTP routes collide on `(entrypoint, hostname)` — two HTTP backends\n * on the same entrypoint port can coexist via different Host headers,\n * so the Host *is* part of the key.\n * - TCP routes collide on `entrypoint` alone — TCP has no Host header,\n * so an entrypoint can serve exactly ONE backend. Two TCP decls on\n * the same entrypoint are an unambiguous error (parallel stacks of\n * a TCP service share the host port and would clobber each other).\n *\n * Architecture invariant #7 — distinct identity → distinct dispatch\n * URL. The TCP arm is the new clause; HTTP arm unchanged. */\nexport const detectCollisions = (\n\troutes: ReadonlyArray<RouteCollisionMetadata>,\n): RouteCollision | null => {\n\tconst seen = new Map<string, Array<RouteCollisionMetadata>>();\n\tfor (const r of routes) {\n\t\tconst portKey =\n\t\t\tr.entrypointPort === null ? `entrypoint:${r.entrypointName}` : `port:${r.entrypointPort}`;\n\t\tconst key = r.wireProtocol === 'tcp' ? `tcp@${portKey}` : `http@${portKey}@${r.hostname}`;\n\t\tconst acc = seen.get(key);\n\t\tif (acc) acc.push(r);\n\t\telse seen.set(key, [r]);\n\t}\n\tfor (const [key, colliding] of seen) {\n\t\tif (colliding.length > 1) {\n\t\t\tconst ids = colliding.map((route) => route.dispatchFileId);\n\t\t\tconst first = colliding[0];\n\t\t\tif (key.startsWith('tcp@')) {\n\t\t\t\treturn new RouteCollision({\n\t\t\t\t\tmessage: routeCollisionMessage({\n\t\t\t\t\t\thostname: '',\n\t\t\t\t\t\tentrypoint: first?.entrypointName ?? '',\n\t\t\t\t\t\tdispatchIds: ids,\n\t\t\t\t\t\twireProtocol: 'tcp',\n\t\t\t\t\t}),\n\t\t\t\t\thostname: '',\n\t\t\t\t\tentrypoint: first?.entrypointName ?? '',\n\t\t\t\t\tdispatchIds: ids,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn new RouteCollision({\n\t\t\t\tmessage: routeCollisionMessage({\n\t\t\t\t\thostname: first?.hostname ?? '',\n\t\t\t\t\tentrypoint: first?.entrypointName ?? '',\n\t\t\t\t\tdispatchIds: ids,\n\t\t\t\t\twireProtocol: 'http',\n\t\t\t\t}),\n\t\t\t\thostname: first?.hostname ?? '',\n\t\t\t\tentrypoint: first?.entrypointName ?? '',\n\t\t\t\tdispatchIds: ids,\n\t\t\t});\n\t\t}\n\t}\n\treturn null;\n};\n\nconst routeCollisionMessage = (collision: {\n\treadonly hostname: string;\n\treadonly entrypoint: string;\n\treadonly dispatchIds: ReadonlyArray<string>;\n\treadonly wireProtocol: ResolvedWireProtocol;\n}): string => {\n\tconst ids = collision.dispatchIds.join(', ');\n\tif (collision.wireProtocol === 'tcp') {\n\t\treturn `router TCP route collision on entrypoint '${collision.entrypoint}' for dispatch ids: ${ids}`;\n\t}\n\treturn (\n\t\t`router route collision on entrypoint '${collision.entrypoint}' ` +\n\t\t`and hostname '${collision.hostname}' for dispatch ids: ${ids}`\n\t);\n};\n\n// ---------------------------------------------------------------------------\n// Static base file — Traefik provider directive\n// ---------------------------------------------------------------------------\n\n/** Filename for the file-provider static config. Traefik is launched\n * with `--providers.file.directory=/etc/traefik/dispatch` so it\n * reads from there directly; this static file is OPTIONAL polish\n * and currently unused. Reserved for future provider tuning. */\nexport const STATIC_PROVIDER_FILENAME = '00-providers.yml';\n"],"mappings":"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"}
1
+ {"version":3,"file":"file-provider.mjs","names":[],"sources":["../../../src/orchestrators/router/file-provider.ts"],"sourcesContent":["// File-provider config generator.\n//\n// Architecture invariant #1: file-provider ONLY, never docker-provider.\n// We materialize per-backend dispatch entries as YAML files in a\n// watched directory; Traefik's file-provider polls + reloads on\n// change. The docker-provider was rejected because the container IP\n// we need is the *shared-network* IP that lands AFTER `network connect`,\n// and the docker-provider captures the per-stack IP at first event\n// and never refreshes.\n//\n// One file per canonical route identity. Each file carries exactly\n// one router + one service (and references the shared CORS middleware\n// when `cors: true`). This layout means atomic add/remove of a route\n// is one file create/unlink — no merge step, no race.\n//\n// Two YAML shapes live under this renderer, discriminated by the\n// resolved route's `wireProtocol`:\n//\n// HTTP / h2c (Host-header dispatch on a shared port):\n//\n// http:\n// routers:\n// <id>-router:\n// rule: \"Host(`<hostname>`)\"\n// entryPoints: [\"<entrypointName>\"]\n// service: \"<id>-svc\"\n// middlewares: [\"devstack-cors\"] # when cors: true\n// services:\n// <id>-svc:\n// loadBalancer:\n// servers:\n// - url: \"http://<upstream-host>:<upstream-port>\"\n//\n// TCP (per-entrypoint-port dispatch; ONE backend per entrypoint):\n//\n// tcp:\n// routers:\n// <id>-router:\n// rule: \"HostSNI(`*`)\"\n// entryPoints: [\"<entrypointName>\"]\n// service: \"<id>-svc\"\n// services:\n// <id>-svc:\n// loadBalancer:\n// servers:\n// - address: \"<upstream-host>:<upstream-port>\"\n//\n// HostSNI(`*`) matches any incoming TCP connection on the entrypoint\n// (Traefik requires every TCP router to have a rule; `*` is the wildcard\n// that means \"any client\"). No CORS section — TCP isn't HTTP and the\n// shared CORS middleware lives under `http.middlewares`.\n\nimport { Effect } from 'effect';\n\nimport type { RoutableDecl } from '../../contracts/routable.ts';\nimport type { RosterHolder } from '../../substrate/cross-process.ts';\nimport type { Identity } from '../../substrate/identity.ts';\nimport { CORS_MIDDLEWARE_NAME } from './cors.ts';\nimport type { EntrypointRegistryShape } from './entrypoints.ts';\nimport { RouteCollision, RouterValidationError, type UnknownEntrypoint } from './errors.ts';\nimport { dispatchFileId, renderUrl, routerHostname } from './hostname.ts';\n\nexport const ROUTE_READINESS_HEADER = 'X-Devstack-Route-Id';\n\n/** Dispatch-file filename prefix. `10-` sorts behind the shared CORS\n * middleware (`00-`) so Traefik's file-provider loads the middleware\n * before any router that references it. Single source of truth for\n * the file-write side (`dispatchFilename`) and the file-read side\n * (`dispatchFileIdFromFilename`) — keep them in lockstep here. */\nconst DISPATCH_FILENAME_PREFIX = '10-';\n\n// ---------------------------------------------------------------------------\n// Resolved-route data structure\n// ---------------------------------------------------------------------------\n\n/** Resolved wire-protocol. `'tcp'` carries no hostname matcher and no\n * CORS. The router orchestrator's renderer + collision-detector branch\n * on this. */\nexport type ResolvedWireProtocol = 'http' | 'h2c' | 'tcp';\n\n/** A `RoutableDecl` resolved by the orchestrator: hostname minted,\n * dispatch-id stringified, entrypoint port resolved, upstream URL\n * resolved. This is the shape the file-provider renderer consumes —\n * decoupled from the resolution path so testing the renderer is\n * pure. */\nexport interface ResolvedRoute {\n\treadonly dispatchFileId: string;\n\t/** Minted hostname for HTTP routes. For TCP routes this is still\n\t * computed (it's the host string the URL surfaces to consumers),\n\t * but the Traefik router rule is `HostSNI(\\`*\\`)` — TCP dispatches\n\t * by entrypoint port, not Host. */\n\treadonly hostname: string;\n\treadonly entrypointName: string;\n\treadonly entrypointPort: number;\n\t/** Upstream URL string. For HTTP this is `http://<host>:<port>`; for\n\t * TCP it's still rendered as `tcp://<host>:<port>` so the renderer\n\t * can pull host+port back out for the `address:` field. */\n\treadonly upstreamUrl: string;\n\treadonly cors: boolean;\n\treadonly wireProtocol: ResolvedWireProtocol;\n}\n\nexport interface RouteCollisionMetadata {\n\treadonly dispatchFileId: string;\n\treadonly hostname: string;\n\treadonly entrypointName: string;\n\treadonly entrypointPort: number | null;\n\treadonly wireProtocol: ResolvedWireProtocol;\n}\n\nexport const ROUTER_ROUTE_LEASE_VERSION = 1;\n\nexport interface RouteLeaseMetadata {\n\treadonly version: typeof ROUTER_ROUTE_LEASE_VERSION;\n\treadonly routerProfileId: string;\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly owner: RosterHolder;\n}\n\nexport interface DispatchRouteMetadata extends RouteCollisionMetadata {\n\treadonly lease: RouteLeaseMetadata | null;\n}\n\nexport type DispatchRouteDecodeReason =\n\t| 'missing-required-route-metadata'\n\t| 'unknown-route-lease-version'\n\t| 'invalid-route-lease-metadata';\n\nexport interface DispatchRouteDecodeDiagnostic {\n\treadonly _tag: 'DispatchRouteDecodeDiagnostic';\n\treadonly dispatchFileId: string;\n\treadonly reason: DispatchRouteDecodeReason;\n\treadonly detail: string;\n}\n\nexport type DispatchRouteParseResult =\n\t| {\n\t\t\treadonly _tag: 'valid';\n\t\t\treadonly route: DispatchRouteMetadata;\n\t\t\treadonly diagnostics: ReadonlyArray<DispatchRouteDecodeDiagnostic>;\n\t }\n\t| {\n\t\t\treadonly _tag: 'invalid';\n\t\t\treadonly dispatchFileId: string;\n\t\t\treadonly diagnostics: ReadonlyArray<DispatchRouteDecodeDiagnostic>;\n\t };\n\n/** Filename within the dispatch directory for a given file-id. The\n * prefix (`DISPATCH_FILENAME_PREFIX`) sorts behind the shared CORS\n * middleware (`00-`) so Traefik picks up the middleware before any\n * router referencing it. */\nexport const dispatchFilename = (fileId: string): string =>\n\t`${DISPATCH_FILENAME_PREFIX}${fileId}.yml`;\n\n// ---------------------------------------------------------------------------\n// Resolution — Routable + Identity + EntrypointRegistry + upstream → ResolvedRoute\n// ---------------------------------------------------------------------------\n\n/** Upstream-resolution interface the file-provider needs. The\n * orchestrator threads in concrete resolvers (`runtime/docker` for\n * containers, the port broker for host-loopback) without leaking\n * service knowledge. Architecture: \"router has no compiled-in\n * awareness of which services exist.\" */\nexport interface UpstreamResolver {\n\t/** For a container-kind upstream, return `(host, port)` where host\n\t * is the container's shared-network IP and port is the container\n\t * port from the plugin. Bounded retry lives inside the resolver\n\t * (architecture invariant #3). */\n\treadonly resolveContainer: (target: {\n\t\treadonly containerName: string;\n\t\treadonly containerPort: number;\n\t}) => Effect.Effect<{ readonly host: string; readonly port: number }, RouterValidationError>;\n\t/** For a host-loopback-kind upstream, return the bound loopback\n\t * port. The plugin owns port allocation and stamps the resolved\n\t * port into its Routable decl after acquire. */\n\treadonly resolveHostLoopback: (target: {\n\t\treadonly port: number;\n\t}) => Effect.Effect<{ readonly host: string; readonly port: number }, RouterValidationError>;\n}\n\n/** Resolve a single Routable into a ResolvedRoute. Pure-ish: the only\n * effectful bit is the upstream resolver (which talks to docker /\n * the port broker) and the entrypoint lookup.\n *\n * Takes the registry as a parameter rather than yielding the service\n * tag so the calling orchestrator can pre-bind it once at layer-\n * construction time, keeping the per-Routable hot path free of\n * Context lookups. */\nexport const resolveRoute = (\n\tidentity: Identity,\n\tdecl: RoutableDecl,\n\tregistry: EntrypointRegistryShape,\n\tupstreams: UpstreamResolver,\n): Effect.Effect<ResolvedRoute, RouterValidationError | UnknownEntrypoint> =>\n\tEffect.gen(function* () {\n\t\tconst entrypoint = yield* registry.byName(decl.endpointName);\n\t\tconst hostname = yield* routerHostname(identity, decl.dispatchId.role);\n\t\tconst fileId = yield* dispatchFileId({ identity, dispatch: decl.dispatchId });\n\t\tconst upstream =\n\t\t\tdecl.upstream.type === 'container'\n\t\t\t\t? yield* upstreams.resolveContainer({\n\t\t\t\t\t\tcontainerName: decl.upstream.containerName,\n\t\t\t\t\t\tcontainerPort: decl.upstream.containerPort,\n\t\t\t\t\t})\n\t\t\t\t: yield* upstreams.resolveHostLoopback({ port: decl.upstream.port });\n\t\tconst wireProtocol: ResolvedWireProtocol =\n\t\t\tdecl.wireProtocol === 'tcp' ? 'tcp' : decl.wireProtocol === 'h2c' ? 'h2c' : 'http';\n\t\t// The router contract says: a `wireProtocol: 'tcp'` decl MUST\n\t\t// reference an entrypoint whose protocol is also `'tcp'`. The\n\t\t// converse holds — HTTP/h2c decls must reference HTTP-family\n\t\t// entrypoints. Mismatch is a programming error in the plugin\n\t\t// author (caught here once, not at every render).\n\t\tconst expectFamily: 'tcp' | 'http' = wireProtocol === 'tcp' ? 'tcp' : 'http';\n\t\tconst entrypointFamily: 'tcp' | 'http' = entrypoint.protocol === 'tcp' ? 'tcp' : 'http';\n\t\tif (expectFamily !== entrypointFamily) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew RouterValidationError({\n\t\t\t\t\tfield: 'entrypointName',\n\t\t\t\t\tvalue: entrypoint.name,\n\t\t\t\t\tdetail:\n\t\t\t\t\t\t`wireProtocol family mismatch: decl is '${wireProtocol}' but ` +\n\t\t\t\t\t\t`entrypoint '${entrypoint.name}' is '${entrypoint.protocol}'`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\t// Validate the upstream URL — defense in depth (the resolvers\n\t\t// already produce safe values, but this stops a future resolver\n\t\t// that returns junk from corrupting the YAML). TCP carries the\n\t\t// `tcp://` scheme so we differentiate from the HTTP path; the\n\t\t// renderer pulls host+port back out either way.\n\t\tconst upstreamUrl = renderUrl({\n\t\t\tprotocol: wireProtocol === 'tcp' ? 'tcp' : 'http',\n\t\t\thostname: upstream.host,\n\t\t\tport: upstream.port,\n\t\t});\n\t\tif (!/^(?:http|tcp):\\/\\/[A-Za-z0-9_.:-]+:\\d+$/.test(upstreamUrl)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew RouterValidationError({\n\t\t\t\t\tfield: 'upstreamUrl',\n\t\t\t\t\tvalue: upstreamUrl,\n\t\t\t\t\tdetail: 'expected http://<host>:<port> or tcp://<host>:<port>',\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\t// CORS is HTTP-only — TCP decls don't carry the field. Render\n\t\t// uses this; the resolved shape carries `false` for TCP.\n\t\tconst cors = decl.wireProtocol === 'tcp' ? false : decl.cors;\n\t\treturn {\n\t\t\tdispatchFileId: fileId,\n\t\t\thostname,\n\t\t\tentrypointName: entrypoint.name,\n\t\t\tentrypointPort: entrypoint.port,\n\t\t\tupstreamUrl,\n\t\t\tcors,\n\t\t\twireProtocol,\n\t\t};\n\t});\n\n// ---------------------------------------------------------------------------\n// Render — ResolvedRoute → YAML body\n// ---------------------------------------------------------------------------\n\n/** Pull host+port out of an `<scheme>://<host>:<port>` URL. Used by the\n * TCP renderer to write the `address:` field. Safe because\n * `resolveRoute` validates the URL shape before we get here. */\nconst splitUpstream = (url: string): { host: string; port: string } => {\n\tconst stripped = url.replace(/^(?:http|tcp):\\/\\//, '');\n\tconst lastColon = stripped.lastIndexOf(':');\n\treturn {\n\t\thost: stripped.slice(0, lastColon),\n\t\tport: stripped.slice(lastColon + 1),\n\t};\n};\n\nconst renderLeaseHeader = (lease: RouteLeaseMetadata): ReadonlyArray<string> => [\n\t`# routeLeaseVersion: ${lease.version}`,\n\t`# routerProfileId: ${lease.routerProfileId}`,\n\t`# ownerApp: ${lease.app}`,\n\t`# ownerStack: ${lease.stack}`,\n\t`# ownerPid: ${lease.owner.pid}`,\n\t`# ownerStartTime: ${lease.owner.startTime}`,\n\t`# ownerHostname: ${lease.owner.hostname}`,\n\t`# ownerClaimedAt: ${lease.owner.claimedAt}`,\n\t`# ownerHeartbeatAt: ${lease.owner.heartbeatAt}`,\n\t`# ownerIntent: ${lease.owner.intent}`,\n];\n\n/** Render the YAML body for a single resolved route. Hand-rolled\n * (same rationale as `cors.ts`): static, controlled, byte-stable so\n * no-op rewrites don't wake the watcher. */\nexport const renderRouteYaml = (route: ResolvedRoute, lease: RouteLeaseMetadata): string => {\n\tif (route.wireProtocol === 'tcp') return renderTcpRouteYaml(route, lease);\n\treturn renderHttpRouteYaml(route, lease);\n};\n\nconst routeReadinessMiddlewareName = (route: ResolvedRoute): string =>\n\t`${route.dispatchFileId}-route-ready`;\n\nconst renderHttpRouteYaml = (route: ResolvedRoute, lease: RouteLeaseMetadata): string => {\n\tconst middlewares = [\n\t\trouteReadinessMiddlewareName(route),\n\t\t...(route.cors ? [CORS_MIDDLEWARE_NAME] : []),\n\t];\n\tconst schemeHint =\n\t\troute.wireProtocol === 'h2c'\n\t\t\t? ` # h2c upstream — gRPC-friendly cleartext HTTP/2.\\n`\n\t\t\t: '';\n\treturn [\n\t\t`# Auto-generated by devstack router orchestrator. Do not edit by hand.`,\n\t\t`# dispatchFileId: ${route.dispatchFileId}`,\n\t\t`# wireProtocol: ${route.wireProtocol}`,\n\t\t`# entrypointName: ${route.entrypointName}`,\n\t\t`# entrypointPort: ${route.entrypointPort}`,\n\t\t`# hostname: ${route.hostname}`,\n\t\t...renderLeaseHeader(lease),\n\t\t`http:`,\n\t\t` routers:`,\n\t\t` ${route.dispatchFileId}-router:`,\n\t\t` rule: \"Host(\\`${route.hostname}\\`)\"`,\n\t\t` entryPoints: [\"${route.entrypointName}\"]`,\n\t\t` service: \"${route.dispatchFileId}-svc\"`,\n\t\t` middlewares: [${middlewares.map((name) => `\"${name}\"`).join(', ')}]`,\n\t\t` middlewares:`,\n\t\t` ${routeReadinessMiddlewareName(route)}:`,\n\t\t` headers:`,\n\t\t` customResponseHeaders:`,\n\t\t` ${ROUTE_READINESS_HEADER}: \"${route.dispatchFileId}\"`,\n\t\t` services:`,\n\t\t` ${route.dispatchFileId}-svc:`,\n\t\t` loadBalancer:`,\n\t\tschemeHint.length > 0 ? schemeHint.trimEnd() : null,\n\t\t` servers:`,\n\t\t` - url: \"${route.upstreamUrl}\"`,\n\t\t``,\n\t]\n\t\t.filter((line): line is string => line !== null)\n\t\t.join('\\n');\n};\n\nconst renderTcpRouteYaml = (route: ResolvedRoute, lease: RouteLeaseMetadata): string => {\n\tconst { host, port } = splitUpstream(route.upstreamUrl);\n\treturn [\n\t\t`# Auto-generated by devstack router orchestrator. Do not edit by hand.`,\n\t\t`# dispatchFileId: ${route.dispatchFileId}`,\n\t\t`# wireProtocol: tcp`,\n\t\t`# entrypointName: ${route.entrypointName}`,\n\t\t`# entrypointPort: ${route.entrypointPort}`,\n\t\t`# hostname: ${route.hostname}`,\n\t\t`# tcpDispatch: entrypoint-port dispatch; HostSNI wildcard`,\n\t\t...renderLeaseHeader(lease),\n\t\t`tcp:`,\n\t\t` routers:`,\n\t\t` ${route.dispatchFileId}-router:`,\n\t\t` rule: \"HostSNI(\\`*\\`)\"`,\n\t\t` entryPoints: [\"${route.entrypointName}\"]`,\n\t\t` service: \"${route.dispatchFileId}-svc\"`,\n\t\t` services:`,\n\t\t` ${route.dispatchFileId}-svc:`,\n\t\t` loadBalancer:`,\n\t\t` servers:`,\n\t\t` - address: \"${host}:${port}\"`,\n\t\t``,\n\t].join('\\n');\n};\n\nexport const dispatchFileIdFromFilename = (filename: string): string | null => {\n\t// Escape `DISPATCH_FILENAME_PREFIX` for use inside a RegExp literal —\n\t// today it's a plain `10-` so no regex metacharacters are at play,\n\t// but the escape makes the read side robust to future prefix changes.\n\tconst escapedPrefix = DISPATCH_FILENAME_PREFIX.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\tconst match = new RegExp(`^${escapedPrefix}(.+)\\\\.yml$`).exec(filename);\n\treturn match?.[1] ?? null;\n};\n\nconst commentValue = (body: string, key: string): string | null => {\n\tconst escaped = key.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\tconst match = new RegExp(`^# ${escaped}: (.*)$`, 'm').exec(body);\n\treturn match?.[1]?.trim() ?? null;\n};\n\nconst matchValue = (body: string, pattern: RegExp): string | null =>\n\tpattern.exec(body)?.[1]?.trim() ?? null;\n\nconst commentNumber = (body: string, key: string): number | null => {\n\tconst raw = commentValue(body, key);\n\tif (raw === null || !/^\\d+$/.test(raw)) return null;\n\treturn Number.parseInt(raw, 10);\n};\n\nconst parseWireProtocol = (body: string): ResolvedWireProtocol | null => {\n\tconst fromComment = commentValue(body, 'wireProtocol');\n\tif (fromComment === 'http' || fromComment === 'h2c' || fromComment === 'tcp') {\n\t\treturn fromComment;\n\t}\n\tif (/^tcp:/m.test(body)) return 'tcp';\n\tif (/^http:/m.test(body)) return 'http';\n\treturn null;\n};\n\nconst hasRouteLeaseMetadata = (body: string): boolean =>\n\t[\n\t\t'routeLeaseVersion',\n\t\t'routerProfileId',\n\t\t'ownerApp',\n\t\t'ownerStack',\n\t\t'ownerPid',\n\t\t'ownerStartTime',\n\t\t'ownerHostname',\n\t\t'ownerClaimedAt',\n\t\t'ownerHeartbeatAt',\n\t\t'ownerIntent',\n\t].some((key) => commentValue(body, key) !== null);\n\nconst parseRouteLeaseMetadata = (\n\tbody: string,\n\tdispatchFileId: string,\n): {\n\treadonly lease: RouteLeaseMetadata | null;\n\treadonly diagnostic: DispatchRouteDecodeDiagnostic | null;\n} => {\n\tif (!hasRouteLeaseMetadata(body)) return { lease: null, diagnostic: null };\n\tconst rawVersion = commentValue(body, 'routeLeaseVersion');\n\tconst version = commentNumber(body, 'routeLeaseVersion');\n\tconst routerProfileId = commentValue(body, 'routerProfileId');\n\tconst app = commentValue(body, 'ownerApp');\n\tconst stack = commentValue(body, 'ownerStack');\n\tconst pid = commentNumber(body, 'ownerPid');\n\tconst startTime = commentNumber(body, 'ownerStartTime');\n\tconst hostname = commentValue(body, 'ownerHostname');\n\tconst claimedAt = commentNumber(body, 'ownerClaimedAt');\n\tconst heartbeatAt = commentNumber(body, 'ownerHeartbeatAt');\n\tconst intent = commentValue(body, 'ownerIntent');\n\tif (version !== ROUTER_ROUTE_LEASE_VERSION) {\n\t\treturn {\n\t\t\tlease: null,\n\t\t\tdiagnostic: {\n\t\t\t\t_tag: 'DispatchRouteDecodeDiagnostic',\n\t\t\t\tdispatchFileId,\n\t\t\t\treason: 'unknown-route-lease-version',\n\t\t\t\tdetail: `expected routeLeaseVersion ${ROUTER_ROUTE_LEASE_VERSION}, got ${rawVersion ?? '<missing>'}`,\n\t\t\t},\n\t\t};\n\t}\n\tif (\n\t\trouterProfileId === null ||\n\t\tapp === null ||\n\t\tstack === null ||\n\t\tpid === null ||\n\t\tstartTime === null ||\n\t\thostname === null ||\n\t\tclaimedAt === null ||\n\t\theartbeatAt === null ||\n\t\t(intent !== 'normal' && intent !== 'snapshot')\n\t) {\n\t\treturn {\n\t\t\tlease: null,\n\t\t\tdiagnostic: {\n\t\t\t\t_tag: 'DispatchRouteDecodeDiagnostic',\n\t\t\t\tdispatchFileId,\n\t\t\t\treason: 'invalid-route-lease-metadata',\n\t\t\t\tdetail: 'route lease metadata is incomplete or malformed',\n\t\t\t},\n\t\t};\n\t}\n\treturn {\n\t\tlease: {\n\t\t\tversion: ROUTER_ROUTE_LEASE_VERSION,\n\t\t\trouterProfileId,\n\t\t\tapp,\n\t\t\tstack,\n\t\t\towner: {\n\t\t\t\tpid,\n\t\t\t\tstartTime,\n\t\t\t\thostname,\n\t\t\t\tclaimedAt,\n\t\t\t\theartbeatAt,\n\t\t\t\tintent,\n\t\t\t},\n\t\t},\n\t\tdiagnostic: null,\n\t};\n};\n\nexport const parseDispatchRouteFile = (\n\tbody: string,\n\tfallbackDispatchFileId: string | null = null,\n): DispatchRouteParseResult => {\n\tconst dispatchFileId = commentValue(body, 'dispatchFileId') ?? fallbackDispatchFileId;\n\tconst wireProtocol = parseWireProtocol(body);\n\tconst entrypointName =\n\t\tcommentValue(body, 'entrypointName') ?? matchValue(body, /entryPoints: \\[\"([^\"]+)\"\\]/);\n\tconst hostname =\n\t\tcommentValue(body, 'hostname') ??\n\t\tmatchValue(body, /Host\\(`([^`]+)`\\)/) ??\n\t\t(wireProtocol === 'tcp' ? '' : null);\n\tconst portRaw = commentValue(body, 'entrypointPort');\n\tconst entrypointPort =\n\t\tportRaw === null || !/^\\d+$/.test(portRaw) ? null : Number.parseInt(portRaw, 10);\n\n\tif (\n\t\tdispatchFileId === null ||\n\t\twireProtocol === null ||\n\t\tentrypointName === null ||\n\t\thostname === null\n\t) {\n\t\tconst protectedDispatchFileId = dispatchFileId ?? '<unknown>';\n\t\treturn {\n\t\t\t_tag: 'invalid',\n\t\t\tdispatchFileId: protectedDispatchFileId,\n\t\t\tdiagnostics: [\n\t\t\t\t{\n\t\t\t\t\t_tag: 'DispatchRouteDecodeDiagnostic',\n\t\t\t\t\tdispatchFileId: protectedDispatchFileId,\n\t\t\t\t\treason: 'missing-required-route-metadata',\n\t\t\t\t\tdetail:\n\t\t\t\t\t\t'route file is missing dispatchFileId, wireProtocol/http|tcp block, entrypointName, or hostname metadata',\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\tconst leaseResult = parseRouteLeaseMetadata(body, dispatchFileId);\n\n\treturn {\n\t\t_tag: 'valid',\n\t\troute: {\n\t\t\tdispatchFileId,\n\t\t\thostname,\n\t\t\tentrypointName,\n\t\t\tentrypointPort,\n\t\t\twireProtocol,\n\t\t\tlease: leaseResult.lease,\n\t\t},\n\t\tdiagnostics: leaseResult.diagnostic === null ? [] : [leaseResult.diagnostic],\n\t};\n};\n\nexport const parseDispatchRouteMetadata = (\n\tbody: string,\n\tfallbackDispatchFileId: string | null = null,\n): DispatchRouteMetadata | null => {\n\tconst parsed = parseDispatchRouteFile(body, fallbackDispatchFileId);\n\treturn parsed._tag === 'valid' ? parsed.route : null;\n};\n\n// ---------------------------------------------------------------------------\n// Collision detection\n// ---------------------------------------------------------------------------\n\n/** Assert no two resolved routes share dispatch keys.\n *\n * - HTTP routes collide on `(entrypoint, hostname)` — two HTTP backends\n * on the same entrypoint port can coexist via different Host headers,\n * so the Host *is* part of the key.\n * - TCP routes collide on `entrypoint` alone — TCP has no Host header,\n * so an entrypoint can serve exactly ONE backend. Two TCP decls on\n * the same entrypoint are an unambiguous error (parallel stacks of\n * a TCP service share the host port and would clobber each other).\n *\n * Architecture invariant #7 — distinct identity → distinct dispatch\n * URL. The TCP arm is the new clause; HTTP arm unchanged. */\nexport const detectCollisions = (\n\troutes: ReadonlyArray<RouteCollisionMetadata>,\n): RouteCollision | null => {\n\tconst seen = new Map<string, Array<RouteCollisionMetadata>>();\n\tfor (const r of routes) {\n\t\tconst portKey =\n\t\t\tr.entrypointPort === null ? `entrypoint:${r.entrypointName}` : `port:${r.entrypointPort}`;\n\t\tconst key = r.wireProtocol === 'tcp' ? `tcp@${portKey}` : `http@${portKey}@${r.hostname}`;\n\t\tconst acc = seen.get(key);\n\t\tif (acc) acc.push(r);\n\t\telse seen.set(key, [r]);\n\t}\n\tfor (const [key, colliding] of seen) {\n\t\tif (colliding.length > 1) {\n\t\t\tconst ids = colliding.map((route) => route.dispatchFileId);\n\t\t\tconst first = colliding[0];\n\t\t\tif (key.startsWith('tcp@')) {\n\t\t\t\treturn new RouteCollision({\n\t\t\t\t\tmessage: routeCollisionMessage({\n\t\t\t\t\t\thostname: '',\n\t\t\t\t\t\tentrypoint: first?.entrypointName ?? '',\n\t\t\t\t\t\tdispatchIds: ids,\n\t\t\t\t\t\twireProtocol: 'tcp',\n\t\t\t\t\t}),\n\t\t\t\t\thostname: '',\n\t\t\t\t\tentrypoint: first?.entrypointName ?? '',\n\t\t\t\t\tdispatchIds: ids,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn new RouteCollision({\n\t\t\t\tmessage: routeCollisionMessage({\n\t\t\t\t\thostname: first?.hostname ?? '',\n\t\t\t\t\tentrypoint: first?.entrypointName ?? '',\n\t\t\t\t\tdispatchIds: ids,\n\t\t\t\t\twireProtocol: 'http',\n\t\t\t\t}),\n\t\t\t\thostname: first?.hostname ?? '',\n\t\t\t\tentrypoint: first?.entrypointName ?? '',\n\t\t\t\tdispatchIds: ids,\n\t\t\t});\n\t\t}\n\t}\n\treturn null;\n};\n\nconst routeCollisionMessage = (collision: {\n\treadonly hostname: string;\n\treadonly entrypoint: string;\n\treadonly dispatchIds: ReadonlyArray<string>;\n\treadonly wireProtocol: ResolvedWireProtocol;\n}): string => {\n\tconst ids = collision.dispatchIds.join(', ');\n\tif (collision.wireProtocol === 'tcp') {\n\t\treturn `router TCP route collision on entrypoint '${collision.entrypoint}' for dispatch ids: ${ids}`;\n\t}\n\treturn (\n\t\t`router route collision on entrypoint '${collision.entrypoint}' ` +\n\t\t`and hostname '${collision.hostname}' for dispatch ids: ${ids}`\n\t);\n};\n\n"],"mappings":"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"}
@@ -1,54 +1,31 @@
1
1
  import { RouterValidationError } from "./errors.mjs";
2
+ import { routedHostname } from "../../substrate/runtime/routed-url.mjs";
2
3
  import { Effect } from "effect";
3
4
  import { createHash } from "node:crypto";
4
- const LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
5
+ //#region src/orchestrators/router/hostname.ts
5
6
  const MAX_DISPATCH_ID_LEN = 140;
6
7
  const MAX_DISPATCH_ID_READABLE_LEN = 48;
7
- const MAX_HOSTNAME_LEN = 200;
8
- /** Validate a single label-shaped string. Returns the input or fails
9
- * with a typed `RouterValidationError`. */
10
- const validateLabel = (field, value) => {
11
- if (!LABEL.test(value)) return Effect.fail(new RouterValidationError({
12
- field,
13
- value,
14
- detail: "expected lower-case RFC-1035 label (alphanumeric + internal hyphens, 1-63 chars)"
15
- }));
16
- return Effect.succeed(value);
17
- };
18
- /** Fold dots → hyphens (architecture invariant #8) and lower-case.
19
- * Pure, no validation. The output is fed to `validateLabel`. */
20
- const normalizeServiceSegment = (raw) => raw.toLowerCase().replace(/\./g, "-");
21
8
  /** Build the human-readable prefix of a dispatch id. This is not the
22
9
  * identity key; uniqueness comes from the hash over the canonical
23
10
  * tuple in `dispatchFileId`. */
24
11
  const normalizeDispatchSegment = (raw) => raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "route";
25
- /** Mint the per-Routable hostname from `(identity, role)`.
26
- *
27
- * Default stack: `<role>.<app>.localhost`
28
- * Other stacks: `<role>.<stack>.<app>.localhost`
29
- *
30
- * The `role` segment is folded + validated; the `app` and `stack`
31
- * segments are also validated (they come from `Identity`, which the
32
- * substrate has already validated once at boot, but defense-in-depth
33
- * is cheap and the failure mode is a programming error).
34
- *
35
- * Architecture invariant #7: this MUST embed the stack dimension for
36
- * non-default stacks. Distinct identities → distinct hostnames. */
37
- const routerHostname = (identity, role) => Effect.gen(function* () {
38
- const app = yield* validateLabel("hostname", identity.app);
39
- const roleSafe = yield* validateLabel("hostname", normalizeServiceSegment(role));
40
- const host = (() => {
41
- if (identity.stack === "main") return `${roleSafe}.${app}.localhost`;
42
- return `${roleSafe}.${identity.stack.toLowerCase()}.${app}.localhost`;
43
- })();
44
- if (host.length > MAX_HOSTNAME_LEN) return yield* Effect.fail(new RouterValidationError({
45
- field: "hostname",
46
- value: host,
47
- detail: `exceeds ${MAX_HOSTNAME_LEN}-char limit`
48
- }));
49
- if (identity.stack !== "main") yield* validateLabel("hostname", identity.stack.toLowerCase());
50
- return host;
12
+ /** Project the substrate-blind hostname error into the router error
13
+ * union. The shape is identical (tagged class with field/value/detail);
14
+ * this exists so intra-L3 composers (file-provider, resolveRoute) can
15
+ * keep returning the existing `RouterError` union without a per-call
16
+ * `mapError` at every call site. */
17
+ const liftHostnameError = (cause) => new RouterValidationError({
18
+ field: cause.field,
19
+ value: cause.value,
20
+ detail: cause.detail
51
21
  });
22
+ /** Intra-L3 alias for substrate's `routedHostname` that maps the
23
+ * substrate error onto the router orchestrator's `RouterError` union.
24
+ *
25
+ * L2 plugins MUST NOT import this — they import `routedHostname` from
26
+ * `substrate/runtime/routed-url.ts` directly. This adapter exists only
27
+ * for the orchestrator's own composers. */
28
+ const routerHostname = (identity, role) => routedHostname(identity, role).pipe(Effect.mapError(liftHostnameError));
52
29
  const canonicalDispatchTuple = (inputs) => JSON.stringify([
53
30
  "devstack-router-dispatch-v1",
54
31
  inputs.identity.app,
@@ -88,20 +65,7 @@ const dispatchFileId = (inputs) => Effect.gen(function* () {
88
65
  }));
89
66
  return id;
90
67
  });
91
- /** URL-construction helper: `<scheme>://<hostname>:<port>`.
92
- *
93
- * - `http` / `h2c` → `http://…` (h2c is HTTP/2 cleartext; Traefik
94
- * handles upstream selection internally, the URL scheme is still
95
- * `http`).
96
- * - `tcp` → `tcp://…` — used by the file-provider renderer to
97
- * distinguish from HTTP at YAML-emit time. Consumers of the
98
- * resolved URL (manifest, codegen) translate back to whatever
99
- * scheme their protocol expects (e.g. `postgres://`, `redis://`). */
100
- const renderUrl = (parts) => {
101
- if (parts.protocol === "tcp") return `tcp://${parts.hostname}:${parts.port}`;
102
- return `http://${parts.hostname}:${parts.port}`;
103
- };
104
68
  //#endregion
105
- export { dispatchFileId, renderUrl, routerHostname };
69
+ export { dispatchFileId, normalizeDispatchSegment, routerHostname };
106
70
 
107
71
  //# sourceMappingURL=hostname.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"hostname.mjs","names":[],"sources":["../../../src/orchestrators/router/hostname.ts"],"sourcesContent":["// Hostname + dispatch-id minting helpers.\n//\n// Architecture distilled-doc §\"Outputs / capabilities provided\":\n//\n// - default (\"main\") stack: `<service>.<app>.localhost`\n// - every other stack: `<service>.<stack>.<app>.localhost`\n//\n// \"service\" here is the dispatch-id's `role` segment — NOT the\n// plugin's name. Plugins emit a `(serviceKey, role)` DispatchId; we\n// fold the role into the hostname, since the role is the\n// user-meaningful side (\"api\", \"key-server\", \"indexer-metrics\") and\n// the service key already encodes plugin + app + stack on the\n// dispatch-file side.\n//\n// Architecture invariants:\n// #7 — Distinct `(app, stack, service)` triples MUST produce\n// distinct hostnames AND distinct dispatch ids. The default-\n// stack omission is intentional UX; every other stack\n// includes the stack segment so parallel stacks of the same\n// app never collide.\n// #8 — Hostname service labels fold dots to a label-safe separator.\n// Dispatch ids are minted from a canonical source tuple plus a\n// SHA-256 digest, so lossy readable folding cannot collide.\n// #13 — User-influenceable strings are validated against a\n// conservative character set before render.\n\nimport { createHash } from 'node:crypto';\n\nimport { Effect } from 'effect';\nimport type { DispatchId } from '../../contracts/routable.ts';\nimport type { Identity } from '../../substrate/identity.ts';\nimport { RouterValidationError } from './errors.ts';\n\n/** The conventional \"default\" stack name. Hostnames for this stack\n * omit the stack segment per the architecture's UX rule. */\nexport const DEFAULT_STACK = 'main';\n\n// RFC-1035 single-label regex: starts/ends with alphanumeric, allows\n// internal hyphens, length 1-63. We use the same shape for app, role,\n// and (post-fold) the stack segment.\nconst LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;\n// The readable prefix is capped, and the SHA-256 suffix is fixed size.\nconst MAX_DISPATCH_ID_LEN = 140;\nconst MAX_DISPATCH_ID_READABLE_LEN = 48;\n// 253 chars is the RFC-1035 full-name max. We stay well below.\nconst MAX_HOSTNAME_LEN = 200;\n\n/** Validate a single label-shaped string. Returns the input or fails\n * with a typed `RouterValidationError`. */\nconst validateLabel = (\n\tfield: RouterValidationError['field'],\n\tvalue: string,\n): Effect.Effect<string, RouterValidationError> => {\n\tif (!LABEL.test(value)) {\n\t\treturn Effect.fail(\n\t\t\tnew RouterValidationError({\n\t\t\t\tfield,\n\t\t\t\tvalue,\n\t\t\t\tdetail: 'expected lower-case RFC-1035 label (alphanumeric + internal hyphens, 1-63 chars)',\n\t\t\t}),\n\t\t);\n\t}\n\treturn Effect.succeed(value);\n};\n\n/** Fold dots → hyphens (architecture invariant #8) and lower-case.\n * Pure, no validation. The output is fed to `validateLabel`. */\nexport const normalizeServiceSegment = (raw: string): string =>\n\traw.toLowerCase().replace(/\\./g, '-');\n\n/** Build the human-readable prefix of a dispatch id. This is not the\n * identity key; uniqueness comes from the hash over the canonical\n * tuple in `dispatchFileId`. */\nexport const normalizeDispatchSegment = (raw: string): string =>\n\traw\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t.replace(/-+/g, '-')\n\t\t.replace(/^-|-$/g, '') || 'route';\n\n// ---------------------------------------------------------------------------\n// Hostname minting\n// ---------------------------------------------------------------------------\n\n/** Mint the per-Routable hostname from `(identity, role)`.\n *\n * Default stack: `<role>.<app>.localhost`\n * Other stacks: `<role>.<stack>.<app>.localhost`\n *\n * The `role` segment is folded + validated; the `app` and `stack`\n * segments are also validated (they come from `Identity`, which the\n * substrate has already validated once at boot, but defense-in-depth\n * is cheap and the failure mode is a programming error).\n *\n * Architecture invariant #7: this MUST embed the stack dimension for\n * non-default stacks. Distinct identities → distinct hostnames. */\nexport const routerHostname = (\n\tidentity: Identity,\n\trole: string,\n): Effect.Effect<string, RouterValidationError> =>\n\tEffect.gen(function* () {\n\t\tconst app = yield* validateLabel('hostname', identity.app);\n\t\tconst folded = normalizeServiceSegment(role);\n\t\tconst roleSafe = yield* validateLabel('hostname', folded);\n\t\tconst host = (() => {\n\t\t\tif (identity.stack === DEFAULT_STACK) {\n\t\t\t\treturn `${roleSafe}.${app}.localhost`;\n\t\t\t}\n\t\t\tconst stackSafe = identity.stack.toLowerCase();\n\t\t\t// `validateLabel` runs *after* lower-casing so the stack name\n\t\t\t// (Identity brand) is held to the same character set as the\n\t\t\t// role and the app. Boot validation should already enforce\n\t\t\t// this; we re-check defensively.\n\t\t\treturn `${roleSafe}.${stackSafe}.${app}.localhost`;\n\t\t})();\n\t\tif (host.length > MAX_HOSTNAME_LEN) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew RouterValidationError({\n\t\t\t\t\tfield: 'hostname',\n\t\t\t\t\tvalue: host,\n\t\t\t\t\tdetail: `exceeds ${MAX_HOSTNAME_LEN}-char limit`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\t// Re-validate the stack segment for non-default stacks now that\n\t\t// it's part of a composed hostname.\n\t\tif (identity.stack !== DEFAULT_STACK) {\n\t\t\tyield* validateLabel('hostname', identity.stack.toLowerCase());\n\t\t}\n\t\treturn host;\n\t});\n\n// ---------------------------------------------------------------------------\n// Dispatch-file id minting\n// ---------------------------------------------------------------------------\n\nexport interface DispatchFileIdInputs {\n\treadonly identity: Identity;\n\treadonly dispatch: DispatchId;\n}\n\nconst canonicalDispatchTuple = (inputs: DispatchFileIdInputs): string =>\n\tJSON.stringify([\n\t\t'devstack-router-dispatch-v1',\n\t\tinputs.identity.app,\n\t\tinputs.identity.stack,\n\t\tinputs.dispatch.serviceKey,\n\t\tinputs.dispatch.role,\n\t]);\n\nconst truncateReadable = (value: string): string =>\n\t(value.length <= MAX_DISPATCH_ID_READABLE_LEN\n\t\t? value\n\t\t: value.slice(0, MAX_DISPATCH_ID_READABLE_LEN).replace(/-+$/g, '')) || 'route';\n\n/** Mint a dispatch-file id from the full route identity tuple. The id\n * is used as the file name in the global file-provider directory\n * (one file per backend), and as the Traefik router/service name\n * inside the file.\n *\n * Format: `r1-<readable-prefix>-<sha256>`.\n *\n * The readable prefix is intentionally lossy; it exists only to make\n * directory listings diagnosable. The SHA-256 suffix is over the\n * canonical `(version, app, stack, serviceKey, role)` tuple, so\n * raw underscores, separator strings, case, and dot/hyphen folding do\n * not affect identity. */\nexport const dispatchFileId = (\n\tinputs: DispatchFileIdInputs,\n): Effect.Effect<string, RouterValidationError> =>\n\tEffect.gen(function* () {\n\t\tconst readable = truncateReadable(\n\t\t\tnormalizeDispatchSegment(\n\t\t\t\t[\n\t\t\t\t\tinputs.identity.app,\n\t\t\t\t\tinputs.identity.stack,\n\t\t\t\t\tinputs.dispatch.serviceKey,\n\t\t\t\t\tinputs.dispatch.role,\n\t\t\t\t].join('-'),\n\t\t\t),\n\t\t);\n\t\tconst hash = createHash('sha256').update(canonicalDispatchTuple(inputs)).digest('hex');\n\t\tconst id = `r1-${readable}-${hash}`;\n\t\tif (id.length > MAX_DISPATCH_ID_LEN) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew RouterValidationError({\n\t\t\t\t\tfield: 'dispatchId',\n\t\t\t\t\tvalue: id,\n\t\t\t\t\tdetail: `exceeds ${MAX_DISPATCH_ID_LEN}-char limit`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (!/^r1-[a-z0-9][a-z0-9-]*-[a-f0-9]{64}$/.test(id)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew RouterValidationError({\n\t\t\t\t\tfield: 'dispatchId',\n\t\t\t\t\tvalue: id,\n\t\t\t\t\tdetail: 'expected r1-<lower-case-slug>-<sha256-hex>',\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn id;\n\t});\n\n/** URL-construction helper: `<scheme>://<hostname>:<port>`.\n *\n * - `http` / `h2c` → `http://…` (h2c is HTTP/2 cleartext; Traefik\n * handles upstream selection internally, the URL scheme is still\n * `http`).\n * - `tcp` → `tcp://…` — used by the file-provider renderer to\n * distinguish from HTTP at YAML-emit time. Consumers of the\n * resolved URL (manifest, codegen) translate back to whatever\n * scheme their protocol expects (e.g. `postgres://`, `redis://`). */\nexport const renderUrl = (parts: {\n\treadonly protocol: 'http' | 'h2c' | 'tcp';\n\treadonly hostname: string;\n\treadonly port: number;\n}): string => {\n\tif (parts.protocol === 'tcp') return `tcp://${parts.hostname}:${parts.port}`;\n\treturn `http://${parts.hostname}:${parts.port}`;\n};\n"],"mappings":";;;AAwCA,MAAM,QAAQ;AAEd,MAAM,sBAAsB;AAC5B,MAAM,+BAA+B;AAErC,MAAM,mBAAmB;;;AAIzB,MAAM,iBACL,OACA,UACkD;AAClD,KAAI,CAAC,MAAM,KAAK,MAAM,CACrB,QAAO,OAAO,KACb,IAAI,sBAAsB;EACzB;EACA;EACA,QAAQ;EACR,CAAC,CACF;AAEF,QAAO,OAAO,QAAQ,MAAM;;;;AAK7B,MAAa,2BAA2B,QACvC,IAAI,aAAa,CAAC,QAAQ,OAAO,IAAI;;;;AAKtC,MAAa,4BAA4B,QACxC,IACE,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,OAAO,IAAI,CACnB,QAAQ,UAAU,GAAG,IAAI;;;;;;;;;;;;;AAkB5B,MAAa,kBACZ,UACA,SAEA,OAAO,IAAI,aAAa;CACvB,MAAM,MAAM,OAAO,cAAc,YAAY,SAAS,IAAI;CAE1D,MAAM,WAAW,OAAO,cAAc,YADvB,wBAAwB,KACiB,CAAC;CACzD,MAAM,cAAc;AACnB,MAAI,SAAS,UAAA,OACZ,QAAO,GAAG,SAAS,GAAG,IAAI;AAO3B,SAAO,GAAG,SAAS,GALD,SAAS,MAAM,aAKF,CAAC,GAAG,IAAI;KACpC;AACJ,KAAI,KAAK,SAAS,iBACjB,QAAO,OAAO,OAAO,KACpB,IAAI,sBAAsB;EACzB,OAAO;EACP,OAAO;EACP,QAAQ,WAAW,iBAAiB;EACpC,CAAC,CACF;AAIF,KAAI,SAAS,UAAA,OACZ,QAAO,cAAc,YAAY,SAAS,MAAM,aAAa,CAAC;AAE/D,QAAO;EACN;AAWH,MAAM,0BAA0B,WAC/B,KAAK,UAAU;CACd;CACA,OAAO,SAAS;CAChB,OAAO,SAAS;CAChB,OAAO,SAAS;CAChB,OAAO,SAAS;CAChB,CAAC;AAEH,MAAM,oBAAoB,WACxB,MAAM,UAAU,+BACd,QACA,MAAM,MAAM,GAAG,6BAA6B,CAAC,QAAQ,QAAQ,GAAG,KAAK;;;;;;;;;;;;;AAczE,MAAa,kBACZ,WAEA,OAAO,IAAI,aAAa;CAYvB,MAAM,KAAK,MAXM,iBAChB,yBACC;EACC,OAAO,SAAS;EAChB,OAAO,SAAS;EAChB,OAAO,SAAS;EAChB,OAAO,SAAS;EAChB,CAAC,KAAK,IAAI,CACX,CAGuB,CAAC,GADb,WAAW,SAAS,CAAC,OAAO,uBAAuB,OAAO,CAAC,CAAC,OAAO,MAC/C;AACjC,KAAI,GAAG,SAAS,oBACf,QAAO,OAAO,OAAO,KACpB,IAAI,sBAAsB;EACzB,OAAO;EACP,OAAO;EACP,QAAQ,WAAW,oBAAoB;EACvC,CAAC,CACF;AAEF,KAAI,CAAC,uCAAuC,KAAK,GAAG,CACnD,QAAO,OAAO,OAAO,KACpB,IAAI,sBAAsB;EACzB,OAAO;EACP,OAAO;EACP,QAAQ;EACR,CAAC,CACF;AAEF,QAAO;EACN;;;;;;;;;;AAWH,MAAa,aAAa,UAIZ;AACb,KAAI,MAAM,aAAa,MAAO,QAAO,SAAS,MAAM,SAAS,GAAG,MAAM;AACtE,QAAO,UAAU,MAAM,SAAS,GAAG,MAAM"}
1
+ {"version":3,"file":"hostname.mjs","names":[],"sources":["../../../src/orchestrators/router/hostname.ts"],"sourcesContent":["// Hostname + dispatch-id minting helpers.\n//\n// Architecture distilled-doc §\"Outputs / capabilities provided\":\n//\n// - default (\"main\") stack: `<service>.<app>.localhost`\n// - every other stack: `<service>.<stack>.<app>.localhost`\n//\n// \"service\" here is the dispatch-id's `role` segment — NOT the\n// plugin's name. Plugins emit a `(serviceKey, role)` DispatchId; we\n// fold the role into the hostname, since the role is the\n// user-meaningful side (\"api\", \"key-server\", \"indexer-metrics\") and\n// the service key already encodes plugin + app + stack on the\n// dispatch-file side.\n//\n// Architecture invariants:\n// #7 — Distinct `(app, stack, service)` triples MUST produce\n// distinct hostnames AND distinct dispatch ids. The default-\n// stack omission is intentional UX; every other stack\n// includes the stack segment so parallel stacks of the same\n// app never collide.\n// #8 — Hostname service labels fold dots to a label-safe separator.\n// Dispatch ids are minted from a canonical source tuple plus a\n// SHA-256 digest, so lossy readable folding cannot collide.\n// #13 — User-influenceable strings are validated against a\n// conservative character set before render.\n//\n// As of B1 (boundary-repair): pure URL composition + hostname minting\n// live in `substrate/runtime/routed-url.ts` so L2 plugins can call\n// them without reaching across the L2→L3 boundary. This module re-\n// exports the substrate primitives, owns dispatch-file id minting\n// (router-orchestrator-specific), and provides a `routerHostname`\n// adapter that projects substrate's `HostnameValidationError` into the\n// router's `RouterValidationError` union for intra-L3 callers\n// (file-provider, resolveRoute).\n\nimport { createHash } from 'node:crypto';\n\nimport { Effect } from 'effect';\nimport type { DispatchId } from '../../contracts/routable.ts';\nimport type { Identity } from '../../substrate/identity.ts';\nimport {\n\tDEFAULT_STACK,\n\tHostnameValidationError,\n\tnormalizeServiceSegment,\n\trenderUrl,\n\troutedHostname,\n} from '../../substrate/runtime/routed-url.ts';\nimport { RouterValidationError } from './errors.ts';\n\n// Re-export substrate-blind primitives for intra-L3 callers that\n// continue to import from this module path.\nexport { DEFAULT_STACK, normalizeServiceSegment, renderUrl };\n\n// The readable prefix is capped, and the SHA-256 suffix is fixed size.\nconst MAX_DISPATCH_ID_LEN = 140;\nconst MAX_DISPATCH_ID_READABLE_LEN = 48;\n\n/** Build the human-readable prefix of a dispatch id. This is not the\n * identity key; uniqueness comes from the hash over the canonical\n * tuple in `dispatchFileId`. */\nexport const normalizeDispatchSegment = (raw: string): string =>\n\traw\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t.replace(/-+/g, '-')\n\t\t.replace(/^-|-$/g, '') || 'route';\n\n// ---------------------------------------------------------------------------\n// Hostname minting — orchestrator-internal adapter\n// ---------------------------------------------------------------------------\n\n/** Project the substrate-blind hostname error into the router error\n * union. The shape is identical (tagged class with field/value/detail);\n * this exists so intra-L3 composers (file-provider, resolveRoute) can\n * keep returning the existing `RouterError` union without a per-call\n * `mapError` at every call site. */\nconst liftHostnameError = (cause: HostnameValidationError): RouterValidationError =>\n\tnew RouterValidationError({\n\t\tfield: cause.field,\n\t\tvalue: cause.value,\n\t\tdetail: cause.detail,\n\t});\n\n/** Intra-L3 alias for substrate's `routedHostname` that maps the\n * substrate error onto the router orchestrator's `RouterError` union.\n *\n * L2 plugins MUST NOT import this — they import `routedHostname` from\n * `substrate/runtime/routed-url.ts` directly. This adapter exists only\n * for the orchestrator's own composers. */\nexport const routerHostname = (\n\tidentity: Identity,\n\trole: string,\n): Effect.Effect<string, RouterValidationError> =>\n\troutedHostname(identity, role).pipe(Effect.mapError(liftHostnameError));\n\n// ---------------------------------------------------------------------------\n// Dispatch-file id minting\n// ---------------------------------------------------------------------------\n\nexport interface DispatchFileIdInputs {\n\treadonly identity: Identity;\n\treadonly dispatch: DispatchId;\n}\n\nconst canonicalDispatchTuple = (inputs: DispatchFileIdInputs): string =>\n\tJSON.stringify([\n\t\t'devstack-router-dispatch-v1',\n\t\tinputs.identity.app,\n\t\tinputs.identity.stack,\n\t\tinputs.dispatch.serviceKey,\n\t\tinputs.dispatch.role,\n\t]);\n\nconst truncateReadable = (value: string): string =>\n\t(value.length <= MAX_DISPATCH_ID_READABLE_LEN\n\t\t? value\n\t\t: value.slice(0, MAX_DISPATCH_ID_READABLE_LEN).replace(/-+$/g, '')) || 'route';\n\n/** Mint a dispatch-file id from the full route identity tuple. The id\n * is used as the file name in the global file-provider directory\n * (one file per backend), and as the Traefik router/service name\n * inside the file.\n *\n * Format: `r1-<readable-prefix>-<sha256>`.\n *\n * The readable prefix is intentionally lossy; it exists only to make\n * directory listings diagnosable. The SHA-256 suffix is over the\n * canonical `(version, app, stack, serviceKey, role)` tuple, so\n * raw underscores, separator strings, case, and dot/hyphen folding do\n * not affect identity. */\nexport const dispatchFileId = (\n\tinputs: DispatchFileIdInputs,\n): Effect.Effect<string, RouterValidationError> =>\n\tEffect.gen(function* () {\n\t\tconst readable = truncateReadable(\n\t\t\tnormalizeDispatchSegment(\n\t\t\t\t[\n\t\t\t\t\tinputs.identity.app,\n\t\t\t\t\tinputs.identity.stack,\n\t\t\t\t\tinputs.dispatch.serviceKey,\n\t\t\t\t\tinputs.dispatch.role,\n\t\t\t\t].join('-'),\n\t\t\t),\n\t\t);\n\t\tconst hash = createHash('sha256').update(canonicalDispatchTuple(inputs)).digest('hex');\n\t\tconst id = `r1-${readable}-${hash}`;\n\t\tif (id.length > MAX_DISPATCH_ID_LEN) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew RouterValidationError({\n\t\t\t\t\tfield: 'dispatchId',\n\t\t\t\t\tvalue: id,\n\t\t\t\t\tdetail: `exceeds ${MAX_DISPATCH_ID_LEN}-char limit`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (!/^r1-[a-z0-9][a-z0-9-]*-[a-f0-9]{64}$/.test(id)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew RouterValidationError({\n\t\t\t\t\tfield: 'dispatchId',\n\t\t\t\t\tvalue: id,\n\t\t\t\t\tdetail: 'expected r1-<lower-case-slug>-<sha256-hex>',\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn id;\n\t});\n"],"mappings":";;;;;AAsDA,MAAM,sBAAsB;AAC5B,MAAM,+BAA+B;;;;AAKrC,MAAa,4BAA4B,QACxC,IACE,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,OAAO,IAAI,CACnB,QAAQ,UAAU,GAAG,IAAI;;;;;;AAW5B,MAAM,qBAAqB,UAC1B,IAAI,sBAAsB;CACzB,OAAO,MAAM;CACb,OAAO,MAAM;CACb,QAAQ,MAAM;CACd,CAAC;;;;;;;AAQH,MAAa,kBACZ,UACA,SAEA,eAAe,UAAU,KAAK,CAAC,KAAK,OAAO,SAAS,kBAAkB,CAAC;AAWxE,MAAM,0BAA0B,WAC/B,KAAK,UAAU;CACd;CACA,OAAO,SAAS;CAChB,OAAO,SAAS;CAChB,OAAO,SAAS;CAChB,OAAO,SAAS;CAChB,CAAC;AAEH,MAAM,oBAAoB,WACxB,MAAM,UAAU,+BACd,QACA,MAAM,MAAM,GAAG,6BAA6B,CAAC,QAAQ,QAAQ,GAAG,KAAK;;;;;;;;;;;;;AAczE,MAAa,kBACZ,WAEA,OAAO,IAAI,aAAa;CAYvB,MAAM,KAAK,MAXM,iBAChB,yBACC;EACC,OAAO,SAAS;EAChB,OAAO,SAAS;EAChB,OAAO,SAAS;EAChB,OAAO,SAAS;EAChB,CAAC,KAAK,IAAI,CACX,CAGuB,CAAC,GADb,WAAW,SAAS,CAAC,OAAO,uBAAuB,OAAO,CAAC,CAAC,OAAO,MAC/C;AACjC,KAAI,GAAG,SAAS,oBACf,QAAO,OAAO,OAAO,KACpB,IAAI,sBAAsB;EACzB,OAAO;EACP,OAAO;EACP,QAAQ,WAAW,oBAAoB;EACvC,CAAC,CACF;AAEF,KAAI,CAAC,uCAAuC,KAAK,GAAG,CACnD,QAAO,OAAO,OAAO,KACpB,IAAI,sBAAsB;EACzB,OAAO;EACP,OAAO;EACP,QAAQ;EACR,CAAC,CACF;AAEF,QAAO;EACN"}
@@ -0,0 +1,11 @@
1
+ import "./errors.mjs";
2
+ import "./sentinels.mjs";
3
+ import "./traefik-container.mjs";
4
+ import "./profile.mjs";
5
+ import "./entrypoints.mjs";
6
+ import "./cors.mjs";
7
+ import "../../substrate/runtime/routed-url.mjs";
8
+ import "./hostname.mjs";
9
+ import "./file-provider.mjs";
10
+ import "./service.mjs";
11
+ export {};
@@ -1,5 +1,5 @@
1
- import { createHash } from "node:crypto";
2
1
  import { readFileSync } from "node:fs";
2
+ import { createHash } from "node:crypto";
3
3
  import { join } from "node:path";
4
4
  import { execFileSync } from "node:child_process";
5
5
  import { homedir, userInfo } from "node:os";
@@ -104,6 +104,6 @@ const makeDefaultRouterProfile = (options = {}) => makeRouterProfile({
104
104
  ...options.stateRoot === void 0 ? {} : { stateRoot: options.stateRoot }
105
105
  });
106
106
  //#endregion
107
- export { makeDefaultRouterProfile };
107
+ export { currentRouterUserId, makeDefaultRouterProfile, makeRouterProfile, resolveDockerContextId };
108
108
 
109
109
  //# sourceMappingURL=profile.mjs.map
@@ -0,0 +1,26 @@
1
+ import { LabelKey } from "../../runtime/docker/labels.mjs";
2
+ import "../../runtime/docker/index.mjs";
3
+ //#region src/orchestrators/router/sentinels.ts
4
+ /** Synthetic app name stamped onto the router-singleton container. The
5
+ * lifecycle-prune orchestrator uses this to distinguish the router
6
+ * group from per-stack groups (per-stack `wipe` doesn't touch the
7
+ * router). */
8
+ const ROUTER_SHARED_APP = "devstack-router";
9
+ /** Prefix for the router-singleton container name. The on-disk
10
+ * profile-state directory under `runtime/router/<profile-fingerprint>/`
11
+ * is matched against this prefix during profile-state cleanup so a
12
+ * router-stack removal also drops the profile dir. */
13
+ const ROUTER_CONTAINER_NAME_PREFIX = "devstack-router-";
14
+ /** Value stamped into the L1 generic `LabelKey.kind` slot for router
15
+ * containers. L1 is plugin-blind: the router orchestrator owns this
16
+ * literal. */
17
+ const ROUTER_KIND_LABEL_VALUE = "router";
18
+ /** Label key carrying the router profile id (so `docker ps` filters by
19
+ * profile alone resolve the right router container). Reuses the L1
20
+ * generic `subkind` slot — same plugin-blind convention as
21
+ * `LabelKey.kind`. */
22
+ const ROUTER_PROFILE_LABEL = LabelKey.subkind;
23
+ //#endregion
24
+ export { ROUTER_CONTAINER_NAME_PREFIX, ROUTER_KIND_LABEL_VALUE, ROUTER_PROFILE_LABEL, ROUTER_SHARED_APP };
25
+
26
+ //# sourceMappingURL=sentinels.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentinels.mjs","names":[],"sources":["../../../src/orchestrators/router/sentinels.ts"],"sourcesContent":["// Router-singleton sentinels.\n//\n// The router orchestrator owns one Traefik container per router profile\n// (NOT per-stack); these literals identify that container in label\n// matchers, lifecycle-prune predicates, and on-disk profile-state\n// fingerprints. Centralizing them here means cleanup, traefik-container,\n// and lifecycle-prune all read the same source.\n//\n// Architecture distilled-doc §\"Layer composition\" — the router is L3;\n// these sentinels are not plugin-author-visible (they're orchestrator-\n// owned) but must stay consistent across the router's modules.\n\nimport { LabelKey } from '../../runtime/docker/index.ts';\n\n/** Synthetic app name stamped onto the router-singleton container. The\n * lifecycle-prune orchestrator uses this to distinguish the router\n * group from per-stack groups (per-stack `wipe` doesn't touch the\n * router). */\nexport const ROUTER_SHARED_APP = 'devstack-router';\n\n/** Prefix for the router-singleton container name. The on-disk\n * profile-state directory under `runtime/router/<profile-fingerprint>/`\n * is matched against this prefix during profile-state cleanup so a\n * router-stack removal also drops the profile dir. */\nexport const ROUTER_CONTAINER_NAME_PREFIX = 'devstack-router-';\n\n/** Value stamped into the L1 generic `LabelKey.kind` slot for router\n * containers. L1 is plugin-blind: the router orchestrator owns this\n * literal. */\nexport const ROUTER_KIND_LABEL_VALUE = 'router';\n\n/** Label key carrying the router profile id (so `docker ps` filters by\n * profile alone resolve the right router container). Reuses the L1\n * generic `subkind` slot — same plugin-blind convention as\n * `LabelKey.kind`. */\nexport const ROUTER_PROFILE_LABEL = LabelKey.subkind;\n\n/** Bumps when the traefik-container ops shape changes. Mismatched\n * containers are recreated rather than adopted. */\nexport const ROUTER_CONTAINER_SPEC_VERSION = '3';\n"],"mappings":";;;;;;;AAkBA,MAAa,oBAAoB;;;;;AAMjC,MAAa,+BAA+B;;;;AAK5C,MAAa,0BAA0B;;;;;AAMvC,MAAa,uBAAuB,SAAS"}
@@ -1,26 +1,29 @@
1
1
  import { IdentityContext } from "../../substrate/runtime/paths.mjs";
2
- import { checkHolderLiveness, ownHolder } from "../../substrate/runtime/cross-process/liveness.mjs";
3
- import { DispatchWriteFailed, RouteCollision, RouteReadinessProbeFailed, RouterBootFailed, RouterDisabledRouteUnsupported, RouterValidationError } from "./errors.mjs";
4
- import { dispatchFileId } from "./hostname.mjs";
5
2
  import { atomicWriteFile } from "../../substrate/runtime/atomic-write.mjs";
6
- import { DockerHost, DockerSpawner } from "../../runtime/docker/client.mjs";
3
+ import { logWarningAndIgnore } from "../../substrate/runtime/observability/ignore-with-log.mjs";
4
+ import { LivenessProbeScope, checkHolderLiveness, layerLivenessProbeScope, ownHolder } from "../../substrate/runtime/cross-process/liveness.mjs";
7
5
  import { acquireStackLock } from "../../substrate/runtime/cross-process/stack-lock.mjs";
6
+ import "../../substrate/runtime/observability/index.mjs";
7
+ import { DockerHost, DockerSpawner } from "../../runtime/docker/client.mjs";
8
8
  import { connect, waitForIp } from "../../runtime/docker/network.mjs";
9
- import { waitForHttpEndpoint } from "../../substrate/runtime/http-probe.mjs";
9
+ import "../../runtime/docker/index.mjs";
10
+ import { DispatchWriteFailed, RouteCollision, RouteReadinessProbeFailed, RouterBootFailed, RouterDisabledRouteUnsupported, RouterValidationError } from "./errors.mjs";
10
11
  import { TraefikContainerOpsService, bootstrap } from "./traefik-container.mjs";
11
12
  import { EntrypointRegistry } from "./entrypoints.mjs";
12
13
  import { CORS_MIDDLEWARE_FILENAME, renderCorsMiddlewareYaml } from "./cors.mjs";
14
+ import { dispatchFileId } from "./hostname.mjs";
13
15
  import { detectCollisions, dispatchFileIdFromFilename, dispatchFilename, parseDispatchRouteFile, renderRouteYaml, resolveRoute } from "./file-provider.mjs";
16
+ import { waitForHttpEndpoint } from "../../substrate/runtime/http-probe.mjs";
14
17
  import { Context, Effect, FileSystem, Layer, Ref, SubscriptionRef } from "effect";
15
18
  import * as path from "node:path";
16
19
  import { request } from "node:http";
17
20
  //#region src/orchestrators/router/service.ts
18
21
  const ROUTER_LOCK_TIMEOUT_MILLIS = 12e4;
19
- var RouterConfig = class extends Context.Service()("@devstack-rewrite/orchestrators/router/RouterConfig") {};
22
+ var RouterConfig = class extends Context.Service()("@devstack/orchestrators/router/RouterConfig") {};
20
23
  /** Default-config layer for tests. Production wires this from
21
24
  * `runtime-composition.ts` at the engine boundary. */
22
25
  const layerRouterConfigLiteral = (cfg) => Layer.succeed(RouterConfig)(cfg);
23
- var UpstreamResolverService = class extends Context.Service()("@devstack-rewrite/orchestrators/router/UpstreamResolver") {};
26
+ var UpstreamResolverService = class extends Context.Service()("@devstack/orchestrators/router/UpstreamResolver") {};
24
27
  const layerDockerUpstreamResolver = (profile) => Layer.effect(UpstreamResolverService, Effect.gen(function* () {
25
28
  const dockerHost = yield* DockerHost;
26
29
  const dockerSpawner = yield* DockerSpawner;
@@ -41,7 +44,7 @@ const layerDockerUpstreamResolver = (profile) => Layer.effect(UpstreamResolverSe
41
44
  })
42
45
  });
43
46
  }));
44
- var RouterService = class extends Context.Service()("@devstack-rewrite/orchestrators/router/Router") {};
47
+ var RouterService = class extends Context.Service()("@devstack/orchestrators/router/Router") {};
45
48
  const warnDispatchDecodeDiagnostic = (diagnostic) => Effect.gen(function* () {
46
49
  yield* Effect.annotateCurrentSpan({
47
50
  "router.dispatch.path": diagnostic.path,
@@ -106,9 +109,10 @@ const classifyDispatchLease = (route) => {
106
109
  return checkHolderLiveness(route.lease.owner).pipe(Effect.map((status) => status === "dead" ? "stale" : "live"), Effect.catch(() => Effect.succeed("live")));
107
110
  };
108
111
  const sweepStaleDispatchRoutes = (fs, profile, routes, dispatchFileId) => Effect.gen(function* () {
112
+ const probe = yield* LivenessProbeScope;
109
113
  const active = [];
110
114
  for (const route of routes) {
111
- if ((yield* classifyDispatchLease(route)) === "stale") {
115
+ if ((route.lease === null ? "unknown-owner" : yield* probe.probeHolderLiveness(route.lease.owner).pipe(Effect.map((s) => s === "dead" ? "stale" : "live"), Effect.catch(() => Effect.succeed("live")))) === "stale") {
112
116
  const filePath = path.join(profile.dispatchDir, dispatchFilename(route.dispatchFileId));
113
117
  yield* fs.remove(filePath).pipe(Effect.mapError((cause) => new DispatchWriteFailed({
114
118
  dispatchFileId,
@@ -121,7 +125,7 @@ const sweepStaleDispatchRoutes = (fs, profile, routes, dispatchFileId) => Effect
121
125
  active.push(route);
122
126
  }
123
127
  return active;
124
- });
128
+ }).pipe(Effect.provide(layerLivenessProbeScope));
125
129
  const makeRouteLease = (profile, identity) => ({
126
130
  version: 1,
127
131
  routerProfileId: profile.id,
@@ -253,9 +257,9 @@ const endpointFromResolvedRoute = (decl, resolved) => {
253
257
  };
254
258
  };
255
259
  const removeDispatchFile = (fs, profile, resolved) => fs.remove(path.join(profile.dispatchDir, dispatchFilename(resolved.dispatchFileId))).pipe(Effect.ignore);
256
- const waitForPublicRouteReadiness = (cfg, endpoint, resolved) => {
260
+ const waitForPublicRouteReadiness = (cfg, decl, endpoint, resolved) => {
257
261
  const options = cfg.routeReadinessProbe;
258
- if (options?.enabled !== true || cfg.disabled || resolved.wireProtocol === "tcp") return Effect.void;
262
+ if (options?.enabled !== true || cfg.disabled || resolved.wireProtocol === "tcp" || decl.readiness === "deferred") return Effect.void;
259
263
  const timeoutMs = options.timeoutMs ?? 6e4;
260
264
  const intervalMs = options.intervalMs ?? DEFAULT_ROUTE_READINESS_INTERVAL_MS;
261
265
  const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_ROUTE_READINESS_REQUEST_TIMEOUT_MS;
@@ -299,12 +303,14 @@ const layerRouterService = Layer.effect(RouterService, Effect.gen(function* () {
299
303
  }
300
304
  const cached = yield* Ref.get(bootRef);
301
305
  if (cached !== null) return cached;
302
- const protectedRouteLeaseIds = yield* Effect.scoped(Effect.gen(function* () {
306
+ return yield* Effect.scoped(Effect.gen(function* () {
303
307
  yield* acquireStackLock(profile.dispatchLockFile, ROUTER_LOCK_TIMEOUT_MILLIS).pipe(Effect.mapError((cause) => new RouterBootFailed({
304
308
  stage: "ensure-container",
305
309
  detail: `failed to acquire router dispatch lock ${profile.dispatchLockFile}`,
306
310
  cause
307
311
  })));
312
+ const bootedByPeer = yield* Ref.get(bootRef);
313
+ if (bootedByPeer !== null) return bootedByPeer;
308
314
  yield* fs.makeDirectory(profile.dispatchDir, { recursive: true }).pipe(Effect.mapError((cause) => new RouterBootFailed({
309
315
  stage: "write-shared-config",
310
316
  detail: `makeDirectory(${profile.dispatchDir}) failed`,
@@ -320,27 +326,25 @@ const layerRouterService = Layer.effect(RouterService, Effect.gen(function* () {
320
326
  detail: cause.detail,
321
327
  cause
322
328
  })));
323
- return [...(yield* sweepStaleDispatchRoutes(fs, profile, existingDispatchScan.routes, "router-boot").pipe(Effect.mapError((cause) => new RouterBootFailed({
329
+ const protectedRouteLeaseIds = [...(yield* sweepStaleDispatchRoutes(fs, profile, existingDispatchScan.routes, "router-boot").pipe(Effect.mapError((cause) => new RouterBootFailed({
324
330
  stage: "write-shared-config",
325
331
  detail: cause.detail,
326
332
  cause
327
333
  })))).map((route) => route.dispatchFileId), ...existingDispatchScan.unknownRouteFileIds];
328
- }));
329
- const report = yield* Effect.scoped(Effect.gen(function* () {
330
334
  yield* acquireStackLock(profile.bootstrapLockFile, ROUTER_LOCK_TIMEOUT_MILLIS).pipe(Effect.mapError((cause) => new RouterBootFailed({
331
335
  stage: "ensure-container",
332
336
  detail: `failed to acquire router bootstrap lock ${profile.bootstrapLockFile}`,
333
337
  cause
334
338
  })));
335
- return yield* bootstrap({
339
+ const booted = yield* bootstrap({
336
340
  image: cfg.image,
337
341
  entrypoints: registry.all(),
338
342
  profile,
339
343
  protectedRouteLeaseIds
340
344
  }).pipe(Effect.provideService(TraefikContainerOpsService, traefikOps));
345
+ yield* Ref.set(bootRef, booted);
346
+ return booted;
341
347
  }));
342
- yield* Ref.set(bootRef, report);
343
- return report;
344
348
  }).pipe(Effect.withSpan("orchestrator.router.boot"));
345
349
  const contributeRoute = (decl) => Effect.gen(function* () {
346
350
  const profile = cfg.profile;
@@ -383,30 +387,29 @@ const layerRouterService = Layer.effect(RouterService, Effect.gen(function* () {
383
387
  return "owned";
384
388
  });
385
389
  let publishOwnership;
386
- if (cfg.disabled) publishOwnership = yield* publishRouteFile;
387
- else publishOwnership = yield* Effect.scoped(Effect.gen(function* () {
390
+ if (cfg.disabled) {
391
+ publishOwnership = yield* publishRouteFile;
392
+ yield* waitForPublicRouteReadiness(cfg, decl, endpoint, resolved);
393
+ if (publishOwnership !== "reused-live") yield* SubscriptionRef.update(applied, (arr) => [...arr, resolved]);
394
+ } else publishOwnership = yield* Effect.scoped(Effect.gen(function* () {
388
395
  yield* acquireStackLock(profile.dispatchLockFile, ROUTER_LOCK_TIMEOUT_MILLIS).pipe(Effect.mapError((cause) => new DispatchWriteFailed({
389
396
  dispatchFileId: resolved.dispatchFileId,
390
397
  path: profile.dispatchLockFile,
391
398
  detail: `failed to acquire dispatch lock ${profile.dispatchLockFile}`,
392
399
  cause
393
400
  })));
394
- return yield* publishRouteFile;
401
+ const ownership = yield* publishRouteFile;
402
+ yield* waitForPublicRouteReadiness(cfg, decl, endpoint, resolved).pipe(Effect.onError(() => ownership !== "owned" ? Effect.void : removeDispatchFile(fs, profile, resolved)));
403
+ if (ownership !== "reused-live") yield* SubscriptionRef.update(applied, (arr) => [...arr, resolved]);
404
+ return ownership;
405
+ }));
406
+ if (publishOwnership !== "reused-live") yield* Effect.addFinalizer(() => Effect.gen(function* () {
407
+ if (publishOwnership === "owned") yield* Effect.scoped(Effect.gen(function* () {
408
+ yield* acquireStackLock(profile.dispatchLockFile, ROUTER_LOCK_TIMEOUT_MILLIS);
409
+ yield* removeDispatchFile(fs, profile, resolved);
410
+ })).pipe(logWarningAndIgnore("router scope-close cleanup failed", { dispatchFileId: resolved.dispatchFileId }));
411
+ yield* SubscriptionRef.update(applied, (arr) => arr.filter((r) => r.dispatchFileId !== resolved.dispatchFileId));
395
412
  }));
396
- yield* waitForPublicRouteReadiness(cfg, endpoint, resolved).pipe(Effect.onError(() => cfg.disabled || publishOwnership !== "owned" ? Effect.void : Effect.scoped(Effect.gen(function* () {
397
- yield* acquireStackLock(profile.dispatchLockFile, ROUTER_LOCK_TIMEOUT_MILLIS).pipe(Effect.ignore);
398
- yield* removeDispatchFile(fs, profile, resolved);
399
- }))));
400
- if (publishOwnership !== "reused-live") {
401
- yield* SubscriptionRef.update(applied, (arr) => [...arr, resolved]);
402
- yield* Effect.addFinalizer(() => Effect.gen(function* () {
403
- if (publishOwnership === "owned") yield* Effect.scoped(Effect.gen(function* () {
404
- yield* acquireStackLock(profile.dispatchLockFile, ROUTER_LOCK_TIMEOUT_MILLIS);
405
- yield* removeDispatchFile(fs, profile, resolved);
406
- })).pipe(Effect.ignore);
407
- yield* SubscriptionRef.update(applied, (arr) => arr.filter((r) => r.dispatchFileId !== resolved.dispatchFileId));
408
- }));
409
- }
410
413
  return endpoint;
411
414
  }).pipe(Effect.withSpan("orchestrator.router.contributeRoute"));
412
415
  return RouterService.of({
@@ -416,6 +419,6 @@ const layerRouterService = Layer.effect(RouterService, Effect.gen(function* () {
416
419
  });
417
420
  }));
418
421
  //#endregion
419
- export { RouterService, layerDockerUpstreamResolver, layerRouterConfigLiteral, layerRouterService };
422
+ export { RouterConfig, RouterService, UpstreamResolverService, layerDockerUpstreamResolver, layerRouterConfigLiteral, layerRouterService };
420
423
 
421
424
  //# sourceMappingURL=service.mjs.map