@mysten-incubation/devstack 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (838) hide show
  1. package/README.md +7 -3
  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 +40 -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,4 +1,5 @@
1
1
  import { defineScopedRefMap } from "../../substrate/runtime/scoped-ref-map/service.mjs";
2
+ import "../../substrate/runtime/scoped-ref-map/index.mjs";
2
3
  import { Context, Effect, Layer } from "effect";
3
4
  //#region src/plugins/package/registry.ts
4
5
  const PackageRefMap = defineScopedRefMap("PackageRegistry");
@@ -11,7 +12,7 @@ const wrapRefMap = (refMap) => ({
11
12
  });
12
13
  /** Context.Service tag for the per-stack `PackageRegistry`. Plugins
13
14
  * yield this in their acquire body. */
14
- var PackageRegistryService = class extends Context.Service()("@devstack-rewrite/plugins/package/PackageRegistry") {};
15
+ var PackageRegistryService = class extends Context.Service()("@devstack/plugins/package/PackageRegistry") {};
15
16
  /** Scope-bound Layer materializing one `PackageRegistry` per stack
16
17
  * scope. Boot wiring (CLI / e2e) provides this once per stack;
17
18
  * every package/coin/wallet/faucet plugin in the stack yields the
@@ -1 +1 @@
1
- {"version":3,"file":"registry.mjs","names":[],"sources":["../../../src/plugins/package/registry.ts"],"sourcesContent":["// Per-stack PackageRegistry — owned by the L2 Package plugin.\n//\n// Architecture (ARCHITECTURE.md § Substrate name-blindness): the\n// substrate exposes ONLY the generic `defineScopedRefMap<K, V>` factory;\n// the package-domain shape (`ResolvedLocalPackage` / `ResolvedKnownPackage`)\n// lives here at L2 where it belongs. Two `localPackage(...)` calls in\n// the same stack see the same Service instance (one per stack scope),\n// so cross-plugin lookups stay consistent and warm-restart verify can\n// use the previously-resolved `packageId` as a hint.\n//\n// Wrapper-service pattern (STYLE_GUIDE §6 / \"L2 wrapper-service around\n// defineScopedRefMap\"): the module-private inner `PackageRefMap` is the\n// raw substrate primitive; the publicly-exported `PackageRegistryService`\n// is the L2 wrapper that the rest of the plugin (and external siblings)\n// yield. Today the wrapper only re-exposes the four substrate ops\n// (`set` / `find` / `has` / `entries` + `changes`), but the shape\n// matches Coin's wrapper so future plugin-specific lookups (e.g.\n// `byPublisher`, `byPackageId`) land in one place instead of every\n// consumer reaching into the raw map.\n\nimport { Context, Effect, Layer, type Stream } from 'effect';\n\nimport {\n\tdefineScopedRefMap,\n\ttype ScopedRefMap,\n} from '../../substrate/runtime/scoped-ref-map/index.ts';\n\n/** Resolved package handle for a local (built + published) package. */\nexport interface ResolvedLocalPackage {\n\treadonly kind: 'local';\n\treadonly name: string;\n\treadonly packageId: string;\n\treadonly upgradeCapId?: string;\n\treadonly sourcePath: string;\n\treadonly mvrPlaceholder: string;\n\t/** Captured object ids — keyed by user-declared name. */\n\treadonly captured: Readonly<Record<string, string>>;\n}\n\n/** Resolved package handle for a known (verify-only) package. */\nexport interface ResolvedKnownPackage {\n\treadonly kind: 'known';\n\treadonly name: string;\n\treadonly packageId: string;\n\treadonly upgradeCapId?: string;\n\treadonly mvrPlaceholder: string;\n}\n\nexport type ResolvedPackage = ResolvedLocalPackage | ResolvedKnownPackage;\n\n/** Registry key — the user-declared symbolic package name. Kept as a\n * type alias (not a brand) because the same string flows verbatim\n * through `localPackage(name, ...)` / `knownPackage(name, ...)` and\n * out via `entries()` to consumers (codegen, snapshot identity). */\nexport type PackageKey = string;\n\n/** L2 wrapper shape — the operations exposed on `PackageRegistryService`.\n * Today these are a 1:1 re-projection of the substrate primitive's\n * generic `ScopedRefMap<K, V>`; future plugin-specific lookups land\n * here without forcing every consumer to learn a new shape. */\nexport interface PackageRegistry {\n\treadonly set: (key: PackageKey, value: ResolvedPackage) => Effect.Effect<void>;\n\treadonly find: (key: PackageKey) => Effect.Effect<ResolvedPackage | null>;\n\treadonly has: (key: PackageKey) => Effect.Effect<boolean>;\n\treadonly entries: () => Effect.Effect<ReadonlyArray<readonly [PackageKey, ResolvedPackage]>>;\n\treadonly changes: Stream.Stream<ReadonlyArray<readonly [PackageKey, ResolvedPackage]>>;\n}\n\n// Module-private inner substrate primitive — instantiated once per\n// logical registry. The service identity is namespaced by the `name`\n// argument; substrate stays name-blind (it sees only `K` and `V`).\nconst PackageRefMap = defineScopedRefMap<PackageKey, ResolvedPackage>('PackageRegistry');\n\nconst wrapRefMap = (refMap: ScopedRefMap<PackageKey, ResolvedPackage>): PackageRegistry => ({\n\tset: refMap.set,\n\tfind: refMap.find,\n\thas: refMap.has,\n\tentries: refMap.entries,\n\tchanges: refMap.changes,\n});\n\n/** Context.Service tag for the per-stack `PackageRegistry`. Plugins\n * yield this in their acquire body. */\nexport class PackageRegistryService extends Context.Service<\n\tPackageRegistryService,\n\tPackageRegistry\n>()('@devstack-rewrite/plugins/package/PackageRegistry') {}\n\n/** Scope-bound Layer materializing one `PackageRegistry` per stack\n * scope. Boot wiring (CLI / e2e) provides this once per stack;\n * every package/coin/wallet/faucet plugin in the stack yields the\n * SAME instance via Context. */\nexport const layerPackageRegistry: Layer.Layer<PackageRegistryService> = Layer.effect(\n\tPackageRegistryService,\n\tEffect.gen(function* () {\n\t\tconst refMap = yield* PackageRefMap.Service;\n\t\treturn PackageRegistryService.of(wrapRefMap(refMap));\n\t}),\n).pipe(Layer.provide(PackageRefMap.layer));\n\n/** Capability-key constant for the per-stack registry — siblings\n * (Coin, Action, manifest emitter, faucet strategies) look it up\n * through the StrategyContributor registry. */\nexport const PACKAGE_REGISTRY_CAPABILITY_KEY = 'package-registry' as const;\n"],"mappings":";;;AAuEA,MAAM,gBAAgB,mBAAgD,kBAAkB;AAExF,MAAM,cAAc,YAAwE;CAC3F,KAAK,OAAO;CACZ,MAAM,OAAO;CACb,KAAK,OAAO;CACZ,SAAS,OAAO;CAChB,SAAS,OAAO;CAChB;;;AAID,IAAa,yBAAb,cAA4C,QAAQ,SAGjD,CAAC,oDAAoD,CAAC;;;;;AAMzD,MAAa,uBAA4D,MAAM,OAC9E,wBACA,OAAO,IAAI,aAAa;CACvB,MAAM,SAAS,OAAO,cAAc;AACpC,QAAO,uBAAuB,GAAG,WAAW,OAAO,CAAC;EACnD,CACF,CAAC,KAAK,MAAM,QAAQ,cAAc,MAAM,CAAC;;;;AAK1C,MAAa,kCAAkC"}
1
+ {"version":3,"file":"registry.mjs","names":[],"sources":["../../../src/plugins/package/registry.ts"],"sourcesContent":["// Per-stack PackageRegistry — owned by the L2 Package plugin.\n//\n// Architecture (ARCHITECTURE.md § Substrate name-blindness): the\n// substrate exposes ONLY the generic `defineScopedRefMap<K, V>` factory;\n// the package-domain shape (`ResolvedLocalPackage` / `ResolvedKnownPackage`)\n// lives here at L2 where it belongs. Two `localPackage(...)` calls in\n// the same stack see the same Service instance (one per stack scope),\n// so cross-plugin lookups stay consistent and warm-restart verify can\n// use the previously-resolved `packageId` as a hint.\n//\n// Wrapper-service pattern (STYLE_GUIDE §6 / \"L2 wrapper-service around\n// defineScopedRefMap\"): the module-private inner `PackageRefMap` is the\n// raw substrate primitive; the publicly-exported `PackageRegistryService`\n// is the L2 wrapper that the rest of the plugin (and external siblings)\n// yield. Today the wrapper only re-exposes the four substrate ops\n// (`set` / `find` / `has` / `entries` + `changes`), but the shape\n// matches Coin's wrapper so future plugin-specific lookups (e.g.\n// `byPublisher`, `byPackageId`) land in one place instead of every\n// consumer reaching into the raw map.\n\nimport { Context, Effect, Layer, type Stream } from 'effect';\n\nimport {\n\tdefineScopedRefMap,\n\ttype ScopedRefMap,\n} from '../../substrate/runtime/scoped-ref-map/index.ts';\n\n/** Resolved package handle for a local (built + published) package. */\nexport interface ResolvedLocalPackage {\n\treadonly kind: 'local';\n\treadonly name: string;\n\treadonly packageId: string;\n\treadonly upgradeCapId?: string;\n\treadonly sourcePath: string;\n\treadonly mvrPlaceholder: string;\n\t/** Captured object ids — keyed by user-declared name. */\n\treadonly captured: Readonly<Record<string, string>>;\n}\n\n/** Resolved package handle for a known (verify-only) package. */\nexport interface ResolvedKnownPackage {\n\treadonly kind: 'known';\n\treadonly name: string;\n\treadonly packageId: string;\n\treadonly upgradeCapId?: string;\n\treadonly mvrPlaceholder: string;\n}\n\nexport type ResolvedPackage = ResolvedLocalPackage | ResolvedKnownPackage;\n\n/** Registry key — the user-declared symbolic package name. Kept as a\n * type alias (not a brand) because the same string flows verbatim\n * through `localPackage(name, ...)` / `knownPackage(name, ...)` and\n * out via `entries()` to consumers (codegen, snapshot identity). */\nexport type PackageKey = string;\n\n/** L2 wrapper shape — the operations exposed on `PackageRegistryService`.\n * Today these are a 1:1 re-projection of the substrate primitive's\n * generic `ScopedRefMap<K, V>`; future plugin-specific lookups land\n * here without forcing every consumer to learn a new shape. */\nexport interface PackageRegistry {\n\treadonly set: (key: PackageKey, value: ResolvedPackage) => Effect.Effect<void>;\n\treadonly find: (key: PackageKey) => Effect.Effect<ResolvedPackage | null>;\n\treadonly has: (key: PackageKey) => Effect.Effect<boolean>;\n\treadonly entries: () => Effect.Effect<ReadonlyArray<readonly [PackageKey, ResolvedPackage]>>;\n\treadonly changes: Stream.Stream<ReadonlyArray<readonly [PackageKey, ResolvedPackage]>>;\n}\n\n// Module-private inner substrate primitive — instantiated once per\n// logical registry. The service identity is namespaced by the `name`\n// argument; substrate stays name-blind (it sees only `K` and `V`).\nconst PackageRefMap = defineScopedRefMap<PackageKey, ResolvedPackage>('PackageRegistry');\n\nconst wrapRefMap = (refMap: ScopedRefMap<PackageKey, ResolvedPackage>): PackageRegistry => ({\n\tset: refMap.set,\n\tfind: refMap.find,\n\thas: refMap.has,\n\tentries: refMap.entries,\n\tchanges: refMap.changes,\n});\n\n/** Context.Service tag for the per-stack `PackageRegistry`. Plugins\n * yield this in their acquire body. */\nexport class PackageRegistryService extends Context.Service<\n\tPackageRegistryService,\n\tPackageRegistry\n>()('@devstack/plugins/package/PackageRegistry') {}\n\n/** Scope-bound Layer materializing one `PackageRegistry` per stack\n * scope. Boot wiring (CLI / e2e) provides this once per stack;\n * every package/coin/wallet/faucet plugin in the stack yields the\n * SAME instance via Context. */\nexport const layerPackageRegistry: Layer.Layer<PackageRegistryService> = Layer.effect(\n\tPackageRegistryService,\n\tEffect.gen(function* () {\n\t\tconst refMap = yield* PackageRefMap.Service;\n\t\treturn PackageRegistryService.of(wrapRefMap(refMap));\n\t}),\n).pipe(Layer.provide(PackageRefMap.layer));\n\n/** Capability-key constant for the per-stack registry — siblings\n * (Coin, Action, manifest emitter, faucet strategies) look it up\n * through the StrategyContributor registry. */\nexport const PACKAGE_REGISTRY_CAPABILITY_KEY = 'package-registry' as const;\n"],"mappings":";;;;AAuEA,MAAM,gBAAgB,mBAAgD,kBAAkB;AAExF,MAAM,cAAc,YAAwE;CAC3F,KAAK,OAAO;CACZ,MAAM,OAAO;CACb,KAAK,OAAO;CACZ,SAAS,OAAO;CAChB,SAAS,OAAO;CAChB;;;AAID,IAAa,yBAAb,cAA4C,QAAQ,SAGjD,CAAC,4CAA4C,CAAC;;;;;AAMjD,MAAa,uBAA4D,MAAM,OAC9E,wBACA,OAAO,IAAI,aAAa;CACvB,MAAM,SAAS,OAAO,cAAc;AACpC,QAAO,uBAAuB,GAAG,WAAW,OAAO,CAAC;EACnD,CACF,CAAC,KAAK,MAAM,QAAQ,cAAc,MAAM,CAAC;;;;AAK1C,MAAa,kCAAkC"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.mjs","names":[],"sources":["../../../src/plugins/package/service.ts"],"sourcesContent":["// Package plugin — main acquire body.\n//\n// Dispatch on `opts.mode` ∈ {local, known} and delegate to the\n// per-mode acquire. This file is INTENTIONALLY thin: the substrate\n// concerns (cache key, verify, produce, register) live inside the\n// ArtifactPublisher primitive; the toolchain concerns (build,\n// scrub) live in `build.ts`. We just route.\n//\n// Substrate dependencies arrive via `definePlugin({ dependsOn })`:\n//\n// - suiResource — resolved SuiClient + chainId\n// - publisher account — publisher address + sign-and-execute\n//\n// And via the StrategyContributor registry (by capability key):\n//\n// - chain-probe:<chainId> — ChainProbe<SuiProbeKey>\n// - artifact-publisher — the ArtifactPublisher (substrate\n// primitive, available everywhere)\n//\n// And via the plugin-owned PackageRegistry service (instantiated from\n// the substrate's generic ScopedRefMap primitive — see `registry.ts`).\n//\n// The wiring of these lookups happens at the barrel (`index.ts`); this\n// file accepts them as parameters so it stays testable.\n\nimport { Effect, type Scope } from 'effect';\n\nimport type {\n\tArtifactPublishError,\n\tArtifactPublisher,\n} from '../../primitives/artifact-publisher.ts';\nimport type { ChainProbe } from '../../contracts/chain-probe.ts';\nimport type { SuiProbeKey } from '../sui/chain-probe.ts';\nimport { acquireKnown, type KnownModeInputs } from './mode-known.ts';\nimport { acquireLocal, type LocalModeInputs } from './mode-local.ts';\nimport type {\n\tPackageRegistry,\n\tResolvedKnownPackage,\n\tResolvedLocalPackage,\n\tResolvedPackage,\n} from './registry.ts';\nimport type { LocalPackagePublishOutput } from './publish-output.ts';\nimport type { PublishError } from './errors.ts';\n\n/** Discriminated mode union — mirrors the public factory shape\n * (`localPackage(...)` and `knownPackage(...)`). The barrel\n * constructs ONE of these per declared package and the body below\n * routes. */\nexport type PackageMode =\n\t| ({ readonly mode: 'local' } & LocalModeInputs)\n\t| ({ readonly mode: 'known' } & KnownModeInputs);\n\nexport interface BootPackageResult {\n\treadonly resolved: ResolvedPackage;\n\t/** Fresh publish output — populated on cache MISS (the produce\n\t * path ran), `null` on cache hit (verify path). Threaded out so\n\t * the barrel can fan out coin auto-discovery (and any other\n\t * output-consuming siblings) once per fresh publish.\n\t *\n\t * Known mode never publishes, so this is always `null` there. */\n\treadonly output: LocalPackagePublishOutput | null;\n}\n\nexport interface BootLocalPackageResult {\n\treadonly resolved: ResolvedLocalPackage;\n\treadonly output: LocalPackagePublishOutput | null;\n}\n\nexport interface BootKnownPackageResult {\n\treadonly resolved: ResolvedKnownPackage;\n\treadonly output: null;\n}\n\n/** Dispatch on the typed mode. */\nexport function bootPackageService(\n\tpublisher: ArtifactPublisher,\n\tprobe: ChainProbe<SuiProbeKey>,\n\tregistry: PackageRegistry,\n\topts: { readonly mode: 'local' } & LocalModeInputs,\n): Effect.Effect<BootLocalPackageResult, PublishError | ArtifactPublishError, Scope.Scope>;\nexport function bootPackageService(\n\tpublisher: ArtifactPublisher,\n\tprobe: ChainProbe<SuiProbeKey>,\n\tregistry: PackageRegistry,\n\topts: { readonly mode: 'known' } & KnownModeInputs,\n): Effect.Effect<BootKnownPackageResult, PublishError | ArtifactPublishError, Scope.Scope>;\nexport function bootPackageService(\n\tpublisher: ArtifactPublisher,\n\tprobe: ChainProbe<SuiProbeKey>,\n\tregistry: PackageRegistry,\n\topts: PackageMode,\n): Effect.Effect<BootPackageResult, PublishError | ArtifactPublishError, Scope.Scope> {\n\tswitch (opts.mode) {\n\t\tcase 'local':\n\t\t\treturn acquireLocal(publisher, probe, registry, opts).pipe(\n\t\t\t\tEffect.map(({ resolved, output }) => ({ resolved, output })),\n\t\t\t);\n\t\tcase 'known':\n\t\t\treturn acquireKnown(probe, registry, opts).pipe(\n\t\t\t\tEffect.map(({ resolved }) => ({ resolved, output: null })),\n\t\t\t);\n\t}\n}\n"],"mappings":";;;;AAsFA,SAAgB,mBACf,WACA,OACA,UACA,MACqF;AACrF,SAAQ,KAAK,MAAb;EACC,KAAK,QACJ,QAAO,aAAa,WAAW,OAAO,UAAU,KAAK,CAAC,KACrD,OAAO,KAAK,EAAE,UAAU,cAAc;GAAE;GAAU;GAAQ,EAAE,CAC5D;EACF,KAAK,QACJ,QAAO,aAAa,OAAO,UAAU,KAAK,CAAC,KAC1C,OAAO,KAAK,EAAE,gBAAgB;GAAE;GAAU,QAAQ;GAAM,EAAE,CAC1D"}
1
+ {"version":3,"file":"service.mjs","names":[],"sources":["../../../src/plugins/package/service.ts"],"sourcesContent":["// Package plugin — main acquire body.\n//\n// Dispatch on `opts.mode` ∈ {local, known} and delegate to the\n// per-mode acquire. This file is INTENTIONALLY thin: the substrate\n// concerns (cache key, verify, produce, register) live inside the\n// ArtifactPublisher primitive; the toolchain concerns (build,\n// scrub) live in `build.ts`. We just route.\n//\n// Substrate dependencies arrive via `definePlugin({ dependsOn })`:\n//\n// - suiResource — resolved SuiClient + chainId\n// - publisher account — publisher address + sign-and-execute\n//\n// And via the StrategyContributor registry (by capability key):\n//\n// - chain-probe:<chainId> — ChainProbe<SuiProbeKey>\n// - artifact-publisher — the ArtifactPublisher (substrate\n// primitive, available everywhere)\n//\n// And via the plugin-owned PackageRegistry service (instantiated from\n// the substrate's generic ScopedRefMap primitive — see `registry.ts`).\n//\n// The wiring of these lookups happens at the barrel (`index.ts`); this\n// file accepts them as parameters so it stays testable.\n\nimport { Effect, type Scope } from 'effect';\n\nimport type {\n\tArtifactPublishError,\n\tArtifactPublisher,\n} from '../../primitives/artifact-publisher.ts';\nimport type { ChainProbe } from '../../contracts/chain-probe.ts';\nimport type { SuiProbeKey } from '../sui/index.ts';\nimport { acquireKnown, type KnownModeInputs } from './mode-known.ts';\nimport { acquireLocal, type LocalModeInputs } from './mode-local.ts';\nimport type {\n\tPackageRegistry,\n\tResolvedKnownPackage,\n\tResolvedLocalPackage,\n\tResolvedPackage,\n} from './registry.ts';\nimport type { LocalPackagePublishOutput } from './publish-output.ts';\nimport type { PublishError } from './errors.ts';\n\n/** Discriminated mode union — mirrors the public factory shape\n * (`localPackage(...)` and `knownPackage(...)`). The barrel\n * constructs ONE of these per declared package and the body below\n * routes. */\nexport type PackageMode =\n\t| ({ readonly mode: 'local' } & LocalModeInputs)\n\t| ({ readonly mode: 'known' } & KnownModeInputs);\n\nexport interface BootPackageResult {\n\treadonly resolved: ResolvedPackage;\n\t/** Fresh publish output — populated on cache MISS (the produce\n\t * path ran), `null` on cache hit (verify path). Threaded out so\n\t * the barrel can fan out coin auto-discovery (and any other\n\t * output-consuming siblings) once per fresh publish.\n\t *\n\t * Known mode never publishes, so this is always `null` there. */\n\treadonly output: LocalPackagePublishOutput | null;\n}\n\nexport interface BootLocalPackageResult {\n\treadonly resolved: ResolvedLocalPackage;\n\treadonly output: LocalPackagePublishOutput | null;\n}\n\nexport interface BootKnownPackageResult {\n\treadonly resolved: ResolvedKnownPackage;\n\treadonly output: null;\n}\n\n/** Dispatch on the typed mode. */\nexport function bootPackageService(\n\tpublisher: ArtifactPublisher,\n\tprobe: ChainProbe<SuiProbeKey>,\n\tregistry: PackageRegistry,\n\topts: { readonly mode: 'local' } & LocalModeInputs,\n): Effect.Effect<BootLocalPackageResult, PublishError | ArtifactPublishError, Scope.Scope>;\nexport function bootPackageService(\n\tpublisher: ArtifactPublisher,\n\tprobe: ChainProbe<SuiProbeKey>,\n\tregistry: PackageRegistry,\n\topts: { readonly mode: 'known' } & KnownModeInputs,\n): Effect.Effect<BootKnownPackageResult, PublishError | ArtifactPublishError, Scope.Scope>;\nexport function bootPackageService(\n\tpublisher: ArtifactPublisher,\n\tprobe: ChainProbe<SuiProbeKey>,\n\tregistry: PackageRegistry,\n\topts: PackageMode,\n): Effect.Effect<BootPackageResult, PublishError | ArtifactPublishError, Scope.Scope> {\n\tswitch (opts.mode) {\n\t\tcase 'local':\n\t\t\treturn acquireLocal(publisher, probe, registry, opts).pipe(\n\t\t\t\tEffect.map(({ resolved, output }) => ({ resolved, output })),\n\t\t\t);\n\t\tcase 'known':\n\t\t\treturn acquireKnown(probe, registry, opts).pipe(\n\t\t\t\tEffect.map(({ resolved }) => ({ resolved, output: null })),\n\t\t\t);\n\t}\n}\n"],"mappings":";;;;AAsFA,SAAgB,mBACf,WACA,OACA,UACA,MACqF;AACrF,SAAQ,KAAK,MAAb;EACC,KAAK,QACJ,QAAO,aAAa,WAAW,OAAO,UAAU,KAAK,CAAC,KACrD,OAAO,KAAK,EAAE,UAAU,cAAc;GAAE;GAAU;GAAQ,EAAE,CAC5D;EACF,KAAK,QACJ,QAAO,aAAa,OAAO,UAAU,KAAK,CAAC,KAC1C,OAAO,KAAK,EAAE,gBAAgB;GAAE;GAAU,QAAQ;GAAM,EAAE,CAC1D"}
@@ -0,0 +1,14 @@
1
+ //#region src/plugins/package/spans.ts
2
+ const PackageSpans = { publish: {
3
+ phase: "package.publish.phase",
4
+ package: "package.publish.package",
5
+ packageName: "package.publish.packageName",
6
+ packageId: "package.publish.packageId",
7
+ sourcePath: "package.publish.sourcePath",
8
+ chainId: "package.publish.chainId",
9
+ publisher: "package.publish.publisher"
10
+ } };
11
+ //#endregion
12
+ export { PackageSpans };
13
+
14
+ //# sourceMappingURL=spans.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spans.mjs","names":[],"sources":["../../../src/plugins/package/spans.ts"],"sourcesContent":["// Package plugin span-attribute vocabulary. Plugin-local — substrate's\n// `SpanAttr` carries only engine-dimensional + http/process generic\n// keys; plugin-domain keys live next to the plugin that owns them.\n//\n// Callsite pattern: `Effect.annotateCurrentSpan({\n// [PackageSpans.publish.phase]: 'build' })`. Free-form string literals\n// are a STYLE_GUIDE §16 violation.\n\nexport const PackageSpans = {\n\tpublish: {\n\t\tphase: 'package.publish.phase',\n\t\tpackage: 'package.publish.package',\n\t\tpackageName: 'package.publish.packageName',\n\t\tpackageId: 'package.publish.packageId',\n\t\tsourcePath: 'package.publish.sourcePath',\n\t\tchainId: 'package.publish.chainId',\n\t\tpublisher: 'package.publish.publisher',\n\t},\n} as const;\n"],"mappings":";AAQA,MAAa,eAAe,EAC3B,SAAS;CACR,OAAO;CACP,SAAS;CACT,aAAa;CACb,WAAW;CACX,YAAY;CACZ,SAAS;CACT,WAAW;CACX,EACD"}
@@ -1,5 +1,6 @@
1
1
  import { ProbeTimeoutError, exitCodeProbeResult, waitForProbe } from "../../substrate/runtime/probes.mjs";
2
2
  import { databaseCreateFailed, postgresConnectionTimeout } from "./errors.mjs";
3
+ import { PostgresSpans } from "./spans.mjs";
3
4
  import { Effect } from "effect";
4
5
  //#region src/plugins/postgres/db-ensure.ts
5
6
  const READY_PROBE_INTERVAL_MS = 500;
@@ -29,16 +30,20 @@ const awaitReady = (exec, user, database, timeoutMs) => Effect.gen(function* ()
29
30
  lastResult = result;
30
31
  return exitCodeProbeResult(result);
31
32
  })
32
- }).pipe(Effect.mapError((cause) => postgresConnectionTimeout({
33
- database,
34
- attempts: cause instanceof ProbeTimeoutError ? cause.attempts : attempts,
35
- elapsedMs: Date.now() - startedAt,
36
- lastExitCode: lastResult?.exitCode,
37
- lastStdout: lastResult?.stdout,
38
- lastStderr: lastResult?.stderr
39
- })), Effect.withSpan("postgres.awaitReady", { attributes: {
40
- "postgres.database": database,
41
- "postgres.timeoutMs": timeoutMs
33
+ }).pipe(Effect.mapError((cause) => {
34
+ const lastError = cause instanceof ProbeTimeoutError ? cause.lastError : cause;
35
+ return postgresConnectionTimeout({
36
+ database,
37
+ attempts: cause instanceof ProbeTimeoutError ? cause.attempts : attempts,
38
+ elapsedMs: Date.now() - startedAt,
39
+ lastExitCode: lastResult?.exitCode,
40
+ lastStdout: lastResult?.stdout,
41
+ lastStderr: lastResult?.stderr,
42
+ ...lastError === void 0 ? {} : { lastError }
43
+ });
44
+ }), Effect.withSpan("devstack.plugin.postgres.awaitReady", { attributes: {
45
+ [PostgresSpans.database]: database,
46
+ [PostgresSpans.timeoutMs]: timeoutMs
42
47
  } }));
43
48
  });
44
49
  /** Idempotently ensure a logical database exists.
@@ -59,12 +64,13 @@ const awaitReady = (exec, user, database, timeoutMs) => Effect.gen(function* ()
59
64
  * cases flags this as a known foot-gun, and the recommended cure
60
65
  * is "use lowercase names". */
61
66
  const ensureDatabase = (exec, user, dbName) => Effect.gen(function* () {
67
+ const escapedDbName = dbName.replace(/'/g, "''");
62
68
  const exists = yield* exec.run([
63
69
  "psql",
64
70
  "-U",
65
71
  user,
66
72
  "-tAc",
67
- `SELECT 1 FROM pg_database WHERE datname = '${dbName}'`
73
+ `SELECT 1 FROM pg_database WHERE datname = '${escapedDbName}'`
68
74
  ]);
69
75
  if (exists.exitCode !== 0) return yield* Effect.fail(databaseCreateFailed({
70
76
  database: dbName,
@@ -78,6 +84,7 @@ const ensureDatabase = (exec, user, dbName) => Effect.gen(function* () {
78
84
  "createdb",
79
85
  "-U",
80
86
  user,
87
+ "--",
81
88
  dbName
82
89
  ]);
83
90
  if (created.exitCode !== 0) return yield* Effect.fail(databaseCreateFailed({
@@ -87,7 +94,7 @@ const ensureDatabase = (exec, user, dbName) => Effect.gen(function* () {
87
94
  stdout: created.stdout,
88
95
  stderr: created.stderr
89
96
  }));
90
- }).pipe(Effect.withSpan("postgres.ensureDatabase", { attributes: { "postgres.database": dbName } }));
97
+ }).pipe(Effect.withSpan("devstack.plugin.postgres.ensureDatabase", { attributes: { [PostgresSpans.database]: dbName } }));
91
98
  /** Sequentially ensure every non-bootstrap database exists. The
92
99
  * bootstrap (first entry) is created by `POSTGRES_DB` at image
93
100
  * entrypoint and MUST be skipped here. */
@@ -1 +1 @@
1
- {"version":3,"file":"db-ensure.mjs","names":[],"sources":["../../../src/plugins/postgres/db-ensure.ts"],"sourcesContent":["// Postgres plugin — idempotent logical-database creation via\n// `docker exec ... psql`.\n//\n// Why exec, not a TS pg client:\n// - Production path keeps a zero-runtime-dep posture (distilled doc\n// § Postgres-specific concerns: \"avoids carrying a TS `pg` runtime\n// dependency in the production path\").\n// - The probe binary already lives inside the image; the dev\n// machine doesn't need a libpq install.\n// - The same machinery serves `pg_isready` (readiness probe) and\n// `psql` (existence check + createdb), so one exec primitive\n// covers both side-channels.\n//\n// Idempotency contract:\n// - The first database is bootstrapped by the upstream image's\n// entrypoint via `POSTGRES_DB`. The plugin's acquire body MUST\n// skip it here — passing it through `ensureDatabase` is harmless\n// (we short-circuit on existence) but wastes a round-trip.\n// - Subsequent databases land via SELECT 1 / createdb. SELECT 1\n// against `pg_database WHERE datname = '<name>'` returns \"1\\n\" on\n// hit; we treat any non-empty trimmed stdout as a hit so a\n// trailing newline doesn't trigger a duplicate-createdb path.\n//\n// The `ContainerExec` capability is consumed via the plugin's service\n// body (`service.ts:containerExec`), which thin-wraps the\n// `ContainerRuntime.exec` contract surface. Daemon-level failures\n// project to a synthetic non-zero `ExecResult` so the retry loop\n// observes them and the typed timeout error carries the captured\n// streams.\n\nimport { Effect } from 'effect';\n\nimport {\n\tdatabaseCreateFailed,\n\tpostgresConnectionTimeout,\n\ttype DatabaseCreateFailed,\n\ttype PostgresConnectionTimeout,\n} from './errors.ts';\nimport {\n\tProbeTimeoutError,\n\texitCodeProbeResult,\n\twaitForProbe,\n} from '../../substrate/runtime/probes.ts';\n\n/** One captured exec invocation. Mirrors the shape returned by\n * `docker exec` (stdout + stderr + exit code), without naming docker\n * — any container runtime that supports an exec primitive can\n * satisfy this. */\nexport interface ExecResult {\n\treadonly exitCode: number;\n\treadonly stdout: string;\n\treadonly stderr: string;\n}\n\n/** Exec callable injected by the plugin's service body. Wraps\n * `ContainerRuntime.exec` (see contract) into the local seam shape so\n * the retry / existence-check loops here stay runtime-agnostic. */\nexport interface ContainerExec {\n\treadonly run: (argv: ReadonlyArray<string>) => Effect.Effect<ExecResult>;\n}\n\nconst READY_PROBE_INTERVAL_MS = 500;\n\n/** Wait until `pg_isready -U <user> -d <db>` exits zero or the\n * overall deadline elapses.\n *\n * Distilled doc § \"Postgres-specific concerns\" — server-aware probe;\n * TCP-listener readiness alone is insufficient (postgres opens the\n * port before it accepts queries). */\nexport const awaitReady = (\n\texec: ContainerExec,\n\tuser: string,\n\tdatabase: string,\n\ttimeoutMs: number,\n): Effect.Effect<void, PostgresConnectionTimeout> =>\n\tEffect.gen(function* () {\n\t\tlet attempts = 0;\n\t\tlet lastResult: ExecResult | undefined;\n\t\tconst startedAt = Date.now();\n\n\t\treturn yield* waitForProbe({\n\t\t\tlabel: `postgres:${database}`,\n\t\t\ttimeoutMs,\n\t\t\tintervalMs: READY_PROBE_INTERVAL_MS,\n\t\t\tprobe: () =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tattempts += 1;\n\t\t\t\t\tconst result = yield* exec.run(['pg_isready', '-U', user, '-d', database]);\n\t\t\t\t\tlastResult = result;\n\t\t\t\t\treturn exitCodeProbeResult(result);\n\t\t\t\t}),\n\t\t}).pipe(\n\t\t\tEffect.mapError((cause) =>\n\t\t\t\tpostgresConnectionTimeout({\n\t\t\t\t\tdatabase,\n\t\t\t\t\tattempts: cause instanceof ProbeTimeoutError ? cause.attempts : attempts,\n\t\t\t\t\telapsedMs: Date.now() - startedAt,\n\t\t\t\t\tlastExitCode: lastResult?.exitCode,\n\t\t\t\t\tlastStdout: lastResult?.stdout,\n\t\t\t\t\tlastStderr: lastResult?.stderr,\n\t\t\t\t}),\n\t\t\t),\n\t\t\tEffect.withSpan('postgres.awaitReady', {\n\t\t\t\tattributes: { 'postgres.database': database, 'postgres.timeoutMs': timeoutMs },\n\t\t\t}),\n\t\t);\n\t});\n\n/** Idempotently ensure a logical database exists.\n *\n * Algorithm:\n * 1. `psql -tAc \"SELECT 1 FROM pg_database WHERE datname = '<db>'\"`.\n * Exit 0 + non-empty stdout => exists, return.\n * Exit != 0 => DatabaseCreateFailed(exists-check).\n * 2. `createdb -U <user> <db>`.\n * Exit 0 => done.\n * Exit != 0 => DatabaseCreateFailed(createdb).\n *\n * Identifier quoting note: this implementation passes the database\n * name as a CLI argument (createdb) and as a SQL string literal\n * (the existence query). Postgres folds unquoted identifiers; if\n * the caller passes `MyDB`, postgres stores it as `mydb`. The\n * current plugin contract doesn't quote — distilled doc § Edge\n * cases flags this as a known foot-gun, and the recommended cure\n * is \"use lowercase names\". */\nexport const ensureDatabase = (\n\texec: ContainerExec,\n\tuser: string,\n\tdbName: string,\n): Effect.Effect<void, DatabaseCreateFailed> =>\n\tEffect.gen(function* () {\n\t\tconst exists = yield* exec.run([\n\t\t\t'psql',\n\t\t\t'-U',\n\t\t\tuser,\n\t\t\t'-tAc',\n\t\t\t`SELECT 1 FROM pg_database WHERE datname = '${dbName}'`,\n\t\t]);\n\t\tif (exists.exitCode !== 0) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tdatabaseCreateFailed({\n\t\t\t\t\tdatabase: dbName,\n\t\t\t\t\top: 'exists-check',\n\t\t\t\t\texitCode: exists.exitCode,\n\t\t\t\t\tstdout: exists.stdout,\n\t\t\t\t\tstderr: exists.stderr,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (exists.stdout.trim().length > 0) {\n\t\t\treturn; // already present\n\t\t}\n\n\t\tconst created = yield* exec.run(['createdb', '-U', user, dbName]);\n\t\tif (created.exitCode !== 0) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tdatabaseCreateFailed({\n\t\t\t\t\tdatabase: dbName,\n\t\t\t\t\top: 'createdb',\n\t\t\t\t\texitCode: created.exitCode,\n\t\t\t\t\tstdout: created.stdout,\n\t\t\t\t\tstderr: created.stderr,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}).pipe(\n\t\tEffect.withSpan('postgres.ensureDatabase', {\n\t\t\tattributes: { 'postgres.database': dbName },\n\t\t}),\n\t);\n\n/** Sequentially ensure every non-bootstrap database exists. The\n * bootstrap (first entry) is created by `POSTGRES_DB` at image\n * entrypoint and MUST be skipped here. */\nexport const ensureDatabases = (\n\texec: ContainerExec,\n\tuser: string,\n\tdatabases: ReadonlyArray<string>,\n): Effect.Effect<void, DatabaseCreateFailed> =>\n\tEffect.gen(function* () {\n\t\tfor (let i = 1; i < databases.length; i++) {\n\t\t\tyield* ensureDatabase(exec, user, databases[i]!);\n\t\t}\n\t});\n"],"mappings":";;;;AA6DA,MAAM,0BAA0B;;;;;;;AAQhC,MAAa,cACZ,MACA,MACA,UACA,cAEA,OAAO,IAAI,aAAa;CACvB,IAAI,WAAW;CACf,IAAI;CACJ,MAAM,YAAY,KAAK,KAAK;AAE5B,QAAO,OAAO,aAAa;EAC1B,OAAO,YAAY;EACnB;EACA,YAAY;EACZ,aACC,OAAO,IAAI,aAAa;AACvB,eAAY;GACZ,MAAM,SAAS,OAAO,KAAK,IAAI;IAAC;IAAc;IAAM;IAAM;IAAM;IAAS,CAAC;AAC1E,gBAAa;AACb,UAAO,oBAAoB,OAAO;IACjC;EACH,CAAC,CAAC,KACF,OAAO,UAAU,UAChB,0BAA0B;EACzB;EACA,UAAU,iBAAiB,oBAAoB,MAAM,WAAW;EAChE,WAAW,KAAK,KAAK,GAAG;EACxB,cAAc,YAAY;EAC1B,YAAY,YAAY;EACxB,YAAY,YAAY;EACxB,CAAC,CACF,EACD,OAAO,SAAS,uBAAuB,EACtC,YAAY;EAAE,qBAAqB;EAAU,sBAAsB;EAAW,EAC9E,CAAC,CACF;EACA;;;;;;;;;;;;;;;;;;AAmBH,MAAa,kBACZ,MACA,MACA,WAEA,OAAO,IAAI,aAAa;CACvB,MAAM,SAAS,OAAO,KAAK,IAAI;EAC9B;EACA;EACA;EACA;EACA,8CAA8C,OAAO;EACrD,CAAC;AACF,KAAI,OAAO,aAAa,EACvB,QAAO,OAAO,OAAO,KACpB,qBAAqB;EACpB,UAAU;EACV,IAAI;EACJ,UAAU,OAAO;EACjB,QAAQ,OAAO;EACf,QAAQ,OAAO;EACf,CAAC,CACF;AAEF,KAAI,OAAO,OAAO,MAAM,CAAC,SAAS,EACjC;CAGD,MAAM,UAAU,OAAO,KAAK,IAAI;EAAC;EAAY;EAAM;EAAM;EAAO,CAAC;AACjE,KAAI,QAAQ,aAAa,EACxB,QAAO,OAAO,OAAO,KACpB,qBAAqB;EACpB,UAAU;EACV,IAAI;EACJ,UAAU,QAAQ;EAClB,QAAQ,QAAQ;EAChB,QAAQ,QAAQ;EAChB,CAAC,CACF;EAED,CAAC,KACF,OAAO,SAAS,2BAA2B,EAC1C,YAAY,EAAE,qBAAqB,QAAQ,EAC3C,CAAC,CACF;;;;AAKF,MAAa,mBACZ,MACA,MACA,cAEA,OAAO,IAAI,aAAa;AACvB,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,IACrC,QAAO,eAAe,MAAM,MAAM,UAAU,GAAI;EAEhD"}
1
+ {"version":3,"file":"db-ensure.mjs","names":[],"sources":["../../../src/plugins/postgres/db-ensure.ts"],"sourcesContent":["// Postgres plugin — idempotent logical-database creation via\n// `docker exec ... psql`.\n//\n// Why exec, not a TS pg client:\n// - Production path keeps a zero-runtime-dep posture (distilled doc\n// § Postgres-specific concerns: \"avoids carrying a TS `pg` runtime\n// dependency in the production path\").\n// - The probe binary already lives inside the image; the dev\n// machine doesn't need a libpq install.\n// - The same machinery serves `pg_isready` (readiness probe) and\n// `psql` (existence check + createdb), so one exec primitive\n// covers both side-channels.\n//\n// Idempotency contract:\n// - The first database is bootstrapped by the upstream image's\n// entrypoint via `POSTGRES_DB`. The plugin's acquire body MUST\n// skip it here — passing it through `ensureDatabase` is harmless\n// (we short-circuit on existence) but wastes a round-trip.\n// - Subsequent databases land via SELECT 1 / createdb. SELECT 1\n// against `pg_database WHERE datname = '<name>'` returns \"1\\n\" on\n// hit; we treat any non-empty trimmed stdout as a hit so a\n// trailing newline doesn't trigger a duplicate-createdb path.\n//\n// The `ContainerExec` capability is consumed via the plugin's service\n// body (`service.ts:containerExec`), which thin-wraps the\n// `ContainerRuntime.exec` contract surface. Daemon-level failures (no\n// such container, daemon unreachable) map to a typed\n// `PostgresPluginError({phase: 'container-start'})` rather than a\n// fabricated non-zero `ExecResult`; `waitForProbe` treats that typed\n// failure as retryable and preserves the underlying cause on the\n// timeout error's `lastError` field.\n\nimport { Effect } from 'effect';\n\nimport {\n\tdatabaseCreateFailed,\n\tpostgresConnectionTimeout,\n\ttype DatabaseCreateFailed,\n\ttype PostgresConnectionTimeout,\n\ttype PostgresPluginError,\n} from './errors.ts';\nimport { PostgresSpans } from './spans.ts';\nimport {\n\tProbeTimeoutError,\n\texitCodeProbeResult,\n\twaitForProbe,\n} from '../../substrate/runtime/probes.ts';\n\n/** One captured exec invocation. Mirrors the shape returned by\n * `docker exec` (stdout + stderr + exit code), without naming docker\n * — any container runtime that supports an exec primitive can\n * satisfy this. */\nexport interface ExecResult {\n\treadonly exitCode: number;\n\treadonly stdout: string;\n\treadonly stderr: string;\n}\n\n/** Exec callable injected by the plugin's service body. Wraps\n * `ContainerRuntime.exec` (see contract) into the local seam shape so\n * the retry / existence-check loops here stay runtime-agnostic.\n *\n * Daemon-level failures surface as `PostgresPluginError`; non-zero\n * exit codes (eg `pg_isready` not ready yet) are returned in the\n * `ExecResult` for the caller to interpret. */\nexport interface ContainerExec {\n\treadonly run: (\n\t\targv: ReadonlyArray<string>,\n\t) => Effect.Effect<ExecResult, PostgresPluginError>;\n}\n\nconst READY_PROBE_INTERVAL_MS = 500;\n\n/** Wait until `pg_isready -U <user> -d <db>` exits zero or the\n * overall deadline elapses.\n *\n * Distilled doc § \"Postgres-specific concerns\" — server-aware probe;\n * TCP-listener readiness alone is insufficient (postgres opens the\n * port before it accepts queries). */\nexport const awaitReady = (\n\texec: ContainerExec,\n\tuser: string,\n\tdatabase: string,\n\ttimeoutMs: number,\n): Effect.Effect<void, PostgresConnectionTimeout> =>\n\tEffect.gen(function* () {\n\t\tlet attempts = 0;\n\t\tlet lastResult: ExecResult | undefined;\n\t\tconst startedAt = Date.now();\n\n\t\treturn yield* waitForProbe({\n\t\t\tlabel: `postgres:${database}`,\n\t\t\ttimeoutMs,\n\t\t\tintervalMs: READY_PROBE_INTERVAL_MS,\n\t\t\tprobe: () =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tattempts += 1;\n\t\t\t\t\tconst result = yield* exec.run(['pg_isready', '-U', user, '-d', database]);\n\t\t\t\t\tlastResult = result;\n\t\t\t\t\treturn exitCodeProbeResult(result);\n\t\t\t\t}),\n\t\t}).pipe(\n\t\t\tEffect.mapError((cause) => {\n\t\t\t\tconst lastError =\n\t\t\t\t\tcause instanceof ProbeTimeoutError ? cause.lastError : (cause as unknown);\n\t\t\t\treturn postgresConnectionTimeout({\n\t\t\t\t\tdatabase,\n\t\t\t\t\tattempts: cause instanceof ProbeTimeoutError ? cause.attempts : attempts,\n\t\t\t\t\telapsedMs: Date.now() - startedAt,\n\t\t\t\t\tlastExitCode: lastResult?.exitCode,\n\t\t\t\t\tlastStdout: lastResult?.stdout,\n\t\t\t\t\tlastStderr: lastResult?.stderr,\n\t\t\t\t\t...(lastError === undefined ? {} : { lastError }),\n\t\t\t\t});\n\t\t\t}),\n\t\t\tEffect.withSpan('devstack.plugin.postgres.awaitReady', {\n\t\t\t\tattributes: {\n\t\t\t\t\t[PostgresSpans.database]: database,\n\t\t\t\t\t[PostgresSpans.timeoutMs]: timeoutMs,\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t});\n\n/** Idempotently ensure a logical database exists.\n *\n * Algorithm:\n * 1. `psql -tAc \"SELECT 1 FROM pg_database WHERE datname = '<db>'\"`.\n * Exit 0 + non-empty stdout => exists, return.\n * Exit != 0 => DatabaseCreateFailed(exists-check).\n * 2. `createdb -U <user> <db>`.\n * Exit 0 => done.\n * Exit != 0 => DatabaseCreateFailed(createdb).\n *\n * Identifier quoting note: this implementation passes the database\n * name as a CLI argument (createdb) and as a SQL string literal\n * (the existence query). Postgres folds unquoted identifiers; if\n * the caller passes `MyDB`, postgres stores it as `mydb`. The\n * current plugin contract doesn't quote — distilled doc § Edge\n * cases flags this as a known foot-gun, and the recommended cure\n * is \"use lowercase names\". */\nexport const ensureDatabase = (\n\texec: ContainerExec,\n\tuser: string,\n\tdbName: string,\n): Effect.Effect<void, DatabaseCreateFailed | PostgresPluginError> =>\n\tEffect.gen(function* () {\n\t\t// Quote-escape per SQL literal rules (double single-quotes inside\n\t\t// the literal). The plugin's own contract restricts callers to\n\t\t// lowercase database identifiers (see distilled-doc § Edge cases\n\t\t// above), but the literal interpolation here is still a foot-\n\t\t// gun: a name containing `'` would break the WHERE clause and\n\t\t// either fail the existence check or, worse, alter its\n\t\t// semantics. Escaping locks the wire shape independent of the\n\t\t// upstream contract.\n\t\tconst escapedDbName = dbName.replace(/'/g, \"''\");\n\t\tconst exists = yield* exec.run([\n\t\t\t'psql',\n\t\t\t'-U',\n\t\t\tuser,\n\t\t\t'-tAc',\n\t\t\t`SELECT 1 FROM pg_database WHERE datname = '${escapedDbName}'`,\n\t\t]);\n\t\tif (exists.exitCode !== 0) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tdatabaseCreateFailed({\n\t\t\t\t\tdatabase: dbName,\n\t\t\t\t\top: 'exists-check',\n\t\t\t\t\texitCode: exists.exitCode,\n\t\t\t\t\tstdout: exists.stdout,\n\t\t\t\t\tstderr: exists.stderr,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (exists.stdout.trim().length > 0) {\n\t\t\treturn; // already present\n\t\t}\n\n\t\t// The `--` argv separator stops `createdb`'s flag parsing — a\n\t\t// `dbName` starting with `-` (e.g. `--help`) would otherwise be\n\t\t// interpreted as a flag rather than a positional database name.\n\t\t// Argv form already neutralizes shell-metacharacter injection;\n\t\t// the separator closes the remaining flag-shape gap.\n\t\tconst created = yield* exec.run(['createdb', '-U', user, '--', dbName]);\n\t\tif (created.exitCode !== 0) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tdatabaseCreateFailed({\n\t\t\t\t\tdatabase: dbName,\n\t\t\t\t\top: 'createdb',\n\t\t\t\t\texitCode: created.exitCode,\n\t\t\t\t\tstdout: created.stdout,\n\t\t\t\t\tstderr: created.stderr,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}).pipe(\n\t\tEffect.withSpan('devstack.plugin.postgres.ensureDatabase', {\n\t\t\tattributes: { [PostgresSpans.database]: dbName },\n\t\t}),\n\t);\n\n/** Sequentially ensure every non-bootstrap database exists. The\n * bootstrap (first entry) is created by `POSTGRES_DB` at image\n * entrypoint and MUST be skipped here. */\nexport const ensureDatabases = (\n\texec: ContainerExec,\n\tuser: string,\n\tdatabases: ReadonlyArray<string>,\n): Effect.Effect<void, DatabaseCreateFailed | PostgresPluginError> =>\n\tEffect.gen(function* () {\n\t\tfor (let i = 1; i < databases.length; i++) {\n\t\t\tyield* ensureDatabase(exec, user, databases[i]!);\n\t\t}\n\t});\n"],"mappings":";;;;;AAuEA,MAAM,0BAA0B;;;;;;;AAQhC,MAAa,cACZ,MACA,MACA,UACA,cAEA,OAAO,IAAI,aAAa;CACvB,IAAI,WAAW;CACf,IAAI;CACJ,MAAM,YAAY,KAAK,KAAK;AAE5B,QAAO,OAAO,aAAa;EAC1B,OAAO,YAAY;EACnB;EACA,YAAY;EACZ,aACC,OAAO,IAAI,aAAa;AACvB,eAAY;GACZ,MAAM,SAAS,OAAO,KAAK,IAAI;IAAC;IAAc;IAAM;IAAM;IAAM;IAAS,CAAC;AAC1E,gBAAa;AACb,UAAO,oBAAoB,OAAO;IACjC;EACH,CAAC,CAAC,KACF,OAAO,UAAU,UAAU;EAC1B,MAAM,YACL,iBAAiB,oBAAoB,MAAM,YAAa;AACzD,SAAO,0BAA0B;GAChC;GACA,UAAU,iBAAiB,oBAAoB,MAAM,WAAW;GAChE,WAAW,KAAK,KAAK,GAAG;GACxB,cAAc,YAAY;GAC1B,YAAY,YAAY;GACxB,YAAY,YAAY;GACxB,GAAI,cAAc,KAAA,IAAY,EAAE,GAAG,EAAE,WAAW;GAChD,CAAC;GACD,EACF,OAAO,SAAS,uCAAuC,EACtD,YAAY;GACV,cAAc,WAAW;GACzB,cAAc,YAAY;EAC3B,EACD,CAAC,CACF;EACA;;;;;;;;;;;;;;;;;;AAmBH,MAAa,kBACZ,MACA,MACA,WAEA,OAAO,IAAI,aAAa;CASvB,MAAM,gBAAgB,OAAO,QAAQ,MAAM,KAAK;CAChD,MAAM,SAAS,OAAO,KAAK,IAAI;EAC9B;EACA;EACA;EACA;EACA,8CAA8C,cAAc;EAC5D,CAAC;AACF,KAAI,OAAO,aAAa,EACvB,QAAO,OAAO,OAAO,KACpB,qBAAqB;EACpB,UAAU;EACV,IAAI;EACJ,UAAU,OAAO;EACjB,QAAQ,OAAO;EACf,QAAQ,OAAO;EACf,CAAC,CACF;AAEF,KAAI,OAAO,OAAO,MAAM,CAAC,SAAS,EACjC;CAQD,MAAM,UAAU,OAAO,KAAK,IAAI;EAAC;EAAY;EAAM;EAAM;EAAM;EAAO,CAAC;AACvE,KAAI,QAAQ,aAAa,EACxB,QAAO,OAAO,OAAO,KACpB,qBAAqB;EACpB,UAAU;EACV,IAAI;EACJ,UAAU,QAAQ;EAClB,QAAQ,QAAQ;EAChB,QAAQ,QAAQ;EAChB,CAAC,CACF;EAED,CAAC,KACF,OAAO,SAAS,2CAA2C,EAC1D,YAAY,GAAG,cAAc,WAAW,QAAQ,EAChD,CAAC,CACF;;;;AAKF,MAAa,mBACZ,MACA,MACA,cAEA,OAAO,IAAI,aAAa;AACvB,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,IACrC,QAAO,eAAe,MAAM,MAAM,UAAU,GAAI;EAEhD"}
@@ -33,6 +33,11 @@ interface PostgresConnectionTimeout {
33
33
  readonly lastExitCode?: number;
34
34
  readonly lastStdout?: string;
35
35
  readonly lastStderr?: string;
36
+ /** Underlying typed error from the last probe attempt — populated
37
+ * when the probe itself failed (eg daemon-level container-start
38
+ * error) rather than `pg_isready` returning a non-zero exit. The
39
+ * cause walker reads this to render the actionable hint. */
40
+ readonly lastError?: unknown;
36
41
  }
37
42
  /** Logical-database creation failed. Distinct from a network /
38
43
  * container / probe failure because the typical root cause is
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/plugins/postgres/errors.ts"],"sourcesContent":["// Postgres plugin — typed errors.\n//\n// Per-plugin tagged errors (architecture § Effect; substrate-redesign\n// directive: errors live with the plugin that raises and consumes\n// them). Postgres is a topological leaf — every raise site AND every\n// consumer is inside `src/plugins/postgres/`, so this file owns the\n// complete error surface.\n//\n// Phase tags mirror the lifecycle states from the distilled doc\n// (10-postgres § Lifecycle states): one phase per failure boundary\n// so the cause walker / TUI / regression tests can pin behaviour by\n// label rather than by message-substring matching.\n//\n// Effect v4: errors are plain interfaces with a `_tag` discriminator.\n// `Effect.catchTag` / `catchTags` match on the `_tag` literal — we do\n// NOT subclass an Effect base class. See architecture § Effect.\n\nimport { defineConfigError, type ConfigIssue } from '../../substrate/runtime/config-validation.ts';\n\n/** Phases for `PostgresPluginError`. Closed sum — additions land in\n * the distilled-doc catalog first. */\nexport type PostgresPhase =\n\t| 'network-create'\n\t| 'image-build'\n\t| 'container-start'\n\t| 'ready-probe'\n\t| 'db-ensure'\n\t| 'unknown';\n\n/** Generic plugin error. Most failure surfaces collapse onto this\n * shape; specific failure modes that warrant their own catchable\n * tag (timeout, createdb-collision) get their own type below. */\nexport interface PostgresPluginError {\n\treadonly _tag: 'PostgresPluginError';\n\treadonly phase: PostgresPhase;\n\treadonly message: string;\n\treadonly cause?: unknown;\n}\n\nexport const postgresPluginError = (\n\tphase: PostgresPhase,\n\tmessage: string,\n\tcause?: unknown,\n): PostgresPluginError => ({ _tag: 'PostgresPluginError', phase, message, cause });\n\nexport interface PostgresConfigError extends ConfigIssue {\n\treadonly _tag: 'PostgresConfigError';\n}\n\nexport const postgresConfigError = defineConfigError('PostgresConfigError');\n\n/** `pg_isready` returned non-zero past the deadline. Carries the\n * database name + last exit code + captured streams so the cause\n * walker can render an actionable hint (slow disk, port mismatch,\n * perms on data dir).\n *\n * Distilled-doc § Edge cases: this is the most common \"what went\n * wrong\" failure during first boot of a freshly-built image; it\n * deserves its own tag so the renderer can show a slow-disk hint\n * before users guess. */\nexport interface PostgresConnectionTimeout {\n\treadonly _tag: 'PostgresConnectionTimeout';\n\treadonly database: string;\n\treadonly attempts: number;\n\treadonly elapsedMs: number;\n\treadonly lastExitCode?: number;\n\treadonly lastStdout?: string;\n\treadonly lastStderr?: string;\n}\n\nexport const postgresConnectionTimeout = (\n\tparts: Omit<PostgresConnectionTimeout, '_tag'>,\n): PostgresConnectionTimeout => ({ _tag: 'PostgresConnectionTimeout', ...parts });\n\n/** Logical-database creation failed. Distinct from a network /\n * container / probe failure because the typical root cause is\n * case-sensitivity collision in postgres identifier folding —\n * surfaced explicitly via its own tag so consumers can render a\n * \"quote the identifier\" hint. */\nexport interface DatabaseCreateFailed {\n\treadonly _tag: 'DatabaseCreateFailed';\n\treadonly database: string;\n\treadonly op: 'exists-check' | 'createdb';\n\treadonly exitCode?: number;\n\treadonly stdout?: string;\n\treadonly stderr?: string;\n\treadonly cause?: unknown;\n}\n\nexport const databaseCreateFailed = (\n\tparts: Omit<DatabaseCreateFailed, '_tag'>,\n): DatabaseCreateFailed => ({ _tag: 'DatabaseCreateFailed', ...parts });\n\n/** Union of every error a Postgres-plugin caller may encounter. */\nexport type PostgresError =\n\t| PostgresPluginError\n\t| PostgresConfigError\n\t| PostgresConnectionTimeout\n\t| DatabaseCreateFailed;\n\n/** Error tags this plugin contributes — surfaced to the cause walker\n * via `PluginErrorContribution`. */\nexport const POSTGRES_ERROR_TAGS: ReadonlyArray<PostgresError['_tag']> = [\n\t'PostgresPluginError',\n\t'PostgresConfigError',\n\t'PostgresConnectionTimeout',\n\t'DatabaseCreateFailed',\n] as const;\n"],"mappings":";;AAuCA,MAAa,uBACZ,OACA,SACA,WAC0B;CAAE,MAAM;CAAuB;CAAO;CAAS;CAAO;AAMjF,MAAa,sBAAsB,kBAAkB,sBAAsB;AAqB3E,MAAa,6BACZ,WACgC;CAAE,MAAM;CAA6B,GAAG;CAAO;AAiBhF,MAAa,wBACZ,WAC2B;CAAE,MAAM;CAAwB,GAAG;CAAO;;;AAWtE,MAAa,sBAA4D;CACxE;CACA;CACA;CACA;CACA"}
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/plugins/postgres/errors.ts"],"sourcesContent":["// Postgres plugin — typed errors.\n//\n// Per-plugin tagged errors (architecture § Effect; substrate-redesign\n// directive: errors live with the plugin that raises and consumes\n// them). Postgres is a topological leaf — every raise site AND every\n// consumer is inside `src/plugins/postgres/`, so this file owns the\n// complete error surface.\n//\n// Phase tags mirror the lifecycle states from the distilled doc\n// (10-postgres § Lifecycle states): one phase per failure boundary\n// so the cause walker / TUI / regression tests can pin behaviour by\n// label rather than by message-substring matching.\n//\n// Effect v4: errors are plain interfaces with a `_tag` discriminator.\n// `Effect.catchTag` / `catchTags` match on the `_tag` literal — we do\n// NOT subclass an Effect base class. See architecture § Effect.\n\nimport { defineConfigError, type ConfigIssue } from '../../substrate/runtime/config-validation.ts';\n\n/** Phases for `PostgresPluginError`. Closed sum — additions land in\n * the distilled-doc catalog first. */\nexport type PostgresPhase =\n\t| 'network-create'\n\t| 'image-build'\n\t| 'container-start'\n\t| 'ready-probe'\n\t| 'db-ensure'\n\t| 'unknown';\n\n/** Generic plugin error. Most failure surfaces collapse onto this\n * shape; specific failure modes that warrant their own catchable\n * tag (timeout, createdb-collision) get their own type below. */\nexport interface PostgresPluginError {\n\treadonly _tag: 'PostgresPluginError';\n\treadonly phase: PostgresPhase;\n\treadonly message: string;\n\treadonly cause?: unknown;\n}\n\nexport const postgresPluginError = (\n\tphase: PostgresPhase,\n\tmessage: string,\n\tcause?: unknown,\n): PostgresPluginError => ({ _tag: 'PostgresPluginError', phase, message, cause });\n\nexport interface PostgresConfigError extends ConfigIssue {\n\treadonly _tag: 'PostgresConfigError';\n}\n\nexport const postgresConfigError = defineConfigError('PostgresConfigError');\n\n/** `pg_isready` returned non-zero past the deadline. Carries the\n * database name + last exit code + captured streams so the cause\n * walker can render an actionable hint (slow disk, port mismatch,\n * perms on data dir).\n *\n * Distilled-doc § Edge cases: this is the most common \"what went\n * wrong\" failure during first boot of a freshly-built image; it\n * deserves its own tag so the renderer can show a slow-disk hint\n * before users guess. */\nexport interface PostgresConnectionTimeout {\n\treadonly _tag: 'PostgresConnectionTimeout';\n\treadonly database: string;\n\treadonly attempts: number;\n\treadonly elapsedMs: number;\n\treadonly lastExitCode?: number;\n\treadonly lastStdout?: string;\n\treadonly lastStderr?: string;\n\t/** Underlying typed error from the last probe attempt — populated\n\t * when the probe itself failed (eg daemon-level container-start\n\t * error) rather than `pg_isready` returning a non-zero exit. The\n\t * cause walker reads this to render the actionable hint. */\n\treadonly lastError?: unknown;\n}\n\nexport const postgresConnectionTimeout = (\n\tparts: Omit<PostgresConnectionTimeout, '_tag'>,\n): PostgresConnectionTimeout => ({ _tag: 'PostgresConnectionTimeout', ...parts });\n\n/** Logical-database creation failed. Distinct from a network /\n * container / probe failure because the typical root cause is\n * case-sensitivity collision in postgres identifier folding —\n * surfaced explicitly via its own tag so consumers can render a\n * \"quote the identifier\" hint. */\nexport interface DatabaseCreateFailed {\n\treadonly _tag: 'DatabaseCreateFailed';\n\treadonly database: string;\n\treadonly op: 'exists-check' | 'createdb';\n\treadonly exitCode?: number;\n\treadonly stdout?: string;\n\treadonly stderr?: string;\n\treadonly cause?: unknown;\n}\n\nexport const databaseCreateFailed = (\n\tparts: Omit<DatabaseCreateFailed, '_tag'>,\n): DatabaseCreateFailed => ({ _tag: 'DatabaseCreateFailed', ...parts });\n\n/** Union of every error a Postgres-plugin caller may encounter. */\nexport type PostgresError =\n\t| PostgresPluginError\n\t| PostgresConfigError\n\t| PostgresConnectionTimeout\n\t| DatabaseCreateFailed;\n\n/** Error tags this plugin contributes — surfaced to the cause walker\n * via `PluginErrorContribution`. */\nexport const POSTGRES_ERROR_TAGS: ReadonlyArray<PostgresError['_tag']> = [\n\t'PostgresPluginError',\n\t'PostgresConfigError',\n\t'PostgresConnectionTimeout',\n\t'DatabaseCreateFailed',\n] as const;\n"],"mappings":";;AAuCA,MAAa,uBACZ,OACA,SACA,WAC0B;CAAE,MAAM;CAAuB;CAAO;CAAS;CAAO;AAMjF,MAAa,sBAAsB,kBAAkB,sBAAsB;AA0B3E,MAAa,6BACZ,WACgC;CAAE,MAAM;CAA6B,GAAG;CAAO;AAiBhF,MAAa,wBACZ,WAC2B;CAAE,MAAM;CAAwB,GAAG;CAAO;;;AAWtE,MAAa,sBAA4D;CACxE;CACA;CACA;CACA;CACA"}
@@ -1,12 +1,14 @@
1
1
  import { definePlugin, resource } from "../../substrate/plugin.mjs";
2
- import { pluginErrorContributions } from "../../api/plugin-errors.mjs";
3
- import { IdentityContext } from "../../substrate/runtime/paths.mjs";
2
+ import "../../api/define-plugin.mjs";
3
+ import { IdentityContext, StackPathsService } from "../../substrate/runtime/paths.mjs";
4
4
  import { ContainerRuntimeService } from "../../runtime/docker/service.mjs";
5
- import { expectNonEmptyArray } from "../../substrate/runtime/config-validation.mjs";
6
5
  import { makePostgresRoutable } from "./routable.mjs";
6
+ import { pluginErrorContributions } from "../../api/plugin-errors.mjs";
7
+ import { passthroughOrWrap } from "../../substrate/runtime/passthrough-or-wrap.mjs";
7
8
  import "./connection.mjs";
8
9
  import { makeCodegenable } from "./codegen.mjs";
9
- import { POSTGRES_ERROR_TAGS, postgresConfigError, postgresPluginError } from "./errors.mjs";
10
+ import { POSTGRES_ERROR_TAGS, postgresPluginError } from "./errors.mjs";
11
+ import "./spans.mjs";
10
12
  import { bootPostgresService } from "./service.mjs";
11
13
  import { makeSnapshotable } from "./snapshot.mjs";
12
14
  import { Effect } from "effect";
@@ -16,30 +18,21 @@ const postgresResource = resource("postgres");
16
18
  const postgresErrorContributions = pluginErrorContributions(POSTGRES_ERROR_TAGS);
17
19
  const buildPlugin = (opts) => {
18
20
  const name = opts.name ?? "postgres";
19
- const databases = expectNonEmptyArray(opts.databases ?? ["devstack"], {
20
- field: "databases",
21
- message: "postgres(): `databases` must be non-empty",
22
- mkError: postgresConfigError
23
- });
24
21
  return definePlugin({
25
22
  id: postgresResource.id,
26
23
  role: "service",
24
+ section: "service",
27
25
  start: () => Effect.gen(function* () {
28
- const { handle } = yield* bootPostgresService(yield* ContainerRuntimeService, yield* IdentityContext, opts);
26
+ const { handle } = yield* bootPostgresService(yield* ContainerRuntimeService, yield* IdentityContext, (yield* StackPathsService).stackRoot, opts);
29
27
  return handle;
30
- }).pipe(Effect.catchTags({
31
- PostgresPluginError: Effect.fail,
32
- PostgresConfigError: Effect.fail,
33
- PostgresConnectionTimeout: Effect.fail,
34
- DatabaseCreateFailed: Effect.fail
35
- }), Effect.catch((cause) => Effect.fail(postgresPluginError("unknown", `postgres(${name}): unknown failure`, cause)))),
28
+ }).pipe(passthroughOrWrap.for()(POSTGRES_ERROR_TAGS, (cause) => postgresPluginError("unknown", `postgres(${name}): unknown failure`, cause))),
36
29
  errorContributions: postgresErrorContributions,
37
30
  capabilities: ({ value, runtime }) => {
38
31
  const snap = makeSnapshotable({
39
32
  app: runtime.identity.app,
40
33
  stack: runtime.identity.stack,
41
34
  name,
42
- databases
35
+ databases: value.databases
43
36
  });
44
37
  const codegen = makeCodegenable({
45
38
  name,
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/postgres/index.ts"],"sourcesContent":["// Postgres plugin — barrel + `postgres(opts?)` factory.\n//\n// Architecture: Postgres is a topological leaf (no upstream service\n// dependencies) and a single-mode service (local container only;\n// \"live\" modes — Cloud SQL / Neon / RDS — are a deferred decision in\n// the distilled doc, anticipated by the plain-endpoint shape but not\n// implemented). One factory; no mode-narrowed namespace.\n//\n// Capabilities emitted:\n// 1. Snapshotable — container's writable layer (PGDATA-relocated),\n// identity guard contributes server name + ordered db list.\n// 2. Codegenable — typed connection bindings for user-app code.\n// 3. Routable (TCP, optional) — gated by `opts.route`. When true,\n// the postgres container fronts the `postgres-tcp` Traefik\n// entrypoint (host port 5432 by default). Disabled by default to\n// preserve the existing parallel-stack story (TCP has no Host\n// header, so an entrypoint serves one backend at a time).\n// Stacks that want host-side reachability can set `route: true`\n// OR set `hostPort` directly (the two are mutually exclusive\n// escape hatches — `route: true` is recommended for in-network\n// composition with traefik, `hostPort` for direct host-port\n// publication).\n//\n// Substrate wiring:\n// - `ContainerRuntimeService` yielded in the acquire body for the\n// container + image + exec contract.\n// - `IdentityContext` yielded for the app/stack identity strings.\n// - `errorContributions` declares POSTGRES_ERROR_TAGS so the\n// supervisor's harvest loop registers them with the\n// FormatterRegistry.\n\nimport { Effect } from 'effect';\n\nimport { definePlugin, resource } from '../../api/define-plugin.ts';\nimport { pluginErrorContributions } from '../../api/plugin-errors.ts';\nimport type { CodegenableDecl } from '../../contracts/codegenable.ts';\nimport type { RoutableDecl } from '../../contracts/routable.ts';\nimport type { SnapshotableDecl } from '../../contracts/snapshotable.ts';\nimport { ContainerRuntimeService } from '../../runtime/docker/service.ts';\nimport { expectNonEmptyArray } from '../../substrate/runtime/config-validation.ts';\nimport { IdentityContext } from '../../substrate/runtime/paths.ts';\n\nimport { makeCodegenable } from './codegen.ts';\nimport { POSTGRES_ERROR_TAGS, postgresConfigError, postgresPluginError } from './errors.ts';\nimport { makePostgresRoutable } from './routable.ts';\nimport { bootPostgresService, type Postgres, type PostgresServiceOptions } from './service.ts';\nimport { makeSnapshotable } from './snapshot.ts';\n\ntype PostgresCapabilities =\n\t| readonly [SnapshotableDecl, CodegenableDecl<'postgres-connection'>]\n\t| readonly [SnapshotableDecl, CodegenableDecl<'postgres-connection'>, RoutableDecl];\n\n// ---------------------------------------------------------------------------\n// Resource identity\n// ---------------------------------------------------------------------------\n\n/** The Postgres plugin's resource identity. */\nconst postgresResource = resource<'postgres', Postgres>('postgres');\nconst postgresErrorContributions = pluginErrorContributions(POSTGRES_ERROR_TAGS);\n\n// ---------------------------------------------------------------------------\n// Plugin construction\n// ---------------------------------------------------------------------------\n\n/** User-facing options on top of the service options — currently just\n * the routable opt-in. We split this out so the service body's\n * `PostgresServiceOptions` stays focused on container/boot knobs and\n * the plugin-level surface owns capability gating. */\nexport interface PostgresPluginOptions extends PostgresServiceOptions {\n\t/** When `true`, the plugin contributes a `wireProtocol: 'tcp'`\n\t * Routable decl that fronts the postgres container on the\n\t * `postgres-tcp` entrypoint (host port 5432 by default). Default\n\t * `false` — parallel stacks coexist via the in-network DNS alias\n\t * and the optional `hostPort` mapping, neither of which involves\n\t * the router.\n\t *\n\t * Architecture parallel-stack note: TCP entrypoints serve ONE\n\t * backend at a time (no Host-header fan-out), so only one stack\n\t * on the host may set `route: true` at a given moment. The router\n\t * orchestrator's collision detector fails fast on the second\n\t * contribution. */\n\treadonly route?: boolean;\n}\n\nconst buildPlugin = (opts: PostgresPluginOptions) => {\n\tconst name = opts.name ?? 'postgres';\n\tconst databases = expectNonEmptyArray(opts.databases ?? ['devstack'], {\n\t\tfield: 'databases',\n\t\tmessage: 'postgres(): `databases` must be non-empty',\n\t\tmkError: postgresConfigError,\n\t});\n\n\treturn definePlugin({\n\t\tid: postgresResource.id,\n\t\trole: 'service',\n\t\tstart: () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\t// Substrate-context plumbing supplies real\n\t\t\t\t// `ContainerRuntime` + `Identity` instances; the\n\t\t\t\t// supervisor's plugin acquisition path provides these\n\t\t\t\t// before this body runs.\n\t\t\t\tconst runtime = yield* ContainerRuntimeService;\n\t\t\t\tconst identity = yield* IdentityContext;\n\t\t\t\tconst { handle } = yield* bootPostgresService(runtime, identity, opts);\n\t\t\t\treturn handle;\n\t\t\t}).pipe(\n\t\t\t\t// Distilled-doc § Invariants: already-typed errors must\n\t\t\t\t// NOT be re-wrapped by the catch-all unknown handler.\n\t\t\t\t// `catchTag` runs first; anything unknown falls into the\n\t\t\t\t// catch-all and surfaces as `phase: 'unknown'`.\n\t\t\t\tEffect.catchTags({\n\t\t\t\t\tPostgresPluginError: Effect.fail,\n\t\t\t\t\tPostgresConfigError: Effect.fail,\n\t\t\t\t\tPostgresConnectionTimeout: Effect.fail,\n\t\t\t\t\tDatabaseCreateFailed: Effect.fail,\n\t\t\t\t}),\n\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\tEffect.fail(postgresPluginError('unknown', `postgres(${name}): unknown failure`, cause)),\n\t\t\t\t),\n\t\t\t),\n\t\terrorContributions: postgresErrorContributions,\n\t\t// Dynamic capability factory: receives the resolved\n\t\t// `Postgres` handle + acquire context. Stamps the REAL\n\t\t// app/stack into the snapshot decl and the REAL\n\t\t// networkAlias + derived password into the codegen\n\t\t// bindings — the static-form placeholders (`<app>`,\n\t\t// `<stack>`, `<network-alias>`, `<derived-at-acquire>`)\n\t\t// are gone.\n\t\tcapabilities: ({ value, runtime }): PostgresCapabilities => {\n\t\t\tconst snap = makeSnapshotable({\n\t\t\t\tapp: runtime.identity.app,\n\t\t\t\tstack: runtime.identity.stack,\n\t\t\t\tname,\n\t\t\t\tdatabases,\n\t\t\t});\n\t\t\tconst codegen = makeCodegenable({\n\t\t\t\tname,\n\t\t\t\tuser: value.user,\n\t\t\t\tpassword: value.password,\n\t\t\t\thost: value.networkAlias,\n\t\t\t\tport: value.port,\n\t\t\t\tdatabases: value.databases,\n\t\t\t});\n\t\t\tconst routable = opts.route\n\t\t\t\t? makePostgresRoutable({\n\t\t\t\t\t\tapp: runtime.identity.app,\n\t\t\t\t\t\tstack: runtime.identity.stack,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcontainerName: `${runtime.identity.app}-${runtime.identity.stack}-${name}`,\n\t\t\t\t\t})\n\t\t\t\t: null;\n\t\t\treturn routable === null ? ([snap, codegen] as const) : ([snap, codegen, routable] as const);\n\t\t},\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// User-facing factory\n// ---------------------------------------------------------------------------\n\n/** Postgres-as-a-container plugin. One per `(stack, name)` pair.\n *\n * Single-mode: only local container exists today. The plain-endpoint\n * shape anticipates a future \"live\" mode (Cloud SQL / Neon / RDS)\n * that emits the same endpoint shape without spawning. */\nexport const postgres = (opts: PostgresPluginOptions = {}) => buildPlugin(opts);\n\n// Re-export the canonical TCP endpoint name for downstream consumers\n// (codegen lookups, doctor / inventory rendering, etc.).\nexport { POSTGRES_TCP_ENDPOINT_NAME } from './routable.ts';\n\n// ---------------------------------------------------------------------------\n// Re-exports for consumers + sibling plugins\n// ---------------------------------------------------------------------------\n\nexport type { Postgres, PostgresServiceOptions } from './service.ts';\nexport type { PostgresConnectionBindings, PostgresConnectionParts } from './connection.ts';\nexport type {\n\tPostgresError,\n\tPostgresPluginError,\n\tPostgresConfigError,\n\tPostgresConnectionTimeout,\n\tDatabaseCreateFailed,\n\tPostgresPhase,\n} from './errors.ts';\nexport { POSTGRES_ERROR_TAGS } from './errors.ts';\nexport type { PostgresIdentityPayload } from './snapshot.ts';\n\n// Connection-string builders — exposed for downstream consumers that\n// need to compose URLs without the resolved handle (e.g. user-app\n// code that reads the codegen output and builds a per-database URL\n// at runtime).\nexport { credentialedUrl, plainUrl, withDatabase } from './connection.ts';\n"],"mappings":";;;;;;;;;;;;;;AAyDA,MAAM,mBAAmB,SAA+B,WAAW;AACnE,MAAM,6BAA6B,yBAAyB,oBAAoB;AA0BhF,MAAM,eAAe,SAAgC;CACpD,MAAM,OAAO,KAAK,QAAQ;CAC1B,MAAM,YAAY,oBAAoB,KAAK,aAAa,CAAC,WAAW,EAAE;EACrE,OAAO;EACP,SAAS;EACT,SAAS;EACT,CAAC;AAEF,QAAO,aAAa;EACnB,IAAI,iBAAiB;EACrB,MAAM;EACN,aACC,OAAO,IAAI,aAAa;GAOvB,MAAM,EAAE,WAAW,OAAO,oBAAoB,OAFvB,yBAEgC,OAD/B,iBACyC,KAAK;AACtE,UAAO;IACN,CAAC,KAKF,OAAO,UAAU;GAChB,qBAAqB,OAAO;GAC5B,qBAAqB,OAAO;GAC5B,2BAA2B,OAAO;GAClC,sBAAsB,OAAO;GAC7B,CAAC,EACF,OAAO,OAAO,UACb,OAAO,KAAK,oBAAoB,WAAW,YAAY,KAAK,qBAAqB,MAAM,CAAC,CACxF,CACD;EACF,oBAAoB;EAQpB,eAAe,EAAE,OAAO,cAAoC;GAC3D,MAAM,OAAO,iBAAiB;IAC7B,KAAK,QAAQ,SAAS;IACtB,OAAO,QAAQ,SAAS;IACxB;IACA;IACA,CAAC;GACF,MAAM,UAAU,gBAAgB;IAC/B;IACA,MAAM,MAAM;IACZ,UAAU,MAAM;IAChB,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,WAAW,MAAM;IACjB,CAAC;GACF,MAAM,WAAW,KAAK,QACnB,qBAAqB;IACrB,KAAK,QAAQ,SAAS;IACtB,OAAO,QAAQ,SAAS;IACxB;IACA,eAAe,GAAG,QAAQ,SAAS,IAAI,GAAG,QAAQ,SAAS,MAAM,GAAG;IACpE,CAAC,GACD;AACH,UAAO,aAAa,OAAQ,CAAC,MAAM,QAAQ,GAAc;IAAC;IAAM;IAAS;IAAS;;EAEnF,CAAC;;;;;;;AAYH,MAAa,YAAY,OAA8B,EAAE,KAAK,YAAY,KAAK"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/postgres/index.ts"],"sourcesContent":["// Postgres plugin — barrel + `postgres(opts?)` factory.\n//\n// Architecture: Postgres is a topological leaf (no upstream service\n// dependencies) and a single-mode service (local container only;\n// \"live\" modes — Cloud SQL / Neon / RDS — are a deferred decision in\n// the distilled doc, anticipated by the plain-endpoint shape but not\n// implemented). One factory; no mode-narrowed namespace.\n//\n// Capabilities emitted:\n// 1. Snapshotable — container's writable layer (PGDATA-relocated),\n// identity guard contributes server name + ordered db list.\n// 2. Codegenable — typed connection bindings for user-app code.\n// 3. Routable (TCP, optional) — gated by `opts.route`. When true,\n// the postgres container fronts the `postgres-tcp` Traefik\n// entrypoint (host port 5432 by default). Disabled by default to\n// preserve the existing parallel-stack story (TCP has no Host\n// header, so an entrypoint serves one backend at a time).\n// Stacks that want host-side reachability can set `route: true`\n// OR set `hostPort` directly (the two are mutually exclusive\n// escape hatches — `route: true` is recommended for in-network\n// composition with traefik, `hostPort` for direct host-port\n// publication).\n//\n// Substrate wiring:\n// - `ContainerRuntimeService` yielded in the acquire body for the\n// container + image + exec contract.\n// - `IdentityContext` yielded for the app/stack identity strings.\n// - `errorContributions` declares POSTGRES_ERROR_TAGS so the\n// supervisor's harvest loop registers them with the\n// FormatterRegistry.\n\nimport { Effect } from 'effect';\n\nimport { definePlugin, resource } from '../../api/define-plugin.ts';\nimport { pluginErrorContributions } from '../../api/plugin-errors.ts';\nimport type { CodegenableDecl } from '../../contracts/codegenable.ts';\nimport type { RoutableDecl } from '../../contracts/routable.ts';\nimport type { SnapshotableDecl } from '../../contracts/snapshotable.ts';\nimport { ContainerRuntimeService } from '../../runtime/docker/service.ts';\nimport { passthroughOrWrap } from '../../substrate/runtime/passthrough-or-wrap.ts';\nimport { IdentityContext, StackPathsService } from '../../substrate/runtime/paths.ts';\n\nimport { makeCodegenable } from './codegen.ts';\nimport { POSTGRES_ERROR_TAGS, postgresPluginError, type PostgresError } from './errors.ts';\nimport { makePostgresRoutable } from './routable.ts';\nimport { bootPostgresService, type Postgres, type PostgresServiceOptions } from './service.ts';\nimport { makeSnapshotable } from './snapshot.ts';\n\ntype PostgresCapabilities =\n\t| readonly [SnapshotableDecl, CodegenableDecl<'postgres-connection'>]\n\t| readonly [SnapshotableDecl, CodegenableDecl<'postgres-connection'>, RoutableDecl];\n\n// ---------------------------------------------------------------------------\n// Resource identity\n// ---------------------------------------------------------------------------\n\n/** The Postgres plugin's resource identity. */\nconst postgresResource = resource<'postgres', Postgres>('postgres');\nconst postgresErrorContributions = pluginErrorContributions(POSTGRES_ERROR_TAGS);\n\n// ---------------------------------------------------------------------------\n// Plugin construction\n// ---------------------------------------------------------------------------\n\n/** User-facing options on top of the service options — currently just\n * the routable opt-in. We split this out so the service body's\n * `PostgresServiceOptions` stays focused on container/boot knobs and\n * the plugin-level surface owns capability gating. */\nexport interface PostgresPluginOptions extends PostgresServiceOptions {\n\t/** When `true`, the plugin contributes a `wireProtocol: 'tcp'`\n\t * Routable decl that fronts the postgres container on the\n\t * `postgres-tcp` entrypoint (host port 5432 by default). Default\n\t * `false` — parallel stacks coexist via the in-network DNS alias\n\t * and the optional `hostPort` mapping, neither of which involves\n\t * the router.\n\t *\n\t * Architecture parallel-stack note: TCP entrypoints serve ONE\n\t * backend at a time (no Host-header fan-out), so only one stack\n\t * on the host may set `route: true` at a given moment. The router\n\t * orchestrator's collision detector fails fast on the second\n\t * contribution. */\n\treadonly route?: boolean;\n}\n\nconst buildPlugin = (opts: PostgresPluginOptions) => {\n\tconst name = opts.name ?? 'postgres';\n\t// Validation (databases non-empty) runs inside `bootPostgresService` via\n\t// `resolveOptions`; the resolved bundle's `databases` flows through to\n\t// the snapshot + codegen decls.\n\n\treturn definePlugin({\n\t\tid: postgresResource.id,\n\t\trole: 'service',\n\t\tsection: 'service',\n\t\tstart: () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\t// Substrate-context plumbing supplies real\n\t\t\t\t// `ContainerRuntime` + `Identity` instances; the\n\t\t\t\t// supervisor's plugin acquisition path provides these\n\t\t\t\t// before this body runs.\n\t\t\t\tconst runtime = yield* ContainerRuntimeService;\n\t\t\t\tconst identity = yield* IdentityContext;\n\t\t\t\tconst paths = yield* StackPathsService;\n\t\t\t\tconst { handle } = yield* bootPostgresService(runtime, identity, paths.stackRoot, opts);\n\t\t\t\treturn handle;\n\t\t\t}).pipe(\n\t\t\t\t// Distilled-doc § Invariants: already-typed errors must\n\t\t\t\t// NOT be re-wrapped by the catch-all unknown handler.\n\t\t\t\t// `passthroughOrWrap` lets the POSTGRES_ERROR_TAGS union\n\t\t\t\t// through unchanged; anything else surfaces as\n\t\t\t\t// `phase: 'unknown'`.\n\t\t\t\tpassthroughOrWrap.for<PostgresError>()(POSTGRES_ERROR_TAGS, (cause) =>\n\t\t\t\t\tpostgresPluginError('unknown', `postgres(${name}): unknown failure`, cause),\n\t\t\t\t),\n\t\t\t),\n\t\terrorContributions: postgresErrorContributions,\n\t\t// Dynamic capability factory: receives the resolved\n\t\t// `Postgres` handle + acquire context. Stamps the REAL\n\t\t// app/stack into the snapshot decl and the per-stack\n\t\t// network alias + derived password into the codegen\n\t\t// bindings.\n\t\t//\n\t\t// Codegen `host` is `value.networkAlias` — the per-stack\n\t\t// alias Docker registers via `--network-alias` on the\n\t\t// container's primary network. Dialing the alias resolves\n\t\t// in-container regardless of which parallel stack the\n\t\t// caller belongs to; the per-stack container name still\n\t\t// resolves too, but is not parallel-stack-portable when\n\t\t// emitted into committed codegen output.\n\t\tcapabilities: ({ value, runtime }): PostgresCapabilities => {\n\t\t\tconst snap = makeSnapshotable({\n\t\t\t\tapp: runtime.identity.app,\n\t\t\t\tstack: runtime.identity.stack,\n\t\t\t\tname,\n\t\t\t\tdatabases: value.databases,\n\t\t\t});\n\t\t\tconst codegen = makeCodegenable({\n\t\t\t\tname,\n\t\t\t\tuser: value.user,\n\t\t\t\tpassword: value.password,\n\t\t\t\thost: value.networkAlias,\n\t\t\t\tport: value.port,\n\t\t\t\tdatabases: value.databases,\n\t\t\t});\n\t\t\tconst routable = opts.route\n\t\t\t\t? makePostgresRoutable({\n\t\t\t\t\t\tapp: runtime.identity.app,\n\t\t\t\t\t\tstack: runtime.identity.stack,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcontainerName: `${runtime.identity.app}-${runtime.identity.stack}-${name}`,\n\t\t\t\t\t})\n\t\t\t\t: null;\n\t\t\treturn routable === null ? ([snap, codegen] as const) : ([snap, codegen, routable] as const);\n\t\t},\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// User-facing factory\n// ---------------------------------------------------------------------------\n\n/** Postgres-as-a-container plugin. One per `(stack, name)` pair.\n *\n * Single-mode: only local container exists today. The plain-endpoint\n * shape anticipates a future \"live\" mode (Cloud SQL / Neon / RDS)\n * that emits the same endpoint shape without spawning. */\nexport const postgres = (opts: PostgresPluginOptions = {}) => buildPlugin(opts);\n\n// Re-export the canonical TCP endpoint name for downstream consumers\n// (codegen lookups, doctor / inventory rendering, etc.).\nexport { POSTGRES_TCP_ENDPOINT_NAME } from './routable.ts';\n\n// ---------------------------------------------------------------------------\n// Re-exports for consumers + sibling plugins\n// ---------------------------------------------------------------------------\n\nexport type { Postgres, PostgresServiceOptions } from './service.ts';\nexport type { PostgresConnectionBindings, PostgresConnectionParts } from './connection.ts';\nexport type {\n\tPostgresError,\n\tPostgresPluginError,\n\tPostgresConfigError,\n\tPostgresConnectionTimeout,\n\tDatabaseCreateFailed,\n\tPostgresPhase,\n} from './errors.ts';\nexport { POSTGRES_ERROR_TAGS } from './errors.ts';\nexport type { PostgresIdentityPayload } from './snapshot.ts';\n\n// Connection-string builders — exposed for downstream consumers that\n// need to compose URLs without the resolved handle (e.g. user-app\n// code that reads the codegen output and builds a per-database URL\n// at runtime).\nexport { credentialedUrl, plainUrl, withDatabase } from './connection.ts';\n\nexport { PostgresSpans } from './spans.ts';\n"],"mappings":";;;;;;;;;;;;;;;;AAyDA,MAAM,mBAAmB,SAA+B,WAAW;AACnE,MAAM,6BAA6B,yBAAyB,oBAAoB;AA0BhF,MAAM,eAAe,SAAgC;CACpD,MAAM,OAAO,KAAK,QAAQ;AAK1B,QAAO,aAAa;EACnB,IAAI,iBAAiB;EACrB,MAAM;EACN,SAAS;EACT,aACC,OAAO,IAAI,aAAa;GAQvB,MAAM,EAAE,WAAW,OAAO,oBAAoB,OAHvB,yBAGgC,OAF/B,kBAEyC,OAD5C,mBACkD,WAAW,KAAK;AACvF,UAAO;IACN,CAAC,KAMF,kBAAkB,KAAoB,CAAC,sBAAsB,UAC5D,oBAAoB,WAAW,YAAY,KAAK,qBAAqB,MAAM,CAC3E,CACD;EACF,oBAAoB;EAcpB,eAAe,EAAE,OAAO,cAAoC;GAC3D,MAAM,OAAO,iBAAiB;IAC7B,KAAK,QAAQ,SAAS;IACtB,OAAO,QAAQ,SAAS;IACxB;IACA,WAAW,MAAM;IACjB,CAAC;GACF,MAAM,UAAU,gBAAgB;IAC/B;IACA,MAAM,MAAM;IACZ,UAAU,MAAM;IAChB,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,WAAW,MAAM;IACjB,CAAC;GACF,MAAM,WAAW,KAAK,QACnB,qBAAqB;IACrB,KAAK,QAAQ,SAAS;IACtB,OAAO,QAAQ,SAAS;IACxB;IACA,eAAe,GAAG,QAAQ,SAAS,IAAI,GAAG,QAAQ,SAAS,MAAM,GAAG;IACpE,CAAC,GACD;AACH,UAAO,aAAa,OAAQ,CAAC,MAAM,QAAQ,GAAc;IAAC;IAAM;IAAS;IAAS;;EAEnF,CAAC;;;;;;;AAYH,MAAa,YAAY,OAA8B,EAAE,KAAK,YAAY,KAAK"}
@@ -1,9 +1,11 @@
1
- import { expectNonEmptyArray } from "../../substrate/runtime/config-validation.mjs";
2
1
  import { ensureManagedContainer } from "../../substrate/runtime/managed-container.mjs";
3
2
  import { credentialedUrl, plainUrl, withDatabase } from "./connection.mjs";
4
3
  import { postgresConfigError, postgresPluginError } from "./errors.mjs";
4
+ import { PostgresSpans } from "./spans.mjs";
5
5
  import { awaitReady, ensureDatabases } from "./db-ensure.mjs";
6
6
  import { Effect } from "effect";
7
+ import { createHash } from "node:crypto";
8
+ import { fileURLToPath } from "node:url";
7
9
  //#region src/plugins/postgres/service.ts
8
10
  const DEFAULT_VERSION = "17-alpine";
9
11
  const DEFAULT_USER = "devstack";
@@ -11,27 +13,45 @@ const DEFAULT_DATABASES = ["devstack"];
11
13
  const DEFAULT_READY_TIMEOUT_MS = 3e4;
12
14
  const DEFAULT_STOP_GRACE_SECONDS = 20;
13
15
  const POSTGRES_PORT = 5432;
14
- /** Deterministic dev password from `(app, stack)`. Tradeoff
16
+ /** Deterministic dev password from `(app, stack, stackRoot)`. Tradeoff
15
17
  * documented in the distilled doc § Postgres-specific concerns:
16
18
  * fine for a single-user dev tool, foot-gun if `hostPort` is set in
17
19
  * a multi-user environment. The user-supplied `password` override on
18
- * `PostgresServiceOptions` is the escape hatch. */
19
- const derivePassword = (app, stack) => `pg-${(app + stack).replace(/[^a-zA-Z0-9]/g, "")}`;
20
+ * `PostgresServiceOptions` is the escape hatch.
21
+ *
22
+ * The short hash + separator disambiguate three collision classes:
23
+ * - (app, stack) pairs whose sanitized concatenation would collide
24
+ * (e.g. `("my-app", "dev")` vs `("my", "appdev")` both sanitize to
25
+ * the same body, but the hash binds the unambiguous boundary).
26
+ * - Two checkouts of the SAME `(app, stack)` on the same machine
27
+ * (different working directories) — folding `stackRoot` into the
28
+ * hash means each checkout's postgres password is distinct. (Note:
29
+ * the `pg_isready` liveness probe does NOT authenticate, so this
30
+ * does not gate the probe.) The benefit is that the per-checkout
31
+ * CREDENTIALED endpoint URL — and any `psql`/`createdb` that
32
+ * actually authenticates — differs per checkout, so one checkout's
33
+ * connection string cannot silently land on the other's container.
34
+ * - The `\x1f` (US, ASCII 31) separator between fields is unambiguous
35
+ * because identity strings and absolute paths cannot legally
36
+ * contain it. */
37
+ const derivePassword = (app, stack, stackRoot) => {
38
+ return `pg-${(app + stack).replace(/[^a-zA-Z0-9]/g, "")}-${createHash("sha256").update(`${app}\x1f${stack}\x1f${stackRoot}`).digest("hex").slice(0, 8)}`;
39
+ };
20
40
  const sanitizeAlias = (s) => s.replace(/[^a-zA-Z0-9-]/g, "-");
21
41
  /** Resolve user-supplied options against defaults + identity-derived
22
42
  * values. Pure; safe to call before the Effect body runs. */
23
- const resolveOptions = (identity, opts) => {
43
+ const resolveOptions = (identity, stackRoot, opts) => {
24
44
  const name = opts.name ?? "postgres";
25
- const databases = expectNonEmptyArray(opts.databases ?? DEFAULT_DATABASES, {
45
+ const databases = opts.databases ?? DEFAULT_DATABASES;
46
+ if (!Array.isArray(databases) || databases.length === 0) throw postgresConfigError({
26
47
  field: "databases",
27
- message: "postgres(): `databases` must be non-empty",
28
- mkError: postgresConfigError
48
+ message: "postgres(): `databases` must be non-empty"
29
49
  });
30
50
  return {
31
51
  name,
32
52
  version: opts.version ?? DEFAULT_VERSION,
33
53
  user: opts.user ?? DEFAULT_USER,
34
- password: opts.password ?? derivePassword(identity.app, identity.stack),
54
+ password: opts.password ?? derivePassword(identity.app, identity.stack, stackRoot),
35
55
  databases,
36
56
  hostPort: opts.hostPort,
37
57
  extraNetworks: opts.extraNetworks ?? [],
@@ -52,24 +72,29 @@ const resolveOptions = (identity, opts) => {
52
72
  * local to the plugin. */
53
73
  const resolveImageContextPath = () => {
54
74
  const here = new URL(import.meta.url);
55
- const ctxUrl = new URL("../../../images/postgres/", here);
56
- return decodeURIComponent(ctxUrl.pathname);
75
+ return fileURLToPath(new URL("../../../images/postgres/", here));
57
76
  };
58
77
  /** Build a `ContainerExec` view bound to a specific container handle.
59
78
  * Thin adapter from the runtime's contract surface to the plugin's
60
79
  * local exec seam. The runtime never promotes non-zero exit to
61
80
  * failure here — the caller (pg_isready retry loop, createdb
62
- * existence-check) decides. */
63
- const containerExec = (runtime, handle) => ({ run: (argv) => runtime.exec(handle, argv).pipe(Effect.catch((err) => Effect.succeed({
64
- exitCode: 255,
65
- stdout: "",
66
- stderr: `runtime.exec failed: ${err.reason} — ${err.detail}`
67
- }))) });
81
+ * existence-check) decides.
82
+ *
83
+ * Daemon-level failures (no such container, daemon unreachable)
84
+ * surface as a typed `PostgresPluginError({phase: 'container-start'})`
85
+ * rather than a fabricated `exitCode: 255` ExecResult. STYLE_GUIDE
86
+ * §16 — typed errors must carry the actual failure shape, not lie
87
+ * about it being a tx-level non-zero exit. The retry loops in
88
+ * `awaitReady` / `ensureDatabase` treat the typed failure as
89
+ * retryable via `waitForProbe`'s default retry behavior; the
90
+ * underlying cause is preserved on the timeout error's `lastError`
91
+ * field for the cause walker. */
92
+ const containerExec = (runtime, handle) => ({ run: (argv) => runtime.exec(handle, argv).pipe(Effect.catch((err) => Effect.fail(postgresPluginError("container-start", `runtime.exec failed: ${err.reason} — ${err.detail}`, err)))) });
68
93
  /** Boot the postgres service. The barrel composes this with the
69
94
  * capability decls. */
70
- const bootPostgresService = (runtime, identity, opts) => Effect.gen(function* () {
95
+ const bootPostgresService = (runtime, identity, stackRoot, opts) => Effect.gen(function* () {
71
96
  const resolved = yield* Effect.try({
72
- try: () => resolveOptions(identity, opts),
97
+ try: () => resolveOptions(identity, stackRoot, opts),
73
98
  catch: (cause) => cause
74
99
  });
75
100
  const networkAlias = sanitizeAlias(`${resolved.name}-${identity.stack}`);
@@ -82,7 +107,13 @@ const bootPostgresService = (runtime, identity, opts) => Effect.gen(function* ()
82
107
  const imageRef = yield* runtime.ensureImage({
83
108
  contextPath: resolveImageContextPath(),
84
109
  dockerfile: "Dockerfile",
85
- buildArgs: { POSTGRES_VERSION: resolved.version }
110
+ buildArgs: { POSTGRES_VERSION: resolved.version },
111
+ owner: {
112
+ app: identity.app,
113
+ stack: identity.stack,
114
+ plugin: "postgres",
115
+ role: "db"
116
+ }
86
117
  }).pipe(Effect.catch((cause) => Effect.fail(postgresPluginError("image-build", `failed to build postgres image (${resolved.version})`, cause))));
87
118
  const containerHandle = yield* ensureManagedContainer({
88
119
  runtime,
@@ -103,7 +134,10 @@ const bootPostgresService = (runtime, identity, opts) => Effect.gen(function* ()
103
134
  hostPort: resolved.hostPort
104
135
  }] : void 0,
105
136
  stopGraceSeconds: resolved.stopGraceSeconds,
106
- networkAttach: [containerNetwork, ...resolved.extraNetworks]
137
+ networkAttach: [{
138
+ name: containerNetwork,
139
+ aliases: [networkAlias]
140
+ }, ...resolved.extraNetworks]
107
141
  },
108
142
  mapError: (cause) => postgresPluginError("container-start", `failed to start postgres container '${resolved.name}'`, cause)
109
143
  });
@@ -135,9 +169,9 @@ const bootPostgresService = (runtime, identity, opts) => Effect.gen(function* ()
135
169
  },
136
170
  containerHandle
137
171
  };
138
- }).pipe(Effect.withSpan("postgres.boot", { attributes: {
139
- "postgres.name": opts.name ?? "postgres",
140
- "postgres.version": opts.version ?? DEFAULT_VERSION
172
+ }).pipe(Effect.withSpan("devstack.plugin.postgres.boot", { attributes: {
173
+ [PostgresSpans.name]: opts.name ?? "postgres",
174
+ [PostgresSpans.version]: opts.version ?? DEFAULT_VERSION
141
175
  } }));
142
176
  //#endregion
143
177
  export { bootPostgresService };
@@ -1 +1 @@
1
- {"version":3,"file":"service.mjs","names":[],"sources":["../../../src/plugins/postgres/service.ts"],"sourcesContent":["// Postgres plugin — service body (real boot).\n//\n// Lifecycle (distilled doc § Lifecycle states):\n// 1. Pre-start: identity (app + stack) resolved, deterministic\n// strings (network name, alias, default password) computed.\n// 2. Per-stack docker network ensured (idempotent; survives the\n// cycle that creates it).\n// 3. Vendored image ensured. The Dockerfile at\n// `<package>/images/postgres/Dockerfile` does ONE thing:\n// `ENV PGDATA=/var/lib/postgresql/data-devstack` so writes land in\n// the container's writable layer rather than the upstream\n// `VOLUME /var/lib/postgresql/data` path (which `docker commit`\n// excludes). This is the single load-bearing invariant for\n// snapshot integrity.\n// 4. Container started with `POSTGRES_USER` / `POSTGRES_PASSWORD` /\n// `POSTGRES_DB` env. Joined to the per-stack network under a\n// deterministic alias. Optional host-port publication.\n// 5. `pg_isready` retry loop runs against the bootstrap database\n// via `runtime.exec`, bounded by `readyTimeoutMs`.\n// 6. Subsequent databases ensured idempotently via `psql -tAc`\n// existence-check + `createdb`.\n// 7. Resolved `Postgres` handle returned. The substrate's outer\n// machinery publishes the manifest projection (plain URL only).\n//\n// What this file does NOT do:\n// - Pause/unpause around `docker commit` — engine-level (snapshot\n// orchestrator walks `managedContainers` labels emitted by\n// `snapshot.ts` and pauses them).\n// - Manage schema migrations or run init SQL — schema is plugin-\n// owned by consumers (distilled doc § Generic database-service\n// requirements: \"no migrations, no init SQL, no schema\n// management\").\n// - Register the stop finalizer — `ContainerRuntime.ensureContainer`\n// owns the container's scope finalizer. The 20s WAL-flush grace is\n// threaded into the container spec so Docker does not escalate busy\n// databases at the runtime default.\n\nimport { Duration, Effect, type Scope } from 'effect';\n\nimport type { ContainerHandle, ContainerRuntime } from '../../contracts/container-runtime.ts';\nimport type { Identity } from '../../substrate/identity.ts';\nimport { expectNonEmptyArray } from '../../substrate/runtime/config-validation.ts';\nimport { ensureManagedContainer } from '../../substrate/runtime/managed-container.ts';\nimport {\n\tcredentialedUrl,\n\tplainUrl,\n\twithDatabase,\n\ttype PostgresConnectionParts,\n} from './connection.ts';\nimport { awaitReady, ensureDatabases, type ContainerExec } from './db-ensure.ts';\nimport {\n\tpostgresPluginError,\n\tpostgresConfigError,\n\ttype DatabaseCreateFailed,\n\ttype PostgresConfigError,\n\ttype PostgresConnectionTimeout,\n\ttype PostgresPluginError,\n} from './errors.ts';\n\n/** Resolved Postgres handle — the tag's resolved value.\n *\n * Distilled doc § Outputs: consumers read this to compose\n * `DATABASE_URL` strings. `url(db)` is the per-database composer;\n * `endpoint` is the cluster-level credentialed URL; `plainEndpoint`\n * is the manifest-safe URL (no password). */\nexport interface Postgres {\n\treadonly name: string;\n\treadonly user: string;\n\t/** Credential. Held in-memory on the resolved value; manifest\n\t * projection strips it. Logging convention: never `console.log`\n\t * the entire handle — log `plainEndpoint` instead. */\n\treadonly password: string;\n\treadonly host: string;\n\treadonly port: number;\n\treadonly databases: ReadonlyArray<string>;\n\t/** Cluster-level credentialed URL. */\n\treadonly endpoint: string;\n\t/** Cluster-level no-credentials URL. Manifest-safe. */\n\treadonly plainEndpoint: string;\n\t/** Per-database credentialed URL composer. */\n\treadonly url: (db: string) => string;\n\t/** Per-stack docker network the container is joined to. */\n\treadonly containerNetwork: string;\n\t/** In-network DNS alias siblings dial. */\n\treadonly networkAlias: string;\n}\n\n/** Service options. Defaults track distilled-doc recommendations:\n * postgres 17-alpine is the latest LTS-stable at the time of writing\n * (one major up from the v3 service's 16-alpine pin). */\nexport interface PostgresServiceOptions {\n\treadonly name?: string;\n\treadonly version?: string;\n\treadonly user?: string;\n\treadonly password?: string;\n\treadonly databases?: ReadonlyArray<string>;\n\treadonly hostPort?: number;\n\treadonly extraNetworks?: ReadonlyArray<string>;\n\treadonly readyTimeoutMs?: number;\n\t/** WAL-flush budget on `docker stop`. Default 20s (matches the\n\t * sui-indexer-db sidecar in v3). 10s (docker default) risks\n\t * SIGKILL on busy DBs → recovery mode on next boot. */\n\treadonly stopGraceSeconds?: number;\n}\n\n/** Internal canonicalised options after defaults applied. */\nexport interface ResolvedPostgresOptions {\n\treadonly name: string;\n\treadonly version: string;\n\treadonly user: string;\n\treadonly password: string;\n\treadonly databases: ReadonlyArray<string>;\n\treadonly hostPort: number | undefined;\n\treadonly extraNetworks: ReadonlyArray<string>;\n\treadonly readyTimeoutMs: number;\n\treadonly stopGraceSeconds: number;\n}\n\nconst DEFAULT_VERSION = '17-alpine';\nconst DEFAULT_USER = 'devstack';\nconst DEFAULT_DATABASES = ['devstack'] as const;\nconst DEFAULT_READY_TIMEOUT_MS = 30_000;\nconst DEFAULT_STOP_GRACE_SECONDS = 20;\nconst POSTGRES_PORT = 5432;\n\n/** Deterministic dev password from `(app, stack)`. Tradeoff\n * documented in the distilled doc § Postgres-specific concerns:\n * fine for a single-user dev tool, foot-gun if `hostPort` is set in\n * a multi-user environment. The user-supplied `password` override on\n * `PostgresServiceOptions` is the escape hatch. */\nconst derivePassword = (app: string, stack: string): string =>\n\t`pg-${(app + stack).replace(/[^a-zA-Z0-9]/g, '')}`;\n\nconst sanitizeAlias = (s: string): string => s.replace(/[^a-zA-Z0-9-]/g, '-');\n\n/** Resolve user-supplied options against defaults + identity-derived\n * values. Pure; safe to call before the Effect body runs. */\nexport const resolveOptions = (\n\tidentity: Identity,\n\topts: PostgresServiceOptions,\n): ResolvedPostgresOptions => {\n\tconst name = opts.name ?? 'postgres';\n\tconst databases = expectNonEmptyArray(opts.databases ?? DEFAULT_DATABASES, {\n\t\tfield: 'databases',\n\t\tmessage: 'postgres(): `databases` must be non-empty',\n\t\tmkError: postgresConfigError,\n\t});\n\treturn {\n\t\tname,\n\t\tversion: opts.version ?? DEFAULT_VERSION,\n\t\tuser: opts.user ?? DEFAULT_USER,\n\t\tpassword: opts.password ?? derivePassword(identity.app, identity.stack),\n\t\tdatabases,\n\t\thostPort: opts.hostPort,\n\t\textraNetworks: opts.extraNetworks ?? [],\n\t\treadyTimeoutMs: opts.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS,\n\t\tstopGraceSeconds: opts.stopGraceSeconds ?? DEFAULT_STOP_GRACE_SECONDS,\n\t};\n};\n\n/** Bundled boot result for the barrel. The dynamic-capability factory\n * projects resolved options + identity-derived strings onto the\n * snapshot + codegen decls. */\nexport interface PostgresBootResult {\n\treadonly resolved: ResolvedPostgresOptions;\n\treadonly handle: Postgres;\n\treadonly containerHandle: ContainerHandle;\n}\n\n/** Resolve the vendored Dockerfile context path for THIS package.\n *\n * The build context is the directory containing the Dockerfile\n * (`<package>/images/postgres/`). We resolve it relative to this\n * source file via `import.meta.url`, which works in both source\n * (tsx) and built (.js) form because the layout is preserved.\n *\n * Distilled doc § Open questions: long-term, this should move to\n * the substrate's L1 image-build helper so all plugins resolve\n * their build contexts the same way. For now, the resolution is\n * local to the plugin. */\nconst resolveImageContextPath = (): string => {\n\t// `import.meta.url` is the URL of this module file. From\n\t// `<pkg>/src/plugins/postgres/service.ts` we walk up three\n\t// directories (`plugins/`, `src/`, `<pkg>/`) and then down into\n\t// `images/postgres/`.\n\tconst here = new URL(import.meta.url);\n\tconst ctxUrl = new URL('../../../images/postgres/', here);\n\t// File: URLs need decoding back to a host path for the docker CLI.\n\treturn decodeURIComponent(ctxUrl.pathname);\n};\n\n/** Build a `ContainerExec` view bound to a specific container handle.\n * Thin adapter from the runtime's contract surface to the plugin's\n * local exec seam. The runtime never promotes non-zero exit to\n * failure here — the caller (pg_isready retry loop, createdb\n * existence-check) decides. */\nconst containerExec = (runtime: ContainerRuntime, handle: ContainerHandle): ContainerExec => ({\n\trun: (argv) =>\n\t\truntime.exec(handle, argv).pipe(\n\t\t\t// Daemon-level failures (no such container, daemon\n\t\t\t// unreachable) collapse to a synthetic non-zero ExecResult\n\t\t\t// so the retry loop sees them and the typed timeout error\n\t\t\t// carries the daemon stderr as `lastStderr` for the cause\n\t\t\t// walker. We do NOT surface ContainerRuntimeError here —\n\t\t\t// the plugin's typed errors are the only failure shapes\n\t\t\t// `awaitReady` / `ensureDatabases` produce, and they\n\t\t\t// already carry the captured streams.\n\t\t\tEffect.catch((err) =>\n\t\t\t\tEffect.succeed({\n\t\t\t\t\texitCode: 255,\n\t\t\t\t\tstdout: '',\n\t\t\t\t\tstderr: `runtime.exec failed: ${err.reason} — ${err.detail}`,\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n});\n\n/** Boot the postgres service. The barrel composes this with the\n * capability decls. */\nexport const bootPostgresService = (\n\truntime: ContainerRuntime,\n\tidentity: Identity,\n\topts: PostgresServiceOptions,\n): Effect.Effect<\n\tPostgresBootResult,\n\tPostgresPluginError | PostgresConfigError | PostgresConnectionTimeout | DatabaseCreateFailed,\n\tScope.Scope\n> =>\n\tEffect.gen(function* () {\n\t\tconst resolved = yield* Effect.try({\n\t\t\ttry: () => resolveOptions(identity, opts),\n\t\t\tcatch: (cause) => cause as PostgresConfigError,\n\t\t});\n\n\t\t// Identity-derived strings. The naming pattern must match what\n\t\t// snapshot label-discovery uses — both call sites converge on\n\t\t// `(app, stack, plugin='postgres', role=<name>)`.\n\t\tconst networkAlias = sanitizeAlias(`${resolved.name}-${identity.stack}`);\n\t\tconst containerNetwork = sanitizeAlias(`devstack-${identity.app}-${identity.stack}-postgres`);\n\n\t\t// 1. Ensure the per-stack docker network. Idempotent — no-op\n\t\t// if the network already exists (warm resume).\n\t\tyield* runtime\n\t\t\t.ensureNetwork({\n\t\t\t\tname: containerNetwork,\n\t\t\t\tapp: identity.app,\n\t\t\t\tstack: identity.stack,\n\t\t\t})\n\t\t\t.pipe(\n\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\tpostgresPluginError(\n\t\t\t\t\t\t\t'network-create',\n\t\t\t\t\t\t\t`failed to ensure postgres network '${containerNetwork}'`,\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\n\t\t// 2. Ensure the vendored image. The Dockerfile sets PGDATA off\n\t\t// the upstream VOLUME path so the writable layer captures\n\t\t// PGDATA — the single load-bearing snapshot invariant\n\t\t// (distilled doc § Postgres-specific concerns).\n\t\tconst imageRef = yield* runtime\n\t\t\t.ensureImage({\n\t\t\t\tcontextPath: resolveImageContextPath(),\n\t\t\t\tdockerfile: 'Dockerfile',\n\t\t\t\tbuildArgs: { POSTGRES_VERSION: resolved.version },\n\t\t\t})\n\t\t\t.pipe(\n\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\tpostgresPluginError(\n\t\t\t\t\t\t\t'image-build',\n\t\t\t\t\t\t\t`failed to build postgres image (${resolved.version})`,\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\n\t\t// 3. Start container. `networkAttach`'s first entry becomes the\n\t\t// `--network` flag on `docker run -d`; subsequent entries\n\t\t// are attached post-start with IP-readback (runtime owns\n\t\t// that detail).\n\t\t//\n\t\t// The runtime adapter does NOT yet thread the in-network\n\t\t// alias through `--network-alias`; the network alias the\n\t\t// handle exposes is the architecture-mandated value, but\n\t\t// siblings should dial by `name` (the container name)\n\t\t// which docker also publishes under the network. The\n\t\t// architecture revision to add `networkAlias` to\n\t\t// EnsureContainerSpec is flagged in `index.ts`.\n\t\tconst containerHandle = yield* ensureManagedContainer({\n\t\t\truntime,\n\t\t\tidentity,\n\t\t\tplugin: 'postgres',\n\t\t\trole: resolved.name,\n\t\t\tspec: {\n\t\t\t\tname: `${identity.app}-${identity.stack}-${resolved.name}`,\n\t\t\t\timage: imageRef,\n\t\t\t\trecreate: 'on-config-change',\n\t\t\t\tenv: {\n\t\t\t\t\tPOSTGRES_USER: resolved.user,\n\t\t\t\t\tPOSTGRES_PASSWORD: resolved.password,\n\t\t\t\t\tPOSTGRES_DB: resolved.databases[0]!,\n\t\t\t\t},\n\t\t\t\tports:\n\t\t\t\t\tresolved.hostPort !== undefined\n\t\t\t\t\t\t? [{ containerPort: POSTGRES_PORT, hostPort: resolved.hostPort }]\n\t\t\t\t\t\t: undefined,\n\t\t\t\tstopGraceSeconds: resolved.stopGraceSeconds,\n\t\t\t\tnetworkAttach: [containerNetwork, ...resolved.extraNetworks],\n\t\t\t},\n\t\t\tmapError: (cause) =>\n\t\t\t\tpostgresPluginError(\n\t\t\t\t\t'container-start',\n\t\t\t\t\t`failed to start postgres container '${resolved.name}'`,\n\t\t\t\t\tcause,\n\t\t\t\t),\n\t\t});\n\n\t\t// 4. Probe readiness via `pg_isready` against the bootstrap\n\t\t// database. Server-aware probe — distilled doc § Postgres-\n\t\t// specific concerns: TCP-listener readiness alone is\n\t\t// insufficient (postgres opens the port before accepting\n\t\t// queries).\n\t\tconst exec = containerExec(runtime, containerHandle);\n\t\tyield* awaitReady(exec, resolved.user, resolved.databases[0]!, resolved.readyTimeoutMs);\n\n\t\t// 5. Ensure subsequent databases idempotently. The bootstrap\n\t\t// database (index 0) is image-entrypoint-created via\n\t\t// POSTGRES_DB and skipped by `ensureDatabases`.\n\t\tyield* ensureDatabases(exec, resolved.user, resolved.databases);\n\n\t\t// 6. Resolve the handle. Host for in-stack siblings is the\n\t\t// container name (which docker registers as a DNS entry on\n\t\t// the attached network). When the substrate's\n\t\t// `networkAlias` plumbing lands we'll swap to that; until\n\t\t// then the container name is the stable in-network handle.\n\t\tconst dnsName = `${identity.app}-${identity.stack}-${resolved.name}`;\n\t\tconst parts: PostgresConnectionParts = {\n\t\t\tuser: resolved.user,\n\t\t\tpassword: resolved.password,\n\t\t\thost: dnsName,\n\t\t\tport: POSTGRES_PORT,\n\t\t};\n\t\tconst endpoint = credentialedUrl(parts);\n\t\tconst plainEndpoint = plainUrl(dnsName, POSTGRES_PORT);\n\n\t\tconst handle: Postgres = {\n\t\t\tname: resolved.name,\n\t\t\tuser: resolved.user,\n\t\t\tpassword: resolved.password,\n\t\t\thost: dnsName,\n\t\t\tport: POSTGRES_PORT,\n\t\t\tdatabases: resolved.databases,\n\t\t\tendpoint,\n\t\t\tplainEndpoint,\n\t\t\turl: (db) => withDatabase(endpoint, db),\n\t\t\tcontainerNetwork,\n\t\t\tnetworkAlias,\n\t\t};\n\n\t\treturn { resolved, handle, containerHandle };\n\t}).pipe(\n\t\tEffect.withSpan('postgres.boot', {\n\t\t\tattributes: {\n\t\t\t\t'postgres.name': opts.name ?? 'postgres',\n\t\t\t\t'postgres.version': opts.version ?? DEFAULT_VERSION,\n\t\t\t},\n\t\t}),\n\t);\n\n/** Stop-grace duration. The substrate's finalizer plumbing reads this\n * to set the docker-stop SIGTERM→SIGKILL window. 20s default avoids\n * busy-DB SIGKILL → recovery-mode on next boot. */\nexport const stopGrace = (resolved: ResolvedPostgresOptions): Duration.Duration =>\n\tDuration.seconds(resolved.stopGraceSeconds);\n"],"mappings":";;;;;;;AAsHA,MAAM,kBAAkB;AACxB,MAAM,eAAe;AACrB,MAAM,oBAAoB,CAAC,WAAW;AACtC,MAAM,2BAA2B;AACjC,MAAM,6BAA6B;AACnC,MAAM,gBAAgB;;;;;;AAOtB,MAAM,kBAAkB,KAAa,UACpC,OAAO,MAAM,OAAO,QAAQ,iBAAiB,GAAG;AAEjD,MAAM,iBAAiB,MAAsB,EAAE,QAAQ,kBAAkB,IAAI;;;AAI7E,MAAa,kBACZ,UACA,SAC6B;CAC7B,MAAM,OAAO,KAAK,QAAQ;CAC1B,MAAM,YAAY,oBAAoB,KAAK,aAAa,mBAAmB;EAC1E,OAAO;EACP,SAAS;EACT,SAAS;EACT,CAAC;AACF,QAAO;EACN;EACA,SAAS,KAAK,WAAW;EACzB,MAAM,KAAK,QAAQ;EACnB,UAAU,KAAK,YAAY,eAAe,SAAS,KAAK,SAAS,MAAM;EACvE;EACA,UAAU,KAAK;EACf,eAAe,KAAK,iBAAiB,EAAE;EACvC,gBAAgB,KAAK,kBAAkB;EACvC,kBAAkB,KAAK,oBAAoB;EAC3C;;;;;;;;;;;;;AAuBF,MAAM,gCAAwC;CAK7C,MAAM,OAAO,IAAI,IAAI,OAAO,KAAK,IAAI;CACrC,MAAM,SAAS,IAAI,IAAI,6BAA6B,KAAK;AAEzD,QAAO,mBAAmB,OAAO,SAAS;;;;;;;AAQ3C,MAAM,iBAAiB,SAA2B,YAA4C,EAC7F,MAAM,SACL,QAAQ,KAAK,QAAQ,KAAK,CAAC,KAS1B,OAAO,OAAO,QACb,OAAO,QAAQ;CACd,UAAU;CACV,QAAQ;CACR,QAAQ,wBAAwB,IAAI,OAAO,KAAK,IAAI;CACpD,CAAC,CACF,CACD,EACF;;;AAID,MAAa,uBACZ,SACA,UACA,SAMA,OAAO,IAAI,aAAa;CACvB,MAAM,WAAW,OAAO,OAAO,IAAI;EAClC,WAAW,eAAe,UAAU,KAAK;EACzC,QAAQ,UAAU;EAClB,CAAC;CAKF,MAAM,eAAe,cAAc,GAAG,SAAS,KAAK,GAAG,SAAS,QAAQ;CACxE,MAAM,mBAAmB,cAAc,YAAY,SAAS,IAAI,GAAG,SAAS,MAAM,WAAW;AAI7F,QAAO,QACL,cAAc;EACd,MAAM;EACN,KAAK,SAAS;EACd,OAAO,SAAS;EAChB,CAAC,CACD,KACA,OAAO,OAAO,UACb,OAAO,KACN,oBACC,kBACA,sCAAsC,iBAAiB,IACvD,MACA,CACD,CACD,CACD;CAMF,MAAM,WAAW,OAAO,QACtB,YAAY;EACZ,aAAa,yBAAyB;EACtC,YAAY;EACZ,WAAW,EAAE,kBAAkB,SAAS,SAAS;EACjD,CAAC,CACD,KACA,OAAO,OAAO,UACb,OAAO,KACN,oBACC,eACA,mCAAmC,SAAS,QAAQ,IACpD,MACA,CACD,CACD,CACD;CAcF,MAAM,kBAAkB,OAAO,uBAAuB;EACrD;EACA;EACA,QAAQ;EACR,MAAM,SAAS;EACf,MAAM;GACL,MAAM,GAAG,SAAS,IAAI,GAAG,SAAS,MAAM,GAAG,SAAS;GACpD,OAAO;GACP,UAAU;GACV,KAAK;IACJ,eAAe,SAAS;IACxB,mBAAmB,SAAS;IAC5B,aAAa,SAAS,UAAU;IAChC;GACD,OACC,SAAS,aAAa,KAAA,IACnB,CAAC;IAAE,eAAe;IAAe,UAAU,SAAS;IAAU,CAAC,GAC/D,KAAA;GACJ,kBAAkB,SAAS;GAC3B,eAAe,CAAC,kBAAkB,GAAG,SAAS,cAAc;GAC5D;EACD,WAAW,UACV,oBACC,mBACA,uCAAuC,SAAS,KAAK,IACrD,MACA;EACF,CAAC;CAOF,MAAM,OAAO,cAAc,SAAS,gBAAgB;AACpD,QAAO,WAAW,MAAM,SAAS,MAAM,SAAS,UAAU,IAAK,SAAS,eAAe;AAKvF,QAAO,gBAAgB,MAAM,SAAS,MAAM,SAAS,UAAU;CAO/D,MAAM,UAAU,GAAG,SAAS,IAAI,GAAG,SAAS,MAAM,GAAG,SAAS;CAO9D,MAAM,WAAW,gBAAgB;EALhC,MAAM,SAAS;EACf,UAAU,SAAS;EACnB,MAAM;EACN,MAAM;EAE+B,CAAC;CACvC,MAAM,gBAAgB,SAAS,SAAS,cAAc;AAgBtD,QAAO;EAAE;EAAU,QAAA;GAblB,MAAM,SAAS;GACf,MAAM,SAAS;GACf,UAAU,SAAS;GACnB,MAAM;GACN,MAAM;GACN,WAAW,SAAS;GACpB;GACA;GACA,MAAM,OAAO,aAAa,UAAU,GAAG;GACvC;GACA;GAGwB;EAAE;EAAiB;EAC3C,CAAC,KACF,OAAO,SAAS,iBAAiB,EAChC,YAAY;CACX,iBAAiB,KAAK,QAAQ;CAC9B,oBAAoB,KAAK,WAAW;CACpC,EACD,CAAC,CACF"}
1
+ {"version":3,"file":"service.mjs","names":[],"sources":["../../../src/plugins/postgres/service.ts"],"sourcesContent":["// Postgres plugin — service body (real boot).\n//\n// Lifecycle (distilled doc § Lifecycle states):\n// 1. Pre-start: identity (app + stack) resolved, deterministic\n// strings (network name, alias, default password) computed.\n// 2. Per-stack docker network ensured (idempotent; survives the\n// cycle that creates it).\n// 3. Vendored image ensured. The Dockerfile at\n// `<package>/images/postgres/Dockerfile` does ONE thing:\n// `ENV PGDATA=/var/lib/postgresql/data-devstack` so writes land in\n// the container's writable layer rather than the upstream\n// `VOLUME /var/lib/postgresql/data` path (which `docker commit`\n// excludes). This is the single load-bearing invariant for\n// snapshot integrity.\n// 4. Container started with `POSTGRES_USER` / `POSTGRES_PASSWORD` /\n// `POSTGRES_DB` env. Joined to the per-stack network under a\n// deterministic alias. Optional host-port publication.\n// 5. `pg_isready` retry loop runs against the bootstrap database\n// via `runtime.exec`, bounded by `readyTimeoutMs`.\n// 6. Subsequent databases ensured idempotently via `psql -tAc`\n// existence-check + `createdb`.\n// 7. Resolved `Postgres` handle returned. The substrate's outer\n// machinery publishes the manifest projection (plain URL only).\n//\n// What this file does NOT do:\n// - Pause/unpause around `docker commit` — engine-level (snapshot\n// orchestrator walks `managedContainers` labels emitted by\n// `snapshot.ts` and pauses them).\n// - Manage schema migrations or run init SQL — schema is plugin-\n// owned by consumers (distilled doc § Generic database-service\n// requirements: \"no migrations, no init SQL, no schema\n// management\").\n// - Register the stop finalizer — `ContainerRuntime.ensureContainer`\n// owns the container's scope finalizer. The 20s WAL-flush grace is\n// threaded into the container spec so Docker does not escalate busy\n// databases at the runtime default.\n\nimport { Duration, Effect, type Scope } from 'effect';\nimport { createHash } from 'node:crypto';\nimport { fileURLToPath } from 'node:url';\n\nimport type { ContainerHandle, ContainerRuntime } from '../../contracts/container-runtime.ts';\nimport type { Identity } from '../../substrate/identity.ts';\nimport { ensureManagedContainer } from '../../substrate/runtime/managed-container.ts';\nimport {\n\tcredentialedUrl,\n\tplainUrl,\n\twithDatabase,\n\ttype PostgresConnectionParts,\n} from './connection.ts';\nimport { awaitReady, ensureDatabases, type ContainerExec } from './db-ensure.ts';\nimport {\n\tpostgresPluginError,\n\tpostgresConfigError,\n\ttype DatabaseCreateFailed,\n\ttype PostgresConfigError,\n\ttype PostgresConnectionTimeout,\n\ttype PostgresPluginError,\n} from './errors.ts';\nimport { PostgresSpans } from './spans.ts';\n\n/** Resolved Postgres handle — the tag's resolved value.\n *\n * Distilled doc § Outputs: consumers read this to compose\n * `DATABASE_URL` strings. `url(db)` is the per-database composer;\n * `endpoint` is the cluster-level credentialed URL; `plainEndpoint`\n * is the manifest-safe URL (no password). */\nexport interface Postgres {\n\treadonly name: string;\n\treadonly user: string;\n\t/** Credential. Held in-memory on the resolved value; manifest\n\t * projection strips it. Logging convention: never `console.log`\n\t * the entire handle — log `plainEndpoint` instead. */\n\treadonly password: string;\n\treadonly host: string;\n\treadonly port: number;\n\treadonly databases: ReadonlyArray<string>;\n\t/** Cluster-level credentialed URL. */\n\treadonly endpoint: string;\n\t/** Cluster-level no-credentials URL. Manifest-safe. */\n\treadonly plainEndpoint: string;\n\t/** Per-database credentialed URL composer. */\n\treadonly url: (db: string) => string;\n\t/** Per-stack docker network the container is joined to. */\n\treadonly containerNetwork: string;\n\t/** In-network DNS alias siblings dial. */\n\treadonly networkAlias: string;\n}\n\n/** Service options. Defaults track distilled-doc recommendations:\n * postgres 17-alpine is the latest LTS-stable at the time of writing\n * (one major up from the v3 service's 16-alpine pin). */\nexport interface PostgresServiceOptions {\n\treadonly name?: string;\n\treadonly version?: string;\n\treadonly user?: string;\n\treadonly password?: string;\n\treadonly databases?: ReadonlyArray<string>;\n\treadonly hostPort?: number;\n\treadonly extraNetworks?: ReadonlyArray<string>;\n\treadonly readyTimeoutMs?: number;\n\t/** WAL-flush budget on `docker stop`. Default 20s (matches the\n\t * sui-indexer-db sidecar in v3). 10s (docker default) risks\n\t * SIGKILL on busy DBs → recovery mode on next boot. */\n\treadonly stopGraceSeconds?: number;\n}\n\n/** Internal canonicalised options after defaults applied. */\nexport interface ResolvedPostgresOptions {\n\treadonly name: string;\n\treadonly version: string;\n\treadonly user: string;\n\treadonly password: string;\n\treadonly databases: ReadonlyArray<string>;\n\treadonly hostPort: number | undefined;\n\treadonly extraNetworks: ReadonlyArray<string>;\n\treadonly readyTimeoutMs: number;\n\treadonly stopGraceSeconds: number;\n}\n\nconst DEFAULT_VERSION = '17-alpine';\nconst DEFAULT_USER = 'devstack';\nconst DEFAULT_DATABASES = ['devstack'] as const;\nconst DEFAULT_READY_TIMEOUT_MS = 30_000;\nconst DEFAULT_STOP_GRACE_SECONDS = 20;\nconst POSTGRES_PORT = 5432;\n\n/** Deterministic dev password from `(app, stack, stackRoot)`. Tradeoff\n * documented in the distilled doc § Postgres-specific concerns:\n * fine for a single-user dev tool, foot-gun if `hostPort` is set in\n * a multi-user environment. The user-supplied `password` override on\n * `PostgresServiceOptions` is the escape hatch.\n *\n * The short hash + separator disambiguate three collision classes:\n * - (app, stack) pairs whose sanitized concatenation would collide\n * (e.g. `(\"my-app\", \"dev\")` vs `(\"my\", \"appdev\")` both sanitize to\n * the same body, but the hash binds the unambiguous boundary).\n * - Two checkouts of the SAME `(app, stack)` on the same machine\n * (different working directories) — folding `stackRoot` into the\n * hash means each checkout's postgres password is distinct. (Note:\n * the `pg_isready` liveness probe does NOT authenticate, so this\n * does not gate the probe.) The benefit is that the per-checkout\n * CREDENTIALED endpoint URL — and any `psql`/`createdb` that\n * actually authenticates — differs per checkout, so one checkout's\n * connection string cannot silently land on the other's container.\n * - The `\\x1f` (US, ASCII 31) separator between fields is unambiguous\n * because identity strings and absolute paths cannot legally\n * contain it. */\nexport const derivePassword = (app: string, stack: string, stackRoot: string): string => {\n\tconst body = (app + stack).replace(/[^a-zA-Z0-9]/g, '');\n\tconst fingerprint = createHash('sha256')\n\t\t.update(`${app}\\x1f${stack}\\x1f${stackRoot}`)\n\t\t.digest('hex')\n\t\t.slice(0, 8);\n\treturn `pg-${body}-${fingerprint}`;\n};\n\nconst sanitizeAlias = (s: string): string => s.replace(/[^a-zA-Z0-9-]/g, '-');\n\n/** Resolve user-supplied options against defaults + identity-derived\n * values. Pure; safe to call before the Effect body runs. */\nexport const resolveOptions = (\n\tidentity: Identity,\n\tstackRoot: string,\n\topts: PostgresServiceOptions,\n): ResolvedPostgresOptions => {\n\tconst name = opts.name ?? 'postgres';\n\tconst databases = opts.databases ?? DEFAULT_DATABASES;\n\tif (!Array.isArray(databases) || databases.length === 0) {\n\t\tthrow postgresConfigError({\n\t\t\tfield: 'databases',\n\t\t\tmessage: 'postgres(): `databases` must be non-empty',\n\t\t});\n\t}\n\treturn {\n\t\tname,\n\t\tversion: opts.version ?? DEFAULT_VERSION,\n\t\tuser: opts.user ?? DEFAULT_USER,\n\t\tpassword: opts.password ?? derivePassword(identity.app, identity.stack, stackRoot),\n\t\tdatabases,\n\t\thostPort: opts.hostPort,\n\t\textraNetworks: opts.extraNetworks ?? [],\n\t\treadyTimeoutMs: opts.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS,\n\t\tstopGraceSeconds: opts.stopGraceSeconds ?? DEFAULT_STOP_GRACE_SECONDS,\n\t};\n};\n\n/** Bundled boot result for the barrel. The dynamic-capability factory\n * projects resolved options + identity-derived strings onto the\n * snapshot + codegen decls. */\nexport interface PostgresBootResult {\n\treadonly resolved: ResolvedPostgresOptions;\n\treadonly handle: Postgres;\n\treadonly containerHandle: ContainerHandle;\n}\n\n/** Resolve the vendored Dockerfile context path for THIS package.\n *\n * The build context is the directory containing the Dockerfile\n * (`<package>/images/postgres/`). We resolve it relative to this\n * source file via `import.meta.url`, which works in both source\n * (tsx) and built (.js) form because the layout is preserved.\n *\n * Distilled doc § Open questions: long-term, this should move to\n * the substrate's L1 image-build helper so all plugins resolve\n * their build contexts the same way. For now, the resolution is\n * local to the plugin. */\nconst resolveImageContextPath = (): string => {\n\t// `import.meta.url` is the URL of this module file. From\n\t// `<pkg>/src/plugins/postgres/service.ts` we walk up three\n\t// directories (`plugins/`, `src/`, `<pkg>/`) and then down into\n\t// `images/postgres/`.\n\tconst here = new URL(import.meta.url);\n\tconst ctxUrl = new URL('../../../images/postgres/', here);\n\t// `fileURLToPath` handles the platform-specific decode (the\n\t// `file:///C:/...` → `C:\\...` round-trip on Windows, plus\n\t// percent-decoding everywhere). `decodeURIComponent(pathname)`\n\t// would leave the leading `/` on Windows paths.\n\treturn fileURLToPath(ctxUrl);\n};\n\n/** Build a `ContainerExec` view bound to a specific container handle.\n * Thin adapter from the runtime's contract surface to the plugin's\n * local exec seam. The runtime never promotes non-zero exit to\n * failure here — the caller (pg_isready retry loop, createdb\n * existence-check) decides.\n *\n * Daemon-level failures (no such container, daemon unreachable)\n * surface as a typed `PostgresPluginError({phase: 'container-start'})`\n * rather than a fabricated `exitCode: 255` ExecResult. STYLE_GUIDE\n * §16 — typed errors must carry the actual failure shape, not lie\n * about it being a tx-level non-zero exit. The retry loops in\n * `awaitReady` / `ensureDatabase` treat the typed failure as\n * retryable via `waitForProbe`'s default retry behavior; the\n * underlying cause is preserved on the timeout error's `lastError`\n * field for the cause walker. */\nconst containerExec = (runtime: ContainerRuntime, handle: ContainerHandle): ContainerExec => ({\n\trun: (argv) =>\n\t\truntime.exec(handle, argv).pipe(\n\t\t\tEffect.catch((err) =>\n\t\t\t\tEffect.fail(\n\t\t\t\t\tpostgresPluginError(\n\t\t\t\t\t\t'container-start',\n\t\t\t\t\t\t`runtime.exec failed: ${err.reason} — ${err.detail}`,\n\t\t\t\t\t\terr,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t),\n\t\t),\n});\n\n/** Boot the postgres service. The barrel composes this with the\n * capability decls. */\nexport const bootPostgresService = (\n\truntime: ContainerRuntime,\n\tidentity: Identity,\n\tstackRoot: string,\n\topts: PostgresServiceOptions,\n): Effect.Effect<\n\tPostgresBootResult,\n\tPostgresPluginError | PostgresConfigError | PostgresConnectionTimeout | DatabaseCreateFailed,\n\tScope.Scope\n> =>\n\tEffect.gen(function* () {\n\t\tconst resolved = yield* Effect.try({\n\t\t\ttry: () => resolveOptions(identity, stackRoot, opts),\n\t\t\tcatch: (cause) => cause as PostgresConfigError,\n\t\t});\n\n\t\t// Identity-derived strings. The naming pattern must match what\n\t\t// snapshot label-discovery uses — both call sites converge on\n\t\t// `(app, stack, plugin='postgres', role=<name>)`.\n\t\tconst networkAlias = sanitizeAlias(`${resolved.name}-${identity.stack}`);\n\t\tconst containerNetwork = sanitizeAlias(`devstack-${identity.app}-${identity.stack}-postgres`);\n\n\t\t// 1. Ensure the per-stack docker network. Idempotent — no-op\n\t\t// if the network already exists (warm resume).\n\t\tyield* runtime\n\t\t\t.ensureNetwork({\n\t\t\t\tname: containerNetwork,\n\t\t\t\tapp: identity.app,\n\t\t\t\tstack: identity.stack,\n\t\t\t})\n\t\t\t.pipe(\n\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\tpostgresPluginError(\n\t\t\t\t\t\t\t'network-create',\n\t\t\t\t\t\t\t`failed to ensure postgres network '${containerNetwork}'`,\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\n\t\t// 2. Ensure the vendored image. The Dockerfile sets PGDATA off\n\t\t// the upstream VOLUME path so the writable layer captures\n\t\t// PGDATA — the single load-bearing snapshot invariant\n\t\t// (distilled doc § Postgres-specific concerns).\n\t\tconst imageRef = yield* runtime\n\t\t\t.ensureImage({\n\t\t\t\tcontextPath: resolveImageContextPath(),\n\t\t\t\tdockerfile: 'Dockerfile',\n\t\t\t\tbuildArgs: { POSTGRES_VERSION: resolved.version },\n\t\t\t\towner: { app: identity.app, stack: identity.stack, plugin: 'postgres', role: 'db' },\n\t\t\t})\n\t\t\t.pipe(\n\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\tpostgresPluginError(\n\t\t\t\t\t\t\t'image-build',\n\t\t\t\t\t\t\t`failed to build postgres image (${resolved.version})`,\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\n\t\t// 3. Start container. `networkAttach`'s first entry becomes the\n\t\t// `--network` flag on `docker run -d`; subsequent entries\n\t\t// are attached post-start with IP-readback (runtime owns\n\t\t// that detail). The first attach carries the per-network\n\t\t// DNS alias (`networkAlias`) — Docker registers it as a\n\t\t// secondary DNS name on the per-stack network so siblings\n\t\t// can dial by the parallel-stack-stable alias rather than\n\t\t// the per-stack container name.\n\t\tconst containerHandle = yield* ensureManagedContainer({\n\t\t\truntime,\n\t\t\tidentity,\n\t\t\tplugin: 'postgres',\n\t\t\trole: resolved.name,\n\t\t\tspec: {\n\t\t\t\tname: `${identity.app}-${identity.stack}-${resolved.name}`,\n\t\t\t\timage: imageRef,\n\t\t\t\trecreate: 'on-config-change',\n\t\t\t\tenv: {\n\t\t\t\t\tPOSTGRES_USER: resolved.user,\n\t\t\t\t\tPOSTGRES_PASSWORD: resolved.password,\n\t\t\t\t\tPOSTGRES_DB: resolved.databases[0]!,\n\t\t\t\t},\n\t\t\t\tports:\n\t\t\t\t\tresolved.hostPort !== undefined\n\t\t\t\t\t\t? [{ containerPort: POSTGRES_PORT, hostPort: resolved.hostPort }]\n\t\t\t\t\t\t: undefined,\n\t\t\t\tstopGraceSeconds: resolved.stopGraceSeconds,\n\t\t\t\tnetworkAttach: [\n\t\t\t\t\t{ name: containerNetwork, aliases: [networkAlias] },\n\t\t\t\t\t...resolved.extraNetworks,\n\t\t\t\t],\n\t\t\t},\n\t\t\tmapError: (cause) =>\n\t\t\t\tpostgresPluginError(\n\t\t\t\t\t'container-start',\n\t\t\t\t\t`failed to start postgres container '${resolved.name}'`,\n\t\t\t\t\tcause,\n\t\t\t\t),\n\t\t});\n\n\t\t// 4. Probe readiness via `pg_isready` against the bootstrap\n\t\t// database. Server-aware probe — distilled doc § Postgres-\n\t\t// specific concerns: TCP-listener readiness alone is\n\t\t// insufficient (postgres opens the port before accepting\n\t\t// queries).\n\t\tconst exec = containerExec(runtime, containerHandle);\n\t\tyield* awaitReady(exec, resolved.user, resolved.databases[0]!, resolved.readyTimeoutMs);\n\n\t\t// 5. Ensure subsequent databases idempotently. The bootstrap\n\t\t// database (index 0) is image-entrypoint-created via\n\t\t// POSTGRES_DB and skipped by `ensureDatabases`.\n\t\tyield* ensureDatabases(exec, resolved.user, resolved.databases);\n\n\t\t// 6. Resolve the handle. Host for in-stack siblings is the\n\t\t// container DNS name (always resolves on the attached\n\t\t// network). The per-stack `networkAlias` is registered via\n\t\t// `--network-alias` on the primary attach (see step 3) and\n\t\t// is the parallel-stack-portable DNS name; codegen consumes\n\t\t// it through `index.ts`.\n\t\tconst dnsName = `${identity.app}-${identity.stack}-${resolved.name}`;\n\t\tconst parts: PostgresConnectionParts = {\n\t\t\tuser: resolved.user,\n\t\t\tpassword: resolved.password,\n\t\t\thost: dnsName,\n\t\t\tport: POSTGRES_PORT,\n\t\t};\n\t\tconst endpoint = credentialedUrl(parts);\n\t\tconst plainEndpoint = plainUrl(dnsName, POSTGRES_PORT);\n\n\t\tconst handle: Postgres = {\n\t\t\tname: resolved.name,\n\t\t\tuser: resolved.user,\n\t\t\tpassword: resolved.password,\n\t\t\thost: dnsName,\n\t\t\tport: POSTGRES_PORT,\n\t\t\tdatabases: resolved.databases,\n\t\t\tendpoint,\n\t\t\tplainEndpoint,\n\t\t\turl: (db) => withDatabase(endpoint, db),\n\t\t\tcontainerNetwork,\n\t\t\tnetworkAlias,\n\t\t};\n\n\t\treturn { resolved, handle, containerHandle };\n\t}).pipe(\n\t\tEffect.withSpan('devstack.plugin.postgres.boot', {\n\t\t\tattributes: {\n\t\t\t\t[PostgresSpans.name]: opts.name ?? 'postgres',\n\t\t\t\t[PostgresSpans.version]: opts.version ?? DEFAULT_VERSION,\n\t\t\t},\n\t\t}),\n\t);\n\n/** Stop-grace duration. The substrate's finalizer plumbing reads this\n * to set the docker-stop SIGTERM→SIGKILL window. 20s default avoids\n * busy-DB SIGKILL → recovery-mode on next boot. */\nexport const stopGrace = (resolved: ResolvedPostgresOptions): Duration.Duration =>\n\tDuration.seconds(resolved.stopGraceSeconds);\n"],"mappings":";;;;;;;;;AAwHA,MAAM,kBAAkB;AACxB,MAAM,eAAe;AACrB,MAAM,oBAAoB,CAAC,WAAW;AACtC,MAAM,2BAA2B;AACjC,MAAM,6BAA6B;AACnC,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;AAuBtB,MAAa,kBAAkB,KAAa,OAAe,cAA8B;AAMxF,QAAO,OALO,MAAM,OAAO,QAAQ,iBAAiB,GAKnC,CAAC,GAJE,WAAW,SAAS,CACtC,OAAO,GAAG,IAAI,MAAM,MAAM,MAAM,YAAY,CAC5C,OAAO,MAAM,CACb,MAAM,GAAG,EACqB;;AAGjC,MAAM,iBAAiB,MAAsB,EAAE,QAAQ,kBAAkB,IAAI;;;AAI7E,MAAa,kBACZ,UACA,WACA,SAC6B;CAC7B,MAAM,OAAO,KAAK,QAAQ;CAC1B,MAAM,YAAY,KAAK,aAAa;AACpC,KAAI,CAAC,MAAM,QAAQ,UAAU,IAAI,UAAU,WAAW,EACrD,OAAM,oBAAoB;EACzB,OAAO;EACP,SAAS;EACT,CAAC;AAEH,QAAO;EACN;EACA,SAAS,KAAK,WAAW;EACzB,MAAM,KAAK,QAAQ;EACnB,UAAU,KAAK,YAAY,eAAe,SAAS,KAAK,SAAS,OAAO,UAAU;EAClF;EACA,UAAU,KAAK;EACf,eAAe,KAAK,iBAAiB,EAAE;EACvC,gBAAgB,KAAK,kBAAkB;EACvC,kBAAkB,KAAK,oBAAoB;EAC3C;;;;;;;;;;;;;AAuBF,MAAM,gCAAwC;CAK7C,MAAM,OAAO,IAAI,IAAI,OAAO,KAAK,IAAI;AAMrC,QAAO,cAAc,IALF,IAAI,6BAA6B,KAKzB,CAAC;;;;;;;;;;;;;;;;;AAkB7B,MAAM,iBAAiB,SAA2B,YAA4C,EAC7F,MAAM,SACL,QAAQ,KAAK,QAAQ,KAAK,CAAC,KAC1B,OAAO,OAAO,QACb,OAAO,KACN,oBACC,mBACA,wBAAwB,IAAI,OAAO,KAAK,IAAI,UAC5C,IACA,CACD,CACD,CACD,EACF;;;AAID,MAAa,uBACZ,SACA,UACA,WACA,SAMA,OAAO,IAAI,aAAa;CACvB,MAAM,WAAW,OAAO,OAAO,IAAI;EAClC,WAAW,eAAe,UAAU,WAAW,KAAK;EACpD,QAAQ,UAAU;EAClB,CAAC;CAKF,MAAM,eAAe,cAAc,GAAG,SAAS,KAAK,GAAG,SAAS,QAAQ;CACxE,MAAM,mBAAmB,cAAc,YAAY,SAAS,IAAI,GAAG,SAAS,MAAM,WAAW;AAI7F,QAAO,QACL,cAAc;EACd,MAAM;EACN,KAAK,SAAS;EACd,OAAO,SAAS;EAChB,CAAC,CACD,KACA,OAAO,OAAO,UACb,OAAO,KACN,oBACC,kBACA,sCAAsC,iBAAiB,IACvD,MACA,CACD,CACD,CACD;CAMF,MAAM,WAAW,OAAO,QACtB,YAAY;EACZ,aAAa,yBAAyB;EACtC,YAAY;EACZ,WAAW,EAAE,kBAAkB,SAAS,SAAS;EACjD,OAAO;GAAE,KAAK,SAAS;GAAK,OAAO,SAAS;GAAO,QAAQ;GAAY,MAAM;GAAM;EACnF,CAAC,CACD,KACA,OAAO,OAAO,UACb,OAAO,KACN,oBACC,eACA,mCAAmC,SAAS,QAAQ,IACpD,MACA,CACD,CACD,CACD;CAUF,MAAM,kBAAkB,OAAO,uBAAuB;EACrD;EACA;EACA,QAAQ;EACR,MAAM,SAAS;EACf,MAAM;GACL,MAAM,GAAG,SAAS,IAAI,GAAG,SAAS,MAAM,GAAG,SAAS;GACpD,OAAO;GACP,UAAU;GACV,KAAK;IACJ,eAAe,SAAS;IACxB,mBAAmB,SAAS;IAC5B,aAAa,SAAS,UAAU;IAChC;GACD,OACC,SAAS,aAAa,KAAA,IACnB,CAAC;IAAE,eAAe;IAAe,UAAU,SAAS;IAAU,CAAC,GAC/D,KAAA;GACJ,kBAAkB,SAAS;GAC3B,eAAe,CACd;IAAE,MAAM;IAAkB,SAAS,CAAC,aAAa;IAAE,EACnD,GAAG,SAAS,cACZ;GACD;EACD,WAAW,UACV,oBACC,mBACA,uCAAuC,SAAS,KAAK,IACrD,MACA;EACF,CAAC;CAOF,MAAM,OAAO,cAAc,SAAS,gBAAgB;AACpD,QAAO,WAAW,MAAM,SAAS,MAAM,SAAS,UAAU,IAAK,SAAS,eAAe;AAKvF,QAAO,gBAAgB,MAAM,SAAS,MAAM,SAAS,UAAU;CAQ/D,MAAM,UAAU,GAAG,SAAS,IAAI,GAAG,SAAS,MAAM,GAAG,SAAS;CAO9D,MAAM,WAAW,gBAAgB;EALhC,MAAM,SAAS;EACf,UAAU,SAAS;EACnB,MAAM;EACN,MAAM;EAE+B,CAAC;CACvC,MAAM,gBAAgB,SAAS,SAAS,cAAc;AAgBtD,QAAO;EAAE;EAAU,QAAA;GAblB,MAAM,SAAS;GACf,MAAM,SAAS;GACf,UAAU,SAAS;GACnB,MAAM;GACN,MAAM;GACN,WAAW,SAAS;GACpB;GACA;GACA,MAAM,OAAO,aAAa,UAAU,GAAG;GACvC;GACA;GAGwB;EAAE;EAAiB;EAC3C,CAAC,KACF,OAAO,SAAS,iCAAiC,EAChD,YAAY;EACV,cAAc,OAAO,KAAK,QAAQ;EAClC,cAAc,UAAU,KAAK,WAAW;CACzC,EACD,CAAC,CACF"}