@smthrs/platform-node 0.0.0-stage → 1.0.0-rc.3

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 (442) hide show
  1. package/CHANGELOG.md +211 -0
  2. package/LICENSE +21 -0
  3. package/README.md +165 -2
  4. package/bin/darwin-arm64/smithers-jj-export +0 -0
  5. package/bin/darwin-x64/smithers-jj-export +0 -0
  6. package/bin/linux-arm64/smithers-jj-export +0 -0
  7. package/bin/linux-x64/smithers-jj-export +4 -0
  8. package/bin/win32-x64/smithers-jj-export.exe +0 -0
  9. package/dist/cjs/AtomicFileSystem.d.ts +147 -0
  10. package/dist/cjs/AtomicFileSystem.d.ts.map +1 -0
  11. package/dist/cjs/AtomicFileSystem.js +234 -0
  12. package/dist/cjs/AtomicFileSystem.js.map +7 -0
  13. package/dist/cjs/EgressHttpClient.d.ts +106 -0
  14. package/dist/cjs/EgressHttpClient.d.ts.map +1 -0
  15. package/dist/cjs/EgressHttpClient.js +202 -0
  16. package/dist/cjs/EgressHttpClient.js.map +7 -0
  17. package/dist/cjs/HostLiveness.d.ts +81 -0
  18. package/dist/cjs/HostLiveness.d.ts.map +1 -0
  19. package/dist/cjs/HostLiveness.js +49 -0
  20. package/dist/cjs/HostLiveness.js.map +7 -0
  21. package/dist/cjs/NodeHost.d.ts +145 -0
  22. package/dist/cjs/NodeHost.d.ts.map +1 -0
  23. package/dist/cjs/NodeHost.js +128 -0
  24. package/dist/cjs/NodeHost.js.map +7 -0
  25. package/dist/cjs/ProcessConfinement.d.ts +61 -0
  26. package/dist/cjs/ProcessConfinement.d.ts.map +1 -0
  27. package/dist/cjs/ProcessConfinement.js +287 -0
  28. package/dist/cjs/ProcessConfinement.js.map +7 -0
  29. package/dist/cjs/ProcessReaper.d.ts +457 -0
  30. package/dist/cjs/ProcessReaper.d.ts.map +1 -0
  31. package/dist/cjs/ProcessReaper.js +349 -0
  32. package/dist/cjs/ProcessReaper.js.map +7 -0
  33. package/dist/cjs/ProcessSandbox.d.ts +377 -0
  34. package/dist/cjs/ProcessSandbox.d.ts.map +1 -0
  35. package/dist/cjs/ProcessSandbox.js +759 -0
  36. package/dist/cjs/ProcessSandbox.js.map +7 -0
  37. package/dist/cjs/ScopedProcess.d.ts +65 -0
  38. package/dist/cjs/ScopedProcess.d.ts.map +1 -0
  39. package/dist/cjs/ScopedProcess.js +87 -0
  40. package/dist/cjs/ScopedProcess.js.map +7 -0
  41. package/dist/cjs/index.d.ts +38 -0
  42. package/dist/cjs/index.d.ts.map +1 -0
  43. package/dist/cjs/index.js +47 -0
  44. package/dist/cjs/index.js.map +7 -0
  45. package/dist/cjs/internal/AtomicFileSystemExecutable.d.ts +61 -0
  46. package/dist/cjs/internal/AtomicFileSystemExecutable.d.ts.map +1 -0
  47. package/dist/cjs/internal/AtomicFileSystemExecutable.js +250 -0
  48. package/dist/cjs/internal/AtomicFileSystemExecutable.js.map +7 -0
  49. package/dist/cjs/internal/AtomicFileSystemProtocol.d.ts +78 -0
  50. package/dist/cjs/internal/AtomicFileSystemProtocol.d.ts.map +1 -0
  51. package/dist/cjs/internal/AtomicFileSystemProtocol.js +382 -0
  52. package/dist/cjs/internal/AtomicFileSystemProtocol.js.map +7 -0
  53. package/dist/cjs/internal/AtomicFileSystemTransport.d.ts +44 -0
  54. package/dist/cjs/internal/AtomicFileSystemTransport.d.ts.map +1 -0
  55. package/dist/cjs/internal/AtomicFileSystemTransport.js +197 -0
  56. package/dist/cjs/internal/AtomicFileSystemTransport.js.map +7 -0
  57. package/dist/cjs/internal/AtomicHelperError.d.ts +35 -0
  58. package/dist/cjs/internal/AtomicHelperError.d.ts.map +1 -0
  59. package/dist/cjs/internal/AtomicHelperError.js +45 -0
  60. package/dist/cjs/internal/AtomicHelperError.js.map +7 -0
  61. package/dist/cjs/internal/AtomicHelperFault.d.ts +59 -0
  62. package/dist/cjs/internal/AtomicHelperFault.d.ts.map +1 -0
  63. package/dist/cjs/internal/AtomicHelperFault.js +66 -0
  64. package/dist/cjs/internal/AtomicHelperFault.js.map +7 -0
  65. package/dist/cjs/internal/PipedProcess.d.ts +35 -0
  66. package/dist/cjs/internal/PipedProcess.d.ts.map +1 -0
  67. package/dist/cjs/internal/PipedProcess.js +309 -0
  68. package/dist/cjs/internal/PipedProcess.js.map +7 -0
  69. package/dist/cjs/internal/ProcSnapshot.d.ts +85 -0
  70. package/dist/cjs/internal/ProcSnapshot.d.ts.map +1 -0
  71. package/dist/cjs/internal/ProcSnapshot.js +123 -0
  72. package/dist/cjs/internal/ProcSnapshot.js.map +7 -0
  73. package/dist/cjs/internal/ProcessCleanup.d.ts +64 -0
  74. package/dist/cjs/internal/ProcessCleanup.d.ts.map +1 -0
  75. package/dist/cjs/internal/ProcessCleanup.js +62 -0
  76. package/dist/cjs/internal/ProcessCleanup.js.map +7 -0
  77. package/dist/cjs/internal/ProcessCleanupPolicyError.d.ts +30 -0
  78. package/dist/cjs/internal/ProcessCleanupPolicyError.d.ts.map +1 -0
  79. package/dist/cjs/internal/ProcessCleanupPolicyError.js +33 -0
  80. package/dist/cjs/internal/ProcessCleanupPolicyError.js.map +7 -0
  81. package/dist/cjs/internal/ProcessFault.d.ts +63 -0
  82. package/dist/cjs/internal/ProcessFault.d.ts.map +1 -0
  83. package/dist/cjs/internal/ProcessFault.js +63 -0
  84. package/dist/cjs/internal/ProcessFault.js.map +7 -0
  85. package/dist/cjs/internal/ProcessSupervisor.d.ts +141 -0
  86. package/dist/cjs/internal/ProcessSupervisor.d.ts.map +1 -0
  87. package/dist/cjs/internal/ProcessSupervisor.js +682 -0
  88. package/dist/cjs/internal/ProcessSupervisor.js.map +7 -0
  89. package/dist/cjs/internal/SupervisorProgram.d.ts +9 -0
  90. package/dist/cjs/internal/SupervisorProgram.d.ts.map +1 -0
  91. package/dist/cjs/internal/SupervisorProgram.js +306 -0
  92. package/dist/cjs/internal/SupervisorProgram.js.map +7 -0
  93. package/dist/cjs/internal/WindowsProcessJob.d.ts +39 -0
  94. package/dist/cjs/internal/WindowsProcessJob.d.ts.map +1 -0
  95. package/dist/cjs/internal/WindowsProcessJob.js +113 -0
  96. package/dist/cjs/internal/WindowsProcessJob.js.map +7 -0
  97. package/dist/cjs/package.json +1 -0
  98. package/dist/esm/AtomicFileSystem.d.ts +147 -0
  99. package/dist/esm/AtomicFileSystem.d.ts.map +1 -0
  100. package/dist/esm/AtomicFileSystem.js +290 -0
  101. package/dist/esm/AtomicFileSystem.js.map +1 -0
  102. package/dist/esm/EgressHttpClient.d.ts +106 -0
  103. package/dist/esm/EgressHttpClient.d.ts.map +1 -0
  104. package/dist/esm/EgressHttpClient.js +259 -0
  105. package/dist/esm/EgressHttpClient.js.map +1 -0
  106. package/dist/esm/HostLiveness.d.ts +81 -0
  107. package/dist/esm/HostLiveness.d.ts.map +1 -0
  108. package/dist/esm/HostLiveness.js +68 -0
  109. package/dist/esm/HostLiveness.js.map +1 -0
  110. package/dist/esm/NodeHost.d.ts +145 -0
  111. package/dist/esm/NodeHost.d.ts.map +1 -0
  112. package/dist/esm/NodeHost.js +162 -0
  113. package/dist/esm/NodeHost.js.map +1 -0
  114. package/dist/esm/ProcessConfinement.d.ts +61 -0
  115. package/dist/esm/ProcessConfinement.d.ts.map +1 -0
  116. package/dist/esm/ProcessConfinement.js +311 -0
  117. package/dist/esm/ProcessConfinement.js.map +1 -0
  118. package/dist/esm/ProcessReaper.d.ts +457 -0
  119. package/dist/esm/ProcessReaper.d.ts.map +1 -0
  120. package/dist/esm/ProcessReaper.js +711 -0
  121. package/dist/esm/ProcessReaper.js.map +1 -0
  122. package/dist/esm/ProcessSandbox.d.ts +377 -0
  123. package/dist/esm/ProcessSandbox.d.ts.map +1 -0
  124. package/dist/esm/ProcessSandbox.js +1014 -0
  125. package/dist/esm/ProcessSandbox.js.map +1 -0
  126. package/dist/esm/ScopedProcess.d.ts +65 -0
  127. package/dist/esm/ScopedProcess.d.ts.map +1 -0
  128. package/dist/esm/ScopedProcess.js +66 -0
  129. package/dist/esm/ScopedProcess.js.map +1 -0
  130. package/dist/esm/index.d.ts +38 -0
  131. package/dist/esm/index.d.ts.map +1 -0
  132. package/dist/esm/index.js +38 -0
  133. package/dist/esm/index.js.map +1 -0
  134. package/dist/esm/internal/AtomicFileSystemExecutable.d.ts +61 -0
  135. package/dist/esm/internal/AtomicFileSystemExecutable.d.ts.map +1 -0
  136. package/dist/esm/internal/AtomicFileSystemExecutable.js +350 -0
  137. package/dist/esm/internal/AtomicFileSystemExecutable.js.map +1 -0
  138. package/dist/esm/internal/AtomicFileSystemProtocol.d.ts +78 -0
  139. package/dist/esm/internal/AtomicFileSystemProtocol.d.ts.map +1 -0
  140. package/dist/esm/internal/AtomicFileSystemProtocol.js +412 -0
  141. package/dist/esm/internal/AtomicFileSystemProtocol.js.map +1 -0
  142. package/dist/esm/internal/AtomicFileSystemTransport.d.ts +44 -0
  143. package/dist/esm/internal/AtomicFileSystemTransport.d.ts.map +1 -0
  144. package/dist/esm/internal/AtomicFileSystemTransport.js +242 -0
  145. package/dist/esm/internal/AtomicFileSystemTransport.js.map +1 -0
  146. package/dist/esm/internal/AtomicHelperError.d.ts +35 -0
  147. package/dist/esm/internal/AtomicHelperError.d.ts.map +1 -0
  148. package/dist/esm/internal/AtomicHelperError.js +39 -0
  149. package/dist/esm/internal/AtomicHelperError.js.map +1 -0
  150. package/dist/esm/internal/AtomicHelperFault.d.ts +59 -0
  151. package/dist/esm/internal/AtomicHelperFault.d.ts.map +1 -0
  152. package/dist/esm/internal/AtomicHelperFault.js +66 -0
  153. package/dist/esm/internal/AtomicHelperFault.js.map +1 -0
  154. package/dist/esm/internal/PipedProcess.d.ts +35 -0
  155. package/dist/esm/internal/PipedProcess.d.ts.map +1 -0
  156. package/dist/esm/internal/PipedProcess.js +321 -0
  157. package/dist/esm/internal/PipedProcess.js.map +1 -0
  158. package/dist/esm/internal/ProcSnapshot.d.ts +85 -0
  159. package/dist/esm/internal/ProcSnapshot.d.ts.map +1 -0
  160. package/dist/esm/internal/ProcSnapshot.js +188 -0
  161. package/dist/esm/internal/ProcSnapshot.js.map +1 -0
  162. package/dist/esm/internal/ProcessCleanup.d.ts +64 -0
  163. package/dist/esm/internal/ProcessCleanup.d.ts.map +1 -0
  164. package/dist/esm/internal/ProcessCleanup.js +45 -0
  165. package/dist/esm/internal/ProcessCleanup.js.map +1 -0
  166. package/dist/esm/internal/ProcessCleanupPolicyError.d.ts +30 -0
  167. package/dist/esm/internal/ProcessCleanupPolicyError.d.ts.map +1 -0
  168. package/dist/esm/internal/ProcessCleanupPolicyError.js +25 -0
  169. package/dist/esm/internal/ProcessCleanupPolicyError.js.map +1 -0
  170. package/dist/esm/internal/ProcessFault.d.ts +63 -0
  171. package/dist/esm/internal/ProcessFault.d.ts.map +1 -0
  172. package/dist/esm/internal/ProcessFault.js +64 -0
  173. package/dist/esm/internal/ProcessFault.js.map +1 -0
  174. package/dist/esm/internal/ProcessSupervisor.d.ts +141 -0
  175. package/dist/esm/internal/ProcessSupervisor.d.ts.map +1 -0
  176. package/dist/esm/internal/ProcessSupervisor.js +682 -0
  177. package/dist/esm/internal/ProcessSupervisor.js.map +1 -0
  178. package/dist/esm/internal/SupervisorProgram.d.ts +9 -0
  179. package/dist/esm/internal/SupervisorProgram.d.ts.map +1 -0
  180. package/dist/esm/internal/SupervisorProgram.js +290 -0
  181. package/dist/esm/internal/SupervisorProgram.js.map +1 -0
  182. package/dist/esm/internal/WindowsProcessJob.d.ts +39 -0
  183. package/dist/esm/internal/WindowsProcessJob.d.ts.map +1 -0
  184. package/dist/esm/internal/WindowsProcessJob.js +115 -0
  185. package/dist/esm/internal/WindowsProcessJob.js.map +1 -0
  186. package/dist/vendor/adapters.json +1757 -0
  187. package/dist/vendor/chunk-2Y7WC4KU.js +109 -0
  188. package/dist/vendor/chunk-3GR5VZ2M.js +118 -0
  189. package/dist/vendor/chunk-42QKY6T6.js +40 -0
  190. package/dist/vendor/chunk-54RLBMTZ.js +61 -0
  191. package/dist/vendor/chunk-5ZMHBXIK.js +55 -0
  192. package/dist/vendor/chunk-6ME2W7TF.js +28 -0
  193. package/dist/vendor/chunk-AMQKOQNT.js +34 -0
  194. package/dist/vendor/chunk-B3FZ67QU.js +59 -0
  195. package/dist/vendor/chunk-BKTBUBQY.js +437 -0
  196. package/dist/vendor/chunk-DS7GF46S.js +34 -0
  197. package/dist/vendor/chunk-DVOAHXPG.js +104 -0
  198. package/dist/vendor/chunk-DWSHTSVX.js +229 -0
  199. package/dist/vendor/chunk-EGBNVEEW.js +14 -0
  200. package/dist/vendor/chunk-EZWY45HP.js +18 -0
  201. package/dist/vendor/chunk-FNZOJHSV.js +71 -0
  202. package/dist/vendor/chunk-GXCHI7BM.js +131 -0
  203. package/dist/vendor/chunk-IIJ52ZU6.js +20 -0
  204. package/dist/vendor/chunk-J7A4CZDO.js +92 -0
  205. package/dist/vendor/chunk-JG5TTWPT.js +85 -0
  206. package/dist/vendor/chunk-JJ24ARBQ.js +394 -0
  207. package/dist/vendor/chunk-KOZMM5PZ.js +426 -0
  208. package/dist/vendor/chunk-KUUXSID7.js +18 -0
  209. package/dist/vendor/chunk-LEUPYT3Q.js +36 -0
  210. package/dist/vendor/chunk-M32LDTW7.js +18 -0
  211. package/dist/vendor/chunk-M7CLUILA.js +0 -0
  212. package/dist/vendor/chunk-MBSKBQ5D.js +21 -0
  213. package/dist/vendor/chunk-MLKGABMK.js +9 -0
  214. package/dist/vendor/chunk-NBVRTDTV.js +129 -0
  215. package/dist/vendor/chunk-OXNGN5H7.js +81 -0
  216. package/dist/vendor/chunk-QPLDT6A6.js +564 -0
  217. package/dist/vendor/chunk-QYBKAIA6.js +18 -0
  218. package/dist/vendor/chunk-RO2R4MJH.js +62 -0
  219. package/dist/vendor/chunk-SIR62W3Y.js +398 -0
  220. package/dist/vendor/chunk-TFIKFFSC.js +26 -0
  221. package/dist/vendor/chunk-TXKLEBBL.js +143 -0
  222. package/dist/vendor/chunk-UFH5UUZK.js +48 -0
  223. package/dist/vendor/chunk-VANKHF46.js +96 -0
  224. package/dist/vendor/chunk-VZBDV3TV.js +18 -0
  225. package/dist/vendor/chunk-XDKNKFBW.js +22 -0
  226. package/dist/vendor/chunk-XH4U3JSV.js +249 -0
  227. package/dist/vendor/chunk-Y35ZAO5U.js +53 -0
  228. package/dist/vendor/chunk-ZJMRMY57.js +0 -0
  229. package/dist/vendor/cjs/node/NodeChildProcessSpawner.d.ts +7 -0
  230. package/dist/vendor/cjs/node/NodeChildProcessSpawner.js +1 -0
  231. package/dist/vendor/cjs/node/NodeClusterHttp.d.ts +63 -0
  232. package/dist/vendor/cjs/node/NodeClusterHttp.js +1 -0
  233. package/dist/vendor/cjs/node/NodeClusterSocket.d.ts +79 -0
  234. package/dist/vendor/cjs/node/NodeClusterSocket.js +1 -0
  235. package/dist/vendor/cjs/node/NodeCrypto.d.ts +10 -0
  236. package/dist/vendor/cjs/node/NodeCrypto.js +1 -0
  237. package/dist/vendor/cjs/node/NodeFileSystem.d.ts +10 -0
  238. package/dist/vendor/cjs/node/NodeFileSystem.js +1 -0
  239. package/dist/vendor/cjs/node/NodeHttpClient.d.ts +179 -0
  240. package/dist/vendor/cjs/node/NodeHttpClient.js +1 -0
  241. package/dist/vendor/cjs/node/NodeHttpIncomingMessage.d.ts +62 -0
  242. package/dist/vendor/cjs/node/NodeHttpIncomingMessage.js +1 -0
  243. package/dist/vendor/cjs/node/NodeHttpPlatform.d.ts +36 -0
  244. package/dist/vendor/cjs/node/NodeHttpPlatform.js +1 -0
  245. package/dist/vendor/cjs/node/NodeHttpServer.d.ts +120 -0
  246. package/dist/vendor/cjs/node/NodeHttpServer.js +1 -0
  247. package/dist/vendor/cjs/node/NodeHttpServerRequest.d.ts +29 -0
  248. package/dist/vendor/cjs/node/NodeHttpServerRequest.js +1 -0
  249. package/dist/vendor/cjs/node/NodeMultipart.d.ts +46 -0
  250. package/dist/vendor/cjs/node/NodeMultipart.js +1 -0
  251. package/dist/vendor/cjs/node/NodeMultipartParser.d.ts +97 -0
  252. package/dist/vendor/cjs/node/NodeMultipartParser.js +1 -0
  253. package/dist/vendor/cjs/node/NodePath.d.ts +27 -0
  254. package/dist/vendor/cjs/node/NodePath.js +1 -0
  255. package/dist/vendor/cjs/node/NodeRedis.d.ts +74 -0
  256. package/dist/vendor/cjs/node/NodeRedis.js +1 -0
  257. package/dist/vendor/cjs/node/NodeRuntime.d.ts +85 -0
  258. package/dist/vendor/cjs/node/NodeRuntime.js +1 -0
  259. package/dist/vendor/cjs/node/NodeServices.d.ts +34 -0
  260. package/dist/vendor/cjs/node/NodeServices.js +1 -0
  261. package/dist/vendor/cjs/node/NodeSink.d.ts +8 -0
  262. package/dist/vendor/cjs/node/NodeSink.js +1 -0
  263. package/dist/vendor/cjs/node/NodeSocket.d.ts +38 -0
  264. package/dist/vendor/cjs/node/NodeSocket.js +1 -0
  265. package/dist/vendor/cjs/node/NodeSocketServer.d.ts +8 -0
  266. package/dist/vendor/cjs/node/NodeSocketServer.js +1 -0
  267. package/dist/vendor/cjs/node/NodeStdio.d.ts +11 -0
  268. package/dist/vendor/cjs/node/NodeStdio.js +1 -0
  269. package/dist/vendor/cjs/node/NodeStream.d.ts +8 -0
  270. package/dist/vendor/cjs/node/NodeStream.js +1 -0
  271. package/dist/vendor/cjs/node/NodeTerminal.d.ts +21 -0
  272. package/dist/vendor/cjs/node/NodeTerminal.js +1 -0
  273. package/dist/vendor/cjs/node/NodeWorker.d.ts +22 -0
  274. package/dist/vendor/cjs/node/NodeWorker.js +1 -0
  275. package/dist/vendor/cjs/node/NodeWorkerRunner.d.ts +12 -0
  276. package/dist/vendor/cjs/node/NodeWorkerRunner.js +1 -0
  277. package/dist/vendor/cjs/node/Undici.d.ts +28 -0
  278. package/dist/vendor/cjs/node/Undici.js +1 -0
  279. package/dist/vendor/cjs/node/index.d.ts +100 -0
  280. package/dist/vendor/cjs/node/index.js +1 -0
  281. package/dist/vendor/cjs/node-shared/NodeChildProcessSpawner.d.ts +57 -0
  282. package/dist/vendor/cjs/node-shared/NodeChildProcessSpawner.js +1 -0
  283. package/dist/vendor/cjs/node-shared/NodeClusterSocket.d.ts +22 -0
  284. package/dist/vendor/cjs/node-shared/NodeClusterSocket.js +1 -0
  285. package/dist/vendor/cjs/node-shared/NodeCrypto.d.ts +27 -0
  286. package/dist/vendor/cjs/node-shared/NodeCrypto.js +1 -0
  287. package/dist/vendor/cjs/node-shared/NodeFileSystem.d.ts +11 -0
  288. package/dist/vendor/cjs/node-shared/NodeFileSystem.js +1 -0
  289. package/dist/vendor/cjs/node-shared/NodeHttpCompression.d.ts +36 -0
  290. package/dist/vendor/cjs/node-shared/NodeHttpCompression.js +1 -0
  291. package/dist/vendor/cjs/node-shared/NodePath.d.ts +27 -0
  292. package/dist/vendor/cjs/node-shared/NodePath.js +1 -0
  293. package/dist/vendor/cjs/node-shared/NodeRuntime.d.ts +47 -0
  294. package/dist/vendor/cjs/node-shared/NodeRuntime.js +1 -0
  295. package/dist/vendor/cjs/node-shared/NodeSink.d.ts +69 -0
  296. package/dist/vendor/cjs/node-shared/NodeSink.js +1 -0
  297. package/dist/vendor/cjs/node-shared/NodeSocket.d.ts +134 -0
  298. package/dist/vendor/cjs/node-shared/NodeSocket.js +1 -0
  299. package/dist/vendor/cjs/node-shared/NodeSocketServer.d.ts +80 -0
  300. package/dist/vendor/cjs/node-shared/NodeSocketServer.js +1 -0
  301. package/dist/vendor/cjs/node-shared/NodeStdio.d.ts +12 -0
  302. package/dist/vendor/cjs/node-shared/NodeStdio.js +1 -0
  303. package/dist/vendor/cjs/node-shared/NodeStream.d.ts +179 -0
  304. package/dist/vendor/cjs/node-shared/NodeStream.js +1 -0
  305. package/dist/vendor/cjs/node-shared/NodeTerminal.d.ts +22 -0
  306. package/dist/vendor/cjs/node-shared/NodeTerminal.js +1 -0
  307. package/dist/vendor/cjs/node-shared/index.d.ts +56 -0
  308. package/dist/vendor/cjs/node-shared/index.js +1 -0
  309. package/dist/vendor/cjs/node-shared/internal/nodeChildProcessSpawner.d.ts +4 -0
  310. package/dist/vendor/cjs/node-shared/internal/nodeChildProcessSpawner.js +1 -0
  311. package/dist/vendor/cjs/node-shared/internal/utils.d.ts +2 -0
  312. package/dist/vendor/cjs/node-shared/internal/utils.js +1 -0
  313. package/dist/vendor/cjs/package.json +1 -0
  314. package/dist/vendor/cjs/platform-node-shared.d.ts +56 -0
  315. package/dist/vendor/cjs/platform-node-shared.js +1 -0
  316. package/dist/vendor/cjs/platform-node.d.ts +100 -0
  317. package/dist/vendor/cjs/platform-node.js +1 -0
  318. package/dist/vendor/node/LICENSE +21 -0
  319. package/dist/vendor/node/NodeChildProcessSpawner.d.ts +7 -0
  320. package/dist/vendor/node/NodeChildProcessSpawner.js +14 -0
  321. package/dist/vendor/node/NodeClusterHttp.d.ts +63 -0
  322. package/dist/vendor/node/NodeClusterHttp.js +43 -0
  323. package/dist/vendor/node/NodeClusterSocket.d.ts +79 -0
  324. package/dist/vendor/node/NodeClusterSocket.js +30 -0
  325. package/dist/vendor/node/NodeCrypto.d.ts +10 -0
  326. package/dist/vendor/node/NodeCrypto.js +8 -0
  327. package/dist/vendor/node/NodeFileSystem.d.ts +10 -0
  328. package/dist/vendor/node/NodeFileSystem.js +9 -0
  329. package/dist/vendor/node/NodeHttpClient.d.ts +179 -0
  330. package/dist/vendor/node/NodeHttpClient.js +46 -0
  331. package/dist/vendor/node/NodeHttpIncomingMessage.d.ts +62 -0
  332. package/dist/vendor/node/NodeHttpIncomingMessage.js +10 -0
  333. package/dist/vendor/node/NodeHttpPlatform.d.ts +36 -0
  334. package/dist/vendor/node/NodeHttpPlatform.js +16 -0
  335. package/dist/vendor/node/NodeHttpServer.d.ts +120 -0
  336. package/dist/vendor/node/NodeHttpServer.js +46 -0
  337. package/dist/vendor/node/NodeHttpServerRequest.d.ts +29 -0
  338. package/dist/vendor/node/NodeHttpServerRequest.js +9 -0
  339. package/dist/vendor/node/NodeMultipart.d.ts +46 -0
  340. package/dist/vendor/node/NodeMultipart.js +15 -0
  341. package/dist/vendor/node/NodeMultipartParser.d.ts +97 -0
  342. package/dist/vendor/node/NodeMultipartParser.js +11 -0
  343. package/dist/vendor/node/NodePath.d.ts +27 -0
  344. package/dist/vendor/node/NodePath.js +12 -0
  345. package/dist/vendor/node/NodeRedis.d.ts +74 -0
  346. package/dist/vendor/node/NodeRedis.js +11 -0
  347. package/dist/vendor/node/NodeRuntime.d.ts +85 -0
  348. package/dist/vendor/node/NodeRuntime.js +8 -0
  349. package/dist/vendor/node/NodeServices.d.ts +34 -0
  350. package/dist/vendor/node/NodeServices.js +23 -0
  351. package/dist/vendor/node/NodeSink.d.ts +8 -0
  352. package/dist/vendor/node/NodeSink.js +12 -0
  353. package/dist/vendor/node/NodeSocket.d.ts +38 -0
  354. package/dist/vendor/node/NodeSocket.js +31 -0
  355. package/dist/vendor/node/NodeSocketServer.d.ts +8 -0
  356. package/dist/vendor/node/NodeSocketServer.js +21 -0
  357. package/dist/vendor/node/NodeStdio.d.ts +11 -0
  358. package/dist/vendor/node/NodeStdio.js +10 -0
  359. package/dist/vendor/node/NodeStream.d.ts +8 -0
  360. package/dist/vendor/node/NodeStream.js +27 -0
  361. package/dist/vendor/node/NodeTerminal.d.ts +21 -0
  362. package/dist/vendor/node/NodeTerminal.js +10 -0
  363. package/dist/vendor/node/NodeWorker.d.ts +22 -0
  364. package/dist/vendor/node/NodeWorker.js +9 -0
  365. package/dist/vendor/node/NodeWorkerRunner.d.ts +12 -0
  366. package/dist/vendor/node/NodeWorkerRunner.js +7 -0
  367. package/dist/vendor/node/Undici.d.ts +28 -0
  368. package/dist/vendor/node/Undici.js +9 -0
  369. package/dist/vendor/node/index.d.ts +100 -0
  370. package/dist/vendor/node/index.js +115 -0
  371. package/dist/vendor/node-shared/LICENSE +21 -0
  372. package/dist/vendor/node-shared/NodeChildProcessSpawner.d.ts +57 -0
  373. package/dist/vendor/node-shared/NodeChildProcessSpawner.js +13 -0
  374. package/dist/vendor/node-shared/NodeClusterSocket.d.ts +22 -0
  375. package/dist/vendor/node-shared/NodeClusterSocket.js +11 -0
  376. package/dist/vendor/node-shared/NodeCrypto.d.ts +27 -0
  377. package/dist/vendor/node-shared/NodeCrypto.js +9 -0
  378. package/dist/vendor/node-shared/NodeFileSystem.d.ts +11 -0
  379. package/dist/vendor/node-shared/NodeFileSystem.js +8 -0
  380. package/dist/vendor/node-shared/NodeHttpCompression.d.ts +36 -0
  381. package/dist/vendor/node-shared/NodeHttpCompression.js +13 -0
  382. package/dist/vendor/node-shared/NodePath.d.ts +27 -0
  383. package/dist/vendor/node-shared/NodePath.js +11 -0
  384. package/dist/vendor/node-shared/NodeRuntime.d.ts +47 -0
  385. package/dist/vendor/node-shared/NodeRuntime.js +7 -0
  386. package/dist/vendor/node-shared/NodeSink.d.ts +69 -0
  387. package/dist/vendor/node-shared/NodeSink.js +11 -0
  388. package/dist/vendor/node-shared/NodeSocket.d.ts +134 -0
  389. package/dist/vendor/node-shared/NodeSocket.js +23 -0
  390. package/dist/vendor/node-shared/NodeSocketServer.d.ts +80 -0
  391. package/dist/vendor/node-shared/NodeSocketServer.js +20 -0
  392. package/dist/vendor/node-shared/NodeStdio.d.ts +12 -0
  393. package/dist/vendor/node-shared/NodeStdio.js +9 -0
  394. package/dist/vendor/node-shared/NodeStream.d.ts +179 -0
  395. package/dist/vendor/node-shared/NodeStream.js +26 -0
  396. package/dist/vendor/node-shared/NodeTerminal.d.ts +22 -0
  397. package/dist/vendor/node-shared/NodeTerminal.js +9 -0
  398. package/dist/vendor/node-shared/index.d.ts +56 -0
  399. package/dist/vendor/node-shared/index.js +58 -0
  400. package/dist/vendor/node-shared/internal/nodeChildProcessSpawner.d.ts +4 -0
  401. package/dist/vendor/node-shared/internal/nodeChildProcessSpawner.js +7 -0
  402. package/dist/vendor/node-shared/internal/utils.d.ts +2 -0
  403. package/dist/vendor/node-shared/internal/utils.js +7 -0
  404. package/dist/vendor/platform-node-shared.d.ts +56 -0
  405. package/dist/vendor/platform-node-shared.js +58 -0
  406. package/dist/vendor/platform-node.d.ts +100 -0
  407. package/dist/vendor/platform-node.js +115 -0
  408. package/docs/README.md +129 -0
  409. package/docs/api.md +552 -0
  410. package/docs/concepts/descriptor-relative-filesystem.md +181 -0
  411. package/docs/concepts/host-bundle.md +125 -0
  412. package/docs/concepts/process-containment.md +182 -0
  413. package/docs/guides/answer-run-ownership.md +87 -0
  414. package/docs/guides/configure-the-filesystem-helper.md +152 -0
  415. package/docs/guides/contain-child-processes.md +139 -0
  416. package/docs/guides/match-files-with-glob.md +151 -0
  417. package/docs/installation.md +204 -0
  418. package/docs/quickstart.md +147 -0
  419. package/docs/troubleshooting.md +130 -0
  420. package/package.json +202 -3
  421. package/src/AtomicFileSystem.ts +415 -0
  422. package/src/EgressHttpClient.ts +327 -0
  423. package/src/HostLiveness.ts +103 -0
  424. package/src/NodeHost.ts +253 -0
  425. package/src/ProcessConfinement.ts +356 -0
  426. package/src/ProcessReaper.ts +951 -0
  427. package/src/ProcessSandbox.ts +1273 -0
  428. package/src/ScopedProcess.ts +123 -0
  429. package/src/index.ts +44 -0
  430. package/src/internal/AtomicFileSystemExecutable.ts +379 -0
  431. package/src/internal/AtomicFileSystemProtocol.ts +502 -0
  432. package/src/internal/AtomicFileSystemTransport.ts +272 -0
  433. package/src/internal/AtomicHelperError.ts +50 -0
  434. package/src/internal/AtomicHelperFault.ts +84 -0
  435. package/src/internal/PipedProcess.ts +357 -0
  436. package/src/internal/ProcSnapshot.ts +193 -0
  437. package/src/internal/ProcessCleanup.ts +99 -0
  438. package/src/internal/ProcessCleanupPolicyError.ts +30 -0
  439. package/src/internal/ProcessFault.ts +82 -0
  440. package/src/internal/ProcessSupervisor.ts +801 -0
  441. package/src/internal/SupervisorProgram.ts +289 -0
  442. package/src/internal/WindowsProcessJob.ts +114 -0
@@ -0,0 +1,801 @@
1
+ /**
2
+ * Private parent connection for a POSIX process owner. Effect retains ownership
3
+ * of the caller's streams; separate sockets carry requests and lifetime status.
4
+ * @since 1.0.0
5
+ */
6
+
7
+ import type { Lifecycle } from "@smthrs/kernel/ContainedSpawner"
8
+ import * as Channel from "effect/Channel"
9
+ import * as Clock from "effect/Clock"
10
+ import * as Effect from "effect/Effect"
11
+ import * as PlatformError from "effect/PlatformError"
12
+ import * as Sink from "effect/Sink"
13
+ import * as Stream from "effect/Stream"
14
+ import * as ChildProcess from "effect/unstable/process/ChildProcess"
15
+ import { type ChildProcessHandle, ExitCode, makeHandle } from "effect/unstable/process/ChildProcessSpawner"
16
+ import { randomBytes } from "node:crypto"
17
+ import { chmodSync, mkdtempSync, rmdirSync, rmSync } from "node:fs"
18
+ import { createConnection, createServer, type Socket } from "node:net"
19
+ import { tmpdir } from "node:os"
20
+ import { join, parse, resolve } from "node:path"
21
+ import * as Tls from "node:tls"
22
+ import { standardFdsOf } from "./PipedProcess.ts"
23
+ import type { Policy, Snapshot, System } from "./ProcessCleanup.ts"
24
+ import { NativeProcessError, processFault } from "./ProcessFault.ts"
25
+ import { source } from "./SupervisorProgram.ts"
26
+ import { resolveJobExecutable, WindowsProcessJob } from "./WindowsProcessJob.ts"
27
+
28
+ const startupMs = 5000
29
+ const deliveryMs = 500
30
+ const exitAllowanceMs = 2500
31
+ // Host-wide process observations can exceed a socket-delivery deadline on a
32
+ // loaded machine. Keep verification bounded without mistaking that delay for
33
+ // an unclean exit; an unknown observation still never counts as success.
34
+ const verificationMs = 2500
35
+ const targets = new WeakMap<ChildProcessHandle, Control>()
36
+ // Native owners and sockets keep running when the caller freezes its Clock.
37
+ // Their delivery bounds and cleanup retries must keep running with them.
38
+ const processClock = Clock.Clock.defaultValue()
39
+
40
+ /**
41
+ * Actual service pid, distinct from the owner recorded by the host.
42
+ * @private
43
+ * @since 1.0.0
44
+ */
45
+ export const targetPidOf = (handle: ChildProcessHandle): number | undefined => targets.get(handle)?.targetPid
46
+
47
+ /** Native error fields remain data on the public PlatformError cause. */
48
+ const nativeError = (cause: unknown): Error & { code?: string | undefined; syscall?: string | undefined } => {
49
+ if (cause instanceof Error) return cause
50
+ const value = (cause ?? {}) as Record<string, unknown>
51
+ const text = (field: string): string | undefined => typeof value[field] === "string" ? value[field] : undefined
52
+ return new NativeProcessError({
53
+ message: text("message") ?? "The process supervisor failed",
54
+ code: text("code"),
55
+ errno: typeof value.errno === "number" ? value.errno : undefined,
56
+ reason: text("reason"),
57
+ syscall: text("syscall"),
58
+ path: text("path"),
59
+ signal: text("signal")
60
+ })
61
+ }
62
+
63
+ /**
64
+ * Preserve the platform's errno and signal vocabulary across the private wire.
65
+ * @private
66
+ * @since 1.0.0
67
+ */
68
+ export const failure = (method: string, command: string, cause: unknown): PlatformError.PlatformError => {
69
+ const error = nativeError(cause)
70
+ const tags: Readonly<Record<string, PlatformError.SystemErrorTag>> = {
71
+ ENOENT: "NotFound",
72
+ EACCES: "PermissionDenied",
73
+ EEXIST: "AlreadyExists",
74
+ EISDIR: "BadResource",
75
+ ENOTDIR: "BadResource",
76
+ ELOOP: "BadResource",
77
+ EBUSY: "Busy"
78
+ }
79
+ return PlatformError.systemError({
80
+ _tag: tags[error.code ?? ""] ?? "Unknown",
81
+ module: "ChildProcess",
82
+ method,
83
+ pathOrDescriptor: command,
84
+ syscall: error.syscall,
85
+ description: error.message,
86
+ cause: error
87
+ })
88
+ }
89
+
90
+ /**
91
+ * The observation behind an unverified cleanup, in the message itself: a
92
+ * failure report usually shows only the message, and whether the receipt, the
93
+ * kernel or the snapshot was missing decides host load versus a live survivor.
94
+ * @private
95
+ * @since 1.0.0
96
+ */
97
+ export const unverifiedCleanup = (
98
+ control: {
99
+ readonly fault: unknown
100
+ readonly cleanupFailed: boolean
101
+ readonly cleanupAcknowledged: boolean
102
+ readonly targetDone: boolean
103
+ },
104
+ cleanupRequired: boolean,
105
+ vacant: boolean,
106
+ observed: Snapshot | undefined
107
+ ): string =>
108
+ [
109
+ `target exited: ${control.targetDone}`,
110
+ `cleanup receipt: ${
111
+ control.cleanupFailed
112
+ ? "failed"
113
+ : !cleanupRequired
114
+ ? "not required"
115
+ : control.cleanupAcknowledged
116
+ ? "acknowledged"
117
+ : "missing"
118
+ }`,
119
+ `group vacant: ${vacant}`,
120
+ observed === undefined
121
+ ? "group snapshot: unavailable"
122
+ : `group members: ${
123
+ observed.members.length === 0
124
+ ? "none"
125
+ : observed.members.map((member) => `${member.pid} ${member.zombie ? "zombie" : "live"}`).join(", ")
126
+ }; host group ${observed.ownGroup}`,
127
+ ...(control.fault === undefined
128
+ ? []
129
+ : [`fault: ${String((control.fault as { message?: unknown })?.message ?? control.fault)}`])
130
+ ].join("; ")
131
+
132
+ const promise = <A>() => {
133
+ let resolve!: (value: A) => void
134
+ let reject!: (cause: unknown) => void
135
+ const promise = new Promise<A>((yes, no) => {
136
+ resolve = yes
137
+ reject = no
138
+ })
139
+ // A status may arrive before its Effect consumer starts waiting.
140
+ void promise.catch(() => {})
141
+ return { promise, resolve, reject }
142
+ }
143
+
144
+ const wait = <A>(value: Promise<A>, method: string, command: string) =>
145
+ Effect.tryPromise({ try: () => value, catch: (cause) => failure(method, command, cause) })
146
+
147
+ const bounded = <A, E, R>(effect: Effect.Effect<A, E, R>, millis: number, method: string, command: string) =>
148
+ effect.pipe(
149
+ Effect.timeoutOrElse({
150
+ duration: millis,
151
+ orElse: () =>
152
+ Effect.fail(
153
+ failure(method, command, processFault("supervisor_timeout", `Process supervisor ${method} timed out`))
154
+ )
155
+ }),
156
+ Effect.provideService(Clock.Clock, processClock)
157
+ )
158
+
159
+ /**
160
+ * Standalone application executables do not implement the runtime's eval CLI.
161
+ * Refuse them before spawning rather than recursively launching the application.
162
+ * @private
163
+ * @since 1.0.0
164
+ */
165
+ export const bootstrapArguments = (runtime: { readonly bun: boolean; readonly main: string; readonly sea: boolean }) =>
166
+ runtime.sea || runtime.main.startsWith("/$bunfs/")
167
+ ? Effect.fail(
168
+ failure(
169
+ "spawn",
170
+ process.execPath,
171
+ processFault(
172
+ "runtime_unsupported",
173
+ "Process supervision requires a Node or Bun runtime executable, not a compiled application"
174
+ )
175
+ )
176
+ )
177
+ : Effect.succeed(runtime.bun ? ["--no-env-file", "--config=/dev/null"] : [])
178
+
179
+ const channelEnvironment = "SMITHERS_PROCESS_CHANNEL"
180
+ const tlsOptions = { minVersion: "TLSv1.3", maxVersion: "TLSv1.3", ciphers: "TLS_AES_128_GCM_SHA256" } as const
181
+
182
+ /**
183
+ * Connect an owner to its authenticated private channel.
184
+ * @private
185
+ * @since 1.0.0
186
+ */
187
+ export const connectOwner = (path: string, environment: Readonly<Record<string, string | undefined>>): Socket => {
188
+ const encoded = environment[channelEnvironment]
189
+ if (encoded === undefined) return createConnection(path)
190
+ const channel = JSON.parse(encoded) as { key: string; status: number; requests: number }
191
+ return Tls.connect({
192
+ ...tlsOptions,
193
+ host: "127.0.0.1",
194
+ port: path.endsWith("/r") ? channel.requests : channel.status,
195
+ pskCallback: () => ({ identity: "smithers-owner", psk: Buffer.from(channel.key, "hex") })
196
+ })
197
+ }
198
+
199
+ /**
200
+ * Kept separate from Effect scopes so native socket callbacks only settle
201
+ * promises; none can start an unowned fiber or signal an observed process id.
202
+ * @private
203
+ * @since 1.0.0
204
+ */
205
+ export class Control {
206
+ readonly ready = promise<number>()
207
+ readonly started = promise<void>()
208
+ readonly exited = promise<ExitCode>()
209
+ readonly lost = promise<never>()
210
+ readonly ended = promise<void>()
211
+ readonly requestsReady = promise<void>()
212
+ readonly directory: string
213
+ readonly path: string
214
+ readonly requestPath: string
215
+ readonly server
216
+ readonly requestServer
217
+ readonly listening: Promise<void>
218
+ private readonly key: Buffer | undefined
219
+ private readonly connections = new Set<Socket>()
220
+ socket: Socket | undefined
221
+ requestSocket: Socket | undefined
222
+ targetDone = false
223
+ ownerCreation: unknown
224
+ targetPid: number | undefined
225
+ ownerDone = false
226
+ spawnFailed = false
227
+ cleanupFailed = false
228
+ cleanupAcknowledged = false
229
+ activationSent = false
230
+ closeSent = false
231
+ fault: unknown
232
+ onTargetExit: () => void = () => {}
233
+ onCleanup: () => void = () => {}
234
+ private receivedReady = false
235
+ private receivedStarted = false
236
+ private withdrawn = false
237
+
238
+ constructor(transport: "native" | "tls" = "native") {
239
+ this.key = transport === "tls" || process.platform === "win32" ? randomBytes(32) : undefined
240
+ this.directory = mkdtempSync(this.key === undefined ? "/tmp/sm-p-" : join(tmpdir(), "sm-p-"))
241
+ this.path = `${this.directory}/s`
242
+ this.requestPath = `${this.directory}/r`
243
+ const servers: Array<ReturnType<typeof createServer>> = []
244
+ try {
245
+ chmodSync(this.directory, 0o700)
246
+ this.server = this.makeServer((socket) => this.accept(socket))
247
+ servers.push(this.server)
248
+ this.requestServer = this.makeServer((socket) => this.acceptRequests(socket))
249
+ servers.push(this.requestServer)
250
+ this.listening = Promise.all([
251
+ new Promise<void>((resolve, reject) => {
252
+ this.server.once("error", reject)
253
+ if (this.key === undefined) this.server.listen(this.path, resolve)
254
+ else this.server.listen(0, "127.0.0.1", resolve)
255
+ }),
256
+ new Promise<void>((resolve, reject) => {
257
+ this.requestServer.once("error", reject)
258
+ if (this.key === undefined) this.requestServer.listen(this.requestPath, resolve)
259
+ else this.requestServer.listen(0, "127.0.0.1", resolve)
260
+ })
261
+ ]).then(() => {})
262
+ } catch (cause) {
263
+ for (const server of servers) server.close()
264
+ rmSync(this.directory, { recursive: true, force: true })
265
+ throw cause
266
+ }
267
+ }
268
+
269
+ private makeServer(accept: (socket: Socket) => void): ReturnType<typeof createServer> {
270
+ if (this.key === undefined) return createServer(accept)
271
+ const server = Tls.createServer({
272
+ ...tlsOptions,
273
+ handshakeTimeout: startupMs,
274
+ pskCallback: (_socket, identity) => identity === "smithers-owner" ? this.key! : null
275
+ }, accept)
276
+ server.on("connection", (socket) => {
277
+ this.connections.add(socket)
278
+ socket.once("close", () => this.connections.delete(socket))
279
+ })
280
+ server.on("tlsClientError", (_error, socket) => socket.destroy())
281
+ return server
282
+ }
283
+
284
+ /** Only the trusted owner receives this environment; target configuration replaces it. */
285
+ environment(): Readonly<Record<string, string>> {
286
+ if (this.key === undefined) return {}
287
+ const status = this.server.address() as { port: number }
288
+ const requests = this.requestServer.address() as { port: number }
289
+ return {
290
+ [channelEnvironment]: JSON.stringify({
291
+ key: this.key.toString("hex"),
292
+ status: status.port,
293
+ requests: requests.port
294
+ })
295
+ }
296
+ }
297
+
298
+ /** The test peer uses the same authenticated transport as the isolated owner. */
299
+ connect(requests = false): Socket {
300
+ return connectOwner(requests ? this.requestPath : this.path, this.environment())
301
+ }
302
+
303
+ /** Validate the stored READY after the raw spawn effect has returned its pid. */
304
+ withdraw(pid: number, actual: number): void {
305
+ if (pid !== actual) throw processFault("identity_mismatch", "Wrong supervisor identity")
306
+ this.server.close()
307
+ this.server.unref()
308
+ this.requestServer.close()
309
+ this.requestServer.unref()
310
+ // Node may unlink the owned socket synchronously inside server.close().
311
+ rmSync(this.path, { force: true })
312
+ rmSync(this.requestPath, { force: true })
313
+ rmdirSync(this.directory)
314
+ this.withdrawn = true
315
+ }
316
+
317
+ /** Request EOF asks for cleanup; leave its independent status reader intact. */
318
+ disconnect(): void {
319
+ this.requestSocket?.destroy()
320
+ }
321
+
322
+ dispose(): void {
323
+ for (const socket of this.connections) socket.destroy()
324
+ this.disconnect()
325
+ this.socket?.destroy()
326
+ this.server.close()
327
+ this.requestServer.close()
328
+ if (!this.withdrawn) rmSync(this.directory, { recursive: true, force: true })
329
+ }
330
+
331
+ rawEnded(): void {
332
+ this.ownerDone = true
333
+ // An accepted socket may still have buffered status frames. Its close event
334
+ // rejects missing outcomes only after Node has delivered those frames.
335
+ if (this.socket === undefined) this.closed()
336
+ }
337
+
338
+ write(message: unknown): Promise<void> {
339
+ return new Promise((resolve, reject) => {
340
+ const socket = this.requestSocket
341
+ if (socket === undefined || socket.destroyed || !socket.writable) {
342
+ reject(processFault("control_channel_closed", "Private process control channel closed"))
343
+ return
344
+ }
345
+ const data = `${JSON.stringify(message)}\n`
346
+ if (Buffer.byteLength(data) > 4 * 1024 * 1024) {
347
+ reject(processFault("configuration_too_large", "Process configuration exceeds the private frame limit"))
348
+ return
349
+ }
350
+ socket.write(data, (error) => error ? reject(error) : resolve())
351
+ })
352
+ }
353
+
354
+ private closed(): void {
355
+ this.disconnect()
356
+ const cause = this.fault ??
357
+ processFault("outcome_missing", "Process supervisor closed before reporting its outcome")
358
+ if (this.activationSent && !this.targetDone && !this.spawnFailed) this.lost.reject(cause)
359
+ this.ready.reject(cause)
360
+ this.requestsReady.reject(cause)
361
+ this.started.reject(cause)
362
+ this.exited.reject(cause)
363
+ this.ended.resolve()
364
+ }
365
+
366
+ private acceptRequests(socket: Socket): void {
367
+ if (this.requestSocket !== undefined) {
368
+ socket.destroy()
369
+ return
370
+ }
371
+ this.requestSocket = socket
372
+ // A late write may fail after the owner has sent its last status frames.
373
+ // Destroying this socket must never discard those frames on the reader.
374
+ socket.on("error", (cause) => {
375
+ this.fault = cause
376
+ })
377
+ this.requestsReady.resolve()
378
+ }
379
+
380
+ private accept(socket: Socket): void {
381
+ if (this.socket !== undefined) {
382
+ socket.destroy()
383
+ return
384
+ }
385
+ this.socket = socket
386
+ let buffer = ""
387
+ socket.setEncoding("utf8")
388
+ socket.on("data", (data: string) => {
389
+ try {
390
+ buffer += data
391
+ if (Buffer.byteLength(buffer) > 16 * 1024) {
392
+ throw processFault("status_too_large", "Process status frame exceeds its limit")
393
+ }
394
+ for (;;) {
395
+ const end = buffer.indexOf("\n")
396
+ if (end < 0) break
397
+ const line = buffer.slice(0, end)
398
+ buffer = buffer.slice(end + 1)
399
+ this.receive(JSON.parse(line))
400
+ }
401
+ } catch (cause) {
402
+ this.fault = cause
403
+ socket.destroy()
404
+ }
405
+ })
406
+ socket.on("error", (cause) => {
407
+ this.fault = cause
408
+ })
409
+ socket.once("close", () => this.closed())
410
+ }
411
+
412
+ private receive(value: unknown): void {
413
+ if (typeof value !== "object" || value === null) throw processFault("status_invalid", "Invalid process status")
414
+ const message = value as Record<string, unknown>
415
+ switch (message.type) {
416
+ case "ready":
417
+ if (
418
+ this.receivedReady || message.version !== 1 || !Number.isSafeInteger(message.pid) || Number(message.pid) <= 1
419
+ ) {
420
+ throw processFault("status_invalid", "Invalid process readiness")
421
+ }
422
+ this.receivedReady = true
423
+ this.ownerCreation = message.created
424
+ this.ready.resolve(Number(message.pid))
425
+ return
426
+ case "spawned":
427
+ if (
428
+ !this.activationSent || this.receivedStarted || !Number.isSafeInteger(message.pid) || Number(message.pid) <= 1
429
+ ) {
430
+ throw processFault("status_invalid", "Invalid target startup")
431
+ }
432
+ this.receivedStarted = true
433
+ this.targetPid = Number(message.pid)
434
+ this.started.resolve()
435
+ return
436
+ case "spawn_error":
437
+ if (!this.activationSent || this.receivedStarted) {
438
+ throw processFault("status_invalid", "Invalid target spawn failure")
439
+ }
440
+ this.spawnFailed = true
441
+ this.fault = message
442
+ this.started.reject(message)
443
+ this.exited.reject(message)
444
+ return
445
+ case "fault":
446
+ this.fault = message
447
+ this.started.reject(message)
448
+ this.exited.reject(message)
449
+ return
450
+ case "exit":
451
+ if (
452
+ !this.receivedStarted || this.targetDone ||
453
+ !(Number.isInteger(message.code) && Number(message.code) >= 0 && message.signal === null ||
454
+ message.code === null && typeof message.signal === "string" && /^SIG[A-Z0-9]+$/.test(message.signal))
455
+ ) {
456
+ throw processFault("status_invalid", "Invalid target exit status")
457
+ }
458
+ this.targetDone = true
459
+ if (message.code === null) {
460
+ this.exited.reject({
461
+ message: `Process interrupted due to receipt of signal: '${message.signal}'`,
462
+ signal: message.signal
463
+ })
464
+ } else this.exited.resolve(ExitCode(Number(message.code)))
465
+ this.onTargetExit()
466
+ return
467
+ case "cleanup_error":
468
+ this.cleanupFailed = true
469
+ this.fault = message
470
+ return
471
+ case "cleanup":
472
+ this.cleanupAcknowledged = true
473
+ this.onCleanup()
474
+ return
475
+ default:
476
+ throw processFault("status_unknown", "Unknown process status")
477
+ }
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Prepare a private, independently bounded process owner before target launch.
483
+ * @private
484
+ * @since 1.0.0
485
+ */
486
+ export const prepare = (
487
+ system: System,
488
+ policy: (
489
+ options: ChildProcess.KillOptions,
490
+ defaults?: ChildProcess.KillOptions
491
+ ) => Effect.Effect<Policy, PlatformError.PlatformError>,
492
+ transport: "native" | "tls" = "native"
493
+ ): Lifecycle =>
494
+ (command, spawn) =>
495
+ Effect.gen(function*() {
496
+ const initial = yield* policy(command.options)
497
+ const bun = process.versions.bun !== undefined
498
+ const sea = bun ? false : (yield* Effect.tryPromise({
499
+ try: () => import("node:sea"),
500
+ catch: (cause) => failure("spawn", process.execPath, cause)
501
+ })).isSea()
502
+ const bootstrap = yield* bootstrapArguments({
503
+ bun,
504
+ sea,
505
+ main: (globalThis as { readonly Bun?: { readonly main?: string } }).Bun?.main ?? ""
506
+ })
507
+ const windows = system.platform === "win32"
508
+ const grouped = !windows && (command.options.detached ?? true)
509
+ const jobExecutable = windows ?
510
+ yield* Effect.try({
511
+ try: resolveJobExecutable,
512
+ catch: (cause) => failure("spawn", command.command, cause)
513
+ }) :
514
+ undefined
515
+ const control = yield* Effect.acquireRelease(
516
+ Effect.try({ try: () => new Control(transport), catch: (cause) => failure("spawn", command.command, cause) }),
517
+ (control) => Effect.sync(() => control.dispose())
518
+ )
519
+ yield* bounded(wait(control.listening, "spawn", command.command), startupMs, "spawn", command.command)
520
+ const raw = yield* spawn(ChildProcess.make(process.execPath, [
521
+ ...bootstrap,
522
+ "-e",
523
+ source,
524
+ control.path,
525
+ grouped ? "group" : "direct"
526
+ ], {
527
+ ...command.options,
528
+ cwd: parse(process.execPath).root,
529
+ env: {
530
+ PATH: "/usr/bin:/bin",
531
+ HOME: "/",
532
+ XDG_CONFIG_HOME: "/",
533
+ BUN_RUNTIME_TRANSPILER_CACHE_PATH: "0",
534
+ ...control.environment(),
535
+ ...(jobExecutable === undefined ? {} : { SMITHERS_PROCESS_JOB_HELPER: jobExecutable })
536
+ },
537
+ extendEnv: false,
538
+ shell: false,
539
+ detached: grouped,
540
+ killSignal: "SIGTERM",
541
+ forceKillAfter: initial.graceMs
542
+ }))
543
+ yield* Effect.exit(raw.exitCode).pipe(Effect.andThen(Effect.sync(() => control.rawEnded())), Effect.forkScoped)
544
+ let job: WindowsProcessJob | undefined
545
+ let settled = false
546
+ let referenced = true
547
+ let reref: Effect.Effect<void, PlatformError.PlatformError> = Effect.void
548
+ let selected = initial
549
+ let requireCleanupReceipt = false
550
+ const snapshot = () => system.snapshot(raw.pid)
551
+ const alone = () => {
552
+ if (!grouped) return control.targetDone
553
+ const observed = snapshot()
554
+ if (observed === undefined || observed.ownGroup === raw.pid) return false
555
+ const running = observed.members.filter((member) => !member.zombie)
556
+ return running.length === 1 && running[0]!.pid === raw.pid
557
+ }
558
+ // This observation only shortens the owner's deadline. The live owner makes
559
+ // the signal, so neither a stale observation nor a reused pid grants a kill.
560
+ control.onTargetExit = () => {
561
+ // An explicit stop already owns its deadline and escaped-child sweep.
562
+ // Natural exit must not replace that policy with a second fast stop.
563
+ if (control.closeSent) return
564
+ if (!alone()) return
565
+ control.closeSent = true
566
+ void control.write({ type: "stop", killSignal: "SIGKILL", fast: true }).catch(() => control.disconnect())
567
+ }
568
+ const finish = yield* Effect.cached(Effect.gen(function*() {
569
+ yield* bounded(Effect.exit(raw.exitCode), selected.graceMs + exitAllowanceMs, "kill", command.command).pipe(
570
+ Effect.catch((cause) =>
571
+ job === undefined ? Effect.fail(cause) : Effect.gen(function*() {
572
+ // The native guardian can terminate a stalled owner independently.
573
+ job!.stop()
574
+ yield* bounded(Effect.exit(raw.exitCode), exitAllowanceMs, "kill", command.command)
575
+ })
576
+ )
577
+ )
578
+ if (job !== undefined) {
579
+ yield* bounded(wait(job.settled.promise, "kill", command.command), 5500, "kill", command.command)
580
+ settled = true
581
+ return
582
+ }
583
+ yield* bounded(wait(control.ended.promise, "kill", command.command), deliveryMs, "kill", command.command).pipe(
584
+ Effect.ignore
585
+ )
586
+ const deadline = Date.now() + verificationMs
587
+ for (;;) {
588
+ // The kernel's ESRCH proves the group empty with no fork. A `ps`
589
+ // snapshot can go unanswered on a loaded host, so it only decides what
590
+ // the kernel cannot: a group still holding zombies, or an unknown one.
591
+ // A live member keeps both answers false. This host is alive, so a
592
+ // vacant group is never this host's own group.
593
+ const vacant = grouped && system.vacant(raw.pid)
594
+ const observed = grouped && !vacant ? snapshot() : undefined
595
+ settled = !control.cleanupFailed && (!requireCleanupReceipt || control.cleanupAcknowledged) && (grouped
596
+ ? vacant ||
597
+ observed !== undefined && observed.ownGroup !== raw.pid && observed.members.every((member) => member.zombie)
598
+ : control.targetDone || control.spawnFailed || !control.activationSent)
599
+ if (settled) return
600
+ if (Date.now() >= deadline) {
601
+ return yield* Effect.fail(
602
+ failure(
603
+ "kill",
604
+ command.command,
605
+ processFault(
606
+ "cleanup_unverified",
607
+ "Process cleanup could not be verified; its ledger record is retained " +
608
+ `(${unverifiedCleanup(control, requireCleanupReceipt, vacant, observed)})`,
609
+ {
610
+ cause: {
611
+ fault: control.fault,
612
+ cleanupFailed: control.cleanupFailed,
613
+ cleanupRequired: requireCleanupReceipt,
614
+ cleanupAcknowledged: control.cleanupAcknowledged,
615
+ targetDone: control.targetDone,
616
+ groupVacant: vacant,
617
+ ownerObserved: observed !== undefined,
618
+ members: observed?.members
619
+ }
620
+ }
621
+ )
622
+ )
623
+ )
624
+ }
625
+ yield* Effect.sleep(10).pipe(Effect.provideService(Clock.Clock, processClock))
626
+ }
627
+ }))
628
+ const kill = (options: ChildProcess.KillOptions = {}) =>
629
+ Effect.gen(function*() {
630
+ const requested = yield* policy(options, command.options)
631
+ if (!referenced) {
632
+ job?.reference(true)
633
+ control.socket?.ref()
634
+ control.requestSocket?.ref()
635
+ yield* reref
636
+ referenced = true
637
+ }
638
+ if (!control.closeSent) {
639
+ control.closeSent = true
640
+ selected = requested
641
+ // An empty original group cannot prove an explicit escaped-descendant
642
+ // sweep succeeded if its status frames were lost. Startup and natural
643
+ // completion have no such additional contract.
644
+ requireCleanupReceipt = grouped && control.activationSent && !control.spawnFailed && !control.targetDone
645
+ yield* bounded(
646
+ wait(
647
+ control.write({
648
+ type: "stop",
649
+ explicit: true,
650
+ ...requested,
651
+ // A live target can move out of this group with setsid().
652
+ // Only an owner whose target was never activated can replace
653
+ // the requested signal; natural exit has its own fast path.
654
+ killSignal: !control.activationSent && alone() ? "SIGKILL" : requested.killSignal
655
+ }),
656
+ "kill",
657
+ command.command
658
+ ),
659
+ deliveryMs,
660
+ "kill",
661
+ command.command
662
+ )
663
+ .pipe(Effect.catch(() => Effect.sync(() => control.disconnect())))
664
+ }
665
+ yield* finish
666
+ }).pipe(Effect.uninterruptible)
667
+ yield* Effect.addFinalizer(() =>
668
+ kill().pipe(
669
+ // A stuck owner keeps its ledger entry. Disable the underlying spawner's
670
+ // unconditional group-kill finalizer rather than signal a stale identity.
671
+ Effect.ensuring(
672
+ Effect.suspend(() =>
673
+ settled ? Effect.void : Effect.andThen(
674
+ raw.unref,
675
+ Effect.sync(() => {
676
+ job?.stop()
677
+ job?.reference(false)
678
+ control.disconnect()
679
+ })
680
+ )
681
+ ).pipe(Effect.orDie)
682
+ ),
683
+ Effect.orDie
684
+ )
685
+ )
686
+ const ready = yield* bounded(
687
+ wait(control.ready.promise, "spawn", command.command),
688
+ startupMs,
689
+ "spawn",
690
+ command.command
691
+ )
692
+ yield* bounded(wait(control.requestsReady.promise, "spawn", command.command), startupMs, "spawn", command.command)
693
+ yield* Effect.try({
694
+ try: () => control.withdraw(ready, raw.pid),
695
+ catch: (cause) => failure("spawn", command.command, cause)
696
+ })
697
+ if (windows) {
698
+ job = yield* Effect.try({
699
+ try: () => new WindowsProcessJob(raw.pid, control.ownerCreation, jobExecutable!),
700
+ catch: (cause) => failure("spawn", command.command, cause)
701
+ })
702
+ yield* bounded(wait(job.ready.promise, "spawn", command.command), startupMs, "spawn", command.command)
703
+ }
704
+ const acknowledgeCleanup = windows || transport === "tls"
705
+ if (acknowledgeCleanup) {
706
+ control.onCleanup = () => {
707
+ if (!control.ownerDone) {
708
+ void control.write({ type: "cleanup_ack" }).catch(() => control.disconnect())
709
+ }
710
+ }
711
+ }
712
+ const options = command.options
713
+ // Match Effect/Node's undefined-vs-empty environment semantics in the host,
714
+ // before replacing the helper's environment with its isolated bootstrap.
715
+ const env = options.extendEnv ? { ...process.env, ...options.env } : options.env ?? { ...process.env }
716
+ yield* bounded(
717
+ wait(
718
+ control.write({
719
+ type: "configure",
720
+ acknowledgeCleanup,
721
+ command: command.command,
722
+ args: command.args,
723
+ cwd: resolve(options.cwd ?? process.cwd()),
724
+ env,
725
+ shell: options.shell,
726
+ windowsHide: options.windowsHide ?? true,
727
+ windowsVerbatimArguments:
728
+ (options as ChildProcess.CommandOptions & { readonly windowsVerbatimArguments?: boolean })
729
+ .windowsVerbatimArguments,
730
+ standardFds: standardFdsOf(raw),
731
+ userFds: [
732
+ ...new Set(
733
+ Object.keys(options.additionalFds ?? {}).map(ChildProcess.parseFdName)
734
+ .filter((fd): fd is number => fd !== undefined)
735
+ )
736
+ ],
737
+ ...initial
738
+ }),
739
+ "spawn",
740
+ command.command
741
+ ),
742
+ deliveryMs,
743
+ "spawn",
744
+ command.command
745
+ )
746
+ const activate = yield* Effect.cached(Effect.gen(function*() {
747
+ control.activationSent = true
748
+ yield* bounded(
749
+ wait(control.write({ type: "start" }), "spawn", command.command),
750
+ deliveryMs,
751
+ "spawn",
752
+ command.command
753
+ )
754
+ yield* bounded(wait(control.started.promise, "spawn", command.command), startupMs, "spawn", command.command)
755
+ }))
756
+ const unref = Effect.gen(function*() {
757
+ if (referenced) {
758
+ reref = yield* raw.unref
759
+ job?.reference(false)
760
+ control.socket?.unref()
761
+ control.requestSocket?.unref()
762
+ referenced = false
763
+ }
764
+ return Effect.gen(function*() {
765
+ if (!referenced) {
766
+ job?.reference(true)
767
+ control.socket?.ref()
768
+ control.requestSocket?.ref()
769
+ yield* reref
770
+ referenced = true
771
+ }
772
+ })
773
+ })
774
+ const output = <A>(stream: Stream.Stream<A, PlatformError.PlatformError>) =>
775
+ stream.pipe(
776
+ Stream.interruptWhen(wait(control.lost.promise, "read", command.command))
777
+ )
778
+ const input = <A>(sink: Sink.Sink<A, Uint8Array, never, PlatformError.PlatformError>) =>
779
+ Sink.fromChannel(
780
+ Sink.toChannel(sink).pipe(Channel.interruptWhen(wait(control.lost.promise, "write", command.command)))
781
+ )
782
+ const handle = makeHandle({
783
+ ...raw,
784
+ exitCode: wait(control.exited.promise, "exitCode", command.command),
785
+ isRunning: Effect.sync(() => !control.targetDone && !control.ownerDone),
786
+ stdin: input(raw.stdin),
787
+ getInputFd: (fd) => input(raw.getInputFd(fd)),
788
+ stdout: output(raw.stdout),
789
+ stderr: output(raw.stderr),
790
+ all: output(raw.all),
791
+ getOutputFd: (fd) => output(raw.getOutputFd(fd)),
792
+ kill,
793
+ unref
794
+ })
795
+ targets.set(handle, control)
796
+ return {
797
+ handle,
798
+ activate,
799
+ settled: Effect.sync(() => settled)
800
+ }
801
+ })