@mysten-incubation/devstack 0.1.0 → 0.2.0

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 (858) hide show
  1. package/README.md +7 -4
  2. package/dashboard-ui/assets/grpc-a4usE3Nk.js +3 -0
  3. package/dashboard-ui/assets/index-B82Bh84P.css +1 -0
  4. package/dashboard-ui/assets/index-CRYJ4pru.js +1277 -0
  5. package/dashboard-ui/index.html +2 -2
  6. package/dist/api/define-capabilities.d.mts +1 -20
  7. package/dist/api/define-capabilities.mjs +15 -22
  8. package/dist/api/define-capabilities.mjs.map +1 -1
  9. package/dist/api/define-devstack-with.d.mts +7 -1
  10. package/dist/api/define-devstack-with.mjs.map +1 -1
  11. package/dist/api/define-devstack.mjs.map +1 -1
  12. package/dist/api/define-plugin.d.mts +2 -1
  13. package/dist/api/inference-network.d.mts +31 -17
  14. package/dist/api/inference-network.mjs +40 -43
  15. package/dist/api/inference-network.mjs.map +1 -1
  16. package/dist/api/load-config.mjs +88 -0
  17. package/dist/api/load-config.mjs.map +1 -0
  18. package/dist/api/mode-narrowed-factory.d.mts +1 -1
  19. package/dist/api/mode-narrowed-factory.mjs.map +1 -1
  20. package/dist/api/run-stack-internal.mjs +202 -0
  21. package/dist/api/run-stack-internal.mjs.map +1 -0
  22. package/dist/api/run-stack.d.mts +82 -31
  23. package/dist/api/run-stack.mjs +22 -141
  24. package/dist/api/run-stack.mjs.map +1 -1
  25. package/dist/build-integrations/dapp-kit/index.d.mts +61 -0
  26. package/dist/build-integrations/dapp-kit/index.mjs +58 -0
  27. package/dist/build-integrations/dapp-kit/index.mjs.map +1 -0
  28. package/dist/build-integrations/playwright/config.d.mts +1 -30
  29. package/dist/build-integrations/playwright/config.mjs +1 -27
  30. package/dist/build-integrations/playwright/config.mjs.map +1 -1
  31. package/dist/build-integrations/playwright/errors.d.mts +4 -7
  32. package/dist/build-integrations/playwright/errors.mjs.map +1 -1
  33. package/dist/build-integrations/playwright/global-setup.d.mts +42 -71
  34. package/dist/build-integrations/playwright/global-setup.mjs +142 -218
  35. package/dist/build-integrations/playwright/global-setup.mjs.map +1 -1
  36. package/dist/build-integrations/playwright/index.d.mts +2 -2
  37. package/dist/build-integrations/playwright/index.mjs +2 -2
  38. package/dist/build-integrations/playwright/stack-context.d.mts +3 -1
  39. package/dist/build-integrations/playwright/stack-context.mjs +4 -2
  40. package/dist/build-integrations/playwright/stack-context.mjs.map +1 -1
  41. package/dist/build-integrations/playwright/wallet-context.mjs +44 -19
  42. package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
  43. package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -1
  44. package/dist/build-integrations/runtime/conventional-routes.mjs.map +1 -1
  45. package/dist/build-integrations/runtime/dapp-kit-slot.mjs.map +1 -1
  46. package/dist/build-integrations/runtime/discover.d.mts +6 -4
  47. package/dist/build-integrations/runtime/discover.mjs +15 -4
  48. package/dist/build-integrations/runtime/discover.mjs.map +1 -1
  49. package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -1
  50. package/dist/build-integrations/runtime/errors.mjs.map +1 -1
  51. package/dist/build-integrations/runtime/read-stack-context.mjs +3 -3
  52. package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -1
  53. package/dist/build-integrations/runtime/resolve-discovery-env.d.mts +23 -7
  54. package/dist/build-integrations/runtime/resolve-discovery-env.mjs +25 -12
  55. package/dist/build-integrations/runtime/resolve-discovery-env.mjs.map +1 -1
  56. package/dist/build-integrations/runtime/stack-context.d.mts +2 -2
  57. package/dist/build-integrations/vite/index.d.mts +84 -19
  58. package/dist/build-integrations/vite/index.mjs +203 -28
  59. package/dist/build-integrations/vite/index.mjs.map +1 -1
  60. package/dist/build-integrations/vitest/config.d.mts +20 -2
  61. package/dist/build-integrations/vitest/config.mjs +16 -4
  62. package/dist/build-integrations/vitest/config.mjs.map +1 -1
  63. package/dist/build-integrations/vitest/env.d.mts +18 -10
  64. package/dist/build-integrations/vitest/env.mjs +11 -10
  65. package/dist/build-integrations/vitest/env.mjs.map +1 -1
  66. package/dist/build-integrations/vitest/errors.d.mts +7 -11
  67. package/dist/build-integrations/vitest/errors.mjs +3 -4
  68. package/dist/build-integrations/vitest/errors.mjs.map +1 -1
  69. package/dist/build-integrations/vitest/global-setup.d.mts +35 -0
  70. package/dist/build-integrations/vitest/global-setup.mjs +156 -0
  71. package/dist/build-integrations/vitest/global-setup.mjs.map +1 -0
  72. package/dist/build-integrations/vitest/index.d.mts +3 -2
  73. package/dist/build-integrations/vitest/index.mjs +3 -2
  74. package/dist/build-integrations/vitest/setup.mjs +2 -2
  75. package/dist/build-integrations/vitest/setup.mjs.map +1 -1
  76. package/dist/build-integrations/vitest/stack-context.d.mts +5 -3
  77. package/dist/build-integrations/vitest/stack-context.mjs +3 -2
  78. package/dist/build-integrations/vitest/stack-context.mjs.map +1 -1
  79. package/dist/cli/bail.mjs.map +1 -1
  80. package/dist/cli/doctor-probes.mjs +1 -3
  81. package/dist/cli/doctor-probes.mjs.map +1 -1
  82. package/dist/cli/main.d.mts +23 -1
  83. package/dist/cli/main.mjs +63 -8
  84. package/dist/cli/main.mjs.map +1 -1
  85. package/dist/cli/prune-direct.mjs.map +1 -1
  86. package/dist/cli/snapshot-reader.mjs.map +1 -1
  87. package/dist/cli/up-lifecycle.mjs.map +1 -1
  88. package/dist/cli/wirings/apply.mjs +28 -21
  89. package/dist/cli/wirings/apply.mjs.map +1 -1
  90. package/dist/cli/wirings/codegen.mjs +108 -0
  91. package/dist/cli/wirings/codegen.mjs.map +1 -0
  92. package/dist/cli/wirings/config-loader.mjs +12 -62
  93. package/dist/cli/wirings/config-loader.mjs.map +1 -1
  94. package/dist/cli/wirings/dump-ids.mjs +86 -0
  95. package/dist/cli/wirings/dump-ids.mjs.map +1 -0
  96. package/dist/cli/wirings/engine-command.mjs +1 -1
  97. package/dist/cli/wirings/engine-command.mjs.map +1 -1
  98. package/dist/cli/wirings/identity.mjs +2 -2
  99. package/dist/cli/wirings/identity.mjs.map +1 -1
  100. package/dist/cli/wirings/provide-file-system.mjs.map +1 -1
  101. package/dist/cli/wirings/read-devstack-version.mjs +17 -0
  102. package/dist/cli/wirings/read-devstack-version.mjs.map +1 -0
  103. package/dist/cli/wirings/snapshot.mjs +27 -34
  104. package/dist/cli/wirings/snapshot.mjs.map +1 -1
  105. package/dist/cli/wirings/up-ipc.mjs +147 -0
  106. package/dist/cli/wirings/up-ipc.mjs.map +1 -0
  107. package/dist/cli/wirings/up.mjs +218 -198
  108. package/dist/cli/wirings/up.mjs.map +1 -1
  109. package/dist/cli/wirings/wipe.mjs +1 -1
  110. package/dist/cli/wirings/wipe.mjs.map +1 -1
  111. package/dist/contracts/chain-probe.d.mts +1 -1
  112. package/dist/contracts/chain-probe.mjs +1 -1
  113. package/dist/contracts/chain-probe.mjs.map +1 -1
  114. package/dist/contracts/codegenable.d.mts +60 -6
  115. package/dist/contracts/codegenable.mjs +28 -0
  116. package/dist/contracts/codegenable.mjs.map +1 -0
  117. package/dist/contracts/config-bindings.mjs +162 -0
  118. package/dist/contracts/config-bindings.mjs.map +1 -0
  119. package/dist/contracts/container-runtime.d.mts +23 -17
  120. package/dist/contracts/plugin-expander.mjs.map +1 -1
  121. package/dist/contracts/snapshotable.d.mts +5 -7
  122. package/dist/contracts/wallet-protocol.d.mts +2 -5
  123. package/dist/contracts/wallet-protocol.mjs +2 -5
  124. package/dist/contracts/wallet-protocol.mjs.map +1 -1
  125. package/dist/index.d.mts +21 -26
  126. package/dist/index.mjs +8 -9
  127. package/dist/orchestrators/boot.d.mts +28 -0
  128. package/dist/orchestrators/boot.mjs +440 -0
  129. package/dist/orchestrators/boot.mjs.map +1 -0
  130. package/dist/orchestrators/codegen/bindings.d.mts +13 -1
  131. package/dist/orchestrators/codegen/bindings.mjs +23 -9
  132. package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
  133. package/dist/orchestrators/codegen/config-runtime.mjs +208 -0
  134. package/dist/orchestrators/codegen/config-runtime.mjs.map +1 -0
  135. package/dist/orchestrators/codegen/emit.mjs +1 -1
  136. package/dist/orchestrators/codegen/emit.mjs.map +1 -1
  137. package/dist/orchestrators/codegen/errors.mjs +21 -1
  138. package/dist/orchestrators/codegen/errors.mjs.map +1 -1
  139. package/dist/orchestrators/codegen/format.mjs +2 -0
  140. package/dist/orchestrators/codegen/format.mjs.map +1 -1
  141. package/dist/orchestrators/codegen/gitignore.mjs +30 -11
  142. package/dist/orchestrators/codegen/gitignore.mjs.map +1 -1
  143. package/dist/orchestrators/codegen/id-config.mjs +107 -0
  144. package/dist/orchestrators/codegen/id-config.mjs.map +1 -0
  145. package/dist/orchestrators/codegen/output-location.mjs +23 -31
  146. package/dist/orchestrators/codegen/output-location.mjs.map +1 -1
  147. package/dist/orchestrators/codegen/paths.mjs +8 -1
  148. package/dist/orchestrators/codegen/paths.mjs.map +1 -1
  149. package/dist/orchestrators/codegen/permissions.mjs.map +1 -1
  150. package/dist/orchestrators/codegen/service.mjs +286 -58
  151. package/dist/orchestrators/codegen/service.mjs.map +1 -1
  152. package/dist/orchestrators/layers.mjs +31 -0
  153. package/dist/orchestrators/layers.mjs.map +1 -0
  154. package/dist/orchestrators/lifecycle-prune/errors.mjs.map +1 -1
  155. package/dist/orchestrators/lifecycle-prune/index.mjs +8 -10
  156. package/dist/orchestrators/lifecycle-prune/index.mjs.map +1 -1
  157. package/dist/orchestrators/network-options.d.mts +52 -0
  158. package/dist/orchestrators/network-options.mjs +43 -0
  159. package/dist/orchestrators/network-options.mjs.map +1 -0
  160. package/dist/orchestrators/router/cleanup.mjs.map +1 -1
  161. package/dist/orchestrators/router/cors.mjs.map +1 -1
  162. package/dist/orchestrators/router/entrypoints.mjs.map +1 -1
  163. package/dist/orchestrators/router/errors.mjs.map +1 -1
  164. package/dist/orchestrators/router/file-provider.mjs.map +1 -1
  165. package/dist/orchestrators/router/hostname.mjs.map +1 -1
  166. package/dist/orchestrators/router/profile.mjs +1 -1
  167. package/dist/orchestrators/router/profile.mjs.map +1 -1
  168. package/dist/orchestrators/router/service.d.mts +2 -2
  169. package/dist/orchestrators/router/service.mjs +12 -27
  170. package/dist/orchestrators/router/service.mjs.map +1 -1
  171. package/dist/orchestrators/router/traefik-container.mjs +1 -1
  172. package/dist/orchestrators/router/traefik-container.mjs.map +1 -1
  173. package/dist/orchestrators/snapshot/capture.mjs +171 -195
  174. package/dist/orchestrators/snapshot/capture.mjs.map +1 -1
  175. package/dist/orchestrators/snapshot/descriptor.d.mts +1 -1
  176. package/dist/orchestrators/snapshot/descriptor.mjs +53 -11
  177. package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -1
  178. package/dist/orchestrators/snapshot/identity-guard.mjs +3 -5
  179. package/dist/orchestrators/snapshot/identity-guard.mjs.map +1 -1
  180. package/dist/orchestrators/snapshot/image-bundle-tags.mjs +32 -101
  181. package/dist/orchestrators/snapshot/image-bundle-tags.mjs.map +1 -1
  182. package/dist/orchestrators/snapshot/index.mjs +1 -4
  183. package/dist/orchestrators/snapshot/integrity.mjs.map +1 -1
  184. package/dist/orchestrators/snapshot/interrupted-restore.mjs +116 -0
  185. package/dist/orchestrators/snapshot/interrupted-restore.mjs.map +1 -0
  186. package/dist/orchestrators/snapshot/phase-error.mjs.map +1 -1
  187. package/dist/orchestrators/snapshot/prune.mjs +40 -31
  188. package/dist/orchestrators/snapshot/prune.mjs.map +1 -1
  189. package/dist/orchestrators/snapshot/restore.mjs +221 -196
  190. package/dist/orchestrators/snapshot/restore.mjs.map +1 -1
  191. package/dist/orchestrators/snapshot/service.mjs +72 -62
  192. package/dist/orchestrators/snapshot/service.mjs.map +1 -1
  193. package/dist/orchestrators/snapshot/wipe.mjs +67 -56
  194. package/dist/orchestrators/snapshot/wipe.mjs.map +1 -1
  195. package/dist/plugins/account/codegen.mjs +3 -0
  196. package/dist/plugins/account/codegen.mjs.map +1 -1
  197. package/dist/plugins/account/errors.d.mts +2 -2
  198. package/dist/plugins/account/errors.mjs +1 -4
  199. package/dist/plugins/account/errors.mjs.map +1 -1
  200. package/dist/plugins/account/funding.mjs +3 -27
  201. package/dist/plugins/account/funding.mjs.map +1 -1
  202. package/dist/plugins/account/index.d.mts +3 -8
  203. package/dist/plugins/account/index.mjs +71 -35
  204. package/dist/plugins/account/index.mjs.map +1 -1
  205. package/dist/plugins/account/keypair.d.mts +8 -7
  206. package/dist/plugins/account/keypair.mjs +5 -18
  207. package/dist/plugins/account/keypair.mjs.map +1 -1
  208. package/dist/plugins/account/lease.mjs.map +1 -1
  209. package/dist/plugins/account/registry.d.mts +1 -46
  210. package/dist/plugins/account/registry.mjs.map +1 -1
  211. package/dist/plugins/account/service.d.mts +43 -34
  212. package/dist/plugins/account/service.mjs +9 -54
  213. package/dist/plugins/account/service.mjs.map +1 -1
  214. package/dist/plugins/account/snapshot.mjs.map +1 -1
  215. package/dist/plugins/account/variants/ephemeral.mjs.map +1 -1
  216. package/dist/plugins/account/variants/impersonate.mjs.map +1 -1
  217. package/dist/plugins/account/variants/signer.mjs.map +1 -1
  218. package/dist/plugins/action/discriminator.mjs.map +1 -1
  219. package/dist/plugins/action/errors.mjs +1 -4
  220. package/dist/plugins/action/errors.mjs.map +1 -1
  221. package/dist/plugins/action/execute.mjs +3 -4
  222. package/dist/plugins/action/execute.mjs.map +1 -1
  223. package/dist/plugins/action/index.d.mts +2 -2
  224. package/dist/plugins/action/index.mjs +7 -11
  225. package/dist/plugins/action/index.mjs.map +1 -1
  226. package/dist/plugins/action/service.d.mts +5 -7
  227. package/dist/plugins/action/service.mjs +2 -16
  228. package/dist/plugins/action/service.mjs.map +1 -1
  229. package/dist/plugins/coin/address-resolution.mjs.map +1 -1
  230. package/dist/plugins/coin/codegen.mjs +97 -16
  231. package/dist/plugins/coin/codegen.mjs.map +1 -1
  232. package/dist/plugins/coin/discovery.mjs.map +1 -1
  233. package/dist/plugins/coin/errors.mjs +1 -4
  234. package/dist/plugins/coin/errors.mjs.map +1 -1
  235. package/dist/plugins/coin/index.d.mts +4 -34
  236. package/dist/plugins/coin/index.mjs +47 -28
  237. package/dist/plugins/coin/index.mjs.map +1 -1
  238. package/dist/plugins/coin/metadata.d.mts +1 -0
  239. package/dist/plugins/coin/metadata.mjs +8 -9
  240. package/dist/plugins/coin/metadata.mjs.map +1 -1
  241. package/dist/plugins/coin/mint.mjs +7 -21
  242. package/dist/plugins/coin/mint.mjs.map +1 -1
  243. package/dist/plugins/coin/registry.mjs +33 -20
  244. package/dist/plugins/coin/registry.mjs.map +1 -1
  245. package/dist/plugins/coin/service.d.mts +1 -1
  246. package/dist/plugins/coin/service.mjs +1 -8
  247. package/dist/plugins/coin/service.mjs.map +1 -1
  248. package/dist/plugins/coin/snapshot.mjs.map +1 -1
  249. package/dist/plugins/coin/type-strings.mjs.map +1 -1
  250. package/dist/plugins/dashboard/domain.mjs +106 -167
  251. package/dist/plugins/dashboard/domain.mjs.map +1 -1
  252. package/dist/plugins/dashboard/index.mjs +13 -13
  253. package/dist/plugins/dashboard/index.mjs.map +1 -1
  254. package/dist/plugins/dashboard/origin-policy.mjs +3 -3
  255. package/dist/plugins/dashboard/origin-policy.mjs.map +1 -1
  256. package/dist/plugins/dashboard/routable.mjs.map +1 -1
  257. package/dist/plugins/dashboard/schema/builder.mjs.map +1 -1
  258. package/dist/plugins/dashboard/schema/enums.mjs.map +1 -1
  259. package/dist/plugins/dashboard/schema/root.mjs +23 -44
  260. package/dist/plugins/dashboard/schema/root.mjs.map +1 -1
  261. package/dist/plugins/dashboard/schema/types.mjs +37 -91
  262. package/dist/plugins/dashboard/schema/types.mjs.map +1 -1
  263. package/dist/plugins/dashboard/schema.mjs.map +1 -1
  264. package/dist/plugins/dashboard/server.mjs.map +1 -1
  265. package/dist/plugins/deepbook/codegen.d.mts +12 -11
  266. package/dist/plugins/deepbook/codegen.mjs +108 -11
  267. package/dist/plugins/deepbook/codegen.mjs.map +1 -1
  268. package/dist/plugins/deepbook/deploy.mjs +7 -14
  269. package/dist/plugins/deepbook/deploy.mjs.map +1 -1
  270. package/dist/plugins/deepbook/errors.mjs +4 -2
  271. package/dist/plugins/deepbook/errors.mjs.map +1 -1
  272. package/dist/plugins/deepbook/faucet-strategy.mjs +4 -10
  273. package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -1
  274. package/dist/plugins/deepbook/hash.mjs.map +1 -1
  275. package/dist/plugins/deepbook/index.d.mts +38 -34
  276. package/dist/plugins/deepbook/index.mjs +133 -64
  277. package/dist/plugins/deepbook/index.mjs.map +1 -1
  278. package/dist/plugins/deepbook/plugin-key.mjs.map +1 -1
  279. package/dist/plugins/deepbook/pyth/index.mjs +4 -8
  280. package/dist/plugins/deepbook/pyth/index.mjs.map +1 -1
  281. package/dist/plugins/deepbook/snapshot.mjs +0 -1
  282. package/dist/plugins/deepbook/snapshot.mjs.map +1 -1
  283. package/dist/plugins/deepbook/type-strings.mjs.map +1 -1
  284. package/dist/plugins/deepbook/types.d.mts +2 -1
  285. package/dist/plugins/deepbook/types.mjs.map +1 -1
  286. package/dist/plugins/faucet/dispatcher.d.mts +3 -3
  287. package/dist/plugins/faucet/dispatcher.mjs.map +1 -1
  288. package/dist/plugins/faucet/errors.mjs.map +1 -1
  289. package/dist/plugins/faucet/http.mjs +2 -10
  290. package/dist/plugins/faucet/http.mjs.map +1 -1
  291. package/dist/plugins/faucet/index.mjs +0 -1
  292. package/dist/plugins/faucet/index.mjs.map +1 -1
  293. package/dist/plugins/host-service/errors.d.mts +1 -3
  294. package/dist/plugins/host-service/errors.mjs +1 -2
  295. package/dist/plugins/host-service/errors.mjs.map +1 -1
  296. package/dist/plugins/host-service/index.d.mts +1 -2
  297. package/dist/plugins/host-service/index.mjs +11 -11
  298. package/dist/plugins/host-service/index.mjs.map +1 -1
  299. package/dist/plugins/host-service/routable.mjs.map +1 -1
  300. package/dist/plugins/host-service/service.mjs +13 -13
  301. package/dist/plugins/host-service/service.mjs.map +1 -1
  302. package/dist/plugins/internal/acquire-on-chain-artifact.mjs.map +1 -1
  303. package/dist/plugins/internal/codegen-helpers.mjs +2 -0
  304. package/dist/plugins/internal/codegen-helpers.mjs.map +1 -1
  305. package/dist/plugins/internal/funding-failure-error.mjs.map +1 -1
  306. package/dist/plugins/{postgres → internal/postgres-sidecar}/connection.mjs +1 -1
  307. package/dist/plugins/internal/postgres-sidecar/connection.mjs.map +1 -0
  308. package/dist/plugins/{postgres → internal/postgres-sidecar}/db-ensure.mjs +30 -16
  309. package/dist/plugins/internal/postgres-sidecar/db-ensure.mjs.map +1 -0
  310. package/dist/plugins/internal/postgres-sidecar/errors.mjs +21 -0
  311. package/dist/plugins/internal/postgres-sidecar/errors.mjs.map +1 -0
  312. package/dist/{substrate/runtime/scoped-multimap → plugins/internal/postgres-sidecar}/index.mjs +1 -0
  313. package/dist/plugins/internal/postgres-sidecar/service.mjs +121 -0
  314. package/dist/plugins/internal/postgres-sidecar/service.mjs.map +1 -0
  315. package/dist/plugins/package/build.mjs +2 -2
  316. package/dist/plugins/package/build.mjs.map +1 -1
  317. package/dist/plugins/package/codegen.d.mts +7 -14
  318. package/dist/plugins/package/codegen.mjs +187 -59
  319. package/dist/plugins/package/codegen.mjs.map +1 -1
  320. package/dist/plugins/package/dep-resolution.mjs +51 -3
  321. package/dist/plugins/package/dep-resolution.mjs.map +1 -1
  322. package/dist/plugins/package/errors.mjs +1 -4
  323. package/dist/plugins/package/errors.mjs.map +1 -1
  324. package/dist/plugins/package/git-source.d.mts +18 -0
  325. package/dist/plugins/package/git-source.mjs +119 -0
  326. package/dist/plugins/package/git-source.mjs.map +1 -0
  327. package/dist/plugins/package/index.d.mts +28 -19
  328. package/dist/plugins/package/index.mjs +107 -32
  329. package/dist/plugins/package/index.mjs.map +1 -1
  330. package/dist/plugins/package/mode-known.mjs +2 -2
  331. package/dist/plugins/package/mode-known.mjs.map +1 -1
  332. package/dist/plugins/package/mode-local.mjs +18 -35
  333. package/dist/plugins/package/mode-local.mjs.map +1 -1
  334. package/dist/plugins/package/publish-executor.mjs +13 -8
  335. package/dist/plugins/package/publish-executor.mjs.map +1 -1
  336. package/dist/plugins/package/publish-output.d.mts +1 -11
  337. package/dist/plugins/package/publish-output.mjs +1 -9
  338. package/dist/plugins/package/publish-output.mjs.map +1 -1
  339. package/dist/plugins/package/registry.d.mts +1 -1
  340. package/dist/plugins/package/registry.mjs +26 -14
  341. package/dist/plugins/package/registry.mjs.map +1 -1
  342. package/dist/plugins/package/service.mjs.map +1 -1
  343. package/dist/plugins/package/snapshot.mjs.map +1 -1
  344. package/dist/plugins/router-entrypoints.mjs +1 -3
  345. package/dist/plugins/router-entrypoints.mjs.map +1 -1
  346. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +1 -2
  347. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -1
  348. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +1 -6
  349. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -1
  350. package/dist/plugins/seal/codegen.d.mts +1 -5
  351. package/dist/plugins/seal/codegen.mjs +83 -12
  352. package/dist/plugins/seal/codegen.mjs.map +1 -1
  353. package/dist/plugins/seal/config-render.mjs +2 -6
  354. package/dist/plugins/seal/config-render.mjs.map +1 -1
  355. package/dist/plugins/seal/deploy.mjs +18 -29
  356. package/dist/plugins/seal/deploy.mjs.map +1 -1
  357. package/dist/plugins/seal/errors.d.mts +4 -5
  358. package/dist/plugins/seal/errors.mjs +1 -8
  359. package/dist/plugins/seal/errors.mjs.map +1 -1
  360. package/dist/plugins/seal/index.d.mts +7 -28
  361. package/dist/plugins/seal/index.mjs +69 -47
  362. package/dist/plugins/seal/index.mjs.map +1 -1
  363. package/dist/plugins/seal/key-manager.mjs.map +1 -1
  364. package/dist/plugins/seal/key-server.mjs +1 -6
  365. package/dist/plugins/seal/key-server.mjs.map +1 -1
  366. package/dist/plugins/seal/keygen.mjs +2 -3
  367. package/dist/plugins/seal/keygen.mjs.map +1 -1
  368. package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
  369. package/dist/plugins/seal/mode/live.mjs +1 -1
  370. package/dist/plugins/seal/mode/live.mjs.map +1 -1
  371. package/dist/plugins/seal/mode/local-keygen.mjs +4 -10
  372. package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
  373. package/dist/plugins/seal/plugin-key.mjs.map +1 -1
  374. package/dist/plugins/seal/registry-publish.mjs.map +1 -1
  375. package/dist/plugins/seal/routable.mjs.map +1 -1
  376. package/dist/plugins/seal/service.mjs.map +1 -1
  377. package/dist/plugins/seal/snapshot.mjs +1 -2
  378. package/dist/plugins/seal/snapshot.mjs.map +1 -1
  379. package/dist/plugins/sui/auto-tick.mjs +5 -5
  380. package/dist/plugins/sui/auto-tick.mjs.map +1 -1
  381. package/dist/plugins/sui/chain-build-container.mjs +2 -2
  382. package/dist/plugins/sui/chain-build-container.mjs.map +1 -1
  383. package/dist/plugins/sui/chain-probe.mjs +4 -4
  384. package/dist/plugins/sui/chain-probe.mjs.map +1 -1
  385. package/dist/plugins/sui/codegen.d.mts +9 -7
  386. package/dist/plugins/sui/codegen.mjs +49 -46
  387. package/dist/plugins/sui/codegen.mjs.map +1 -1
  388. package/dist/plugins/sui/errors.d.mts +1 -1
  389. package/dist/plugins/sui/errors.mjs +1 -9
  390. package/dist/plugins/sui/errors.mjs.map +1 -1
  391. package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/index.d.mts +1 -1
  392. package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/index.mjs +3 -7
  393. package/dist/plugins/sui/exec/index.mjs.map +1 -0
  394. package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/sign-and-dispatch.mjs +1 -1
  395. package/dist/plugins/sui/exec/sign-and-dispatch.mjs.map +1 -0
  396. package/dist/plugins/sui/fork-faucet-strategy.mjs +2 -6
  397. package/dist/plugins/sui/fork-faucet-strategy.mjs.map +1 -1
  398. package/dist/plugins/sui/fork-orchestration.mjs +13 -23
  399. package/dist/plugins/sui/fork-orchestration.mjs.map +1 -1
  400. package/dist/plugins/sui/fork-transaction.mjs.map +1 -1
  401. package/dist/plugins/sui/index.d.mts +34 -83
  402. package/dist/plugins/sui/index.mjs +200 -68
  403. package/dist/plugins/sui/index.mjs.map +1 -1
  404. package/dist/{substrate/runtime/sui-ledger → plugins/sui/ledger}/object-ref.mjs +1 -1
  405. package/dist/plugins/sui/ledger/object-ref.mjs.map +1 -0
  406. package/dist/plugins/sui/local-faucet-strategy.mjs +1 -5
  407. package/dist/plugins/sui/local-faucet-strategy.mjs.map +1 -1
  408. package/dist/plugins/sui/log-attrs.mjs +11 -0
  409. package/dist/plugins/sui/log-attrs.mjs.map +1 -0
  410. package/dist/plugins/sui/mode/external.mjs +4 -12
  411. package/dist/plugins/sui/mode/external.mjs.map +1 -1
  412. package/dist/plugins/sui/mode/fork.mjs +19 -16
  413. package/dist/plugins/sui/mode/fork.mjs.map +1 -1
  414. package/dist/plugins/sui/mode/live.mjs +4 -17
  415. package/dist/plugins/sui/mode/live.mjs.map +1 -1
  416. package/dist/plugins/sui/mode/local.d.mts +1 -0
  417. package/dist/plugins/sui/mode/local.mjs +203 -61
  418. package/dist/plugins/sui/mode/local.mjs.map +1 -1
  419. package/dist/plugins/sui/mode/shared-boot.mjs +18 -23
  420. package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -1
  421. package/dist/plugins/sui/mode/shared.d.mts +6 -7
  422. package/dist/plugins/sui/mode/shared.mjs.map +1 -1
  423. package/dist/plugins/sui/mode/spec.d.mts +23 -6
  424. package/dist/{substrate/runtime/sui-move-build → plugins/sui/move}/index.mjs +28 -30
  425. package/dist/plugins/sui/move/index.mjs.map +1 -0
  426. package/dist/plugins/sui/move-summary-runner.mjs +8 -4
  427. package/dist/plugins/sui/move-summary-runner.mjs.map +1 -1
  428. package/dist/{substrate/network.d.mts → plugins/sui/network-config.d.mts} +11 -10
  429. package/dist/plugins/sui/network-resolver.d.mts +6 -6
  430. package/dist/plugins/sui/routable.mjs.map +1 -1
  431. package/dist/plugins/sui/service.mjs +10 -6
  432. package/dist/plugins/sui/service.mjs.map +1 -1
  433. package/dist/plugins/sui/snapshot.mjs +10 -5
  434. package/dist/plugins/sui/snapshot.mjs.map +1 -1
  435. package/dist/plugins/wallet/codegen.d.mts +8 -6
  436. package/dist/plugins/wallet/codegen.mjs +4 -10
  437. package/dist/plugins/wallet/codegen.mjs.map +1 -1
  438. package/dist/plugins/wallet/errors.mjs +1 -4
  439. package/dist/plugins/wallet/errors.mjs.map +1 -1
  440. package/dist/plugins/wallet/index.d.mts +3 -6
  441. package/dist/plugins/wallet/index.mjs +16 -21
  442. package/dist/plugins/wallet/index.mjs.map +1 -1
  443. package/dist/plugins/wallet/origin-policy.mjs +3 -3
  444. package/dist/plugins/wallet/origin-policy.mjs.map +1 -1
  445. package/dist/plugins/wallet/pairing.mjs +6 -24
  446. package/dist/plugins/wallet/pairing.mjs.map +1 -1
  447. package/dist/plugins/wallet/protocol.mjs.map +1 -1
  448. package/dist/plugins/wallet/routable.mjs.map +1 -1
  449. package/dist/plugins/wallet/server.mjs +17 -24
  450. package/dist/plugins/wallet/server.mjs.map +1 -1
  451. package/dist/plugins/wallet/service.d.mts +2 -2
  452. package/dist/plugins/wallet/service.mjs +4 -15
  453. package/dist/plugins/wallet/service.mjs.map +1 -1
  454. package/dist/plugins/wallet/snapshot.mjs.map +1 -1
  455. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +1 -5
  456. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -1
  457. package/dist/plugins/walrus/codegen.d.mts +2 -13
  458. package/dist/plugins/walrus/codegen.mjs +93 -28
  459. package/dist/plugins/walrus/codegen.mjs.map +1 -1
  460. package/dist/plugins/walrus/deploy-paths.mjs +2 -3
  461. package/dist/plugins/walrus/deploy-paths.mjs.map +1 -1
  462. package/dist/plugins/walrus/deploy.mjs +26 -19
  463. package/dist/plugins/walrus/deploy.mjs.map +1 -1
  464. package/dist/plugins/walrus/errors.mjs +1 -8
  465. package/dist/plugins/walrus/errors.mjs.map +1 -1
  466. package/dist/plugins/walrus/faucet-strategy.mjs +15 -1
  467. package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -1
  468. package/dist/plugins/walrus/index.d.mts +13 -44
  469. package/dist/plugins/walrus/index.mjs +119 -122
  470. package/dist/plugins/walrus/index.mjs.map +1 -1
  471. package/dist/plugins/walrus/mode/known-deploy.mjs +6 -6
  472. package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
  473. package/dist/plugins/walrus/mode/local-cluster.d.mts +2 -2
  474. package/dist/plugins/walrus/mode/local-cluster.mjs +2 -10
  475. package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
  476. package/dist/plugins/walrus/plugin-key.mjs.map +1 -1
  477. package/dist/plugins/walrus/registry-publish.mjs.map +1 -1
  478. package/dist/plugins/walrus/routable.mjs.map +1 -1
  479. package/dist/plugins/walrus/service.mjs.map +1 -1
  480. package/dist/plugins/walrus/snapshot.mjs +3 -3
  481. package/dist/plugins/walrus/snapshot.mjs.map +1 -1
  482. package/dist/plugins/walrus/storage-nodes.mjs +53 -14
  483. package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
  484. package/dist/plugins/walrus/wal-swap.mjs +4 -8
  485. package/dist/plugins/walrus/wal-swap.mjs.map +1 -1
  486. package/dist/primitives/artifact-publisher.mjs.map +1 -1
  487. package/dist/primitives/cache.d.mts +1 -1
  488. package/dist/runtime/docker/client.mjs +47 -3
  489. package/dist/runtime/docker/client.mjs.map +1 -1
  490. package/dist/runtime/docker/container.mjs +37 -57
  491. package/dist/runtime/docker/container.mjs.map +1 -1
  492. package/dist/runtime/docker/errors.mjs.map +1 -1
  493. package/dist/runtime/docker/exec.mjs +2 -2
  494. package/dist/runtime/docker/exec.mjs.map +1 -1
  495. package/dist/runtime/docker/image.mjs +10 -11
  496. package/dist/runtime/docker/image.mjs.map +1 -1
  497. package/dist/runtime/docker/index.mjs +0 -1
  498. package/dist/runtime/docker/inspect-and-decode.mjs.map +1 -1
  499. package/dist/runtime/docker/inventory.mjs +9 -9
  500. package/dist/runtime/docker/inventory.mjs.map +1 -1
  501. package/dist/runtime/docker/labels.mjs +12 -1
  502. package/dist/runtime/docker/labels.mjs.map +1 -1
  503. package/dist/runtime/docker/network.mjs +6 -6
  504. package/dist/runtime/docker/network.mjs.map +1 -1
  505. package/dist/runtime/docker/render-run-args.mjs.map +1 -1
  506. package/dist/runtime/docker/service.mjs +31 -45
  507. package/dist/runtime/docker/service.mjs.map +1 -1
  508. package/dist/runtime/docker/sweep.mjs +14 -64
  509. package/dist/runtime/docker/sweep.mjs.map +1 -1
  510. package/dist/runtime/docker/volume.mjs +1 -1
  511. package/dist/runtime/docker/volume.mjs.map +1 -1
  512. package/dist/runtime/docker/wrap.mjs +14 -2
  513. package/dist/runtime/docker/wrap.mjs.map +1 -1
  514. package/dist/substrate/brand.d.mts +2 -5
  515. package/dist/substrate/brand.mjs +3 -2
  516. package/dist/substrate/brand.mjs.map +1 -1
  517. package/dist/substrate/cross-process.mjs +2 -16
  518. package/dist/substrate/cross-process.mjs.map +1 -1
  519. package/dist/substrate/event-time.mjs +0 -2
  520. package/dist/substrate/event-time.mjs.map +1 -1
  521. package/dist/substrate/events.d.mts +46 -16
  522. package/dist/substrate/identity.d.mts +11 -5
  523. package/dist/substrate/manifest.d.mts +33 -26
  524. package/dist/substrate/manifest.mjs +8 -3
  525. package/dist/substrate/manifest.mjs.map +1 -1
  526. package/dist/substrate/options.d.mts +33 -4
  527. package/dist/substrate/plugin-ctx.d.mts +70 -0
  528. package/dist/substrate/plugin-ctx.mjs +66 -0
  529. package/dist/substrate/plugin-ctx.mjs.map +1 -0
  530. package/dist/substrate/plugin.d.mts +46 -38
  531. package/dist/substrate/plugin.mjs +13 -9
  532. package/dist/substrate/plugin.mjs.map +1 -1
  533. package/dist/substrate/projection.d.mts +1 -1
  534. package/dist/substrate/runtime/atomic-write.mjs +3 -4
  535. package/dist/substrate/runtime/atomic-write.mjs.map +1 -1
  536. package/dist/substrate/runtime/cache/schema.mjs.map +1 -1
  537. package/dist/substrate/runtime/cache/service.mjs +50 -20
  538. package/dist/substrate/runtime/cache/service.mjs.map +1 -1
  539. package/dist/substrate/runtime/config-validation.mjs.map +1 -1
  540. package/dist/substrate/runtime/control-plane/domain.mjs +34 -12
  541. package/dist/substrate/runtime/control-plane/domain.mjs.map +1 -1
  542. package/dist/substrate/runtime/control-plane/service.mjs.map +1 -1
  543. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +14 -17
  544. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -1
  545. package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -1
  546. package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs.map +1 -1
  547. package/dist/substrate/runtime/cross-process/command-channel/runtime-control-lock.mjs.map +1 -1
  548. package/dist/substrate/runtime/cross-process/index.mjs +0 -2
  549. package/dist/substrate/runtime/cross-process/live-clock.mjs +1 -1
  550. package/dist/substrate/runtime/cross-process/live-clock.mjs.map +1 -1
  551. package/dist/substrate/runtime/cross-process/liveness.mjs +11 -18
  552. package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
  553. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs +2 -2
  554. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs.map +1 -1
  555. package/dist/substrate/runtime/cross-process/roster.mjs +17 -127
  556. package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -1
  557. package/dist/substrate/runtime/cross-process/self-pid.mjs.map +1 -1
  558. package/dist/substrate/runtime/cross-process/stack-lock.mjs +29 -28
  559. package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -1
  560. package/dist/substrate/runtime/current-plugin.mjs.map +1 -1
  561. package/dist/substrate/runtime/errors.mjs +1 -13
  562. package/dist/substrate/runtime/errors.mjs.map +1 -1
  563. package/dist/substrate/runtime/format-unknown-error.mjs.map +1 -1
  564. package/dist/substrate/runtime/host-bind-mount-owner.mjs.map +1 -1
  565. package/dist/substrate/runtime/host-gateway.mjs.map +1 -1
  566. package/dist/substrate/runtime/host-tree-tar/index.mjs +15 -163
  567. package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -1
  568. package/dist/substrate/runtime/http-probe.d.mts +1 -3
  569. package/dist/substrate/runtime/http-probe.mjs.map +1 -1
  570. package/dist/substrate/runtime/index.mjs +1 -6
  571. package/dist/substrate/runtime/lease-broker/service.mjs +5 -23
  572. package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -1
  573. package/dist/substrate/runtime/lifecycle/dep-graph.mjs +3 -3
  574. package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -1
  575. package/dist/substrate/runtime/lifecycle/graph-input-id.d.mts +1 -0
  576. package/dist/substrate/runtime/lifecycle/graph-input-id.mjs +133 -0
  577. package/dist/substrate/runtime/lifecycle/graph-input-id.mjs.map +1 -0
  578. package/dist/substrate/runtime/lifecycle/index.mjs +2 -2
  579. package/dist/substrate/runtime/lifecycle/plugin-registry.d.mts +1 -1
  580. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +22 -1
  581. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -1
  582. package/dist/substrate/runtime/lifecycle/ready-gate.mjs +2 -15
  583. package/dist/substrate/runtime/lifecycle/ready-gate.mjs.map +1 -1
  584. package/dist/substrate/runtime/lifecycle/selective-restart.mjs +29 -25
  585. package/dist/substrate/runtime/lifecycle/selective-restart.mjs.map +1 -1
  586. package/dist/substrate/runtime/lifecycle/signals.mjs +1 -1
  587. package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -1
  588. package/dist/substrate/runtime/lifecycle/state-machine.mjs.map +1 -1
  589. package/dist/substrate/runtime/lifecycle/watch-attribution.d.mts +1 -1
  590. package/dist/substrate/runtime/lifecycle/watch-attribution.mjs +0 -1
  591. package/dist/substrate/runtime/lifecycle/watch-attribution.mjs.map +1 -1
  592. package/dist/substrate/runtime/managed-container.d.mts +5 -1
  593. package/dist/substrate/runtime/managed-container.mjs +8 -10
  594. package/dist/substrate/runtime/managed-container.mjs.map +1 -1
  595. package/dist/substrate/runtime/manifest/manifest.mjs +8 -30
  596. package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
  597. package/dist/substrate/runtime/mode-errors.d.mts +1 -2
  598. package/dist/substrate/runtime/mode-errors.mjs.map +1 -1
  599. package/dist/substrate/runtime/observability/cascade-formatter.mjs +1 -11
  600. package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -1
  601. package/dist/substrate/runtime/observability/ignore-with-log.mjs.map +1 -1
  602. package/dist/substrate/runtime/observability/index.d.mts +1 -1
  603. package/dist/substrate/runtime/observability/index.mjs +4 -6
  604. package/dist/substrate/runtime/observability/log-attrs.mjs +46 -0
  605. package/dist/substrate/runtime/observability/log-attrs.mjs.map +1 -0
  606. package/dist/substrate/runtime/observability/log-store.mjs.map +1 -1
  607. package/dist/substrate/runtime/observability/logger.mjs +3 -3
  608. package/dist/substrate/runtime/observability/logger.mjs.map +1 -1
  609. package/dist/substrate/runtime/observability/output-truncate.mjs.map +1 -1
  610. package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -1
  611. package/dist/substrate/runtime/observability/process-lines.mjs.map +1 -1
  612. package/dist/substrate/runtime/observability/redaction.mjs.map +1 -1
  613. package/dist/substrate/runtime/observability/subprocess-capture.mjs +2 -2
  614. package/dist/substrate/runtime/observability/subprocess-capture.mjs.map +1 -1
  615. package/dist/substrate/runtime/passthrough-or-wrap.mjs.map +1 -1
  616. package/dist/substrate/runtime/paths.mjs +27 -9
  617. package/dist/substrate/runtime/paths.mjs.map +1 -1
  618. package/dist/substrate/runtime/phase-preserving-produce.mjs.map +1 -1
  619. package/dist/substrate/runtime/port-broker/service.mjs +10 -27
  620. package/dist/substrate/runtime/port-broker/service.mjs.map +1 -1
  621. package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -1
  622. package/dist/substrate/runtime/probes.d.mts +2 -3
  623. package/dist/substrate/runtime/probes.mjs.map +1 -1
  624. package/dist/substrate/runtime/process-supervisor.mjs.map +1 -1
  625. package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -1
  626. package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -1
  627. package/dist/substrate/runtime/projection/update.mjs +87 -11
  628. package/dist/substrate/runtime/projection/update.mjs.map +1 -1
  629. package/dist/substrate/runtime/random-suffix.mjs.map +1 -1
  630. package/dist/substrate/runtime/reconcile/fs-plan.mjs +146 -0
  631. package/dist/substrate/runtime/reconcile/fs-plan.mjs.map +1 -0
  632. package/dist/substrate/runtime/reconcile/graph.mjs +19 -0
  633. package/dist/substrate/runtime/reconcile/graph.mjs.map +1 -0
  634. package/dist/substrate/runtime/reconcile/index.mjs +5 -0
  635. package/dist/substrate/runtime/reconcile/label.mjs +41 -0
  636. package/dist/substrate/runtime/reconcile/label.mjs.map +1 -0
  637. package/dist/substrate/runtime/reconcile/spec.mjs +15 -0
  638. package/dist/substrate/runtime/reconcile/spec.mjs.map +1 -0
  639. package/dist/substrate/runtime/retry-policy.d.mts +2 -3
  640. package/dist/substrate/runtime/retry-policy.mjs.map +1 -1
  641. package/dist/substrate/runtime/routed-url.mjs.map +1 -1
  642. package/dist/substrate/runtime/runtime-decode.mjs.map +1 -1
  643. package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -1
  644. package/dist/substrate/runtime/stage-and-swap/index.mjs +49 -12
  645. package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -1
  646. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs.map +1 -1
  647. package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -1
  648. package/dist/substrate/runtime/strategy-registry/service.mjs +29 -17
  649. package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -1
  650. package/dist/substrate/runtime/subnet-broker.mjs.map +1 -1
  651. package/dist/substrate/runtime/supervisor/acquire-node.mjs +188 -56
  652. package/dist/substrate/runtime/supervisor/acquire-node.mjs.map +1 -1
  653. package/dist/substrate/runtime/supervisor/background-tasks.mjs +79 -65
  654. package/dist/substrate/runtime/supervisor/background-tasks.mjs.map +1 -1
  655. package/dist/substrate/runtime/supervisor/command-loop.mjs +82 -23
  656. package/dist/substrate/runtime/supervisor/command-loop.mjs.map +1 -1
  657. package/dist/substrate/runtime/supervisor/contribution-dispatcher.mjs +16 -0
  658. package/dist/substrate/runtime/supervisor/contribution-dispatcher.mjs.map +1 -0
  659. package/dist/substrate/runtime/supervisor/errors.mjs +11 -2
  660. package/dist/substrate/runtime/supervisor/errors.mjs.map +1 -1
  661. package/dist/substrate/runtime/supervisor/index.mjs +2 -0
  662. package/dist/substrate/runtime/supervisor/shutdown.mjs +6 -6
  663. package/dist/substrate/runtime/supervisor/shutdown.mjs.map +1 -1
  664. package/dist/substrate/runtime/supervisor/start-supervisor.mjs +60 -70
  665. package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -1
  666. package/dist/substrate/runtime/supervisor/state.d.mts +1 -1
  667. package/dist/substrate/runtime/supervisor/state.mjs +1 -1
  668. package/dist/substrate/runtime/supervisor/state.mjs.map +1 -1
  669. package/dist/substrate/runtime/supervisor/teardown.mjs +10 -9
  670. package/dist/substrate/runtime/supervisor/teardown.mjs.map +1 -1
  671. package/dist/substrate/runtime/supervisor/wiring.mjs +6 -20
  672. package/dist/substrate/runtime/supervisor/wiring.mjs.map +1 -1
  673. package/dist/substrate/runtime/tar/reader.mjs +216 -0
  674. package/dist/substrate/runtime/tar/reader.mjs.map +1 -0
  675. package/dist/substrate/runtime/typed-env.mjs.map +1 -1
  676. package/dist/substrate/versioned-doc-schema.mjs.map +1 -1
  677. package/dist/substrate/versioned-doc-sync.mjs +5 -5
  678. package/dist/substrate/versioned-doc-sync.mjs.map +1 -1
  679. package/dist/surfaces/cli/command-tree.mjs +49 -7
  680. package/dist/surfaces/cli/command-tree.mjs.map +1 -1
  681. package/dist/surfaces/cli/commands/config.mjs +1 -1
  682. package/dist/surfaces/cli/commands/config.mjs.map +1 -1
  683. package/dist/surfaces/cli/commands/confirm-node.mjs.map +1 -1
  684. package/dist/surfaces/cli/commands/confirm.mjs.map +1 -1
  685. package/dist/surfaces/cli/commands/doctor.mjs +1 -1
  686. package/dist/surfaces/cli/commands/doctor.mjs.map +1 -1
  687. package/dist/surfaces/cli/commands/prune-picker-entry.mjs.map +1 -1
  688. package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -1
  689. package/dist/surfaces/cli/commands/prune.mjs +1 -1
  690. package/dist/surfaces/cli/commands/prune.mjs.map +1 -1
  691. package/dist/surfaces/cli/commands/snapshot.mjs +1 -1
  692. package/dist/surfaces/cli/commands/snapshot.mjs.map +1 -1
  693. package/dist/surfaces/cli/commands/status.mjs +1 -1
  694. package/dist/surfaces/cli/commands/status.mjs.map +1 -1
  695. package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -1
  696. package/dist/surfaces/cli/commands/wipe.mjs +3 -4
  697. package/dist/surfaces/cli/commands/wipe.mjs.map +1 -1
  698. package/dist/surfaces/cli/envelope.mjs.map +1 -1
  699. package/dist/surfaces/cli/errors.mjs.map +1 -1
  700. package/dist/surfaces/cli/flags.mjs.map +1 -1
  701. package/dist/surfaces/cli/index.mjs +45 -14
  702. package/dist/surfaces/cli/index.mjs.map +1 -1
  703. package/dist/surfaces/cli/output.mjs.map +1 -1
  704. package/dist/surfaces/cli/sysexits.mjs.map +1 -1
  705. package/dist/surfaces/tui/app.mjs +0 -21
  706. package/dist/surfaces/tui/app.mjs.map +1 -1
  707. package/dist/surfaces/tui/dashboard.mjs +0 -48
  708. package/dist/surfaces/tui/dashboard.mjs.map +1 -1
  709. package/dist/surfaces/tui/display-derivation.mjs +80 -16
  710. package/dist/surfaces/tui/display-derivation.mjs.map +1 -1
  711. package/dist/surfaces/tui/errors.mjs.map +1 -1
  712. package/dist/surfaces/tui/event-log.mjs +0 -9
  713. package/dist/surfaces/tui/event-log.mjs.map +1 -1
  714. package/dist/surfaces/tui/heartbeat.mjs.map +1 -1
  715. package/dist/surfaces/tui/index.mjs +2 -2
  716. package/dist/surfaces/tui/index.mjs.map +1 -1
  717. package/dist/surfaces/tui/input.mjs.map +1 -1
  718. package/dist/surfaces/tui/mode-detect.mjs.map +1 -1
  719. package/dist/surfaces/tui/mount-ink.mjs.map +1 -1
  720. package/dist/surfaces/tui/plain-renderer.mjs +26 -19
  721. package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
  722. package/dist/surfaces/tui/resource-table.mjs +8 -10
  723. package/dist/surfaces/tui/resource-table.mjs.map +1 -1
  724. package/images/postgres/Dockerfile +0 -1
  725. package/images/sui/Dockerfile +27 -54
  726. package/images/sui/entrypoint.sh +17 -179
  727. package/package.json +23 -15
  728. package/dashboard-ui/assets/index-Bmi1UtAg.js +0 -1356
  729. package/dashboard-ui/assets/index-D5EShVt4.js +0 -3
  730. package/dashboard-ui/assets/index-Deml9drg.css +0 -1
  731. package/dist/api/plugin-errors.d.mts +0 -7
  732. package/dist/api/plugin-errors.mjs +0 -10
  733. package/dist/api/plugin-errors.mjs.map +0 -1
  734. package/dist/cli/wirings/build-verb-layers.mjs +0 -42
  735. package/dist/cli/wirings/build-verb-layers.mjs.map +0 -1
  736. package/dist/contracts/capability-decl.d.mts +0 -40
  737. package/dist/orchestrators/built-in-plugin-layers.mjs +0 -54
  738. package/dist/orchestrators/built-in-plugin-layers.mjs.map +0 -1
  739. package/dist/orchestrators/run.mjs +0 -91
  740. package/dist/orchestrators/run.mjs.map +0 -1
  741. package/dist/orchestrators/runtime-composition.d.mts +0 -10
  742. package/dist/orchestrators/runtime-composition.mjs +0 -252
  743. package/dist/orchestrators/runtime-composition.mjs.map +0 -1
  744. package/dist/orchestrators/snapshot/capture-command.d.mts +0 -1
  745. package/dist/orchestrators/snapshot/capture-command.mjs +0 -25
  746. package/dist/orchestrators/snapshot/capture-command.mjs.map +0 -1
  747. package/dist/orchestrators/snapshot/pending-marker.mjs +0 -99
  748. package/dist/orchestrators/snapshot/pending-marker.mjs.map +0 -1
  749. package/dist/orchestrators/snapshot/recover-pending.mjs +0 -223
  750. package/dist/orchestrators/snapshot/recover-pending.mjs.map +0 -1
  751. package/dist/orchestrators/snapshot/state-document.d.mts +0 -1
  752. package/dist/orchestrators/snapshot/state-document.mjs +0 -58
  753. package/dist/orchestrators/snapshot/state-document.mjs.map +0 -1
  754. package/dist/plugins/account/spans.mjs +0 -16
  755. package/dist/plugins/account/spans.mjs.map +0 -1
  756. package/dist/plugins/account/variants/env.mjs +0 -24
  757. package/dist/plugins/account/variants/env.mjs.map +0 -1
  758. package/dist/plugins/account/variants/inline.mjs +0 -13
  759. package/dist/plugins/account/variants/inline.mjs.map +0 -1
  760. package/dist/plugins/account/variants/keystore.mjs +0 -91
  761. package/dist/plugins/account/variants/keystore.mjs.map +0 -1
  762. package/dist/plugins/action/spans.mjs +0 -11
  763. package/dist/plugins/action/spans.mjs.map +0 -1
  764. package/dist/plugins/coin/spans.mjs +0 -20
  765. package/dist/plugins/coin/spans.mjs.map +0 -1
  766. package/dist/plugins/deepbook/spans.mjs +0 -18
  767. package/dist/plugins/deepbook/spans.mjs.map +0 -1
  768. package/dist/plugins/faucet/spans.mjs +0 -12
  769. package/dist/plugins/faucet/spans.mjs.map +0 -1
  770. package/dist/plugins/package/spans.mjs +0 -14
  771. package/dist/plugins/package/spans.mjs.map +0 -1
  772. package/dist/plugins/postgres/codegen.mjs +0 -42
  773. package/dist/plugins/postgres/codegen.mjs.map +0 -1
  774. package/dist/plugins/postgres/connection.d.mts +0 -33
  775. package/dist/plugins/postgres/connection.mjs.map +0 -1
  776. package/dist/plugins/postgres/db-ensure.mjs.map +0 -1
  777. package/dist/plugins/postgres/errors.d.mts +0 -60
  778. package/dist/plugins/postgres/errors.mjs +0 -29
  779. package/dist/plugins/postgres/errors.mjs.map +0 -1
  780. package/dist/plugins/postgres/index.d.mts +0 -37
  781. package/dist/plugins/postgres/index.mjs +0 -68
  782. package/dist/plugins/postgres/index.mjs.map +0 -1
  783. package/dist/plugins/postgres/routable.mjs +0 -33
  784. package/dist/plugins/postgres/routable.mjs.map +0 -1
  785. package/dist/plugins/postgres/service.d.mts +0 -50
  786. package/dist/plugins/postgres/service.mjs +0 -179
  787. package/dist/plugins/postgres/service.mjs.map +0 -1
  788. package/dist/plugins/postgres/snapshot.mjs +0 -32
  789. package/dist/plugins/postgres/snapshot.mjs.map +0 -1
  790. package/dist/plugins/postgres/spans.mjs +0 -11
  791. package/dist/plugins/postgres/spans.mjs.map +0 -1
  792. package/dist/plugins/seal/spans.mjs +0 -18
  793. package/dist/plugins/seal/spans.mjs.map +0 -1
  794. package/dist/plugins/sui/spans.mjs +0 -17
  795. package/dist/plugins/sui/spans.mjs.map +0 -1
  796. package/dist/plugins/wallet/spans.mjs +0 -22
  797. package/dist/plugins/wallet/spans.mjs.map +0 -1
  798. package/dist/plugins/walrus/registry-publish.d.mts +0 -24
  799. package/dist/plugins/walrus/spans.mjs +0 -18
  800. package/dist/plugins/walrus/spans.mjs.map +0 -1
  801. package/dist/runtime/docker/logs.d.mts +0 -1
  802. package/dist/runtime/docker/logs.mjs +0 -34
  803. package/dist/runtime/docker/logs.mjs.map +0 -1
  804. package/dist/substrate/runtime/artifact-publisher/index.mjs +0 -86
  805. package/dist/substrate/runtime/artifact-publisher/index.mjs.map +0 -1
  806. package/dist/substrate/runtime/capability-sinks/index.d.mts +0 -1
  807. package/dist/substrate/runtime/capability-sinks/index.mjs +0 -3
  808. package/dist/substrate/runtime/capability-sinks/layer.d.mts +0 -1
  809. package/dist/substrate/runtime/capability-sinks/layer.mjs +0 -31
  810. package/dist/substrate/runtime/capability-sinks/layer.mjs.map +0 -1
  811. package/dist/substrate/runtime/capability-sinks/service.d.mts +0 -95
  812. package/dist/substrate/runtime/capability-sinks/service.mjs +0 -69
  813. package/dist/substrate/runtime/capability-sinks/service.mjs.map +0 -1
  814. package/dist/substrate/runtime/cross-process/lock.d.mts +0 -1
  815. package/dist/substrate/runtime/cross-process/lock.mjs +0 -23
  816. package/dist/substrate/runtime/cross-process/lock.mjs.map +0 -1
  817. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +0 -113
  818. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +0 -1
  819. package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs +0 -31
  820. package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +0 -1
  821. package/dist/substrate/runtime/observability/formatter-registry.d.mts +0 -1
  822. package/dist/substrate/runtime/observability/formatter-registry.mjs +0 -48
  823. package/dist/substrate/runtime/observability/formatter-registry.mjs.map +0 -1
  824. package/dist/substrate/runtime/observability/span-store.d.mts +0 -1
  825. package/dist/substrate/runtime/observability/span-store.mjs +0 -110
  826. package/dist/substrate/runtime/observability/span-store.mjs.map +0 -1
  827. package/dist/substrate/runtime/observability/spans.d.mts +0 -1
  828. package/dist/substrate/runtime/observability/spans.mjs +0 -87
  829. package/dist/substrate/runtime/observability/spans.mjs.map +0 -1
  830. package/dist/substrate/runtime/projection/index.mjs +0 -4
  831. package/dist/substrate/runtime/projection/persisted.mjs +0 -213
  832. package/dist/substrate/runtime/projection/persisted.mjs.map +0 -1
  833. package/dist/substrate/runtime/scoped-multimap/service.mjs +0 -52
  834. package/dist/substrate/runtime/scoped-multimap/service.mjs.map +0 -1
  835. package/dist/substrate/runtime/scoped-ref-map/index.mjs +0 -2
  836. package/dist/substrate/runtime/scoped-ref-map/service.mjs +0 -83
  837. package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +0 -1
  838. package/dist/substrate/runtime/state-store/index.mjs +0 -3
  839. package/dist/substrate/runtime/state-store/schema.d.mts +0 -1
  840. package/dist/substrate/runtime/state-store/schema.mjs +0 -41
  841. package/dist/substrate/runtime/state-store/schema.mjs.map +0 -1
  842. package/dist/substrate/runtime/state-store/service.d.mts +0 -1
  843. package/dist/substrate/runtime/state-store/service.mjs +0 -145
  844. package/dist/substrate/runtime/state-store/service.mjs.map +0 -1
  845. package/dist/substrate/runtime/sui-execute/index.mjs.map +0 -1
  846. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs.map +0 -1
  847. package/dist/substrate/runtime/sui-ledger/object-ref.mjs.map +0 -1
  848. package/dist/substrate/runtime/sui-move-build/index.mjs.map +0 -1
  849. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs +0 -151
  850. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs.map +0 -1
  851. package/dist/substrate/state-store.d.mts +0 -1
  852. /package/dist/orchestrators/{snapshot/pending-marker.d.mts → codegen/id-config.d.mts} +0 -0
  853. /package/dist/orchestrators/snapshot/{recover-pending.d.mts → interrupted-restore.d.mts} +0 -0
  854. /package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/sign-and-dispatch.d.mts +0 -0
  855. /package/dist/plugins/{postgres/snapshot.d.mts → sui/ledger/object-ref.d.mts} +0 -0
  856. /package/dist/{substrate/runtime/sui-move-build → plugins/sui/move}/index.d.mts +0 -0
  857. /package/dist/substrate/runtime/{state-store/index.d.mts → reconcile/graph.d.mts} +0 -0
  858. /package/dist/substrate/runtime/{cross-process/snapshot-reservation.d.mts → supervisor/contribution-dispatcher.d.mts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"update.mjs","names":[],"sources":["../../../../src/substrate/runtime/projection/update.ts"],"sourcesContent":["// Projection updater.\n//\n// Architecture § Renderer § \"Subscribable projection — exact field\n// enumeration\" (G2). The updater is a typed event-handler: each\n// `EngineEvent` produces a structurally-typed projection mutation.\n// Renderers never see the event taxonomy here; they only see the\n// resulting state.\n//\n// The updater is *pure data*: it takes the old state + an event,\n// returns the new state. Atomicity comes from\n// `SubscriptionRef.update` at the call site.\n//\n// Discipline:\n// - No display vocabulary (`title`, `primary`, `extras`) anywhere\n// in the produced state.\n// - All fields the architecture enumerates are reachable; new\n// event tags are picked up via exhaustive `tag` match (the\n// `_exhaustive: never` line will fail to type-check if a new\n// tag is added without a handler here).\n\nimport { Effect, Schema, SubscriptionRef } from 'effect';\n\nimport type { PluginKey } from '../../brand.ts';\nimport type { EngineEvent } from '../../events.ts';\nimport { eventAtOrNull } from '../../event-time.ts';\nimport type { LifecycleStatus } from '../../lifecycle.ts';\nimport type {\n\tAccountProjection,\n\tBuildEntry,\n\tEndpoint,\n\tPackageProjection,\n\tRow,\n\tStructuredError,\n\tSubscribableState,\n} from '../../projection.ts';\nimport { applyLifecycleFact, factFromEvent } from '../lifecycle/lifecycle-fact.ts';\nimport { SpanAttr } from '../observability/spans.ts';\nimport { AccountProjectionSchema, PackageProjectionSchema } from './persisted.ts';\n\n// -----------------------------------------------------------------------------\n// Capacity policy\n// -----------------------------------------------------------------------------\n\n/** Bounded buffer for the top-level errors list. */\nconst MAX_ERRORS_KEPT = 100;\n/** Bounded buffer for the top-level build-entries list. */\nconst MAX_BUILD_ENTRIES_KEPT = 200;\n/** Per-row bounded log-tail length. Architecture: default 100 lines. */\nconst MAX_ROW_LOG_LINES = 100;\n\n// -----------------------------------------------------------------------------\n// Decode-result type — shared between the reducer's optional pre-flight\n// cache and the `tryDecodeProjectionPayload` helper. Hoisted above the\n// reducer so the optional `prevalidated` parameter can reference it.\n// -----------------------------------------------------------------------------\n\ntype DecodeResult<T> =\n\t| { readonly ok: true; readonly value: T }\n\t| { readonly ok: false; readonly cause: unknown };\n\n// -----------------------------------------------------------------------------\n// Pure reducer\n// -----------------------------------------------------------------------------\n\n/**\n * Pure projection reducer. One event in, one new state out. All field\n * writes preserve `readonly` invariants by constructing fresh\n * objects/arrays rather than mutating.\n *\n * The `tag` switch is exhaustive against `EngineEvent['tag']`; the\n * trailing `_exhaustive: never` causes TS to flag any new tag that\n * isn't handled here.\n *\n * @param prevalidated - Optional pre-flight decode result for\n * `projection.updated` payloads. When the Effect-aware `updateRef`\n * seam has already decoded the payload (to drive the warning\n * emission inside the fiber's logger Layer), it threads the result\n * here so the reducer doesn't decode the same payload twice on\n * the hot path. Direct `applyEvent` callers (tests, in-process\n * projection consumers) leave this undefined and the reducer\n * re-decodes — the schema is sync + deterministic so the cached\n * result and a fresh decode agree.\n */\nexport const applyEvent = (\n\tstate: SubscribableState,\n\tevent: EngineEvent,\n\tprevalidated?: DecodeResult<unknown>,\n): SubscribableState => {\n\t// Every event advances `lastEvent.at`. `lastEvent.seq` is bumped at\n\t// the `updateRef` wrapper, not here — this reducer is pure data.\n\tconst withTouched = (next: Partial<SubscribableState>): SubscribableState => ({\n\t\t...state,\n\t\t...next,\n\t\tlastEvent: { seq: state.lastEvent.seq, at: eventAtOrNull(event) ?? state.lastEvent.at },\n\t});\n\n\t// Lifecycle-shaped events flow through the typed `LifecycleFact`\n\t// bridge — the projection consumes the merge-not-replace fact\n\t// instead of writing each lifecycle field independently. Non-\n\t// lifecycle events take their original specialised paths below.\n\tconst fact = factFromEvent(event);\n\tif (fact !== null) {\n\t\treturn withTouched({\n\t\t\trows: upsertRow(state.rows, fact.pluginKey, (row) => applyLifecycleFact(row, fact.delta)),\n\t\t});\n\t}\n\n\tswitch (event.tag) {\n\t\tcase 'lifecycle.statusChanged':\n\t\tcase 'lifecycle.phaseSet':\n\t\t\t// Handled above via `factFromEvent` — kept in the switch for\n\t\t\t// `_exhaustive` discipline.\n\t\t\treturn withTouched({});\n\n\t\tcase 'log.appended':\n\t\t\treturn withTouched({\n\t\t\t\trows: upsertRow(state.rows, event.pluginKey, (row) =>\n\t\t\t\t\tappendLogLine(row, event.line, event.level),\n\t\t\t\t),\n\t\t\t});\n\n\t\tcase 'endpoint.registered':\n\t\t\treturn withTouched({\n\t\t\t\tendpoints: upsertEndpoint(state.endpoints, event.endpoint),\n\t\t\t\trows: attachEndpoint(state.rows, event.endpoint),\n\t\t\t});\n\n\t\tcase 'projection.updated':\n\t\t\t// Substrate stays name-blind on the event vocabulary; the\n\t\t\t// reducer is the projection orchestrator and owns the per-\n\t\t\t// `kind` decode. New plugin-author projection kinds slot in\n\t\t\t// here without a new event variant — extending this switch\n\t\t\t// is the load-bearing knob.\n\t\t\t//\n\t\t\t// Per STYLE_GUIDE §20: a misbehaving plugin emitting a\n\t\t\t// malformed payload must NOT crash the reducer or the\n\t\t\t// surrounding projection stream. Decode per-kind via the\n\t\t\t// canonical Schema; on failure, drop the slice update\n\t\t\t// (`lastEvent.at` still advances so the renderer sees the\n\t\t\t// event was observed). Warning emission is handled by the\n\t\t\t// Effect-aware seam at the `updateRef` call site so it runs\n\t\t\t// inside the fiber's structured-logging context — the\n\t\t\t// reducer itself stays pure-data sync (no logger access),\n\t\t\t// just signaling a decode failure via a non-`ok` decode\n\t\t\t// result.\n\t\t\tif (event.kind === 'account') {\n\t\t\t\tconst decoded =\n\t\t\t\t\tprevalidated ?? tryDecodeProjectionPayload(AccountProjectionSchema, event.payload);\n\t\t\t\t// `event.payload` (not `decoded.value`) is forwarded once the\n\t\t\t\t// schema has confirmed the structural shape — the runtime brand\n\t\t\t\t// (`account/${string}`) is TS-only and Schema can't express\n\t\t\t\t// it. The cast is justified by the preceding decode, not a\n\t\t\t\t// `Schema.decodeUnknownSync(...) as A` bare-cast (§20).\n\t\t\t\treturn decoded.ok\n\t\t\t\t\t? withTouched({\n\t\t\t\t\t\t\taccounts: upsertAccount(state.accounts, event.payload as AccountProjection),\n\t\t\t\t\t\t})\n\t\t\t\t\t: withTouched({});\n\t\t\t}\n\t\t\tif (event.kind === 'package') {\n\t\t\t\tconst decoded =\n\t\t\t\t\tprevalidated ?? tryDecodeProjectionPayload(PackageProjectionSchema, event.payload);\n\t\t\t\treturn decoded.ok\n\t\t\t\t\t? withTouched({\n\t\t\t\t\t\t\tpackages: upsertPackage(state.packages, event.payload as PackageProjection),\n\t\t\t\t\t\t})\n\t\t\t\t\t: withTouched({});\n\t\t\t}\n\t\t\treturn withTouched({});\n\n\t\tcase 'endpoint.released':\n\t\t\t// Renderer projection keeps endpoint history as last-known\n\t\t\t// operator affordance. A released service may no longer be\n\t\t\t// reachable, but hiding the URL during shutdown/restart makes\n\t\t\t// the TUI lose the most useful debugging handle.\n\t\t\treturn withTouched({});\n\n\t\tcase 'strategy.registered':\n\t\tcase 'strategy.unregistered':\n\t\tcase 'manifest.flushed':\n\t\tcase 'codegen.emitted':\n\t\tcase 'snapshot.captureStarted':\n\t\tcase 'snapshot.captureProgress':\n\t\tcase 'snapshot.captureSkipped':\n\t\tcase 'snapshot.captureFailed':\n\t\tcase 'snapshot.captured':\n\t\tcase 'snapshot.restored':\n\t\t\t// Engine-internal events that don't carry a projection slice\n\t\t\t// — surfaced on the live event stream for renderers that care,\n\t\t\t// but contribute no field to the subscribable state. (Adding\n\t\t\t// a projection field for any of these requires an architecture\n\t\t\t// revision per G2.)\n\t\t\treturn withTouched({});\n\n\t\tcase 'shutdown.escalated':\n\t\t\treturn withTouched({\n\t\t\t\tcycle: { ...state.cycle, phase: 'shutting-down' },\n\t\t\t});\n\n\t\tcase 'error.reported':\n\t\t\treturn withTouched({\n\t\t\t\terrors: pushBounded(state.errors, event.error, MAX_ERRORS_KEPT),\n\t\t\t\trows: event.error.pluginKey\n\t\t\t\t\t? upsertRow(state.rows, event.error.pluginKey, (row) => ({\n\t\t\t\t\t\t\t...row,\n\t\t\t\t\t\t\tlastError: event.error,\n\t\t\t\t\t\t}))\n\t\t\t\t\t: state.rows,\n\t\t\t});\n\n\t\tcase 'build.statusChanged':\n\t\t\treturn withTouched({\n\t\t\t\tstackBuild: pushBounded(state.stackBuild, event.entry, MAX_BUILD_ENTRIES_KEPT),\n\t\t\t});\n\n\t\tcase 'restart.requested':\n\t\t\treturn withTouched({\n\t\t\t\tcycle: { ...state.cycle, phase: 'restarting' },\n\t\t\t\trows:\n\t\t\t\t\tevent.target === 'stack'\n\t\t\t\t\t\t? state.rows.map((r) => ({ ...r, selectiveRestartHighlight: false }))\n\t\t\t\t\t\t: state.rows.map((r) =>\n\t\t\t\t\t\t\t\tr.key === (event.target as { pluginKey: PluginKey }).pluginKey\n\t\t\t\t\t\t\t\t\t? { ...r, selectiveRestartHighlight: true }\n\t\t\t\t\t\t\t\t\t: r,\n\t\t\t\t\t\t\t),\n\t\t\t});\n\n\t\tcase 'restart.completed':\n\t\t\treturn withTouched({\n\t\t\t\tcycle: { ...state.cycle, phase: 'running' },\n\t\t\t\trows: state.rows.map((r) => ({ ...r, selectiveRestartHighlight: false })),\n\t\t\t});\n\n\t\tcase 'engine.orchestrator.dispatchFailed':\n\t\t\t// Orchestrator-side dispatch failure surfaced for renderers /\n\t\t\t// log consumers; carries no projection slice (the originating\n\t\t\t// plugin remains in its current lifecycle state — sink failure\n\t\t\t// is orchestrator-fault, not plugin-fault).\n\t\t\treturn withTouched({});\n\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = event;\n\t\t\tvoid _exhaustive;\n\t\t\treturn state;\n\t\t}\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Engine-side mutators (not driven by EngineEvent)\n// -----------------------------------------------------------------------------\n\n/**\n * Replace `identity` on the projection. Called once at boot per cycle;\n * does NOT change on hot-restart.\n */\nexport const setIdentity = (\n\tstate: SubscribableState,\n\tidentity: SubscribableState['identity'],\n): SubscribableState => ({ ...state, identity });\n\n/**\n * Bump the cycle counter. Called at the start of each engine cycle.\n * Resets only fields the architecture says are per-cycle:\n * - `cycle.id`, `cycle.startedAt`, `cycle.phase`\n * - per-row `selectiveRestartHighlight`\n * The error log, endpoints, build log, and `lastEvent.seq` are\n * intentionally preserved across cycles so the renderer sees\n * continuous history.\n */\nexport const bumpCycle = (\n\tstate: SubscribableState,\n\tnow: number,\n\tphase: SubscribableState['cycle']['phase'] = 'booting',\n): SubscribableState => ({\n\t...state,\n\tcycle: { id: state.cycle.id + 1, startedAt: now, phase },\n\trows: state.rows.map((r) => ({ ...r, selectiveRestartHighlight: false })),\n});\n\n/**\n * Register (or replace) a row in the projection. The supervisor wires\n * the call in.\n */\nexport const declareRow = (state: SubscribableState, row: Row): SubscribableState => {\n\tconst idx = state.rows.findIndex((r) => r.key === row.key);\n\tif (idx === -1) return { ...state, rows: [...state.rows, row] };\n\tconst next = state.rows.slice();\n\tnext[idx] = row;\n\treturn { ...state, rows: next };\n};\n\n/**\n * Drop a row. Used during selective-restart when a plugin is being\n * removed (rare; most restarts replay the same row).\n */\nexport const dropRow = (state: SubscribableState, key: PluginKey): SubscribableState => ({\n\t...state,\n\trows: state.rows.filter((r) => r.key !== key),\n});\n\nexport const declareAccount = (\n\tstate: SubscribableState,\n\taccount: AccountProjection,\n): SubscribableState => ({\n\t...state,\n\taccounts: upsertAccount(state.accounts, account),\n});\n\nexport const declarePackage = (\n\tstate: SubscribableState,\n\tpkg: PackageProjection,\n): SubscribableState => ({\n\t...state,\n\tpackages: upsertPackage(state.packages, pkg),\n});\n\n// -----------------------------------------------------------------------------\n// SubscriptionRef-driven updaters\n// -----------------------------------------------------------------------------\n\n/**\n * Apply an `EngineEvent` to the ref, bumping the sequence number\n * atomically. The seq is monotonic per process and survives cycle\n * boundaries.\n *\n * Uses `state.lastEvent.seq + 1` for monotonic per-process counts.\n *\n * Side-channel: when the event is a `projection.updated` with a\n * malformed payload, the reducer drops the slice and we emit a\n * structured warning here, inside the surrounding fiber, so it lands\n * on the fiber's structured-logging path (`Effect.logWarning`). This\n * is the only place in the reducer where a warning is emitted; the\n * pure `applyEvent` reducer never touches the logger.\n */\nexport const updateRef = (\n\tref: SubscriptionRef.SubscriptionRef<SubscribableState>,\n\tevent: EngineEvent,\n): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\t// Pre-flight: detect malformed projection-updated payloads so the\n\t\t// warning runs inside the fiber's logger context. We thread the\n\t\t// decode result into the reducer below so the hot path decodes\n\t\t// the payload exactly once per event — the previous shape ran\n\t\t// the same `Schema.decodeUnknownSync` pass twice (once here,\n\t\t// once inside `applyEvent`) on every projection.updated event.\n\t\tlet prevalidated: DecodeResult<unknown> | undefined;\n\t\tif (event.tag === 'projection.updated') {\n\t\t\tif (event.kind === 'account' || event.kind === 'package') {\n\t\t\t\tconst schema = event.kind === 'account' ? AccountProjectionSchema : PackageProjectionSchema;\n\t\t\t\tprevalidated = tryDecodeProjectionPayload(schema, event.payload);\n\t\t\t\tif (!prevalidated.ok) {\n\t\t\t\t\tyield* Effect.logWarning(\n\t\t\t\t\t\t`projection.updated: dropping malformed ${event.kind} payload for key=${event.key}`,\n\t\t\t\t\t).pipe(\n\t\t\t\t\t\tEffect.annotateLogs({\n\t\t\t\t\t\t\t[SpanAttr.errorMessage]: formatDecodeIssue(prevalidated.cause),\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tyield* SubscriptionRef.update(ref, (state) => {\n\t\t\tconst next = applyEvent(state, event, prevalidated);\n\t\t\treturn {\n\t\t\t\t...next,\n\t\t\t\tlastEvent: { seq: state.lastEvent.seq + 1, at: next.lastEvent.at },\n\t\t\t};\n\t\t});\n\t});\n\n// -----------------------------------------------------------------------------\n// Internals\n// -----------------------------------------------------------------------------\n\n/**\n * Sync per-kind validator for `projection.updated` payloads. Returns\n * an `ok` decode result on success or a non-`ok` one on decode\n * failure. Pure-data sync: no logger access, no `Effect.runSync` —\n * the reducer is documented as pure, and earlier versions of this\n * seam called `Effect.runSync(Effect.logWarning(...))` outside any\n * fiber context, which logged off-fiber (bypassing the supervisor's\n * structured-logging context). The Effect-aware `updateRef` wrapper\n * now emits the warning inside the fiber so the warning is captured\n * consistently with the rest of the supervisor's logs.\n *\n * On success callers still pass `event.payload` to the upsert (not the\n * decoded copy) — the runtime brand types (`account/${string}`,\n * `package/${string}`) are TS-only and the decoded copy would strip\n * them. The schema acts as a structural guard only.\n */\nconst tryDecodeProjectionPayload = <S extends Schema.Decoder<unknown>>(\n\tschema: S,\n\tpayload: unknown,\n): DecodeResult<S['Type']> => {\n\ttry {\n\t\treturn { ok: true, value: Schema.decodeUnknownSync(schema)(payload) };\n\t} catch (cause) {\n\t\treturn { ok: false, cause };\n\t}\n};\n\n/**\n * Render a `Schema.decodeUnknownSync` throw into a human-readable\n * string for the warn annotation. `SchemaError` in Effect v4 stores\n * the parse issue and exposes `.message` (= `issue.toString()`); using\n * that surfaces the actual decode-issue path/expectation. Fallback to\n * `String(cause)` for non-SchemaError throws (defensive — the schema's\n * sync decoder should always throw `SchemaError`).\n */\nconst formatDecodeIssue = (cause: unknown): string => {\n\tif (Schema.isSchemaError(cause)) {\n\t\treturn cause.message;\n\t}\n\tif (cause instanceof Error) {\n\t\treturn cause.message;\n\t}\n\treturn String(cause);\n};\n\nconst upsertRow = (\n\trows: ReadonlyArray<Row>,\n\tkey: PluginKey,\n\tupdate: (row: Row) => Row,\n): ReadonlyArray<Row> => {\n\tconst idx = rows.findIndex((r) => r.key === key);\n\tif (idx === -1) {\n\t\t// Row was never declared. We do NOT auto-create — declaration is\n\t\t// the plugin's job. Drop the event silently; the renderer can't\n\t\t// render a row it doesn't know about.\n\t\treturn rows;\n\t}\n\tconst next = rows.slice();\n\tnext[idx] = update(rows[idx]!);\n\treturn next;\n};\n\nconst appendLogLine = (row: Row, line: string, level: 'info' | 'warn' | 'error'): Row => {\n\tconst nextLines = [...row.logTail.lines, line];\n\tconst truncated = nextLines.length > MAX_ROW_LOG_LINES;\n\tconst trimmed = truncated ? nextLines.slice(-MAX_ROW_LOG_LINES) : nextLines;\n\treturn {\n\t\t...row,\n\t\tlogTail: {\n\t\t\tlines: trimmed,\n\t\t\tlevel: maxLevel(row.logTail.level, level),\n\t\t\ttruncated: row.logTail.truncated || truncated,\n\t\t},\n\t};\n};\n\nconst maxLevel = (\n\ta: 'info' | 'warn' | 'error',\n\tb: 'info' | 'warn' | 'error',\n): 'info' | 'warn' | 'error' => {\n\tconst rank = { info: 0, warn: 1, error: 2 } as const;\n\treturn rank[a] >= rank[b] ? a : b;\n};\n\nconst upsertEndpoint = (\n\tendpoints: ReadonlyArray<Endpoint>,\n\tendpoint: Endpoint,\n): ReadonlyArray<Endpoint> => {\n\tconst idx = endpoints.findIndex((e) => e.endpointKey === endpoint.endpointKey);\n\tif (idx === -1) return [...endpoints, endpoint];\n\tconst next = endpoints.slice();\n\tnext[idx] = endpoint;\n\treturn next;\n};\n\nconst upsertAccount = (\n\taccounts: ReadonlyArray<AccountProjection>,\n\taccount: AccountProjection,\n): ReadonlyArray<AccountProjection> => {\n\tconst idx = accounts.findIndex((entry) => entry.key === account.key);\n\tif (idx === -1) return [...accounts, account];\n\tconst next = accounts.slice();\n\tnext[idx] = {\n\t\t...accounts[idx]!,\n\t\t...account,\n\t\tfunding: {\n\t\t\t...accounts[idx]!.funding,\n\t\t\t...account.funding,\n\t\t},\n\t};\n\treturn next;\n};\n\nconst upsertPackage = (\n\tpackages: ReadonlyArray<PackageProjection>,\n\tpkg: PackageProjection,\n): ReadonlyArray<PackageProjection> => {\n\tconst idx = packages.findIndex((entry) => entry.key === pkg.key);\n\tif (idx === -1) return [...packages, pkg];\n\tconst next = packages.slice();\n\tnext[idx] = {\n\t\t...packages[idx]!,\n\t\t...pkg,\n\t};\n\treturn next;\n};\n\nconst attachEndpoint = (rows: ReadonlyArray<Row>, endpoint: Endpoint): ReadonlyArray<Row> => {\n\treturn rows.map((row) =>\n\t\tendpoint.pluginKey === row.key\n\t\t\t? row.endpoints.includes(endpoint.endpointKey)\n\t\t\t\t? row\n\t\t\t\t: { ...row, endpoints: [...row.endpoints, endpoint.endpointKey] }\n\t\t\t: row,\n\t);\n};\n\nconst pushBounded = <T>(xs: ReadonlyArray<T>, value: T, max: number): ReadonlyArray<T> => {\n\tconst next = [...xs, value];\n\treturn next.length > max ? next.slice(-max) : next;\n};\n\n// Re-export the bounded-capacity constants for tests / docs.\nexport const __capacities = {\n\tMAX_ERRORS_KEPT,\n\tMAX_BUILD_ENTRIES_KEPT,\n\tMAX_ROW_LOG_LINES,\n} as const;\n\n// Re-export referenced sub-types for downstream consumers (tests,\n// renderer-side wrappers) so they don't reach into substrate/\n// directly.\nexport type {\n\tAccountProjection,\n\tBuildEntry,\n\tEndpoint,\n\tLifecycleStatus,\n\tPackageProjection,\n\tRow,\n\tStructuredError,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"update.mjs","names":[],"sources":["../../../../src/substrate/runtime/projection/update.ts"],"sourcesContent":["// Projection updater.\n//\n// Architecture § Renderer § \"Subscribable projection — exact field\n// enumeration\" (G2). The updater is a typed event-handler: each\n// `EngineEvent` produces a structurally-typed projection mutation.\n// Renderers never see the event taxonomy here; they only see the\n// resulting state.\n//\n// The updater is *pure data*: it takes the old state + an event,\n// returns the new state. Atomicity comes from\n// `SubscriptionRef.update` at the call site.\n//\n// Discipline:\n// - No display vocabulary (`title`, `primary`, `extras`) anywhere\n// in the produced state.\n// - All fields the architecture enumerates are reachable; new\n// event tags are picked up via exhaustive `tag` match (the\n// `_exhaustive: never` line will fail to type-check if a new\n// tag is added without a handler here).\n\nimport { Effect, Schema, SubscriptionRef } from 'effect';\n\nimport type { PluginKey } from '../../brand.ts';\nimport type { EngineEvent } from '../../events.ts';\nimport { eventAtOrNull } from '../../event-time.ts';\nimport type { LifecycleFact, LifecycleStatus } from '../../lifecycle.ts';\nimport type {\n\tAccountProjection,\n\tBuildEntry,\n\tEndpoint,\n\tPackageProjection,\n\tRow,\n\tStructuredError,\n\tSubscribableState,\n} from '../../projection.ts';\nimport { LogAttr } from '../observability/log-attrs.ts';\n\n// -----------------------------------------------------------------------------\n// Per-kind `projection.updated` payload schemas\n// -----------------------------------------------------------------------------\n//\n// The reducer is the projection orchestrator and owns the per-`kind`\n// structural decode of `projection.updated` payloads (STYLE_GUIDE §20:\n// a misbehaving plugin emitting a malformed payload must NOT crash the\n// reducer or the surrounding projection stream — the decode acts as a\n// structural guard so a bad slice is dropped, not applied). These two\n// schemas mirror `AccountProjection` / `PackageProjection` in\n// `substrate/projection.ts`; the runtime brand on `key`\n// (`account/${string}` / `package/${string}`) is TS-only and Schema\n// can't express it, so callers forward the original `event.payload`\n// after a successful decode rather than the decoded (brand-stripped)\n// copy.\n//\n// The plain-renderer reads the raw event stream (not the reduced\n// state) and re-runs the same decode for the same payloads; it imports\n// these schemas from here, the single canonical definition.\n\n/** Structural schema for a `projection.updated[account]` payload. */\nexport const AccountProjectionSchema = Schema.Struct({\n\tkey: Schema.String,\n\trowKey: Schema.NullOr(Schema.String),\n\tname: Schema.String,\n\taddress: Schema.NullOr(Schema.String),\n\tscheme: Schema.NullOr(Schema.Literals(['ed25519', 'secp256k1', 'secp256r1'])),\n\tsource: Schema.NullOr(Schema.Literals(['real', 'impersonate'])),\n\tfunding: Schema.Struct({\n\t\tstatus: Schema.Literals(['pending', 'funded', 'skipped', 'failed', 'unknown']),\n\t\tbalanceMist: Schema.NullOr(Schema.String),\n\t\trequestedMist: Schema.NullOr(Schema.String),\n\t\tentries: Schema.optional(\n\t\t\tSchema.Array(\n\t\t\t\tSchema.Struct({\n\t\t\t\t\tcoin: Schema.String,\n\t\t\t\t\tfullCoinType: Schema.String,\n\t\t\t\t\tamount: Schema.String,\n\t\t\t\t\t// Mirrors `AccountProjection.funding.entries[].status` in\n\t\t\t\t\t// `substrate/projection.ts`. `'already-satisfied'` is\n\t\t\t\t\t// the pre-existing-balance short-circuit emitted by the\n\t\t\t\t\t// account funding pass — semantically a success, kept\n\t\t\t\t\t// distinct from `'funded'` so renderers can surface the\n\t\t\t\t\t// cached-vs-fresh distinction.\n\t\t\t\t\tstatus: Schema.Literals(['funded', 'already-satisfied', 'skipped']),\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t}),\n\twalletVisible: Schema.Boolean,\n\tupdatedAt: Schema.Number,\n});\n\n/** Structural schema for a `projection.updated[package]` payload. */\nexport const PackageProjectionSchema = Schema.Struct({\n\tkey: Schema.String,\n\trowKey: Schema.NullOr(Schema.String),\n\tname: Schema.String,\n\tkind: Schema.Literals(['local', 'known']),\n\tpackageId: Schema.String,\n\tupgradeCapId: Schema.NullOr(Schema.String),\n\tmvrPlaceholder: Schema.String,\n\tsourcePath: Schema.NullOr(Schema.String),\n\tupdatedAt: Schema.Number,\n});\n\n// -----------------------------------------------------------------------------\n// Capacity policy\n// -----------------------------------------------------------------------------\n\n/** Bounded buffer for the top-level errors list. */\nconst MAX_ERRORS_KEPT = 100;\n/** Bounded buffer for the top-level build-entries list. */\nconst MAX_BUILD_ENTRIES_KEPT = 200;\n/** Per-row bounded log-tail length. Architecture: default 100 lines. */\nconst MAX_ROW_LOG_LINES = 100;\n\n// -----------------------------------------------------------------------------\n// Decode-result type — shared between the reducer's optional pre-flight\n// cache and the `tryDecodeProjectionPayload` helper. Hoisted above the\n// reducer so the optional `prevalidated` parameter can reference it.\n// -----------------------------------------------------------------------------\n\ntype DecodeResult<T> =\n\t| { readonly ok: true; readonly value: T }\n\t| { readonly ok: false; readonly cause: unknown };\n\n// -----------------------------------------------------------------------------\n// LifecycleFact bridge\n// -----------------------------------------------------------------------------\n//\n// `substrate/lifecycle.ts` declares `LifecycleFact` — the merge-not-\n// replace per-plugin lifecycle slice the projection consumes. The\n// reducer routes lifecycle-shaped events through this typed bridge\n// instead of writing each status / phase / restart field\n// independently. The reducer for non-lifecycle events is unchanged.\n\n/** Per-plugin fact delta. Each field is optional — only the fields the\n * source event carries are populated. `applyLifecycleFact` merges a\n * delta into the existing `Row`. */\nexport interface LifecycleFactDelta {\n\treadonly status?: LifecycleStatus;\n\treadonly phase?: LifecycleFact['phase'];\n\treadonly selectiveRestartHighlight?: boolean;\n}\n\n/** Project a lifecycle-shaped `EngineEvent` into a per-plugin delta.\n * Returns `null` for events that don't carry lifecycle information so\n * the reducer can short-circuit. The substrate stays event-name-blind\n * by routing through this single mapping table. */\nexport const factFromEvent = (\n\tevent: EngineEvent,\n): { readonly pluginKey: PluginKey; readonly delta: LifecycleFactDelta } | null => {\n\tswitch (event.tag) {\n\t\tcase 'lifecycle.statusChanged':\n\t\t\treturn {\n\t\t\t\tpluginKey: event.pluginKey,\n\t\t\t\tdelta: { status: event.to },\n\t\t\t};\n\t\tcase 'lifecycle.phaseSet':\n\t\t\treturn {\n\t\t\t\tpluginKey: event.pluginKey,\n\t\t\t\tdelta: { phase: event.phase },\n\t\t\t};\n\t\tdefault:\n\t\t\t// `restart.requested` ALSO updates cycle.phase + clears\n\t\t\t// other rows' highlights — that's a multi-row reducer\n\t\t\t// concern the projection handles directly. We deliberately\n\t\t\t// keep the bridge scoped to the closed `LifecycleFact`\n\t\t\t// shape (status / phase / selectiveRestartHighlight) so\n\t\t\t// callers can derive per-plugin facts without rebuilding\n\t\t\t// the cycle phase too.\n\t\t\treturn null;\n\t}\n};\n\n/** Apply a fact delta to a row. Pure. Fields not in the delta are\n * preserved verbatim — the merge-not-replace shape `LifecycleFact`\n * promises. */\nexport const applyLifecycleFact = (row: Row, delta: LifecycleFactDelta): Row => ({\n\t...row,\n\t...(delta.status !== undefined ? { status: delta.status } : {}),\n\t...(delta.phase !== undefined ? { phase: delta.phase } : {}),\n\t...(delta.selectiveRestartHighlight !== undefined\n\t\t? { selectiveRestartHighlight: delta.selectiveRestartHighlight }\n\t\t: {}),\n});\n\n// -----------------------------------------------------------------------------\n// Pure reducer\n// -----------------------------------------------------------------------------\n\n/**\n * Pure projection reducer. One event in, one new state out. All field\n * writes preserve `readonly` invariants by constructing fresh\n * objects/arrays rather than mutating.\n *\n * The `tag` switch is exhaustive against `EngineEvent['tag']`; the\n * trailing `_exhaustive: never` causes TS to flag any new tag that\n * isn't handled here.\n *\n * @param prevalidated - Optional pre-flight decode result for\n * `projection.updated` payloads. When the Effect-aware `updateRef`\n * seam has already decoded the payload (to drive the warning\n * emission inside the fiber's logger Layer), it threads the result\n * here so the reducer doesn't decode the same payload twice on\n * the hot path. Direct `applyEvent` callers (tests, in-process\n * projection consumers) leave this undefined and the reducer\n * re-decodes — the schema is sync + deterministic so the cached\n * result and a fresh decode agree.\n */\nexport const applyEvent = (\n\tstate: SubscribableState,\n\tevent: EngineEvent,\n\tprevalidated?: DecodeResult<unknown>,\n): SubscribableState => {\n\t// Every event advances `lastEvent.at`. `lastEvent.seq` is bumped at\n\t// the `updateRef` wrapper, not here — this reducer is pure data.\n\tconst withTouched = (next: Partial<SubscribableState>): SubscribableState => ({\n\t\t...state,\n\t\t...next,\n\t\tlastEvent: { seq: state.lastEvent.seq, at: eventAtOrNull(event) ?? state.lastEvent.at },\n\t});\n\n\t// Lifecycle-shaped events flow through the typed `LifecycleFact`\n\t// bridge — the projection consumes the merge-not-replace fact\n\t// instead of writing each lifecycle field independently. Non-\n\t// lifecycle events take their original specialised paths below.\n\tconst fact = factFromEvent(event);\n\tif (fact !== null) {\n\t\treturn withTouched({\n\t\t\trows: upsertRow(state.rows, fact.pluginKey, (row) => applyLifecycleFact(row, fact.delta)),\n\t\t});\n\t}\n\n\tswitch (event.tag) {\n\t\tcase 'lifecycle.statusChanged':\n\t\tcase 'lifecycle.phaseSet':\n\t\t\t// Handled above via `factFromEvent` — kept in the switch for\n\t\t\t// `_exhaustive` discipline.\n\t\t\treturn withTouched({});\n\n\t\tcase 'log.appended':\n\t\t\treturn withTouched({\n\t\t\t\trows: upsertRow(state.rows, event.pluginKey, (row) =>\n\t\t\t\t\tappendLogLine(row, event.line, event.level),\n\t\t\t\t),\n\t\t\t});\n\n\t\tcase 'endpoint.registered':\n\t\t\treturn withTouched({\n\t\t\t\tendpoints: upsertEndpoint(state.endpoints, event.endpoint),\n\t\t\t\trows: attachEndpoint(state.rows, event.endpoint),\n\t\t\t});\n\n\t\tcase 'projection.updated':\n\t\t\t// Substrate stays name-blind on the event vocabulary; the\n\t\t\t// reducer is the projection orchestrator and owns the per-\n\t\t\t// `kind` decode. New plugin-author projection kinds slot in\n\t\t\t// here without a new event variant — extending this switch\n\t\t\t// is the load-bearing knob.\n\t\t\t//\n\t\t\t// Per STYLE_GUIDE §20: a misbehaving plugin emitting a\n\t\t\t// malformed payload must NOT crash the reducer or the\n\t\t\t// surrounding projection stream. Decode per-kind via the\n\t\t\t// canonical Schema; on failure, drop the slice update\n\t\t\t// (`lastEvent.at` still advances so the renderer sees the\n\t\t\t// event was observed). Warning emission is handled by the\n\t\t\t// Effect-aware seam at the `updateRef` call site so it runs\n\t\t\t// inside the fiber's structured-logging context — the\n\t\t\t// reducer itself stays pure-data sync (no logger access),\n\t\t\t// just signaling a decode failure via a non-`ok` decode\n\t\t\t// result.\n\t\t\tif (event.kind === 'account') {\n\t\t\t\tconst decoded =\n\t\t\t\t\tprevalidated ?? tryDecodeProjectionPayload(AccountProjectionSchema, event.payload);\n\t\t\t\t// `event.payload` (not `decoded.value`) is forwarded once the\n\t\t\t\t// schema has confirmed the structural shape — the runtime brand\n\t\t\t\t// (`account/${string}`) is TS-only and Schema can't express\n\t\t\t\t// it. The cast is justified by the preceding decode, not a\n\t\t\t\t// `Schema.decodeUnknownSync(...) as A` bare-cast (§20).\n\t\t\t\treturn decoded.ok\n\t\t\t\t\t? withTouched({\n\t\t\t\t\t\t\taccounts: upsertAccount(state.accounts, event.payload as AccountProjection),\n\t\t\t\t\t\t})\n\t\t\t\t\t: withTouched({});\n\t\t\t}\n\t\t\tif (event.kind === 'package') {\n\t\t\t\tconst decoded =\n\t\t\t\t\tprevalidated ?? tryDecodeProjectionPayload(PackageProjectionSchema, event.payload);\n\t\t\t\treturn decoded.ok\n\t\t\t\t\t? withTouched({\n\t\t\t\t\t\t\tpackages: upsertPackage(state.packages, event.payload as PackageProjection),\n\t\t\t\t\t\t})\n\t\t\t\t\t: withTouched({});\n\t\t\t}\n\t\t\treturn withTouched({});\n\n\t\tcase 'endpoint.released':\n\t\t\t// Renderer projection keeps endpoint history as last-known\n\t\t\t// operator affordance. A released service may no longer be\n\t\t\t// reachable, but hiding the URL during shutdown/restart makes\n\t\t\t// the TUI lose the most useful debugging handle.\n\t\t\treturn withTouched({});\n\n\t\tcase 'strategy.registered':\n\t\tcase 'strategy.unregistered':\n\t\tcase 'manifest.flushed':\n\t\tcase 'codegen.emitted':\n\t\t\t// Engine-internal events that don't carry a projection slice\n\t\t\t// — surfaced on the live event stream for renderers that care,\n\t\t\t// but contribute no field to the subscribable state. (Adding\n\t\t\t// a projection field for any of these requires an architecture\n\t\t\t// revision per G2.)\n\t\t\treturn withTouched({});\n\n\t\tcase 'snapshot.captureSkipped':\n\t\tcase 'snapshot.captureFailed':\n\t\tcase 'snapshot.captured':\n\t\t\t// Terminal capture outcomes — the containers are resumed and the\n\t\t\t// stack is live again. Guarded clear of the transient\n\t\t\t// 'snapshotting' phase back to 'running': only un-stick the\n\t\t\t// snapshotting phase, so a stray terminal event can't yank the\n\t\t\t// phase out of an in-flight restart/shutdown.\n\t\t\treturn withTouched({\n\t\t\t\tcycle:\n\t\t\t\t\tstate.cycle.phase === 'snapshotting' ? { ...state.cycle, phase: 'running' } : state.cycle,\n\t\t\t});\n\n\t\tcase 'snapshot.restored':\n\t\t\t// Published by the command-loop's `snapshot.restore` case AFTER\n\t\t\t// the destructive restore (live managed containers removed) and\n\t\t\t// BEFORE the follow-on full-drain re-acquire. Mark the cycle\n\t\t\t// 'restoring' so the dashboard reflects the in-flight restore; the\n\t\t\t// re-acquire then emits its own `restart.requested`/`restart.\n\t\t\t// completed` (→ 'restarting' → 'running') plus per-row acquiring→\n\t\t\t// ready transitions, which carry the rest of the restore-half\n\t\t\t// status updates and settle the phase back to 'running'.\n\t\t\treturn withTouched({\n\t\t\t\tcycle: { ...state.cycle, phase: 'restoring' },\n\t\t\t});\n\n\t\tcase 'shutdown.escalated':\n\t\t\treturn withTouched({\n\t\t\t\tcycle: { ...state.cycle, phase: 'shutting-down' },\n\t\t\t});\n\n\t\tcase 'error.reported':\n\t\t\treturn withTouched({\n\t\t\t\terrors: pushBounded(state.errors, event.error, MAX_ERRORS_KEPT),\n\t\t\t\trows: event.error.pluginKey\n\t\t\t\t\t? upsertRow(state.rows, event.error.pluginKey, (row) => ({\n\t\t\t\t\t\t\t...row,\n\t\t\t\t\t\t\tlastError: event.error,\n\t\t\t\t\t\t}))\n\t\t\t\t\t: state.rows,\n\t\t\t});\n\n\t\tcase 'build.statusChanged':\n\t\t\treturn withTouched({\n\t\t\t\tstackBuild: pushBounded(state.stackBuild, event.entry, MAX_BUILD_ENTRIES_KEPT),\n\t\t\t});\n\n\t\tcase 'restart.requested':\n\t\t\treturn withTouched({\n\t\t\t\tcycle: { ...state.cycle, phase: 'restarting' },\n\t\t\t\trows:\n\t\t\t\t\tevent.target === 'stack'\n\t\t\t\t\t\t? state.rows.map((r) => ({ ...r, selectiveRestartHighlight: false }))\n\t\t\t\t\t\t: state.rows.map((r) =>\n\t\t\t\t\t\t\t\tr.key === (event.target as { pluginKey: PluginKey }).pluginKey\n\t\t\t\t\t\t\t\t\t? { ...r, selectiveRestartHighlight: true }\n\t\t\t\t\t\t\t\t\t: r,\n\t\t\t\t\t\t\t),\n\t\t\t});\n\n\t\tcase 'restart.completed':\n\t\t\treturn withTouched({\n\t\t\t\tcycle: { ...state.cycle, phase: 'running' },\n\t\t\t\trows: state.rows.map((r) => ({ ...r, selectiveRestartHighlight: false })),\n\t\t\t});\n\n\t\tcase 'engine.orchestrator.dispatchFailed':\n\t\t\t// Orchestrator-side dispatch failure surfaced for renderers /\n\t\t\t// log consumers; carries no projection slice (the originating\n\t\t\t// plugin remains in its current lifecycle state — sink failure\n\t\t\t// is orchestrator-fault, not plugin-fault).\n\t\t\treturn withTouched({});\n\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = event;\n\t\t\tvoid _exhaustive;\n\t\t\treturn state;\n\t\t}\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Engine-side mutators (not driven by EngineEvent)\n// -----------------------------------------------------------------------------\n\n/**\n * Replace `identity` on the projection. Called once at boot per cycle;\n * does NOT change on hot-restart.\n */\nexport const setIdentity = (\n\tstate: SubscribableState,\n\tidentity: SubscribableState['identity'],\n): SubscribableState => ({ ...state, identity });\n\nexport const declareAccount = (\n\tstate: SubscribableState,\n\taccount: AccountProjection,\n): SubscribableState => ({\n\t...state,\n\taccounts: upsertAccount(state.accounts, account),\n});\n\n// -----------------------------------------------------------------------------\n// SubscriptionRef-driven updaters\n// -----------------------------------------------------------------------------\n\n/**\n * Apply an `EngineEvent` to the ref, bumping the sequence number\n * atomically. The seq is monotonic per process and survives cycle\n * boundaries.\n *\n * Uses `state.lastEvent.seq + 1` for monotonic per-process counts.\n *\n * Side-channel: when the event is a `projection.updated` with a\n * malformed payload, the reducer drops the slice and we emit a\n * structured warning here, inside the surrounding fiber, so it lands\n * on the fiber's structured-logging path (`Effect.logWarning`). This\n * is the only place in the reducer where a warning is emitted; the\n * pure `applyEvent` reducer never touches the logger.\n */\nexport const updateRef = (\n\tref: SubscriptionRef.SubscriptionRef<SubscribableState>,\n\tevent: EngineEvent,\n): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\t// Pre-flight: detect malformed projection-updated payloads so the\n\t\t// warning runs inside the fiber's logger context. We thread the\n\t\t// decode result into the reducer below so the hot path decodes\n\t\t// the payload exactly once per event — the previous shape ran\n\t\t// the same `Schema.decodeUnknownSync` pass twice (once here,\n\t\t// once inside `applyEvent`) on every projection.updated event.\n\t\tlet prevalidated: DecodeResult<unknown> | undefined;\n\t\tif (event.tag === 'projection.updated') {\n\t\t\tif (event.kind === 'account' || event.kind === 'package') {\n\t\t\t\tconst schema = event.kind === 'account' ? AccountProjectionSchema : PackageProjectionSchema;\n\t\t\t\tprevalidated = tryDecodeProjectionPayload(schema, event.payload);\n\t\t\t\tif (!prevalidated.ok) {\n\t\t\t\t\tyield* Effect.logWarning(\n\t\t\t\t\t\t`projection.updated: dropping malformed ${event.kind} payload for key=${event.key}`,\n\t\t\t\t\t).pipe(\n\t\t\t\t\t\tEffect.annotateLogs({\n\t\t\t\t\t\t\t[LogAttr.errorMessage]: formatDecodeIssue(prevalidated.cause),\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tyield* SubscriptionRef.update(ref, (state) => {\n\t\t\tconst next = applyEvent(state, event, prevalidated);\n\t\t\treturn {\n\t\t\t\t...next,\n\t\t\t\tlastEvent: { seq: state.lastEvent.seq + 1, at: next.lastEvent.at },\n\t\t\t};\n\t\t});\n\t});\n\n// -----------------------------------------------------------------------------\n// Internals\n// -----------------------------------------------------------------------------\n\n/**\n * Sync per-kind validator for `projection.updated` payloads. Returns\n * an `ok` decode result on success or a non-`ok` one on decode\n * failure. Pure-data sync: no logger access, no `Effect.runSync` —\n * the reducer is documented as pure, and earlier versions of this\n * seam called `Effect.runSync(Effect.logWarning(...))` outside any\n * fiber context, which logged off-fiber (bypassing the supervisor's\n * structured-logging context). The Effect-aware `updateRef` wrapper\n * now emits the warning inside the fiber so the warning is captured\n * consistently with the rest of the supervisor's logs.\n *\n * On success callers still pass `event.payload` to the upsert (not the\n * decoded copy) — the runtime brand types (`account/${string}`,\n * `package/${string}`) are TS-only and the decoded copy would strip\n * them. The schema acts as a structural guard only.\n */\nconst tryDecodeProjectionPayload = <S extends Schema.Decoder<unknown>>(\n\tschema: S,\n\tpayload: unknown,\n): DecodeResult<S['Type']> => {\n\ttry {\n\t\treturn { ok: true, value: Schema.decodeUnknownSync(schema)(payload) };\n\t} catch (cause) {\n\t\treturn { ok: false, cause };\n\t}\n};\n\n/**\n * Render a `Schema.decodeUnknownSync` throw into a human-readable\n * string for the warn annotation. `SchemaError` in Effect v4 stores\n * the parse issue and exposes `.message` (= `issue.toString()`); using\n * that surfaces the actual decode-issue path/expectation. Fallback to\n * `String(cause)` for non-SchemaError throws (defensive — the schema's\n * sync decoder should always throw `SchemaError`).\n */\nconst formatDecodeIssue = (cause: unknown): string => {\n\tif (Schema.isSchemaError(cause)) {\n\t\treturn cause.message;\n\t}\n\tif (cause instanceof Error) {\n\t\treturn cause.message;\n\t}\n\treturn String(cause);\n};\n\nconst upsertRow = (\n\trows: ReadonlyArray<Row>,\n\tkey: PluginKey,\n\tupdate: (row: Row) => Row,\n): ReadonlyArray<Row> => {\n\tconst idx = rows.findIndex((r) => r.key === key);\n\tif (idx === -1) {\n\t\t// Row was never declared. We do NOT auto-create — declaration is\n\t\t// the plugin's job. Drop the event silently; the renderer can't\n\t\t// render a row it doesn't know about.\n\t\treturn rows;\n\t}\n\tconst next = rows.slice();\n\tnext[idx] = update(rows[idx]!);\n\treturn next;\n};\n\nconst appendLogLine = (row: Row, line: string, level: 'info' | 'warn' | 'error'): Row => {\n\tconst nextLines = [...row.logTail.lines, line];\n\tconst truncated = nextLines.length > MAX_ROW_LOG_LINES;\n\tconst trimmed = truncated ? nextLines.slice(-MAX_ROW_LOG_LINES) : nextLines;\n\treturn {\n\t\t...row,\n\t\tlogTail: {\n\t\t\tlines: trimmed,\n\t\t\tlevel: maxLevel(row.logTail.level, level),\n\t\t\ttruncated: row.logTail.truncated || truncated,\n\t\t},\n\t};\n};\n\nconst maxLevel = (\n\ta: 'info' | 'warn' | 'error',\n\tb: 'info' | 'warn' | 'error',\n): 'info' | 'warn' | 'error' => {\n\tconst rank = { info: 0, warn: 1, error: 2 } as const;\n\treturn rank[a] >= rank[b] ? a : b;\n};\n\nconst upsertEndpoint = (\n\tendpoints: ReadonlyArray<Endpoint>,\n\tendpoint: Endpoint,\n): ReadonlyArray<Endpoint> => {\n\tconst idx = endpoints.findIndex((e) => e.endpointKey === endpoint.endpointKey);\n\tif (idx === -1) return [...endpoints, endpoint];\n\tconst next = endpoints.slice();\n\tnext[idx] = endpoint;\n\treturn next;\n};\n\nconst upsertAccount = (\n\taccounts: ReadonlyArray<AccountProjection>,\n\taccount: AccountProjection,\n): ReadonlyArray<AccountProjection> => {\n\tconst idx = accounts.findIndex((entry) => entry.key === account.key);\n\tif (idx === -1) return [...accounts, account];\n\tconst next = accounts.slice();\n\tnext[idx] = {\n\t\t...accounts[idx]!,\n\t\t...account,\n\t\tfunding: {\n\t\t\t...accounts[idx]!.funding,\n\t\t\t...account.funding,\n\t\t},\n\t};\n\treturn next;\n};\n\nconst upsertPackage = (\n\tpackages: ReadonlyArray<PackageProjection>,\n\tpkg: PackageProjection,\n): ReadonlyArray<PackageProjection> => {\n\tconst idx = packages.findIndex((entry) => entry.key === pkg.key);\n\tif (idx === -1) return [...packages, pkg];\n\tconst next = packages.slice();\n\tnext[idx] = {\n\t\t...packages[idx]!,\n\t\t...pkg,\n\t};\n\treturn next;\n};\n\nconst attachEndpoint = (rows: ReadonlyArray<Row>, endpoint: Endpoint): ReadonlyArray<Row> => {\n\treturn rows.map((row) =>\n\t\tendpoint.pluginKey === row.key\n\t\t\t? row.endpoints.includes(endpoint.endpointKey)\n\t\t\t\t? row\n\t\t\t\t: { ...row, endpoints: [...row.endpoints, endpoint.endpointKey] }\n\t\t\t: row,\n\t);\n};\n\nconst pushBounded = <T>(xs: ReadonlyArray<T>, value: T, max: number): ReadonlyArray<T> => {\n\tconst next = [...xs, value];\n\treturn next.length > max ? next.slice(-max) : next;\n};\n\n// Re-export referenced sub-types for downstream consumers (tests,\n// renderer-side wrappers) so they don't reach into substrate/\n// directly.\nexport type {\n\tAccountProjection,\n\tBuildEntry,\n\tEndpoint,\n\tLifecycleStatus,\n\tPackageProjection,\n\tRow,\n\tStructuredError,\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"random-suffix.mjs","names":[],"sources":["../../../src/substrate/runtime/random-suffix.ts"],"sourcesContent":["// Random hex-suffix helper.\n//\n// Snapshot capture names, codegen cycle ids, and restore staging tags\n// all want the same shape: a random hex string of a chosen length,\n// derived from `crypto.randomUUID()` so collisions are\n// cryptographically negligible across same-process parallel callers\n// (the `Math.random()`-based predecessors flagged in STYLE_GUIDE §17\n// did NOT have that guarantee).\n//\n// The choice of length is intentionally a caller knob — different\n// surfaces have different staleness windows and collision tolerance:\n//\n// - 8 chars: operator-visible names (snapshot id, snap-* prefix).\n// Short enough to type/share.\n// - 12 chars: per-stack container-tag suffixes. Wide enough that\n// two concurrent restores under the same stack don't clash.\n// - 16 chars: codegen cycle ids — transient staging-directory names\n// (`.staging.<id>` / `.bak.<id>`) that the substrate rm's\n// after publish. Defense-in-depth for the race window\n// where two emit cycles under a custom-CLI caller could\n// mint overlapping staging dirs against the same shared\n// outputDir; a collision there would corrupt a half-built\n// tree, not just clash an operator-visible name.\n// - 24 chars: restore staging image tags. Image tags are global to\n// the docker daemon — two parallel restores across stacks\n// share the namespace, so the extra entropy keeps them\n// from clashing under heavy CI parallelism.\n\nimport { randomUUID } from 'node:crypto';\n\n/** Mint a random hex suffix of `length` chars. Uses\n * `crypto.randomUUID()` so collisions are cryptographically\n * negligible (STYLE_GUIDE §17). `length` is bounded by the UUID's\n * 32 hex chars; callers MUST pass `1..=32`. */\nexport const mintRandomSuffix = (length: number): string =>\n\trandomUUID().replace(/-/g, '').slice(0, length);\n"],"mappings":";;;;;;AAkCA,MAAa,oBAAoB,WAChC,YAAY,CAAC,QAAQ,MAAM,GAAG,CAAC,MAAM,GAAG,OAAO"}
1
+ {"version":3,"file":"random-suffix.mjs","names":[],"sources":["../../../src/substrate/runtime/random-suffix.ts"],"sourcesContent":["// Random hex-suffix helper.\n//\n// Snapshot capture names, codegen cycle ids, and restore staging tags\n// all want the same shape: a random hex string of a chosen length,\n// derived from `crypto.randomUUID()` so collisions are\n// cryptographically negligible across same-process parallel callers\n// (the `Math.random()`-based predecessors flagged in STYLE_GUIDE §17\n// did NOT have that guarantee).\n//\n// The choice of length is intentionally a caller knob — different\n// surfaces have different staleness windows and collision tolerance:\n//\n// - 8 chars: operator-visible names (snapshot id, snap-* prefix).\n// Short enough to type/share.\n// - 12 chars: per-stack container-tag suffixes. Wide enough that\n// two concurrent restores under the same stack don't clash.\n// - 16 chars: codegen cycle ids — transient staging-directory names\n// (`.staging.<id>` / `.bak.<id>`) that the substrate rm's\n// after publish. Defense-in-depth for the race window\n// where two emit cycles under a custom-CLI caller could\n// mint overlapping staging dirs against the same shared\n// outputDir; a collision there would corrupt a half-built\n// tree, not just clash an operator-visible name.\n// - 24 chars: restore staging image tags. Image tags are global to\n// the docker daemon — two parallel restores across stacks\n// share the namespace, so the extra entropy keeps them\n// from clashing under heavy CI parallelism.\n\nimport { randomUUID } from 'node:crypto';\n\n/** Mint a random hex suffix of `length` chars. Uses\n * `crypto.randomUUID()` so collisions are cryptographically\n * negligible (STYLE_GUIDE §17). `length` is bounded by the UUID's\n * 32 hex chars; callers MUST pass `1..=32`. */\nexport const mintRandomSuffix = (length: number): string =>\n\trandomUUID().replace(/-/g, '').slice(0, length);\n"],"mappings":";;;;;;AAkCA,MAAa,oBAAoB,WAChC,WAAW,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,MAAM"}
@@ -0,0 +1,146 @@
1
+ import { StageAndSwapError, stageAndSwap } from "../stage-and-swap/index.mjs";
2
+ import { Effect, FileSystem } from "effect";
3
+ //#region src/substrate/runtime/reconcile/fs-plan.ts
4
+ /** Remove every direct child of `stackRoot` the preserve predicate does
5
+ * NOT keep. Returns `{ preservedCount, sawChildren }` so a following
6
+ * `reap-empty` can decide whether the root is now empty using the SAME
7
+ * directory listing. */
8
+ const runSweepChildren = (op) => Effect.gen(function* () {
9
+ const fs = yield* FileSystem.FileSystem;
10
+ const children = yield* fs.readDirectory(op.stackRoot).pipe(Effect.catch(() => Effect.succeed([])));
11
+ let preservedCount = 0;
12
+ for (const name of children) {
13
+ if (op.preserve(name)) {
14
+ preservedCount += 1;
15
+ continue;
16
+ }
17
+ yield* fs.remove(`${op.stackRoot}/${name}`, {
18
+ recursive: true,
19
+ force: true
20
+ }).pipe(Effect.catch(op.onError));
21
+ }
22
+ return {
23
+ preservedCount,
24
+ sawChildren: children.length > 0
25
+ };
26
+ });
27
+ /** Reap `stackRoot` itself when ZERO preserved children survive (so a wipe
28
+ * never leaks an empty `stacks/<stack>/` shell). Best-effort. `recursive`
29
+ * is required to remove a directory at all; it is safe precisely because
30
+ * the `preservedCount === 0` guard means no preserved subtree exists. */
31
+ const runReapEmpty = (op, preservedCount, sawChildren) => Effect.gen(function* () {
32
+ const fs = yield* FileSystem.FileSystem;
33
+ if (preservedCount === 0 && sawChildren) yield* fs.remove(op.stackRoot, {
34
+ recursive: true,
35
+ force: true
36
+ }).pipe(Effect.ignore);
37
+ });
38
+ /** Reap snapshot-catalog directories whose `meta.json` is missing /
39
+ * unreadable (partial artifacts). Returns `{ reaped, inspected }` so
40
+ * prune keeps its `PruneResult.reaped` + `inspected`. Reads the catalog
41
+ * read-only first, then classifies each entry via the caller's
42
+ * `isMetaMissing`. An absent catalog yields zero inspected (early
43
+ * return). */
44
+ const runReapMetaMissing = (op) => Effect.gen(function* () {
45
+ const fs = yield* FileSystem.FileSystem;
46
+ if (!(yield* fs.exists(op.catalogDir).pipe(Effect.catch(() => Effect.succeed(false))))) return {
47
+ reaped: [],
48
+ inspected: 0
49
+ };
50
+ const ids = yield* fs.readDirectory(op.catalogDir).pipe(Effect.catch(op.onReaddirError));
51
+ const reaped = [];
52
+ for (const id of ids) {
53
+ const dir = `${op.catalogDir}/${id}`;
54
+ if (yield* op.isMetaMissing(dir)) {
55
+ yield* fs.remove(dir, {
56
+ recursive: true,
57
+ force: true
58
+ }).pipe(Effect.catch(op.onRemoveError));
59
+ reaped.push(id);
60
+ }
61
+ }
62
+ return {
63
+ reaped,
64
+ inspected: ids.length
65
+ };
66
+ });
67
+ /** Sweep committed byproduct images via the runtime adapter's label-scoped
68
+ * cleanup (the `reap-byproducts` mechanism). Returns the swept count so
69
+ * prune keeps its `PruneResult.imagesSwept`. */
70
+ const runReapImages = (op, deps) => Effect.gen(function* () {
71
+ if (deps.runtime === void 0 || deps.imageLabelFilter === void 0) return yield* Effect.die("fs-plan reap-images: FsPlanDeps must carry `runtime` + `imageLabelFilter`");
72
+ return yield* deps.runtime.removeManagedImages(deps.imageLabelFilter).pipe(Effect.catch(op.onError));
73
+ });
74
+ /** Publish a new `targetPath` tree via the UNCHANGED `stageAndSwap`
75
+ * primitive (NOT modified, NOT reimplemented). The executor only
76
+ * ASSEMBLES `stageAndSwap`'s args from the op: the build body, the
77
+ * staging/backup sibling paths, the per-direction `preserveFromTarget` /
78
+ * `preserveOnPreseed` riders (NOT a cache-policy projection — guardrail
79
+ * §3.1), and the optional publish lock. The build's success value is
80
+ * observed by the caller through its OWN closure (restore pushes staged
81
+ * image refs into a caller-held array), so it is discarded here. The
82
+ * primitive's `StageAndSwapError` is mapped through the op's `onSwapError`
83
+ * failer into the caller's error tag `E`, mirroring the DIRECT ops — the
84
+ * executor never invents an error tag. */
85
+ const runSwapTree = (op) => {
86
+ return stageAndSwap({
87
+ targetPath: op.targetPath,
88
+ stagingPath: op.stagingPath,
89
+ backupPath: op.backupPath,
90
+ build: op.buildEffect,
91
+ ...op.preserveFromTarget === void 0 ? {} : { preserveFromTarget: op.preserveFromTarget },
92
+ ...op.preserveOnPreseed === void 0 ? {} : { preserveOnPreseed: op.preserveOnPreseed },
93
+ ...op.publishLockPath === void 0 ? {} : { publishLockPath: op.publishLockPath }
94
+ }).pipe(Effect.catch((error) => error instanceof StageAndSwapError ? op.onSwapError(error) : Effect.fail(error)), Effect.asVoid);
95
+ };
96
+ /**
97
+ * Run an ordered `ReconcileFsPlan` in sequence, accumulating the
98
+ * counts/ids the routed flows surface (`FsPlanResult`). DIRECT ops mutate
99
+ * the runtime tree / image store; the `swap-tree` op publishes a new tree
100
+ * through the unchanged `stageAndSwap` (its build's success value is
101
+ * observed by the caller's own closure, so it does not thread back into
102
+ * `FsPlanResult`).
103
+ *
104
+ * `reap-empty` reads the preserved-child count produced by the IMMEDIATELY
105
+ * PRECEDING `sweep-children` op (wipe's plan is always
106
+ * `[sweep-children, reap-empty]`) — the count threads through the fold so
107
+ * the root is reaped only when no preserved child survived.
108
+ */
109
+ const executeFsPlan = (plan, deps = {}) => Effect.gen(function* () {
110
+ const reapedIds = [];
111
+ let inspected = 0;
112
+ let imagesSwept = 0;
113
+ let lastSweep = {
114
+ preservedCount: 0,
115
+ sawChildren: false
116
+ };
117
+ for (const op of plan.ops) switch (op.op) {
118
+ case "sweep-children":
119
+ lastSweep = yield* runSweepChildren(op);
120
+ break;
121
+ case "reap-empty":
122
+ yield* runReapEmpty(op, lastSweep.preservedCount, lastSweep.sawChildren);
123
+ break;
124
+ case "reap-meta-missing": {
125
+ const result = yield* runReapMetaMissing(op);
126
+ reapedIds.push(...result.reaped);
127
+ inspected += result.inspected;
128
+ break;
129
+ }
130
+ case "reap-images":
131
+ imagesSwept += yield* runReapImages(op, deps);
132
+ break;
133
+ case "swap-tree":
134
+ yield* runSwapTree(op);
135
+ break;
136
+ }
137
+ return {
138
+ inspected,
139
+ reapedIds,
140
+ imagesSwept
141
+ };
142
+ });
143
+ //#endregion
144
+ export { executeFsPlan };
145
+
146
+ //# sourceMappingURL=fs-plan.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs-plan.mjs","names":[],"sources":["../../../../src/substrate/runtime/reconcile/fs-plan.ts"],"sourcesContent":["// fs-plan executor.\n//\n// Runs a typed `ReconcileFsPlan` (an ordered list of `ReconcileFsOp`s,\n// see `./spec.ts`) over the file-tree + docker image store. This is the\n// single executor every label-scope flow's fsPlan compiles down to;\n// `reconcileLabel` (`./label.ts`) calls it after the container target\n// converges.\n//\n// Two op families:\n//\n// - DIRECT ops — `sweep-children`, `reap-empty`, `reap-meta-missing`,\n// `reap-images`. These are exactly the ops wipe + prune need; each\n// carries the caller's per-direction predicate / classifier + a phase\n// failer so routing a flow through the executor PRESERVES that flow's\n// existing error tags. The executor never picks its own error tag and\n// never collapses the per-direction preserve predicate into a\n// cache-policy projection.\n//\n// - SWAP-TREE op (`swap-tree`) — publishes a new tree via the UNCHANGED\n// `stageAndSwap` primitive (NOT modified, NOT reimplemented). The\n// executor only ASSEMBLES stageAndSwap's args from the op. Both restore\n// and capture route through this one op; each caller supplies its own\n// staging-tree build body as the op's generic `buildEffect`.\n//\n// `stageAndSwap` is untouched; the preserve-list builders are\n// per-direction constants supplied by the CALLER, never derived inside\n// the executor.\n\nimport { Effect, FileSystem } from 'effect';\n\nimport type { ContainerRuntime } from '../../../contracts/container-runtime.ts';\nimport type { ContainerLabelTuple } from '../../../contracts/snapshotable.ts';\nimport { stageAndSwap, StageAndSwapError } from '../stage-and-swap/index.ts';\nimport type { ReconcileFsOp, ReconcileFsPlan, ReconcileLabelTuple } from './spec.ts';\n\n// -----------------------------------------------------------------------------\n// Executor deps + result\n// -----------------------------------------------------------------------------\n\n/** Everything the fs-plan executor needs beyond `FileSystem` (provided as\n * a requirement). `runtime` + `imageLabelFilter` back the `reap-images`\n * op; flows with no image op may omit both. */\nexport interface FsPlanDeps {\n\treadonly runtime?: ContainerRuntime;\n\t/** The label tuple the `reap-images` op sweeps on (already narrowed to\n\t * its target role by the caller, e.g. `role: SNAPSHOT_IMAGE_ROLE`). */\n\treadonly imageLabelFilter?: ReconcileLabelTuple;\n}\n\n/** Per-plan result the executor accumulates — the counts/ids the routed\n * flows still surface (prune's `inspected` + `reaped` + `imagesSwept`).\n * Wipe consumes none of these (it returns `void`); they default empty. */\nexport interface FsPlanResult {\n\t/** Catalog entries examined by `reap-meta-missing` (prune's\n\t * `inspected`). Zero for plans with no catalog-reap op. */\n\treadonly inspected: number;\n\treadonly reapedIds: ReadonlyArray<string>;\n\treadonly imagesSwept: number;\n}\n\n// -----------------------------------------------------------------------------\n// Per-op runners (DIRECT ops — implemented)\n// -----------------------------------------------------------------------------\n\n/** Remove every direct child of `stackRoot` the preserve predicate does\n * NOT keep. Returns `{ preservedCount, sawChildren }` so a following\n * `reap-empty` can decide whether the root is now empty using the SAME\n * directory listing. */\nconst runSweepChildren = <E>(\n\top: Extract<ReconcileFsOp<E>, { op: 'sweep-children' }>,\n): Effect.Effect<{ preservedCount: number; sawChildren: boolean }, E, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst children = yield* fs\n\t\t\t.readDirectory(op.stackRoot)\n\t\t\t.pipe(Effect.catch(() => Effect.succeed([] as ReadonlyArray<string>)));\n\t\tlet preservedCount = 0;\n\t\tfor (const name of children) {\n\t\t\tif (op.preserve(name)) {\n\t\t\t\tpreservedCount += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tyield* fs\n\t\t\t\t.remove(`${op.stackRoot}/${name}`, { recursive: true, force: true })\n\t\t\t\t.pipe(Effect.catch(op.onError));\n\t\t}\n\t\treturn { preservedCount, sawChildren: children.length > 0 };\n\t});\n\n/** Reap `stackRoot` itself when ZERO preserved children survive (so a wipe\n * never leaks an empty `stacks/<stack>/` shell). Best-effort. `recursive`\n * is required to remove a directory at all; it is safe precisely because\n * the `preservedCount === 0` guard means no preserved subtree exists. */\nconst runReapEmpty = (\n\top: Extract<ReconcileFsOp<never>, { op: 'reap-empty' }>,\n\tpreservedCount: number,\n\tsawChildren: boolean,\n): Effect.Effect<void, never, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tif (preservedCount === 0 && sawChildren) {\n\t\t\tyield* fs.remove(op.stackRoot, { recursive: true, force: true }).pipe(Effect.ignore);\n\t\t}\n\t});\n\n/** Reap snapshot-catalog directories whose `meta.json` is missing /\n * unreadable (partial artifacts). Returns `{ reaped, inspected }` so\n * prune keeps its `PruneResult.reaped` + `inspected`. Reads the catalog\n * read-only first, then classifies each entry via the caller's\n * `isMetaMissing`. An absent catalog yields zero inspected (early\n * return). */\nconst runReapMetaMissing = <E>(\n\top: Extract<ReconcileFsOp<E>, { op: 'reap-meta-missing' }>,\n): Effect.Effect<{ reaped: ReadonlyArray<string>; inspected: number }, E, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst catalogExists = yield* fs\n\t\t\t.exists(op.catalogDir)\n\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\tif (!catalogExists) return { reaped: [] as ReadonlyArray<string>, inspected: 0 };\n\t\tconst ids = yield* fs.readDirectory(op.catalogDir).pipe(Effect.catch(op.onReaddirError));\n\t\tconst reaped: Array<string> = [];\n\t\tfor (const id of ids) {\n\t\t\tconst dir = `${op.catalogDir}/${id}`;\n\t\t\tconst metaMissing = yield* op.isMetaMissing(dir);\n\t\t\tif (metaMissing) {\n\t\t\t\tyield* fs\n\t\t\t\t\t.remove(dir, { recursive: true, force: true })\n\t\t\t\t\t.pipe(Effect.catch(op.onRemoveError));\n\t\t\t\treaped.push(id);\n\t\t\t}\n\t\t}\n\t\treturn { reaped, inspected: ids.length };\n\t});\n\n/** Sweep committed byproduct images via the runtime adapter's label-scoped\n * cleanup (the `reap-byproducts` mechanism). Returns the swept count so\n * prune keeps its `PruneResult.imagesSwept`. */\nconst runReapImages = <E>(\n\top: Extract<ReconcileFsOp<E>, { op: 'reap-images' }>,\n\tdeps: FsPlanDeps,\n): Effect.Effect<number, E> =>\n\tEffect.gen(function* () {\n\t\tif (deps.runtime === undefined || deps.imageLabelFilter === undefined) {\n\t\t\t// A `reap-images` op without a runtime/filter is a wiring bug, not\n\t\t\t// a runtime condition — fail closed so it surfaces immediately.\n\t\t\treturn yield* Effect.die(\n\t\t\t\t'fs-plan reap-images: FsPlanDeps must carry `runtime` + `imageLabelFilter`',\n\t\t\t);\n\t\t}\n\t\treturn yield* deps.runtime\n\t\t\t.removeManagedImages(deps.imageLabelFilter as Partial<ContainerLabelTuple>)\n\t\t\t.pipe(Effect.catch(op.onError));\n\t});\n\n// -----------------------------------------------------------------------------\n// Per-op runner (SWAP-TREE op — publish the op's `buildEffect` tree)\n// -----------------------------------------------------------------------------\n\n/** Publish a new `targetPath` tree via the UNCHANGED `stageAndSwap`\n * primitive (NOT modified, NOT reimplemented). The executor only\n * ASSEMBLES `stageAndSwap`'s args from the op: the build body, the\n * staging/backup sibling paths, the per-direction `preserveFromTarget` /\n * `preserveOnPreseed` riders (NOT a cache-policy projection — guardrail\n * §3.1), and the optional publish lock. The build's success value is\n * observed by the caller through its OWN closure (restore pushes staged\n * image refs into a caller-held array), so it is discarded here. The\n * primitive's `StageAndSwapError` is mapped through the op's `onSwapError`\n * failer into the caller's error tag `E`, mirroring the DIRECT ops — the\n * executor never invents an error tag. */\nconst runSwapTree = <E>(\n\top: Extract<ReconcileFsOp<E>, { op: 'swap-tree' }>,\n): Effect.Effect<void, E, FileSystem.FileSystem> => {\n\t// `op.buildEffect` fails with the caller's `E`; `stageAndSwap` adds its\n\t// own `StageAndSwapError` from the rename. Map ONLY that concrete\n\t// stage-and-swap error through the op's `onSwapError` failer (the build's\n\t// own `E` errors pass straight through). `catchAll` + an explicit\n\t// `_tag` guard keeps this fully typed without `catchTag` widening the\n\t// failer's parameter when `E` is generic.\n\tconst swapped: Effect.Effect<unknown, E | StageAndSwapError, FileSystem.FileSystem> =\n\t\tstageAndSwap({\n\t\t\ttargetPath: op.targetPath,\n\t\t\tstagingPath: op.stagingPath,\n\t\t\tbackupPath: op.backupPath,\n\t\t\tbuild: op.buildEffect,\n\t\t\t...(op.preserveFromTarget === undefined ? {} : { preserveFromTarget: op.preserveFromTarget }),\n\t\t\t...(op.preserveOnPreseed === undefined ? {} : { preserveOnPreseed: op.preserveOnPreseed }),\n\t\t\t...(op.publishLockPath === undefined ? {} : { publishLockPath: op.publishLockPath }),\n\t\t});\n\treturn swapped.pipe(\n\t\tEffect.catch((error: E | StageAndSwapError) =>\n\t\t\terror instanceof StageAndSwapError ? op.onSwapError(error) : Effect.fail(error),\n\t\t),\n\t\tEffect.asVoid,\n\t);\n};\n\n// -----------------------------------------------------------------------------\n// The executor\n// -----------------------------------------------------------------------------\n\n/**\n * Run an ordered `ReconcileFsPlan` in sequence, accumulating the\n * counts/ids the routed flows surface (`FsPlanResult`). DIRECT ops mutate\n * the runtime tree / image store; the `swap-tree` op publishes a new tree\n * through the unchanged `stageAndSwap` (its build's success value is\n * observed by the caller's own closure, so it does not thread back into\n * `FsPlanResult`).\n *\n * `reap-empty` reads the preserved-child count produced by the IMMEDIATELY\n * PRECEDING `sweep-children` op (wipe's plan is always\n * `[sweep-children, reap-empty]`) — the count threads through the fold so\n * the root is reaped only when no preserved child survived.\n */\nexport const executeFsPlan = <E>(\n\tplan: ReconcileFsPlan<E>,\n\tdeps: FsPlanDeps = {},\n): Effect.Effect<FsPlanResult, E, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst reapedIds: Array<string> = [];\n\t\tlet inspected = 0;\n\t\tlet imagesSwept = 0;\n\t\t// Threaded from the last `sweep-children` so a following `reap-empty`\n\t\t// sees the preserved-count computed by that sweep.\n\t\tlet lastSweep = { preservedCount: 0, sawChildren: false };\n\n\t\tfor (const op of plan.ops) {\n\t\t\tswitch (op.op) {\n\t\t\t\tcase 'sweep-children': {\n\t\t\t\t\tlastSweep = yield* runSweepChildren(op);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'reap-empty': {\n\t\t\t\t\tyield* runReapEmpty(op, lastSweep.preservedCount, lastSweep.sawChildren);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'reap-meta-missing': {\n\t\t\t\t\tconst result = yield* runReapMetaMissing(op);\n\t\t\t\t\treapedIds.push(...result.reaped);\n\t\t\t\t\tinspected += result.inspected;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'reap-images': {\n\t\t\t\t\timagesSwept += yield* runReapImages(op, deps);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'swap-tree': {\n\t\t\t\t\t// Build the staging tree via the op's `buildEffect` and\n\t\t\t\t\t// publish it through the UNCHANGED `stageAndSwap`. The\n\t\t\t\t\t// preserve riders (`preserveFromTarget` /\n\t\t\t\t\t// `preserveOnPreseed`) map 1:1 onto `stageAndSwap`'s args as\n\t\t\t\t\t// PER-DIRECTION constants — NOT a cache-policy projection.\n\t\t\t\t\t// Both restore and capture route their build bodies through\n\t\t\t\t\t// this same runner.\n\t\t\t\t\tyield* runSwapTree(op);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { inspected, reapedIds, imagesSwept } satisfies FsPlanResult;\n\t});\n"],"mappings":";;;;;;;AAoEA,MAAM,oBACL,OAEA,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,WAAW,OAAO,GACtB,cAAc,GAAG,SAAS,CAAC,CAC3B,KAAK,OAAO,YAAY,OAAO,QAAQ,CAAC,CAA0B,CAAC,CAAC;CACtE,IAAI,iBAAiB;CACrB,KAAK,MAAM,QAAQ,UAAU;EAC5B,IAAI,GAAG,SAAS,IAAI,GAAG;GACtB,kBAAkB;GAClB;EACD;EACA,OAAO,GACL,OAAO,GAAG,GAAG,UAAU,GAAG,QAAQ;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC,CAAC,CACnE,KAAK,OAAO,MAAM,GAAG,OAAO,CAAC;CAChC;CACA,OAAO;EAAE;EAAgB,aAAa,SAAS,SAAS;CAAE;AAC3D,CAAC;;;;;AAMF,MAAM,gBACL,IACA,gBACA,gBAEA,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;CAC7B,IAAI,mBAAmB,KAAK,aAC3B,OAAO,GAAG,OAAO,GAAG,WAAW;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC,CAAC,CAAC,KAAK,OAAO,MAAM;AAErF,CAAC;;;;;;;AAQF,MAAM,sBACL,OAEA,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;CAI7B,IAAI,EAAC,OAHwB,GAC3B,OAAO,GAAG,UAAU,CAAC,CACrB,KAAK,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CAAC,IAC5B,OAAO;EAAE,QAAQ,CAAC;EAA4B,WAAW;CAAE;CAC/E,MAAM,MAAM,OAAO,GAAG,cAAc,GAAG,UAAU,CAAC,CAAC,KAAK,OAAO,MAAM,GAAG,cAAc,CAAC;CACvF,MAAM,SAAwB,CAAC;CAC/B,KAAK,MAAM,MAAM,KAAK;EACrB,MAAM,MAAM,GAAG,GAAG,WAAW,GAAG;EAEhC,IAAI,OADuB,GAAG,cAAc,GAAG,GAC9B;GAChB,OAAO,GACL,OAAO,KAAK;IAAE,WAAW;IAAM,OAAO;GAAK,CAAC,CAAC,CAC7C,KAAK,OAAO,MAAM,GAAG,aAAa,CAAC;GACrC,OAAO,KAAK,EAAE;EACf;CACD;CACA,OAAO;EAAE;EAAQ,WAAW,IAAI;CAAO;AACxC,CAAC;;;;AAKF,MAAM,iBACL,IACA,SAEA,OAAO,IAAI,aAAa;CACvB,IAAI,KAAK,YAAY,KAAA,KAAa,KAAK,qBAAqB,KAAA,GAG3D,OAAO,OAAO,OAAO,IACpB,2EACD;CAED,OAAO,OAAO,KAAK,QACjB,oBAAoB,KAAK,gBAAgD,CAAC,CAC1E,KAAK,OAAO,MAAM,GAAG,OAAO,CAAC;AAChC,CAAC;;;;;;;;;;;;AAiBF,MAAM,eACL,OACmD;CAiBnD,OATC,aAAa;EACZ,YAAY,GAAG;EACf,aAAa,GAAG;EAChB,YAAY,GAAG;EACf,OAAO,GAAG;EACV,GAAI,GAAG,uBAAuB,KAAA,IAAY,CAAC,IAAI,EAAE,oBAAoB,GAAG,mBAAmB;EAC3F,GAAI,GAAG,sBAAsB,KAAA,IAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,kBAAkB;EACxF,GAAI,GAAG,oBAAoB,KAAA,IAAY,CAAC,IAAI,EAAE,iBAAiB,GAAG,gBAAgB;CACnF,CACY,CAAC,CAAC,KACd,OAAO,OAAO,UACb,iBAAiB,oBAAoB,GAAG,YAAY,KAAK,IAAI,OAAO,KAAK,KAAK,CAC/E,GACA,OAAO,MACR;AACD;;;;;;;;;;;;;;AAmBA,MAAa,iBACZ,MACA,OAAmB,CAAC,MAEpB,OAAO,IAAI,aAAa;CACvB,MAAM,YAA2B,CAAC;CAClC,IAAI,YAAY;CAChB,IAAI,cAAc;CAGlB,IAAI,YAAY;EAAE,gBAAgB;EAAG,aAAa;CAAM;CAExD,KAAK,MAAM,MAAM,KAAK,KACrB,QAAQ,GAAG,IAAX;EACC,KAAK;GACJ,YAAY,OAAO,iBAAiB,EAAE;GACtC;EAED,KAAK;GACJ,OAAO,aAAa,IAAI,UAAU,gBAAgB,UAAU,WAAW;GACvE;EAED,KAAK,qBAAqB;GACzB,MAAM,SAAS,OAAO,mBAAmB,EAAE;GAC3C,UAAU,KAAK,GAAG,OAAO,MAAM;GAC/B,aAAa,OAAO;GACpB;EACD;EACA,KAAK;GACJ,eAAe,OAAO,cAAc,IAAI,IAAI;GAC5C;EAED,KAAK;GAQJ,OAAO,YAAY,EAAE;GACrB;CAEF;CAGD,OAAO;EAAE;EAAW;EAAW;CAAY;AAC5C,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { orderByLevel } from "../lifecycle/dep-graph.mjs";
2
+ import "../lifecycle/index.mjs";
3
+ //#region src/substrate/runtime/reconcile/graph.ts
4
+ /** Order a slice (carried on a `graph-keys` scope) into teardown +
5
+ * acquire orderings. Both orderings are always produced because a
6
+ * `drain∘converge` flow (selective-restart) needs both. Pure; delegates
7
+ * level math to the kept `orderByLevel`. */
8
+ const plan = (graph, scope) => {
9
+ const slice = new Set(scope.keys);
10
+ return {
11
+ slice,
12
+ teardownOrder: orderByLevel(graph, slice, "reverse"),
13
+ acquireOrder: orderByLevel(graph, slice, "forward")
14
+ };
15
+ };
16
+ //#endregion
17
+ export { plan };
18
+
19
+ //# sourceMappingURL=graph.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.mjs","names":[],"sources":["../../../../src/substrate/runtime/reconcile/graph.ts"],"sourcesContent":["// Dep-graph ordering — `plan(graph, scope)`, the single dep-ordering body.\n//\n// Given a slice (carried on a `graph-keys` scope) it produces the\n// teardown (reverse-dep) and acquire (forward-dep) orderings via the\n// kept `orderByLevel` level-ordering helper. Full-graph drains call it\n// directly with every key; the exclude-predicate slice (`planExcluding`)\n// and the downstream-closure slice + validation (`planRestart`) live in\n// `lifecycle/selective-restart.ts` and delegate the ordering here. Only\n// the ordering choreography lives in `plan`; each caller owns its own\n// slice computation.\n//\n// The supervisor flows then sequence the EXISTING `teardownKeys` /\n// `acquireKeys` execution primitives over these orderings directly (a\n// `drain` over `teardownOrder`, a `converge` over `acquireOrder`); there\n// is no graph-axis reconcile wrapper — `plan` is pure ordering.\n\nimport type { PluginKey } from '../../brand.ts';\nimport { orderByLevel, type ResolvedGraph } from '../lifecycle/index.ts';\n\n// -----------------------------------------------------------------------------\n// plan — the single dep-ordering body\n// -----------------------------------------------------------------------------\n\n/** A dep-ordered reconcile plan over a slice of the graph. Both arrays\n * cover exactly `slice`; they differ only in iteration direction\n * (`teardownOrder` reverse-dep, `acquireOrder` forward-dep). */\nexport interface GraphPlan {\n\treadonly slice: ReadonlySet<PluginKey>;\n\treadonly teardownOrder: ReadonlyArray<PluginKey>;\n\treadonly acquireOrder: ReadonlyArray<PluginKey>;\n}\n\n/** Order a slice (carried on a `graph-keys` scope) into teardown +\n * acquire orderings. Both orderings are always produced because a\n * `drain∘converge` flow (selective-restart) needs both. Pure; delegates\n * level math to the kept `orderByLevel`. */\nexport const plan = (\n\tgraph: ResolvedGraph,\n\tscope: { readonly kind: 'graph-keys'; readonly keys: ReadonlyArray<PluginKey> },\n): GraphPlan => {\n\tconst slice = new Set<PluginKey>(scope.keys);\n\tconst teardownOrder = orderByLevel(graph, slice, 'reverse');\n\tconst acquireOrder = orderByLevel(graph, slice, 'forward');\n\treturn { slice, teardownOrder, acquireOrder };\n};\n"],"mappings":";;;;;;;AAoCA,MAAa,QACZ,OACA,UACe;CACf,MAAM,QAAQ,IAAI,IAAe,MAAM,IAAI;CAG3C,OAAO;EAAE;EAAO,eAFM,aAAa,OAAO,OAAO,SAErB;EAAG,cADV,aAAa,OAAO,OAAO,SACN;CAAE;AAC7C"}
@@ -0,0 +1,5 @@
1
+ import "./graph.mjs";
2
+ import "./spec.mjs";
3
+ import "./fs-plan.mjs";
4
+ import "./label.mjs";
5
+ export {};
@@ -0,0 +1,41 @@
1
+ import { executeFsPlan } from "./fs-plan.mjs";
2
+ import { Effect } from "effect";
3
+ //#region src/substrate/runtime/reconcile/label.ts
4
+ /**
5
+ * Reconcile a flat label-scope spec toward its target + fs-plan. Returns
6
+ * the `FsPlanResult` (reaped ids + swept image count) the routed flows
7
+ * surface; flows that mutate nothing on disk just get the empty result.
8
+ *
9
+ * Order is `target → fsPlan`: for `wipe` the containers/networks/volumes
10
+ * are gone BEFORE the runtime tree is swept; for `prune` there is no
11
+ * container target, so it is purely the fsPlan (catalog reap + image
12
+ * reap).
13
+ */
14
+ const reconcileLabel = (spec, deps) => Effect.gen(function* () {
15
+ if (spec.scope.kind !== "label") return yield* Effect.die("reconcileLabel: expected a label scope; graph-keys flows drive teardownKeys/acquireKeys directly");
16
+ const tuple = spec.scope.tuple;
17
+ if (spec.target === "absent") {
18
+ const labelMatch = {
19
+ app: tuple.app,
20
+ stack: tuple.stack,
21
+ ...tuple.plugin === void 0 ? {} : { plugin: tuple.plugin },
22
+ ...tuple.role === void 0 ? {} : { role: tuple.role }
23
+ };
24
+ yield* deps.runtime.removeManagedContainers(labelMatch).pipe(Effect.catch(deps.onContainersError ?? ((cause) => Effect.die(cause))));
25
+ yield* deps.runtime.removeManagedNetworks(labelMatch).pipe(Effect.catch(deps.onNetworksError ?? ((cause) => Effect.die(cause))));
26
+ yield* deps.runtime.removeManagedVolumes(labelMatch).pipe(Effect.catch(deps.onVolumesError ?? ((cause) => Effect.die(cause))));
27
+ }
28
+ if (spec.fsPlan === void 0) return {
29
+ inspected: 0,
30
+ reapedIds: [],
31
+ imagesSwept: 0
32
+ };
33
+ return yield* executeFsPlan(spec.fsPlan, {
34
+ runtime: deps.runtime,
35
+ imageLabelFilter: tuple
36
+ });
37
+ });
38
+ //#endregion
39
+ export { reconcileLabel };
40
+
41
+ //# sourceMappingURL=label.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label.mjs","names":[],"sources":["../../../../src/substrate/runtime/reconcile/label.ts"],"sourcesContent":["// Reconcile over a flat label scope.\n//\n// The out-of-supervisor sibling of the in-supervisor graph flows (which\n// sequence `teardownKeys` / `acquireKeys` directly over `plan`'s\n// orderings, `./graph.ts`). Where the graph flows are dep-ordered, the\n// LABEL axis is a FLAT sweep over docker resources matched by an\n// `{app, stack[, plugin, role]}` tuple plus a file-tree fs-plan — there\n// is no live dep-graph to order (guardrail: graph-scope gets dep-order,\n// label-scope gets the flat `removeManaged*` sweep).\n//\n// `reconcileLabel(spec, deps)` executes, in order:\n//\n// 1. container TARGET — when `target === 'absent'`, force-remove managed\n// containers + networks + volumes at the label tuple via the EXISTING\n// `removeManaged{Containers,Networks,Volumes}` family (NOT\n// reimplemented). Each removal is mapped through a CALLER-supplied\n// failer so the routed flow keeps its existing error tags (wipe's\n// `sweep-containers` / `sweep-networks-volumes`). `target === 'running'`\n// is not a label-scope flow today (die seam).\n//\n// 2. fsPlan — the file-tree + image ops via `executeFsPlan`\n// (`./fs-plan.ts`). Prune carries NO container target (its only\n// docker mutation is the `reap-images` op inside the fsPlan); wipe\n// carries both.\n//\n// `decideRunAction` / `ensureContainer` are untouched — label scope\n// never picks a per-container docker action (the\n// `removeManaged*` family is a flat label sweep, not the per-node\n// converger). `stageAndSwap` is untouched.\n\nimport { Effect, FileSystem } from 'effect';\n\nimport type { ContainerRuntime } from '../../../contracts/container-runtime.ts';\nimport type { ContainerLabelTuple } from '../../../contracts/snapshotable.ts';\nimport { executeFsPlan, type FsPlanResult } from './fs-plan.ts';\nimport type { ReconcileSpec } from './spec.ts';\n\n// -----------------------------------------------------------------------------\n// Deps\n// -----------------------------------------------------------------------------\n\n/** A per-resource failure mapper — maps a `removeManaged*` defect onto the\n * caller's phase-tagged error (so routing wipe through here keeps its\n * `sweep-containers` / `sweep-networks-volumes` tags). */\nexport type ReconcileLabelFailer<E> = (cause: unknown) => Effect.Effect<never, E>;\n\n/** Everything `reconcileLabel` needs to drive the kept `removeManaged*`\n * family + the fs-plan executor. The container failers are only consulted\n * when `spec.target === 'absent'`; a target-less flow (prune) omits them.\n * `imageLabelFilter` is threaded to the fs-plan's `reap-images` op. */\nexport interface ReconcileLabelDeps<E> {\n\treadonly runtime: ContainerRuntime;\n\t/** Caller failers for the container TARGET sweep (absent-hard). */\n\treadonly onContainersError?: ReconcileLabelFailer<E>;\n\treadonly onNetworksError?: ReconcileLabelFailer<E>;\n\treadonly onVolumesError?: ReconcileLabelFailer<E>;\n}\n\n// -----------------------------------------------------------------------------\n// reconcileLabel\n// -----------------------------------------------------------------------------\n\n/**\n * Reconcile a flat label-scope spec toward its target + fs-plan. Returns\n * the `FsPlanResult` (reaped ids + swept image count) the routed flows\n * surface; flows that mutate nothing on disk just get the empty result.\n *\n * Order is `target → fsPlan`: for `wipe` the containers/networks/volumes\n * are gone BEFORE the runtime tree is swept; for `prune` there is no\n * container target, so it is purely the fsPlan (catalog reap + image\n * reap).\n */\nexport const reconcileLabel = <E>(\n\tspec: ReconcileSpec<E>,\n\tdeps: ReconcileLabelDeps<E>,\n): Effect.Effect<FsPlanResult, E, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tif (spec.scope.kind !== 'label') {\n\t\t\t// Wiring bug — `reconcileLabel` is the label-axis executor; the\n\t\t\t// in-supervisor graph flows drive `teardownKeys` / `acquireKeys`\n\t\t\t// directly and never build a label-scope spec.\n\t\t\treturn yield* Effect.die(\n\t\t\t\t'reconcileLabel: expected a label scope; graph-keys flows drive teardownKeys/acquireKeys directly',\n\t\t\t);\n\t\t}\n\t\tconst tuple = spec.scope.tuple;\n\n\t\t// 1. Container TARGET — flat label sweep via the kept removeManaged*\n\t\t// family. No dep ordering for label scope.\n\t\tif (spec.target === 'absent') {\n\t\t\tconst labelMatch: Partial<ContainerLabelTuple> = {\n\t\t\t\tapp: tuple.app,\n\t\t\t\tstack: tuple.stack,\n\t\t\t\t...(tuple.plugin === undefined ? {} : { plugin: tuple.plugin }),\n\t\t\t\t...(tuple.role === undefined ? {} : { role: tuple.role }),\n\t\t\t};\n\t\t\tyield* deps.runtime\n\t\t\t\t.removeManagedContainers(labelMatch)\n\t\t\t\t.pipe(Effect.catch(deps.onContainersError ?? ((cause) => Effect.die(cause))));\n\t\t\tyield* deps.runtime\n\t\t\t\t.removeManagedNetworks(labelMatch)\n\t\t\t\t.pipe(Effect.catch(deps.onNetworksError ?? ((cause) => Effect.die(cause))));\n\t\t\tyield* deps.runtime\n\t\t\t\t.removeManagedVolumes(labelMatch)\n\t\t\t\t.pipe(Effect.catch(deps.onVolumesError ?? ((cause) => Effect.die(cause))));\n\t\t}\n\n\t\t// 2. fsPlan — file-tree + image ops. Threads the runtime +\n\t\t// (role-narrowed) label filter so the `reap-images` op can sweep.\n\t\tif (spec.fsPlan === undefined) {\n\t\t\treturn { inspected: 0, reapedIds: [], imagesSwept: 0 } satisfies FsPlanResult;\n\t\t}\n\t\treturn yield* executeFsPlan(spec.fsPlan, {\n\t\t\truntime: deps.runtime,\n\t\t\timageLabelFilter: tuple,\n\t\t});\n\t});\n"],"mappings":";;;;;;;;;;;;;AAwEA,MAAa,kBACZ,MACA,SAEA,OAAO,IAAI,aAAa;CACvB,IAAI,KAAK,MAAM,SAAS,SAIvB,OAAO,OAAO,OAAO,IACpB,kGACD;CAED,MAAM,QAAQ,KAAK,MAAM;CAIzB,IAAI,KAAK,WAAW,UAAU;EAC7B,MAAM,aAA2C;GAChD,KAAK,MAAM;GACX,OAAO,MAAM;GACb,GAAI,MAAM,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,MAAM,OAAO;GAC7D,GAAI,MAAM,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,MAAM,KAAK;EACxD;EACA,OAAO,KAAK,QACV,wBAAwB,UAAU,CAAC,CACnC,KAAK,OAAO,MAAM,KAAK,uBAAuB,UAAU,OAAO,IAAI,KAAK,EAAE,CAAC;EAC7E,OAAO,KAAK,QACV,sBAAsB,UAAU,CAAC,CACjC,KAAK,OAAO,MAAM,KAAK,qBAAqB,UAAU,OAAO,IAAI,KAAK,EAAE,CAAC;EAC3E,OAAO,KAAK,QACV,qBAAqB,UAAU,CAAC,CAChC,KAAK,OAAO,MAAM,KAAK,oBAAoB,UAAU,OAAO,IAAI,KAAK,EAAE,CAAC;CAC3E;CAIA,IAAI,KAAK,WAAW,KAAA,GACnB,OAAO;EAAE,WAAW;EAAG,WAAW,CAAC;EAAG,aAAa;CAAE;CAEtD,OAAO,OAAO,cAAc,KAAK,QAAQ;EACxC,SAAS,KAAK;EACd,kBAAkB;CACnB,CAAC;AACF,CAAC"}
@@ -0,0 +1,15 @@
1
+ //#region src/substrate/runtime/reconcile/spec.ts
2
+ /** A flat label scope over an out-of-supervisor surface (docker resources
3
+ * by `{app, stack[, plugin, role]}`). The label flows (wipe / prune)
4
+ * are wired through `reconcileLabel`. */
5
+ const labelScope = (tuple) => ({
6
+ kind: "label",
7
+ tuple
8
+ });
9
+ /** Pure spec constructor. No behavior — just folds the axes into the
10
+ * closed `ReconcileSpec` shape. Generic on the fs-plan error tag `E`. */
11
+ const reconcileSpec = (spec) => spec;
12
+ //#endregion
13
+ export { labelScope, reconcileSpec };
14
+
15
+ //# sourceMappingURL=spec.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.mjs","names":[],"sources":["../../../../src/substrate/runtime/reconcile/spec.ts"],"sourcesContent":["// Reconcile seam contract — pure types + constructors, zero behavior.\n//\n// One `reconcile(spec)` model unifies the lifecycle flows (up / down /\n// restart / restore / wipe / prune / capture) over a small set of\n// orthogonal axes. This module defines ONLY the seam contract — the\n// structured spec every flow compiles down to.\n//\n// The axes:\n// - target — `running | absent` (intent only; `decideRunAction`\n// picks the concrete docker action, engine untouched).\n// - fsPlan — staged file-tree mutation vocabulary.\n// - scope — `graph-keys(subset)` (in-supervisor, dep-ordered) |\n// `label(tuple)` (out-of-supervisor, flat sweep).\n// - direction — `converge` (forward dep order) | `drain` (reverse).\n//\n// The label axis (`reconcileLabel`, `./label.ts`) consumes this spec for\n// `scope.kind === 'label'` sweeps + their fsPlan. The in-supervisor graph\n// flows do NOT route through this spec: they sequence `teardownKeys` /\n// `acquireKeys` directly over the orderings from `plan` (`./graph.ts`).\n// The `graph-keys` scope variant is the shape `plan` consumes. Slots not\n// used by a given flow are typed-but-inert there.\n\nimport type { AppName, PluginKey, StackName } from '../../brand.ts';\n\n// -----------------------------------------------------------------------------\n// Scope — where the reconcile applies\n// -----------------------------------------------------------------------------\n\n/** A flat label tuple identifying an out-of-supervisor surface (docker\n * resources by `app`/`stack`, optionally narrowed to one `plugin` or a\n * `role`). Used by the label-scope flows (wipe / prune / capture) that\n * sweep resources without a live dep-graph.\n *\n * `role` is a free `string` — it carries the docker ownership-label role\n * value (`ContainerLabelTuple.role`), which spans the plugin roles\n * (`service | task`) AND the synthetic byproduct roles (e.g.\n * `SNAPSHOT_IMAGE_ROLE = 'snapshot-image'`, which prune narrows its image\n * sweep to). It is NOT restricted to `PluginRole`. */\nexport interface ReconcileLabelTuple {\n\treadonly app: AppName;\n\treadonly stack: StackName;\n\treadonly plugin?: PluginKey;\n\treadonly role?: string;\n}\n\n/** Where a reconcile applies. `graph-keys` is the in-supervisor,\n * dep-ordered subset; `label` is the flat out-of-supervisor sweep. */\nexport type ReconcileScope =\n\t| { readonly kind: 'graph-keys'; readonly keys: ReadonlyArray<PluginKey> }\n\t| { readonly kind: 'label'; readonly tuple: ReconcileLabelTuple };\n\n// -----------------------------------------------------------------------------\n// fsPlan — the staged file-tree mutation vocabulary\n// -----------------------------------------------------------------------------\n//\n// A `ReconcileFsPlan` is an ORDERED list of `ReconcileFsOp`s the executor\n// (`./fs-plan.ts`) runs in sequence. Two op families:\n//\n// - DIRECT fs/runtime ops — `sweep-children`, `reap-empty`,\n// `reap-meta-missing`, `reap-images`. These are the ops wipe + prune\n// need; they mutate the runtime tree / docker image store directly\n// (no tree swap).\n// - SWAP-TREE ops — `swap-tree`. These run through the unchanged\n// `stageAndSwap` primitive; the build body is carried as a generic\n// `buildEffect`, and the preserve riders (`preserveFromTarget` /\n// `preserveOnPreseed`) stay PER-DIRECTION named constants.\n//\n// Each op carries the live callbacks / failers it needs (this is an\n// in-process plan, not a serialized one — like `ReconcileLabelDeps` it\n// holds live objects). The preserve-list BUILDERS are per-direction: wipe\n// supplies its wholesale `isPreservedChild` predicate here; restore's\n// per-namespace + control-file preserve list is a SEPARATE constant.\n\nimport type { Effect as EffectT, FileSystem } from 'effect';\n\nimport type { StageAndSwapError, StageAndSwapPreservedPath } from '../stage-and-swap/index.ts';\n\n/** A per-op failure mapper: turns an underlying defect into the caller's\n * phase-tagged error so routing a flow through the executor preserves\n * that flow's existing error tags (e.g. `WipePhaseError` /\n * `PrunePhaseError`). The executor never invents its own error tag. */\nexport type ReconcileFsFailer<E> = (cause: unknown) => EffectT.Effect<never, E>;\n\n/** DIRECT op — remove every direct child of `stackRoot` for which\n * `preserve(name)` is FALSE. The wipe preserve predicate\n * (`isPreservedChild`) is supplied here as a per-direction constant (no\n * wholesale-preserve collapse into cache policy). */\nexport interface SweepChildrenOp<E> {\n\treadonly op: 'sweep-children';\n\treadonly stackRoot: string;\n\t/** True when a direct child is PRESERVED (kept). */\n\treadonly preserve: (name: string) => boolean;\n\treadonly onError: ReconcileFsFailer<E>;\n}\n\n/** DIRECT op — reap `stackRoot` itself when ZERO preserved children\n * survive (so a wipe never leaks an empty `stacks/<stack>/` shell).\n * Best-effort: a racing re-created child just leaves the dir. */\nexport interface ReapEmptyOp {\n\treadonly op: 'reap-empty';\n\treadonly stackRoot: string;\n}\n\n/** DIRECT op — reap snapshot-catalog directories under `catalogDir` whose\n * `meta.json` is missing/unreadable (partial artifacts). `isMetaMissing`\n * is the per-direction classifier (prune supplies its meta read+decode).\n * Reaped ids are reported back so prune keeps its `PruneResult.reaped`. */\nexport interface ReapMetaMissingOp<E> {\n\treadonly op: 'reap-meta-missing';\n\treadonly catalogDir: string;\n\t/** Classify a catalog entry as a partial artifact (no readable meta).\n\t * May read the filesystem (prune's classifier reads + decodes\n\t * `meta.json`), so it carries the `FileSystem` requirement. */\n\treadonly isMetaMissing: (dir: string) => EffectT.Effect<boolean, E, FileSystem.FileSystem>;\n\treadonly onReaddirError: ReconcileFsFailer<E>;\n\treadonly onRemoveError: ReconcileFsFailer<E>;\n}\n\n/** DIRECT op — sweep committed byproduct images via the runtime adapter's\n * label-scoped image cleanup. Prune narrows the label tuple to\n * `role: SNAPSHOT_IMAGE_ROLE` so only snapshot byproducts are reaped —\n * never the live stack's build images. The swept count is reported back\n * so prune keeps its `PruneResult.imagesSwept`. This op is the\n * `reap-byproducts` cache-disposition's concrete mechanism. */\nexport interface ReapImagesOp<E> {\n\treadonly op: 'reap-images';\n\treadonly onError: ReconcileFsFailer<E>;\n}\n\n/** SWAP-TREE op — restore publishes its untarred artifact; capture builds\n * via tar subtrees. Publish a new `targetPath` tree by running\n * `buildEffect` (which populates the staging dir) then the UNCHANGED\n * `stageAndSwap` rename — `stageAndSwap` is NOT modified and NOT\n * reimplemented; the executor only assembles its args from this op. The\n * build's result is observed by\n * the caller through its OWN closure (e.g. restore pushes staged image\n * refs into a caller-held array), so the executor discards it and returns\n * the default `FsPlanResult` — the build value never threads back through\n * the op vocabulary.\n *\n * The preserve riders map 1:1 onto `stageAndSwap`'s args as PER-DIRECTION\n * named constants, NOT a cache-policy projection: `preserveFromTarget` is\n * restore's control-file list (the deploy cache is NOT preserved from live —\n * it rides the snapshot's host-tree, self-contained); `preserveOnPreseed`\n * is codegen's whole-tree pre-build clone. */\nexport interface SwapTreeOp<E> {\n\treadonly op: 'swap-tree';\n\treadonly targetPath: string;\n\treadonly stagingPath: string;\n\treadonly backupPath: string;\n\t/** The user effect that populates `stagingPath` (restore untars the\n\t * host-tree + loads/stages the committed image bundle). Carries the\n\t * caller's error tag `E`; its success value is observed via the\n\t * caller's own closure (see above) and discarded by the executor. */\n\treadonly buildEffect: EffectT.Effect<unknown, E, FileSystem.FileSystem>;\n\t/** Per-direction preserve rider — NOT a cache-policy projection.\n\t * Restore supplies its control-file list here as a restore-direction\n\t * constant (the deploy cache rides the snapshot's host-tree, so it is\n\t * NOT preserved from live). */\n\treadonly preserveFromTarget?: ReadonlyArray<StageAndSwapPreservedPath>;\n\t/** Per-direction preserve rider — codegen's whole-tree pre-build clone\n\t * (mtime-stable). */\n\treadonly preserveOnPreseed?: boolean;\n\t/** Caller-supplied publish lock (restore blocks command/event writers\n\t * while the stack root is momentarily absent during the rename). */\n\treadonly publishLockPath?: string;\n\t/** Maps `stageAndSwap`'s `StageAndSwapError` into the caller's error\n\t * channel `E`, mirroring the failer the DIRECT ops carry. Restore\n\t * passes an identity pass-through (it keeps `StageAndSwapError` in its\n\t * own public signature, behavior-preserving), so the executor never\n\t * invents an error tag and `executeFsPlan<E>` stays `Effect<…, E, …>`\n\t * rather than widening every label-flow's error with a swap error it\n\t * can never raise. */\n\treadonly onSwapError: (cause: StageAndSwapError) => EffectT.Effect<never, E>;\n}\n\n/** One file-tree mutation op. Every op is parameterized on the caller's\n * error tag `E`; `swap-tree`'s `buildEffect` carries `E` directly and the\n * executor surfaces `E | StageAndSwapError` from the rename. */\nexport type ReconcileFsOp<E> =\n\t| SweepChildrenOp<E>\n\t| ReapEmptyOp\n\t| ReapMetaMissingOp<E>\n\t| ReapImagesOp<E>\n\t| SwapTreeOp<E>;\n\n/** The staged file-tree mutation plan: an ordered list of ops the\n * executor runs in sequence. */\nexport interface ReconcileFsPlan<E = never> {\n\treadonly ops: ReadonlyArray<ReconcileFsOp<E>>;\n}\n\n// -----------------------------------------------------------------------------\n// The unified spec\n// -----------------------------------------------------------------------------\n\n/** The container intent. `decideRunAction` picks the concrete docker\n * action (`fresh|adopt|unpause-adopt|resume|recreate|refuse|stop`); the\n * caller only declares the desired end-state. */\nexport type ReconcileTarget = 'running' | 'absent';\n\n/** Traversal direction over the dep-graph: `converge` is forward (acquire)\n * order, `drain` is reverse (teardown) order. */\nexport type ReconcileDirection = 'converge' | 'drain';\n\n/** The structured spec the label-scope lifecycle flows (wipe / prune)\n * compile down to. The `E` parameter is the caller's fs-plan error tag\n * (e.g. `WipePhaseError` / `PrunePhaseError`). The label axis\n * (`reconcileLabel`) consumes `target` + `scope` (label) and executes\n * `fsPlan` over the label scope; `direction` records the flow's intent.\n * (The in-supervisor graph flows do NOT build a spec — they sequence\n * `teardownKeys` / `acquireKeys` directly over `plan`'s orderings.) */\nexport interface ReconcileSpec<E = never> {\n\treadonly target: ReconcileTarget;\n\treadonly fsPlan?: ReconcileFsPlan<E>;\n\treadonly scope: ReconcileScope;\n\treadonly direction: ReconcileDirection;\n}\n\n// -----------------------------------------------------------------------------\n// Pure constructors (no behavior)\n// -----------------------------------------------------------------------------\n\n/** A flat label scope over an out-of-supervisor surface (docker resources\n * by `{app, stack[, plugin, role]}`). The label flows (wipe / prune)\n * are wired through `reconcileLabel`. */\nexport const labelScope = (tuple: ReconcileLabelTuple): ReconcileScope => ({\n\tkind: 'label',\n\ttuple,\n});\n\n/** Pure spec constructor. No behavior — just folds the axes into the\n * closed `ReconcileSpec` shape. Generic on the fs-plan error tag `E`. */\nexport const reconcileSpec = <E = never>(spec: ReconcileSpec<E>): ReconcileSpec<E> => spec;\n"],"mappings":";;;;AAkOA,MAAa,cAAc,WAAgD;CAC1E,MAAM;CACN;AACD;;;AAIA,MAAa,iBAA4B,SAA6C"}
@@ -1,5 +1,4 @@
1
1
  import { Schedule } from "effect";
2
- import * as _$effect_Duration0 from "effect/Duration";
3
2
 
4
3
  //#region src/substrate/runtime/retry-policy.d.ts
5
4
  declare namespace retry_policy_d_exports {
@@ -11,7 +10,7 @@ interface ExponentialRetryOptions {
11
10
  readonly factor?: number;
12
11
  readonly jitter?: boolean;
13
12
  }
14
- declare const makeExponentialRetrySchedule: (options: ExponentialRetryOptions) => Schedule.Schedule<[_$effect_Duration0.Duration, number], unknown, never, never>;
13
+ declare const makeExponentialRetrySchedule: (options: ExponentialRetryOptions) => Schedule.Schedule<[import("effect/Duration").Duration, number], unknown, never, never>;
15
14
  declare const makeSpacedRetrySchedule: (delayMs: number, maxRetries: number) => Schedule.Schedule<[number, number], unknown, never, never>;
16
15
  /** Wall-clock-bounded fixed-interval poll. Selects "poll every
17
16
  * `intervalMs` until total elapsed ≥ `timeoutMs`". Returns a
@@ -21,7 +20,7 @@ declare const makeSpacedRetrySchedule: (delayMs: number, maxRetries: number) =>
21
20
  *
22
21
  * Use for balance polls, post-funding settlement waits, and any
23
22
  * similar wall-clock-bounded reconciliation loops. */
24
- declare const makeBoundedSpacedSchedule: (intervalMs: number, timeoutMs: number) => Schedule.Schedule<[number, _$effect_Duration0.Duration], unknown, never, never>;
23
+ declare const makeBoundedSpacedSchedule: (intervalMs: number, timeoutMs: number) => Schedule.Schedule<[number, import("effect/Duration").Duration], unknown, never, never>;
25
24
  /** Balance-poll profile shared by funding settlement waits. The
26
25
  * interval is short enough to keep wall-clock overshoot small;
27
26
  * the timeout is the canonical funding settlement budget. */
@@ -1 +1 @@
1
- {"version":3,"file":"retry-policy.mjs","names":[],"sources":["../../../src/substrate/runtime/retry-policy.ts"],"sourcesContent":["import { Schedule } from 'effect';\n\nexport interface ExponentialRetryOptions {\n\treadonly initialDelayMs: number;\n\treadonly maxRetries: number;\n\treadonly factor?: number;\n\treadonly jitter?: boolean;\n}\n\nexport const makeExponentialRetrySchedule = (options: ExponentialRetryOptions) => {\n\tconst schedule = Schedule.exponential(`${options.initialDelayMs} millis`, options.factor ?? 2);\n\tconst jittered = options.jitter === false ? schedule : schedule.pipe(Schedule.jittered);\n\treturn jittered.pipe(Schedule.both(Schedule.recurs(options.maxRetries)));\n};\n\nexport const makeSpacedRetrySchedule = (delayMs: number, maxRetries: number) =>\n\tSchedule.spaced(`${delayMs} millis`).pipe(Schedule.both(Schedule.recurs(maxRetries)));\n\n/** Wall-clock-bounded fixed-interval poll. Selects \"poll every\n * `intervalMs` until total elapsed ≥ `timeoutMs`\". Returns a\n * schedule that callers compose with `Effect.repeat` /\n * `Effect.retry` for poll-until-ready loops where the exit\n * condition is owned by the caller body.\n *\n * Use for balance polls, post-funding settlement waits, and any\n * similar wall-clock-bounded reconciliation loops. */\nexport const makeBoundedSpacedSchedule = (intervalMs: number, timeoutMs: number) =>\n\tSchedule.both(Schedule.spaced(`${intervalMs} millis`), Schedule.during(`${timeoutMs} millis`));\n\n/** Balance-poll profile shared by funding settlement waits. The\n * interval is short enough to keep wall-clock overshoot small;\n * the timeout is the canonical funding settlement budget. */\nexport const BALANCE_POLL_PROFILE = {\n\tintervalMs: 250,\n\ttimeoutMs: 30_000,\n} as const;\n\n/** Single-call deadline for the per-poll balance-reader. Faucet\n * RPC calls occasionally hang; the deadline keeps a stalled\n * read from starving the surrounding poll loop. */\nexport const FUNDING_BALANCE_READ_TIMEOUT_MS = 5_000;\n\n/** Docker Desktop bind-mount visibility race. The host directory\n * is just-created but the Docker VFS hasn't surfaced it yet;\n * attempts cap at 5s total (10 × 500ms). */\nexport const DEPLOY_BIND_SOURCE_RETRY_PROFILE = {\n\tattempts: 10,\n\tdelayMs: 500,\n} as const;\n\n/** HTTP faucet POST retry profile. The faucet's TCP socket binds\n * before the validator can transfer coins (a 60s cold-warm\n * window). `attempts × backoff` saturates well inside the\n * surrounding wall-clock budget. */\nexport const FAUCET_HTTP_RETRY_PROFILE = {\n\tmaxAttempts: 15,\n\tinitialDelayMs: 500,\n\tbackoffFactor: 1.5,\n\twallClockBudgetMs: 90_000,\n\tperRequestDeadlineMs: 5_000,\n} as const;\n\n/** Sui \"stale object version\" retry profile — used when a Move call\n * references an object that's been mutated by a concurrent transaction\n * (deepbook pool seeding hits this when the registry / pool object is\n * re-versioned mid-flight). The retry rebuilds the transaction with\n * fresh refs each attempt; 20 × 500ms keeps the worst case under 10s. */\nexport const STALE_OBJECT_VERSION_RETRY_PROFILE = {\n\tattempts: 20,\n\tdelayMs: 500,\n} as const;\n\n/** Docker `network rm` retry profile — used by best-effort network\n * prune after our own endpoints are force-disconnected. Foreign\n * holders and stale bridge-driver endpoints may keep the network\n * busy for a short window; 6 × 250ms saturates well inside the\n * prune wall-clock budget while letting transient races resolve.\n * Callers may override per-invocation via\n * `DevstackNetworkRemovalOptions`. */\nexport const NETWORK_REMOVE_RETRY_PROFILE = {\n\tattempts: 6,\n\tdelayMs: 250,\n} as const;\n"],"mappings":";;;;;;;;;;;;;;AASA,MAAa,gCAAgC,YAAqC;CACjF,MAAM,WAAW,SAAS,YAAY,GAAG,QAAQ,eAAe,UAAU,QAAQ,UAAU,EAAE;AAE9F,SADiB,QAAQ,WAAW,QAAQ,WAAW,SAAS,KAAK,SAAS,SAAS,EACvE,KAAK,SAAS,KAAK,SAAS,OAAO,QAAQ,WAAW,CAAC,CAAC;;AAGzE,MAAa,2BAA2B,SAAiB,eACxD,SAAS,OAAO,GAAG,QAAQ,SAAS,CAAC,KAAK,SAAS,KAAK,SAAS,OAAO,WAAW,CAAC,CAAC;;;;;;;;;AAUtF,MAAa,6BAA6B,YAAoB,cAC7D,SAAS,KAAK,SAAS,OAAO,GAAG,WAAW,SAAS,EAAE,SAAS,OAAO,GAAG,UAAU,SAAS,CAAC;;;;AAK/F,MAAa,uBAAuB;CACnC,YAAY;CACZ,WAAW;CACX;;;;AAKD,MAAa,kCAAkC;;;;AAK/C,MAAa,mCAAmC;CAC/C,UAAU;CACV,SAAS;CACT;;;;;AAMD,MAAa,4BAA4B;CACxC,aAAa;CACb,gBAAgB;CAChB,eAAe;CACf,mBAAmB;CACnB,sBAAsB;CACtB;;;;;;AAOD,MAAa,qCAAqC;CACjD,UAAU;CACV,SAAS;CACT;;;;;;;;AASD,MAAa,+BAA+B;CAC3C,UAAU;CACV,SAAS;CACT"}
1
+ {"version":3,"file":"retry-policy.mjs","names":[],"sources":["../../../src/substrate/runtime/retry-policy.ts"],"sourcesContent":["import { Schedule } from 'effect';\n\nexport interface ExponentialRetryOptions {\n\treadonly initialDelayMs: number;\n\treadonly maxRetries: number;\n\treadonly factor?: number;\n\treadonly jitter?: boolean;\n}\n\nexport const makeExponentialRetrySchedule = (options: ExponentialRetryOptions) => {\n\tconst schedule = Schedule.exponential(`${options.initialDelayMs} millis`, options.factor ?? 2);\n\tconst jittered = options.jitter === false ? schedule : schedule.pipe(Schedule.jittered);\n\treturn jittered.pipe(Schedule.both(Schedule.recurs(options.maxRetries)));\n};\n\nexport const makeSpacedRetrySchedule = (delayMs: number, maxRetries: number) =>\n\tSchedule.spaced(`${delayMs} millis`).pipe(Schedule.both(Schedule.recurs(maxRetries)));\n\n/** Wall-clock-bounded fixed-interval poll. Selects \"poll every\n * `intervalMs` until total elapsed ≥ `timeoutMs`\". Returns a\n * schedule that callers compose with `Effect.repeat` /\n * `Effect.retry` for poll-until-ready loops where the exit\n * condition is owned by the caller body.\n *\n * Use for balance polls, post-funding settlement waits, and any\n * similar wall-clock-bounded reconciliation loops. */\nexport const makeBoundedSpacedSchedule = (intervalMs: number, timeoutMs: number) =>\n\tSchedule.both(Schedule.spaced(`${intervalMs} millis`), Schedule.during(`${timeoutMs} millis`));\n\n/** Balance-poll profile shared by funding settlement waits. The\n * interval is short enough to keep wall-clock overshoot small;\n * the timeout is the canonical funding settlement budget. */\nexport const BALANCE_POLL_PROFILE = {\n\tintervalMs: 250,\n\ttimeoutMs: 30_000,\n} as const;\n\n/** Single-call deadline for the per-poll balance-reader. Faucet\n * RPC calls occasionally hang; the deadline keeps a stalled\n * read from starving the surrounding poll loop. */\nexport const FUNDING_BALANCE_READ_TIMEOUT_MS = 5_000;\n\n/** Docker Desktop bind-mount visibility race. The host directory\n * is just-created but the Docker VFS hasn't surfaced it yet;\n * attempts cap at 5s total (10 × 500ms). */\nexport const DEPLOY_BIND_SOURCE_RETRY_PROFILE = {\n\tattempts: 10,\n\tdelayMs: 500,\n} as const;\n\n/** HTTP faucet POST retry profile. The faucet's TCP socket binds\n * before the validator can transfer coins (a 60s cold-warm\n * window). `attempts × backoff` saturates well inside the\n * surrounding wall-clock budget. */\nexport const FAUCET_HTTP_RETRY_PROFILE = {\n\tmaxAttempts: 15,\n\tinitialDelayMs: 500,\n\tbackoffFactor: 1.5,\n\twallClockBudgetMs: 90_000,\n\tperRequestDeadlineMs: 5_000,\n} as const;\n\n/** Sui \"stale object version\" retry profile — used when a Move call\n * references an object that's been mutated by a concurrent transaction\n * (deepbook pool seeding hits this when the registry / pool object is\n * re-versioned mid-flight). The retry rebuilds the transaction with\n * fresh refs each attempt; 20 × 500ms keeps the worst case under 10s. */\nexport const STALE_OBJECT_VERSION_RETRY_PROFILE = {\n\tattempts: 20,\n\tdelayMs: 500,\n} as const;\n\n/** Docker `network rm` retry profile — used by best-effort network\n * prune after our own endpoints are force-disconnected. Foreign\n * holders and stale bridge-driver endpoints may keep the network\n * busy for a short window; 6 × 250ms saturates well inside the\n * prune wall-clock budget while letting transient races resolve.\n * Callers may override per-invocation via\n * `DevstackNetworkRemovalOptions`. */\nexport const NETWORK_REMOVE_RETRY_PROFILE = {\n\tattempts: 6,\n\tdelayMs: 250,\n} as const;\n"],"mappings":";;;;;;;;;;;;;;AASA,MAAa,gCAAgC,YAAqC;CACjF,MAAM,WAAW,SAAS,YAAY,GAAG,QAAQ,eAAe,UAAU,QAAQ,UAAU,CAAC;CAE7F,QADiB,QAAQ,WAAW,QAAQ,WAAW,SAAS,KAAK,SAAS,QAAQ,EAAA,CACtE,KAAK,SAAS,KAAK,SAAS,OAAO,QAAQ,UAAU,CAAC,CAAC;AACxE;AAEA,MAAa,2BAA2B,SAAiB,eACxD,SAAS,OAAO,GAAG,QAAQ,QAAQ,CAAC,CAAC,KAAK,SAAS,KAAK,SAAS,OAAO,UAAU,CAAC,CAAC;;;;;;;;;AAUrF,MAAa,6BAA6B,YAAoB,cAC7D,SAAS,KAAK,SAAS,OAAO,GAAG,WAAW,QAAQ,GAAG,SAAS,OAAO,GAAG,UAAU,QAAQ,CAAC;;;;AAK9F,MAAa,uBAAuB;CACnC,YAAY;CACZ,WAAW;AACZ;;;;AAKA,MAAa,kCAAkC;;;;AAK/C,MAAa,mCAAmC;CAC/C,UAAU;CACV,SAAS;AACV;;;;;AAMA,MAAa,4BAA4B;CACxC,aAAa;CACb,gBAAgB;CAChB,eAAe;CACf,mBAAmB;CACnB,sBAAsB;AACvB;;;;;;AAOA,MAAa,qCAAqC;CACjD,UAAU;CACV,SAAS;AACV;;;;;;;;AASA,MAAa,+BAA+B;CAC3C,UAAU;CACV,SAAS;AACV"}
@@ -1 +1 @@
1
- {"version":3,"file":"routed-url.mjs","names":[],"sources":["../../../src/substrate/runtime/routed-url.ts"],"sourcesContent":["// Substrate-blind URL composer + routed-hostname accessor.\n//\n// Consumed by L2 plugins (wallet, sui modes, future authors) so plugin\n// code does not reach across the L2→L3 boundary into\n// `orchestrators/router/hostname.ts`. The pure URL composition and\n// hostname-minting logic depends only on `substrate/identity.ts` and is\n// genuinely substrate-level; lifting it here removes a long-standing\n// boundary violation flagged in ARCHITECTURE.md (L2 row: \"NEVER imports\n// from … L3 orchestrators\").\n//\n// Architecture invariants honoured (mirrored from the original\n// orchestrators/router/hostname.ts authoritative comment):\n//\n// #7 — Distinct `(app, stack, role)` triples MUST produce distinct\n// hostnames. Default stack ('main') omits the stack segment;\n// every other stack includes it.\n// #8 — Hostname service labels fold dots to a label-safe separator\n// before validation.\n// #13 — User-influenceable strings are validated against a\n// conservative character set before render.\n//\n// The L3 router orchestrator imports from here and adapts the\n// substrate-blind `HostnameValidationError` into its richer\n// `RouterError` union for orchestrator-internal callers (file-provider,\n// resolveRoute) that compose with `UnknownEntrypoint`, `RouteCollision`,\n// etc. Substrate stays name-blind: this module mentions no plugin or\n// orchestrator identifiers.\n\nimport { Effect, Schema } from 'effect';\n\nimport type { Identity } from '../identity.ts';\n\n/** The conventional \"default\" stack name. Hostnames for this stack omit\n * 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. Used for app, role, and (post-fold)\n// stack labels.\nconst LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;\n// 253 chars is the RFC-1035 full-name max. We stay well below.\nconst MAX_HOSTNAME_LEN = 200;\n\n/** A user-influenceable hostname-segment string did not pass\n * validation. Substrate-blind shape mirroring the orchestrator's\n * `RouterValidationError`; L3 orchestrators wrap this into their\n * richer error union for internal composition. */\nexport class HostnameValidationError extends Schema.TaggedErrorClass<HostnameValidationError>()(\n\t'HostnameValidationError',\n\t{\n\t\tfield: Schema.Literals(['hostname', 'dispatchId', 'entrypointName', 'upstreamUrl']),\n\t\tvalue: Schema.String,\n\t\tdetail: Schema.String,\n\t},\n) {}\n\n/** Validate a single label-shaped string. */\nconst validateLabel = (\n\tfield: HostnameValidationError['field'],\n\tvalue: string,\n): Effect.Effect<string, HostnameValidationError> => {\n\tif (!LABEL.test(value)) {\n\t\treturn Effect.fail(\n\t\t\tnew HostnameValidationError({\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/** Mint the per-Routable hostname from `(identity, role)`.\n *\n * Default stack: `<role>.<app>.localhost`\n * Other stacks: `<role>.<stack>.<app>.localhost`\n *\n * Substrate-blind: the substrate knows about identities (app, stack)\n * and arbitrary role strings — it does not know about specific plugins\n * or services. L2 plugins call this directly; L3 orchestrators wrap\n * the failure case into their richer error union. */\nexport const routedHostname = (\n\tidentity: Identity,\n\trole: string,\n): Effect.Effect<string, HostnameValidationError> =>\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\t// The `StackName` brand does NOT character-validate at construction\n\t\t// (`brand.ts` / `api/inference-network.ts:resolveStackName`), so we\n\t\t// re-validate here for parity with `app`. A malformed stack name\n\t\t// (uppercase / underscores / out-of-RFC-1035 chars) must fail at\n\t\t// hostname assembly, not silently pass through.\n\t\tconst stackSafe =\n\t\t\tidentity.stack === DEFAULT_STACK\n\t\t\t\t? null\n\t\t\t\t: yield* validateLabel('hostname', identity.stack.toLowerCase());\n\t\tconst assembled =\n\t\t\tstackSafe === null\n\t\t\t\t? `${roleSafe}.${app}.localhost`\n\t\t\t\t: `${roleSafe}.${stackSafe}.${app}.localhost`;\n\t\tif (assembled.length > MAX_HOSTNAME_LEN) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew HostnameValidationError({\n\t\t\t\t\tfield: 'hostname',\n\t\t\t\t\tvalue: assembled,\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\treturn assembled;\n\t});\n\n/** Pure URL composer.\n *\n * - `http` / `h2c` → `http://…` (h2c is HTTP/2 cleartext; Traefik\n * handles upstream selection internally, the URL scheme stays\n * `http`).\n * - `https` → `https://…`.\n * - `tcp` → `tcp://…` — used by the file-provider renderer\n * to 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://`).\n *\n * The port is always included in the rendered URL (no well-known-port\n * omission); the file-provider renderer parses the URL back into\n * `(assembled, port)` for the TCP `address:` field and would reject a\n * port-less form. `path` is appended verbatim when supplied (it MUST\n * include the leading slash). */\nexport const renderUrl = (parts: {\n\treadonly protocol: 'http' | 'https' | 'h2c' | 'tcp';\n\treadonly hostname: string;\n\treadonly port: number;\n\treadonly path?: string;\n}): string => {\n\tconst scheme = parts.protocol === 'https' ? 'https' : parts.protocol === 'tcp' ? 'tcp' : 'http';\n\tconst path = parts.path ?? '';\n\treturn `${scheme}://${parts.hostname}:${parts.port}${path}`;\n};\n"],"mappings":";;;;AAkCA,MAAa,gBAAgB;AAK7B,MAAM,QAAQ;AAEd,MAAM,mBAAmB;;;;;AAMzB,IAAa,0BAAb,cAA6C,OAAO,kBAA2C,CAC9F,2BACA;CACC,OAAO,OAAO,SAAS;EAAC;EAAY;EAAc;EAAkB;EAAc,CAAC;CACnF,OAAO,OAAO;CACd,QAAQ,OAAO;CACf,CACD,CAAC;;AAGF,MAAM,iBACL,OACA,UACoD;AACpD,KAAI,CAAC,MAAM,KAAK,MAAM,CACrB,QAAO,OAAO,KACb,IAAI,wBAAwB;EAC3B;EACA;EACA,QAAQ;EACR,CAAC,CACF;AAEF,QAAO,OAAO,QAAQ,MAAM;;;;AAK7B,MAAa,2BAA2B,QACvC,IAAI,aAAa,CAAC,QAAQ,OAAO,IAAI;;;;;;;;;;AAWtC,MAAa,kBACZ,UACA,SAEA,OAAO,IAAI,aAAa;CACvB,MAAM,MAAM,OAAO,cAAc,YAAY,SAAS,IAAI;CAE1D,MAAM,WAAW,OAAO,cAAc,YADvB,wBAAwB,KACiB,CAAC;CAMzD,MAAM,YACL,SAAS,UAAA,SACN,OACA,OAAO,cAAc,YAAY,SAAS,MAAM,aAAa,CAAC;CAClE,MAAM,YACL,cAAc,OACX,GAAG,SAAS,GAAG,IAAI,cACnB,GAAG,SAAS,GAAG,UAAU,GAAG,IAAI;AACpC,KAAI,UAAU,SAAS,iBACtB,QAAO,OAAO,OAAO,KACpB,IAAI,wBAAwB;EAC3B,OAAO;EACP,OAAO;EACP,QAAQ,WAAW,iBAAiB;EACpC,CAAC,CACF;AAEF,QAAO;EACN;;;;;;;;;;;;;;;;;AAkBH,MAAa,aAAa,UAKZ;CACb,MAAM,SAAS,MAAM,aAAa,UAAU,UAAU,MAAM,aAAa,QAAQ,QAAQ;CACzF,MAAM,OAAO,MAAM,QAAQ;AAC3B,QAAO,GAAG,OAAO,KAAK,MAAM,SAAS,GAAG,MAAM,OAAO"}
1
+ {"version":3,"file":"routed-url.mjs","names":[],"sources":["../../../src/substrate/runtime/routed-url.ts"],"sourcesContent":["// Substrate-blind URL composer + routed-hostname accessor.\n//\n// Consumed by L2 plugins (wallet, sui modes, future authors) so plugin\n// code does not reach across the L2→L3 boundary into\n// `orchestrators/router/hostname.ts`. The pure URL composition and\n// hostname-minting logic depends only on `substrate/identity.ts` and is\n// genuinely substrate-level; lifting it here removes a long-standing\n// boundary violation flagged in ARCHITECTURE.md (L2 row: \"NEVER imports\n// from … L3 orchestrators\").\n//\n// Architecture invariants honoured (mirrored from the original\n// orchestrators/router/hostname.ts authoritative comment):\n//\n// #7 — Distinct `(app, stack, role)` triples MUST produce distinct\n// hostnames. Default stack ('main') omits the stack segment;\n// every other stack includes it.\n// #8 — Hostname service labels fold dots to a label-safe separator\n// before validation.\n// #13 — User-influenceable strings are validated against a\n// conservative character set before render.\n//\n// The L3 router orchestrator imports from here and adapts the\n// substrate-blind `HostnameValidationError` into its richer\n// `RouterError` union for orchestrator-internal callers (file-provider,\n// resolveRoute) that compose with `UnknownEntrypoint`, `RouteCollision`,\n// etc. Substrate stays name-blind: this module mentions no plugin or\n// orchestrator identifiers.\n\nimport { Effect, Schema } from 'effect';\n\nimport type { Identity } from '../identity.ts';\n\n/** The conventional \"default\" stack name. Hostnames for this stack omit\n * 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. Used for app, role, and (post-fold)\n// stack labels.\nconst LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;\n// 253 chars is the RFC-1035 full-name max. We stay well below.\nconst MAX_HOSTNAME_LEN = 200;\n\n/** A user-influenceable hostname-segment string did not pass\n * validation. Substrate-blind shape mirroring the orchestrator's\n * `RouterValidationError`; L3 orchestrators wrap this into their\n * richer error union for internal composition. */\nexport class HostnameValidationError extends Schema.TaggedErrorClass<HostnameValidationError>()(\n\t'HostnameValidationError',\n\t{\n\t\tfield: Schema.Literals(['hostname', 'dispatchId', 'entrypointName', 'upstreamUrl']),\n\t\tvalue: Schema.String,\n\t\tdetail: Schema.String,\n\t},\n) {}\n\n/** Validate a single label-shaped string. */\nconst validateLabel = (\n\tfield: HostnameValidationError['field'],\n\tvalue: string,\n): Effect.Effect<string, HostnameValidationError> => {\n\tif (!LABEL.test(value)) {\n\t\treturn Effect.fail(\n\t\t\tnew HostnameValidationError({\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/** Mint the per-Routable hostname from `(identity, role)`.\n *\n * Default stack: `<role>.<app>.localhost`\n * Other stacks: `<role>.<stack>.<app>.localhost`\n *\n * Substrate-blind: the substrate knows about identities (app, stack)\n * and arbitrary role strings — it does not know about specific plugins\n * or services. L2 plugins call this directly; L3 orchestrators wrap\n * the failure case into their richer error union. */\nexport const routedHostname = (\n\tidentity: Identity,\n\trole: string,\n): Effect.Effect<string, HostnameValidationError> =>\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\t// The `StackName` brand does NOT character-validate at construction\n\t\t// (`brand.ts` / `api/inference-network.ts:resolveStackName`), so we\n\t\t// re-validate here for parity with `app`. A malformed stack name\n\t\t// (uppercase / underscores / out-of-RFC-1035 chars) must fail at\n\t\t// hostname assembly, not silently pass through.\n\t\tconst stackSafe =\n\t\t\tidentity.stack === DEFAULT_STACK\n\t\t\t\t? null\n\t\t\t\t: yield* validateLabel('hostname', identity.stack.toLowerCase());\n\t\tconst assembled =\n\t\t\tstackSafe === null\n\t\t\t\t? `${roleSafe}.${app}.localhost`\n\t\t\t\t: `${roleSafe}.${stackSafe}.${app}.localhost`;\n\t\tif (assembled.length > MAX_HOSTNAME_LEN) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew HostnameValidationError({\n\t\t\t\t\tfield: 'hostname',\n\t\t\t\t\tvalue: assembled,\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\treturn assembled;\n\t});\n\n/** Pure URL composer.\n *\n * - `http` / `h2c` → `http://…` (h2c is HTTP/2 cleartext; Traefik\n * handles upstream selection internally, the URL scheme stays\n * `http`).\n * - `https` → `https://…`.\n * - `tcp` → `tcp://…` — used by the file-provider renderer\n * to 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://`).\n *\n * The port is always included in the rendered URL (no well-known-port\n * omission); the file-provider renderer parses the URL back into\n * `(assembled, port)` for the TCP `address:` field and would reject a\n * port-less form. `path` is appended verbatim when supplied (it MUST\n * include the leading slash). */\nexport const renderUrl = (parts: {\n\treadonly protocol: 'http' | 'https' | 'h2c' | 'tcp';\n\treadonly hostname: string;\n\treadonly port: number;\n\treadonly path?: string;\n}): string => {\n\tconst scheme = parts.protocol === 'https' ? 'https' : parts.protocol === 'tcp' ? 'tcp' : 'http';\n\tconst path = parts.path ?? '';\n\treturn `${scheme}://${parts.hostname}:${parts.port}${path}`;\n};\n"],"mappings":";;;;AAkCA,MAAa,gBAAgB;AAK7B,MAAM,QAAQ;AAEd,MAAM,mBAAmB;;;;;AAMzB,IAAa,0BAAb,cAA6C,OAAO,iBAA0C,CAAC,CAC9F,2BACA;CACC,OAAO,OAAO,SAAS;EAAC;EAAY;EAAc;EAAkB;CAAa,CAAC;CAClF,OAAO,OAAO;CACd,QAAQ,OAAO;AAChB,CACD,CAAC,CAAC,CAAC;;AAGH,MAAM,iBACL,OACA,UACoD;CACpD,IAAI,CAAC,MAAM,KAAK,KAAK,GACpB,OAAO,OAAO,KACb,IAAI,wBAAwB;EAC3B;EACA;EACA,QAAQ;CACT,CAAC,CACF;CAED,OAAO,OAAO,QAAQ,KAAK;AAC5B;;;AAIA,MAAa,2BAA2B,QACvC,IAAI,YAAY,CAAC,CAAC,QAAQ,OAAO,GAAG;;;;;;;;;;AAWrC,MAAa,kBACZ,UACA,SAEA,OAAO,IAAI,aAAa;CACvB,MAAM,MAAM,OAAO,cAAc,YAAY,SAAS,GAAG;CAEzD,MAAM,WAAW,OAAO,cAAc,YADvB,wBAAwB,IACgB,CAAC;CAMxD,MAAM,YACL,SAAS,UAAA,SACN,OACA,OAAO,cAAc,YAAY,SAAS,MAAM,YAAY,CAAC;CACjE,MAAM,YACL,cAAc,OACX,GAAG,SAAS,GAAG,IAAI,cACnB,GAAG,SAAS,GAAG,UAAU,GAAG,IAAI;CACpC,IAAI,UAAU,SAAS,kBACtB,OAAO,OAAO,OAAO,KACpB,IAAI,wBAAwB;EAC3B,OAAO;EACP,OAAO;EACP,QAAQ,WAAW,iBAAiB;CACrC,CAAC,CACF;CAED,OAAO;AACR,CAAC;;;;;;;;;;;;;;;;;AAkBF,MAAa,aAAa,UAKZ;CACb,MAAM,SAAS,MAAM,aAAa,UAAU,UAAU,MAAM,aAAa,QAAQ,QAAQ;CACzF,MAAM,OAAO,MAAM,QAAQ;CAC3B,OAAO,GAAG,OAAO,KAAK,MAAM,SAAS,GAAG,MAAM,OAAO;AACtD"}