@mysten-incubation/devstack 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (838) hide show
  1. package/README.md +8 -4
  2. package/dashboard-ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  3. package/dashboard-ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  4. package/dashboard-ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  5. package/dashboard-ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  6. package/dashboard-ui/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
  7. package/dashboard-ui/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
  8. package/dashboard-ui/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
  9. package/dashboard-ui/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
  10. package/dashboard-ui/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
  11. package/dashboard-ui/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
  12. package/dashboard-ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  13. package/dashboard-ui/assets/index-Bmi1UtAg.js +1356 -0
  14. package/dashboard-ui/assets/index-D5EShVt4.js +3 -0
  15. package/dashboard-ui/assets/index-Deml9drg.css +1 -0
  16. package/dashboard-ui/index.html +13 -0
  17. package/dist/api/define-capabilities.d.mts +14 -3
  18. package/dist/api/define-capabilities.mjs +18 -1
  19. package/dist/api/define-capabilities.mjs.map +1 -1
  20. package/dist/api/define-devstack.d.mts +1 -1
  21. package/dist/api/define-devstack.mjs +6 -26
  22. package/dist/api/define-devstack.mjs.map +1 -1
  23. package/dist/api/define-plugin.d.mts +1 -0
  24. package/dist/api/define-plugin.mjs +2 -0
  25. package/dist/api/inference-network.d.mts +132 -0
  26. package/dist/api/inference-network.mjs +105 -16
  27. package/dist/api/inference-network.mjs.map +1 -1
  28. package/dist/api/plugin-errors.d.mts +7 -0
  29. package/dist/api/run-stack.d.mts +20 -9
  30. package/dist/api/run-stack.mjs +56 -29
  31. package/dist/api/run-stack.mjs.map +1 -1
  32. package/dist/build-integrations/playwright/config.d.mts +6 -4
  33. package/dist/build-integrations/playwright/config.mjs +5 -3
  34. package/dist/build-integrations/playwright/config.mjs.map +1 -1
  35. package/dist/build-integrations/playwright/errors.d.mts +1 -1
  36. package/dist/build-integrations/playwright/global-setup.d.mts +43 -17
  37. package/dist/build-integrations/playwright/global-setup.mjs +250 -13
  38. package/dist/build-integrations/playwright/global-setup.mjs.map +1 -1
  39. package/dist/build-integrations/playwright/index.d.mts +5 -3
  40. package/dist/build-integrations/playwright/index.mjs +4 -2
  41. package/dist/build-integrations/playwright/stack-context.d.mts +0 -1
  42. package/dist/build-integrations/playwright/stack-context.mjs +24 -63
  43. package/dist/build-integrations/playwright/stack-context.mjs.map +1 -1
  44. package/dist/build-integrations/playwright/wallet-context.d.mts +7 -10
  45. package/dist/build-integrations/playwright/wallet-context.mjs +8 -13
  46. package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
  47. package/dist/build-integrations/runtime/cold-start-url.d.mts +5 -0
  48. package/dist/build-integrations/runtime/cold-start-url.mjs +4 -3
  49. package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -1
  50. package/dist/build-integrations/runtime/conventional-routes.d.mts +51 -0
  51. package/dist/build-integrations/runtime/conventional-routes.mjs +92 -0
  52. package/dist/build-integrations/runtime/conventional-routes.mjs.map +1 -0
  53. package/dist/build-integrations/runtime/discover.d.mts +38 -30
  54. package/dist/build-integrations/runtime/discover.mjs +73 -70
  55. package/dist/build-integrations/runtime/discover.mjs.map +1 -1
  56. package/dist/build-integrations/runtime/endpoint-registry.d.mts +3 -1
  57. package/dist/build-integrations/runtime/endpoint-registry.mjs +3 -1
  58. package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -1
  59. package/dist/build-integrations/runtime/index.d.mts +7 -2
  60. package/dist/build-integrations/runtime/index.mjs +9 -2
  61. package/dist/build-integrations/runtime/manifest-types.d.mts +2 -0
  62. package/dist/build-integrations/runtime/manifest-types.mjs +3 -0
  63. package/dist/build-integrations/runtime/playwright-stack-context-slot.d.mts +29 -0
  64. package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs +10 -0
  65. package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs.map +1 -0
  66. package/dist/build-integrations/runtime/read-stack-context.d.mts +1 -1
  67. package/dist/build-integrations/runtime/read-stack-context.mjs +10 -4
  68. package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -1
  69. package/dist/build-integrations/runtime/resolve-discovery-env.d.mts +48 -0
  70. package/dist/build-integrations/runtime/resolve-discovery-env.mjs +42 -0
  71. package/dist/build-integrations/runtime/resolve-discovery-env.mjs.map +1 -0
  72. package/dist/build-integrations/runtime/stack-context.d.mts +1 -1
  73. package/dist/build-integrations/runtime/wallet-paths.mjs +2 -0
  74. package/dist/build-integrations/vite/index.d.mts +51 -0
  75. package/dist/build-integrations/vite/index.mjs +73 -0
  76. package/dist/build-integrations/vite/index.mjs.map +1 -0
  77. package/dist/build-integrations/vitest/env.d.mts +4 -3
  78. package/dist/build-integrations/vitest/env.mjs +6 -4
  79. package/dist/build-integrations/vitest/env.mjs.map +1 -1
  80. package/dist/build-integrations/vitest/errors.d.mts +25 -4
  81. package/dist/build-integrations/vitest/errors.mjs +15 -2
  82. package/dist/build-integrations/vitest/errors.mjs.map +1 -1
  83. package/dist/build-integrations/vitest/index.d.mts +2 -2
  84. package/dist/build-integrations/vitest/index.mjs +3 -3
  85. package/dist/build-integrations/vitest/setup.d.mts +4 -2
  86. package/dist/build-integrations/vitest/setup.mjs +2 -2
  87. package/dist/build-integrations/vitest/setup.mjs.map +1 -1
  88. package/dist/build-integrations/vitest/stack-context.d.mts +0 -1
  89. package/dist/build-integrations/vitest/stack-context.mjs +33 -13
  90. package/dist/build-integrations/vitest/stack-context.mjs.map +1 -1
  91. package/dist/cli/bail.mjs +23 -0
  92. package/dist/cli/bail.mjs.map +1 -0
  93. package/dist/{surfaces/cli/commands → cli}/doctor-probes.mjs +26 -16
  94. package/dist/cli/doctor-probes.mjs.map +1 -0
  95. package/dist/cli/main.d.mts +15 -1
  96. package/dist/cli/main.mjs +166 -482
  97. package/dist/cli/main.mjs.map +1 -1
  98. package/dist/cli/prune-direct.mjs +31 -201
  99. package/dist/cli/prune-direct.mjs.map +1 -1
  100. package/dist/cli/snapshot-reader.mjs +14 -11
  101. package/dist/cli/snapshot-reader.mjs.map +1 -1
  102. package/dist/cli/up-lifecycle.mjs +4 -1
  103. package/dist/cli/up-lifecycle.mjs.map +1 -1
  104. package/dist/cli/wirings/apply.mjs +95 -0
  105. package/dist/cli/wirings/apply.mjs.map +1 -0
  106. package/dist/cli/wirings/build-verb-layers.mjs +42 -0
  107. package/dist/cli/wirings/build-verb-layers.mjs.map +1 -0
  108. package/dist/cli/wirings/config-loader.mjs +69 -0
  109. package/dist/cli/wirings/config-loader.mjs.map +1 -0
  110. package/dist/cli/wirings/engine-command.mjs +30 -0
  111. package/dist/cli/wirings/engine-command.mjs.map +1 -0
  112. package/dist/cli/wirings/identity.mjs +72 -0
  113. package/dist/cli/wirings/identity.mjs.map +1 -0
  114. package/dist/cli/wirings/provide-file-system.mjs +7 -0
  115. package/dist/cli/wirings/provide-file-system.mjs.map +1 -0
  116. package/dist/cli/wirings/snapshot.mjs +177 -0
  117. package/dist/cli/wirings/snapshot.mjs.map +1 -0
  118. package/dist/cli/wirings/up.mjs +287 -0
  119. package/dist/cli/wirings/up.mjs.map +1 -0
  120. package/dist/cli/wirings/wipe.mjs +40 -0
  121. package/dist/cli/wirings/wipe.mjs.map +1 -0
  122. package/dist/contracts/capability-decl.d.mts +0 -2
  123. package/dist/contracts/codegenable.d.mts +43 -0
  124. package/dist/contracts/container-runtime.d.mts +26 -1
  125. package/dist/contracts/faucet-strategy.d.mts +25 -0
  126. package/dist/contracts/funding-strategy.d.mts +42 -0
  127. package/dist/contracts/plugin-expander.mjs +60 -0
  128. package/dist/contracts/plugin-expander.mjs.map +1 -0
  129. package/dist/contracts/projection.d.mts +5 -3
  130. package/dist/contracts/renderer.d.mts +23 -0
  131. package/dist/contracts/routable.d.mts +14 -1
  132. package/dist/contracts/snapshotable.d.mts +25 -3
  133. package/dist/contracts/wallet-protocol.d.mts +52 -0
  134. package/dist/contracts/wallet-protocol.mjs +52 -0
  135. package/dist/contracts/wallet-protocol.mjs.map +1 -0
  136. package/dist/index.d.mts +26 -25
  137. package/dist/index.mjs +20 -13
  138. package/dist/{runtime → orchestrators}/built-in-plugin-layers.mjs +10 -5
  139. package/dist/orchestrators/built-in-plugin-layers.mjs.map +1 -0
  140. package/dist/orchestrators/codegen/bindings.d.mts +1 -2
  141. package/dist/orchestrators/codegen/bindings.mjs +52 -186
  142. package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
  143. package/dist/orchestrators/codegen/emit.mjs +19 -1
  144. package/dist/orchestrators/codegen/emit.mjs.map +1 -1
  145. package/dist/orchestrators/codegen/errors.mjs +8 -3
  146. package/dist/orchestrators/codegen/errors.mjs.map +1 -1
  147. package/dist/orchestrators/codegen/format.mjs +47 -39
  148. package/dist/orchestrators/codegen/format.mjs.map +1 -1
  149. package/dist/orchestrators/codegen/gitignore.mjs +3 -1
  150. package/dist/orchestrators/codegen/gitignore.mjs.map +1 -1
  151. package/dist/orchestrators/codegen/output-location.mjs +41 -0
  152. package/dist/orchestrators/codegen/output-location.mjs.map +1 -0
  153. package/dist/orchestrators/codegen/paths.d.mts +1 -1
  154. package/dist/orchestrators/codegen/paths.mjs +40 -14
  155. package/dist/orchestrators/codegen/paths.mjs.map +1 -1
  156. package/dist/orchestrators/codegen/permissions.mjs +3 -1
  157. package/dist/orchestrators/codegen/permissions.mjs.map +1 -1
  158. package/dist/orchestrators/codegen/service.mjs +157 -105
  159. package/dist/orchestrators/codegen/service.mjs.map +1 -1
  160. package/dist/orchestrators/lifecycle-prune/errors.mjs +31 -0
  161. package/dist/orchestrators/lifecycle-prune/errors.mjs.map +1 -0
  162. package/dist/orchestrators/lifecycle-prune/index.mjs +350 -0
  163. package/dist/orchestrators/lifecycle-prune/index.mjs.map +1 -0
  164. package/dist/orchestrators/router/cleanup.mjs +26 -27
  165. package/dist/orchestrators/router/cleanup.mjs.map +1 -1
  166. package/dist/orchestrators/router/entrypoints.mjs +2 -2
  167. package/dist/orchestrators/router/entrypoints.mjs.map +1 -1
  168. package/dist/orchestrators/router/file-provider.mjs +14 -5
  169. package/dist/orchestrators/router/file-provider.mjs.map +1 -1
  170. package/dist/orchestrators/router/hostname.mjs +19 -55
  171. package/dist/orchestrators/router/hostname.mjs.map +1 -1
  172. package/dist/orchestrators/router/index.mjs +11 -0
  173. package/dist/orchestrators/router/profile.mjs +2 -2
  174. package/dist/orchestrators/router/sentinels.mjs +26 -0
  175. package/dist/orchestrators/router/sentinels.mjs.map +1 -0
  176. package/dist/orchestrators/router/service.mjs +40 -37
  177. package/dist/orchestrators/router/service.mjs.map +1 -1
  178. package/dist/orchestrators/router/traefik-container.mjs +27 -16
  179. package/dist/orchestrators/router/traefik-container.mjs.map +1 -1
  180. package/dist/{substrate/runtime → orchestrators}/run.mjs +26 -21
  181. package/dist/orchestrators/run.mjs.map +1 -0
  182. package/dist/orchestrators/runtime-composition.d.mts +1 -1
  183. package/dist/orchestrators/runtime-composition.mjs +123 -39
  184. package/dist/orchestrators/runtime-composition.mjs.map +1 -1
  185. package/dist/orchestrators/snapshot/capture-command.d.mts +1 -0
  186. package/dist/orchestrators/snapshot/capture-command.mjs +25 -0
  187. package/dist/orchestrators/snapshot/capture-command.mjs.map +1 -0
  188. package/dist/orchestrators/snapshot/capture.mjs +42 -28
  189. package/dist/orchestrators/snapshot/capture.mjs.map +1 -1
  190. package/dist/orchestrators/snapshot/descriptor.mjs +35 -10
  191. package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -1
  192. package/dist/orchestrators/snapshot/identity-guard.mjs +1 -1
  193. package/dist/orchestrators/snapshot/index.mjs +13 -0
  194. package/dist/orchestrators/snapshot/integrity.d.mts +1 -1
  195. package/dist/orchestrators/snapshot/integrity.mjs +73 -17
  196. package/dist/orchestrators/snapshot/integrity.mjs.map +1 -1
  197. package/dist/orchestrators/snapshot/pending-marker.d.mts +1 -0
  198. package/dist/orchestrators/snapshot/pending-marker.mjs +99 -0
  199. package/dist/orchestrators/snapshot/pending-marker.mjs.map +1 -0
  200. package/dist/orchestrators/snapshot/phase-error.mjs +23 -0
  201. package/dist/orchestrators/snapshot/phase-error.mjs.map +1 -0
  202. package/dist/orchestrators/snapshot/prune.mjs +20 -55
  203. package/dist/orchestrators/snapshot/prune.mjs.map +1 -1
  204. package/dist/orchestrators/snapshot/recover-pending.d.mts +1 -0
  205. package/dist/orchestrators/snapshot/recover-pending.mjs +223 -0
  206. package/dist/orchestrators/snapshot/recover-pending.mjs.map +1 -0
  207. package/dist/orchestrators/snapshot/restore.mjs +118 -84
  208. package/dist/orchestrators/snapshot/restore.mjs.map +1 -1
  209. package/dist/orchestrators/snapshot/service.mjs +53 -56
  210. package/dist/orchestrators/snapshot/service.mjs.map +1 -1
  211. package/dist/orchestrators/snapshot/state-document.d.mts +1 -0
  212. package/dist/orchestrators/snapshot/state-document.mjs +47 -7
  213. package/dist/orchestrators/snapshot/state-document.mjs.map +1 -1
  214. package/dist/orchestrators/snapshot/wipe.mjs +67 -10
  215. package/dist/orchestrators/snapshot/wipe.mjs.map +1 -1
  216. package/dist/plugins/account/codegen.mjs +9 -8
  217. package/dist/plugins/account/codegen.mjs.map +1 -1
  218. package/dist/plugins/account/errors.d.mts +33 -6
  219. package/dist/plugins/account/errors.mjs.map +1 -1
  220. package/dist/plugins/account/funding.d.mts +29 -19
  221. package/dist/plugins/account/funding.mjs +84 -66
  222. package/dist/plugins/account/funding.mjs.map +1 -1
  223. package/dist/plugins/account/index.d.mts +2 -4
  224. package/dist/plugins/account/index.mjs +64 -31
  225. package/dist/plugins/account/index.mjs.map +1 -1
  226. package/dist/plugins/account/keypair.mjs +6 -9
  227. package/dist/plugins/account/keypair.mjs.map +1 -1
  228. package/dist/plugins/account/lease.mjs +1 -0
  229. package/dist/plugins/account/lease.mjs.map +1 -1
  230. package/dist/plugins/account/registry.d.mts +14 -1
  231. package/dist/plugins/account/registry.mjs +18 -18
  232. package/dist/plugins/account/registry.mjs.map +1 -1
  233. package/dist/plugins/account/service.d.mts +23 -4
  234. package/dist/plugins/account/service.mjs +83 -45
  235. package/dist/plugins/account/service.mjs.map +1 -1
  236. package/dist/plugins/account/snapshot.mjs +2 -5
  237. package/dist/plugins/account/snapshot.mjs.map +1 -1
  238. package/dist/plugins/account/spans.mjs +16 -0
  239. package/dist/plugins/account/spans.mjs.map +1 -0
  240. package/dist/plugins/account/variants/keystore.mjs +6 -8
  241. package/dist/plugins/account/variants/keystore.mjs.map +1 -1
  242. package/dist/plugins/action/build-context.d.mts +3 -2
  243. package/dist/plugins/action/errors.d.mts +16 -12
  244. package/dist/plugins/action/errors.mjs.map +1 -1
  245. package/dist/plugins/action/execute.mjs +65 -103
  246. package/dist/plugins/action/execute.mjs.map +1 -1
  247. package/dist/plugins/action/index.d.mts +0 -2
  248. package/dist/plugins/action/index.mjs +17 -13
  249. package/dist/plugins/action/index.mjs.map +1 -1
  250. package/dist/plugins/action/service.mjs +38 -35
  251. package/dist/plugins/action/service.mjs.map +1 -1
  252. package/dist/plugins/action/spans.mjs +11 -0
  253. package/dist/plugins/action/spans.mjs.map +1 -0
  254. package/dist/plugins/coin/codegen.mjs +9 -8
  255. package/dist/plugins/coin/codegen.mjs.map +1 -1
  256. package/dist/plugins/coin/discovery.mjs +8 -4
  257. package/dist/plugins/coin/discovery.mjs.map +1 -1
  258. package/dist/plugins/coin/index.d.mts +11 -29
  259. package/dist/plugins/coin/index.mjs +56 -27
  260. package/dist/plugins/coin/index.mjs.map +1 -1
  261. package/dist/plugins/coin/metadata.mjs +8 -15
  262. package/dist/plugins/coin/metadata.mjs.map +1 -1
  263. package/dist/plugins/coin/mint.d.mts +30 -11
  264. package/dist/plugins/coin/mint.mjs +78 -74
  265. package/dist/plugins/coin/mint.mjs.map +1 -1
  266. package/dist/plugins/coin/registry.mjs +4 -7
  267. package/dist/plugins/coin/registry.mjs.map +1 -1
  268. package/dist/plugins/coin/service.d.mts +39 -1
  269. package/dist/plugins/coin/service.mjs +11 -4
  270. package/dist/plugins/coin/service.mjs.map +1 -1
  271. package/dist/plugins/coin/spans.mjs +20 -0
  272. package/dist/plugins/coin/spans.mjs.map +1 -0
  273. package/dist/plugins/dashboard/domain.mjs +402 -0
  274. package/dist/plugins/dashboard/domain.mjs.map +1 -0
  275. package/dist/plugins/dashboard/index.d.mts +29 -0
  276. package/dist/plugins/dashboard/index.mjs +91 -0
  277. package/dist/plugins/dashboard/index.mjs.map +1 -0
  278. package/dist/plugins/dashboard/origin-policy.mjs +36 -0
  279. package/dist/plugins/dashboard/origin-policy.mjs.map +1 -0
  280. package/dist/plugins/dashboard/routable.mjs +27 -0
  281. package/dist/plugins/dashboard/routable.mjs.map +1 -0
  282. package/dist/plugins/dashboard/schema/builder.mjs +21 -0
  283. package/dist/plugins/dashboard/schema/builder.mjs.map +1 -0
  284. package/dist/plugins/dashboard/schema/enums.mjs +72 -0
  285. package/dist/plugins/dashboard/schema/enums.mjs.map +1 -0
  286. package/dist/plugins/dashboard/schema/root.mjs +351 -0
  287. package/dist/plugins/dashboard/schema/root.mjs.map +1 -0
  288. package/dist/plugins/dashboard/schema/types.mjs +508 -0
  289. package/dist/plugins/dashboard/schema/types.mjs.map +1 -0
  290. package/dist/plugins/dashboard/schema.mjs +11 -0
  291. package/dist/plugins/dashboard/schema.mjs.map +1 -0
  292. package/dist/plugins/dashboard/server.mjs +166 -0
  293. package/dist/plugins/dashboard/server.mjs.map +1 -0
  294. package/dist/plugins/deepbook/deploy.mjs +53 -115
  295. package/dist/plugins/deepbook/deploy.mjs.map +1 -1
  296. package/dist/plugins/deepbook/errors.mjs +1 -0
  297. package/dist/plugins/deepbook/errors.mjs.map +1 -1
  298. package/dist/plugins/deepbook/faucet-strategy.mjs +47 -34
  299. package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -1
  300. package/dist/plugins/deepbook/hash.mjs +9 -0
  301. package/dist/plugins/deepbook/hash.mjs.map +1 -0
  302. package/dist/plugins/deepbook/index.d.mts +1 -10
  303. package/dist/plugins/deepbook/index.mjs +25 -22
  304. package/dist/plugins/deepbook/index.mjs.map +1 -1
  305. package/dist/plugins/deepbook/pyth/index.mjs +26 -55
  306. package/dist/plugins/deepbook/pyth/index.mjs.map +1 -1
  307. package/dist/plugins/deepbook/snapshot.mjs +17 -29
  308. package/dist/plugins/deepbook/snapshot.mjs.map +1 -1
  309. package/dist/plugins/deepbook/spans.mjs +18 -0
  310. package/dist/plugins/deepbook/spans.mjs.map +1 -0
  311. package/dist/plugins/deepbook/type-strings.mjs +65 -0
  312. package/dist/plugins/deepbook/type-strings.mjs.map +1 -0
  313. package/dist/plugins/deepbook/types.d.mts +2 -3
  314. package/dist/plugins/deepbook/types.mjs.map +1 -1
  315. package/dist/plugins/faucet/dispatcher.d.mts +21 -2
  316. package/dist/plugins/faucet/dispatcher.mjs +1 -2
  317. package/dist/plugins/faucet/dispatcher.mjs.map +1 -1
  318. package/dist/plugins/faucet/errors.d.mts +11 -24
  319. package/dist/plugins/faucet/errors.mjs.map +1 -1
  320. package/dist/plugins/faucet/http.mjs +33 -14
  321. package/dist/plugins/faucet/http.mjs.map +1 -1
  322. package/dist/plugins/faucet/index.d.mts +15 -5
  323. package/dist/plugins/faucet/index.mjs +3 -0
  324. package/dist/plugins/faucet/index.mjs.map +1 -1
  325. package/dist/plugins/faucet/spans.mjs +12 -0
  326. package/dist/plugins/faucet/spans.mjs.map +1 -0
  327. package/dist/plugins/host-service/errors.d.mts +1 -1
  328. package/dist/plugins/host-service/index.mjs +34 -8
  329. package/dist/plugins/host-service/index.mjs.map +1 -1
  330. package/dist/plugins/host-service/routable.mjs +2 -1
  331. package/dist/plugins/host-service/routable.mjs.map +1 -1
  332. package/dist/plugins/host-service/service.d.mts +21 -0
  333. package/dist/plugins/host-service/service.mjs +136 -81
  334. package/dist/plugins/host-service/service.mjs.map +1 -1
  335. package/dist/plugins/internal/acquire-on-chain-artifact.mjs +23 -0
  336. package/dist/plugins/internal/acquire-on-chain-artifact.mjs.map +1 -0
  337. package/dist/plugins/internal/codegen-helpers.mjs +18 -0
  338. package/dist/plugins/internal/codegen-helpers.mjs.map +1 -0
  339. package/dist/plugins/internal/funding-failure-error.mjs +32 -0
  340. package/dist/plugins/internal/funding-failure-error.mjs.map +1 -0
  341. package/dist/plugins/package/build.mjs +1 -1
  342. package/dist/plugins/package/build.mjs.map +1 -1
  343. package/dist/plugins/package/codegen.mjs +26 -0
  344. package/dist/plugins/package/codegen.mjs.map +1 -1
  345. package/dist/plugins/package/errors.d.mts +16 -6
  346. package/dist/plugins/package/errors.mjs.map +1 -1
  347. package/dist/plugins/package/index.d.mts +0 -4
  348. package/dist/plugins/package/index.mjs +27 -21
  349. package/dist/plugins/package/index.mjs.map +1 -1
  350. package/dist/plugins/package/mode-known.mjs +26 -12
  351. package/dist/plugins/package/mode-known.mjs.map +1 -1
  352. package/dist/plugins/package/mode-local.mjs +47 -41
  353. package/dist/plugins/package/mode-local.mjs.map +1 -1
  354. package/dist/plugins/package/publish-executor.mjs +55 -114
  355. package/dist/plugins/package/publish-executor.mjs.map +1 -1
  356. package/dist/plugins/package/registry.mjs +2 -1
  357. package/dist/plugins/package/registry.mjs.map +1 -1
  358. package/dist/plugins/package/service.mjs.map +1 -1
  359. package/dist/plugins/package/spans.mjs +14 -0
  360. package/dist/plugins/package/spans.mjs.map +1 -0
  361. package/dist/plugins/postgres/db-ensure.mjs +19 -12
  362. package/dist/plugins/postgres/db-ensure.mjs.map +1 -1
  363. package/dist/plugins/postgres/errors.d.mts +5 -0
  364. package/dist/plugins/postgres/errors.mjs.map +1 -1
  365. package/dist/plugins/postgres/index.mjs +10 -17
  366. package/dist/plugins/postgres/index.mjs.map +1 -1
  367. package/dist/plugins/postgres/service.mjs +58 -24
  368. package/dist/plugins/postgres/service.mjs.map +1 -1
  369. package/dist/plugins/postgres/snapshot.mjs.map +1 -1
  370. package/dist/plugins/postgres/spans.mjs +11 -0
  371. package/dist/plugins/postgres/spans.mjs.map +1 -0
  372. package/dist/plugins/router-entrypoints.mjs +4 -4
  373. package/dist/plugins/router-entrypoints.mjs.map +1 -1
  374. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +15 -13
  375. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -1
  376. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +15 -19
  377. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -1
  378. package/dist/plugins/seal/codegen.d.mts +2 -3
  379. package/dist/plugins/seal/codegen.mjs.map +1 -1
  380. package/dist/plugins/seal/config-render.mjs +35 -8
  381. package/dist/plugins/seal/config-render.mjs.map +1 -1
  382. package/dist/plugins/seal/deploy.mjs +31 -39
  383. package/dist/plugins/seal/deploy.mjs.map +1 -1
  384. package/dist/plugins/seal/errors.d.mts +1 -3
  385. package/dist/plugins/seal/errors.mjs +7 -1
  386. package/dist/plugins/seal/errors.mjs.map +1 -1
  387. package/dist/plugins/seal/index.d.mts +1 -6
  388. package/dist/plugins/seal/index.mjs +40 -14
  389. package/dist/plugins/seal/index.mjs.map +1 -1
  390. package/dist/plugins/seal/key-manager.d.mts +1 -9
  391. package/dist/plugins/seal/key-manager.mjs +2 -18
  392. package/dist/plugins/seal/key-manager.mjs.map +1 -1
  393. package/dist/plugins/seal/key-server.mjs +37 -36
  394. package/dist/plugins/seal/key-server.mjs.map +1 -1
  395. package/dist/plugins/seal/keygen.mjs +16 -17
  396. package/dist/plugins/seal/keygen.mjs.map +1 -1
  397. package/dist/plugins/seal/mode/fork-known.mjs +36 -14
  398. package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
  399. package/dist/plugins/seal/mode/live.d.mts +6 -4
  400. package/dist/plugins/seal/mode/live.mjs +14 -21
  401. package/dist/plugins/seal/mode/live.mjs.map +1 -1
  402. package/dist/plugins/seal/mode/local-keygen.mjs +57 -21
  403. package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
  404. package/dist/plugins/seal/registry-publish.mjs +1 -0
  405. package/dist/plugins/seal/registry-publish.mjs.map +1 -1
  406. package/dist/plugins/seal/routable.mjs.map +1 -1
  407. package/dist/plugins/seal/service.mjs.map +1 -1
  408. package/dist/plugins/seal/spans.mjs +18 -0
  409. package/dist/plugins/seal/spans.mjs.map +1 -0
  410. package/dist/plugins/sui/auto-tick.mjs +17 -23
  411. package/dist/plugins/sui/auto-tick.mjs.map +1 -1
  412. package/dist/plugins/sui/chain-build-container.mjs +18 -0
  413. package/dist/plugins/sui/chain-build-container.mjs.map +1 -0
  414. package/dist/plugins/sui/chain-probe.d.mts +5 -5
  415. package/dist/plugins/sui/chain-probe.mjs +28 -58
  416. package/dist/plugins/sui/chain-probe.mjs.map +1 -1
  417. package/dist/plugins/sui/codegen.mjs +26 -0
  418. package/dist/plugins/sui/codegen.mjs.map +1 -1
  419. package/dist/plugins/sui/errors.d.mts +2 -29
  420. package/dist/plugins/sui/errors.mjs +1 -3
  421. package/dist/plugins/sui/errors.mjs.map +1 -1
  422. package/dist/plugins/sui/fork-faucet-strategy.d.mts +1 -0
  423. package/dist/plugins/sui/fork-faucet-strategy.mjs +55 -0
  424. package/dist/plugins/sui/fork-faucet-strategy.mjs.map +1 -0
  425. package/dist/plugins/sui/fork-orchestration.d.mts +1 -0
  426. package/dist/plugins/sui/fork-orchestration.mjs +170 -2
  427. package/dist/plugins/sui/fork-orchestration.mjs.map +1 -1
  428. package/dist/plugins/sui/fork-transaction.d.mts +2 -0
  429. package/dist/plugins/sui/fork-transaction.mjs +92 -28
  430. package/dist/plugins/sui/fork-transaction.mjs.map +1 -1
  431. package/dist/plugins/sui/index.d.mts +33 -70
  432. package/dist/plugins/sui/index.mjs +58 -35
  433. package/dist/plugins/sui/index.mjs.map +1 -1
  434. package/dist/plugins/{faucet/strategies/sui-local.mjs → sui/local-faucet-strategy.mjs} +10 -7
  435. package/dist/plugins/sui/local-faucet-strategy.mjs.map +1 -0
  436. package/dist/plugins/sui/mode/external.mjs +7 -13
  437. package/dist/plugins/sui/mode/external.mjs.map +1 -1
  438. package/dist/plugins/sui/mode/fork.mjs +136 -59
  439. package/dist/plugins/sui/mode/fork.mjs.map +1 -1
  440. package/dist/plugins/sui/mode/live.mjs +10 -16
  441. package/dist/plugins/sui/mode/live.mjs.map +1 -1
  442. package/dist/plugins/sui/mode/local.mjs +37 -36
  443. package/dist/plugins/sui/mode/local.mjs.map +1 -1
  444. package/dist/plugins/sui/mode/shared-boot.mjs +38 -115
  445. package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -1
  446. package/dist/plugins/sui/mode/shared.d.mts +1 -5
  447. package/dist/plugins/sui/mode/shared.mjs.map +1 -1
  448. package/dist/plugins/sui/mode/spec.d.mts +18 -0
  449. package/dist/plugins/sui/move-summary-runner.mjs +207 -0
  450. package/dist/plugins/sui/move-summary-runner.mjs.map +1 -0
  451. package/dist/plugins/sui/network-resolver.d.mts +4 -3
  452. package/dist/plugins/sui/service.mjs.map +1 -1
  453. package/dist/plugins/sui/snapshot.mjs +3 -0
  454. package/dist/plugins/sui/snapshot.mjs.map +1 -1
  455. package/dist/plugins/sui/spans.mjs +17 -0
  456. package/dist/plugins/sui/spans.mjs.map +1 -0
  457. package/dist/plugins/wallet/codegen.d.mts +0 -1
  458. package/dist/plugins/wallet/codegen.mjs +8 -9
  459. package/dist/plugins/wallet/codegen.mjs.map +1 -1
  460. package/dist/plugins/wallet/index.d.mts +6 -8
  461. package/dist/plugins/wallet/index.mjs +46 -35
  462. package/dist/plugins/wallet/index.mjs.map +1 -1
  463. package/dist/plugins/wallet/origin-policy.mjs +12 -28
  464. package/dist/plugins/wallet/origin-policy.mjs.map +1 -1
  465. package/dist/plugins/wallet/pairing.mjs +6 -4
  466. package/dist/plugins/wallet/pairing.mjs.map +1 -1
  467. package/dist/plugins/wallet/protocol.d.mts +1 -0
  468. package/dist/plugins/wallet/protocol.mjs +5 -44
  469. package/dist/plugins/wallet/protocol.mjs.map +1 -1
  470. package/dist/plugins/wallet/routable.mjs +2 -8
  471. package/dist/plugins/wallet/routable.mjs.map +1 -1
  472. package/dist/plugins/wallet/server.mjs +36 -44
  473. package/dist/plugins/wallet/server.mjs.map +1 -1
  474. package/dist/plugins/wallet/service.d.mts +1 -5
  475. package/dist/plugins/wallet/service.mjs +22 -18
  476. package/dist/plugins/wallet/service.mjs.map +1 -1
  477. package/dist/plugins/wallet/spans.mjs +22 -0
  478. package/dist/plugins/wallet/spans.mjs.map +1 -0
  479. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +33 -8
  480. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -1
  481. package/dist/plugins/walrus/codegen.d.mts +3 -3
  482. package/dist/plugins/walrus/codegen.mjs.map +1 -1
  483. package/dist/plugins/walrus/deploy-paths.mjs +21 -5
  484. package/dist/plugins/walrus/deploy-paths.mjs.map +1 -1
  485. package/dist/plugins/walrus/deploy.mjs +67 -69
  486. package/dist/plugins/walrus/deploy.mjs.map +1 -1
  487. package/dist/plugins/walrus/errors.mjs +1 -0
  488. package/dist/plugins/walrus/errors.mjs.map +1 -1
  489. package/dist/plugins/walrus/faucet-strategy.mjs +12 -7
  490. package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -1
  491. package/dist/plugins/walrus/index.d.mts +2 -13
  492. package/dist/plugins/walrus/index.mjs +18 -23
  493. package/dist/plugins/walrus/index.mjs.map +1 -1
  494. package/dist/plugins/walrus/mode/known-deploy.mjs +7 -5
  495. package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
  496. package/dist/plugins/walrus/mode/local-cluster.mjs +26 -7
  497. package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
  498. package/dist/plugins/walrus/registry-publish.mjs.map +1 -1
  499. package/dist/plugins/walrus/routable.mjs +7 -23
  500. package/dist/plugins/walrus/routable.mjs.map +1 -1
  501. package/dist/plugins/walrus/service.mjs.map +1 -1
  502. package/dist/plugins/walrus/spans.mjs +18 -0
  503. package/dist/plugins/walrus/spans.mjs.map +1 -0
  504. package/dist/plugins/walrus/storage-nodes.d.mts +6 -2
  505. package/dist/plugins/walrus/storage-nodes.mjs +15 -8
  506. package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
  507. package/dist/plugins/walrus/wal-swap.mjs +58 -15
  508. package/dist/plugins/walrus/wal-swap.mjs.map +1 -1
  509. package/dist/primitives/artifact-publisher.d.mts +1 -1
  510. package/dist/primitives/artifact-publisher.mjs +15 -0
  511. package/dist/primitives/artifact-publisher.mjs.map +1 -0
  512. package/dist/runtime/docker/client.mjs +17 -4
  513. package/dist/runtime/docker/client.mjs.map +1 -1
  514. package/dist/runtime/docker/container.mjs +219 -132
  515. package/dist/runtime/docker/container.mjs.map +1 -1
  516. package/dist/runtime/docker/errors.mjs +0 -6
  517. package/dist/runtime/docker/errors.mjs.map +1 -1
  518. package/dist/runtime/docker/exec.mjs +21 -18
  519. package/dist/runtime/docker/exec.mjs.map +1 -1
  520. package/dist/runtime/docker/image.mjs +36 -13
  521. package/dist/runtime/docker/image.mjs.map +1 -1
  522. package/dist/runtime/docker/index.mjs +14 -0
  523. package/dist/runtime/docker/inspect-and-decode.mjs +51 -0
  524. package/dist/runtime/docker/inspect-and-decode.mjs.map +1 -0
  525. package/dist/runtime/docker/inventory.mjs +14 -11
  526. package/dist/runtime/docker/inventory.mjs.map +1 -1
  527. package/dist/runtime/docker/labels.mjs +38 -5
  528. package/dist/runtime/docker/labels.mjs.map +1 -1
  529. package/dist/runtime/docker/logs.d.mts +1 -1
  530. package/dist/runtime/docker/logs.mjs.map +1 -1
  531. package/dist/runtime/docker/network.mjs +83 -47
  532. package/dist/runtime/docker/network.mjs.map +1 -1
  533. package/dist/runtime/docker/render-run-args.mjs +82 -0
  534. package/dist/runtime/docker/render-run-args.mjs.map +1 -0
  535. package/dist/runtime/docker/service.d.mts +1 -1
  536. package/dist/runtime/docker/service.mjs +12 -7
  537. package/dist/runtime/docker/service.mjs.map +1 -1
  538. package/dist/runtime/docker/sweep.mjs +118 -43
  539. package/dist/runtime/docker/sweep.mjs.map +1 -1
  540. package/dist/runtime/docker/volume.mjs +2 -0
  541. package/dist/runtime/docker/volume.mjs.map +1 -1
  542. package/dist/runtime/docker/wrap.mjs +30 -1
  543. package/dist/runtime/docker/wrap.mjs.map +1 -1
  544. package/dist/substrate/cross-process.mjs +14 -8
  545. package/dist/substrate/cross-process.mjs.map +1 -1
  546. package/dist/substrate/event-time.mjs +33 -0
  547. package/dist/substrate/event-time.mjs.map +1 -0
  548. package/dist/substrate/events.d.mts +28 -8
  549. package/dist/substrate/identity.d.mts +2 -1
  550. package/dist/substrate/manifest.d.mts +68 -5
  551. package/dist/substrate/manifest.mjs +34 -5
  552. package/dist/substrate/manifest.mjs.map +1 -1
  553. package/dist/substrate/network.d.mts +3 -5
  554. package/dist/substrate/options.d.mts +0 -2
  555. package/dist/substrate/plugin.d.mts +13 -0
  556. package/dist/substrate/plugin.mjs +4 -2
  557. package/dist/substrate/plugin.mjs.map +1 -1
  558. package/dist/substrate/projection.d.mts +35 -3
  559. package/dist/substrate/runtime/artifact-publisher/index.mjs +19 -9
  560. package/dist/substrate/runtime/artifact-publisher/index.mjs.map +1 -1
  561. package/dist/substrate/runtime/atomic-write.mjs +97 -32
  562. package/dist/substrate/runtime/atomic-write.mjs.map +1 -1
  563. package/dist/substrate/runtime/cache/index.mjs +3 -0
  564. package/dist/substrate/runtime/cache/schema.mjs +2 -2
  565. package/dist/substrate/runtime/cache/schema.mjs.map +1 -1
  566. package/dist/substrate/runtime/cache/service.mjs +9 -7
  567. package/dist/substrate/runtime/cache/service.mjs.map +1 -1
  568. package/dist/substrate/runtime/capability-sinks/index.d.mts +1 -1
  569. package/dist/substrate/runtime/capability-sinks/index.mjs +3 -0
  570. package/dist/substrate/runtime/capability-sinks/layer.mjs +1 -1
  571. package/dist/substrate/runtime/capability-sinks/service.d.mts +61 -2
  572. package/dist/substrate/runtime/capability-sinks/service.mjs +21 -26
  573. package/dist/substrate/runtime/capability-sinks/service.mjs.map +1 -1
  574. package/dist/substrate/runtime/config-validation.d.mts +1 -13
  575. package/dist/substrate/runtime/config-validation.mjs +2 -91
  576. package/dist/substrate/runtime/config-validation.mjs.map +1 -1
  577. package/dist/substrate/runtime/container-runtime.d.mts +1 -0
  578. package/dist/substrate/runtime/container-runtime.mjs +2 -0
  579. package/dist/substrate/runtime/control-plane/domain.mjs +101 -0
  580. package/dist/substrate/runtime/control-plane/domain.mjs.map +1 -0
  581. package/dist/substrate/runtime/control-plane/service.mjs +7 -0
  582. package/dist/substrate/runtime/control-plane/service.mjs.map +1 -0
  583. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +46 -19
  584. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -1
  585. package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs +30 -12
  586. package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -1
  587. package/dist/substrate/runtime/cross-process/command-channel/index.mjs +5 -0
  588. package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs +11 -2
  589. package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs.map +1 -1
  590. package/dist/substrate/runtime/cross-process/command-channel/runtime-control-lock.mjs +1 -1
  591. package/dist/substrate/runtime/cross-process/index.mjs +11 -0
  592. package/dist/substrate/runtime/cross-process/live-clock.mjs +34 -0
  593. package/dist/substrate/runtime/cross-process/live-clock.mjs.map +1 -0
  594. package/dist/substrate/runtime/cross-process/liveness.mjs +52 -10
  595. package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
  596. package/dist/substrate/runtime/cross-process/lock.d.mts +1 -0
  597. package/dist/substrate/runtime/cross-process/lock.mjs +23 -0
  598. package/dist/substrate/runtime/cross-process/lock.mjs.map +1 -0
  599. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs +64 -0
  600. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs.map +1 -0
  601. package/dist/substrate/runtime/cross-process/roster.mjs +97 -83
  602. package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -1
  603. package/dist/substrate/runtime/cross-process/self-pid.mjs +8 -0
  604. package/dist/substrate/runtime/cross-process/self-pid.mjs.map +1 -0
  605. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +24 -28
  606. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +1 -1
  607. package/dist/substrate/runtime/cross-process/stack-lock.mjs +41 -20
  608. package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -1
  609. package/dist/substrate/runtime/current-plugin.mjs +2 -2
  610. package/dist/substrate/runtime/current-plugin.mjs.map +1 -1
  611. package/dist/substrate/runtime/errors.mjs +12 -6
  612. package/dist/substrate/runtime/errors.mjs.map +1 -1
  613. package/dist/substrate/runtime/format-unknown-error.mjs +30 -0
  614. package/dist/substrate/runtime/format-unknown-error.mjs.map +1 -0
  615. package/dist/substrate/runtime/host-bind-mount-owner.mjs +10 -0
  616. package/dist/substrate/runtime/host-bind-mount-owner.mjs.map +1 -0
  617. package/dist/substrate/runtime/host-gateway.mjs +20 -0
  618. package/dist/substrate/runtime/host-gateway.mjs.map +1 -0
  619. package/dist/substrate/runtime/host-tree-tar/index.mjs +61 -50
  620. package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -1
  621. package/dist/substrate/runtime/http-probe.d.mts +1 -1
  622. package/dist/substrate/runtime/index.mjs +14 -0
  623. package/dist/substrate/runtime/lease-broker/index.d.mts +1 -1
  624. package/dist/substrate/runtime/lease-broker/index.mjs +2 -0
  625. package/dist/substrate/runtime/lease-broker/service.d.mts +1 -64
  626. package/dist/substrate/runtime/lease-broker/service.mjs +1 -1
  627. package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -1
  628. package/dist/substrate/runtime/lifecycle/dep-graph.mjs +22 -3
  629. package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -1
  630. package/dist/substrate/runtime/lifecycle/index.mjs +9 -0
  631. package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +1 -1
  632. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +20 -3
  633. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -1
  634. package/dist/substrate/runtime/lifecycle/selective-restart.mjs +1 -1
  635. package/dist/substrate/runtime/lifecycle/signals.mjs +5 -5
  636. package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -1
  637. package/dist/substrate/runtime/lifecycle/state-machine.mjs +1 -1
  638. package/dist/substrate/runtime/managed-container.d.mts +19 -1
  639. package/dist/substrate/runtime/managed-container.mjs +20 -1
  640. package/dist/substrate/runtime/managed-container.mjs.map +1 -1
  641. package/dist/substrate/runtime/manifest/index.mjs +3 -0
  642. package/dist/substrate/runtime/manifest/manifest.mjs +15 -11
  643. package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
  644. package/dist/substrate/runtime/mode-errors.mjs +20 -0
  645. package/dist/substrate/runtime/mode-errors.mjs.map +1 -0
  646. package/dist/substrate/runtime/observability/cascade-formatter.mjs +9 -13
  647. package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -1
  648. package/dist/substrate/runtime/observability/formatter-registry.mjs +19 -36
  649. package/dist/substrate/runtime/observability/formatter-registry.mjs.map +1 -1
  650. package/dist/substrate/runtime/observability/ignore-with-log.d.mts +1 -0
  651. package/dist/substrate/runtime/observability/ignore-with-log.mjs +17 -0
  652. package/dist/substrate/runtime/observability/ignore-with-log.mjs.map +1 -0
  653. package/dist/substrate/runtime/observability/index.d.mts +1 -1
  654. package/dist/substrate/runtime/observability/index.mjs +13 -0
  655. package/dist/substrate/runtime/observability/log-store.d.mts +1 -0
  656. package/dist/substrate/runtime/observability/log-store.mjs +175 -0
  657. package/dist/substrate/runtime/observability/log-store.mjs.map +1 -0
  658. package/dist/substrate/runtime/observability/logger.d.mts +2 -1
  659. package/dist/substrate/runtime/observability/logger.mjs +5 -9
  660. package/dist/substrate/runtime/observability/logger.mjs.map +1 -1
  661. package/dist/substrate/runtime/observability/output-truncate.mjs +35 -0
  662. package/dist/substrate/runtime/observability/output-truncate.mjs.map +1 -0
  663. package/dist/substrate/runtime/observability/pretty-error.mjs +9 -12
  664. package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -1
  665. package/dist/substrate/runtime/observability/redaction.d.mts +2 -13
  666. package/dist/substrate/runtime/observability/redaction.mjs +8 -14
  667. package/dist/substrate/runtime/observability/redaction.mjs.map +1 -1
  668. package/dist/substrate/runtime/observability/span-store.d.mts +1 -0
  669. package/dist/substrate/runtime/observability/span-store.mjs +110 -0
  670. package/dist/substrate/runtime/observability/span-store.mjs.map +1 -0
  671. package/dist/substrate/runtime/observability/spans.mjs +14 -14
  672. package/dist/substrate/runtime/observability/spans.mjs.map +1 -1
  673. package/dist/substrate/runtime/observability/subprocess-capture.mjs +1 -1
  674. package/dist/substrate/runtime/passthrough-or-wrap.mjs +33 -0
  675. package/dist/substrate/runtime/passthrough-or-wrap.mjs.map +1 -0
  676. package/dist/substrate/runtime/paths.d.mts +1 -1
  677. package/dist/substrate/runtime/paths.mjs +4 -3
  678. package/dist/substrate/runtime/paths.mjs.map +1 -1
  679. package/dist/substrate/runtime/phase-preserving-produce.mjs +40 -0
  680. package/dist/substrate/runtime/phase-preserving-produce.mjs.map +1 -0
  681. package/dist/substrate/runtime/port-broker/index.mjs +3 -0
  682. package/dist/substrate/runtime/port-broker/service.mjs +170 -126
  683. package/dist/substrate/runtime/port-broker/service.mjs.map +1 -1
  684. package/dist/substrate/runtime/post-acquire-tasks.mjs +1 -1
  685. package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -1
  686. package/dist/substrate/runtime/probes.d.mts +7 -5
  687. package/dist/substrate/runtime/probes.mjs +3 -1
  688. package/dist/substrate/runtime/probes.mjs.map +1 -1
  689. package/dist/substrate/runtime/projection/index.mjs +4 -0
  690. package/dist/substrate/runtime/projection/operational-endpoints.mjs +1 -2
  691. package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -1
  692. package/dist/substrate/runtime/projection/persisted.mjs +33 -15
  693. package/dist/substrate/runtime/projection/persisted.mjs.map +1 -1
  694. package/dist/substrate/runtime/projection/state-ref.mjs +26 -2
  695. package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -1
  696. package/dist/substrate/runtime/projection/update.mjs +88 -24
  697. package/dist/substrate/runtime/projection/update.mjs.map +1 -1
  698. package/dist/substrate/runtime/random-suffix.mjs +11 -0
  699. package/dist/substrate/runtime/random-suffix.mjs.map +1 -0
  700. package/dist/substrate/runtime/retry-policy.d.mts +59 -1
  701. package/dist/substrate/runtime/retry-policy.mjs +66 -1
  702. package/dist/substrate/runtime/retry-policy.mjs.map +1 -1
  703. package/dist/substrate/runtime/routed-url.d.mts +1 -0
  704. package/dist/substrate/runtime/routed-url.mjs +79 -0
  705. package/dist/substrate/runtime/routed-url.mjs.map +1 -0
  706. package/dist/substrate/runtime/runtime-decode.d.mts +1 -1
  707. package/dist/substrate/runtime/runtime-decode.mjs.map +1 -1
  708. package/dist/substrate/runtime/scoped-http-server.mjs +2 -3
  709. package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -1
  710. package/dist/substrate/runtime/scoped-multimap/index.mjs +2 -0
  711. package/dist/substrate/runtime/scoped-multimap/service.mjs +52 -0
  712. package/dist/substrate/runtime/scoped-multimap/service.mjs.map +1 -0
  713. package/dist/substrate/runtime/scoped-ref-map/index.mjs +2 -0
  714. package/dist/substrate/runtime/scoped-ref-map/service.mjs +2 -2
  715. package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +1 -1
  716. package/dist/substrate/runtime/stage-and-swap/index.mjs +72 -4
  717. package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -1
  718. package/dist/substrate/runtime/state-store/index.d.mts +1 -0
  719. package/dist/substrate/runtime/state-store/index.mjs +3 -0
  720. package/dist/substrate/runtime/state-store/schema.d.mts +1 -0
  721. package/dist/substrate/runtime/state-store/schema.mjs +19 -14
  722. package/dist/substrate/runtime/state-store/schema.mjs.map +1 -1
  723. package/dist/substrate/runtime/state-store/service.d.mts +1 -0
  724. package/dist/substrate/runtime/state-store/service.mjs +145 -0
  725. package/dist/substrate/runtime/state-store/service.mjs.map +1 -0
  726. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.d.mts +1 -0
  727. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs +33 -0
  728. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs.map +1 -0
  729. package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs +1 -1
  730. package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -1
  731. package/dist/substrate/runtime/strategy-registry/index.mjs +4 -0
  732. package/dist/substrate/runtime/strategy-registry/service.mjs +19 -33
  733. package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -1
  734. package/dist/substrate/runtime/subnet-broker.mjs +60 -0
  735. package/dist/substrate/runtime/subnet-broker.mjs.map +1 -0
  736. package/dist/substrate/runtime/sui-execute/index.d.mts +21 -1
  737. package/dist/substrate/runtime/sui-execute/index.mjs +84 -20
  738. package/dist/substrate/runtime/sui-execute/index.mjs.map +1 -1
  739. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.d.mts +1 -0
  740. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs +32 -0
  741. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs.map +1 -0
  742. package/dist/substrate/runtime/sui-ledger/object-ref.mjs +32 -0
  743. package/dist/substrate/runtime/sui-ledger/object-ref.mjs.map +1 -0
  744. package/dist/substrate/runtime/sui-move-build/index.mjs +93 -19
  745. package/dist/substrate/runtime/sui-move-build/index.mjs.map +1 -1
  746. package/dist/substrate/runtime/supervisor/acquire-node.mjs +181 -0
  747. package/dist/substrate/runtime/supervisor/acquire-node.mjs.map +1 -0
  748. package/dist/substrate/runtime/supervisor/background-tasks.mjs +144 -0
  749. package/dist/substrate/runtime/supervisor/background-tasks.mjs.map +1 -0
  750. package/dist/substrate/runtime/supervisor/command-loop.mjs +68 -0
  751. package/dist/substrate/runtime/supervisor/command-loop.mjs.map +1 -0
  752. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs +151 -0
  753. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs.map +1 -0
  754. package/dist/substrate/runtime/supervisor/errors.d.mts +1 -0
  755. package/dist/substrate/runtime/supervisor/errors.mjs +9 -0
  756. package/dist/substrate/runtime/supervisor/errors.mjs.map +1 -0
  757. package/dist/substrate/runtime/supervisor/index.d.mts +1 -0
  758. package/dist/substrate/runtime/supervisor/index.mjs +3 -0
  759. package/dist/substrate/runtime/supervisor/shutdown.mjs +51 -0
  760. package/dist/substrate/runtime/supervisor/shutdown.mjs.map +1 -0
  761. package/dist/substrate/runtime/supervisor/start-supervisor.d.mts +1 -0
  762. package/dist/substrate/runtime/supervisor/start-supervisor.mjs +250 -0
  763. package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -0
  764. package/dist/substrate/runtime/supervisor/state.d.mts +1 -0
  765. package/dist/substrate/runtime/supervisor/state.mjs +22 -0
  766. package/dist/substrate/runtime/supervisor/state.mjs.map +1 -0
  767. package/dist/substrate/runtime/supervisor/teardown.mjs +57 -0
  768. package/dist/substrate/runtime/supervisor/teardown.mjs.map +1 -0
  769. package/dist/substrate/runtime/supervisor/types.d.mts +1 -0
  770. package/dist/substrate/runtime/supervisor/wiring.mjs +133 -0
  771. package/dist/substrate/runtime/supervisor/wiring.mjs.map +1 -0
  772. package/dist/substrate/runtime/typed-env.mjs +8 -0
  773. package/dist/substrate/runtime/typed-env.mjs.map +1 -0
  774. package/dist/substrate/state-store.d.mts +1 -0
  775. package/dist/substrate/versioned-doc-schema.mjs +17 -0
  776. package/dist/substrate/versioned-doc-schema.mjs.map +1 -0
  777. package/dist/substrate/versioned-doc-sync.mjs +94 -0
  778. package/dist/substrate/versioned-doc-sync.mjs.map +1 -0
  779. package/dist/surfaces/cli/command-tree.mjs.map +1 -1
  780. package/dist/surfaces/cli/commands/index.mjs +9 -0
  781. package/dist/surfaces/cli/commands/prune-picker-entry.mjs +13 -0
  782. package/dist/surfaces/cli/commands/prune-picker-entry.mjs.map +1 -0
  783. package/dist/surfaces/cli/commands/prune-picker.mjs +3 -2
  784. package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -1
  785. package/dist/surfaces/cli/commands/prune.mjs +34 -17
  786. package/dist/surfaces/cli/commands/prune.mjs.map +1 -1
  787. package/dist/surfaces/cli/commands/status.mjs +2 -2
  788. package/dist/surfaces/cli/commands/status.mjs.map +1 -1
  789. package/dist/surfaces/cli/commands/supervisor-presence.mjs +5 -3
  790. package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -1
  791. package/dist/surfaces/cli/commands/wipe.mjs +38 -4
  792. package/dist/surfaces/cli/commands/wipe.mjs.map +1 -1
  793. package/dist/surfaces/cli/errors.mjs +1 -1
  794. package/dist/surfaces/cli/errors.mjs.map +1 -1
  795. package/dist/surfaces/cli/flags.mjs +0 -1
  796. package/dist/surfaces/cli/flags.mjs.map +1 -1
  797. package/dist/surfaces/cli/index.mjs +98 -18
  798. package/dist/surfaces/cli/index.mjs.map +1 -1
  799. package/dist/surfaces/cli/output.mjs +1 -0
  800. package/dist/surfaces/cli/output.mjs.map +1 -1
  801. package/dist/surfaces/cli/sysexits.mjs +2 -1
  802. package/dist/surfaces/cli/sysexits.mjs.map +1 -1
  803. package/dist/surfaces/tui/app.mjs +25 -3
  804. package/dist/surfaces/tui/app.mjs.map +1 -1
  805. package/dist/surfaces/tui/display-derivation.mjs +23 -74
  806. package/dist/surfaces/tui/display-derivation.mjs.map +1 -1
  807. package/dist/surfaces/tui/event-log.mjs +35 -23
  808. package/dist/surfaces/tui/event-log.mjs.map +1 -1
  809. package/dist/surfaces/tui/input.mjs +5 -1
  810. package/dist/surfaces/tui/input.mjs.map +1 -1
  811. package/dist/surfaces/tui/plain-renderer.mjs +70 -34
  812. package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
  813. package/package.json +24 -4
  814. package/dist/contracts/liveness-classifier.d.mts +0 -19
  815. package/dist/contracts/network-resolver.d.mts +0 -15
  816. package/dist/contracts/network-resolver.mjs +0 -7
  817. package/dist/contracts/network-resolver.mjs.map +0 -1
  818. package/dist/orchestrators/codegen/extras.mjs +0 -16
  819. package/dist/orchestrators/codegen/extras.mjs.map +0 -1
  820. package/dist/plugins/deepbook/routable.mjs +0 -21
  821. package/dist/plugins/deepbook/routable.mjs.map +0 -1
  822. package/dist/plugins/faucet/service.d.mts +0 -20
  823. package/dist/plugins/faucet/strategies/sui-local.d.mts +0 -23
  824. package/dist/plugins/faucet/strategies/sui-local.mjs.map +0 -1
  825. package/dist/plugins/sui/seed-objects.d.mts +0 -18
  826. package/dist/plugins/sui/seed-objects.mjs +0 -25
  827. package/dist/plugins/sui/seed-objects.mjs.map +0 -1
  828. package/dist/runtime/built-in-plugin-layers.mjs.map +0 -1
  829. package/dist/substrate/runtime/context-helpers.mjs +0 -32
  830. package/dist/substrate/runtime/context-helpers.mjs.map +0 -1
  831. package/dist/substrate/runtime/run.mjs.map +0 -1
  832. package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs +0 -30
  833. package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs.map +0 -1
  834. package/dist/substrate/runtime/supervisor.d.mts +0 -1
  835. package/dist/substrate/runtime/supervisor.mjs +0 -831
  836. package/dist/substrate/runtime/supervisor.mjs.map +0 -1
  837. package/dist/surfaces/cli/commands/doctor-probes.mjs.map +0 -1
  838. /package/dist/{substrate/runtime/strategy-registry/faucet-capability-for.d.mts → plugins/faucet/http.d.mts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/substrate/runtime/host-tree-tar/index.ts"],"sourcesContent":["// host-tree-tar — stream a host filesystem subtree as a tar archive.\n//\n// Architecture §\"Substrate primitives roster\" (Open slot O21):\n// snapshot orchestrator hard-fails today on the `tarHostTree` /\n// `untarHostTree` stubs. This primitive fills the slot.\n//\n// Design:\n// - Stream-based — `tarHostTree` returns a `Stream<Uint8Array>`;\n// `untarHostTree` consumes one. The entire tree NEVER materialises\n// in memory (snapshot trees can hold gigabytes of cache state).\n// - Mode-preserving — the snapshot orchestrator carries 0o600 /\n// 0o700 mode bits on secret material (postgres keys, account\n// keystores). System `tar -p` (extract) and `tar --no-recursion`\n// (control) preserve them natively. We do NOT roll our own\n// archive writer (a TypeScript-side reimplementation would have\n// to track POSIX mode flags + extended attributes; system tar is\n// correct by default).\n// - Subprocess-based — spawn `tar` (BSD-tar on macOS, GNU-tar on\n// Linux); both honour the flags used here (`-c` / `-x` / `-f -`).\n// Exit code != 0 surfaces as `TarSpawnFailed`.\n//\n// Boundary:\n// - Substrate-level: this is a generic filesystem primitive; no\n// plugin names referenced.\n// - The caller controls which subtrees are archived (snapshot\n// orchestrator passes a list of relative paths under a parent\n// dir). Permission preservation is unconditional.\n\nimport { spawn } from 'node:child_process';\n\nimport { Effect, Schema, Scope, Stream } from 'effect';\n\nimport {\n\tawaitProcessExit,\n\tdescribeProcessExitStatus,\n\ttype ManagedProcessChild,\n\ttype ManagedProcessExitStatus,\n} from '../process-supervisor.ts';\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/** Tagged failure during tar / untar. `stage` discriminates the\n * precise step (spawn vs stream-read vs exit-code).\n *\n * `cause` carries the underlying error verbatim; the cascade\n * formatter walks it. */\nexport class HostTreeTarError extends Schema.TaggedErrorClass<HostTreeTarError>()(\n\t'HostTreeTarError',\n\t{\n\t\tstage: Schema.Literals([\n\t\t\t'spawn',\n\t\t\t'stream-stdout',\n\t\t\t'stream-stdin',\n\t\t\t'exit-code',\n\t\t\t'entry-validation',\n\t\t\t'no-subtrees',\n\t\t]),\n\t\toperation: Schema.Literals(['tar', 'untar']),\n\t\tdetail: Schema.String,\n\t\t/** Captured stderr from the `tar` subprocess (last ~4 KB). */\n\t\tstderrTail: Schema.optional(Schema.String),\n\t\texitCode: Schema.optional(Schema.Number),\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n// ---------------------------------------------------------------------------\n// Tar one or more subtrees → Stream<Uint8Array>\n// ---------------------------------------------------------------------------\n\n/** Spec for `tarHostTree`.\n *\n * - `parentDir`: directory the relative paths are anchored under.\n * `tar -C <parentDir>` runs the archive with this as cwd, so the\n * archive entries are stored relative to `parentDir`.\n * - `relPaths`: subtree paths relative to `parentDir`. Empty list\n * fails fast (`stage: 'no-subtrees'`) — the caller is responsible\n * for the host-tree-included guard. */\nexport interface TarHostTreeSpec {\n\treadonly parentDir: string;\n\treadonly relPaths: ReadonlyArray<string>;\n}\n\nconst TAR_STDERR_TAIL_BYTES = 4096;\n\nconst collectStderrTail = (chunks: Array<Uint8Array>): string => {\n\tif (chunks.length === 0) return '';\n\tlet total = 0;\n\tfor (let i = chunks.length - 1; i >= 0; i--) {\n\t\ttotal += chunks[i]!.length;\n\t\tif (total >= TAR_STDERR_TAIL_BYTES) break;\n\t}\n\tconst bytes = Buffer.concat(chunks);\n\treturn bytes.subarray(Math.max(0, bytes.length - TAR_STDERR_TAIL_BYTES)).toString('utf8');\n};\n\nconst tarExitError = (\n\toperation: 'tar' | 'untar',\n\tstatus: ManagedProcessExitStatus,\n\tstderrChunks: Array<Uint8Array>,\n): HostTreeTarError =>\n\tnew HostTreeTarError({\n\t\tstage: 'exit-code',\n\t\toperation,\n\t\texitCode: status.code ?? -1,\n\t\tdetail: `'tar' exited with ${describeProcessExitStatus(status)}`,\n\t\tstderrTail: collectStderrTail(stderrChunks),\n\t});\n\nconst TAR_BLOCK_SIZE = 512;\nconst MAX_TAR_EXTENDED_PATH_BYTES = 1024 * 1024;\n\nconst bytesToString = (bytes: Uint8Array): string => {\n\tconst nul = bytes.indexOf(0);\n\tconst end = nul === -1 ? bytes.length : nul;\n\treturn Buffer.from(bytes.subarray(0, end)).toString('utf8');\n};\n\nconst concatBytes = (a: Uint8Array, b: Uint8Array): Uint8Array => {\n\tif (a.length === 0) return b;\n\tif (b.length === 0) return a;\n\tconst out = new Uint8Array(a.length + b.length);\n\tout.set(a, 0);\n\tout.set(b, a.length);\n\treturn out;\n};\n\nconst consumeBytes = (buffer: Uint8Array, count: number): Uint8Array => buffer.subarray(count);\n\nconst isZeroBlock = (block: Uint8Array): boolean => block.every((byte) => byte === 0);\n\nconst parseTarSize = (header: Uint8Array): number | null => {\n\tconst raw = bytesToString(header.subarray(124, 136)).trim();\n\tif (raw === '') return 0;\n\tif (!/^[0-7]+$/.test(raw)) return null;\n\tconst parsed = Number.parseInt(raw, 8);\n\treturn Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null;\n};\n\nconst tarPathFromHeader = (header: Uint8Array): string => {\n\tconst name = bytesToString(header.subarray(0, 100));\n\tconst prefix = bytesToString(header.subarray(345, 500));\n\treturn prefix === '' ? name : `${prefix}/${name}`;\n};\n\nconst tarLinkPathFromHeader = (header: Uint8Array): string =>\n\tbytesToString(header.subarray(157, 257));\n\nconst trimExtendedPath = (bytes: Uint8Array): string => {\n\tconst value = Buffer.from(bytes).toString('utf8').replace(/\\n$/g, '');\n\tlet end = value.length;\n\twhile (end > 0 && value.charCodeAt(end - 1) === 0) end -= 1;\n\treturn value.slice(0, end);\n};\n\nconst isSafeArchivePath = (entryPath: string): boolean => {\n\tif (\n\t\tentryPath === '' ||\n\t\tentryPath === '.' ||\n\t\tentryPath.includes('\\0') ||\n\t\tentryPath.includes('\\\\') ||\n\t\tentryPath.startsWith('/') ||\n\t\t/^[A-Za-z]:/.test(entryPath)\n\t) {\n\t\treturn false;\n\t}\n\tconst meaningfulSegments = entryPath\n\t\t.split('/')\n\t\t.filter((segment) => segment !== '' && segment !== '.');\n\treturn meaningfulSegments.length > 0 && !meaningfulSegments.includes('..');\n};\n\nconst parsePaxRecords = (bytes: Uint8Array): Record<string, string> => {\n\tconst text = Buffer.from(bytes).toString('utf8');\n\tconst records: Record<string, string> = {};\n\tlet offset = 0;\n\twhile (offset < text.length) {\n\t\tconst space = text.indexOf(' ', offset);\n\t\tif (space === -1) break;\n\t\tconst lengthText = text.slice(offset, space);\n\t\tconst recordLength = Number.parseInt(lengthText, 10);\n\t\tif (!Number.isSafeInteger(recordLength) || recordLength <= 0) break;\n\t\tconst record = text.slice(space + 1, offset + recordLength).replace(/\\n$/g, '');\n\t\tconst eq = record.indexOf('=');\n\t\tif (eq > 0) {\n\t\t\trecords[record.slice(0, eq)] = record.slice(eq + 1);\n\t\t}\n\t\toffset += recordLength;\n\t}\n\treturn records;\n};\n\ninterface TarContentState {\n\treadonly kind: 'pax-local' | 'pax-global' | 'gnu-path' | 'gnu-link';\n\treadonly size: number;\n\treadonly paddedSize: number;\n\treadonly chunks: Array<Uint8Array>;\n\treadonly contentBytesRead: number;\n\treadonly totalBytesRead: number;\n}\n\ninterface TarValidationState {\n\tbuffer: Uint8Array;\n\tskipRemaining: number;\n\tcontent: TarContentState | null;\n\tpendingPath: string | null;\n\tpendingLinkPath: string | null;\n}\n\nconst tarValidationError = (detail: string): HostTreeTarError =>\n\tnew HostTreeTarError({\n\t\tstage: 'entry-validation',\n\t\toperation: 'untar',\n\t\tdetail,\n\t});\n\nconst applyExtendedContent = (\n\tstate: TarValidationState,\n\tcontent: TarContentState,\n): HostTreeTarError | null => {\n\tconst bytes =\n\t\tcontent.chunks.length === 1\n\t\t\t? content.chunks[0]!\n\t\t\t: Buffer.concat(content.chunks.map((chunk) => Buffer.from(chunk)));\n\tif (content.kind === 'gnu-path') {\n\t\tstate.pendingPath = trimExtendedPath(bytes);\n\t\treturn null;\n\t}\n\tif (content.kind === 'gnu-link') {\n\t\tstate.pendingLinkPath = trimExtendedPath(bytes);\n\t\treturn null;\n\t}\n\tconst records = parsePaxRecords(bytes);\n\tif (\n\t\tcontent.kind === 'pax-global' &&\n\t\t(records.path !== undefined || records.linkpath !== undefined)\n\t) {\n\t\treturn tarValidationError('global pax path/linkpath records are not supported in snapshots');\n\t}\n\tif (content.kind === 'pax-local') {\n\t\tif (records.path !== undefined) state.pendingPath = records.path;\n\t\tif (records.linkpath !== undefined) state.pendingLinkPath = records.linkpath;\n\t}\n\treturn null;\n};\n\nconst processTarValidationChunk = (\n\tstate: TarValidationState,\n\tchunk: Uint8Array,\n): HostTreeTarError | null => {\n\tstate.buffer = concatBytes(state.buffer, chunk);\n\twhile (state.buffer.length > 0) {\n\t\tif (state.content !== null) {\n\t\t\tconst content = state.content;\n\t\t\tconst remaining = content.paddedSize - content.totalBytesRead;\n\t\t\tconst take = Math.min(remaining, state.buffer.length);\n\t\t\tconst contentTake = Math.max(0, Math.min(take, content.size - content.contentBytesRead));\n\t\t\tif (contentTake > 0) {\n\t\t\t\tcontent.chunks.push(state.buffer.subarray(0, contentTake));\n\t\t\t}\n\t\t\tstate.content = {\n\t\t\t\t...content,\n\t\t\t\tcontentBytesRead: content.contentBytesRead + contentTake,\n\t\t\t\ttotalBytesRead: content.totalBytesRead + take,\n\t\t\t};\n\t\t\tstate.buffer = consumeBytes(state.buffer, take);\n\t\t\tif (state.content.totalBytesRead === state.content.paddedSize) {\n\t\t\t\tconst completed = state.content;\n\t\t\t\tstate.content = null;\n\t\t\t\tconst error = applyExtendedContent(state, completed);\n\t\t\t\tif (error !== null) return error;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (state.skipRemaining > 0) {\n\t\t\tconst take = Math.min(state.skipRemaining, state.buffer.length);\n\t\t\tstate.skipRemaining -= take;\n\t\t\tstate.buffer = consumeBytes(state.buffer, take);\n\t\t\tcontinue;\n\t\t}\n\t\tif (state.buffer.length < TAR_BLOCK_SIZE) return null;\n\t\tconst header = state.buffer.subarray(0, TAR_BLOCK_SIZE);\n\t\tstate.buffer = consumeBytes(state.buffer, TAR_BLOCK_SIZE);\n\t\tif (isZeroBlock(header)) continue;\n\n\t\tconst size = parseTarSize(header);\n\t\tif (size === null) return tarValidationError('tar entry has an invalid size header');\n\t\tconst paddedSize = Math.ceil(size / TAR_BLOCK_SIZE) * TAR_BLOCK_SIZE;\n\t\tconst typeflag = String.fromCharCode(header[156] ?? 0).replace('\\0', '');\n\t\tif (typeflag === 'x' || typeflag === 'g' || typeflag === 'L' || typeflag === 'K') {\n\t\t\tif (size > MAX_TAR_EXTENDED_PATH_BYTES) {\n\t\t\t\treturn tarValidationError('tar extended path record is too large');\n\t\t\t}\n\t\t\tstate.content = {\n\t\t\t\tkind:\n\t\t\t\t\ttypeflag === 'x'\n\t\t\t\t\t\t? 'pax-local'\n\t\t\t\t\t\t: typeflag === 'g'\n\t\t\t\t\t\t\t? 'pax-global'\n\t\t\t\t\t\t\t: typeflag === 'L'\n\t\t\t\t\t\t\t\t? 'gnu-path'\n\t\t\t\t\t\t\t\t: 'gnu-link',\n\t\t\t\tsize,\n\t\t\t\tpaddedSize,\n\t\t\t\tchunks: [],\n\t\t\t\tcontentBytesRead: 0,\n\t\t\t\ttotalBytesRead: 0,\n\t\t\t};\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entryPath = state.pendingPath ?? tarPathFromHeader(header);\n\t\tstate.pendingPath = null;\n\t\tif (!isSafeArchivePath(entryPath)) {\n\t\t\treturn tarValidationError(`unsafe tar entry path: ${entryPath}`);\n\t\t}\n\t\tconst linkPath = state.pendingLinkPath ?? tarLinkPathFromHeader(header);\n\t\tstate.pendingLinkPath = null;\n\t\tif ((typeflag === '1' || typeflag === '2') && !isSafeArchivePath(linkPath)) {\n\t\t\treturn tarValidationError(`unsafe tar link target: ${linkPath}`);\n\t\t}\n\t\tstate.skipRemaining = paddedSize;\n\t}\n\treturn null;\n};\n\nconst finishTarValidation = (state: TarValidationState): HostTreeTarError | null => {\n\tif (state.content !== null) return tarValidationError('tar ended inside an extended header');\n\tif (state.skipRemaining !== 0) return tarValidationError('tar ended inside file content');\n\tif (state.buffer.length !== 0 && !isZeroBlock(state.buffer)) {\n\t\treturn tarValidationError('tar ended with a partial header');\n\t}\n\treturn null;\n};\n\nexport const validateHostTreeTarEntries = <R>(\n\tstream: Stream.Stream<Uint8Array, HostTreeTarError, R>,\n): Effect.Effect<void, HostTreeTarError, R> =>\n\tEffect.gen(function* () {\n\t\tconst state: TarValidationState = {\n\t\t\tbuffer: new Uint8Array(0),\n\t\t\tskipRemaining: 0,\n\t\t\tcontent: null,\n\t\t\tpendingPath: null,\n\t\t\tpendingLinkPath: null,\n\t\t};\n\t\tyield* Stream.runForEach(stream, (chunk) => {\n\t\t\tconst error = processTarValidationChunk(state, chunk);\n\t\t\treturn error === null ? Effect.void : Effect.fail(error);\n\t\t});\n\t\tconst finalError = finishTarValidation(state);\n\t\tif (finalError !== null) return yield* Effect.fail(finalError);\n\t});\n\n/**\n * Stream the tar archive of `relPaths` (relative to `parentDir`) as a\n * `Stream<Uint8Array>`. Mode bits are preserved.\n *\n * The subprocess is bound to the consuming Scope: closing the scope\n * before the stream finishes kills the subprocess and surfaces\n * `stream-stdout` if the consumer was mid-read.\n *\n * Caller consumes via `Stream.run(...)` — e.g. pipe to a file via\n * `Stream.run(stream, Sink.fromWritable(() => createWriteStream(...)))`\n * or to an in-memory chunk array for the integrity hash.\n */\nexport const tarHostTree = (\n\tspec: TarHostTreeSpec,\n): Stream.Stream<Uint8Array, HostTreeTarError, never> =>\n\t// `Stream.unwrap` provides the channel's surrounding scope to the\n\t// inner Effect (see `effect/src/Channel.ts:7918` —\n\t// `Scope.provide(scope)`), so `Effect.addFinalizer` below binds to\n\t// the stream's lifecycle: the subprocess kill-on-close fires when\n\t// the stream terminates. The R-channel `Exclude<R, Scope>` reads\n\t// \"scope satisfied by the stream\" — not \"scope dropped\".\n\tStream.unwrap(\n\t\tEffect.gen(function* () {\n\t\t\tif (spec.relPaths.length === 0) {\n\t\t\t\treturn Stream.fail(\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'no-subtrees',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: 'tarHostTree called with empty relPaths list',\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// `-c` create, `-f -` write to stdout, `-C <dir>` cd-before,\n\t\t\t// `-p` preserve permissions (matches BSD-tar + GNU-tar). The\n\t\t\t// subprocess emits its own bytes; we don't apply any\n\t\t\t// transcoding.\n\t\t\tconst args = ['-c', '-f', '-', '-C', spec.parentDir, '-p', ...spec.relPaths];\n\t\t\tconst child = yield* Effect.try({\n\t\t\t\ttry: () =>\n\t\t\t\t\tspawn('tar', args, {\n\t\t\t\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\t\t\t}),\n\t\t\t\tcatch: (cause) =>\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `failed to spawn 'tar ${args.join(' ')}'`,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t}),\n\t\t\t});\n\n\t\t\t// Stderr collection lives outside the data stream — we\n\t\t\t// accumulate the tail so an exit-code failure carries\n\t\t\t// actionable context. The stderr listener is removed on\n\t\t\t// scope close.\n\t\t\tconst stderrChunks: Array<Uint8Array> = [];\n\t\t\tconst onStderr = (chunk: Buffer): void => {\n\t\t\t\tstderrChunks.push(chunk);\n\t\t\t\tif (stderrChunks.length > 256) stderrChunks.splice(0, stderrChunks.length - 256);\n\t\t\t};\n\t\t\tchild.stderr?.on('data', onStderr);\n\n\t\t\t// Finalise the subprocess on scope close: kill if alive,\n\t\t\t// drop listeners either way.\n\t\t\tyield* Effect.addFinalizer(() =>\n\t\t\t\tEffect.sync(() => {\n\t\t\t\t\tchild.stderr?.off('data', onStderr);\n\t\t\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\t\t\tchild.kill('SIGKILL');\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\t// Wrap stdout (a Node Readable) as a Stream<Uint8Array>.\n\t\t\t// `Stream.fromReadable` is the substrate-blessed shape.\n\t\t\tconst stdout = child.stdout;\n\t\t\tif (stdout === null) {\n\t\t\t\treturn Stream.fail(\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `'tar' subprocess produced no stdout pipe`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Node `Readable` is an `AsyncIterable<Buffer>`; `Buffer`\n\t\t\t// extends `Uint8Array` structurally. The cast asserts the\n\t\t\t// nominal-vs-structural gap at the Node boundary and stays\n\t\t\t// off the user-facing API.\n\t\t\tconst dataStream = Stream.fromAsyncIterable(\n\t\t\t\tstdout as unknown as AsyncIterable<Uint8Array>,\n\t\t\t\t(cause) =>\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'stream-stdout',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `read from tar stdout failed`,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t}),\n\t\t\t);\n\n\t\t\t// After stdout closes, await the subprocess exit and surface\n\t\t\t// non-zero codes with the captured stderr tail. This appends\n\t\t\t// a \"tail\" sub-stream that emits zero bytes but can fail —\n\t\t\t// the consumer's `Stream.run` sees the exit-code failure\n\t\t\t// uniformly with stream-read failures.\n\t\t\tconst exitGate: Stream.Stream<Uint8Array, HostTreeTarError, never> = Stream.fromEffect(\n\t\t\t\tEffect.promise(() => awaitProcessExit(child as ManagedProcessChild)).pipe(\n\t\t\t\t\tEffect.flatMap((status) =>\n\t\t\t\t\t\tstatus.code === 0 && status.signal === null\n\t\t\t\t\t\t\t? Effect.succeed(new Uint8Array(0))\n\t\t\t\t\t\t\t: Effect.fail(tarExitError('tar', status, stderrChunks)),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t).pipe(Stream.filter((bytes) => bytes.length > 0));\n\n\t\t\treturn Stream.concat(dataStream, exitGate);\n\t\t}),\n\t);\n\n// ---------------------------------------------------------------------------\n// Untar a Stream<Uint8Array> into a target directory\n// ---------------------------------------------------------------------------\n\nexport interface UntarHostTreeSpec {\n\treadonly target: string;\n}\n\n/**\n * Extract a tar `Stream<Uint8Array>` into `target`, preserving mode\n * bits. The target directory MUST already exist; the caller is\n * responsible (stage-and-swap creates the staging directory before\n * calling).\n *\n * The subprocess consumes from the stream and writes into `target`;\n * scope close kills the subprocess. The Effect resolves when `tar`\n * exits cleanly (code 0).\n */\nexport const untarHostTree = <R>(\n\tstream: Stream.Stream<Uint8Array, HostTreeTarError, R>,\n\tspec: UntarHostTreeSpec,\n): Effect.Effect<void, HostTreeTarError, R | Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tconst args = ['-x', '-f', '-', '-C', spec.target, '-p'];\n\t\tconst child = yield* Effect.try({\n\t\t\ttry: () =>\n\t\t\t\tspawn('tar', args, {\n\t\t\t\t\tstdio: ['pipe', 'ignore', 'pipe'],\n\t\t\t\t}),\n\t\t\tcatch: (cause) =>\n\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\toperation: 'untar',\n\t\t\t\t\tdetail: `failed to spawn 'tar ${args.join(' ')}'`,\n\t\t\t\t\tcause,\n\t\t\t\t}),\n\t\t});\n\n\t\tconst stderrChunks: Array<Uint8Array> = [];\n\t\tconst onStderr = (chunk: Buffer): void => {\n\t\t\tstderrChunks.push(chunk);\n\t\t\tif (stderrChunks.length > 256) stderrChunks.splice(0, stderrChunks.length - 256);\n\t\t};\n\t\tchild.stderr?.on('data', onStderr);\n\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tEffect.sync(() => {\n\t\t\t\tchild.stderr?.off('data', onStderr);\n\t\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\t\tchild.kill('SIGKILL');\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\tconst stdin = child.stdin;\n\t\tif (stdin === null) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\toperation: 'untar',\n\t\t\t\t\tdetail: `'tar' subprocess produced no stdin pipe`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst validationState: TarValidationState = {\n\t\t\tbuffer: new Uint8Array(0),\n\t\t\tskipRemaining: 0,\n\t\t\tcontent: null,\n\t\t\tpendingPath: null,\n\t\t\tpendingLinkPath: null,\n\t\t};\n\n\t\tconst writeChunk = (chunk: Uint8Array): Effect.Effect<void, HostTreeTarError> =>\n\t\t\tEffect.callback<void, HostTreeTarError>((resume) => {\n\t\t\t\tlet settled = false;\n\t\t\t\tconst settle = (effect: Effect.Effect<void, HostTreeTarError>): void => {\n\t\t\t\t\tif (!settled) {\n\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\tresume(effect);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst onDrain = (): void => settle(Effect.void);\n\t\t\t\tconst ok = stdin.write(chunk, (cause) => {\n\t\t\t\t\tstdin.off('drain', onDrain);\n\t\t\t\t\tif (cause) {\n\t\t\t\t\t\tsettle(\n\t\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\t\t\t\tstage: 'stream-stdin',\n\t\t\t\t\t\t\t\t\toperation: 'untar',\n\t\t\t\t\t\t\t\t\tdetail: `write to tar stdin failed`,\n\t\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsettle(Effect.void);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (!ok) {\n\t\t\t\t\tstdin.once('drain', onDrain);\n\t\t\t\t}\n\t\t\t});\n\n\t\t// Pump the source stream into tar's stdin. Each chunk is\n\t\t// validated before it is handed to the tar subprocess, so unsafe\n\t\t// archive paths are rejected at the extraction boundary even if a\n\t\t// caller skipped any earlier preflight validation.\n\t\tyield* Stream.runForEach(stream, (chunk) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst validationError = processTarValidationChunk(validationState, chunk);\n\t\t\t\tif (validationError !== null) {\n\t\t\t\t\treturn yield* Effect.fail(validationError);\n\t\t\t\t}\n\t\t\t\tyield* writeChunk(chunk);\n\t\t\t}),\n\t\t).pipe(\n\t\t\tEffect.catch((cause) =>\n\t\t\t\tcause instanceof HostTreeTarError\n\t\t\t\t\t? Effect.fail(cause)\n\t\t\t\t\t: Effect.fail(\n\t\t\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\t\t\tstage: 'stream-stdin',\n\t\t\t\t\t\t\t\toperation: 'untar',\n\t\t\t\t\t\t\t\tdetail: `source stream raised before EOF`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t),\n\t\t);\n\n\t\tconst finalValidationError = finishTarValidation(validationState);\n\t\tif (finalValidationError !== null) {\n\t\t\treturn yield* Effect.fail(finalValidationError);\n\t\t}\n\n\t\t// Close stdin so tar sees EOF and exits.\n\t\tyield* Effect.sync(() => stdin.end());\n\n\t\t// Await exit; non-zero surfaces with stderr tail.\n\t\tconst status = yield* Effect.promise(() => awaitProcessExit(child as ManagedProcessChild));\n\t\tif (status.code !== 0 || status.signal !== null) {\n\t\t\treturn yield* Effect.fail(tarExitError('untar', status, stderrChunks));\n\t\t}\n\t}).pipe(Effect.withSpan('substrate.host-tree-tar.untar'));\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/substrate/runtime/host-tree-tar/index.ts"],"sourcesContent":["// host-tree-tar — stream a host filesystem subtree as a tar archive.\n//\n// Architecture §\"Substrate primitives roster\" (Open slot O21):\n// snapshot orchestrator hard-fails today on the `tarHostTree` /\n// `untarHostTree` stubs. This primitive fills the slot.\n//\n// Design:\n// - Stream-based — `tarHostTree` returns a `Stream<Uint8Array>`;\n// `untarHostTree` consumes one. The entire tree NEVER materialises\n// in memory (snapshot trees can hold gigabytes of cache state).\n// - Mode-preserving — the snapshot orchestrator carries 0o600 /\n// 0o700 mode bits on secret material (postgres keys, account\n// keystores). System `tar -p` (extract) and `tar --no-recursion`\n// (control) preserve them natively. We do NOT roll our own\n// archive writer (a TypeScript-side reimplementation would have\n// to track POSIX mode flags + extended attributes; system tar is\n// correct by default).\n// - Subprocess-based — spawn `tar` (BSD-tar on macOS, GNU-tar on\n// Linux); both honour the flags used here (`-c` / `-x` / `-f -`).\n// Exit code != 0 surfaces as `TarSpawnFailed`.\n//\n// Boundary:\n// - Substrate-level: this is a generic filesystem primitive; no\n// plugin names referenced.\n// - The caller controls which subtrees are archived (snapshot\n// orchestrator passes a list of relative paths under a parent\n// dir). Permission preservation is unconditional.\n\nimport { spawn, type ChildProcess, type StdioOptions } from 'node:child_process';\n\nimport { Effect, Schema, Scope, Stream } from 'effect';\n\nimport {\n\tawaitProcessExit,\n\tdescribeProcessExitStatus,\n\ttype ManagedProcessChild,\n\ttype ManagedProcessExitStatus,\n} from '../process-supervisor.ts';\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/** Tagged failure during tar / untar. `stage` discriminates the\n * precise step (spawn vs stream-read vs exit-code).\n *\n * `cause` carries the underlying error verbatim; the cascade\n * formatter walks it. */\nexport class HostTreeTarError extends Schema.TaggedErrorClass<HostTreeTarError>()(\n\t'HostTreeTarError',\n\t{\n\t\tstage: Schema.Literals([\n\t\t\t'spawn',\n\t\t\t'stream-stdout',\n\t\t\t'stream-stdin',\n\t\t\t'exit-code',\n\t\t\t'entry-validation',\n\t\t\t'no-subtrees',\n\t\t]),\n\t\toperation: Schema.Literals(['tar', 'untar']),\n\t\tdetail: Schema.String,\n\t\t/** Captured stderr from the `tar` subprocess (last ~4 KB). */\n\t\tstderrTail: Schema.optional(Schema.String),\n\t\texitCode: Schema.optional(Schema.Number),\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n// ---------------------------------------------------------------------------\n// Tar one or more subtrees → Stream<Uint8Array>\n// ---------------------------------------------------------------------------\n\n/** Spec for `tarHostTree`.\n *\n * - `parentDir`: directory the relative paths are anchored under.\n * `tar -C <parentDir>` runs the archive with this as cwd, so the\n * archive entries are stored relative to `parentDir`.\n * - `relPaths`: subtree paths relative to `parentDir`. Empty list\n * fails fast (`stage: 'no-subtrees'`) — the caller is responsible\n * for the host-tree-included guard. */\nexport interface TarHostTreeSpec {\n\treadonly parentDir: string;\n\treadonly relPaths: ReadonlyArray<string>;\n}\n\nconst TAR_STDERR_TAIL_BYTES = 4096;\n\nconst collectStderrTail = (chunks: Array<Uint8Array>): string => {\n\tif (chunks.length === 0) return '';\n\tlet total = 0;\n\tfor (let i = chunks.length - 1; i >= 0; i--) {\n\t\ttotal += chunks[i]!.length;\n\t\tif (total >= TAR_STDERR_TAIL_BYTES) break;\n\t}\n\tconst bytes = Buffer.concat(chunks);\n\treturn bytes.subarray(Math.max(0, bytes.length - TAR_STDERR_TAIL_BYTES)).toString('utf8');\n};\n\nconst tarExitError = (\n\toperation: 'tar' | 'untar',\n\tstatus: ManagedProcessExitStatus,\n\tstderrChunks: Array<Uint8Array>,\n): HostTreeTarError =>\n\tnew HostTreeTarError({\n\t\tstage: 'exit-code',\n\t\toperation,\n\t\texitCode: status.code ?? -1,\n\t\tdetail: `'tar' exited with ${describeProcessExitStatus(status)}`,\n\t\tstderrTail: collectStderrTail(stderrChunks),\n\t});\n\n/** Spawn `tar` with a bounded stderr ring buffer and a SIGKILL-on-\n * scope-close finalizer. Both `tarHostTree` and `untarHostTree` share\n * this body verbatim — only the args, stdio direction (which pipe is\n * the data channel), and the `operation` tag differ. The data pipe\n * (`stdout` for tar, `stdin` for untar) is read off the returned\n * `child` by the caller; stderr is always piped so the exit-code\n * failure can carry the captured tail.\n *\n * The stderr listener is removed and a live child is killed on scope\n * close — the finalizer binds to the surrounding Scope (the stream's\n * lifecycle for tar, the consuming Effect's for untar). */\nconst spawnTar = (\n\toperation: 'tar' | 'untar',\n\targs: ReadonlyArray<string>,\n\tstdio: StdioOptions,\n): Effect.Effect<\n\t{ readonly child: ChildProcess; readonly stderrChunks: Array<Uint8Array> },\n\tHostTreeTarError,\n\tScope.Scope\n> =>\n\tEffect.gen(function* () {\n\t\tconst child = yield* Effect.try({\n\t\t\ttry: () => spawn('tar', [...args], { stdio }),\n\t\t\tcatch: (cause) =>\n\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\toperation,\n\t\t\t\t\tdetail: `failed to spawn 'tar ${args.join(' ')}'`,\n\t\t\t\t\tcause,\n\t\t\t\t}),\n\t\t});\n\n\t\t// Stderr collection lives outside the data stream — we accumulate\n\t\t// the tail so an exit-code failure carries actionable context. The\n\t\t// stderr listener is removed on scope close.\n\t\tconst stderrChunks: Array<Uint8Array> = [];\n\t\tconst onStderr = (chunk: Buffer): void => {\n\t\t\tstderrChunks.push(chunk);\n\t\t\tif (stderrChunks.length > 256) stderrChunks.splice(0, stderrChunks.length - 256);\n\t\t};\n\t\tchild.stderr?.on('data', onStderr);\n\n\t\t// Finalise the subprocess on scope close: kill if alive, drop\n\t\t// listeners either way.\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tEffect.sync(() => {\n\t\t\t\tchild.stderr?.off('data', onStderr);\n\t\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\t\tchild.kill('SIGKILL');\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\treturn { child, stderrChunks };\n\t});\n\nconst TAR_BLOCK_SIZE = 512;\nconst MAX_TAR_EXTENDED_PATH_BYTES = 1024 * 1024;\n\nconst bytesToString = (bytes: Uint8Array): string => {\n\tconst nul = bytes.indexOf(0);\n\tconst end = nul === -1 ? bytes.length : nul;\n\treturn Buffer.from(bytes.subarray(0, end)).toString('utf8');\n};\n\nconst concatBytes = (a: Uint8Array, b: Uint8Array): Uint8Array => {\n\tif (a.length === 0) return b;\n\tif (b.length === 0) return a;\n\tconst out = new Uint8Array(a.length + b.length);\n\tout.set(a, 0);\n\tout.set(b, a.length);\n\treturn out;\n};\n\nconst consumeBytes = (buffer: Uint8Array, count: number): Uint8Array => buffer.subarray(count);\n\nconst isZeroBlock = (block: Uint8Array): boolean => block.every((byte) => byte === 0);\n\nconst parseTarSize = (header: Uint8Array): number | null => {\n\tconst raw = bytesToString(header.subarray(124, 136)).trim();\n\tif (raw === '') return 0;\n\tif (!/^[0-7]+$/.test(raw)) return null;\n\tconst parsed = Number.parseInt(raw, 8);\n\treturn Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null;\n};\n\nconst tarPathFromHeader = (header: Uint8Array): string => {\n\tconst name = bytesToString(header.subarray(0, 100));\n\tconst prefix = bytesToString(header.subarray(345, 500));\n\treturn prefix === '' ? name : `${prefix}/${name}`;\n};\n\nconst tarLinkPathFromHeader = (header: Uint8Array): string =>\n\tbytesToString(header.subarray(157, 257));\n\nconst trimExtendedPath = (bytes: Uint8Array): string => {\n\tconst value = Buffer.from(bytes).toString('utf8').replace(/\\n$/g, '');\n\tlet end = value.length;\n\twhile (end > 0 && value.charCodeAt(end - 1) === 0) end -= 1;\n\treturn value.slice(0, end);\n};\n\nconst isSafeArchivePath = (entryPath: string): boolean => {\n\tif (\n\t\tentryPath === '' ||\n\t\tentryPath === '.' ||\n\t\tentryPath.includes('\\0') ||\n\t\tentryPath.includes('\\\\') ||\n\t\tentryPath.startsWith('/') ||\n\t\t/^[A-Za-z]:/.test(entryPath)\n\t) {\n\t\treturn false;\n\t}\n\tconst meaningfulSegments = entryPath\n\t\t.split('/')\n\t\t.filter((segment) => segment !== '' && segment !== '.');\n\treturn meaningfulSegments.length > 0 && !meaningfulSegments.includes('..');\n};\n\nconst parsePaxRecords = (bytes: Uint8Array): Record<string, string> => {\n\tconst text = Buffer.from(bytes).toString('utf8');\n\tconst records: Record<string, string> = {};\n\tlet offset = 0;\n\twhile (offset < text.length) {\n\t\tconst space = text.indexOf(' ', offset);\n\t\tif (space === -1) break;\n\t\tconst lengthText = text.slice(offset, space);\n\t\tconst recordLength = Number.parseInt(lengthText, 10);\n\t\tif (!Number.isSafeInteger(recordLength) || recordLength <= 0) break;\n\t\tconst record = text.slice(space + 1, offset + recordLength).replace(/\\n$/g, '');\n\t\tconst eq = record.indexOf('=');\n\t\tif (eq > 0) {\n\t\t\trecords[record.slice(0, eq)] = record.slice(eq + 1);\n\t\t}\n\t\toffset += recordLength;\n\t}\n\treturn records;\n};\n\ninterface TarContentState {\n\treadonly kind: 'pax-local' | 'pax-global' | 'gnu-path' | 'gnu-link';\n\treadonly size: number;\n\treadonly paddedSize: number;\n\treadonly chunks: Array<Uint8Array>;\n\treadonly contentBytesRead: number;\n\treadonly totalBytesRead: number;\n}\n\ninterface TarValidationState {\n\tbuffer: Uint8Array;\n\tskipRemaining: number;\n\tcontent: TarContentState | null;\n\tpendingPath: string | null;\n\tpendingLinkPath: string | null;\n}\n\nconst tarValidationError = (detail: string): HostTreeTarError =>\n\tnew HostTreeTarError({\n\t\tstage: 'entry-validation',\n\t\toperation: 'untar',\n\t\tdetail,\n\t});\n\nconst applyExtendedContent = (\n\tstate: TarValidationState,\n\tcontent: TarContentState,\n): HostTreeTarError | null => {\n\tconst bytes =\n\t\tcontent.chunks.length === 1\n\t\t\t? content.chunks[0]!\n\t\t\t: Buffer.concat(content.chunks.map((chunk) => Buffer.from(chunk)));\n\tif (content.kind === 'gnu-path') {\n\t\tstate.pendingPath = trimExtendedPath(bytes);\n\t\treturn null;\n\t}\n\tif (content.kind === 'gnu-link') {\n\t\tstate.pendingLinkPath = trimExtendedPath(bytes);\n\t\treturn null;\n\t}\n\tconst records = parsePaxRecords(bytes);\n\tif (\n\t\tcontent.kind === 'pax-global' &&\n\t\t(records.path !== undefined || records.linkpath !== undefined)\n\t) {\n\t\treturn tarValidationError('global pax path/linkpath records are not supported in snapshots');\n\t}\n\tif (content.kind === 'pax-local') {\n\t\tif (records.path !== undefined) state.pendingPath = records.path;\n\t\tif (records.linkpath !== undefined) state.pendingLinkPath = records.linkpath;\n\t}\n\treturn null;\n};\n\nconst processTarValidationChunk = (\n\tstate: TarValidationState,\n\tchunk: Uint8Array,\n): HostTreeTarError | null => {\n\t// Fast path: we're mid-skip over a large file body and this chunk is\n\t// fully consumed by the skip. Avoid `concatBytes` (O(N) per call →\n\t// O(N²) across a multi-MB body streamed in 64KB chunks). The buffer\n\t// must be empty so we don't re-order bytes relative to the parser.\n\tif (\n\t\tstate.content === null &&\n\t\tstate.skipRemaining >= chunk.length &&\n\t\tstate.buffer.length === 0\n\t) {\n\t\tstate.skipRemaining -= chunk.length;\n\t\treturn null;\n\t}\n\tstate.buffer = concatBytes(state.buffer, chunk);\n\twhile (state.buffer.length > 0) {\n\t\tif (state.content !== null) {\n\t\t\tconst content = state.content;\n\t\t\tconst remaining = content.paddedSize - content.totalBytesRead;\n\t\t\tconst take = Math.min(remaining, state.buffer.length);\n\t\t\tconst contentTake = Math.max(0, Math.min(take, content.size - content.contentBytesRead));\n\t\t\tif (contentTake > 0) {\n\t\t\t\tcontent.chunks.push(state.buffer.subarray(0, contentTake));\n\t\t\t}\n\t\t\tstate.content = {\n\t\t\t\t...content,\n\t\t\t\tcontentBytesRead: content.contentBytesRead + contentTake,\n\t\t\t\ttotalBytesRead: content.totalBytesRead + take,\n\t\t\t};\n\t\t\tstate.buffer = consumeBytes(state.buffer, take);\n\t\t\tif (state.content.totalBytesRead === state.content.paddedSize) {\n\t\t\t\tconst completed = state.content;\n\t\t\t\tstate.content = null;\n\t\t\t\tconst error = applyExtendedContent(state, completed);\n\t\t\t\tif (error !== null) return error;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (state.skipRemaining > 0) {\n\t\t\tconst take = Math.min(state.skipRemaining, state.buffer.length);\n\t\t\tstate.skipRemaining -= take;\n\t\t\tstate.buffer = consumeBytes(state.buffer, take);\n\t\t\tcontinue;\n\t\t}\n\t\tif (state.buffer.length < TAR_BLOCK_SIZE) return null;\n\t\tconst header = state.buffer.subarray(0, TAR_BLOCK_SIZE);\n\t\tstate.buffer = consumeBytes(state.buffer, TAR_BLOCK_SIZE);\n\t\tif (isZeroBlock(header)) continue;\n\n\t\tconst size = parseTarSize(header);\n\t\tif (size === null) return tarValidationError('tar entry has an invalid size header');\n\t\tconst paddedSize = Math.ceil(size / TAR_BLOCK_SIZE) * TAR_BLOCK_SIZE;\n\t\tconst typeflag = String.fromCharCode(header[156] ?? 0).replace('\\0', '');\n\t\tif (typeflag === 'x' || typeflag === 'g' || typeflag === 'L' || typeflag === 'K') {\n\t\t\tif (size > MAX_TAR_EXTENDED_PATH_BYTES) {\n\t\t\t\treturn tarValidationError('tar extended path record is too large');\n\t\t\t}\n\t\t\tstate.content = {\n\t\t\t\tkind:\n\t\t\t\t\ttypeflag === 'x'\n\t\t\t\t\t\t? 'pax-local'\n\t\t\t\t\t\t: typeflag === 'g'\n\t\t\t\t\t\t\t? 'pax-global'\n\t\t\t\t\t\t\t: typeflag === 'L'\n\t\t\t\t\t\t\t\t? 'gnu-path'\n\t\t\t\t\t\t\t\t: 'gnu-link',\n\t\t\t\tsize,\n\t\t\t\tpaddedSize,\n\t\t\t\tchunks: [],\n\t\t\t\tcontentBytesRead: 0,\n\t\t\t\ttotalBytesRead: 0,\n\t\t\t};\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entryPath = state.pendingPath ?? tarPathFromHeader(header);\n\t\tstate.pendingPath = null;\n\t\tif (!isSafeArchivePath(entryPath)) {\n\t\t\treturn tarValidationError(`unsafe tar entry path: ${entryPath}`);\n\t\t}\n\t\tconst linkPath = state.pendingLinkPath ?? tarLinkPathFromHeader(header);\n\t\tstate.pendingLinkPath = null;\n\t\tif ((typeflag === '1' || typeflag === '2') && !isSafeArchivePath(linkPath)) {\n\t\t\treturn tarValidationError(`unsafe tar link target: ${linkPath}`);\n\t\t}\n\t\tstate.skipRemaining = paddedSize;\n\t}\n\treturn null;\n};\n\nconst finishTarValidation = (state: TarValidationState): HostTreeTarError | null => {\n\tif (state.content !== null) return tarValidationError('tar ended inside an extended header');\n\tif (state.skipRemaining !== 0) return tarValidationError('tar ended inside file content');\n\tif (state.buffer.length !== 0 && !isZeroBlock(state.buffer)) {\n\t\treturn tarValidationError('tar ended with a partial header');\n\t}\n\treturn null;\n};\n\nexport const validateHostTreeTarEntries = <R>(\n\tstream: Stream.Stream<Uint8Array, HostTreeTarError, R>,\n): Effect.Effect<void, HostTreeTarError, R> =>\n\tEffect.gen(function* () {\n\t\tconst state: TarValidationState = {\n\t\t\tbuffer: new Uint8Array(0),\n\t\t\tskipRemaining: 0,\n\t\t\tcontent: null,\n\t\t\tpendingPath: null,\n\t\t\tpendingLinkPath: null,\n\t\t};\n\t\tyield* Stream.runForEach(stream, (chunk) => {\n\t\t\tconst error = processTarValidationChunk(state, chunk);\n\t\t\treturn error === null ? Effect.void : Effect.fail(error);\n\t\t});\n\t\tconst finalError = finishTarValidation(state);\n\t\tif (finalError !== null) return yield* Effect.fail(finalError);\n\t});\n\n/**\n * Stream the tar archive of `relPaths` (relative to `parentDir`) as a\n * `Stream<Uint8Array>`. Mode bits are preserved.\n *\n * The subprocess is bound to the consuming Scope: closing the scope\n * before the stream finishes kills the subprocess and surfaces\n * `stream-stdout` if the consumer was mid-read.\n *\n * Caller consumes via `Stream.run(...)` — e.g. pipe to a file via\n * `Stream.run(stream, Sink.fromWritable(() => createWriteStream(...)))`\n * or to an in-memory chunk array for the integrity hash.\n */\nexport const tarHostTree = (\n\tspec: TarHostTreeSpec,\n): Stream.Stream<Uint8Array, HostTreeTarError, never> =>\n\t// `Stream.unwrap` provides the channel's surrounding scope to the\n\t// inner Effect (see `effect/src/Channel.ts:7918` —\n\t// `Scope.provide(scope)`), so `Effect.addFinalizer` below binds to\n\t// the stream's lifecycle: the subprocess kill-on-close fires when\n\t// the stream terminates. The R-channel `Exclude<R, Scope>` reads\n\t// \"scope satisfied by the stream\" — not \"scope dropped\".\n\tStream.unwrap(\n\t\tEffect.gen(function* () {\n\t\t\tif (spec.relPaths.length === 0) {\n\t\t\t\treturn Stream.fail(\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'no-subtrees',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: 'tarHostTree called with empty relPaths list',\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Producer-boundary path defense — mirror the extraction-side\n\t\t\t// `isSafeArchivePath`. The caller controls `relPaths`, and they\n\t\t\t// are spliced straight into `tar -C <parentDir> … relPaths`. An\n\t\t\t// absolute path or a `..` segment would archive a subtree\n\t\t\t// outside `parentDir` (and on extraction escape `target`). We\n\t\t\t// reject at the producer rather than relying on the consumer's\n\t\t\t// entry validation alone.\n\t\t\tconst unsafe = spec.relPaths.find((relPath) => !isSafeArchivePath(relPath));\n\t\t\tif (unsafe !== undefined) {\n\t\t\t\treturn Stream.fail(\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'entry-validation',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `unsafe tar relPath (absolute or contains '..'): ${unsafe}`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// `-c` create, `-f -` write to stdout, `-C <dir>` cd-before,\n\t\t\t// `-p` preserve permissions (matches BSD-tar + GNU-tar). The\n\t\t\t// subprocess emits its own bytes; we don't apply any\n\t\t\t// transcoding.\n\t\t\tconst args = ['-c', '-f', '-', '-C', spec.parentDir, '-p', ...spec.relPaths];\n\t\t\tconst { child, stderrChunks } = yield* spawnTar('tar', args, ['ignore', 'pipe', 'pipe']);\n\n\t\t\t// Wrap stdout (a Node Readable) as a Stream<Uint8Array>.\n\t\t\t// `Stream.fromReadable` is the substrate-blessed shape.\n\t\t\tconst stdout = child.stdout;\n\t\t\tif (stdout === null) {\n\t\t\t\treturn Stream.fail(\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `'tar' subprocess produced no stdout pipe`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Node `Readable` is an `AsyncIterable<Buffer>`; `Buffer`\n\t\t\t// extends `Uint8Array` structurally. The cast asserts the\n\t\t\t// nominal-vs-structural gap at the Node boundary and stays\n\t\t\t// off the user-facing API.\n\t\t\tconst dataStream = Stream.fromAsyncIterable(\n\t\t\t\tstdout as unknown as AsyncIterable<Uint8Array>,\n\t\t\t\t(cause) =>\n\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\tstage: 'stream-stdout',\n\t\t\t\t\t\toperation: 'tar',\n\t\t\t\t\t\tdetail: `read from tar stdout failed`,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t}),\n\t\t\t);\n\n\t\t\t// After stdout closes, await the subprocess exit and surface\n\t\t\t// non-zero codes with the captured stderr tail. This appends\n\t\t\t// a \"tail\" sub-stream that emits zero bytes but can fail —\n\t\t\t// the consumer's `Stream.run` sees the exit-code failure\n\t\t\t// uniformly with stream-read failures.\n\t\t\tconst exitGate: Stream.Stream<Uint8Array, HostTreeTarError, never> = Stream.fromEffect(\n\t\t\t\tEffect.promise(() => awaitProcessExit(child as ManagedProcessChild)).pipe(\n\t\t\t\t\tEffect.flatMap((status) =>\n\t\t\t\t\t\tstatus.code === 0 && status.signal === null\n\t\t\t\t\t\t\t? Effect.succeed(new Uint8Array(0))\n\t\t\t\t\t\t\t: Effect.fail(tarExitError('tar', status, stderrChunks)),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t).pipe(Stream.filter((bytes) => bytes.length > 0));\n\n\t\t\treturn Stream.concat(dataStream, exitGate);\n\t\t}),\n\t);\n\n// ---------------------------------------------------------------------------\n// Untar a Stream<Uint8Array> into a target directory\n// ---------------------------------------------------------------------------\n\nexport interface UntarHostTreeSpec {\n\treadonly target: string;\n}\n\n/**\n * Extract a tar `Stream<Uint8Array>` into `target`, preserving mode\n * bits. The target directory MUST already exist; the caller is\n * responsible (stage-and-swap creates the staging directory before\n * calling).\n *\n * The subprocess consumes from the stream and writes into `target`;\n * scope close kills the subprocess. The Effect resolves when `tar`\n * exits cleanly (code 0).\n */\nexport const untarHostTree = <R>(\n\tstream: Stream.Stream<Uint8Array, HostTreeTarError, R>,\n\tspec: UntarHostTreeSpec,\n): Effect.Effect<void, HostTreeTarError, R | Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tconst args = ['-x', '-f', '-', '-C', spec.target, '-p'];\n\t\tconst { child, stderrChunks } = yield* spawnTar('untar', args, ['pipe', 'ignore', 'pipe']);\n\n\t\tconst stdin = child.stdin;\n\t\tif (stdin === null) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\tstage: 'spawn',\n\t\t\t\t\toperation: 'untar',\n\t\t\t\t\tdetail: `'tar' subprocess produced no stdin pipe`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst validationState: TarValidationState = {\n\t\t\tbuffer: new Uint8Array(0),\n\t\t\tskipRemaining: 0,\n\t\t\tcontent: null,\n\t\t\tpendingPath: null,\n\t\t\tpendingLinkPath: null,\n\t\t};\n\n\t\tconst writeChunk = (chunk: Uint8Array): Effect.Effect<void, HostTreeTarError> =>\n\t\t\tEffect.callback<void, HostTreeTarError>((resume) => {\n\t\t\t\tlet settled = false;\n\t\t\t\tconst settle = (effect: Effect.Effect<void, HostTreeTarError>): void => {\n\t\t\t\t\tif (!settled) {\n\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\tresume(effect);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst onDrain = (): void => settle(Effect.void);\n\t\t\t\tconst ok = stdin.write(chunk, (cause) => {\n\t\t\t\t\tstdin.off('drain', onDrain);\n\t\t\t\t\tif (cause) {\n\t\t\t\t\t\tsettle(\n\t\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\t\t\t\tstage: 'stream-stdin',\n\t\t\t\t\t\t\t\t\toperation: 'untar',\n\t\t\t\t\t\t\t\t\tdetail: `write to tar stdin failed`,\n\t\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsettle(Effect.void);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (!ok) {\n\t\t\t\t\tstdin.once('drain', onDrain);\n\t\t\t\t}\n\t\t\t\t// Interrupt while parked on backpressure must remove the\n\t\t\t\t// drain listener so it can't resume an interrupted fiber.\n\t\t\t\treturn Effect.sync(() => {\n\t\t\t\t\tstdin.off('drain', onDrain);\n\t\t\t\t});\n\t\t\t});\n\n\t\t// Pump the source stream into tar's stdin. Each chunk is\n\t\t// validated before it is handed to the tar subprocess, so unsafe\n\t\t// archive paths are rejected at the extraction boundary even if a\n\t\t// caller skipped any earlier preflight validation.\n\t\tyield* Stream.runForEach(stream, (chunk) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst validationError = processTarValidationChunk(validationState, chunk);\n\t\t\t\tif (validationError !== null) {\n\t\t\t\t\treturn yield* Effect.fail(validationError);\n\t\t\t\t}\n\t\t\t\tyield* writeChunk(chunk);\n\t\t\t}),\n\t\t).pipe(\n\t\t\tEffect.catch((cause) =>\n\t\t\t\tcause instanceof HostTreeTarError\n\t\t\t\t\t? Effect.fail(cause)\n\t\t\t\t\t: Effect.fail(\n\t\t\t\t\t\t\tnew HostTreeTarError({\n\t\t\t\t\t\t\t\tstage: 'stream-stdin',\n\t\t\t\t\t\t\t\toperation: 'untar',\n\t\t\t\t\t\t\t\tdetail: `source stream raised before EOF`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t),\n\t\t);\n\n\t\tconst finalValidationError = finishTarValidation(validationState);\n\t\tif (finalValidationError !== null) {\n\t\t\treturn yield* Effect.fail(finalValidationError);\n\t\t}\n\n\t\t// Close stdin so tar sees EOF and exits.\n\t\tyield* Effect.sync(() => stdin.end());\n\n\t\t// Await exit; non-zero surfaces with stderr tail.\n\t\tconst status = yield* Effect.promise(() => awaitProcessExit(child as ManagedProcessChild));\n\t\tif (status.code !== 0 || status.signal !== null) {\n\t\t\treturn yield* Effect.fail(tarExitError('untar', status, stderrChunks));\n\t\t}\n\t}).pipe(Effect.withSpan('substrate.host-tree-tar.untar'));\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { Effect } from "effect";
2
- import * as _$effect_Types0 from "effect/Types";
3
2
  import * as _$effect_Cause0 from "effect/Cause";
3
+ import * as _$effect_Types0 from "effect/Types";
4
4
 
5
5
  //#region src/substrate/runtime/http-probe.d.ts
6
6
  declare namespace http_probe_d_exports {
@@ -0,0 +1,14 @@
1
+ import "./capability-sinks/service.mjs";
2
+ import "./observability/formatter-registry.mjs";
3
+ import "./capability-sinks/layer.mjs";
4
+ import "./capability-sinks/index.mjs";
5
+ import "./projection/state-ref.mjs";
6
+ import "./projection/persisted.mjs";
7
+ import "./cross-process/command-channel/channel.mjs";
8
+ import "./cross-process/command-channel/index.mjs";
9
+ import "./cross-process/roster.mjs";
10
+ import "./observability/logger.mjs";
11
+ import "./observability/index.mjs";
12
+ import "./supervisor/start-supervisor.mjs";
13
+ import "./supervisor/index.mjs";
14
+ export {};
@@ -1 +1 @@
1
- import { Lease, LeaseBroker, LeaseKey, Owner } from "./service.mjs";
1
+ export { };
@@ -0,0 +1,2 @@
1
+ import "./service.mjs";
2
+ export {};
@@ -1,64 +1 @@
1
- import { Brand } from "../../brand.mjs";
2
- import { Context, Effect, Layer, Scope } from "effect";
3
-
4
- //#region src/substrate/runtime/lease-broker/service.d.ts
5
- /** Opaque resource identifier. Substrate-blind: the broker treats
6
- * this as a string for map keying and never inspects it. Plugin
7
- * authors choose the encoding (`account:<addr>`, `gpu:<slot>`, etc.). */
8
- type LeaseKey = Brand<string, 'LeaseKey'>;
9
- /** Free-form owner identity, used for diagnostics (`holders()` output,
10
- * span attributes). The broker does NOT use this for re-entrancy:
11
- * two `acquire` calls from the same owner against the same key will
12
- * deadlock the second call. */
13
- type Owner = string;
14
- /** Lease handle. Carries only the diagnostic pair; the release is the
15
- * scope finalizer, not a method.
16
- *
17
- * Why no `release()`: every consumer we have today wraps the lease
18
- * in `Effect.scoped` and lets the scope finalizer fire. Exposing an
19
- * explicit `release()` would invite double-release, release-while-
20
- * scope-still-open, and the "I released early but forgot to clear
21
- * the variable" footgun. The scope IS the lifetime. */
22
- interface Lease {
23
- readonly key: LeaseKey;
24
- readonly owner: Owner;
25
- }
26
- /** Service shape — what plugins yield from Context. */
27
- interface LeaseBroker {
28
- /**
29
- * Acquire the lease for `key`, attributed to `owner`. Blocks if the
30
- * lease is already held, waiting in FIFO order behind earlier
31
- * waiters. Cancellable: an interrupt while waiting unwinds without
32
- * acquiring.
33
- *
34
- * Scope-bound release: the broker installs an uninterruptible
35
- * finalizer on the surrounding scope. Calling `Effect.scoped` over
36
- * an `acquire(...)` is the canonical shape.
37
- *
38
- * Non-reentrant: `acquire` from the same owner against a key the
39
- * owner already holds will deadlock. Per-address sequence-number
40
- * semantics require at-most-one in-flight; nesting is the caller's
41
- * bug.
42
- */
43
- readonly acquire: (key: LeaseKey, owner: Owner) => Effect.Effect<Lease, never, Scope.Scope>;
44
- /**
45
- * Non-blocking variant. Returns `null` if the lease is held by any
46
- * owner (including the calling owner); otherwise atomically claims
47
- * the lease and returns the handle. The atomic-claim CAS is the
48
- * same as `acquire`'s first step — the only difference is the
49
- * `null` return vs. enqueue.
50
- *
51
- * Scope-bound release: same finalizer install as `acquire` when
52
- * the claim wins.
53
- */
54
- readonly tryAcquire: (key: LeaseKey, owner: Owner) => Effect.Effect<Lease | null, never, Scope.Scope>;
55
- /**
56
- * Snapshot of current holders, keyed by `LeaseKey`. For diagnostics
57
- * (renderer, debug logs); the result is a point-in-time read and
58
- * may be stale by the time it's inspected.
59
- */
60
- readonly holders: () => Effect.Effect<ReadonlyMap<LeaseKey, Owner>>;
61
- }
62
- //#endregion
63
- export { Lease, LeaseBroker, LeaseKey, Owner };
64
- //# sourceMappingURL=service.d.mts.map
1
+ import { Context, Effect, Layer, Scope } from "effect";
@@ -4,7 +4,7 @@ import { Context, Deferred, Effect, Layer, Ref } from "effect";
4
4
  * which unbranded strings become branded; downstream code only sees
5
5
  * branded values. */
6
6
  const leaseKey = (s) => s;
7
- var LeaseBrokerService = class extends Context.Service()("@devstack-rewrite/substrate/LeaseBroker") {};
7
+ var LeaseBrokerService = class extends Context.Service()("@devstack/substrate/LeaseBroker") {};
8
8
  /**
9
9
  * In-process Layer. One broker per stack scope; closing the layer's
10
10
  * scope drops every entry. Parallel stacks each get their own broker
@@ -1 +1 @@
1
- {"version":3,"file":"service.mjs","names":[],"sources":["../../../../src/substrate/runtime/lease-broker/service.ts"],"sourcesContent":["// Lease broker — substrate-level service interface.\n//\n// Architecture (`notes/redesign/architecture.md` § \"L0 substrate\n// primitives\"): the substrate owns a generic lease primitive keyed by\n// an opaque resource identifier. Any caller that needs at-most-one-\n// in-flight serialization on a resource yields this service and calls\n// `acquire(key, owner)`. The first canonical consumer is the per-\n// address sequence-number lock (`plugins/account/address-lock.ts`,\n// today plugin-local; PR3 lifts it to consume this primitive);\n// callers in the same shape — sign+execute pipelines, per-connection\n// gate, per-slot work queue — share the same seam.\n//\n// Substrate name-blindness: the broker has no concept of what a\n// `LeaseKey` represents. The plugin author chooses the key shape; the\n// broker treats it as an opaque string with a brand. A wallet might\n// key by `account:<address>`, a GPU-bound plugin by `gpu:<slot>` —\n// the broker doesn't care.\n//\n// Lifetime: scope-bound. `acquire` returns a `Lease` handle whose\n// release fires when the surrounding `Effect.Scope` closes. There is\n// no `release()` method on `Lease` — the only release path is the\n// scope finalizer, which makes \"I forgot to release\" structurally\n// impossible.\n//\n// Mechanics: per-key state in a single `Ref<State>`. The blocking\n// queue is a FIFO of `Waiter`s carrying a `Deferred` that the\n// outgoing holder's finalizer resolves AFTER atomically installing\n// the next holder. `tryAcquire` does the same atomic-claim CAS as\n// `acquire`'s first step, but skips the wait — returning `null` if\n// the key is held.\n\nimport { Context, Deferred, Effect, Layer, Ref, Scope } from 'effect';\n\nimport type { Brand } from '../../brand.ts';\n\n// ----------------------------------------------------------------------\n// Public shape\n// ----------------------------------------------------------------------\n\n/** Opaque resource identifier. Substrate-blind: the broker treats\n * this as a string for map keying and never inspects it. Plugin\n * authors choose the encoding (`account:<addr>`, `gpu:<slot>`, etc.). */\nexport type LeaseKey = Brand<string, 'LeaseKey'>;\n\n/** Construct a `LeaseKey` from a free-form string. The boundary at\n * which unbranded strings become branded; downstream code only sees\n * branded values. */\nexport const leaseKey = (s: string): LeaseKey => s as LeaseKey;\n\n/** Free-form owner identity, used for diagnostics (`holders()` output,\n * span attributes). The broker does NOT use this for re-entrancy:\n * two `acquire` calls from the same owner against the same key will\n * deadlock the second call. */\nexport type Owner = string;\n\n/** Lease handle. Carries only the diagnostic pair; the release is the\n * scope finalizer, not a method.\n *\n * Why no `release()`: every consumer we have today wraps the lease\n * in `Effect.scoped` and lets the scope finalizer fire. Exposing an\n * explicit `release()` would invite double-release, release-while-\n * scope-still-open, and the \"I released early but forgot to clear\n * the variable\" footgun. The scope IS the lifetime. */\nexport interface Lease {\n\treadonly key: LeaseKey;\n\treadonly owner: Owner;\n}\n\n/** Service shape — what plugins yield from Context. */\nexport interface LeaseBroker {\n\t/**\n\t * Acquire the lease for `key`, attributed to `owner`. Blocks if the\n\t * lease is already held, waiting in FIFO order behind earlier\n\t * waiters. Cancellable: an interrupt while waiting unwinds without\n\t * acquiring.\n\t *\n\t * Scope-bound release: the broker installs an uninterruptible\n\t * finalizer on the surrounding scope. Calling `Effect.scoped` over\n\t * an `acquire(...)` is the canonical shape.\n\t *\n\t * Non-reentrant: `acquire` from the same owner against a key the\n\t * owner already holds will deadlock. Per-address sequence-number\n\t * semantics require at-most-one in-flight; nesting is the caller's\n\t * bug.\n\t */\n\treadonly acquire: (key: LeaseKey, owner: Owner) => Effect.Effect<Lease, never, Scope.Scope>;\n\n\t/**\n\t * Non-blocking variant. Returns `null` if the lease is held by any\n\t * owner (including the calling owner); otherwise atomically claims\n\t * the lease and returns the handle. The atomic-claim CAS is the\n\t * same as `acquire`'s first step — the only difference is the\n\t * `null` return vs. enqueue.\n\t *\n\t * Scope-bound release: same finalizer install as `acquire` when\n\t * the claim wins.\n\t */\n\treadonly tryAcquire: (\n\t\tkey: LeaseKey,\n\t\towner: Owner,\n\t) => Effect.Effect<Lease | null, never, Scope.Scope>;\n\n\t/**\n\t * Snapshot of current holders, keyed by `LeaseKey`. For diagnostics\n\t * (renderer, debug logs); the result is a point-in-time read and\n\t * may be stale by the time it's inspected.\n\t */\n\treadonly holders: () => Effect.Effect<ReadonlyMap<LeaseKey, Owner>>;\n}\n\n// ----------------------------------------------------------------------\n// Internal state\n// ----------------------------------------------------------------------\n\n/** A fiber parked on `Deferred.await` waiting to become the next\n * holder. The outgoing holder's finalizer transfers ownership by\n * setting `holder = waiter.owner` on the per-key entry AND resolving\n * the deferred — the awoken waiter doesn't re-CAS.\n *\n * `Waiter` reference identity is the cancellation key: an interrupted\n * waiter removes itself by reference-equality. */\ninterface Waiter {\n\treadonly owner: Owner;\n\treadonly signal: Deferred.Deferred<void>;\n}\n\ninterface KeyEntry {\n\treadonly holder: Owner;\n\treadonly waiters: ReadonlyArray<Waiter>;\n}\n\ntype State = ReadonlyMap<LeaseKey, KeyEntry>;\n\n// ----------------------------------------------------------------------\n// Service tag + Layer\n// ----------------------------------------------------------------------\n\nexport class LeaseBrokerService extends Context.Service<LeaseBrokerService, LeaseBroker>()(\n\t'@devstack-rewrite/substrate/LeaseBroker',\n) {}\n\n/**\n * In-process Layer. One broker per stack scope; closing the layer's\n * scope drops every entry. Parallel stacks each get their own broker\n * — the broker is name-blind, so cross-stack coordination on the\n * same logical key (e.g. an address that two stacks share) is NOT\n * provided here. A cross-process layer can be slotted in later as a\n * sibling factory; today's consumers are in-process only.\n */\nexport const layerLeaseBroker: Layer.Layer<LeaseBrokerService> = Layer.effect(\n\tLeaseBrokerService,\n\tEffect.gen(function* () {\n\t\tconst state = yield* Ref.make<State>(new Map());\n\n\t\t/** Atomic finalizer body: clear `key`'s holder slot and, if any\n\t\t * waiter is queued, promote the head waiter to holder. Returns\n\t\t * the promoted waiter (so the caller can resolve their signal)\n\t\t * or `null` when the key is fully released. Uninterruptible by\n\t\t * construction — invoked from inside `Effect.addFinalizer` which\n\t\t * the runtime wraps uninterruptibly. */\n\t\tconst releaseAndPromote = (key: LeaseKey): Effect.Effect<Waiter | null> =>\n\t\t\tRef.modify<State, Waiter | null>(state, (current) => {\n\t\t\t\tconst entry = current.get(key);\n\t\t\t\tif (!entry) return [null, current];\n\t\t\t\tconst head = entry.waiters[0];\n\t\t\t\tif (head === undefined) {\n\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\tnext.delete(key);\n\t\t\t\t\treturn [null, next];\n\t\t\t\t}\n\t\t\t\tconst rest = entry.waiters.slice(1);\n\t\t\t\tconst next = new Map(current);\n\t\t\t\tnext.set(key, { holder: head.owner, waiters: rest });\n\t\t\t\treturn [head, next];\n\t\t\t});\n\n\t\t/** Remove `waiter` from `key`'s waiter list. Called when an\n\t\t * enqueued waiter is interrupted while parked on its signal. */\n\t\tconst removeWaiterIfQueued = (key: LeaseKey, waiter: Waiter): Effect.Effect<void> =>\n\t\t\tRef.update(state, (current) => {\n\t\t\t\tconst entry = current.get(key);\n\t\t\t\tif (!entry) return current;\n\t\t\t\tconst filtered = entry.waiters.filter((w) => w !== waiter);\n\t\t\t\tif (filtered.length === entry.waiters.length) return current;\n\t\t\t\tconst next = new Map(current);\n\t\t\t\tnext.set(key, { holder: entry.holder, waiters: filtered });\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t/** Cleanup on cancellation of a waiting `acquire`: if we were\n\t\t * still queued, drop ourselves; if we were already promoted to\n\t\t * holder by a release that raced with our interrupt, release on\n\t\t * our behalf so the lease doesn't leak. */\n\t\tconst cleanupCancelledWait = (key: LeaseKey, waiter: Waiter): Effect.Effect<void> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst becameHolder = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\tconst entry = current.get(key);\n\t\t\t\t\tif (!entry) return [false, current];\n\t\t\t\t\tif (entry.holder === waiter.owner && entry.waiters.indexOf(waiter) === -1) {\n\t\t\t\t\t\t// Promoted-but-not-awaited: we were handed the lease\n\t\t\t\t\t\t// in releaseAndPromote but interrupted before the\n\t\t\t\t\t\t// `Deferred.await` returned. Take responsibility for\n\t\t\t\t\t\t// releasing it.\n\t\t\t\t\t\treturn [true, current];\n\t\t\t\t\t}\n\t\t\t\t\treturn [false, current];\n\t\t\t\t});\n\t\t\t\tif (becameHolder) {\n\t\t\t\t\tconst promoted = yield* releaseAndPromote(key);\n\t\t\t\t\tif (promoted !== null) {\n\t\t\t\t\t\tyield* Deferred.succeed(promoted.signal, undefined);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tyield* removeWaiterIfQueued(key, waiter);\n\t\t\t\t}\n\t\t\t});\n\n\t\t/** Install the scope-bound release finalizer. Uninterruptible —\n\t\t * losing the release on a Ctrl-C double-tap would leak the\n\t\t * lease and stall everything queued behind it. */\n\t\tconst installReleaseFinalizer = (key: LeaseKey): Effect.Effect<void, never, Scope.Scope> =>\n\t\t\tEffect.addFinalizer(() =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst promoted = yield* releaseAndPromote(key);\n\t\t\t\t\tif (promoted !== null) {\n\t\t\t\t\t\tyield* Deferred.succeed(promoted.signal, undefined);\n\t\t\t\t\t}\n\t\t\t\t}).pipe(Effect.uninterruptible),\n\t\t\t);\n\n\t\tconst acquire: LeaseBroker['acquire'] = (key, owner) =>\n\t\t\tEffect.uninterruptibleMask((restore) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst signal = yield* Deferred.make<void>();\n\t\t\t\t\tconst waiter: Waiter = { owner, signal };\n\n\t\t\t\t\tconst becameHolder = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\t\tconst entry = current.get(key);\n\t\t\t\t\t\tif (!entry) {\n\t\t\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\t\t\tnext.set(key, { holder: owner, waiters: [] });\n\t\t\t\t\t\t\treturn [true, next];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\t\tnext.set(key, {\n\t\t\t\t\t\t\tholder: entry.holder,\n\t\t\t\t\t\t\twaiters: [...entry.waiters, waiter],\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn [false, next];\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!becameHolder) {\n\t\t\t\t\t\tyield* restore(Deferred.await(signal)).pipe(\n\t\t\t\t\t\t\tEffect.onInterrupt(() =>\n\t\t\t\t\t\t\t\tcleanupCancelledWait(key, waiter).pipe(Effect.uninterruptible),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tyield* installReleaseFinalizer(key);\n\t\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t\t'leaseBroker.key': key,\n\t\t\t\t\t\t'leaseBroker.owner': owner,\n\t\t\t\t\t\t'leaseBroker.contended': !becameHolder,\n\t\t\t\t\t});\n\t\t\t\t\treturn { key, owner } satisfies Lease;\n\t\t\t\t}),\n\t\t\t).pipe(Effect.withSpan('substrate.leaseBroker.acquire'));\n\n\t\tconst tryAcquire: LeaseBroker['tryAcquire'] = (key, owner) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst claimed = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\tif (current.has(key)) return [false, current];\n\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\tnext.set(key, { holder: owner, waiters: [] });\n\t\t\t\t\treturn [true, next];\n\t\t\t\t});\n\t\t\t\tif (!claimed) {\n\t\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t\t'leaseBroker.key': key,\n\t\t\t\t\t\t'leaseBroker.owner': owner,\n\t\t\t\t\t\t'leaseBroker.claimed': false,\n\t\t\t\t\t});\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tyield* installReleaseFinalizer(key);\n\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t'leaseBroker.key': key,\n\t\t\t\t\t'leaseBroker.owner': owner,\n\t\t\t\t\t'leaseBroker.claimed': true,\n\t\t\t\t});\n\t\t\t\treturn { key, owner } satisfies Lease;\n\t\t\t}).pipe(\n\t\t\t\t// Make the claim-and-install-finalizer pair atomic w.r.t.\n\t\t\t\t// interruption: an interrupt arriving AFTER `Ref.modify`\n\t\t\t\t// claims the slot but BEFORE the finalizer is wired would\n\t\t\t\t// leak the lease. `Ref.modify` itself is a pure-data\n\t\t\t\t// transition, so wrapping the whole pipeline uninterruptibly\n\t\t\t\t// is safe.\n\t\t\t\tEffect.uninterruptible,\n\t\t\t\tEffect.withSpan('substrate.leaseBroker.tryAcquire'),\n\t\t\t);\n\n\t\tconst holders: LeaseBroker['holders'] = () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst current = yield* Ref.get(state);\n\t\t\t\tconst out = new Map<LeaseKey, Owner>();\n\t\t\t\tfor (const [k, entry] of current) out.set(k, entry.holder);\n\t\t\t\treturn out;\n\t\t\t});\n\n\t\treturn LeaseBrokerService.of({ acquire, tryAcquire, holders });\n\t}),\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.mjs","names":[],"sources":["../../../../src/substrate/runtime/lease-broker/service.ts"],"sourcesContent":["// Lease broker — substrate-level service interface.\n//\n// Architecture (`notes/redesign/architecture.md` § \"L0 substrate\n// primitives\"): the substrate owns a generic lease primitive keyed by\n// an opaque resource identifier. Any caller that needs at-most-one-\n// in-flight serialization on a resource yields this service and calls\n// `acquire(key, owner)`. The first canonical consumer is the per-\n// address sequence-number lock (`plugins/account/address-lock.ts`,\n// today plugin-local; PR3 lifts it to consume this primitive);\n// callers in the same shape — sign+execute pipelines, per-connection\n// gate, per-slot work queue — share the same seam.\n//\n// Substrate name-blindness: the broker has no concept of what a\n// `LeaseKey` represents. The plugin author chooses the key shape; the\n// broker treats it as an opaque string with a brand. A wallet might\n// key by `account:<address>`, a GPU-bound plugin by `gpu:<slot>` —\n// the broker doesn't care.\n//\n// Lifetime: scope-bound. `acquire` returns a `Lease` handle whose\n// release fires when the surrounding `Effect.Scope` closes. There is\n// no `release()` method on `Lease` — the only release path is the\n// scope finalizer, which makes \"I forgot to release\" structurally\n// impossible.\n//\n// Mechanics: per-key state in a single `Ref<State>`. The blocking\n// queue is a FIFO of `Waiter`s carrying a `Deferred` that the\n// outgoing holder's finalizer resolves AFTER atomically installing\n// the next holder. `tryAcquire` does the same atomic-claim CAS as\n// `acquire`'s first step, but skips the wait — returning `null` if\n// the key is held.\n\nimport { Context, Deferred, Effect, Layer, Ref, Scope } from 'effect';\n\nimport type { Brand } from '../../brand.ts';\n\n// ----------------------------------------------------------------------\n// Public shape\n// ----------------------------------------------------------------------\n\n/** Opaque resource identifier. Substrate-blind: the broker treats\n * this as a string for map keying and never inspects it. Plugin\n * authors choose the encoding (`account:<addr>`, `gpu:<slot>`, etc.). */\nexport type LeaseKey = Brand<string, 'LeaseKey'>;\n\n/** Construct a `LeaseKey` from a free-form string. The boundary at\n * which unbranded strings become branded; downstream code only sees\n * branded values. */\nexport const leaseKey = (s: string): LeaseKey => s as LeaseKey;\n\n/** Free-form owner identity, used for diagnostics (`holders()` output,\n * span attributes). The broker does NOT use this for re-entrancy:\n * two `acquire` calls from the same owner against the same key will\n * deadlock the second call. */\nexport type Owner = string;\n\n/** Lease handle. Carries only the diagnostic pair; the release is the\n * scope finalizer, not a method.\n *\n * Why no `release()`: every consumer we have today wraps the lease\n * in `Effect.scoped` and lets the scope finalizer fire. Exposing an\n * explicit `release()` would invite double-release, release-while-\n * scope-still-open, and the \"I released early but forgot to clear\n * the variable\" footgun. The scope IS the lifetime. */\nexport interface Lease {\n\treadonly key: LeaseKey;\n\treadonly owner: Owner;\n}\n\n/** Service shape — what plugins yield from Context. */\nexport interface LeaseBroker {\n\t/**\n\t * Acquire the lease for `key`, attributed to `owner`. Blocks if the\n\t * lease is already held, waiting in FIFO order behind earlier\n\t * waiters. Cancellable: an interrupt while waiting unwinds without\n\t * acquiring.\n\t *\n\t * Scope-bound release: the broker installs an uninterruptible\n\t * finalizer on the surrounding scope. Calling `Effect.scoped` over\n\t * an `acquire(...)` is the canonical shape.\n\t *\n\t * Non-reentrant: `acquire` from the same owner against a key the\n\t * owner already holds will deadlock. Per-address sequence-number\n\t * semantics require at-most-one in-flight; nesting is the caller's\n\t * bug.\n\t */\n\treadonly acquire: (key: LeaseKey, owner: Owner) => Effect.Effect<Lease, never, Scope.Scope>;\n\n\t/**\n\t * Non-blocking variant. Returns `null` if the lease is held by any\n\t * owner (including the calling owner); otherwise atomically claims\n\t * the lease and returns the handle. The atomic-claim CAS is the\n\t * same as `acquire`'s first step — the only difference is the\n\t * `null` return vs. enqueue.\n\t *\n\t * Scope-bound release: same finalizer install as `acquire` when\n\t * the claim wins.\n\t */\n\treadonly tryAcquire: (\n\t\tkey: LeaseKey,\n\t\towner: Owner,\n\t) => Effect.Effect<Lease | null, never, Scope.Scope>;\n\n\t/**\n\t * Snapshot of current holders, keyed by `LeaseKey`. For diagnostics\n\t * (renderer, debug logs); the result is a point-in-time read and\n\t * may be stale by the time it's inspected.\n\t */\n\treadonly holders: () => Effect.Effect<ReadonlyMap<LeaseKey, Owner>>;\n}\n\n// ----------------------------------------------------------------------\n// Internal state\n// ----------------------------------------------------------------------\n\n/** A fiber parked on `Deferred.await` waiting to become the next\n * holder. The outgoing holder's finalizer transfers ownership by\n * setting `holder = waiter.owner` on the per-key entry AND resolving\n * the deferred — the awoken waiter doesn't re-CAS.\n *\n * `Waiter` reference identity is the cancellation key: an interrupted\n * waiter removes itself by reference-equality. */\ninterface Waiter {\n\treadonly owner: Owner;\n\treadonly signal: Deferred.Deferred<void>;\n}\n\ninterface KeyEntry {\n\treadonly holder: Owner;\n\treadonly waiters: ReadonlyArray<Waiter>;\n}\n\ntype State = ReadonlyMap<LeaseKey, KeyEntry>;\n\n// ----------------------------------------------------------------------\n// Service tag + Layer\n// ----------------------------------------------------------------------\n\nexport class LeaseBrokerService extends Context.Service<LeaseBrokerService, LeaseBroker>()(\n\t'@devstack/substrate/LeaseBroker',\n) {}\n\n/**\n * In-process Layer. One broker per stack scope; closing the layer's\n * scope drops every entry. Parallel stacks each get their own broker\n * — the broker is name-blind, so cross-stack coordination on the\n * same logical key (e.g. an address that two stacks share) is NOT\n * provided here. A cross-process layer can be slotted in later as a\n * sibling factory; today's consumers are in-process only.\n */\nexport const layerLeaseBroker: Layer.Layer<LeaseBrokerService> = Layer.effect(\n\tLeaseBrokerService,\n\tEffect.gen(function* () {\n\t\tconst state = yield* Ref.make<State>(new Map());\n\n\t\t/** Atomic finalizer body: clear `key`'s holder slot and, if any\n\t\t * waiter is queued, promote the head waiter to holder. Returns\n\t\t * the promoted waiter (so the caller can resolve their signal)\n\t\t * or `null` when the key is fully released. Uninterruptible by\n\t\t * construction — invoked from inside `Effect.addFinalizer` which\n\t\t * the runtime wraps uninterruptibly. */\n\t\tconst releaseAndPromote = (key: LeaseKey): Effect.Effect<Waiter | null> =>\n\t\t\tRef.modify<State, Waiter | null>(state, (current) => {\n\t\t\t\tconst entry = current.get(key);\n\t\t\t\tif (!entry) return [null, current];\n\t\t\t\tconst head = entry.waiters[0];\n\t\t\t\tif (head === undefined) {\n\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\tnext.delete(key);\n\t\t\t\t\treturn [null, next];\n\t\t\t\t}\n\t\t\t\tconst rest = entry.waiters.slice(1);\n\t\t\t\tconst next = new Map(current);\n\t\t\t\tnext.set(key, { holder: head.owner, waiters: rest });\n\t\t\t\treturn [head, next];\n\t\t\t});\n\n\t\t/** Remove `waiter` from `key`'s waiter list. Called when an\n\t\t * enqueued waiter is interrupted while parked on its signal. */\n\t\tconst removeWaiterIfQueued = (key: LeaseKey, waiter: Waiter): Effect.Effect<void> =>\n\t\t\tRef.update(state, (current) => {\n\t\t\t\tconst entry = current.get(key);\n\t\t\t\tif (!entry) return current;\n\t\t\t\tconst filtered = entry.waiters.filter((w) => w !== waiter);\n\t\t\t\tif (filtered.length === entry.waiters.length) return current;\n\t\t\t\tconst next = new Map(current);\n\t\t\t\tnext.set(key, { holder: entry.holder, waiters: filtered });\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t/** Cleanup on cancellation of a waiting `acquire`: if we were\n\t\t * still queued, drop ourselves; if we were already promoted to\n\t\t * holder by a release that raced with our interrupt, release on\n\t\t * our behalf so the lease doesn't leak. */\n\t\tconst cleanupCancelledWait = (key: LeaseKey, waiter: Waiter): Effect.Effect<void> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst becameHolder = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\tconst entry = current.get(key);\n\t\t\t\t\tif (!entry) return [false, current];\n\t\t\t\t\tif (entry.holder === waiter.owner && entry.waiters.indexOf(waiter) === -1) {\n\t\t\t\t\t\t// Promoted-but-not-awaited: we were handed the lease\n\t\t\t\t\t\t// in releaseAndPromote but interrupted before the\n\t\t\t\t\t\t// `Deferred.await` returned. Take responsibility for\n\t\t\t\t\t\t// releasing it.\n\t\t\t\t\t\treturn [true, current];\n\t\t\t\t\t}\n\t\t\t\t\treturn [false, current];\n\t\t\t\t});\n\t\t\t\tif (becameHolder) {\n\t\t\t\t\tconst promoted = yield* releaseAndPromote(key);\n\t\t\t\t\tif (promoted !== null) {\n\t\t\t\t\t\tyield* Deferred.succeed(promoted.signal, undefined);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tyield* removeWaiterIfQueued(key, waiter);\n\t\t\t\t}\n\t\t\t});\n\n\t\t/** Install the scope-bound release finalizer. Uninterruptible —\n\t\t * losing the release on a Ctrl-C double-tap would leak the\n\t\t * lease and stall everything queued behind it. */\n\t\tconst installReleaseFinalizer = (key: LeaseKey): Effect.Effect<void, never, Scope.Scope> =>\n\t\t\tEffect.addFinalizer(() =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst promoted = yield* releaseAndPromote(key);\n\t\t\t\t\tif (promoted !== null) {\n\t\t\t\t\t\tyield* Deferred.succeed(promoted.signal, undefined);\n\t\t\t\t\t}\n\t\t\t\t}).pipe(Effect.uninterruptible),\n\t\t\t);\n\n\t\tconst acquire: LeaseBroker['acquire'] = (key, owner) =>\n\t\t\tEffect.uninterruptibleMask((restore) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst signal = yield* Deferred.make<void>();\n\t\t\t\t\tconst waiter: Waiter = { owner, signal };\n\n\t\t\t\t\tconst becameHolder = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\t\tconst entry = current.get(key);\n\t\t\t\t\t\tif (!entry) {\n\t\t\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\t\t\tnext.set(key, { holder: owner, waiters: [] });\n\t\t\t\t\t\t\treturn [true, next];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\t\tnext.set(key, {\n\t\t\t\t\t\t\tholder: entry.holder,\n\t\t\t\t\t\t\twaiters: [...entry.waiters, waiter],\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn [false, next];\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!becameHolder) {\n\t\t\t\t\t\tyield* restore(Deferred.await(signal)).pipe(\n\t\t\t\t\t\t\tEffect.onInterrupt(() =>\n\t\t\t\t\t\t\t\tcleanupCancelledWait(key, waiter).pipe(Effect.uninterruptible),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tyield* installReleaseFinalizer(key);\n\t\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t\t'leaseBroker.key': key,\n\t\t\t\t\t\t'leaseBroker.owner': owner,\n\t\t\t\t\t\t'leaseBroker.contended': !becameHolder,\n\t\t\t\t\t});\n\t\t\t\t\treturn { key, owner } satisfies Lease;\n\t\t\t\t}),\n\t\t\t).pipe(Effect.withSpan('substrate.leaseBroker.acquire'));\n\n\t\tconst tryAcquire: LeaseBroker['tryAcquire'] = (key, owner) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst claimed = yield* Ref.modify<State, boolean>(state, (current) => {\n\t\t\t\t\tif (current.has(key)) return [false, current];\n\t\t\t\t\tconst next = new Map(current);\n\t\t\t\t\tnext.set(key, { holder: owner, waiters: [] });\n\t\t\t\t\treturn [true, next];\n\t\t\t\t});\n\t\t\t\tif (!claimed) {\n\t\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t\t'leaseBroker.key': key,\n\t\t\t\t\t\t'leaseBroker.owner': owner,\n\t\t\t\t\t\t'leaseBroker.claimed': false,\n\t\t\t\t\t});\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tyield* installReleaseFinalizer(key);\n\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t'leaseBroker.key': key,\n\t\t\t\t\t'leaseBroker.owner': owner,\n\t\t\t\t\t'leaseBroker.claimed': true,\n\t\t\t\t});\n\t\t\t\treturn { key, owner } satisfies Lease;\n\t\t\t}).pipe(\n\t\t\t\t// Make the claim-and-install-finalizer pair atomic w.r.t.\n\t\t\t\t// interruption: an interrupt arriving AFTER `Ref.modify`\n\t\t\t\t// claims the slot but BEFORE the finalizer is wired would\n\t\t\t\t// leak the lease. `Ref.modify` itself is a pure-data\n\t\t\t\t// transition, so wrapping the whole pipeline uninterruptibly\n\t\t\t\t// is safe.\n\t\t\t\tEffect.uninterruptible,\n\t\t\t\tEffect.withSpan('substrate.leaseBroker.tryAcquire'),\n\t\t\t);\n\n\t\tconst holders: LeaseBroker['holders'] = () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst current = yield* Ref.get(state);\n\t\t\t\tconst out = new Map<LeaseKey, Owner>();\n\t\t\t\tfor (const [k, entry] of current) out.set(k, entry.holder);\n\t\t\t\treturn out;\n\t\t\t});\n\n\t\treturn LeaseBrokerService.of({ acquire, tryAcquire, holders });\n\t}),\n);\n"],"mappings":";;;;;AA+CA,MAAa,YAAY,MAAwB;AA0FjD,IAAa,qBAAb,cAAwC,QAAQ,SAA0C,CACzF,kCACA,CAAC;;;;;;;;;AAUF,MAAa,mBAAoD,MAAM,OACtE,oBACA,OAAO,IAAI,aAAa;CACvB,MAAM,QAAQ,OAAO,IAAI,qBAAY,IAAI,KAAK,CAAC;;;;;;;CAQ/C,MAAM,qBAAqB,QAC1B,IAAI,OAA6B,QAAQ,YAAY;EACpD,MAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,MAAI,CAAC,MAAO,QAAO,CAAC,MAAM,QAAQ;EAClC,MAAM,OAAO,MAAM,QAAQ;AAC3B,MAAI,SAAS,KAAA,GAAW;GACvB,MAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,QAAK,OAAO,IAAI;AAChB,UAAO,CAAC,MAAM,KAAK;;EAEpB,MAAM,OAAO,MAAM,QAAQ,MAAM,EAAE;EACnC,MAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,OAAK,IAAI,KAAK;GAAE,QAAQ,KAAK;GAAO,SAAS;GAAM,CAAC;AACpD,SAAO,CAAC,MAAM,KAAK;GAClB;;;CAIH,MAAM,wBAAwB,KAAe,WAC5C,IAAI,OAAO,QAAQ,YAAY;EAC9B,MAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,MAAI,CAAC,MAAO,QAAO;EACnB,MAAM,WAAW,MAAM,QAAQ,QAAQ,MAAM,MAAM,OAAO;AAC1D,MAAI,SAAS,WAAW,MAAM,QAAQ,OAAQ,QAAO;EACrD,MAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,OAAK,IAAI,KAAK;GAAE,QAAQ,MAAM;GAAQ,SAAS;GAAU,CAAC;AAC1D,SAAO;GACN;;;;;CAMH,MAAM,wBAAwB,KAAe,WAC5C,OAAO,IAAI,aAAa;AAavB,MAAI,OAZwB,IAAI,OAAuB,QAAQ,YAAY;GAC1E,MAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,OAAI,CAAC,MAAO,QAAO,CAAC,OAAO,QAAQ;AACnC,OAAI,MAAM,WAAW,OAAO,SAAS,MAAM,QAAQ,QAAQ,OAAO,KAAK,GAKtE,QAAO,CAAC,MAAM,QAAQ;AAEvB,UAAO,CAAC,OAAO,QAAQ;IACtB,EACgB;GACjB,MAAM,WAAW,OAAO,kBAAkB,IAAI;AAC9C,OAAI,aAAa,KAChB,QAAO,SAAS,QAAQ,SAAS,QAAQ,KAAA,EAAU;QAGpD,QAAO,qBAAqB,KAAK,OAAO;GAExC;;;;CAKH,MAAM,2BAA2B,QAChC,OAAO,mBACN,OAAO,IAAI,aAAa;EACvB,MAAM,WAAW,OAAO,kBAAkB,IAAI;AAC9C,MAAI,aAAa,KAChB,QAAO,SAAS,QAAQ,SAAS,QAAQ,KAAA,EAAU;GAEnD,CAAC,KAAK,OAAO,gBAAgB,CAC/B;CAEF,MAAM,WAAmC,KAAK,UAC7C,OAAO,qBAAqB,YAC3B,OAAO,IAAI,aAAa;EACvB,MAAM,SAAS,OAAO,SAAS,MAAY;EAC3C,MAAM,SAAiB;GAAE;GAAO;GAAQ;EAExC,MAAM,eAAe,OAAO,IAAI,OAAuB,QAAQ,YAAY;GAC1E,MAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,OAAI,CAAC,OAAO;IACX,MAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,SAAK,IAAI,KAAK;KAAE,QAAQ;KAAO,SAAS,EAAE;KAAE,CAAC;AAC7C,WAAO,CAAC,MAAM,KAAK;;GAEpB,MAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,QAAK,IAAI,KAAK;IACb,QAAQ,MAAM;IACd,SAAS,CAAC,GAAG,MAAM,SAAS,OAAO;IACnC,CAAC;AACF,UAAO,CAAC,OAAO,KAAK;IACnB;AAEF,MAAI,CAAC,aACJ,QAAO,QAAQ,SAAS,MAAM,OAAO,CAAC,CAAC,KACtC,OAAO,kBACN,qBAAqB,KAAK,OAAO,CAAC,KAAK,OAAO,gBAAgB,CAC9D,CACD;AAGF,SAAO,wBAAwB,IAAI;AACnC,SAAO,OAAO,oBAAoB;GACjC,mBAAmB;GACnB,qBAAqB;GACrB,yBAAyB,CAAC;GAC1B,CAAC;AACF,SAAO;GAAE;GAAK;GAAO;GACpB,CACF,CAAC,KAAK,OAAO,SAAS,gCAAgC,CAAC;CAEzD,MAAM,cAAyC,KAAK,UACnD,OAAO,IAAI,aAAa;AAOvB,MAAI,EAAC,OANkB,IAAI,OAAuB,QAAQ,YAAY;AACrE,OAAI,QAAQ,IAAI,IAAI,CAAE,QAAO,CAAC,OAAO,QAAQ;GAC7C,MAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,QAAK,IAAI,KAAK;IAAE,QAAQ;IAAO,SAAS,EAAE;IAAE,CAAC;AAC7C,UAAO,CAAC,MAAM,KAAK;IAClB,GACY;AACb,UAAO,OAAO,oBAAoB;IACjC,mBAAmB;IACnB,qBAAqB;IACrB,uBAAuB;IACvB,CAAC;AACF,UAAO;;AAER,SAAO,wBAAwB,IAAI;AACnC,SAAO,OAAO,oBAAoB;GACjC,mBAAmB;GACnB,qBAAqB;GACrB,uBAAuB;GACvB,CAAC;AACF,SAAO;GAAE;GAAK;GAAO;GACpB,CAAC,KAOF,OAAO,iBACP,OAAO,SAAS,mCAAmC,CACnD;CAEF,MAAM,gBACL,OAAO,IAAI,aAAa;EACvB,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;EACrC,MAAM,sBAAM,IAAI,KAAsB;AACtC,OAAK,MAAM,CAAC,GAAG,UAAU,QAAS,KAAI,IAAI,GAAG,MAAM,OAAO;AAC1D,SAAO;GACN;AAEH,QAAO,mBAAmB,GAAG;EAAE;EAAS;EAAY;EAAS,CAAC;EAC7D,CACF"}
@@ -11,6 +11,16 @@ var DepGraphCycleError = class extends Data.TaggedError("DepGraphCycleError") {}
11
11
  * the compile-time `MissingProviders` check, but runtime-built stacks can
12
12
  * hit it. */
13
13
  var UnresolvedDependencyError = class extends Data.TaggedError("UnresolvedDependencyError") {};
14
+ /** Two members declared the same `resource id`. The compile-time
15
+ * `MissingProviders` check only catches collisions on the user-typed
16
+ * dependency path, NOT two members declared in the same stack with the
17
+ * same `definePlugin({ id })`. Without this guard the dep-graph would
18
+ * last-writer-wins the provider index and silently bind every dependent
19
+ * to the second declaration. Phase 22e/E3 added a complementary
20
+ * package-plugin-level guard (`plugins/package/mode-local.ts`) for the
21
+ * registry path; this surfaces the same class of mistake for ALL plugins
22
+ * at the substrate dep-graph layer. */
23
+ var DuplicateResourceIdError = class extends Data.TaggedError("DuplicateResourceIdError") {};
14
24
  /** Mint a stable PluginKey for a plugin. Plugins may declare a stable
15
25
  * key; otherwise keys derive from resource id + ordinal so duplicates
16
26
  * do not collide. */
@@ -36,7 +46,8 @@ const expand = (members) => {
36
46
  *
37
47
  * Steps:
38
48
  * 1. Mint a plugin key for every member.
39
- * 2. Build the resource-id -> key index (providers).
49
+ * 2. Build the resource-id -> key index (providers). Two members with
50
+ * the same `id` fail with `DuplicateResourceIdError`.
40
51
  * 3. For each node, resolve its dependency refs to upstream keys.
41
52
  * 4. Kahn's algorithm: peel zero-indegree nodes into level 0, decrement
42
53
  * downstream indegrees, repeat.
@@ -49,7 +60,15 @@ const resolveGraph = (members) => Effect.gen(function* () {
49
60
  yield* Effect.annotateCurrentSpan({ "devstack.dep-graph.memberCount": members.length });
50
61
  const named = expand(members);
51
62
  const providerByResourceId = /* @__PURE__ */ new Map();
52
- for (const { key, member } of named) providerByResourceId.set(member.id, key);
63
+ for (const { key, member } of named) {
64
+ const existing = providerByResourceId.get(member.id);
65
+ if (existing !== void 0) return yield* Effect.fail(new DuplicateResourceIdError({
66
+ resourceId: member.id,
67
+ firstPluginKey: existing,
68
+ secondPluginKey: key
69
+ }));
70
+ providerByResourceId.set(member.id, key);
71
+ }
53
72
  const nodes = /* @__PURE__ */ new Map();
54
73
  const indegree = /* @__PURE__ */ new Map();
55
74
  const downstream = /* @__PURE__ */ new Map();
@@ -133,6 +152,6 @@ const orderByLevel = (graph, keys, direction) => {
133
152
  return direction === "reverse" ? out.slice().reverse() : out;
134
153
  };
135
154
  //#endregion
136
- export { downstreamClosure, orderByLevel, resolveGraph };
155
+ export { DepGraphCycleError, DuplicateResourceIdError, UnresolvedDependencyError, downstreamClosure, orderByLevel, resolveGraph };
137
156
 
138
157
  //# sourceMappingURL=dep-graph.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"dep-graph.mjs","names":["makePluginKey"],"sources":["../../../../src/substrate/runtime/lifecycle/dep-graph.ts"],"sourcesContent":["// Dep-graph resolution — turn a `Stack` member tuple into a topo-sorted\n// acquire schedule.\n//\n// Architecture § Plugin lifecycle within a stack:\n// \"Resolution. Upstream keys (concrete or capability-typed) resolve\n// once. Bootstrap asset dedup-by-key fires before the scheduler\n// starts node fibers. Scheduling. Plugin enters `pending`. When all\n// upstream keys are `ready`, the node begins `acquiring`. Per-key\n// serialization: only one acquire at a time per key.\"\n//\n// This module is the pure dep-graph math: members in, levels out.\n\nimport { Data, Effect } from 'effect';\n\nimport type { PluginKey } from '../../brand.ts';\nimport { pluginKey as makePluginKey } from '../../brand.ts';\nimport { uniqueResourceRefs, type AnyPlugin, type AnyResourceRef } from '../../plugin.ts';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** Cycle detected in the dep graph. Carries the offending key path so\n * the user sees the loop. Compile-time `defineDevstack` doesn't catch\n * every cycle (capability-typed consumes resolve at runtime), so this\n * surfaces a typed error at boot. */\nexport class DepGraphCycleError extends Data.TaggedError('DepGraphCycleError')<{\n\treadonly cycle: ReadonlyArray<PluginKey>;\n}> {}\n\n/** A dependency resource matched no provider. This is normally caught by\n * the compile-time `MissingProviders` check, but runtime-built stacks can\n * hit it. */\nexport class UnresolvedDependencyError extends Data.TaggedError('UnresolvedDependencyError')<{\n\treadonly pluginKey: PluginKey;\n\treadonly missingResourceId: string;\n}> {}\n\nexport type DepGraphError = DepGraphCycleError | UnresolvedDependencyError;\n\n// -----------------------------------------------------------------------------\n// Node shape\n// -----------------------------------------------------------------------------\n\n/** A resolved node in the dep graph. Wraps the original plugin with a\n * substrate-assigned `PluginKey`. */\nexport interface DepNode {\n\treadonly key: PluginKey;\n\treadonly member: AnyPlugin;\n\t/** Unique dependency refs and their resolved upstream keys. The two\n\t * arrays are positional peers. */\n\treadonly upstreamResources: ReadonlyArray<AnyResourceRef>;\n\treadonly upstreamKeys: ReadonlyArray<PluginKey>;\n}\n\n/** The result of resolving a member tuple. Levels are batches the\n * scheduler can acquire in parallel; each level's nodes only depend\n * on prior levels. */\nexport interface ResolvedGraph {\n\treadonly nodes: ReadonlyMap<PluginKey, DepNode>;\n\treadonly levels: ReadonlyArray<ReadonlyArray<PluginKey>>;\n\t/** Reverse-dep index: each key → keys that depend on it. The\n\t * supervisor uses this for selective-restart cascade. */\n\treadonly downstream: ReadonlyMap<PluginKey, ReadonlySet<PluginKey>>;\n}\n\n// -----------------------------------------------------------------------------\n// Plugin key minting\n// -----------------------------------------------------------------------------\n\ninterface NamedMember {\n\treadonly key: PluginKey;\n\treadonly member: AnyPlugin;\n}\n\n/** Mint a stable PluginKey for a plugin. Plugins may declare a stable\n * key; otherwise keys derive from resource id + ordinal so duplicates\n * do not collide. */\nconst mintKey = (member: AnyPlugin, ordinal: number): PluginKey => {\n\tif (member.pluginKey !== undefined) {\n\t\treturn makePluginKey(String(member.pluginKey));\n\t}\n\treturn makePluginKey(`${member.id}#${ordinal}`);\n};\n\n/** Walk the member tuple and emit one named-member entry per node. */\nconst expand = (members: ReadonlyArray<AnyPlugin>): ReadonlyArray<NamedMember> => {\n\tconst out: NamedMember[] = [];\n\tlet ordinal = 0;\n\tfor (const member of members) {\n\t\tconst key = mintKey(member, ordinal++);\n\t\tout.push({ key, member });\n\t}\n\treturn out;\n};\n\n// -----------------------------------------------------------------------------\n// Topological sort (Kahn's algorithm)\n// -----------------------------------------------------------------------------\n\n/**\n * Resolve a flat member tuple into a topo-sorted level batch.\n *\n * Steps:\n * 1. Mint a plugin key for every member.\n * 2. Build the resource-id -> key index (providers).\n * 3. For each node, resolve its dependency refs to upstream keys.\n * 4. Kahn's algorithm: peel zero-indegree nodes into level 0, decrement\n * downstream indegrees, repeat.\n * 5. If any node remains, return `DepGraphCycleError`.\n *\n * Span-instrumented so trace exports show the resolution alongside the\n * supervisor's acquire spans.\n */\nexport const resolveGraph = (\n\tmembers: ReadonlyArray<AnyPlugin>,\n): Effect.Effect<ResolvedGraph, DepGraphError> =>\n\tEffect.gen(function* () {\n\t\tyield* Effect.annotateCurrentSpan({ 'devstack.dep-graph.memberCount': members.length });\n\t\tconst named = expand(members);\n\n\t\t// Provider index: resource-id → key. Last writer wins; the\n\t\t// compile-time `MissingProviders` check ensures uniqueness on\n\t\t// the user-typed path, so a duplicate here would be a programmer\n\t\t// error in a runtime-typed stack — we don't enforce uniqueness at\n\t\t// runtime; the duplicate just resolves to the latest declaration.\n\t\tconst providerByResourceId = new Map<string, PluginKey>();\n\t\tfor (const { key, member } of named) {\n\t\t\tproviderByResourceId.set(member.id, key);\n\t\t}\n\n\t\tconst nodes = new Map<PluginKey, DepNode>();\n\t\tconst indegree = new Map<PluginKey, number>();\n\t\tconst downstream = new Map<PluginKey, Set<PluginKey>>();\n\t\tfor (const { key } of named) {\n\t\t\tindegree.set(key, 0);\n\t\t\tdownstream.set(key, new Set());\n\t\t}\n\n\t\tfor (const { key, member } of named) {\n\t\t\tconst upstreamKeys: PluginKey[] = [];\n\t\t\tconst upstreamResources = uniqueResourceRefs(member.dependsOn);\n\t\t\tfor (const resource of upstreamResources) {\n\t\t\t\tconst providerKey = providerByResourceId.get(resource.id);\n\t\t\t\tif (providerKey === undefined) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew UnresolvedDependencyError({ pluginKey: key, missingResourceId: resource.id }),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tupstreamKeys.push(providerKey);\n\t\t\t\tdownstream.get(providerKey)?.add(key);\n\t\t\t\tindegree.set(key, (indegree.get(key) ?? 0) + 1);\n\t\t\t}\n\t\t\tnodes.set(key, {\n\t\t\t\tkey,\n\t\t\t\tmember,\n\t\t\t\tupstreamResources,\n\t\t\t\tupstreamKeys,\n\t\t\t});\n\t\t}\n\n\t\t// Kahn's algorithm — peel zero-indegree nodes level by level.\n\t\tconst levels: PluginKey[][] = [];\n\t\tconst settled = new Set<PluginKey>();\n\t\twhile (settled.size < nodes.size) {\n\t\t\tconst layer: PluginKey[] = [];\n\t\t\tfor (const [key, deg] of indegree) {\n\t\t\t\tif (settled.has(key)) continue;\n\t\t\t\tif (deg === 0) layer.push(key);\n\t\t\t}\n\t\t\tif (layer.length === 0) {\n\t\t\t\t// Cycle: collect every unsettled node as the cycle's witness.\n\t\t\t\t// More precise cycle extraction (Tarjan SCC) is overkill for\n\t\t\t\t// the user-facing message; we surface the trapped keys.\n\t\t\t\tconst trapped: PluginKey[] = [];\n\t\t\t\tfor (const key of nodes.keys()) {\n\t\t\t\t\tif (!settled.has(key)) trapped.push(key);\n\t\t\t\t}\n\t\t\t\treturn yield* Effect.fail(new DepGraphCycleError({ cycle: trapped }));\n\t\t\t}\n\t\t\tlevels.push(layer);\n\t\t\tfor (const key of layer) {\n\t\t\t\tsettled.add(key);\n\t\t\t\tfor (const child of downstream.get(key) ?? []) {\n\t\t\t\t\tindegree.set(child, (indegree.get(child) ?? 1) - 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Freeze downstream sets to ReadonlySet shape.\n\t\tconst downstreamFrozen = new Map<PluginKey, ReadonlySet<PluginKey>>();\n\t\tfor (const [k, v] of downstream) downstreamFrozen.set(k, v);\n\n\t\treturn {\n\t\t\tnodes,\n\t\t\tlevels,\n\t\t\tdownstream: downstreamFrozen,\n\t\t} satisfies ResolvedGraph;\n\t}).pipe(Effect.withSpan('lifecycle.dep-graph.resolve'));\n\n// -----------------------------------------------------------------------------\n// Selective-restart helpers\n// -----------------------------------------------------------------------------\n\n/**\n * Compute the closure of nodes downstream of `root` (inclusive).\n * Used by the supervisor to tear down a subgraph in reverse-dep order\n * and re-acquire in forward order.\n *\n * Architecture § Watch-triggered invalidation:\n * Invalidating a producer invalidates downstream consumers along\n * dep-graph edges (cascade semantics).\n */\nexport const downstreamClosure = (\n\tgraph: ResolvedGraph,\n\troot: PluginKey,\n): ReadonlySet<PluginKey> => {\n\tconst closure = new Set<PluginKey>([root]);\n\tconst queue: PluginKey[] = [root];\n\twhile (queue.length > 0) {\n\t\tconst next = queue.shift()!;\n\t\tfor (const child of graph.downstream.get(next) ?? []) {\n\t\t\tif (!closure.has(child)) {\n\t\t\t\tclosure.add(child);\n\t\t\t\tqueue.push(child);\n\t\t\t}\n\t\t}\n\t}\n\treturn closure;\n};\n\n/** Order a set of keys by their dep-graph level — forward (low-to-high)\n * for acquire, reverse (high-to-low) for teardown. */\nexport const orderByLevel = (\n\tgraph: ResolvedGraph,\n\tkeys: ReadonlySet<PluginKey>,\n\tdirection: 'forward' | 'reverse',\n): ReadonlyArray<PluginKey> => {\n\tconst out: PluginKey[] = [];\n\tfor (const level of graph.levels) {\n\t\tfor (const key of level) if (keys.has(key)) out.push(key);\n\t}\n\treturn direction === 'reverse' ? out.slice().reverse() : out;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"dep-graph.mjs","names":["makePluginKey"],"sources":["../../../../src/substrate/runtime/lifecycle/dep-graph.ts"],"sourcesContent":["// Dep-graph resolution — turn a `Stack` member tuple into a topo-sorted\n// acquire schedule.\n//\n// Architecture § Plugin lifecycle within a stack:\n// \"Resolution. Upstream keys (concrete or capability-typed) resolve\n// once. Bootstrap asset dedup-by-key fires before the scheduler\n// starts node fibers. Scheduling. Plugin enters `pending`. When all\n// upstream keys are `ready`, the node begins `acquiring`. Per-key\n// serialization: only one acquire at a time per key.\"\n//\n// This module is the pure dep-graph math: members in, levels out.\n\nimport { Data, Effect } from 'effect';\n\nimport type { PluginKey } from '../../brand.ts';\nimport { pluginKey as makePluginKey } from '../../brand.ts';\nimport { uniqueResourceRefs, type AnyPlugin, type AnyResourceRef } from '../../plugin.ts';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** Cycle detected in the dep graph. Carries the offending key path so\n * the user sees the loop. Compile-time `defineDevstack` doesn't catch\n * every cycle (capability-typed consumes resolve at runtime), so this\n * surfaces a typed error at boot. */\nexport class DepGraphCycleError extends Data.TaggedError('DepGraphCycleError')<{\n\treadonly cycle: ReadonlyArray<PluginKey>;\n}> {}\n\n/** A dependency resource matched no provider. This is normally caught by\n * the compile-time `MissingProviders` check, but runtime-built stacks can\n * hit it. */\nexport class UnresolvedDependencyError extends Data.TaggedError('UnresolvedDependencyError')<{\n\treadonly pluginKey: PluginKey;\n\treadonly missingResourceId: string;\n}> {}\n\n/** Two members declared the same `resource id`. The compile-time\n * `MissingProviders` check only catches collisions on the user-typed\n * dependency path, NOT two members declared in the same stack with the\n * same `definePlugin({ id })`. Without this guard the dep-graph would\n * last-writer-wins the provider index and silently bind every dependent\n * to the second declaration. Phase 22e/E3 added a complementary\n * package-plugin-level guard (`plugins/package/mode-local.ts`) for the\n * registry path; this surfaces the same class of mistake for ALL plugins\n * at the substrate dep-graph layer. */\nexport class DuplicateResourceIdError extends Data.TaggedError('DuplicateResourceIdError')<{\n\treadonly resourceId: string;\n\treadonly firstPluginKey: PluginKey;\n\treadonly secondPluginKey: PluginKey;\n}> {}\n\nexport type DepGraphError =\n\t| DepGraphCycleError\n\t| UnresolvedDependencyError\n\t| DuplicateResourceIdError;\n\n// -----------------------------------------------------------------------------\n// Node shape\n// -----------------------------------------------------------------------------\n\n/** A resolved node in the dep graph. Wraps the original plugin with a\n * substrate-assigned `PluginKey`. */\nexport interface DepNode {\n\treadonly key: PluginKey;\n\treadonly member: AnyPlugin;\n\t/** Unique dependency refs and their resolved upstream keys. The two\n\t * arrays are positional peers. */\n\treadonly upstreamResources: ReadonlyArray<AnyResourceRef>;\n\treadonly upstreamKeys: ReadonlyArray<PluginKey>;\n}\n\n/** The result of resolving a member tuple. Levels are batches the\n * scheduler can acquire in parallel; each level's nodes only depend\n * on prior levels. */\nexport interface ResolvedGraph {\n\treadonly nodes: ReadonlyMap<PluginKey, DepNode>;\n\treadonly levels: ReadonlyArray<ReadonlyArray<PluginKey>>;\n\t/** Reverse-dep index: each key → keys that depend on it. The\n\t * supervisor uses this for selective-restart cascade. */\n\treadonly downstream: ReadonlyMap<PluginKey, ReadonlySet<PluginKey>>;\n}\n\n// -----------------------------------------------------------------------------\n// Plugin key minting\n// -----------------------------------------------------------------------------\n\ninterface NamedMember {\n\treadonly key: PluginKey;\n\treadonly member: AnyPlugin;\n}\n\n/** Mint a stable PluginKey for a plugin. Plugins may declare a stable\n * key; otherwise keys derive from resource id + ordinal so duplicates\n * do not collide. */\nconst mintKey = (member: AnyPlugin, ordinal: number): PluginKey => {\n\tif (member.pluginKey !== undefined) {\n\t\treturn makePluginKey(String(member.pluginKey));\n\t}\n\treturn makePluginKey(`${member.id}#${ordinal}`);\n};\n\n/** Walk the member tuple and emit one named-member entry per node. */\nconst expand = (members: ReadonlyArray<AnyPlugin>): ReadonlyArray<NamedMember> => {\n\tconst out: NamedMember[] = [];\n\tlet ordinal = 0;\n\tfor (const member of members) {\n\t\tconst key = mintKey(member, ordinal++);\n\t\tout.push({ key, member });\n\t}\n\treturn out;\n};\n\n// -----------------------------------------------------------------------------\n// Topological sort (Kahn's algorithm)\n// -----------------------------------------------------------------------------\n\n/**\n * Resolve a flat member tuple into a topo-sorted level batch.\n *\n * Steps:\n * 1. Mint a plugin key for every member.\n * 2. Build the resource-id -> key index (providers). Two members with\n * the same `id` fail with `DuplicateResourceIdError`.\n * 3. For each node, resolve its dependency refs to upstream keys.\n * 4. Kahn's algorithm: peel zero-indegree nodes into level 0, decrement\n * downstream indegrees, repeat.\n * 5. If any node remains, return `DepGraphCycleError`.\n *\n * Span-instrumented so trace exports show the resolution alongside the\n * supervisor's acquire spans.\n */\nexport const resolveGraph = (\n\tmembers: ReadonlyArray<AnyPlugin>,\n): Effect.Effect<ResolvedGraph, DepGraphError> =>\n\tEffect.gen(function* () {\n\t\tyield* Effect.annotateCurrentSpan({ 'devstack.dep-graph.memberCount': members.length });\n\t\tconst named = expand(members);\n\n\t\t// Provider index: resource-id → key. The compile-time\n\t\t// `MissingProviders` check ensures uniqueness on the user-typed\n\t\t// dependency path, but a runtime-built stack (or a typo'd\n\t\t// `definePlugin({ id })`) can still ship two members with the\n\t\t// same `resource id`. Without an explicit guard the index would\n\t\t// last-writer-wins and every dependent would silently bind to\n\t\t// the second declaration; we fail early with a typed error\n\t\t// instead so a developer sees the conflicting plugin keys at\n\t\t// boot rather than chasing a \"wrong provider\" mystery downstream.\n\t\t// Phase 22e/E3's package-plugin-level guard in\n\t\t// `plugins/package/mode-local.ts` is complementary — it fires on\n\t\t// the registry path before this resolver runs.\n\t\tconst providerByResourceId = new Map<string, PluginKey>();\n\t\tfor (const { key, member } of named) {\n\t\t\tconst existing = providerByResourceId.get(member.id);\n\t\t\tif (existing !== undefined) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew DuplicateResourceIdError({\n\t\t\t\t\t\tresourceId: member.id,\n\t\t\t\t\t\tfirstPluginKey: existing,\n\t\t\t\t\t\tsecondPluginKey: key,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tproviderByResourceId.set(member.id, key);\n\t\t}\n\n\t\tconst nodes = new Map<PluginKey, DepNode>();\n\t\tconst indegree = new Map<PluginKey, number>();\n\t\tconst downstream = new Map<PluginKey, Set<PluginKey>>();\n\t\tfor (const { key } of named) {\n\t\t\tindegree.set(key, 0);\n\t\t\tdownstream.set(key, new Set());\n\t\t}\n\n\t\tfor (const { key, member } of named) {\n\t\t\tconst upstreamKeys: PluginKey[] = [];\n\t\t\tconst upstreamResources = uniqueResourceRefs(member.dependsOn);\n\t\t\tfor (const resource of upstreamResources) {\n\t\t\t\tconst providerKey = providerByResourceId.get(resource.id);\n\t\t\t\tif (providerKey === undefined) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew UnresolvedDependencyError({ pluginKey: key, missingResourceId: resource.id }),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tupstreamKeys.push(providerKey);\n\t\t\t\tdownstream.get(providerKey)?.add(key);\n\t\t\t\tindegree.set(key, (indegree.get(key) ?? 0) + 1);\n\t\t\t}\n\t\t\tnodes.set(key, {\n\t\t\t\tkey,\n\t\t\t\tmember,\n\t\t\t\tupstreamResources,\n\t\t\t\tupstreamKeys,\n\t\t\t});\n\t\t}\n\n\t\t// Kahn's algorithm — peel zero-indegree nodes level by level.\n\t\tconst levels: PluginKey[][] = [];\n\t\tconst settled = new Set<PluginKey>();\n\t\twhile (settled.size < nodes.size) {\n\t\t\tconst layer: PluginKey[] = [];\n\t\t\tfor (const [key, deg] of indegree) {\n\t\t\t\tif (settled.has(key)) continue;\n\t\t\t\tif (deg === 0) layer.push(key);\n\t\t\t}\n\t\t\tif (layer.length === 0) {\n\t\t\t\t// Cycle: collect every unsettled node as the cycle's witness.\n\t\t\t\t// More precise cycle extraction (Tarjan SCC) is overkill for\n\t\t\t\t// the user-facing message; we surface the trapped keys.\n\t\t\t\tconst trapped: PluginKey[] = [];\n\t\t\t\tfor (const key of nodes.keys()) {\n\t\t\t\t\tif (!settled.has(key)) trapped.push(key);\n\t\t\t\t}\n\t\t\t\treturn yield* Effect.fail(new DepGraphCycleError({ cycle: trapped }));\n\t\t\t}\n\t\t\tlevels.push(layer);\n\t\t\tfor (const key of layer) {\n\t\t\t\tsettled.add(key);\n\t\t\t\tfor (const child of downstream.get(key) ?? []) {\n\t\t\t\t\tindegree.set(child, (indegree.get(child) ?? 1) - 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Freeze downstream sets to ReadonlySet shape.\n\t\tconst downstreamFrozen = new Map<PluginKey, ReadonlySet<PluginKey>>();\n\t\tfor (const [k, v] of downstream) downstreamFrozen.set(k, v);\n\n\t\treturn {\n\t\t\tnodes,\n\t\t\tlevels,\n\t\t\tdownstream: downstreamFrozen,\n\t\t} satisfies ResolvedGraph;\n\t}).pipe(Effect.withSpan('lifecycle.dep-graph.resolve'));\n\n// -----------------------------------------------------------------------------\n// Selective-restart helpers\n// -----------------------------------------------------------------------------\n\n/**\n * Compute the closure of nodes downstream of `root` (inclusive).\n * Used by the supervisor to tear down a subgraph in reverse-dep order\n * and re-acquire in forward order.\n *\n * Architecture § Watch-triggered invalidation:\n * Invalidating a producer invalidates downstream consumers along\n * dep-graph edges (cascade semantics).\n */\nexport const downstreamClosure = (\n\tgraph: ResolvedGraph,\n\troot: PluginKey,\n): ReadonlySet<PluginKey> => {\n\tconst closure = new Set<PluginKey>([root]);\n\tconst queue: PluginKey[] = [root];\n\twhile (queue.length > 0) {\n\t\tconst next = queue.shift()!;\n\t\tfor (const child of graph.downstream.get(next) ?? []) {\n\t\t\tif (!closure.has(child)) {\n\t\t\t\tclosure.add(child);\n\t\t\t\tqueue.push(child);\n\t\t\t}\n\t\t}\n\t}\n\treturn closure;\n};\n\n/** Order a set of keys by their dep-graph level — forward (low-to-high)\n * for acquire, reverse (high-to-low) for teardown. */\nexport const orderByLevel = (\n\tgraph: ResolvedGraph,\n\tkeys: ReadonlySet<PluginKey>,\n\tdirection: 'forward' | 'reverse',\n): ReadonlyArray<PluginKey> => {\n\tconst out: PluginKey[] = [];\n\tfor (const level of graph.levels) {\n\t\tfor (const key of level) if (keys.has(key)) out.push(key);\n\t}\n\treturn direction === 'reverse' ? out.slice().reverse() : out;\n};\n"],"mappings":";;;;;;;;AA0BA,IAAa,qBAAb,cAAwC,KAAK,YAAY,qBAAqB,CAE3E;;;;AAKH,IAAa,4BAAb,cAA+C,KAAK,YAAY,4BAA4B,CAGzF;;;;;;;;;;AAWH,IAAa,2BAAb,cAA8C,KAAK,YAAY,2BAA2B,CAIvF;;;;AA6CH,MAAM,WAAW,QAAmB,YAA+B;AAClE,KAAI,OAAO,cAAc,KAAA,EACxB,QAAOA,UAAc,OAAO,OAAO,UAAU,CAAC;AAE/C,QAAOA,UAAc,GAAG,OAAO,GAAG,GAAG,UAAU;;;AAIhD,MAAM,UAAU,YAAkE;CACjF,MAAM,MAAqB,EAAE;CAC7B,IAAI,UAAU;AACd,MAAK,MAAM,UAAU,SAAS;EAC7B,MAAM,MAAM,QAAQ,QAAQ,UAAU;AACtC,MAAI,KAAK;GAAE;GAAK;GAAQ,CAAC;;AAE1B,QAAO;;;;;;;;;;;;;;;;;AAsBR,MAAa,gBACZ,YAEA,OAAO,IAAI,aAAa;AACvB,QAAO,OAAO,oBAAoB,EAAE,kCAAkC,QAAQ,QAAQ,CAAC;CACvF,MAAM,QAAQ,OAAO,QAAQ;CAc7B,MAAM,uCAAuB,IAAI,KAAwB;AACzD,MAAK,MAAM,EAAE,KAAK,YAAY,OAAO;EACpC,MAAM,WAAW,qBAAqB,IAAI,OAAO,GAAG;AACpD,MAAI,aAAa,KAAA,EAChB,QAAO,OAAO,OAAO,KACpB,IAAI,yBAAyB;GAC5B,YAAY,OAAO;GACnB,gBAAgB;GAChB,iBAAiB;GACjB,CAAC,CACF;AAEF,uBAAqB,IAAI,OAAO,IAAI,IAAI;;CAGzC,MAAM,wBAAQ,IAAI,KAAyB;CAC3C,MAAM,2BAAW,IAAI,KAAwB;CAC7C,MAAM,6BAAa,IAAI,KAAgC;AACvD,MAAK,MAAM,EAAE,SAAS,OAAO;AAC5B,WAAS,IAAI,KAAK,EAAE;AACpB,aAAW,IAAI,qBAAK,IAAI,KAAK,CAAC;;AAG/B,MAAK,MAAM,EAAE,KAAK,YAAY,OAAO;EACpC,MAAM,eAA4B,EAAE;EACpC,MAAM,oBAAoB,mBAAmB,OAAO,UAAU;AAC9D,OAAK,MAAM,YAAY,mBAAmB;GACzC,MAAM,cAAc,qBAAqB,IAAI,SAAS,GAAG;AACzD,OAAI,gBAAgB,KAAA,EACnB,QAAO,OAAO,OAAO,KACpB,IAAI,0BAA0B;IAAE,WAAW;IAAK,mBAAmB,SAAS;IAAI,CAAC,CACjF;AAEF,gBAAa,KAAK,YAAY;AAC9B,cAAW,IAAI,YAAY,EAAE,IAAI,IAAI;AACrC,YAAS,IAAI,MAAM,SAAS,IAAI,IAAI,IAAI,KAAK,EAAE;;AAEhD,QAAM,IAAI,KAAK;GACd;GACA;GACA;GACA;GACA,CAAC;;CAIH,MAAM,SAAwB,EAAE;CAChC,MAAM,0BAAU,IAAI,KAAgB;AACpC,QAAO,QAAQ,OAAO,MAAM,MAAM;EACjC,MAAM,QAAqB,EAAE;AAC7B,OAAK,MAAM,CAAC,KAAK,QAAQ,UAAU;AAClC,OAAI,QAAQ,IAAI,IAAI,CAAE;AACtB,OAAI,QAAQ,EAAG,OAAM,KAAK,IAAI;;AAE/B,MAAI,MAAM,WAAW,GAAG;GAIvB,MAAM,UAAuB,EAAE;AAC/B,QAAK,MAAM,OAAO,MAAM,MAAM,CAC7B,KAAI,CAAC,QAAQ,IAAI,IAAI,CAAE,SAAQ,KAAK,IAAI;AAEzC,UAAO,OAAO,OAAO,KAAK,IAAI,mBAAmB,EAAE,OAAO,SAAS,CAAC,CAAC;;AAEtE,SAAO,KAAK,MAAM;AAClB,OAAK,MAAM,OAAO,OAAO;AACxB,WAAQ,IAAI,IAAI;AAChB,QAAK,MAAM,SAAS,WAAW,IAAI,IAAI,IAAI,EAAE,CAC5C,UAAS,IAAI,QAAQ,SAAS,IAAI,MAAM,IAAI,KAAK,EAAE;;;CAMtD,MAAM,mCAAmB,IAAI,KAAwC;AACrE,MAAK,MAAM,CAAC,GAAG,MAAM,WAAY,kBAAiB,IAAI,GAAG,EAAE;AAE3D,QAAO;EACN;EACA;EACA,YAAY;EACZ;EACA,CAAC,KAAK,OAAO,SAAS,8BAA8B,CAAC;;;;;;;;;;AAexD,MAAa,qBACZ,OACA,SAC4B;CAC5B,MAAM,UAAU,IAAI,IAAe,CAAC,KAAK,CAAC;CAC1C,MAAM,QAAqB,CAAC,KAAK;AACjC,QAAO,MAAM,SAAS,GAAG;EACxB,MAAM,OAAO,MAAM,OAAO;AAC1B,OAAK,MAAM,SAAS,MAAM,WAAW,IAAI,KAAK,IAAI,EAAE,CACnD,KAAI,CAAC,QAAQ,IAAI,MAAM,EAAE;AACxB,WAAQ,IAAI,MAAM;AAClB,SAAM,KAAK,MAAM;;;AAIpB,QAAO;;;;AAKR,MAAa,gBACZ,OACA,MACA,cAC8B;CAC9B,MAAM,MAAmB,EAAE;AAC3B,MAAK,MAAM,SAAS,MAAM,OACzB,MAAK,MAAM,OAAO,MAAO,KAAI,KAAK,IAAI,IAAI,CAAE,KAAI,KAAK,IAAI;AAE1D,QAAO,cAAc,YAAY,IAAI,OAAO,CAAC,SAAS,GAAG"}
@@ -0,0 +1,9 @@
1
+ import "./state-machine.mjs";
2
+ import "./plugin-registry.mjs";
3
+ import "./lifecycle-fact.mjs";
4
+ import "./dep-graph.mjs";
5
+ import "./ready-gate.mjs";
6
+ import "./watch-attribution.mjs";
7
+ import "./selective-restart.mjs";
8
+ import "./signals.mjs";
9
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-fact.mjs","names":[],"sources":["../../../../src/substrate/runtime/lifecycle/lifecycle-fact.ts"],"sourcesContent":["// LifecycleFact bridge.\n//\n// `substrate/lifecycle.ts` declares `LifecycleFact` — the merge-not-\n// replace per-plugin lifecycle slice the projection consumes. Until\n// now the projection updater consumed raw `EngineEvent` cases directly\n// and the `LifecycleFact` type was orphan.\n//\n// This module is the typed bridge: it projects an `EngineEvent` into a\n// `LifecycleFact` delta (when the event represents a lifecycle change)\n// and exposes a single applier `applyLifecycleFact(row, fact)` the\n// projection updater calls instead of writing out each\n// status / phase / restart field independently. The reducer for non-\n// lifecycle events is unchanged.\n\nimport type { Row } from '../../projection.ts';\nimport type { LifecycleFact, LifecycleStatus } from '../../lifecycle.ts';\nimport type { EngineEvent } from '../../events.ts';\n\n/** Per-plugin fact delta. Each field is optional — only the fields the\n * source event carries are populated. The projection's `applyLifecycleFact`\n * merges a 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: string; 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/** Reconstruct the closed `LifecycleFact` shape from a `Row`. Used by\n * diagnostic surfaces that want the typed fact instead of the wider\n * display Row. */\nexport const factFromRow = (row: Row): LifecycleFact => ({\n\tstatus: row.status,\n\tphase: row.phase,\n\tselectiveRestartHighlight: row.selectiveRestartHighlight,\n});\n"],"mappings":";;;;;AA+BA,MAAa,iBACZ,UAC+E;AAC/E,SAAQ,MAAM,KAAd;EACC,KAAK,0BACJ,QAAO;GACN,WAAW,MAAM;GACjB,OAAO,EAAE,QAAQ,MAAM,IAAI;GAC3B;EACF,KAAK,qBACJ,QAAO;GACN,WAAW,MAAM;GACjB,OAAO,EAAE,OAAO,MAAM,OAAO;GAC7B;EACF,QAQC,QAAO;;;;;;AAOV,MAAa,sBAAsB,KAAU,WAAoC;CAChF,GAAG;CACH,GAAI,MAAM,WAAW,KAAA,IAAY,EAAE,QAAQ,MAAM,QAAQ,GAAG,EAAE;CAC9D,GAAI,MAAM,UAAU,KAAA,IAAY,EAAE,OAAO,MAAM,OAAO,GAAG,EAAE;CAC3D,GAAI,MAAM,8BAA8B,KAAA,IACrC,EAAE,2BAA2B,MAAM,2BAA2B,GAC9D,EAAE;CACL"}
1
+ {"version":3,"file":"lifecycle-fact.mjs","names":[],"sources":["../../../../src/substrate/runtime/lifecycle/lifecycle-fact.ts"],"sourcesContent":["// LifecycleFact bridge.\n//\n// `substrate/lifecycle.ts` declares `LifecycleFact` — the merge-not-\n// replace per-plugin lifecycle slice the projection consumes. Until\n// now the projection updater consumed raw `EngineEvent` cases directly\n// and the `LifecycleFact` type was orphan.\n//\n// This module is the typed bridge: it projects an `EngineEvent` into a\n// `LifecycleFact` delta (when the event represents a lifecycle change)\n// and exposes a single applier `applyLifecycleFact(row, fact)` the\n// projection updater calls instead of writing out each\n// status / phase / restart field independently. The reducer for non-\n// lifecycle events is unchanged.\n\nimport type { PluginKey } from '../../brand.ts';\nimport type { Row } from '../../projection.ts';\nimport type { LifecycleFact, LifecycleStatus } from '../../lifecycle.ts';\nimport type { EngineEvent } from '../../events.ts';\n\n/** Per-plugin fact delta. Each field is optional — only the fields the\n * source event carries are populated. The projection's `applyLifecycleFact`\n * merges a 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/** Reconstruct the closed `LifecycleFact` shape from a `Row`. Used by\n * diagnostic surfaces that want the typed fact instead of the wider\n * display Row. */\nexport const factFromRow = (row: Row): LifecycleFact => ({\n\tstatus: row.status,\n\tphase: row.phase,\n\tselectiveRestartHighlight: row.selectiveRestartHighlight,\n});\n"],"mappings":";;;;;AAgCA,MAAa,iBACZ,UACkF;AAClF,SAAQ,MAAM,KAAd;EACC,KAAK,0BACJ,QAAO;GACN,WAAW,MAAM;GACjB,OAAO,EAAE,QAAQ,MAAM,IAAI;GAC3B;EACF,KAAK,qBACJ,QAAO;GACN,WAAW,MAAM;GACjB,OAAO,EAAE,OAAO,MAAM,OAAO;GAC7B;EACF,QAQC,QAAO;;;;;;AAOV,MAAa,sBAAsB,KAAU,WAAoC;CAChF,GAAG;CACH,GAAI,MAAM,WAAW,KAAA,IAAY,EAAE,QAAQ,MAAM,QAAQ,GAAG,EAAE;CAC9D,GAAI,MAAM,UAAU,KAAA,IAAY,EAAE,OAAO,MAAM,OAAO,GAAG,EAAE;CAC3D,GAAI,MAAM,8BAA8B,KAAA,IACrC,EAAE,2BAA2B,MAAM,2BAA2B,GAC9D,EAAE;CACL"}
@@ -88,6 +88,9 @@ const makeRegistry = (entries, onTransition) => {
88
88
  resolved.delete(key);
89
89
  const readyGate = yield* Deferred.make();
90
90
  const scope = yield* Scope.fork(parentScope);
91
+ const from = yield* Ref.get(entry.statusRef);
92
+ yield* Ref.set(entry.statusRef, "pending");
93
+ if (from !== "pending") yield* onTransition(key, from, "pending");
91
94
  mutableEntries.set(key, {
92
95
  node: entry.node,
93
96
  statusRef: entry.statusRef,
@@ -121,13 +124,27 @@ const buildDependencyReaderFor = (registry, node) => {
121
124
  });
122
125
  return (resource) => {
123
126
  const key = resourceIdToKey.get(resource.id);
124
- if (key === void 0) throw new Error(`DependencyReader: resource '${resource.id}' not in this plugin's declared dependencies`);
127
+ if (key === void 0) throw new DependencyReaderViolation({
128
+ kind: "undeclared-dependency",
129
+ target: resource.id
130
+ });
125
131
  const value = readResolvedSync(registry, key);
126
- if (value === void 0) throw new Error(`DependencyReader: upstream '${key}' has no resolved value yet — supervisor must mark the entry ready before downstream acquire.`);
132
+ if (value === void 0) throw new DependencyReaderViolation({
133
+ kind: "unresolved-upstream",
134
+ target: key
135
+ });
127
136
  return value;
128
137
  };
129
138
  };
139
+ /** Tagged defect surfaced when a plugin's `start` callback reads a
140
+ * dependency outside its declared `dependsOn` set, or an upstream
141
+ * that hasn't been marked ready yet. Throws synchronously inside the
142
+ * dependency-reader closure; the Effect runtime captures it as a
143
+ * defect the cascade-formatter projects via `_tag`. The `kind`
144
+ * discriminator and `target` carry the offending name; consumers
145
+ * read both. */
146
+ var DependencyReaderViolation = class extends Data.TaggedError("DependencyReaderViolation") {};
130
147
  //#endregion
131
- export { buildDependencyReaderFor, makeEntry, makeRegistry, readResolvedSync };
148
+ export { DependencyReaderViolation, PluginAcquireFailed, UnknownDependency, buildDependencyReaderFor, makeEntry, makeRegistry, readResolvedSync };
132
149
 
133
150
  //# sourceMappingURL=plugin-registry.mjs.map