@smthrs/platform-node 1.0.0-rc.5 → 1.0.0-rc.7

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 (274) hide show
  1. package/README.md +12 -12
  2. package/bin/win32-x64/smithers-jj-export.exe +0 -0
  3. package/dist/cjs/EgressHttpClient.d.ts +1 -1
  4. package/dist/cjs/EgressHttpClient.d.ts.map +1 -1
  5. package/dist/cjs/EgressHttpClient.js +4 -4
  6. package/dist/cjs/EgressHttpClient.js.map +2 -2
  7. package/dist/cjs/NodeHost.d.ts +2 -2
  8. package/dist/cjs/NodeHost.d.ts.map +1 -1
  9. package/dist/cjs/NodeHost.js.map +2 -2
  10. package/dist/cjs/ProcessConfinement.js +1 -1
  11. package/dist/cjs/ProcessConfinement.js.map +2 -2
  12. package/dist/cjs/ProcessReaper.d.ts +1 -1
  13. package/dist/cjs/ProcessReaper.d.ts.map +1 -1
  14. package/dist/cjs/ProcessReaper.js +1 -1
  15. package/dist/cjs/ProcessReaper.js.map +1 -1
  16. package/dist/cjs/ScopedProcess.d.ts +2 -2
  17. package/dist/cjs/ScopedProcess.d.ts.map +1 -1
  18. package/dist/cjs/ScopedProcess.js +1 -1
  19. package/dist/cjs/ScopedProcess.js.map +2 -2
  20. package/dist/cjs/internal/PipedProcess.d.ts +2 -2
  21. package/dist/cjs/internal/PipedProcess.d.ts.map +1 -1
  22. package/dist/cjs/internal/PipedProcess.js +2 -2
  23. package/dist/cjs/internal/PipedProcess.js.map +2 -2
  24. package/dist/cjs/internal/ProcessCleanup.d.ts +1 -1
  25. package/dist/cjs/internal/ProcessCleanup.d.ts.map +1 -1
  26. package/dist/cjs/internal/ProcessCleanup.js.map +1 -1
  27. package/dist/cjs/internal/ProcessSupervisor.d.ts +2 -2
  28. package/dist/cjs/internal/ProcessSupervisor.d.ts.map +1 -1
  29. package/dist/cjs/internal/ProcessSupervisor.js +2 -2
  30. package/dist/cjs/internal/ProcessSupervisor.js.map +2 -2
  31. package/dist/esm/EgressHttpClient.d.ts +1 -1
  32. package/dist/esm/EgressHttpClient.d.ts.map +1 -1
  33. package/dist/esm/EgressHttpClient.js +4 -4
  34. package/dist/esm/EgressHttpClient.js.map +1 -1
  35. package/dist/esm/NodeHost.d.ts +2 -2
  36. package/dist/esm/NodeHost.d.ts.map +1 -1
  37. package/dist/esm/NodeHost.js.map +1 -1
  38. package/dist/esm/ProcessConfinement.js +1 -1
  39. package/dist/esm/ProcessConfinement.js.map +1 -1
  40. package/dist/esm/ProcessReaper.d.ts +1 -1
  41. package/dist/esm/ProcessReaper.d.ts.map +1 -1
  42. package/dist/esm/ProcessReaper.js +1 -1
  43. package/dist/esm/ProcessReaper.js.map +1 -1
  44. package/dist/esm/ScopedProcess.d.ts +2 -2
  45. package/dist/esm/ScopedProcess.d.ts.map +1 -1
  46. package/dist/esm/ScopedProcess.js +1 -1
  47. package/dist/esm/ScopedProcess.js.map +1 -1
  48. package/dist/esm/internal/PipedProcess.d.ts +2 -2
  49. package/dist/esm/internal/PipedProcess.d.ts.map +1 -1
  50. package/dist/esm/internal/PipedProcess.js +2 -2
  51. package/dist/esm/internal/PipedProcess.js.map +1 -1
  52. package/dist/esm/internal/ProcessCleanup.d.ts +1 -1
  53. package/dist/esm/internal/ProcessCleanup.d.ts.map +1 -1
  54. package/dist/esm/internal/ProcessCleanup.js.map +1 -1
  55. package/dist/esm/internal/ProcessSupervisor.d.ts +2 -2
  56. package/dist/esm/internal/ProcessSupervisor.d.ts.map +1 -1
  57. package/dist/esm/internal/ProcessSupervisor.js +2 -2
  58. package/dist/esm/internal/ProcessSupervisor.js.map +1 -1
  59. package/dist/vendor/adapters.json +622 -382
  60. package/dist/vendor/{chunk-42QKY6T6.js → chunk-2ZAGBWTH.js} +1 -1
  61. package/dist/vendor/{chunk-3GR5VZ2M.js → chunk-3SL3HKQA.js} +3 -3
  62. package/dist/vendor/{chunk-VANKHF46.js → chunk-423HXGL6.js} +55 -23
  63. package/dist/vendor/{chunk-6ME2W7TF.js → chunk-4WIQ2XN7.js} +2 -2
  64. package/dist/vendor/{chunk-Y35ZAO5U.js → chunk-6FSJ2PMW.js} +8 -8
  65. package/dist/vendor/{chunk-5ZMHBXIK.js → chunk-6L2NJEOV.js} +11 -4
  66. package/dist/vendor/{chunk-TFIKFFSC.js → chunk-7NMJM44J.js} +2 -2
  67. package/dist/vendor/{chunk-OXNGN5H7.js → chunk-A7UXCEJA.js} +1 -1
  68. package/dist/vendor/chunk-ADP5LRUF.js +274 -0
  69. package/dist/vendor/{chunk-DS7GF46S.js → chunk-AWNTTSHL.js} +2 -2
  70. package/dist/vendor/{chunk-IIJ52ZU6.js → chunk-BUKVIAEM.js} +2 -2
  71. package/dist/vendor/{chunk-BKTBUBQY.js → chunk-BY3SDXID.js} +71 -7
  72. package/dist/vendor/{chunk-JJ24ARBQ.js → chunk-C3PGUWW5.js} +53 -33
  73. package/dist/vendor/{chunk-J7A4CZDO.js → chunk-D2ZL4PLZ.js} +8 -7
  74. package/dist/vendor/{chunk-XDKNKFBW.js → chunk-D4HEMNGU.js} +2 -2
  75. package/dist/vendor/{chunk-DVOAHXPG.js → chunk-FFD6R5JZ.js} +3 -3
  76. package/dist/vendor/{chunk-UFH5UUZK.js → chunk-HDV6VIRM.js} +1 -1
  77. package/dist/vendor/chunk-IWSDM4CG.js +16 -0
  78. package/dist/vendor/{chunk-KUUXSID7.js → chunk-KABHKTVG.js} +2 -2
  79. package/dist/vendor/{chunk-B3FZ67QU.js → chunk-KFDBU6RR.js} +4 -3
  80. package/dist/vendor/chunk-LNWGIDIG.js +20 -0
  81. package/dist/vendor/{chunk-TXKLEBBL.js → chunk-MUEED3C3.js} +3 -2
  82. package/dist/vendor/{chunk-EZWY45HP.js → chunk-N6TK35N5.js} +2 -2
  83. package/dist/vendor/{chunk-QPLDT6A6.js → chunk-NQO6V4KI.js} +29 -13
  84. package/dist/vendor/{chunk-AMQKOQNT.js → chunk-NTEWGPPG.js} +9 -9
  85. package/dist/vendor/chunk-NUJQMLJP.js +325 -0
  86. package/dist/vendor/{chunk-54RLBMTZ.js → chunk-OF3N2FN4.js} +1 -1
  87. package/dist/vendor/{chunk-RO2R4MJH.js → chunk-PA32ARKD.js} +17 -17
  88. package/dist/vendor/{chunk-JG5TTWPT.js → chunk-PECOFLCU.js} +3 -3
  89. package/dist/vendor/{chunk-VZBDV3TV.js → chunk-PU7QVYND.js} +2 -2
  90. package/dist/vendor/{chunk-2Y7WC4KU.js → chunk-QHR2RC5M.js} +2 -2
  91. package/dist/vendor/{chunk-QYBKAIA6.js → chunk-R327POEZ.js} +2 -2
  92. package/dist/vendor/chunk-RKVXYKHJ.js +29 -0
  93. package/dist/vendor/{chunk-XH4U3JSV.js → chunk-RQH5DSBA.js} +2 -2
  94. package/dist/vendor/{chunk-DWSHTSVX.js → chunk-S6QUQLBV.js} +5 -5
  95. package/dist/vendor/{chunk-KOZMM5PZ.js → chunk-UPIQ52FQ.js} +12 -12
  96. package/dist/vendor/{chunk-EGBNVEEW.js → chunk-UUQXJRMK.js} +1 -1
  97. package/dist/vendor/{chunk-LEUPYT3Q.js → chunk-UUYTTEDH.js} +1 -1
  98. package/dist/vendor/{chunk-NBVRTDTV.js → chunk-V2EB7FHY.js} +2 -2
  99. package/dist/vendor/{chunk-M32LDTW7.js → chunk-VIWU4EBF.js} +2 -2
  100. package/dist/vendor/{chunk-SIR62W3Y.js → chunk-VWSWEUN5.js} +51 -31
  101. package/dist/vendor/{chunk-MBSKBQ5D.js → chunk-WPZDL2D7.js} +1 -1
  102. package/dist/vendor/chunk-YDY5P7GL.js +20 -0
  103. package/dist/vendor/{chunk-FNZOJHSV.js → chunk-YFD4QFIL.js} +17 -17
  104. package/dist/vendor/{chunk-GXCHI7BM.js → chunk-ZVJCBTJG.js} +5 -5
  105. package/dist/vendor/cjs/node/NodeAddressResolver.d.ts +16 -0
  106. package/dist/vendor/cjs/node/NodeAddressResolver.js +1 -0
  107. package/dist/vendor/cjs/node/NodeChildProcessSpawner.d.ts +1 -0
  108. package/dist/vendor/cjs/node/NodeClusterHttp.d.ts +13 -22
  109. package/dist/vendor/cjs/node/NodeClusterSocket.d.ts +14 -8
  110. package/dist/vendor/cjs/node/NodeCrypto.d.ts +1 -0
  111. package/dist/vendor/cjs/node/NodeDatagramSocket.d.ts +10 -0
  112. package/dist/vendor/cjs/node/NodeDatagramSocket.js +1 -0
  113. package/dist/vendor/cjs/node/NodeDns.d.ts +51 -0
  114. package/dist/vendor/cjs/node/NodeDns.js +1 -0
  115. package/dist/vendor/cjs/node/NodeFileSystem.d.ts +1 -0
  116. package/dist/vendor/cjs/node/NodeHttpClient.d.ts +21 -2
  117. package/dist/vendor/cjs/node/NodeHttpIncomingMessage.d.ts +5 -3
  118. package/dist/vendor/cjs/node/NodeHttpPlatform.d.ts +8 -6
  119. package/dist/vendor/cjs/node/NodeHttpServer.d.ts +21 -10
  120. package/dist/vendor/cjs/node/NodeHttpServerRequest.d.ts +4 -1
  121. package/dist/vendor/cjs/node/NodeMultipart.d.ts +5 -1
  122. package/dist/vendor/cjs/node/NodeMultipartParser.d.ts +9 -1
  123. package/dist/vendor/cjs/node/NodePath.d.ts +3 -0
  124. package/dist/vendor/cjs/node/NodeRedis.d.ts +5 -1
  125. package/dist/vendor/cjs/node/NodeRuntime.d.ts +3 -0
  126. package/dist/vendor/cjs/node/NodeServices.d.ts +4 -1
  127. package/dist/vendor/cjs/node/NodeSink.d.ts +1 -0
  128. package/dist/vendor/cjs/node/NodeSocket.d.ts +10 -3
  129. package/dist/vendor/cjs/node/NodeSocketServer.d.ts +1 -0
  130. package/dist/vendor/cjs/node/NodeStdio.d.ts +1 -0
  131. package/dist/vendor/cjs/node/NodeStream.d.ts +1 -0
  132. package/dist/vendor/cjs/node/NodeTerminal.d.ts +6 -3
  133. package/dist/vendor/cjs/node/NodeWorker.d.ts +3 -1
  134. package/dist/vendor/cjs/node/NodeWorkerRunner.d.ts +2 -1
  135. package/dist/vendor/cjs/node/Undici.d.ts +3 -0
  136. package/dist/vendor/cjs/node/index.d.ts +40 -0
  137. package/dist/vendor/cjs/node-shared/NodeAddressResolver.d.ts +13 -0
  138. package/dist/vendor/cjs/node-shared/NodeAddressResolver.js +1 -0
  139. package/dist/vendor/cjs/node-shared/NodeChildProcessSpawner.d.ts +12 -3
  140. package/dist/vendor/cjs/node-shared/NodeClusterSocket.d.ts +17 -4
  141. package/dist/vendor/cjs/node-shared/NodeCrypto.d.ts +3 -0
  142. package/dist/vendor/cjs/node-shared/NodeDatagramSocket.d.ts +246 -0
  143. package/dist/vendor/cjs/node-shared/NodeDatagramSocket.js +1 -0
  144. package/dist/vendor/cjs/node-shared/NodeDns.d.ts +221 -0
  145. package/dist/vendor/cjs/node-shared/NodeDns.js +1 -0
  146. package/dist/vendor/cjs/node-shared/NodeFileSystem.d.ts +1 -0
  147. package/dist/vendor/cjs/node-shared/NodeHttpCompression.d.ts +5 -1
  148. package/dist/vendor/cjs/node-shared/NodePath.d.ts +3 -0
  149. package/dist/vendor/cjs/node-shared/NodeRuntime.d.ts +4 -0
  150. package/dist/vendor/cjs/node-shared/NodeSink.d.ts +4 -0
  151. package/dist/vendor/cjs/node-shared/NodeSocket.d.ts +11 -1
  152. package/dist/vendor/cjs/node-shared/NodeSocketServer.d.ts +8 -1
  153. package/dist/vendor/cjs/node-shared/NodeStdio.d.ts +1 -0
  154. package/dist/vendor/cjs/node-shared/NodeStream.d.ts +15 -0
  155. package/dist/vendor/cjs/node-shared/NodeTerminal.d.ts +6 -4
  156. package/dist/vendor/cjs/node-shared/index.d.ts +29 -0
  157. package/dist/vendor/cjs/node-shared/internal/nodeChildProcessSpawner.d.ts +1 -1
  158. package/dist/vendor/cjs/platform-node-shared.d.ts +29 -0
  159. package/dist/vendor/cjs/platform-node.d.ts +40 -0
  160. package/dist/vendor/node/NodeAddressResolver.d.ts +16 -0
  161. package/dist/vendor/node/NodeAddressResolver.js +8 -0
  162. package/dist/vendor/node/NodeChildProcessSpawner.d.ts +1 -0
  163. package/dist/vendor/node/NodeChildProcessSpawner.js +6 -6
  164. package/dist/vendor/node/NodeClusterHttp.d.ts +13 -22
  165. package/dist/vendor/node/NodeClusterHttp.js +32 -32
  166. package/dist/vendor/node/NodeClusterSocket.d.ts +14 -8
  167. package/dist/vendor/node/NodeClusterSocket.js +15 -15
  168. package/dist/vendor/node/NodeCrypto.d.ts +1 -0
  169. package/dist/vendor/node/NodeCrypto.js +2 -2
  170. package/dist/vendor/node/NodeDatagramSocket.d.ts +10 -0
  171. package/dist/vendor/node/NodeDatagramSocket.js +12 -0
  172. package/dist/vendor/node/NodeDns.d.ts +51 -0
  173. package/dist/vendor/node/NodeDns.js +12 -0
  174. package/dist/vendor/node/NodeFileSystem.d.ts +1 -0
  175. package/dist/vendor/node/NodeFileSystem.js +3 -3
  176. package/dist/vendor/node/NodeHttpClient.d.ts +21 -2
  177. package/dist/vendor/node/NodeHttpClient.js +6 -6
  178. package/dist/vendor/node/NodeHttpIncomingMessage.d.ts +5 -3
  179. package/dist/vendor/node/NodeHttpIncomingMessage.js +4 -4
  180. package/dist/vendor/node/NodeHttpPlatform.d.ts +8 -6
  181. package/dist/vendor/node/NodeHttpPlatform.js +8 -8
  182. package/dist/vendor/node/NodeHttpServer.d.ts +21 -10
  183. package/dist/vendor/node/NodeHttpServer.js +26 -26
  184. package/dist/vendor/node/NodeHttpServerRequest.d.ts +4 -1
  185. package/dist/vendor/node/NodeHttpServerRequest.js +1 -1
  186. package/dist/vendor/node/NodeMultipart.d.ts +5 -1
  187. package/dist/vendor/node/NodeMultipart.js +5 -5
  188. package/dist/vendor/node/NodeMultipartParser.d.ts +9 -1
  189. package/dist/vendor/node/NodeMultipartParser.js +1 -1
  190. package/dist/vendor/node/NodePath.d.ts +3 -0
  191. package/dist/vendor/node/NodePath.js +2 -2
  192. package/dist/vendor/node/NodeRedis.d.ts +5 -1
  193. package/dist/vendor/node/NodeRedis.js +1 -1
  194. package/dist/vendor/node/NodeRuntime.d.ts +3 -0
  195. package/dist/vendor/node/NodeRuntime.js +2 -2
  196. package/dist/vendor/node/NodeServices.d.ts +4 -1
  197. package/dist/vendor/node/NodeServices.js +17 -17
  198. package/dist/vendor/node/NodeSink.d.ts +1 -0
  199. package/dist/vendor/node/NodeSink.js +2 -2
  200. package/dist/vendor/node/NodeSocket.d.ts +10 -3
  201. package/dist/vendor/node/NodeSocket.js +2 -2
  202. package/dist/vendor/node/NodeSocketServer.d.ts +1 -0
  203. package/dist/vendor/node/NodeSocketServer.js +3 -3
  204. package/dist/vendor/node/NodeStdio.d.ts +1 -0
  205. package/dist/vendor/node/NodeStdio.js +4 -4
  206. package/dist/vendor/node/NodeStream.d.ts +1 -0
  207. package/dist/vendor/node/NodeStream.js +3 -3
  208. package/dist/vendor/node/NodeTerminal.d.ts +6 -3
  209. package/dist/vendor/node/NodeTerminal.js +2 -2
  210. package/dist/vendor/node/NodeWorker.d.ts +3 -1
  211. package/dist/vendor/node/NodeWorker.js +1 -1
  212. package/dist/vendor/node/NodeWorkerRunner.d.ts +2 -1
  213. package/dist/vendor/node/NodeWorkerRunner.js +1 -1
  214. package/dist/vendor/node/Undici.d.ts +3 -0
  215. package/dist/vendor/node/Undici.js +1 -1
  216. package/dist/vendor/node/index.d.ts +40 -0
  217. package/dist/vendor/node/index.js +70 -55
  218. package/dist/vendor/node-shared/NodeAddressResolver.d.ts +13 -0
  219. package/dist/vendor/node-shared/NodeAddressResolver.js +7 -0
  220. package/dist/vendor/node-shared/NodeChildProcessSpawner.d.ts +12 -3
  221. package/dist/vendor/node-shared/NodeChildProcessSpawner.js +5 -5
  222. package/dist/vendor/node-shared/NodeClusterSocket.d.ts +17 -4
  223. package/dist/vendor/node-shared/NodeClusterSocket.js +3 -3
  224. package/dist/vendor/node-shared/NodeCrypto.d.ts +3 -0
  225. package/dist/vendor/node-shared/NodeCrypto.js +1 -1
  226. package/dist/vendor/node-shared/NodeDatagramSocket.d.ts +246 -0
  227. package/dist/vendor/node-shared/NodeDatagramSocket.js +11 -0
  228. package/dist/vendor/node-shared/NodeDns.d.ts +221 -0
  229. package/dist/vendor/node-shared/NodeDns.js +31 -0
  230. package/dist/vendor/node-shared/NodeFileSystem.d.ts +1 -0
  231. package/dist/vendor/node-shared/NodeFileSystem.js +2 -2
  232. package/dist/vendor/node-shared/NodeHttpCompression.d.ts +5 -1
  233. package/dist/vendor/node-shared/NodeHttpCompression.js +1 -1
  234. package/dist/vendor/node-shared/NodePath.d.ts +3 -0
  235. package/dist/vendor/node-shared/NodePath.js +1 -1
  236. package/dist/vendor/node-shared/NodeRuntime.d.ts +4 -0
  237. package/dist/vendor/node-shared/NodeRuntime.js +1 -1
  238. package/dist/vendor/node-shared/NodeSink.d.ts +4 -0
  239. package/dist/vendor/node-shared/NodeSink.js +1 -1
  240. package/dist/vendor/node-shared/NodeSocket.d.ts +11 -1
  241. package/dist/vendor/node-shared/NodeSocket.js +1 -1
  242. package/dist/vendor/node-shared/NodeSocketServer.d.ts +8 -1
  243. package/dist/vendor/node-shared/NodeSocketServer.js +2 -2
  244. package/dist/vendor/node-shared/NodeStdio.d.ts +1 -0
  245. package/dist/vendor/node-shared/NodeStdio.js +3 -3
  246. package/dist/vendor/node-shared/NodeStream.d.ts +15 -0
  247. package/dist/vendor/node-shared/NodeStream.js +2 -2
  248. package/dist/vendor/node-shared/NodeTerminal.d.ts +6 -4
  249. package/dist/vendor/node-shared/NodeTerminal.js +1 -1
  250. package/dist/vendor/node-shared/index.d.ts +29 -0
  251. package/dist/vendor/node-shared/index.js +34 -22
  252. package/dist/vendor/node-shared/internal/nodeChildProcessSpawner.d.ts +1 -1
  253. package/dist/vendor/node-shared/internal/nodeChildProcessSpawner.js +1 -1
  254. package/dist/vendor/node-shared/internal/utils.js +1 -1
  255. package/dist/vendor/platform-node-shared.d.ts +29 -0
  256. package/dist/vendor/platform-node-shared.js +34 -22
  257. package/dist/vendor/platform-node.d.ts +40 -0
  258. package/dist/vendor/platform-node.js +70 -55
  259. package/docs/README.md +2 -2
  260. package/docs/api.md +2 -2
  261. package/docs/concepts/host-bundle.md +2 -2
  262. package/docs/installation.md +8 -9
  263. package/docs/quickstart.md +2 -2
  264. package/package.json +12 -12
  265. package/src/EgressHttpClient.ts +4 -4
  266. package/src/NodeHost.ts +2 -2
  267. package/src/ProcessConfinement.ts +1 -1
  268. package/src/ProcessReaper.ts +2 -2
  269. package/src/ScopedProcess.ts +2 -2
  270. package/src/internal/PipedProcess.ts +2 -2
  271. package/src/internal/ProcessCleanup.ts +1 -1
  272. package/src/internal/ProcessSupervisor.ts +2 -2
  273. /package/dist/vendor/{chunk-M7CLUILA.js → chunk-3WUGYBFU.js} +0 -0
  274. /package/dist/vendor/{chunk-ZJMRMY57.js → chunk-ASGOJ3XA.js} +0 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ProcessReaper.ts"],
4
- "sourcesContent": ["/**\n * Reaping the processes a dead host incarnation abandoned.\n *\n * Scope closure cleans up work a live host still owns. On POSIX, a prepared\n * supervisor also receives private-channel EOF when its host disappears and\n * independently requests group cleanup. The durable ledger remains the fallback\n * when that cleanup is incomplete or cannot be verified; the next incarnation\n * uses this module to examine the recorded process identities before acting.\n *\n * The rules are narrow on purpose, because the input is a durable record that\n * outlived the process that wrote it and a pid is reused. A record is signalled\n * only when ALL of these hold:\n *\n * - it belongs to a DIFFERENT incarnation of the SAME `hostId`\n * ({@link ProcessLedger.Service.orphans} already filters that);\n * - its owner is gone. A pid that exists but belongs to another user counts as\n * alive, exactly as {@link HostLiveness} counts it, so only `ESRCH` reads as\n * dead and every other answer keeps the record;\n * - it names a target the platform can actually signal. On POSIX that is a\n * process group the child leads itself, so `pgid` has to be a safe integer\n * above 1 and equal to `pid`; on Windows it is a pid, so `pgid` has to be\n * absent. The check is made against the DURABLE record rather than trusted\n * from it: `process.kill(-0, \"SIGKILL\")` signals this host's own process\n * group, and `@smthrs/kernel`'s ledger schema admits zero;\n * - that group is not THIS process's group, and its pid is not this process's\n * pid. The host's real process group is read from the operating system,\n * because a stored number can claim anything and the shell that started this\n * host shares its group. A host that cannot read its own group keeps the\n * record rather than signalling it on a guard that did not run;\n * - the number still names the process the record describes. A record written\n * before this machine booted names a pid from a pid space that no longer\n * exists, and within one boot the recorded start time has to match the start\n * time the operating system reports for that pid. A host that cannot ASK the\n * question answers {@link StartTime} `unavailable`, which refuses the kill\n * and keeps the record; no evidence never authorizes a signal.\n *\n * The outcome of the signal is part of the record. A kill that succeeds, or\n * that reports the group is already gone, retires the record through\n * {@link ProcessLedger.Service.reaped}; a kill that FAILS for any other reason\n * leaves the record inherited so the next incarnation tries again. A record\n * refused on grounds the operating system will not revisit \u2014 a number no\n * platform can signal, a pid that is gone, a start time that does not match \u2014 is\n * retired through {@link ProcessLedger.Service.skipped}, which says in the\n * journal that nothing was signalled and stops every later incarnation\n * re-examining a number the operating system has moved on from. The three\n * refusals a later incarnation CAN answer differently \u2014 `owner-alive`,\n * `identity-unverified`, and `own-group-unknown` \u2014 retire nothing.\n *\n * @since 0.1.0\n */\n\nimport * as ContainedSpawner from \"@smthrs/kernel/ContainedSpawner\"\nimport * as ProcessLedger from \"@smthrs/kernel/ProcessLedger\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as Metric from \"effect/Metric\"\nimport type * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport { ChildProcessSpawner, make as makeSpawner } from \"effect/unstable/process/ChildProcessSpawner\"\nimport { spawnSync } from \"node:child_process\"\nimport { uptime } from \"node:os\"\nimport { parse, win32 } from \"node:path\"\nimport { packageRoot, resolveDefaultExecutable } from \"./internal/AtomicFileSystemExecutable.ts\"\nimport { usableExecutable } from \"./internal/AtomicFileSystemTransport.ts\"\nimport * as PipedProcess from \"./internal/PipedProcess.ts\"\nimport * as ProcessCleanup from \"./internal/ProcessCleanup.ts\"\nimport * as ProcSnapshot from \"./internal/ProcSnapshot.ts\"\n\n/**\n * What the operating system can say about a pid.\n *\n * `unknown` is not `dead`. A pid this host may not signal answers `EPERM`, and\n * a reaper that read that as \"gone\" would kill another user's children on the\n * strength of a record it cannot verify.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Liveness = \"alive\" | \"dead\" | \"unknown\"\n\n/**\n * What happened when a process group was signalled.\n *\n * @category models\n * @since 0.1.0\n */\nexport type KillOutcome =\n /** The signal was delivered. */\n | \"signalled\"\n /** There was nothing left to signal, which is the same end state. */\n | \"already-gone\"\n /** The signal was refused, and the record must be tried again later. */\n | \"failed\"\n\n/**\n * What the operating system can say about when a pid started.\n *\n * The three negative answers are deliberately distinct, because they authorize\n * three different decisions. `gone` is evidence \u2014 the pid does not exist, so\n * the record describes nothing and is retired unsignalled. `unavailable` is the\n * ABSENCE of evidence: `ps` is missing, unanswerable, or printed something no\n * clock can read, so the record is kept for an incarnation that can ask.\n * `unsupported` is a platform that has no such column at all, which falls back\n * to the boot-time comparison alone.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type StartTime =\n /** The operating system reports this start time, in epoch milliseconds. */\n | { readonly _tag: \"started\"; readonly startedAtMs: number }\n /** The pid names no process. */\n | { readonly _tag: \"gone\" }\n /** The question could not be asked on this host. */\n | { readonly _tag: \"unavailable\" }\n /** The platform has no start-time column at all. */\n | { readonly _tag: \"unsupported\" }\n\n/**\n * The operating-system operations reaping needs.\n *\n * It is a seam, not an abstraction: `flows` ships exactly the two\n * implementations below and a test drives the Windows one on a POSIX host,\n * which is the only way that path is ever exercised in this repository.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface System {\n /** Whether a pid names a process that still exists. */\n readonly isAlive: (pid: number) => Liveness\n /** When the process behind a pid started. */\n readonly startedAtMs: (pid: number) => StartTime\n /** The process group THIS process belongs to, or `null` where there are none. */\n readonly ownGroup: () => number | null\n /** When this machine booted, in epoch milliseconds. */\n readonly bootedAtMs: () => number\n /**\n * Whether the numbers in a record name something this platform may signal,\n * checked before any other guard consults them.\n */\n readonly refuseTarget: (record: ProcessLedger.ProcessRecord) => Refusal | undefined\n /** Ends an abandoned process and everything below it. */\n readonly killTree: (record: ProcessLedger.ProcessRecord) => KillOutcome\n}\n\nconst errorCode = (cause: unknown): string | undefined => (cause as { readonly code?: string } | null)?.code\n\nconst liveness = (pid: number): Liveness => {\n try {\n // Signal 0 delivers nothing; it only asks whether the pid is signalable.\n process.kill(pid, 0)\n return \"alive\"\n } catch (cause) {\n // ESRCH is the only answer that means gone. EPERM means the process\n // exists and belongs to another user; anything else is a question this\n // host could not ask, and both keep the record rather than kill on it.\n return errorCode(cause) === \"ESRCH\" ? \"dead\" : \"unknown\"\n }\n}\n\nconst bootedAtMs = (): number => Date.now() - Math.round(uptime() * 1000)\n\n/**\n * The start time `ps` reports for a pid.\n *\n * `lstart` is the one start-time column both BSD (macOS) and procps (Linux)\n * spell the same way, and it prints an absolute time rather than an elapsed\n * one, so the answer does not drift between reading it and comparing it. It is\n * also a LOCALIZED column, so the probe pins `LC_ALL=C` and `TZ=UTC`.\n * Its text has no zone; parsing explicitly as UTC keeps the identity independent\n * of the host runtime's timezone.\n */\nconst startedAtMs = (executable: string) => (pid: number): StartTime => {\n const answer = ps(executable, \"lstart\", pid)\n if (answer._tag === \"gone\") {\n // A nonzero exit is `ps` saying the pid does not exist, but it is also what\n // a `ps` that rejected the column, or one that is not the `ps` this probe\n // expects, says about a pid that is very much alive. The kernel is asked\n // directly before a live process is retired as gone.\n return liveness(pid) === \"dead\" ? answer : { _tag: \"unavailable\" }\n }\n if (answer._tag !== \"printed\") return answer\n const parsed = Date.parse(`${answer.text} UTC`)\n return Number.isNaN(parsed) ? { _tag: \"unavailable\" } : { _tag: \"started\", startedAtMs: parsed }\n}\n\n/**\n * The absolute path the process probe is run from.\n *\n * Never a `PATH` lookup. The inherited environment belongs to whoever started\n * this host, and a planted `ps` that prints a fabricated start time \u2014 or none\n * at all \u2014 would decide whether a `SIGKILL` is authorized.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultPsExecutable = \"/bin/ps\"\n\n/** How long the probe may take before it counts as an answer nobody gave. */\nconst psTimeoutMs = 5000\n\n/**\n * The Windows directory the host was started with, when it names one.\n */\nconst windowsRoot = (): string => {\n const systemRoot = process.env.SystemRoot\n return systemRoot !== undefined && win32.isAbsolute(systemRoot) ? systemRoot : \"C:\\\\Windows\"\n}\n\n/**\n * The system `taskkill`, by absolute path. Windows resolves a bare name from\n * the current directory before `PATH`, so a flow that wrote `taskkill.exe`\n * into the host's working directory would otherwise run as the host.\n */\nconst taskkillExecutable = (): string => win32.join(windowsRoot(), \"System32\", \"taskkill.exe\")\n\n/** A live-release probe has its own short bound within the shutdown deadline. */\nconst cleanupProbeTimeoutMs = 2000\n\n/**\n * One fixed-path snapshot supplies group membership, start identities and our\n * own group together. Only numeric fields and C-locale lstart are read; command\n * text and the caller's PATH never influence signal authority.\n */\nconst psGroupSnapshot = (pgid: number): ProcessCleanup.Snapshot | undefined => {\n const result = spawnSync(defaultPsExecutable, [\"-A\", \"-o\", \"pid=,pgid=,stat=,lstart=\"], {\n encoding: \"utf8\",\n timeout: cleanupProbeTimeoutMs,\n killSignal: \"SIGKILL\",\n env: { LC_ALL: \"C\", TZ: \"UTC\", PATH: \"/usr/bin:/bin\" }\n })\n if (result.error !== undefined || result.signal !== null || result.status !== 0) return undefined\n let own: number | undefined\n const members: Array<ProcessCleanup.Member> = []\n for (const line of result.stdout.trim().split(\"\\n\")) {\n const match = /^\\s*([0-9]+)\\s+([0-9]+)\\s+(\\S+)\\s+(.+)$/.exec(line)\n if (match === null) return undefined\n const pid = Number(match[1])\n const group = Number(match[2])\n if (!Number.isSafeInteger(pid) || !Number.isSafeInteger(group)) return undefined\n if (pid === process.pid) own = group\n if (group !== pgid) continue\n const started = Date.parse(`${match[4]!} UTC`)\n if (Number.isNaN(started)) return undefined\n members.push({ pid, startedAtMs: started, zombie: match[3]!.startsWith(\"Z\") })\n }\n return own === undefined || own <= 0 ? undefined : { ownGroup: own, members }\n}\n\n/**\n * The observation the supervisor polls, from the source that platform has.\n *\n * Linux publishes the whole process table as files under `/proc`, which is\n * where `ps` itself reads it, so the kernel is asked directly. That needs no\n * `procps` in the image, cannot be answered by a planted binary, and costs no\n * fork on a poll that runs every 10ms. A missing `/bin/ps` is not a\n * hypothetical: `procps` is not in `debian:bookworm-slim`, and on Cloud CI run\n * 11727 every spawned target in every task finished its work and then failed\n * cleanup verification with `Unknown: ChildProcess.kill`, because this\n * observation could not be made.\n *\n * Every other POSIX host keeps the `ps` reading, which is the only portable\n * spelling of this question off Linux.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const groupSnapshotFor = (platform: string): (pgid: number) => ProcessCleanup.Snapshot | undefined =>\n platform === \"linux\" ? ProcSnapshot.snapshot(ProcSnapshot.defaultProcRoot) : psGroupSnapshot\n\n/**\n * Whether the kernel reports no process at all in a group.\n *\n * Signal 0 delivers nothing; it only asks the kernel whether the group has a\n * member. `ESRCH` is the one answer that proves the group empty, with no fork\n * and no process table to parse, so it still answers on a loaded host where a\n * `ps` snapshot cannot be taken. A live member, a zombie (macOS answers\n * `EPERM`, Linux succeeds), another user's member or any other error answers\n * `false`, which leaves the question to the snapshot.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const groupVacant = (pgid: number): boolean => {\n // kill(0) and kill(-1) address the caller's group and every process.\n if (!Number.isSafeInteger(pgid) || pgid <= 1) return false\n try {\n process.kill(-pgid, 0)\n return false\n } catch (cause) {\n return errorCode(cause) === \"ESRCH\"\n }\n}\n\n/**\n * Live cleanup for Node and Bun contained spawners.\n *\n * A trusted supervisor remains the group owner after the target exits. It\n * enforces cleanup on target completion, parent loss and explicit release; the\n * target starts only after its owner is durably recorded. Host observations\n * never signal a pid: they only shorten the owner's deadline or verify an empty\n * group before retiring its record. Failed verification retains the record.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const processLifecycle = ProcessCleanup.lifecycle({\n platform: process.platform,\n snapshot: groupSnapshotFor(process.platform),\n vacant: groupVacant\n})\n\n/**\n * The default grace period for a contained host spawner.\n *\n * The platform is always the running platform; callers cannot describe a\n * Windows ledger identity for a process that actually leads a POSIX group.\n * @category models\n * @since 1.0.0\n */\nexport type SpawnerOptions = Omit<ContainedSpawner.Options, \"platform\">\n\n/**\n * Provides a ledger-backed spawner with this platform's prepared cleanup.\n *\n * Native pipe listeners live until their descriptors close, including writes\n * interrupted by owner loss. The kernel expands pipelines before this private\n * standard-command adapter is called. Windows owners join a native job before\n * activation; POSIX owners enforce their process-group cleanup deadline.\n *\n * The caller supplies the durable or in-memory ledger. This layer records new\n * owners; {@link layer} separately reaps\n * records inherited from a previous host incarnation.\n * @category layers\n * @since 1.0.0\n */\nexport const layerSpawner = (\n options?: SpawnerOptions\n): Layer.Layer<ChildProcessSpawner, never, ProcessLedger.ProcessLedger> => {\n const contained = ContainedSpawner.layer({ graceMs: options?.graceMs, platform: process.platform }, processLifecycle)\n const standard = makeSpawner((command) =>\n PipedProcess.spawn(command as ChildProcess.StandardCommand, undefined, true)\n )\n return Layer.provide(contained, Layer.succeed(ChildProcessSpawner)(standard))\n}\n\n/** A whole number and nothing else, which is all a `pgid` column may be. */\nconst decimal = /^[0-9]+$/\n\n/**\n * One `ps` column for one pid.\n *\n * `printed` is an answer; every other tag is the absence of one. A nonzero exit\n * with no error is `ps` saying the pid does not exist, which is evidence; a\n * spawn that failed, a signal, or a timeout is not.\n */\nconst ps = (\n executable: string,\n column: string,\n pid: number\n): { readonly _tag: \"printed\"; readonly text: string } | StartTime => {\n const result = spawnSync(executable, [\"-o\", `${column}=`, \"-p\", String(pid)], {\n encoding: \"utf8\",\n timeout: psTimeoutMs,\n // `SIGKILL` rather than the default `SIGTERM`, because this call blocks the\n // event loop while a host is being built: `spawnSync` keeps waiting past\n // the deadline for a process that ignores the signal, so a `TERM` the probe\n // can decline is not a deadline at all.\n killSignal: \"SIGKILL\",\n // An empty-ish environment for the same reason the helper in\n // `AtomicFileSystem` gets one: nothing the caller exported may change what\n // this answers. Pin both the column's spelling and its timezone; the parser\n // supplies UTC explicitly because `lstart` does not print a zone.\n env: { LC_ALL: \"C\", TZ: \"UTC\", PATH: \"/usr/bin:/bin\" }\n })\n if (result.error !== undefined || result.signal !== null || result.status === null) {\n return { _tag: \"unavailable\" }\n }\n if (result.status !== 0) return { _tag: \"gone\" }\n const printed = result.stdout.trim()\n return printed === \"\" ? { _tag: \"unavailable\" } : { _tag: \"printed\", text: printed }\n}\n\n/**\n * This process's own process group.\n *\n * Node exposes no `getpgrp`, so the answer comes from `ps`, which both BSD and\n * procps spell `pgid`. A host that cannot read it answers `null` and loses one\n * guard, which is why the pid comparison below is kept as well.\n *\n * The text is required to be one run of decimal digits naming a real group,\n * rather than run through `Number.parseInt`, which reads a PREFIX: `\"12 34\"`,\n * `\"12abc\"`, and `\"0x10\"` all become a number that is not this host's group. A\n * wrong number is worse here than no number at all. It silently passes the\n * own-group comparison in {@link refuse}, and because it is not `null` it also\n * suppresses the `own-group-unknown` refusal that exists to say the comparison\n * could not be made, so the guard neither runs nor reports that it did not run\n * and a `SIGKILL` goes out on it. Anything else answers `null`, which refuses.\n */\nconst ownGroup = (executable: string) => (): number | null => {\n const answer = ps(executable, \"pgid\", process.pid)\n if (answer._tag !== \"printed\") return null\n if (!decimal.test(answer.text)) return null\n const parsed = Number(answer.text)\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null\n}\n\n/**\n * How a {@link System} asks the operating system its questions, and whose\n * identity it must never signal.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface SystemOptions {\n /**\n * The absolute `ps` the identity probe runs. Default\n * {@link defaultPsExecutable}. It is configuration for a host that installs\n * `ps` somewhere else, and the seam the probe's own parsing is driven\n * through; it is never a `PATH` lookup. Windows has no such probe and\n * ignores it.\n */\n readonly psExecutable?: string | undefined\n /** Absolute native helper for Windows process identity queries; never a PATH lookup. */\n readonly processExecutable?: string | undefined\n /**\n * The pid this system must never signal, nor signal the group of. Default\n * `process.pid`. {@link Options.ownerPid} is the same identity at the sweep\n * level; this one is what {@link System.killTree} enforces on its own,\n * because that function is exported and a caller can reach it without going\n * through {@link reap}.\n */\n readonly ownerPid?: number | undefined\n}\n\n/**\n * Whether a durable record names a POSIX process group this host may signal.\n *\n * `process.kill(-pgid, ...)` turns the stored number into a target directly, so\n * every value that is not a real foreign group has to be refused before it gets\n * there: `0` signals THIS process's group, a negative value stops naming a\n * group at all and names a single pid, and a `pgid` that differs from `pid`\n * describes a child that does not lead the group it claims \u2014\n * `ContainedSpawner.groupOf` writes `pgid === pid` for every detached POSIX\n * child, so anything else is a record no writer in this repository produced.\n */\nconst refusePosixTarget = (record: ProcessLedger.ProcessRecord): Refusal | undefined => {\n if (record.pgid === null) return \"no-group\"\n if (!Number.isSafeInteger(record.pid) || record.pid <= 1) return \"invalid-record\"\n if (!Number.isSafeInteger(record.pgid) || record.pgid <= 1) return \"invalid-record\"\n return record.pgid === record.pid ? undefined : \"invalid-record\"\n}\n\n/**\n * Whether a durable record names a Windows process tree this host may signal.\n *\n * Windows has no process groups, so `ContainedSpawner.groupOf` records `null`\n * there and `taskkill /T` walks the tree from the pid instead. A win32 record\n * carrying a number is therefore a record some other platform wrote.\n */\nconst refuseWindowsTarget = (record: ProcessLedger.ProcessRecord): Refusal | undefined => {\n if (record.pgid !== null) return \"invalid-record\"\n return Number.isSafeInteger(record.pid) && record.pid > 1 && record.pid <= 0xffff_ffff ? undefined : \"invalid-record\"\n}\n\n/**\n * Reaping on a POSIX host, with the identity probe configured.\n *\n * The group is killed outright rather than asked politely first. `SIGTERM`\n * buys a graceful shutdown only when someone is left to wait for it, and the\n * incarnation that owned these processes is by definition gone.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const posixSystemWith = (options?: SystemOptions): System =>\n posixSystemFrom({\n startedAtMs: startedAtMs(options?.psExecutable ?? defaultPsExecutable),\n ownGroup: ownGroup(options?.psExecutable ?? defaultPsExecutable),\n bootedAtMs\n }, options?.ownerPid ?? process.pid)\n\n/**\n * Where a Linux {@link System} reads the process table, and whose identity it\n * must never signal.\n *\n * @category models\n * @since 1.0.0-rc.1\n */\nexport interface ProcSystemOptions {\n /** The `/proc` mount the probes read. Default `/proc`. */\n readonly procRoot?: string | undefined\n /** The pid this system must never signal, nor signal the group of. Default `process.pid`. */\n readonly ownerPid?: number | undefined\n}\n\n/**\n * Reaping on Linux, with every identity question read from `/proc` rather\n * than from `ps`.\n *\n * `procps` is not in `debian:bookworm-slim` (Cloud CI run 11727), and a\n * `ps`-based reaper on such a host can read neither its own group nor any\n * start time, so it refuses every orphan as `own-group-unknown` on every\n * restart. The live-cleanup observation ({@link groupSnapshotFor}) already\n * reads `/proc` for the same reason; this is the crash-recovery half.\n *\n * A pid with no `/proc/<pid>/stat` is `gone` only when the kernel also says so,\n * because a `hidepid` mount hides another user's live process the same way.\n *\n * @category constructors\n * @since 1.0.0-rc.1\n */\nexport const procSystemWith = (options?: ProcSystemOptions): System => {\n const root = options?.procRoot ?? ProcSnapshot.defaultProcRoot\n const readStart = ProcSnapshot.startedAtMs(root)\n return posixSystemFrom({\n startedAtMs: (pid) => {\n const answer = readStart(pid)\n return answer._tag === \"gone\" && liveness(pid) !== \"dead\" ? { _tag: \"unavailable\" } : answer\n },\n ownGroup: ProcSnapshot.ownGroup(root),\n bootedAtMs: () => ProcSnapshot.bootMs(root) ?? bootedAtMs()\n }, options?.ownerPid ?? process.pid)\n}\n\n/** The POSIX kill path over whichever probes the platform answers with. */\nconst posixSystemFrom = (\n probes: Pick<System, \"startedAtMs\" | \"ownGroup\" | \"bootedAtMs\">,\n ownerPid: number\n): System => {\n const readOwnGroup = probes.ownGroup\n return {\n isAlive: liveness,\n startedAtMs: probes.startedAtMs,\n ownGroup: readOwnGroup,\n bootedAtMs: probes.bootedAtMs,\n refuseTarget: refusePosixTarget,\n killTree: (record) => {\n // Repeated here and not only in `refuse`: this function is exported, and\n // the negation below is the one place a stored number becomes a signal.\n if (refusePosixTarget(record) !== undefined) return \"failed\"\n // The host's own identity, repeated for the same reason. `refuse` holds\n // one `ownGroup` reading for the whole sweep; a caller that reaches this\n // function directly has made no such reading, and `process.kill(-pgid)`\n // does not care which way the number arrived. A group that cannot be\n // read is a comparison that did not happen, so nothing is signalled.\n if (record.pid === ownerPid || record.pgid === ownerPid) return \"failed\"\n const group = readOwnGroup()\n if (group === null || record.pid === group || record.pgid === group) return \"failed\"\n try {\n process.kill(-(record.pgid as number), \"SIGKILL\")\n return \"signalled\"\n } catch (cause) {\n // The group settled between the liveness check and the signal: the end\n // state is the one that was wanted. Anything else is a kill that did NOT\n // happen, and saying so is what gets the record tried again.\n return errorCode(cause) === \"ESRCH\" ? \"already-gone\" : \"failed\"\n }\n }\n }\n}\n\n/**\n * Reaping on a POSIX host: one `SIGKILL` to the abandoned process group, with\n * the identity probe at {@link defaultPsExecutable}.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const posixSystem: System = posixSystemWith()\n\n/**\n * Reaping on Windows, with the identity the sweep must never signal\n * configured.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const windowsSystemWith = (options?: SystemOptions): System => {\n const ownerPid = options?.ownerPid ?? process.pid\n const startedAtMs = (pid: number): StartTime => {\n if (!Number.isSafeInteger(pid) || pid <= 1 || pid > 0xffff_ffff) return { _tag: \"unavailable\" }\n try {\n const configured = options?.processExecutable ?? process.env.SMITHERS_WORKSPACE_JJ_EXPORT_BINARY\n const executable = configured === undefined\n ? resolveDefaultExecutable(packageRoot, undefined)\n : usableExecutable(configured, undefined)\n const result = spawnSync(executable, [\"--process-info\", String(pid)], {\n cwd: parse(process.execPath).root,\n env: {},\n encoding: \"utf8\",\n timeout: psTimeoutMs,\n killSignal: \"SIGKILL\",\n maxBuffer: 4096\n })\n if (result.error !== undefined || result.signal !== null || result.status !== 0) return { _tag: \"unavailable\" }\n const value = JSON.parse(result.stdout) as { readonly status?: unknown; readonly startedAtMs?: unknown } | null\n if (value?.status === \"gone\") return { _tag: \"gone\" }\n return value?.status === \"started\" && typeof value.startedAtMs === \"number\" &&\n Number.isSafeInteger(value.startedAtMs) && value.startedAtMs >= 0\n ? { _tag: \"started\", startedAtMs: value.startedAtMs }\n : { _tag: \"unavailable\" }\n } catch {\n return { _tag: \"unavailable\" }\n }\n }\n return {\n isAlive: liveness,\n startedAtMs,\n ownGroup: () => null,\n bootedAtMs,\n refuseTarget: (record) => record.pid === ownerPid ? \"own-group\" : refuseWindowsTarget(record),\n killTree: (record) => {\n if (refuseWindowsTarget(record) !== undefined) return \"failed\"\n // `taskkill /T` walks the tree DOWN from the pid it is given, so a record\n // naming this host takes the host and everything it started with it.\n if (record.pid === ownerPid) return \"failed\"\n const result = spawnSync(taskkillExecutable(), [\"/pid\", String(record.pid), \"/T\", \"/F\"], {\n // An inert cwd and only the variable Windows system binaries need.\n cwd: parse(process.execPath).root,\n env: { SystemRoot: windowsRoot() },\n stdio: \"ignore\"\n })\n if (result.error !== undefined) return \"failed\"\n // 128 is `taskkill`'s \"no such process\", which is the same end state a\n // successful kill produces.\n if (result.status === 0) return \"signalled\"\n return result.status === 128 ? \"already-gone\" : \"failed\"\n }\n }\n}\n\n/**\n * Reaping on Windows, which has no process groups: `taskkill /T /F` by pid.\n *\n * **Windows is outside the 1.0.0-rc.0 support contract**\n * (`docs/pages/installation.md`). Full Windows process containment remains\n * under verification. It is kept \u2014 and reachable through {@link reap} rather than only\n * by calling it directly \u2014 because a reaper that silently retired every win32\n * record would be the \"excluded feature that appears to work partially\" the\n * release policy forbids. Treat it as unsupported best-effort.\n *\n * The packaged native helper reads creation time from a pinned process handle.\n * A missing helper, malformed reply, or denied query keeps the durable record\n * for retry. Windows has no process groups, so own-group refusal uses the pid.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const windowsSystem: System = windowsSystemWith()\n\n/**\n * Reaping on Linux from the kernel's own `/proc`.\n *\n * @category constructors\n * @since 1.0.0-rc.1\n */\nexport const procSystem: System = procSystemWith()\n\n/**\n * Selects the reaping implementation a platform needs.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const systemFor = (platform: string): System =>\n platform === \"win32\" ? windowsSystem : platform === \"linux\" ? procSystem : posixSystem\n\n/**\n * How far apart a recorded start time and the operating system's may be and\n * still describe the same process.\n *\n * `lstart` has one-second granularity and the ledger writes its record just\n * after the spawn returns, so the two never agree exactly. Two seconds covers\n * the gap; a pid the operating system handed to somebody else is minutes or\n * hours away from it, not milliseconds.\n *\n * @category models\n * @since 0.1.0\n */\nexport const identityToleranceMs = 2000\n\n/**\n * Reaper configuration.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Options {\n /** The pid this host must never signal a group for. Default `process.pid`. */\n readonly ownerPid?: number | undefined\n /** The operating-system seam. Default {@link systemFor} of `process.platform`. */\n readonly system?: System | undefined\n}\n\n/**\n * Why a record was not signalled.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Refusal =\n /** The incarnation that started it is still running and will contain it. */\n | \"owner-alive\"\n /** It shared its owner's process group, so there is no group to signal. */\n | \"no-group\"\n /** It named this host's own process group or pid. */\n | \"own-group\"\n /** This host could not read its OWN process group, so it cannot rule that out. */\n | \"own-group-unknown\"\n /** Its numbers do not name anything this platform may signal. */\n | \"invalid-record\"\n /** It was written before this machine booted, so the pid space is not the same. */\n | \"pre-boot\"\n /** The pid it names does not exist, so there is no identity to confirm. */\n | \"process-gone\"\n /** The pid exists but did not start when the record says it did. */\n | \"identity-mismatch\"\n /** This host could not ask when the pid started, so it must not signal it. */\n | \"identity-unverified\"\n /** The signal was refused, so the record stays for the next incarnation. */\n | \"kill-failed\"\n\n/**\n * The refusals that leave a record inherited instead of retiring it.\n *\n * A refusal is final when the operating system has moved on from the number:\n * saying so in the journal is what stops every later incarnation re-examining\n * it. These three are not final. `owner-alive` means the incarnation that owns\n * the record will contain its own children; `identity-unverified` and\n * `own-group-unknown` mean this host could not ask a question at all \u2014 a host\n * that CAN ask has to get the chance.\n */\nconst retained: ReadonlySet<Refusal> = new Set<Refusal>([\n \"owner-alive\",\n \"identity-unverified\",\n \"own-group-unknown\"\n])\n\n/**\n * One record, and what the reaper decided about it.\n *\n * The two outcomes are a union rather than one shape with an optional field so\n * that `killed` narrows the reason: after `if (!entry.killed)` the\n * {@link Refusal} is there to read, and a killed record cannot be built\n * carrying a reason it never had. The runtime objects are unchanged \u2014 a killed\n * entry simply omits `refusal`.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Reaped =\n | {\n readonly record: ProcessLedger.ProcessRecord\n readonly killed: true\n /** Never present when `killed` is `true`. */\n readonly refusal?: never\n }\n | {\n readonly record: ProcessLedger.ProcessRecord\n readonly killed: false\n /** Present exactly when `killed` is `false`. */\n readonly refusal: Refusal\n }\n\n/** The identity half of the decision, split out because it reads as a list. */\nconst refuseOnIdentity = (\n system: System,\n record: ProcessLedger.ProcessRecord,\n bootMs: number\n): Refusal | undefined => {\n // A record older than this boot names a pid from a pid space that no longer\n // exists. The tolerance points BACKWARDS from the boot instant on purpose:\n // `uptime` is second-granular, so a record written moments after boot could\n // otherwise be read as predating it, and refusing to kill is the safe way to\n // be wrong.\n //\n // The consequence is a narrow, deliberate leak, and it is a leak rather than\n // a retry: `reap` retires a `pre-boot` refusal through `ledger.skipped`, so a\n // group spawned within `identityToleranceMs` of the computed boot instant is\n // never reaped by any later incarnation either. That is the price of refusing\n // to kill on an instant `uptime` can only place to the nearest second.\n if (record.startedAtMs < bootMs + identityToleranceMs) return \"pre-boot\"\n const started = system.startedAtMs(record.pid)\n // A platform with no start-time column at all has already been checked\n // against this boot, which is the whole of its documented weaker guarantee.\n if (started._tag === \"unsupported\") return undefined\n if (started._tag === \"gone\") return \"process-gone\"\n if (started._tag === \"unavailable\") return \"identity-unverified\"\n return Math.abs(started.startedAtMs - record.startedAtMs) <= identityToleranceMs\n ? undefined\n : \"identity-mismatch\"\n}\n\n/** Everything the reaper checks before it is willing to signal a record. */\nconst refuse = (\n system: System,\n record: ProcessLedger.ProcessRecord,\n ownerPid: number,\n ownGroup: number | null,\n bootMs: number\n): Refusal | undefined => {\n // The host's own identity comes first, before the platform is consulted at\n // all: a record naming this process is refused on every platform, whatever\n // shape its numbers have.\n if (record.pgid === ownerPid || record.pid === ownerPid) return \"own-group\"\n if (ownGroup !== null && (record.pgid === ownGroup || record.pid === ownGroup)) return \"own-group\"\n const target = system.refuseTarget(record)\n if (target !== undefined) return target\n // A record naming a group can only be cleared of naming THIS host's group by\n // reading that group. When the probe cannot answer, the comparison above was\n // not made rather than made and passed, so the record is kept instead of\n // signalled on a guard that did not run.\n if (record.pgid !== null && ownGroup === null) return \"own-group-unknown\"\n if (system.isAlive(record.ownerPid) !== \"dead\") return \"owner-alive\"\n return refuseOnIdentity(system, record, bootMs)\n}\n\n/**\n * Kills the process groups a previous incarnation of this host abandoned.\n *\n * Returns one entry per inherited record so a caller can log what it decided;\n * `killed: false` carries the {@link Refusal} that produced it. Fails with\n * {@link ProcessLedger.ProcessLedgerReplayError} when the inherited history\n * cannot be read to its end: nothing is signalled or retired, so the next\n * sweep inherits every record again.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const reap = (\n options?: Options\n): Effect.Effect<ReadonlyArray<Reaped>, ProcessLedger.ProcessLedgerReplayError, ProcessLedger.ProcessLedger> =>\n Effect.gen(function*() {\n const ledger = yield* ProcessLedger.ProcessLedger\n const system = options?.system ?? systemFor(process.platform)\n const ownerPid = options?.ownerPid ?? process.pid\n const ownGroup = system.ownGroup()\n const bootMs = system.bootedAtMs()\n const orphans = yield* ledger.orphans\n const decided: Array<Reaped> = []\n for (const record of orphans) {\n const refusal = refuse(system, record, ownerPid, ownGroup, bootMs)\n if (refusal === undefined) {\n const outcome = system.killTree(record)\n if (outcome === \"failed\") {\n // Nothing was killed, so nothing is retired: leaving the record\n // inherited is what makes the next incarnation try again.\n decided.push({ record, killed: false, refusal: \"kill-failed\" })\n continue\n }\n yield* retire(ledger.reaped(record), record.pid, \"reaped\")\n decided.push({ record, killed: true })\n continue\n }\n // A retained refusal is not this host's to retire; every other refusal is\n // final and says so in the journal.\n if (!retained.has(refusal)) {\n yield* retire(ledger.skipped(record, refusal), record.pid, \"reap-skipped\")\n }\n decided.push({ record, killed: false, refusal })\n }\n return decided\n })\n\n/**\n * Writes one ledger retirement, logging rather than failing the sweep.\n *\n * A retirement that does not commit is not a lost kill: the process is already\n * dead and the record stays inherited, so the next incarnation reads it, finds\n * the pid gone, and retires it then. Failing the whole sweep over it would\n * take the host down for a bookkeeping write.\n */\nconst retire = (\n write: Effect.Effect<void, unknown>,\n pid: number,\n what: string\n): Effect.Effect<void> =>\n write.pipe(\n Effect.catch((error) => Effect.logWarning(`process reaper could not journal ${what} for ${pid}`, error))\n )\n\n/**\n * Refusals that leave a dead incarnation's process running with no live owner\n * to contain it: the host could not ask, or its signal failed.\n */\nconst unreaped: ReadonlySet<Refusal> = new Set<Refusal>([\"identity-unverified\", \"own-group-unknown\", \"kill-failed\"])\n\n/**\n * Counter over inherited records the reaper did not kill, with a `refusal`\n * attribute naming why.\n *\n * @category metrics\n * @since 1.0.0-rc.1\n */\nexport const refusals = Metric.counter(\"flows_process_reaper_refusals\", {\n description: \"Inherited process records the reaper did not kill, by refusal\"\n})\n\n/**\n * Reports one sweep: a counter update per refusal and one summary line. The\n * line is a warning when a record stays inherited (a retained refusal) or a\n * signal failed, because either leaves a crashed incarnation's processes\n * running with nothing else to say so.\n *\n * @category layers\n * @since 1.0.0-rc.1\n */\nexport const report = (decided: ReadonlyArray<Reaped>): Effect.Effect<void> =>\n Effect.gen(function*() {\n if (decided.length === 0) return\n const counts: Partial<Record<Refusal, number>> = {}\n let killed = 0\n for (const entry of decided) {\n if (entry.killed) {\n killed += 1\n continue\n }\n counts[entry.refusal] = (counts[entry.refusal] ?? 0) + 1\n }\n for (const [refusal, count] of Object.entries(counts)) {\n yield* Metric.update(Metric.withAttributes(refusals, { refusal }), count)\n }\n const annotations = { killed, ...counts }\n const alarming = decided.some((entry) => !entry.killed && unreaped.has(entry.refusal))\n const line = \"process reaper swept inherited records\"\n yield* (alarming ? Effect.logWarning(line) : Effect.logInfo(line)).pipe(Effect.annotateLogs(annotations))\n })\n\n/**\n * Runs {@link reap} once while the layer is built.\n *\n * Compose it into a host layer so standing a host up is also what cleans up\n * after the incarnation that crashed. A history that cannot be read refuses\n * the sweep with an error log and leaves every record inherited; the host\n * still starts, because the processes it will spawn are recorded and\n * contained regardless. Every decision is {@link report}ed.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer = (options?: Options): Layer.Layer<never, never, ProcessLedger.ProcessLedger> =>\n Layer.effectDiscard(\n reap(options).pipe(\n Effect.flatMap(report),\n Effect.catchTag(\n \"@smthrs/kernel/ProcessLedgerReplayError\",\n (error) => Effect.logError(\"process reaper refused to sweep: the ledger history is incomplete\", error)\n )\n )\n )\n"],
4
+ "sourcesContent": ["/**\n * Reaping the processes a dead host incarnation abandoned.\n *\n * Scope closure cleans up work a live host still owns. On POSIX, a prepared\n * supervisor also receives private-channel EOF when its host disappears and\n * independently requests group cleanup. The durable ledger remains the fallback\n * when that cleanup is incomplete or cannot be verified; the next incarnation\n * uses this module to examine the recorded process identities before acting.\n *\n * The rules are narrow on purpose, because the input is a durable record that\n * outlived the process that wrote it and a pid is reused. A record is signalled\n * only when ALL of these hold:\n *\n * - it belongs to a DIFFERENT incarnation of the SAME `hostId`\n * ({@link ProcessLedger.Service.orphans} already filters that);\n * - its owner is gone. A pid that exists but belongs to another user counts as\n * alive, exactly as {@link HostLiveness} counts it, so only `ESRCH` reads as\n * dead and every other answer keeps the record;\n * - it names a target the platform can actually signal. On POSIX that is a\n * process group the child leads itself, so `pgid` has to be a safe integer\n * above 1 and equal to `pid`; on Windows it is a pid, so `pgid` has to be\n * absent. The check is made against the DURABLE record rather than trusted\n * from it: `process.kill(-0, \"SIGKILL\")` signals this host's own process\n * group, and `@smthrs/kernel`'s ledger schema admits zero;\n * - that group is not THIS process's group, and its pid is not this process's\n * pid. The host's real process group is read from the operating system,\n * because a stored number can claim anything and the shell that started this\n * host shares its group. A host that cannot read its own group keeps the\n * record rather than signalling it on a guard that did not run;\n * - the number still names the process the record describes. A record written\n * before this machine booted names a pid from a pid space that no longer\n * exists, and within one boot the recorded start time has to match the start\n * time the operating system reports for that pid. A host that cannot ASK the\n * question answers {@link StartTime} `unavailable`, which refuses the kill\n * and keeps the record; no evidence never authorizes a signal.\n *\n * The outcome of the signal is part of the record. A kill that succeeds, or\n * that reports the group is already gone, retires the record through\n * {@link ProcessLedger.Service.reaped}; a kill that FAILS for any other reason\n * leaves the record inherited so the next incarnation tries again. A record\n * refused on grounds the operating system will not revisit \u2014 a number no\n * platform can signal, a pid that is gone, a start time that does not match \u2014 is\n * retired through {@link ProcessLedger.Service.skipped}, which says in the\n * journal that nothing was signalled and stops every later incarnation\n * re-examining a number the operating system has moved on from. The three\n * refusals a later incarnation CAN answer differently \u2014 `owner-alive`,\n * `identity-unverified`, and `own-group-unknown` \u2014 retire nothing.\n *\n * @since 0.1.0\n */\n\nimport * as ContainedSpawner from \"@smthrs/kernel/ContainedSpawner\"\nimport * as ProcessLedger from \"@smthrs/kernel/ProcessLedger\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as Metric from \"effect/Metric\"\nimport type * as ChildProcess from \"effect/process/ChildProcess\"\nimport { ChildProcessSpawner, make as makeSpawner } from \"effect/process/ChildProcessSpawner\"\nimport { spawnSync } from \"node:child_process\"\nimport { uptime } from \"node:os\"\nimport { parse, win32 } from \"node:path\"\nimport { packageRoot, resolveDefaultExecutable } from \"./internal/AtomicFileSystemExecutable.ts\"\nimport { usableExecutable } from \"./internal/AtomicFileSystemTransport.ts\"\nimport * as PipedProcess from \"./internal/PipedProcess.ts\"\nimport * as ProcessCleanup from \"./internal/ProcessCleanup.ts\"\nimport * as ProcSnapshot from \"./internal/ProcSnapshot.ts\"\n\n/**\n * What the operating system can say about a pid.\n *\n * `unknown` is not `dead`. A pid this host may not signal answers `EPERM`, and\n * a reaper that read that as \"gone\" would kill another user's children on the\n * strength of a record it cannot verify.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Liveness = \"alive\" | \"dead\" | \"unknown\"\n\n/**\n * What happened when a process group was signalled.\n *\n * @category models\n * @since 0.1.0\n */\nexport type KillOutcome =\n /** The signal was delivered. */\n | \"signalled\"\n /** There was nothing left to signal, which is the same end state. */\n | \"already-gone\"\n /** The signal was refused, and the record must be tried again later. */\n | \"failed\"\n\n/**\n * What the operating system can say about when a pid started.\n *\n * The three negative answers are deliberately distinct, because they authorize\n * three different decisions. `gone` is evidence \u2014 the pid does not exist, so\n * the record describes nothing and is retired unsignalled. `unavailable` is the\n * ABSENCE of evidence: `ps` is missing, unanswerable, or printed something no\n * clock can read, so the record is kept for an incarnation that can ask.\n * `unsupported` is a platform that has no such column at all, which falls back\n * to the boot-time comparison alone.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type StartTime =\n /** The operating system reports this start time, in epoch milliseconds. */\n | { readonly _tag: \"started\"; readonly startedAtMs: number }\n /** The pid names no process. */\n | { readonly _tag: \"gone\" }\n /** The question could not be asked on this host. */\n | { readonly _tag: \"unavailable\" }\n /** The platform has no start-time column at all. */\n | { readonly _tag: \"unsupported\" }\n\n/**\n * The operating-system operations reaping needs.\n *\n * It is a seam, not an abstraction: `flows` ships exactly the two\n * implementations below and a test drives the Windows one on a POSIX host,\n * which is the only way that path is ever exercised in this repository.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface System {\n /** Whether a pid names a process that still exists. */\n readonly isAlive: (pid: number) => Liveness\n /** When the process behind a pid started. */\n readonly startedAtMs: (pid: number) => StartTime\n /** The process group THIS process belongs to, or `null` where there are none. */\n readonly ownGroup: () => number | null\n /** When this machine booted, in epoch milliseconds. */\n readonly bootedAtMs: () => number\n /**\n * Whether the numbers in a record name something this platform may signal,\n * checked before any other guard consults them.\n */\n readonly refuseTarget: (record: ProcessLedger.ProcessRecord) => Refusal | undefined\n /** Ends an abandoned process and everything below it. */\n readonly killTree: (record: ProcessLedger.ProcessRecord) => KillOutcome\n}\n\nconst errorCode = (cause: unknown): string | undefined => (cause as { readonly code?: string } | null)?.code\n\nconst liveness = (pid: number): Liveness => {\n try {\n // Signal 0 delivers nothing; it only asks whether the pid is signalable.\n process.kill(pid, 0)\n return \"alive\"\n } catch (cause) {\n // ESRCH is the only answer that means gone. EPERM means the process\n // exists and belongs to another user; anything else is a question this\n // host could not ask, and both keep the record rather than kill on it.\n return errorCode(cause) === \"ESRCH\" ? \"dead\" : \"unknown\"\n }\n}\n\nconst bootedAtMs = (): number => Date.now() - Math.round(uptime() * 1000)\n\n/**\n * The start time `ps` reports for a pid.\n *\n * `lstart` is the one start-time column both BSD (macOS) and procps (Linux)\n * spell the same way, and it prints an absolute time rather than an elapsed\n * one, so the answer does not drift between reading it and comparing it. It is\n * also a LOCALIZED column, so the probe pins `LC_ALL=C` and `TZ=UTC`.\n * Its text has no zone; parsing explicitly as UTC keeps the identity independent\n * of the host runtime's timezone.\n */\nconst startedAtMs = (executable: string) => (pid: number): StartTime => {\n const answer = ps(executable, \"lstart\", pid)\n if (answer._tag === \"gone\") {\n // A nonzero exit is `ps` saying the pid does not exist, but it is also what\n // a `ps` that rejected the column, or one that is not the `ps` this probe\n // expects, says about a pid that is very much alive. The kernel is asked\n // directly before a live process is retired as gone.\n return liveness(pid) === \"dead\" ? answer : { _tag: \"unavailable\" }\n }\n if (answer._tag !== \"printed\") return answer\n const parsed = Date.parse(`${answer.text} UTC`)\n return Number.isNaN(parsed) ? { _tag: \"unavailable\" } : { _tag: \"started\", startedAtMs: parsed }\n}\n\n/**\n * The absolute path the process probe is run from.\n *\n * Never a `PATH` lookup. The inherited environment belongs to whoever started\n * this host, and a planted `ps` that prints a fabricated start time \u2014 or none\n * at all \u2014 would decide whether a `SIGKILL` is authorized.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultPsExecutable = \"/bin/ps\"\n\n/** How long the probe may take before it counts as an answer nobody gave. */\nconst psTimeoutMs = 5000\n\n/**\n * The Windows directory the host was started with, when it names one.\n */\nconst windowsRoot = (): string => {\n const systemRoot = process.env.SystemRoot\n return systemRoot !== undefined && win32.isAbsolute(systemRoot) ? systemRoot : \"C:\\\\Windows\"\n}\n\n/**\n * The system `taskkill`, by absolute path. Windows resolves a bare name from\n * the current directory before `PATH`, so a flow that wrote `taskkill.exe`\n * into the host's working directory would otherwise run as the host.\n */\nconst taskkillExecutable = (): string => win32.join(windowsRoot(), \"System32\", \"taskkill.exe\")\n\n/** A live-release probe has its own short bound within the shutdown deadline. */\nconst cleanupProbeTimeoutMs = 2000\n\n/**\n * One fixed-path snapshot supplies group membership, start identities and our\n * own group together. Only numeric fields and C-locale lstart are read; command\n * text and the caller's PATH never influence signal authority.\n */\nconst psGroupSnapshot = (pgid: number): ProcessCleanup.Snapshot | undefined => {\n const result = spawnSync(defaultPsExecutable, [\"-A\", \"-o\", \"pid=,pgid=,stat=,lstart=\"], {\n encoding: \"utf8\",\n timeout: cleanupProbeTimeoutMs,\n killSignal: \"SIGKILL\",\n env: { LC_ALL: \"C\", TZ: \"UTC\", PATH: \"/usr/bin:/bin\" }\n })\n if (result.error !== undefined || result.signal !== null || result.status !== 0) return undefined\n let own: number | undefined\n const members: Array<ProcessCleanup.Member> = []\n for (const line of result.stdout.trim().split(\"\\n\")) {\n const match = /^\\s*([0-9]+)\\s+([0-9]+)\\s+(\\S+)\\s+(.+)$/.exec(line)\n if (match === null) return undefined\n const pid = Number(match[1])\n const group = Number(match[2])\n if (!Number.isSafeInteger(pid) || !Number.isSafeInteger(group)) return undefined\n if (pid === process.pid) own = group\n if (group !== pgid) continue\n const started = Date.parse(`${match[4]!} UTC`)\n if (Number.isNaN(started)) return undefined\n members.push({ pid, startedAtMs: started, zombie: match[3]!.startsWith(\"Z\") })\n }\n return own === undefined || own <= 0 ? undefined : { ownGroup: own, members }\n}\n\n/**\n * The observation the supervisor polls, from the source that platform has.\n *\n * Linux publishes the whole process table as files under `/proc`, which is\n * where `ps` itself reads it, so the kernel is asked directly. That needs no\n * `procps` in the image, cannot be answered by a planted binary, and costs no\n * fork on a poll that runs every 10ms. A missing `/bin/ps` is not a\n * hypothetical: `procps` is not in `debian:bookworm-slim`, and on Cloud CI run\n * 11727 every spawned target in every task finished its work and then failed\n * cleanup verification with `Unknown: ChildProcess.kill`, because this\n * observation could not be made.\n *\n * Every other POSIX host keeps the `ps` reading, which is the only portable\n * spelling of this question off Linux.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const groupSnapshotFor = (platform: string): (pgid: number) => ProcessCleanup.Snapshot | undefined =>\n platform === \"linux\" ? ProcSnapshot.snapshot(ProcSnapshot.defaultProcRoot) : psGroupSnapshot\n\n/**\n * Whether the kernel reports no process at all in a group.\n *\n * Signal 0 delivers nothing; it only asks the kernel whether the group has a\n * member. `ESRCH` is the one answer that proves the group empty, with no fork\n * and no process table to parse, so it still answers on a loaded host where a\n * `ps` snapshot cannot be taken. A live member, a zombie (macOS answers\n * `EPERM`, Linux succeeds), another user's member or any other error answers\n * `false`, which leaves the question to the snapshot.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const groupVacant = (pgid: number): boolean => {\n // kill(0) and kill(-1) address the caller's group and every process.\n if (!Number.isSafeInteger(pgid) || pgid <= 1) return false\n try {\n process.kill(-pgid, 0)\n return false\n } catch (cause) {\n return errorCode(cause) === \"ESRCH\"\n }\n}\n\n/**\n * Live cleanup for Node and Bun contained spawners.\n *\n * A trusted supervisor remains the group owner after the target exits. It\n * enforces cleanup on target completion, parent loss and explicit release; the\n * target starts only after its owner is durably recorded. Host observations\n * never signal a pid: they only shorten the owner's deadline or verify an empty\n * group before retiring its record. Failed verification retains the record.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const processLifecycle = ProcessCleanup.lifecycle({\n platform: process.platform,\n snapshot: groupSnapshotFor(process.platform),\n vacant: groupVacant\n})\n\n/**\n * The default grace period for a contained host spawner.\n *\n * The platform is always the running platform; callers cannot describe a\n * Windows ledger identity for a process that actually leads a POSIX group.\n * @category models\n * @since 1.0.0\n */\nexport type SpawnerOptions = Omit<ContainedSpawner.Options, \"platform\">\n\n/**\n * Provides a ledger-backed spawner with this platform's prepared cleanup.\n *\n * Native pipe listeners live until their descriptors close, including writes\n * interrupted by owner loss. The kernel expands pipelines before this private\n * standard-command adapter is called. Windows owners join a native job before\n * activation; POSIX owners enforce their process-group cleanup deadline.\n *\n * The caller supplies the durable or in-memory ledger. This layer records new\n * owners; {@link layer} separately reaps\n * records inherited from a previous host incarnation.\n * @category layers\n * @since 1.0.0\n */\nexport const layerSpawner = (\n options?: SpawnerOptions\n): Layer.Layer<ChildProcessSpawner, never, ProcessLedger.ProcessLedger> => {\n const contained = ContainedSpawner.layer({ graceMs: options?.graceMs, platform: process.platform }, processLifecycle)\n const standard = makeSpawner((command) =>\n PipedProcess.spawn(command as ChildProcess.StandardCommand, undefined, true)\n )\n return Layer.provide(contained, Layer.succeed(ChildProcessSpawner)(standard))\n}\n\n/** A whole number and nothing else, which is all a `pgid` column may be. */\nconst decimal = /^[0-9]+$/\n\n/**\n * One `ps` column for one pid.\n *\n * `printed` is an answer; every other tag is the absence of one. A nonzero exit\n * with no error is `ps` saying the pid does not exist, which is evidence; a\n * spawn that failed, a signal, or a timeout is not.\n */\nconst ps = (\n executable: string,\n column: string,\n pid: number\n): { readonly _tag: \"printed\"; readonly text: string } | StartTime => {\n const result = spawnSync(executable, [\"-o\", `${column}=`, \"-p\", String(pid)], {\n encoding: \"utf8\",\n timeout: psTimeoutMs,\n // `SIGKILL` rather than the default `SIGTERM`, because this call blocks the\n // event loop while a host is being built: `spawnSync` keeps waiting past\n // the deadline for a process that ignores the signal, so a `TERM` the probe\n // can decline is not a deadline at all.\n killSignal: \"SIGKILL\",\n // An empty-ish environment for the same reason the helper in\n // `AtomicFileSystem` gets one: nothing the caller exported may change what\n // this answers. Pin both the column's spelling and its timezone; the parser\n // supplies UTC explicitly because `lstart` does not print a zone.\n env: { LC_ALL: \"C\", TZ: \"UTC\", PATH: \"/usr/bin:/bin\" }\n })\n if (result.error !== undefined || result.signal !== null || result.status === null) {\n return { _tag: \"unavailable\" }\n }\n if (result.status !== 0) return { _tag: \"gone\" }\n const printed = result.stdout.trim()\n return printed === \"\" ? { _tag: \"unavailable\" } : { _tag: \"printed\", text: printed }\n}\n\n/**\n * This process's own process group.\n *\n * Node exposes no `getpgrp`, so the answer comes from `ps`, which both BSD and\n * procps spell `pgid`. A host that cannot read it answers `null` and loses one\n * guard, which is why the pid comparison below is kept as well.\n *\n * The text is required to be one run of decimal digits naming a real group,\n * rather than run through `Number.parseInt`, which reads a PREFIX: `\"12 34\"`,\n * `\"12abc\"`, and `\"0x10\"` all become a number that is not this host's group. A\n * wrong number is worse here than no number at all. It silently passes the\n * own-group comparison in {@link refuse}, and because it is not `null` it also\n * suppresses the `own-group-unknown` refusal that exists to say the comparison\n * could not be made, so the guard neither runs nor reports that it did not run\n * and a `SIGKILL` goes out on it. Anything else answers `null`, which refuses.\n */\nconst ownGroup = (executable: string) => (): number | null => {\n const answer = ps(executable, \"pgid\", process.pid)\n if (answer._tag !== \"printed\") return null\n if (!decimal.test(answer.text)) return null\n const parsed = Number(answer.text)\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null\n}\n\n/**\n * How a {@link System} asks the operating system its questions, and whose\n * identity it must never signal.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface SystemOptions {\n /**\n * The absolute `ps` the identity probe runs. Default\n * {@link defaultPsExecutable}. It is configuration for a host that installs\n * `ps` somewhere else, and the seam the probe's own parsing is driven\n * through; it is never a `PATH` lookup. Windows has no such probe and\n * ignores it.\n */\n readonly psExecutable?: string | undefined\n /** Absolute native helper for Windows process identity queries; never a PATH lookup. */\n readonly processExecutable?: string | undefined\n /**\n * The pid this system must never signal, nor signal the group of. Default\n * `process.pid`. {@link Options.ownerPid} is the same identity at the sweep\n * level; this one is what {@link System.killTree} enforces on its own,\n * because that function is exported and a caller can reach it without going\n * through {@link reap}.\n */\n readonly ownerPid?: number | undefined\n}\n\n/**\n * Whether a durable record names a POSIX process group this host may signal.\n *\n * `process.kill(-pgid, ...)` turns the stored number into a target directly, so\n * every value that is not a real foreign group has to be refused before it gets\n * there: `0` signals THIS process's group, a negative value stops naming a\n * group at all and names a single pid, and a `pgid` that differs from `pid`\n * describes a child that does not lead the group it claims \u2014\n * `ContainedSpawner.groupOf` writes `pgid === pid` for every detached POSIX\n * child, so anything else is a record no writer in this repository produced.\n */\nconst refusePosixTarget = (record: ProcessLedger.ProcessRecord): Refusal | undefined => {\n if (record.pgid === null) return \"no-group\"\n if (!Number.isSafeInteger(record.pid) || record.pid <= 1) return \"invalid-record\"\n if (!Number.isSafeInteger(record.pgid) || record.pgid <= 1) return \"invalid-record\"\n return record.pgid === record.pid ? undefined : \"invalid-record\"\n}\n\n/**\n * Whether a durable record names a Windows process tree this host may signal.\n *\n * Windows has no process groups, so `ContainedSpawner.groupOf` records `null`\n * there and `taskkill /T` walks the tree from the pid instead. A win32 record\n * carrying a number is therefore a record some other platform wrote.\n */\nconst refuseWindowsTarget = (record: ProcessLedger.ProcessRecord): Refusal | undefined => {\n if (record.pgid !== null) return \"invalid-record\"\n return Number.isSafeInteger(record.pid) && record.pid > 1 && record.pid <= 0xffff_ffff ? undefined : \"invalid-record\"\n}\n\n/**\n * Reaping on a POSIX host, with the identity probe configured.\n *\n * The group is killed outright rather than asked politely first. `SIGTERM`\n * buys a graceful shutdown only when someone is left to wait for it, and the\n * incarnation that owned these processes is by definition gone.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const posixSystemWith = (options?: SystemOptions): System =>\n posixSystemFrom({\n startedAtMs: startedAtMs(options?.psExecutable ?? defaultPsExecutable),\n ownGroup: ownGroup(options?.psExecutable ?? defaultPsExecutable),\n bootedAtMs\n }, options?.ownerPid ?? process.pid)\n\n/**\n * Where a Linux {@link System} reads the process table, and whose identity it\n * must never signal.\n *\n * @category models\n * @since 1.0.0-rc.1\n */\nexport interface ProcSystemOptions {\n /** The `/proc` mount the probes read. Default `/proc`. */\n readonly procRoot?: string | undefined\n /** The pid this system must never signal, nor signal the group of. Default `process.pid`. */\n readonly ownerPid?: number | undefined\n}\n\n/**\n * Reaping on Linux, with every identity question read from `/proc` rather\n * than from `ps`.\n *\n * `procps` is not in `debian:bookworm-slim` (Cloud CI run 11727), and a\n * `ps`-based reaper on such a host can read neither its own group nor any\n * start time, so it refuses every orphan as `own-group-unknown` on every\n * restart. The live-cleanup observation ({@link groupSnapshotFor}) already\n * reads `/proc` for the same reason; this is the crash-recovery half.\n *\n * A pid with no `/proc/<pid>/stat` is `gone` only when the kernel also says so,\n * because a `hidepid` mount hides another user's live process the same way.\n *\n * @category constructors\n * @since 1.0.0-rc.1\n */\nexport const procSystemWith = (options?: ProcSystemOptions): System => {\n const root = options?.procRoot ?? ProcSnapshot.defaultProcRoot\n const readStart = ProcSnapshot.startedAtMs(root)\n return posixSystemFrom({\n startedAtMs: (pid) => {\n const answer = readStart(pid)\n return answer._tag === \"gone\" && liveness(pid) !== \"dead\" ? { _tag: \"unavailable\" } : answer\n },\n ownGroup: ProcSnapshot.ownGroup(root),\n bootedAtMs: () => ProcSnapshot.bootMs(root) ?? bootedAtMs()\n }, options?.ownerPid ?? process.pid)\n}\n\n/** The POSIX kill path over whichever probes the platform answers with. */\nconst posixSystemFrom = (\n probes: Pick<System, \"startedAtMs\" | \"ownGroup\" | \"bootedAtMs\">,\n ownerPid: number\n): System => {\n const readOwnGroup = probes.ownGroup\n return {\n isAlive: liveness,\n startedAtMs: probes.startedAtMs,\n ownGroup: readOwnGroup,\n bootedAtMs: probes.bootedAtMs,\n refuseTarget: refusePosixTarget,\n killTree: (record) => {\n // Repeated here and not only in `refuse`: this function is exported, and\n // the negation below is the one place a stored number becomes a signal.\n if (refusePosixTarget(record) !== undefined) return \"failed\"\n // The host's own identity, repeated for the same reason. `refuse` holds\n // one `ownGroup` reading for the whole sweep; a caller that reaches this\n // function directly has made no such reading, and `process.kill(-pgid)`\n // does not care which way the number arrived. A group that cannot be\n // read is a comparison that did not happen, so nothing is signalled.\n if (record.pid === ownerPid || record.pgid === ownerPid) return \"failed\"\n const group = readOwnGroup()\n if (group === null || record.pid === group || record.pgid === group) return \"failed\"\n try {\n process.kill(-(record.pgid as number), \"SIGKILL\")\n return \"signalled\"\n } catch (cause) {\n // The group settled between the liveness check and the signal: the end\n // state is the one that was wanted. Anything else is a kill that did NOT\n // happen, and saying so is what gets the record tried again.\n return errorCode(cause) === \"ESRCH\" ? \"already-gone\" : \"failed\"\n }\n }\n }\n}\n\n/**\n * Reaping on a POSIX host: one `SIGKILL` to the abandoned process group, with\n * the identity probe at {@link defaultPsExecutable}.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const posixSystem: System = posixSystemWith()\n\n/**\n * Reaping on Windows, with the identity the sweep must never signal\n * configured.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const windowsSystemWith = (options?: SystemOptions): System => {\n const ownerPid = options?.ownerPid ?? process.pid\n const startedAtMs = (pid: number): StartTime => {\n if (!Number.isSafeInteger(pid) || pid <= 1 || pid > 0xffff_ffff) return { _tag: \"unavailable\" }\n try {\n const configured = options?.processExecutable ?? process.env.SMITHERS_WORKSPACE_JJ_EXPORT_BINARY\n const executable = configured === undefined\n ? resolveDefaultExecutable(packageRoot, undefined)\n : usableExecutable(configured, undefined)\n const result = spawnSync(executable, [\"--process-info\", String(pid)], {\n cwd: parse(process.execPath).root,\n env: {},\n encoding: \"utf8\",\n timeout: psTimeoutMs,\n killSignal: \"SIGKILL\",\n maxBuffer: 4096\n })\n if (result.error !== undefined || result.signal !== null || result.status !== 0) return { _tag: \"unavailable\" }\n const value = JSON.parse(result.stdout) as { readonly status?: unknown; readonly startedAtMs?: unknown } | null\n if (value?.status === \"gone\") return { _tag: \"gone\" }\n return value?.status === \"started\" && typeof value.startedAtMs === \"number\" &&\n Number.isSafeInteger(value.startedAtMs) && value.startedAtMs >= 0\n ? { _tag: \"started\", startedAtMs: value.startedAtMs }\n : { _tag: \"unavailable\" }\n } catch {\n return { _tag: \"unavailable\" }\n }\n }\n return {\n isAlive: liveness,\n startedAtMs,\n ownGroup: () => null,\n bootedAtMs,\n refuseTarget: (record) => record.pid === ownerPid ? \"own-group\" : refuseWindowsTarget(record),\n killTree: (record) => {\n if (refuseWindowsTarget(record) !== undefined) return \"failed\"\n // `taskkill /T` walks the tree DOWN from the pid it is given, so a record\n // naming this host takes the host and everything it started with it.\n if (record.pid === ownerPid) return \"failed\"\n const result = spawnSync(taskkillExecutable(), [\"/pid\", String(record.pid), \"/T\", \"/F\"], {\n // An inert cwd and only the variable Windows system binaries need.\n cwd: parse(process.execPath).root,\n env: { SystemRoot: windowsRoot() },\n stdio: \"ignore\"\n })\n if (result.error !== undefined) return \"failed\"\n // 128 is `taskkill`'s \"no such process\", which is the same end state a\n // successful kill produces.\n if (result.status === 0) return \"signalled\"\n return result.status === 128 ? \"already-gone\" : \"failed\"\n }\n }\n}\n\n/**\n * Reaping on Windows, which has no process groups: `taskkill /T /F` by pid.\n *\n * **Windows is outside the 1.0.0-rc.0 support contract**\n * (`docs/pages/installation.md`). Full Windows process containment remains\n * under verification. It is kept \u2014 and reachable through {@link reap} rather than only\n * by calling it directly \u2014 because a reaper that silently retired every win32\n * record would be the \"excluded feature that appears to work partially\" the\n * release policy forbids. Treat it as unsupported best-effort.\n *\n * The packaged native helper reads creation time from a pinned process handle.\n * A missing helper, malformed reply, or denied query keeps the durable record\n * for retry. Windows has no process groups, so own-group refusal uses the pid.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const windowsSystem: System = windowsSystemWith()\n\n/**\n * Reaping on Linux from the kernel's own `/proc`.\n *\n * @category constructors\n * @since 1.0.0-rc.1\n */\nexport const procSystem: System = procSystemWith()\n\n/**\n * Selects the reaping implementation a platform needs.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const systemFor = (platform: string): System =>\n platform === \"win32\" ? windowsSystem : platform === \"linux\" ? procSystem : posixSystem\n\n/**\n * How far apart a recorded start time and the operating system's may be and\n * still describe the same process.\n *\n * `lstart` has one-second granularity and the ledger writes its record just\n * after the spawn returns, so the two never agree exactly. Two seconds covers\n * the gap; a pid the operating system handed to somebody else is minutes or\n * hours away from it, not milliseconds.\n *\n * @category models\n * @since 0.1.0\n */\nexport const identityToleranceMs = 2000\n\n/**\n * Reaper configuration.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Options {\n /** The pid this host must never signal a group for. Default `process.pid`. */\n readonly ownerPid?: number | undefined\n /** The operating-system seam. Default {@link systemFor} of `process.platform`. */\n readonly system?: System | undefined\n}\n\n/**\n * Why a record was not signalled.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Refusal =\n /** The incarnation that started it is still running and will contain it. */\n | \"owner-alive\"\n /** It shared its owner's process group, so there is no group to signal. */\n | \"no-group\"\n /** It named this host's own process group or pid. */\n | \"own-group\"\n /** This host could not read its OWN process group, so it cannot rule that out. */\n | \"own-group-unknown\"\n /** Its numbers do not name anything this platform may signal. */\n | \"invalid-record\"\n /** It was written before this machine booted, so the pid space is not the same. */\n | \"pre-boot\"\n /** The pid it names does not exist, so there is no identity to confirm. */\n | \"process-gone\"\n /** The pid exists but did not start when the record says it did. */\n | \"identity-mismatch\"\n /** This host could not ask when the pid started, so it must not signal it. */\n | \"identity-unverified\"\n /** The signal was refused, so the record stays for the next incarnation. */\n | \"kill-failed\"\n\n/**\n * The refusals that leave a record inherited instead of retiring it.\n *\n * A refusal is final when the operating system has moved on from the number:\n * saying so in the journal is what stops every later incarnation re-examining\n * it. These three are not final. `owner-alive` means the incarnation that owns\n * the record will contain its own children; `identity-unverified` and\n * `own-group-unknown` mean this host could not ask a question at all \u2014 a host\n * that CAN ask has to get the chance.\n */\nconst retained: ReadonlySet<Refusal> = new Set<Refusal>([\n \"owner-alive\",\n \"identity-unverified\",\n \"own-group-unknown\"\n])\n\n/**\n * One record, and what the reaper decided about it.\n *\n * The two outcomes are a union rather than one shape with an optional field so\n * that `killed` narrows the reason: after `if (!entry.killed)` the\n * {@link Refusal} is there to read, and a killed record cannot be built\n * carrying a reason it never had. The runtime objects are unchanged \u2014 a killed\n * entry simply omits `refusal`.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Reaped =\n | {\n readonly record: ProcessLedger.ProcessRecord\n readonly killed: true\n /** Never present when `killed` is `true`. */\n readonly refusal?: never\n }\n | {\n readonly record: ProcessLedger.ProcessRecord\n readonly killed: false\n /** Present exactly when `killed` is `false`. */\n readonly refusal: Refusal\n }\n\n/** The identity half of the decision, split out because it reads as a list. */\nconst refuseOnIdentity = (\n system: System,\n record: ProcessLedger.ProcessRecord,\n bootMs: number\n): Refusal | undefined => {\n // A record older than this boot names a pid from a pid space that no longer\n // exists. The tolerance points BACKWARDS from the boot instant on purpose:\n // `uptime` is second-granular, so a record written moments after boot could\n // otherwise be read as predating it, and refusing to kill is the safe way to\n // be wrong.\n //\n // The consequence is a narrow, deliberate leak, and it is a leak rather than\n // a retry: `reap` retires a `pre-boot` refusal through `ledger.skipped`, so a\n // group spawned within `identityToleranceMs` of the computed boot instant is\n // never reaped by any later incarnation either. That is the price of refusing\n // to kill on an instant `uptime` can only place to the nearest second.\n if (record.startedAtMs < bootMs + identityToleranceMs) return \"pre-boot\"\n const started = system.startedAtMs(record.pid)\n // A platform with no start-time column at all has already been checked\n // against this boot, which is the whole of its documented weaker guarantee.\n if (started._tag === \"unsupported\") return undefined\n if (started._tag === \"gone\") return \"process-gone\"\n if (started._tag === \"unavailable\") return \"identity-unverified\"\n return Math.abs(started.startedAtMs - record.startedAtMs) <= identityToleranceMs\n ? undefined\n : \"identity-mismatch\"\n}\n\n/** Everything the reaper checks before it is willing to signal a record. */\nconst refuse = (\n system: System,\n record: ProcessLedger.ProcessRecord,\n ownerPid: number,\n ownGroup: number | null,\n bootMs: number\n): Refusal | undefined => {\n // The host's own identity comes first, before the platform is consulted at\n // all: a record naming this process is refused on every platform, whatever\n // shape its numbers have.\n if (record.pgid === ownerPid || record.pid === ownerPid) return \"own-group\"\n if (ownGroup !== null && (record.pgid === ownGroup || record.pid === ownGroup)) return \"own-group\"\n const target = system.refuseTarget(record)\n if (target !== undefined) return target\n // A record naming a group can only be cleared of naming THIS host's group by\n // reading that group. When the probe cannot answer, the comparison above was\n // not made rather than made and passed, so the record is kept instead of\n // signalled on a guard that did not run.\n if (record.pgid !== null && ownGroup === null) return \"own-group-unknown\"\n if (system.isAlive(record.ownerPid) !== \"dead\") return \"owner-alive\"\n return refuseOnIdentity(system, record, bootMs)\n}\n\n/**\n * Kills the process groups a previous incarnation of this host abandoned.\n *\n * Returns one entry per inherited record so a caller can log what it decided;\n * `killed: false` carries the {@link Refusal} that produced it. Fails with\n * {@link ProcessLedger.ProcessLedgerReplayError} when the inherited history\n * cannot be read to its end: nothing is signalled or retired, so the next\n * sweep inherits every record again.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const reap = (\n options?: Options\n): Effect.Effect<ReadonlyArray<Reaped>, ProcessLedger.ProcessLedgerReplayError, ProcessLedger.ProcessLedger> =>\n Effect.gen(function*() {\n const ledger = yield* ProcessLedger.ProcessLedger\n const system = options?.system ?? systemFor(process.platform)\n const ownerPid = options?.ownerPid ?? process.pid\n const ownGroup = system.ownGroup()\n const bootMs = system.bootedAtMs()\n const orphans = yield* ledger.orphans\n const decided: Array<Reaped> = []\n for (const record of orphans) {\n const refusal = refuse(system, record, ownerPid, ownGroup, bootMs)\n if (refusal === undefined) {\n const outcome = system.killTree(record)\n if (outcome === \"failed\") {\n // Nothing was killed, so nothing is retired: leaving the record\n // inherited is what makes the next incarnation try again.\n decided.push({ record, killed: false, refusal: \"kill-failed\" })\n continue\n }\n yield* retire(ledger.reaped(record), record.pid, \"reaped\")\n decided.push({ record, killed: true })\n continue\n }\n // A retained refusal is not this host's to retire; every other refusal is\n // final and says so in the journal.\n if (!retained.has(refusal)) {\n yield* retire(ledger.skipped(record, refusal), record.pid, \"reap-skipped\")\n }\n decided.push({ record, killed: false, refusal })\n }\n return decided\n })\n\n/**\n * Writes one ledger retirement, logging rather than failing the sweep.\n *\n * A retirement that does not commit is not a lost kill: the process is already\n * dead and the record stays inherited, so the next incarnation reads it, finds\n * the pid gone, and retires it then. Failing the whole sweep over it would\n * take the host down for a bookkeeping write.\n */\nconst retire = (\n write: Effect.Effect<void, unknown>,\n pid: number,\n what: string\n): Effect.Effect<void> =>\n write.pipe(\n Effect.catch((error) => Effect.logWarning(`process reaper could not journal ${what} for ${pid}`, error))\n )\n\n/**\n * Refusals that leave a dead incarnation's process running with no live owner\n * to contain it: the host could not ask, or its signal failed.\n */\nconst unreaped: ReadonlySet<Refusal> = new Set<Refusal>([\"identity-unverified\", \"own-group-unknown\", \"kill-failed\"])\n\n/**\n * Counter over inherited records the reaper did not kill, with a `refusal`\n * attribute naming why.\n *\n * @category metrics\n * @since 1.0.0-rc.1\n */\nexport const refusals = Metric.counter(\"flows_process_reaper_refusals\", {\n description: \"Inherited process records the reaper did not kill, by refusal\"\n})\n\n/**\n * Reports one sweep: a counter update per refusal and one summary line. The\n * line is a warning when a record stays inherited (a retained refusal) or a\n * signal failed, because either leaves a crashed incarnation's processes\n * running with nothing else to say so.\n *\n * @category layers\n * @since 1.0.0-rc.1\n */\nexport const report = (decided: ReadonlyArray<Reaped>): Effect.Effect<void> =>\n Effect.gen(function*() {\n if (decided.length === 0) return\n const counts: Partial<Record<Refusal, number>> = {}\n let killed = 0\n for (const entry of decided) {\n if (entry.killed) {\n killed += 1\n continue\n }\n counts[entry.refusal] = (counts[entry.refusal] ?? 0) + 1\n }\n for (const [refusal, count] of Object.entries(counts)) {\n yield* Metric.update(Metric.withAttributes(refusals, { refusal }), count)\n }\n const annotations = { killed, ...counts }\n const alarming = decided.some((entry) => !entry.killed && unreaped.has(entry.refusal))\n const line = \"process reaper swept inherited records\"\n yield* (alarming ? Effect.logWarning(line) : Effect.logInfo(line)).pipe(Effect.annotateLogs(annotations))\n })\n\n/**\n * Runs {@link reap} once while the layer is built.\n *\n * Compose it into a host layer so standing a host up is also what cleans up\n * after the incarnation that crashed. A history that cannot be read refuses\n * the sweep with an error log and leaves every record inherited; the host\n * still starts, because the processes it will spawn are recorded and\n * contained regardless. Every decision is {@link report}ed.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer = (options?: Options): Layer.Layer<never, never, ProcessLedger.ProcessLedger> =>\n Layer.effectDiscard(\n reap(options).pipe(\n Effect.flatMap(report),\n Effect.catchTag(\n \"@smthrs/kernel/ProcessLedgerReplayError\",\n (error) => Effect.logError(\"process reaper refused to sweep: the ledger history is incomplete\", error)\n )\n )\n )\n"],
5
5
  "mappings": "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",
6
6
  "names": ["makeSpawner", "startedAtMs", "ownGroup"]
7
7
  }
@@ -4,9 +4,9 @@
4
4
  */
5
5
  import * as Effect from "effect/Effect";
6
6
  import type * as PlatformError from "effect/PlatformError";
7
+ import * as ChildProcess from "effect/process/ChildProcess";
8
+ import type { ChildProcessHandle } from "effect/process/ChildProcessSpawner";
7
9
  import * as Scope from "effect/Scope";
8
- import * as ChildProcess from "effect/unstable/process/ChildProcess";
9
- import type { ChildProcessHandle } from "effect/unstable/process/ChildProcessSpawner";
10
10
  /**
11
11
  * One command's literal argv, environment, pipes and termination policy.
12
12
  * @category models
@@ -1 +1 @@
1
- {"version":3,"file":"ScopedProcess.d.ts","sourceRoot":"","sources":["../../src/ScopedProcess.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,KAAK,aAAa,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAA;AAKrF;;;;GAIG;AACH,MAAM,WAAW,OAAQ,SAAQ,YAAY,CAAC,WAAW;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,SAAS,CAAA;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAA;IAC9C,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACvD,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC3C;AAED;;;;GAIG;AACH,MAAM,WAAW,MAAO,SAAQ,kBAAkB;IAChD,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,KAAK,GAChB,SAAS,OAAO,KACf,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,CAqC9D,CAAA;AAEH;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,MAAM,GAAG,IAAI,CAAA;CAC5C;AAED;;;;;GAKG;AACH,eAAO,MAAM,MAAM,GAAI,QAAQ,kBAAkB,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,aAAa,CAUlG,CAAA"}
1
+ {"version":3,"file":"ScopedProcess.d.ts","sourceRoot":"","sources":["../../src/ScopedProcess.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,KAAK,aAAa,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,YAAY,MAAM,6BAA6B,CAAA;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAC5E,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAKrC;;;;GAIG;AACH,MAAM,WAAW,OAAQ,SAAQ,YAAY,CAAC,WAAW;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,SAAS,CAAA;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAA;IAC9C,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACvD,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC3C;AAED;;;;GAIG;AACH,MAAM,WAAW,MAAO,SAAQ,kBAAkB;IAChD,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,KAAK,GAChB,SAAS,OAAO,KACf,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,CAqC9D,CAAA;AAEH;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,MAAM,GAAG,IAAI,CAAA;CAC5C;AAED;;;;;GAKG;AACH,eAAO,MAAM,MAAM,GAAI,QAAQ,kBAAkB,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,aAAa,CAUlG,CAAA"}
@@ -34,8 +34,8 @@ __export(ScopedProcess_exports, {
34
34
  module.exports = __toCommonJS(ScopedProcess_exports);
35
35
  var Effect = __toESM(require("effect/Effect"), 1);
36
36
  var Exit = __toESM(require("effect/Exit"), 1);
37
+ var ChildProcess = __toESM(require("effect/process/ChildProcess"), 1);
37
38
  var Scope = __toESM(require("effect/Scope"), 1);
38
- var ChildProcess = __toESM(require("effect/unstable/process/ChildProcess"), 1);
39
39
  var PipedProcess = __toESM(require("./internal/PipedProcess.js"), 1);
40
40
  var import_ProcessSupervisor = require("./internal/ProcessSupervisor.js");
41
41
  var ProcessReaper = __toESM(require("./ProcessReaper.js"), 1);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ScopedProcess.ts"],
4
- "sourcesContent": ["/**\n * Scoped transient commands with the host's process-tree cleanup policy.\n * @since 1.0.0\n */\n\nimport * as Effect from \"effect/Effect\"\nimport * as Exit from \"effect/Exit\"\nimport type * as PlatformError from \"effect/PlatformError\"\nimport * as Scope from \"effect/Scope\"\nimport * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport type { ChildProcessHandle } from \"effect/unstable/process/ChildProcessSpawner\"\nimport * as PipedProcess from \"./internal/PipedProcess.ts\"\nimport { targetPidOf } from \"./internal/ProcessSupervisor.ts\"\nimport * as ProcessReaper from \"./ProcessReaper.ts\"\n\n/**\n * One command's literal argv, environment, pipes and termination policy.\n * @category models\n * @since 1.0.0\n */\nexport interface Options extends ChildProcess.KillOptions {\n readonly command: string\n readonly args?: ReadonlyArray<string> | undefined\n readonly cwd?: string | undefined\n readonly env?: Readonly<Record<string, string | undefined>> | undefined\n readonly stdin?: \"ignore\" | \"pipe\" | undefined\n readonly windowsVerbatimArguments?: boolean | undefined\n readonly windowsHide?: boolean | undefined\n}\n\n/**\n * The managed owner identity and the actual target identity are distinct.\n * @category models\n * @since 1.0.0\n */\nexport interface Handle extends ChildProcessHandle {\n /** The native target's pid, for diagnostics and service liveness. */\n readonly targetPid: number\n}\n\n/**\n * Starts a pipe-based command and contains its lifetime inside the caller's\n * scope. The returned pid names the live supervisor; exitCode reports\n * the actual target. The private parent connection stops the tree on host loss,\n * and the supervisor's group guardian stops the group on supervisor loss.\n * Transient commands have no durable ledger; durable hosts use NodeHost instead.\n *\n * Unconsumed output stays in bounded native buffers until it is read or the\n * scope closes. Consume stdout and stderr alongside exitCode: a command can\n * block when an unread pipe is full.\n *\n * Failed startup closes its child scope before returning an error, even when\n * the caller catches that error and retains the surrounding scope.\n * @category constructors\n * @since 1.0.0\n */\nexport const spawn = (\n options: Options\n): Effect.Effect<Handle, PlatformError.PlatformError, Scope.Scope> =>\n Effect.uninterruptibleMask((restore) =>\n Effect.gen(function*() {\n const scope = yield* Scope.fork(yield* Scope.Scope)\n const command = ChildProcess.make(options.command, [...options.args ?? []], {\n cwd: options.cwd,\n env: options.env,\n extendEnv: false,\n stdin: options.stdin ?? \"ignore\",\n stdout: \"pipe\",\n stderr: \"pipe\",\n detached: process.platform !== \"win32\",\n killSignal: options.killSignal ?? \"SIGTERM\",\n forceKillAfter: options.forceKillAfter ?? 2000,\n windowsHide: options.windowsHide ?? true,\n // Preserve the target's native option through the private protocol.\n ...{ windowsVerbatimArguments: options.windowsVerbatimArguments }\n })\n return yield* restore(\n Effect.gen(function*() {\n const prepared = yield* ProcessReaper.processLifecycle(\n command,\n (command) => PipedProcess.spawn(command, false, true)\n )\n yield* prepared.activate\n const targetPid = targetPidOf(prepared.handle)\n if (targetPid === undefined) {\n return yield* Effect.fail(\n PipedProcess.failure(\"spawn\", new Error(\"The supervisor did not identify its target\"))\n )\n }\n return Object.defineProperty(prepared.handle, \"targetPid\", { value: targetPid, enumerable: true }) as Handle\n }).pipe(Scope.provide(scope))\n ).pipe(\n Effect.onError((cause) => Scope.close(scope, Exit.failCause(cause)))\n )\n })\n )\n\n/**\n * The native command's result, preserving a signal instead of inventing a code.\n * @category models\n * @since 1.0.0\n */\nexport interface Status {\n readonly code: number | null\n readonly signal: ChildProcess.Signal | null\n}\n\n/**\n * Reads the target's result. Failures without an actual target signal remain\n * platform failures, including loss of the supervisor before a result arrives.\n * @category execution\n * @since 1.0.0\n */\nexport const status = (handle: ChildProcessHandle): Effect.Effect<Status, PlatformError.PlatformError> =>\n handle.exitCode.pipe(\n Effect.map((code): Status => ({ code, signal: null })),\n Effect.catch((error) => {\n const cause = \"cause\" in error.reason ? error.reason.cause : undefined\n const signal = typeof cause === \"object\" && cause !== null && \"signal\" in cause ? cause.signal : undefined\n return typeof signal === \"string\" && /^SIG[A-Z0-9]+$/.test(signal)\n ? Effect.succeed({ code: null, signal: signal as ChildProcess.Signal })\n : Effect.fail(error)\n })\n )\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,aAAwB;AACxB,WAAsB;AAEtB,YAAuB;AACvB,mBAA8B;AAE9B,mBAA8B;AAC9B,+BAA4B;AAC5B,oBAA+B;AA2CxB,MAAM,QAAQ,CACnB,YAEA,OAAO;AAAA,EAAoB,CAAC,YAC1B,OAAO,IAAI,aAAY;AACrB,UAAM,QAAQ,OAAO,MAAM,KAAK,OAAO,MAAM,KAAK;AAClD,UAAM,UAAU,aAAa,KAAK,QAAQ,SAAS,CAAC,GAAG,QAAQ,QAAQ,CAAC,CAAC,GAAG;AAAA,MAC1E,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,MACb,WAAW;AAAA,MACX,OAAO,QAAQ,SAAS;AAAA,MACxB,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,UAAU,QAAQ,aAAa;AAAA,MAC/B,YAAY,QAAQ,cAAc;AAAA,MAClC,gBAAgB,QAAQ,kBAAkB;AAAA,MAC1C,aAAa,QAAQ,eAAe;AAAA;AAAA,MAEpC,GAAG,EAAE,0BAA0B,QAAQ,yBAAyB;AAAA,IAClE,CAAC;AACD,WAAO,OAAO;AAAA,MACZ,OAAO,IAAI,aAAY;AACrB,cAAM,WAAW,OAAO,cAAc;AAAA,UACpC;AAAA,UACA,CAACA,aAAY,aAAa,MAAMA,UAAS,OAAO,IAAI;AAAA,QACtD;AACA,eAAO,SAAS;AAChB,cAAM,gBAAY,sCAAY,SAAS,MAAM;AAC7C,YAAI,cAAc,QAAW;AAC3B,iBAAO,OAAO,OAAO;AAAA,YACnB,aAAa,QAAQ,SAAS,IAAI,MAAM,4CAA4C,CAAC;AAAA,UACvF;AAAA,QACF;AACA,eAAO,OAAO,eAAe,SAAS,QAAQ,aAAa,EAAE,OAAO,WAAW,YAAY,KAAK,CAAC;AAAA,MACnG,CAAC,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC;AAAA,IAC9B,EAAE;AAAA,MACA,OAAO,QAAQ,CAAC,UAAU,MAAM,MAAM,OAAO,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,IACrE;AAAA,EACF,CAAC;AACH;AAkBK,MAAM,SAAS,CAAC,WACrB,OAAO,SAAS;AAAA,EACd,OAAO,IAAI,CAAC,UAAkB,EAAE,MAAM,QAAQ,KAAK,EAAE;AAAA,EACrD,OAAO,MAAM,CAAC,UAAU;AACtB,UAAM,QAAQ,WAAW,MAAM,SAAS,MAAM,OAAO,QAAQ;AAC7D,UAAM,SAAS,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,QAAQ,MAAM,SAAS;AACjG,WAAO,OAAO,WAAW,YAAY,iBAAiB,KAAK,MAAM,IAC7D,OAAO,QAAQ,EAAE,MAAM,MAAM,OAAsC,CAAC,IACpE,OAAO,KAAK,KAAK;AAAA,EACvB,CAAC;AACH;",
4
+ "sourcesContent": ["/**\n * Scoped transient commands with the host's process-tree cleanup policy.\n * @since 1.0.0\n */\n\nimport * as Effect from \"effect/Effect\"\nimport * as Exit from \"effect/Exit\"\nimport type * as PlatformError from \"effect/PlatformError\"\nimport * as ChildProcess from \"effect/process/ChildProcess\"\nimport type { ChildProcessHandle } from \"effect/process/ChildProcessSpawner\"\nimport * as Scope from \"effect/Scope\"\nimport * as PipedProcess from \"./internal/PipedProcess.ts\"\nimport { targetPidOf } from \"./internal/ProcessSupervisor.ts\"\nimport * as ProcessReaper from \"./ProcessReaper.ts\"\n\n/**\n * One command's literal argv, environment, pipes and termination policy.\n * @category models\n * @since 1.0.0\n */\nexport interface Options extends ChildProcess.KillOptions {\n readonly command: string\n readonly args?: ReadonlyArray<string> | undefined\n readonly cwd?: string | undefined\n readonly env?: Readonly<Record<string, string | undefined>> | undefined\n readonly stdin?: \"ignore\" | \"pipe\" | undefined\n readonly windowsVerbatimArguments?: boolean | undefined\n readonly windowsHide?: boolean | undefined\n}\n\n/**\n * The managed owner identity and the actual target identity are distinct.\n * @category models\n * @since 1.0.0\n */\nexport interface Handle extends ChildProcessHandle {\n /** The native target's pid, for diagnostics and service liveness. */\n readonly targetPid: number\n}\n\n/**\n * Starts a pipe-based command and contains its lifetime inside the caller's\n * scope. The returned pid names the live supervisor; exitCode reports\n * the actual target. The private parent connection stops the tree on host loss,\n * and the supervisor's group guardian stops the group on supervisor loss.\n * Transient commands have no durable ledger; durable hosts use NodeHost instead.\n *\n * Unconsumed output stays in bounded native buffers until it is read or the\n * scope closes. Consume stdout and stderr alongside exitCode: a command can\n * block when an unread pipe is full.\n *\n * Failed startup closes its child scope before returning an error, even when\n * the caller catches that error and retains the surrounding scope.\n * @category constructors\n * @since 1.0.0\n */\nexport const spawn = (\n options: Options\n): Effect.Effect<Handle, PlatformError.PlatformError, Scope.Scope> =>\n Effect.uninterruptibleMask((restore) =>\n Effect.gen(function*() {\n const scope = yield* Scope.fork(yield* Scope.Scope)\n const command = ChildProcess.make(options.command, [...options.args ?? []], {\n cwd: options.cwd,\n env: options.env,\n extendEnv: false,\n stdin: options.stdin ?? \"ignore\",\n stdout: \"pipe\",\n stderr: \"pipe\",\n detached: process.platform !== \"win32\",\n killSignal: options.killSignal ?? \"SIGTERM\",\n forceKillAfter: options.forceKillAfter ?? 2000,\n windowsHide: options.windowsHide ?? true,\n // Preserve the target's native option through the private protocol.\n ...{ windowsVerbatimArguments: options.windowsVerbatimArguments }\n })\n return yield* restore(\n Effect.gen(function*() {\n const prepared = yield* ProcessReaper.processLifecycle(\n command,\n (command) => PipedProcess.spawn(command, false, true)\n )\n yield* prepared.activate\n const targetPid = targetPidOf(prepared.handle)\n if (targetPid === undefined) {\n return yield* Effect.fail(\n PipedProcess.failure(\"spawn\", new Error(\"The supervisor did not identify its target\"))\n )\n }\n return Object.defineProperty(prepared.handle, \"targetPid\", { value: targetPid, enumerable: true }) as Handle\n }).pipe(Scope.provide(scope))\n ).pipe(\n Effect.onError((cause) => Scope.close(scope, Exit.failCause(cause)))\n )\n })\n )\n\n/**\n * The native command's result, preserving a signal instead of inventing a code.\n * @category models\n * @since 1.0.0\n */\nexport interface Status {\n readonly code: number | null\n readonly signal: ChildProcess.Signal | null\n}\n\n/**\n * Reads the target's result. Failures without an actual target signal remain\n * platform failures, including loss of the supervisor before a result arrives.\n * @category execution\n * @since 1.0.0\n */\nexport const status = (handle: ChildProcessHandle): Effect.Effect<Status, PlatformError.PlatformError> =>\n handle.exitCode.pipe(\n Effect.map((code): Status => ({ code, signal: null })),\n Effect.catch((error) => {\n const cause = \"cause\" in error.reason ? error.reason.cause : undefined\n const signal = typeof cause === \"object\" && cause !== null && \"signal\" in cause ? cause.signal : undefined\n return typeof signal === \"string\" && /^SIG[A-Z0-9]+$/.test(signal)\n ? Effect.succeed({ code: null, signal: signal as ChildProcess.Signal })\n : Effect.fail(error)\n })\n )\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,aAAwB;AACxB,WAAsB;AAEtB,mBAA8B;AAE9B,YAAuB;AACvB,mBAA8B;AAC9B,+BAA4B;AAC5B,oBAA+B;AA2CxB,MAAM,QAAQ,CACnB,YAEA,OAAO;AAAA,EAAoB,CAAC,YAC1B,OAAO,IAAI,aAAY;AACrB,UAAM,QAAQ,OAAO,MAAM,KAAK,OAAO,MAAM,KAAK;AAClD,UAAM,UAAU,aAAa,KAAK,QAAQ,SAAS,CAAC,GAAG,QAAQ,QAAQ,CAAC,CAAC,GAAG;AAAA,MAC1E,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,MACb,WAAW;AAAA,MACX,OAAO,QAAQ,SAAS;AAAA,MACxB,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,UAAU,QAAQ,aAAa;AAAA,MAC/B,YAAY,QAAQ,cAAc;AAAA,MAClC,gBAAgB,QAAQ,kBAAkB;AAAA,MAC1C,aAAa,QAAQ,eAAe;AAAA;AAAA,MAEpC,GAAG,EAAE,0BAA0B,QAAQ,yBAAyB;AAAA,IAClE,CAAC;AACD,WAAO,OAAO;AAAA,MACZ,OAAO,IAAI,aAAY;AACrB,cAAM,WAAW,OAAO,cAAc;AAAA,UACpC;AAAA,UACA,CAACA,aAAY,aAAa,MAAMA,UAAS,OAAO,IAAI;AAAA,QACtD;AACA,eAAO,SAAS;AAChB,cAAM,gBAAY,sCAAY,SAAS,MAAM;AAC7C,YAAI,cAAc,QAAW;AAC3B,iBAAO,OAAO,OAAO;AAAA,YACnB,aAAa,QAAQ,SAAS,IAAI,MAAM,4CAA4C,CAAC;AAAA,UACvF;AAAA,QACF;AACA,eAAO,OAAO,eAAe,SAAS,QAAQ,aAAa,EAAE,OAAO,WAAW,YAAY,KAAK,CAAC;AAAA,MACnG,CAAC,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC;AAAA,IAC9B,EAAE;AAAA,MACA,OAAO,QAAQ,CAAC,UAAU,MAAM,MAAM,OAAO,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,IACrE;AAAA,EACF,CAAC;AACH;AAkBK,MAAM,SAAS,CAAC,WACrB,OAAO,SAAS;AAAA,EACd,OAAO,IAAI,CAAC,UAAkB,EAAE,MAAM,QAAQ,KAAK,EAAE;AAAA,EACrD,OAAO,MAAM,CAAC,UAAU;AACtB,UAAM,QAAQ,WAAW,MAAM,SAAS,MAAM,OAAO,QAAQ;AAC7D,UAAM,SAAS,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,QAAQ,MAAM,SAAS;AACjG,WAAO,OAAO,WAAW,YAAY,iBAAiB,KAAK,MAAM,IAC7D,OAAO,QAAQ,EAAE,MAAM,MAAM,OAAsC,CAAC,IACpE,OAAO,KAAK,KAAK;AAAA,EACvB,CAAC;AACH;",
6
6
  "names": ["command"]
7
7
  }
@@ -4,9 +4,9 @@
4
4
  */
5
5
  import * as Effect from "effect/Effect";
6
6
  import * as PlatformError from "effect/PlatformError";
7
+ import * as ChildProcess from "effect/process/ChildProcess";
8
+ import { type ChildProcessHandle } from "effect/process/ChildProcessSpawner";
7
9
  import type * as Scope from "effect/Scope";
8
- import * as ChildProcess from "effect/unstable/process/ChildProcess";
9
- import { type ChildProcessHandle } from "effect/unstable/process/ChildProcessSpawner";
10
10
  /**
11
11
  * Windows owners receive caller streams above the reserved CRT standard slots.
12
12
  * @private
@@ -1 +1 @@
1
- {"version":3,"file":"PipedProcess.d.ts","sourceRoot":"","sources":["../../../src/internal/PipedProcess.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAA;AACrD,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAG1C,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAE,KAAK,kBAAkB,EAAmC,MAAM,6CAA6C,CAAA;AAMtH;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ,kBAAkB,KAAG,aAAa,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,SAChE,CAAA;AAa9B;;;;GAIG;AACH,eAAO,MAAM,OAAO,GAAI,QAAQ,MAAM,EAAE,OAAO,OAAO,KAAG,aAAa,CAAC,aAUtE,CAAA;AAyCD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,GAChB,SAAS,YAAY,CAAC,eAAe,EACrC,0BAA0B,OAAO,GAAG,SAAS,EAC7C,eAAa,KACZ,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,CAsPzE,CAAA"}
1
+ {"version":3,"file":"PipedProcess.d.ts","sourceRoot":"","sources":["../../../src/internal/PipedProcess.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAA;AACrD,OAAO,KAAK,YAAY,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAAE,KAAK,kBAAkB,EAAmC,MAAM,oCAAoC,CAAA;AAC7G,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAQ1C;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ,kBAAkB,KAAG,aAAa,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,SAChE,CAAA;AAa9B;;;;GAIG;AACH,eAAO,MAAM,OAAO,GAAI,QAAQ,MAAM,EAAE,OAAO,OAAO,KAAG,aAAa,CAAC,aAUtE,CAAA;AAyCD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,GAChB,SAAS,YAAY,CAAC,eAAe,EACrC,0BAA0B,OAAO,GAAG,SAAS,EAC7C,eAAa,KACZ,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,CAsPzE,CAAA"}
@@ -37,10 +37,10 @@ var NodeSink = __toESM(require("../../vendor/cjs/node/NodeSink.js"), 1);
37
37
  var NodeStream = __toESM(require("../../vendor/cjs/node/NodeStream.js"), 1);
38
38
  var Effect = __toESM(require("effect/Effect"), 1);
39
39
  var PlatformError = __toESM(require("effect/PlatformError"), 1);
40
+ var ChildProcess = __toESM(require("effect/process/ChildProcess"), 1);
41
+ var import_ChildProcessSpawner = require("effect/process/ChildProcessSpawner");
40
42
  var Sink = __toESM(require("effect/Sink"), 1);
41
43
  var Stream = __toESM(require("effect/Stream"), 1);
42
- var ChildProcess = __toESM(require("effect/unstable/process/ChildProcess"), 1);
43
- var import_ChildProcessSpawner = require("effect/unstable/process/ChildProcessSpawner");
44
44
  var Native = __toESM(require("node:child_process"), 1);
45
45
  var import_ProcessFault = require("./ProcessFault.js");
46
46
  const ownerDescriptors = /* @__PURE__ */ new WeakMap();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/internal/PipedProcess.ts"],
4
- "sourcesContent": ["/**\n * Native I/O ownership for transient commands and prepared process owners.\n * @since 1.0.0\n */\n\nimport * as NodeSink from \"@effect/platform-node/NodeSink\"\nimport * as NodeStream from \"@effect/platform-node/NodeStream\"\nimport * as Effect from \"effect/Effect\"\nimport * as PlatformError from \"effect/PlatformError\"\nimport type * as Scope from \"effect/Scope\"\nimport * as Sink from \"effect/Sink\"\nimport * as Stream from \"effect/Stream\"\nimport * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport { type ChildProcessHandle, ExitCode, makeHandle, ProcessId } from \"effect/unstable/process/ChildProcessSpawner\"\nimport * as Native from \"node:child_process\"\nimport { NativeProcessError } from \"./ProcessFault.ts\"\n\nconst ownerDescriptors = new WeakMap<ChildProcessHandle, ReadonlyArray<number | \"ignore\">>()\n\n/**\n * Windows owners receive caller streams above the reserved CRT standard slots.\n * @private\n * @since 1.0.0\n */\nexport const standardFdsOf = (handle: ChildProcessHandle): ReadonlyArray<number | \"ignore\"> | undefined =>\n ownerDescriptors.get(handle)\n\nconst promise = <A>() => {\n let resolve!: (value: A) => void\n let reject!: (cause: unknown) => void\n const promise = new Promise<A>((onSuccess, onFailure) => {\n resolve = onSuccess\n reject = onFailure\n })\n void promise.catch(() => {})\n return { promise, resolve, reject }\n}\n\n/**\n * Converts native errors without losing their errno or signal.\n * @private\n * @since 1.0.0\n */\nexport const failure = (method: string, cause: unknown): PlatformError.PlatformError => {\n const error = cause instanceof Error ? cause : new NativeProcessError({ message: String(cause) })\n const code = (error as NodeJS.ErrnoException).code\n return PlatformError.systemError({\n _tag: code === \"ENOENT\" ? \"NotFound\" : code === \"EACCES\" || code === \"EPERM\" ? \"PermissionDenied\" : \"Unknown\",\n module: \"ChildProcess\",\n method,\n description: error.message,\n cause: error\n })\n}\n\nconst inputConfig = (value: ChildProcess.CommandOptions[\"stdin\"]): ChildProcess.StdinConfig =>\n value === undefined || typeof value === \"string\" || Stream.isStream(value)\n ? { stream: value ?? \"pipe\" }\n : value\n\nconst outputConfig = (\n value: ChildProcess.CommandOptions[\"stdout\"]\n): ChildProcess.CommandOutput =>\n value === undefined || typeof value === \"string\" || Sink.isSink(value)\n ? value ?? \"pipe\"\n : value.stream ?? \"pipe\"\n\nconst descriptorConfig = (options: ChildProcess.CommandOptions) => {\n const stdin = inputConfig(options.stdin)\n const stdout = outputConfig(options.stdout)\n const stderr = outputConfig(options.stderr)\n const additional: Array<{ readonly fd: number; readonly config: ChildProcess.AdditionalFdConfig }> = []\n for (const [name, config] of Object.entries(options.additionalFds ?? {})) {\n const fd = ChildProcess.parseFdName(name)\n if (fd === undefined) continue\n // A sparse descriptor declaration must not allocate an unbounded stdio\n // table. The OS may impose a smaller limit when opening the actual pipes.\n if (!Number.isSafeInteger(fd) || fd > 65_535) {\n throw new RangeError(\"Additional file descriptors must be below 65536\")\n }\n additional.push({ fd, config })\n }\n additional.sort((left, right) => left.fd - right.fd)\n const stdio: Array<Native.IOType> = Array.from(\n { length: Math.max(2, ...additional.map(({ fd }) => fd)) + 1 },\n () => \"ignore\"\n )\n stdio[0] = typeof stdin.stream === \"string\" ? stdin.stream : \"pipe\"\n stdio[1] = typeof stdout === \"string\" ? stdout : \"pipe\"\n stdio[2] = typeof stderr === \"string\" ? stderr : \"pipe\"\n for (const { fd } of additional) stdio[fd] = \"pipe\"\n return { stdin, stdout, stderr, additional, stdio }\n}\n\n/**\n * Native standard-command I/O, including configured streams and custom pipes.\n * Lifetime error listeners remain on every owned pipe after any consumer is\n * interrupted, so a pending native write cannot raise an unhandled late EPIPE.\n * Consumers still receive the original error through NodeStream/NodeSink.\n * This adapter also preserves native Windows verbatim arguments.\n *\n * It never sends a negative-pid signal. POSIX tree cleanup belongs to the\n * prepared supervisor, which remains alive while it signals its own group.\n * @private\n * @since 1.0.0\n */\nexport const spawn = (\n command: ChildProcess.StandardCommand,\n windowsVerbatimArguments: boolean | undefined,\n owner = false\n): Effect.Effect<ChildProcessHandle, PlatformError.PlatformError, Scope.Scope> =>\n Effect.gen(function*() {\n const descriptors = yield* Effect.try({\n try: () => descriptorConfig(command.options),\n catch: (cause) => failure(\"spawn\", cause)\n })\n const standardFds: Array<number | \"ignore\"> = [0, 1, 2]\n const physicalFds = [0, 1, 2]\n const stdio: Array<Native.IOType | number> = [...descriptors.stdio]\n if (owner && process.platform === \"win32\") {\n const first = stdio.length\n for (const fd of [0, 1, 2]) {\n const mode = stdio[fd]!\n physicalFds[fd] = first + fd\n standardFds[fd] = mode === \"ignore\" ? \"ignore\" : first + fd\n stdio.push(mode === \"inherit\" ? fd : mode)\n stdio[fd] = \"ignore\"\n }\n }\n const state = yield* Effect.acquireRelease(\n Effect.try({\n try: () => {\n const ready = promise<void>()\n // Startup errors reject ready before any handle is exposed. Once\n // started, the native exit event or retained native status supplies\n // this result.\n let recordExit!: (result: readonly [number | null, NodeJS.Signals | null]) => void\n const exited = new Promise<readonly [number | null, NodeJS.Signals | null]>((resolve) => {\n recordExit = resolve\n })\n const options = command.options\n const child = Native.spawn(command.command, [...command.args], {\n cwd: options.cwd,\n env: options.extendEnv ? { ...process.env, ...options.env } : options.env,\n shell: options.shell,\n detached: options.detached ?? process.platform !== \"win32\",\n windowsHide: options.windowsHide ?? true,\n windowsVerbatimArguments,\n stdio\n })\n const state = {\n child,\n ready: ready.promise,\n exited,\n ended: false,\n started: false,\n referenced: true,\n pipeErrors: new WeakMap<object, Error>(),\n error: undefined as Error | undefined\n }\n child.once(\"spawn\", () => {\n state.started = true\n ready.resolve()\n })\n child.on(\"error\", (error) => {\n state.error = error\n if (!state.started) {\n state.ended = true\n ready.reject(error)\n }\n })\n const onExit = (code: number | null, signal: NodeJS.Signals | null) => {\n state.ended = true\n recordExit([code, signal])\n }\n child.once(\"exit\", onExit)\n // Bun can emit spawn before Native.spawn returns on a cold start.\n // A returned pid proves that the native child was created; failed\n // native spawns have no pid and still reject through the error event.\n if (child.pid !== undefined) {\n state.started = true\n ready.resolve()\n }\n // Likewise retain an exit already reflected by the native handle\n // before listeners could attach. Later events settle the same result.\n if (child.exitCode != null || child.signalCode != null) {\n onExit(child.exitCode ?? null, child.signalCode ?? null)\n }\n // A pipe can fail before a consumer has started reading or writing.\n // Preserve the cause for consumers that attach after its error event.\n for (const pipe of child.stdio) {\n pipe?.on(\"error\", (error: Error) => {\n state.pipeErrors.set(pipe, error)\n })\n }\n // Node resumes unread stdio after exit to deliver its close event.\n // A fast target can exit before activation returns the public handle;\n // flowing at that point discards bytes before any consumer subscribes.\n // Keep declared outputs in readable mode for their entire lifetime.\n // This listener does not drain or copy data: the native high-water\n // mark still applies backpressure until NodeStream pulls the bytes.\n for (\n const fd of [\n physicalFds[1]!,\n physicalFds[2]!,\n ...descriptors.additional.filter(({ config }) => config.type === \"output\").map(({ fd }) => fd)\n ]\n ) {\n const pipe = child.stdio[fd]\n pipe?.on(\"readable\", () => {})\n // POSIX sockets can close the unused writer independently. Bun\n // needs this to retain buffered output after child exit. Windows\n // named-pipe shutdown instead starts libuv's EOF timer, which can\n // close both directions before the child writes. Leave it open.\n if (process.platform !== \"win32\" && fd > 2 && pipe !== null && pipe !== undefined && \"end\" in pipe) {\n pipe.end()\n }\n }\n return state\n },\n catch: (cause) => failure(\"spawn\", cause)\n }),\n (state) =>\n Effect.gen(function*() {\n if (state.referenced && !state.ended) {\n yield* Effect.try({\n try: () => {\n // Native kill returns false for an exit whose event is still\n // queued. Require that event below; never infer a live failure\n // or successful cleanup from the boolean alone.\n if (!state.child.kill(\"SIGKILL\") && state.error !== undefined) throw state.error\n },\n catch: (cause) => failure(\"kill\", cause)\n })\n yield* Effect.promise(() => state.exited).pipe(\n Effect.timeout(\"2 seconds\")\n )\n }\n }).pipe(\n Effect.ensuring(Effect.sync(() => {\n for (const pipe of state.child.stdio) {\n if (pipe !== null && pipe !== undefined && \"destroy\" in pipe && typeof pipe.destroy === \"function\") {\n pipe.destroy()\n }\n }\n })),\n Effect.orDie\n )\n )\n yield* Effect.tryPromise({ try: () => state.ready, catch: (cause) => failure(\"spawn\", cause) })\n const wait = Effect.promise(() => state.exited)\n const kill: ChildProcessHandle[\"kill\"] = (options) =>\n Effect.suspend(() => {\n if (state.ended) return Effect.void\n const signal = options?.killSignal ?? command.options.killSignal ?? \"SIGTERM\"\n const send = (signal: ChildProcess.Signal) =>\n Effect.try({\n try: () => {\n if (!state.child.kill(signal) && state.error !== undefined) throw state.error\n },\n catch: (cause) => failure(\"kill\", cause)\n })\n return send(signal).pipe(\n Effect.andThen(wait.pipe(Effect.timeoutOrElse({\n duration: options?.forceKillAfter ?? command.options.forceKillAfter ?? 2000,\n orElse: () => send(\"SIGKILL\").pipe(Effect.andThen(wait), Effect.timeout(\"2 seconds\"))\n }))),\n Effect.mapError((cause) => cause instanceof PlatformError.PlatformError ? cause : failure(\"kill\", cause)),\n Effect.asVoid,\n Effect.uninterruptible\n )\n })\n const input = (fd: number, config?: ChildProcess.StdinConfig): ChildProcessHandle[\"stdin\"] => {\n const pipe = state.child.stdio[fd]\n if (pipe === null || pipe === undefined || !(\"write\" in pipe)) return Sink.drain\n const writable = pipe\n const method = fd === 0 ? \"stdin\" : `fd${fd}`\n return Sink.suspend(() => {\n const error = state.pipeErrors.get(pipe) ?? writable.errored ?? undefined\n if (error !== undefined) return Sink.fail(failure(method, error))\n if (writable.destroyed || writable.writableEnded) {\n return Sink.fail(failure(method, new NativeProcessError({ message: `${method} is closed`, code: \"EPIPE\" })))\n }\n return NodeSink.fromWritable({\n evaluate: () => writable,\n onError: (cause) => failure(method, cause),\n encoding: config?.encoding,\n endOnDone: config?.endOnDone\n })\n })\n }\n const output = (fd: number, transform?: ChildProcess.CommandOutput): ChildProcessHandle[\"stdout\"] => {\n const pipe = state.child.stdio[fd]\n const stream = pipe !== null && pipe !== undefined && \"read\" in pipe\n ? Stream.suspend(() => {\n const method = fd === 1 ? \"stdout\" : fd === 2 ? \"stderr\" : `fd${fd}`\n const error = state.pipeErrors.get(pipe) ?? pipe.errored ?? undefined\n return error !== undefined ?\n Stream.fail(failure(method, error)) :\n NodeStream.fromReadable<Uint8Array, PlatformError.PlatformError>({\n evaluate: () => pipe,\n onError: (cause) => failure(method, cause)\n })\n })\n : Stream.empty\n return Sink.isSink(transform) ? Stream.transduce(stream, transform) : stream\n }\n const stdin = input(physicalFds[0]!, descriptors.stdin)\n const stdout = output(physicalFds[1]!, descriptors.stdout)\n const stderr = output(physicalFds[2]!, descriptors.stderr)\n if (Stream.isStream(descriptors.stdin.stream)) {\n yield* Stream.run(descriptors.stdin.stream, stdin).pipe(Effect.forkScoped)\n }\n const inputs = new Map<number, ChildProcessHandle[\"stdin\"]>()\n const outputs = new Map<number, ChildProcessHandle[\"stdout\"]>()\n for (const { fd, config } of descriptors.additional) {\n if (config.type === \"input\") {\n const sink = input(fd)\n inputs.set(fd, sink)\n if (config.stream !== undefined) yield* Stream.run(config.stream, sink).pipe(Effect.forkScoped)\n } else outputs.set(fd, output(fd, config.sink))\n }\n const handle = makeHandle({\n pid: ProcessId(state.child.pid!),\n exitCode: wait.pipe(Effect.flatMap(([code, signal]) =>\n code === null\n ? Effect.fail(\n failure(\n \"exitCode\",\n new NativeProcessError({\n message: `Process interrupted due to receipt of signal: '${signal}'`,\n signal: String(signal)\n })\n )\n )\n : Effect.succeed(ExitCode(code))\n )),\n isRunning: Effect.sync(() => !state.ended),\n kill,\n stdin,\n stdout,\n stderr,\n all: Stream.merge(stdout, stderr),\n getInputFd: (fd) => inputs.get(fd) ?? Sink.drain,\n getOutputFd: (fd) => outputs.get(fd) ?? Stream.empty,\n unref: Effect.sync(() => {\n state.referenced = false\n state.child.unref()\n return Effect.sync(() => {\n state.referenced = true\n state.child.ref()\n })\n })\n })\n if (owner && process.platform === \"win32\") ownerDescriptors.set(handle, standardFds)\n return handle\n })\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Native I/O ownership for transient commands and prepared process owners.\n * @since 1.0.0\n */\n\nimport * as NodeSink from \"@effect/platform-node/NodeSink\"\nimport * as NodeStream from \"@effect/platform-node/NodeStream\"\nimport * as Effect from \"effect/Effect\"\nimport * as PlatformError from \"effect/PlatformError\"\nimport * as ChildProcess from \"effect/process/ChildProcess\"\nimport { type ChildProcessHandle, ExitCode, makeHandle, ProcessId } from \"effect/process/ChildProcessSpawner\"\nimport type * as Scope from \"effect/Scope\"\nimport * as Sink from \"effect/Sink\"\nimport * as Stream from \"effect/Stream\"\nimport * as Native from \"node:child_process\"\nimport { NativeProcessError } from \"./ProcessFault.ts\"\n\nconst ownerDescriptors = new WeakMap<ChildProcessHandle, ReadonlyArray<number | \"ignore\">>()\n\n/**\n * Windows owners receive caller streams above the reserved CRT standard slots.\n * @private\n * @since 1.0.0\n */\nexport const standardFdsOf = (handle: ChildProcessHandle): ReadonlyArray<number | \"ignore\"> | undefined =>\n ownerDescriptors.get(handle)\n\nconst promise = <A>() => {\n let resolve!: (value: A) => void\n let reject!: (cause: unknown) => void\n const promise = new Promise<A>((onSuccess, onFailure) => {\n resolve = onSuccess\n reject = onFailure\n })\n void promise.catch(() => {})\n return { promise, resolve, reject }\n}\n\n/**\n * Converts native errors without losing their errno or signal.\n * @private\n * @since 1.0.0\n */\nexport const failure = (method: string, cause: unknown): PlatformError.PlatformError => {\n const error = cause instanceof Error ? cause : new NativeProcessError({ message: String(cause) })\n const code = (error as NodeJS.ErrnoException).code\n return PlatformError.systemError({\n _tag: code === \"ENOENT\" ? \"NotFound\" : code === \"EACCES\" || code === \"EPERM\" ? \"PermissionDenied\" : \"Unknown\",\n module: \"ChildProcess\",\n method,\n description: error.message,\n cause: error\n })\n}\n\nconst inputConfig = (value: ChildProcess.CommandOptions[\"stdin\"]): ChildProcess.StdinConfig =>\n value === undefined || typeof value === \"string\" || Stream.isStream(value)\n ? { stream: value ?? \"pipe\" }\n : value\n\nconst outputConfig = (\n value: ChildProcess.CommandOptions[\"stdout\"]\n): ChildProcess.CommandOutput =>\n value === undefined || typeof value === \"string\" || Sink.isSink(value)\n ? value ?? \"pipe\"\n : value.stream ?? \"pipe\"\n\nconst descriptorConfig = (options: ChildProcess.CommandOptions) => {\n const stdin = inputConfig(options.stdin)\n const stdout = outputConfig(options.stdout)\n const stderr = outputConfig(options.stderr)\n const additional: Array<{ readonly fd: number; readonly config: ChildProcess.AdditionalFdConfig }> = []\n for (const [name, config] of Object.entries(options.additionalFds ?? {})) {\n const fd = ChildProcess.parseFdName(name)\n if (fd === undefined) continue\n // A sparse descriptor declaration must not allocate an unbounded stdio\n // table. The OS may impose a smaller limit when opening the actual pipes.\n if (!Number.isSafeInteger(fd) || fd > 65_535) {\n throw new RangeError(\"Additional file descriptors must be below 65536\")\n }\n additional.push({ fd, config })\n }\n additional.sort((left, right) => left.fd - right.fd)\n const stdio: Array<Native.IOType> = Array.from(\n { length: Math.max(2, ...additional.map(({ fd }) => fd)) + 1 },\n () => \"ignore\"\n )\n stdio[0] = typeof stdin.stream === \"string\" ? stdin.stream : \"pipe\"\n stdio[1] = typeof stdout === \"string\" ? stdout : \"pipe\"\n stdio[2] = typeof stderr === \"string\" ? stderr : \"pipe\"\n for (const { fd } of additional) stdio[fd] = \"pipe\"\n return { stdin, stdout, stderr, additional, stdio }\n}\n\n/**\n * Native standard-command I/O, including configured streams and custom pipes.\n * Lifetime error listeners remain on every owned pipe after any consumer is\n * interrupted, so a pending native write cannot raise an unhandled late EPIPE.\n * Consumers still receive the original error through NodeStream/NodeSink.\n * This adapter also preserves native Windows verbatim arguments.\n *\n * It never sends a negative-pid signal. POSIX tree cleanup belongs to the\n * prepared supervisor, which remains alive while it signals its own group.\n * @private\n * @since 1.0.0\n */\nexport const spawn = (\n command: ChildProcess.StandardCommand,\n windowsVerbatimArguments: boolean | undefined,\n owner = false\n): Effect.Effect<ChildProcessHandle, PlatformError.PlatformError, Scope.Scope> =>\n Effect.gen(function*() {\n const descriptors = yield* Effect.try({\n try: () => descriptorConfig(command.options),\n catch: (cause) => failure(\"spawn\", cause)\n })\n const standardFds: Array<number | \"ignore\"> = [0, 1, 2]\n const physicalFds = [0, 1, 2]\n const stdio: Array<Native.IOType | number> = [...descriptors.stdio]\n if (owner && process.platform === \"win32\") {\n const first = stdio.length\n for (const fd of [0, 1, 2]) {\n const mode = stdio[fd]!\n physicalFds[fd] = first + fd\n standardFds[fd] = mode === \"ignore\" ? \"ignore\" : first + fd\n stdio.push(mode === \"inherit\" ? fd : mode)\n stdio[fd] = \"ignore\"\n }\n }\n const state = yield* Effect.acquireRelease(\n Effect.try({\n try: () => {\n const ready = promise<void>()\n // Startup errors reject ready before any handle is exposed. Once\n // started, the native exit event or retained native status supplies\n // this result.\n let recordExit!: (result: readonly [number | null, NodeJS.Signals | null]) => void\n const exited = new Promise<readonly [number | null, NodeJS.Signals | null]>((resolve) => {\n recordExit = resolve\n })\n const options = command.options\n const child = Native.spawn(command.command, [...command.args], {\n cwd: options.cwd,\n env: options.extendEnv ? { ...process.env, ...options.env } : options.env,\n shell: options.shell,\n detached: options.detached ?? process.platform !== \"win32\",\n windowsHide: options.windowsHide ?? true,\n windowsVerbatimArguments,\n stdio\n })\n const state = {\n child,\n ready: ready.promise,\n exited,\n ended: false,\n started: false,\n referenced: true,\n pipeErrors: new WeakMap<object, Error>(),\n error: undefined as Error | undefined\n }\n child.once(\"spawn\", () => {\n state.started = true\n ready.resolve()\n })\n child.on(\"error\", (error) => {\n state.error = error\n if (!state.started) {\n state.ended = true\n ready.reject(error)\n }\n })\n const onExit = (code: number | null, signal: NodeJS.Signals | null) => {\n state.ended = true\n recordExit([code, signal])\n }\n child.once(\"exit\", onExit)\n // Bun can emit spawn before Native.spawn returns on a cold start.\n // A returned pid proves that the native child was created; failed\n // native spawns have no pid and still reject through the error event.\n if (child.pid !== undefined) {\n state.started = true\n ready.resolve()\n }\n // Likewise retain an exit already reflected by the native handle\n // before listeners could attach. Later events settle the same result.\n if (child.exitCode != null || child.signalCode != null) {\n onExit(child.exitCode ?? null, child.signalCode ?? null)\n }\n // A pipe can fail before a consumer has started reading or writing.\n // Preserve the cause for consumers that attach after its error event.\n for (const pipe of child.stdio) {\n pipe?.on(\"error\", (error: Error) => {\n state.pipeErrors.set(pipe, error)\n })\n }\n // Node resumes unread stdio after exit to deliver its close event.\n // A fast target can exit before activation returns the public handle;\n // flowing at that point discards bytes before any consumer subscribes.\n // Keep declared outputs in readable mode for their entire lifetime.\n // This listener does not drain or copy data: the native high-water\n // mark still applies backpressure until NodeStream pulls the bytes.\n for (\n const fd of [\n physicalFds[1]!,\n physicalFds[2]!,\n ...descriptors.additional.filter(({ config }) => config.type === \"output\").map(({ fd }) => fd)\n ]\n ) {\n const pipe = child.stdio[fd]\n pipe?.on(\"readable\", () => {})\n // POSIX sockets can close the unused writer independently. Bun\n // needs this to retain buffered output after child exit. Windows\n // named-pipe shutdown instead starts libuv's EOF timer, which can\n // close both directions before the child writes. Leave it open.\n if (process.platform !== \"win32\" && fd > 2 && pipe !== null && pipe !== undefined && \"end\" in pipe) {\n pipe.end()\n }\n }\n return state\n },\n catch: (cause) => failure(\"spawn\", cause)\n }),\n (state) =>\n Effect.gen(function*() {\n if (state.referenced && !state.ended) {\n yield* Effect.try({\n try: () => {\n // Native kill returns false for an exit whose event is still\n // queued. Require that event below; never infer a live failure\n // or successful cleanup from the boolean alone.\n if (!state.child.kill(\"SIGKILL\") && state.error !== undefined) throw state.error\n },\n catch: (cause) => failure(\"kill\", cause)\n })\n yield* Effect.promise(() => state.exited).pipe(\n Effect.timeout(\"2 seconds\")\n )\n }\n }).pipe(\n Effect.ensuring(Effect.sync(() => {\n for (const pipe of state.child.stdio) {\n if (pipe !== null && pipe !== undefined && \"destroy\" in pipe && typeof pipe.destroy === \"function\") {\n pipe.destroy()\n }\n }\n })),\n Effect.orDie\n )\n )\n yield* Effect.tryPromise({ try: () => state.ready, catch: (cause) => failure(\"spawn\", cause) })\n const wait = Effect.promise(() => state.exited)\n const kill: ChildProcessHandle[\"kill\"] = (options) =>\n Effect.suspend(() => {\n if (state.ended) return Effect.void\n const signal = options?.killSignal ?? command.options.killSignal ?? \"SIGTERM\"\n const send = (signal: ChildProcess.Signal) =>\n Effect.try({\n try: () => {\n if (!state.child.kill(signal) && state.error !== undefined) throw state.error\n },\n catch: (cause) => failure(\"kill\", cause)\n })\n return send(signal).pipe(\n Effect.andThen(wait.pipe(Effect.timeoutOrElse({\n duration: options?.forceKillAfter ?? command.options.forceKillAfter ?? 2000,\n orElse: () => send(\"SIGKILL\").pipe(Effect.andThen(wait), Effect.timeout(\"2 seconds\"))\n }))),\n Effect.mapError((cause) => cause instanceof PlatformError.PlatformError ? cause : failure(\"kill\", cause)),\n Effect.asVoid,\n Effect.uninterruptible\n )\n })\n const input = (fd: number, config?: ChildProcess.StdinConfig): ChildProcessHandle[\"stdin\"] => {\n const pipe = state.child.stdio[fd]\n if (pipe === null || pipe === undefined || !(\"write\" in pipe)) return Sink.drain\n const writable = pipe\n const method = fd === 0 ? \"stdin\" : `fd${fd}`\n return Sink.suspend(() => {\n const error = state.pipeErrors.get(pipe) ?? writable.errored ?? undefined\n if (error !== undefined) return Sink.fail(failure(method, error))\n if (writable.destroyed || writable.writableEnded) {\n return Sink.fail(failure(method, new NativeProcessError({ message: `${method} is closed`, code: \"EPIPE\" })))\n }\n return NodeSink.fromWritable({\n evaluate: () => writable,\n onError: (cause) => failure(method, cause),\n encoding: config?.encoding,\n endOnDone: config?.endOnDone\n })\n })\n }\n const output = (fd: number, transform?: ChildProcess.CommandOutput): ChildProcessHandle[\"stdout\"] => {\n const pipe = state.child.stdio[fd]\n const stream = pipe !== null && pipe !== undefined && \"read\" in pipe\n ? Stream.suspend(() => {\n const method = fd === 1 ? \"stdout\" : fd === 2 ? \"stderr\" : `fd${fd}`\n const error = state.pipeErrors.get(pipe) ?? pipe.errored ?? undefined\n return error !== undefined ?\n Stream.fail(failure(method, error)) :\n NodeStream.fromReadable<Uint8Array, PlatformError.PlatformError>({\n evaluate: () => pipe,\n onError: (cause) => failure(method, cause)\n })\n })\n : Stream.empty\n return Sink.isSink(transform) ? Stream.transduce(stream, transform) : stream\n }\n const stdin = input(physicalFds[0]!, descriptors.stdin)\n const stdout = output(physicalFds[1]!, descriptors.stdout)\n const stderr = output(physicalFds[2]!, descriptors.stderr)\n if (Stream.isStream(descriptors.stdin.stream)) {\n yield* Stream.run(descriptors.stdin.stream, stdin).pipe(Effect.forkScoped)\n }\n const inputs = new Map<number, ChildProcessHandle[\"stdin\"]>()\n const outputs = new Map<number, ChildProcessHandle[\"stdout\"]>()\n for (const { fd, config } of descriptors.additional) {\n if (config.type === \"input\") {\n const sink = input(fd)\n inputs.set(fd, sink)\n if (config.stream !== undefined) yield* Stream.run(config.stream, sink).pipe(Effect.forkScoped)\n } else outputs.set(fd, output(fd, config.sink))\n }\n const handle = makeHandle({\n pid: ProcessId(state.child.pid!),\n exitCode: wait.pipe(Effect.flatMap(([code, signal]) =>\n code === null\n ? Effect.fail(\n failure(\n \"exitCode\",\n new NativeProcessError({\n message: `Process interrupted due to receipt of signal: '${signal}'`,\n signal: String(signal)\n })\n )\n )\n : Effect.succeed(ExitCode(code))\n )),\n isRunning: Effect.sync(() => !state.ended),\n kill,\n stdin,\n stdout,\n stderr,\n all: Stream.merge(stdout, stderr),\n getInputFd: (fd) => inputs.get(fd) ?? Sink.drain,\n getOutputFd: (fd) => outputs.get(fd) ?? Stream.empty,\n unref: Effect.sync(() => {\n state.referenced = false\n state.child.unref()\n return Effect.sync(() => {\n state.referenced = true\n state.child.ref()\n })\n })\n })\n if (owner && process.platform === \"win32\") ownerDescriptors.set(handle, standardFds)\n return handle\n })\n"],
5
+ "mappings": "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",
6
6
  "names": ["promise", "state", "fd", "signal"]
7
7
  }
@@ -5,7 +5,7 @@
5
5
  import { type Lifecycle } from "@smthrs/kernel/ContainedSpawner";
6
6
  import * as Effect from "effect/Effect";
7
7
  import type * as PlatformError from "effect/PlatformError";
8
- import type * as ChildProcess from "effect/unstable/process/ChildProcess";
8
+ import type * as ChildProcess from "effect/process/ChildProcess";
9
9
  /**
10
10
  * An operating-system identity observed in a group.
11
11
  * @private
@@ -1 +1 @@
1
- {"version":3,"file":"ProcessCleanup.d.ts","sourceRoot":"","sources":["../../../src/internal/ProcessCleanup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,iCAAiC,CAAA;AAEhF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,aAAa,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,KAAK,YAAY,MAAM,sCAAsC,CAAA;AAKzE;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACxC;AAED;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,GAAG,SAAS,CAAA;IACzD;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAC3C;AAED;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,MAAM,CAAA;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,MAAM,GACjB,SAAS,YAAY,CAAC,WAAW,EACjC,WAAU,YAAY,CAAC,WAAgB,KACtC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,aAAa,CAsBhD,CAAA;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,KAAG,SAA+C,CAAA"}
1
+ {"version":3,"file":"ProcessCleanup.d.ts","sourceRoot":"","sources":["../../../src/internal/ProcessCleanup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,iCAAiC,CAAA;AAEhF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,aAAa,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,KAAK,YAAY,MAAM,6BAA6B,CAAA;AAKhE;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACxC;AAED;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,GAAG,SAAS,CAAA;IACzD;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAC3C;AAED;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,MAAM,CAAA;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,MAAM,GACjB,SAAS,YAAY,CAAC,WAAW,EACjC,WAAU,YAAY,CAAC,WAAgB,KACtC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,aAAa,CAsBhD,CAAA;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,KAAG,SAA+C,CAAA"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/internal/ProcessCleanup.ts"],
4
- "sourcesContent": ["/**\n * Prepared process ownership and bounded release for Node and Bun hosts.\n * @since 1.0.0\n */\n\nimport { defaultGraceMs, type Lifecycle } from \"@smthrs/kernel/ContainedSpawner\"\nimport * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport type * as PlatformError from \"effect/PlatformError\"\nimport type * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport { constants } from \"node:os\"\nimport { ProcessCleanupPolicyError } from \"./ProcessCleanupPolicyError.ts\"\nimport * as Supervisor from \"./ProcessSupervisor.ts\"\n\n/**\n * An operating-system identity observed in a group.\n * @private\n * @since 1.0.0\n */\nexport interface Member {\n readonly pid: number\n readonly startedAtMs: number\n readonly zombie: boolean\n}\n\n/**\n * One bounded observation, including the host group.\n * @private\n * @since 1.0.0\n */\nexport interface Snapshot {\n readonly ownGroup: number\n readonly members: ReadonlyArray<Member>\n}\n\n/**\n * Observations can shorten or verify cleanup, but never authorize signals.\n * @private\n * @since 1.0.0\n */\nexport interface System {\n readonly platform: string\n readonly snapshot: (pgid: number) => Snapshot | undefined\n /**\n * True only when the kernel proves the group has no process at all. False\n * means occupied or unknown; `snapshot` still decides those.\n */\n readonly vacant: (pgid: number) => boolean\n}\n\n/**\n * A finite termination deadline understood by the private owner.\n * @private\n * @since 1.0.0\n */\nexport interface Policy {\n readonly killSignal: ChildProcess.Signal\n readonly graceMs: number\n}\n\n/**\n * Normalize and validate the deadline before it enters a native timer.\n * @private\n * @since 1.0.0\n */\nexport const policy = (\n options: ChildProcess.KillOptions,\n defaults: ChildProcess.KillOptions = {}\n): Effect.Effect<Policy, PlatformError.PlatformError> =>\n Effect.try({\n try: () => {\n const killSignal = options.killSignal ?? defaults.killSignal ?? \"SIGTERM\"\n if (killSignal === \"SIGSTOP\" || constants.signals[killSignal] === undefined) {\n throw new ProcessCleanupPolicyError({\n code: \"kill_signal_unsupported\",\n message: `Unsupported process termination signal ${killSignal}`\n })\n }\n const graceMs = Duration.toMillis(options.forceKillAfter ?? defaults.forceKillAfter ?? defaultGraceMs)\n // Node/Bun clamp an overflowing timeout to 1 ms. Refuse that policy rather\n // than silently replacing a caller's grace with immediate termination.\n if (!Number.isFinite(graceMs) || graceMs < 0 || graceMs > 2_147_483_647) {\n throw new ProcessCleanupPolicyError({\n code: \"grace_out_of_range\",\n message: \"Process cleanup grace must fit a finite nonnegative native timer\"\n })\n }\n return { killSignal, graceMs }\n },\n catch: (cause) => Supervisor.failure(\"kill\", \"process cleanup policy\", cause)\n })\n\n/**\n * Prepare a live owner before target activation. Windows attaches that owner\n * to a native job; POSIX uses its independently bounded process supervisor.\n * @private\n * @since 1.0.0\n */\nexport const lifecycle = (system: System): Lifecycle => Supervisor.prepare(system, policy)\n"],
4
+ "sourcesContent": ["/**\n * Prepared process ownership and bounded release for Node and Bun hosts.\n * @since 1.0.0\n */\n\nimport { defaultGraceMs, type Lifecycle } from \"@smthrs/kernel/ContainedSpawner\"\nimport * as Duration from \"effect/Duration\"\nimport * as Effect from \"effect/Effect\"\nimport type * as PlatformError from \"effect/PlatformError\"\nimport type * as ChildProcess from \"effect/process/ChildProcess\"\nimport { constants } from \"node:os\"\nimport { ProcessCleanupPolicyError } from \"./ProcessCleanupPolicyError.ts\"\nimport * as Supervisor from \"./ProcessSupervisor.ts\"\n\n/**\n * An operating-system identity observed in a group.\n * @private\n * @since 1.0.0\n */\nexport interface Member {\n readonly pid: number\n readonly startedAtMs: number\n readonly zombie: boolean\n}\n\n/**\n * One bounded observation, including the host group.\n * @private\n * @since 1.0.0\n */\nexport interface Snapshot {\n readonly ownGroup: number\n readonly members: ReadonlyArray<Member>\n}\n\n/**\n * Observations can shorten or verify cleanup, but never authorize signals.\n * @private\n * @since 1.0.0\n */\nexport interface System {\n readonly platform: string\n readonly snapshot: (pgid: number) => Snapshot | undefined\n /**\n * True only when the kernel proves the group has no process at all. False\n * means occupied or unknown; `snapshot` still decides those.\n */\n readonly vacant: (pgid: number) => boolean\n}\n\n/**\n * A finite termination deadline understood by the private owner.\n * @private\n * @since 1.0.0\n */\nexport interface Policy {\n readonly killSignal: ChildProcess.Signal\n readonly graceMs: number\n}\n\n/**\n * Normalize and validate the deadline before it enters a native timer.\n * @private\n * @since 1.0.0\n */\nexport const policy = (\n options: ChildProcess.KillOptions,\n defaults: ChildProcess.KillOptions = {}\n): Effect.Effect<Policy, PlatformError.PlatformError> =>\n Effect.try({\n try: () => {\n const killSignal = options.killSignal ?? defaults.killSignal ?? \"SIGTERM\"\n if (killSignal === \"SIGSTOP\" || constants.signals[killSignal] === undefined) {\n throw new ProcessCleanupPolicyError({\n code: \"kill_signal_unsupported\",\n message: `Unsupported process termination signal ${killSignal}`\n })\n }\n const graceMs = Duration.toMillis(options.forceKillAfter ?? defaults.forceKillAfter ?? defaultGraceMs)\n // Node/Bun clamp an overflowing timeout to 1 ms. Refuse that policy rather\n // than silently replacing a caller's grace with immediate termination.\n if (!Number.isFinite(graceMs) || graceMs < 0 || graceMs > 2_147_483_647) {\n throw new ProcessCleanupPolicyError({\n code: \"grace_out_of_range\",\n message: \"Process cleanup grace must fit a finite nonnegative native timer\"\n })\n }\n return { killSignal, graceMs }\n },\n catch: (cause) => Supervisor.failure(\"kill\", \"process cleanup policy\", cause)\n })\n\n/**\n * Prepare a live owner before target activation. Windows attaches that owner\n * to a native job; POSIX uses its independently bounded process supervisor.\n * @private\n * @since 1.0.0\n */\nexport const lifecycle = (system: System): Lifecycle => Supervisor.prepare(system, policy)\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,8BAA+C;AAC/C,eAA0B;AAC1B,aAAwB;AAGxB,qBAA0B;AAC1B,uCAA0C;AAC1C,iBAA4B;AAqDrB,MAAM,SAAS,CACpB,SACA,WAAqC,CAAC,MAEtC,OAAO,IAAI;AAAA,EACT,KAAK,MAAM;AACT,UAAM,aAAa,QAAQ,cAAc,SAAS,cAAc;AAChE,QAAI,eAAe,aAAa,yBAAU,QAAQ,UAAU,MAAM,QAAW;AAC3E,YAAM,IAAI,2DAA0B;AAAA,QAClC,MAAM;AAAA,QACN,SAAS,0CAA0C,UAAU;AAAA,MAC/D,CAAC;AAAA,IACH;AACA,UAAM,UAAU,SAAS,SAAS,QAAQ,kBAAkB,SAAS,kBAAkB,sCAAc;AAGrG,QAAI,CAAC,OAAO,SAAS,OAAO,KAAK,UAAU,KAAK,UAAU,YAAe;AACvE,YAAM,IAAI,2DAA0B;AAAA,QAClC,MAAM;AAAA,QACN,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AACA,WAAO,EAAE,YAAY,QAAQ;AAAA,EAC/B;AAAA,EACA,OAAO,CAAC,UAAU,WAAW,QAAQ,QAAQ,0BAA0B,KAAK;AAC9E,CAAC;AAQI,MAAM,YAAY,CAAC,WAA8B,WAAW,QAAQ,QAAQ,MAAM;",
6
6
  "names": []
7
7
  }
@@ -6,8 +6,8 @@
6
6
  import type { Lifecycle } from "@smthrs/kernel/ContainedSpawner";
7
7
  import * as Effect from "effect/Effect";
8
8
  import * as PlatformError from "effect/PlatformError";
9
- import * as ChildProcess from "effect/unstable/process/ChildProcess";
10
- import { type ChildProcessHandle, ExitCode } from "effect/unstable/process/ChildProcessSpawner";
9
+ import * as ChildProcess from "effect/process/ChildProcess";
10
+ import { type ChildProcessHandle, ExitCode } from "effect/process/ChildProcessSpawner";
11
11
  import { type Socket } from "node:net";
12
12
  import type { Policy, Snapshot, System } from "./ProcessCleanup.ts";
13
13
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ProcessSupervisor.d.ts","sourceRoot":"","sources":["../../../src/internal/ProcessSupervisor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ProcessSupervisor.d.ts","sourceRoot":"","sources":["../../../src/internal/ProcessSupervisor.ts"],"names":[],"mappings":"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"}
@@ -41,10 +41,10 @@ var Channel = __toESM(require("effect/Channel"), 1);
41
41
  var Clock = __toESM(require("effect/Clock"), 1);
42
42
  var Effect = __toESM(require("effect/Effect"), 1);
43
43
  var PlatformError = __toESM(require("effect/PlatformError"), 1);
44
+ var ChildProcess = __toESM(require("effect/process/ChildProcess"), 1);
45
+ var import_ChildProcessSpawner = require("effect/process/ChildProcessSpawner");
44
46
  var Sink = __toESM(require("effect/Sink"), 1);
45
47
  var Stream = __toESM(require("effect/Stream"), 1);
46
- var ChildProcess = __toESM(require("effect/unstable/process/ChildProcess"), 1);
47
- var import_ChildProcessSpawner = require("effect/unstable/process/ChildProcessSpawner");
48
48
  var import_node_crypto = require("node:crypto");
49
49
  var import_node_fs = require("node:fs");
50
50
  var import_node_net = require("node:net");