@graftland/plugin-remote 0.0.0-placeholder.0 → 0.2.0-beta.2

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 (380) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +75 -2
  3. package/README.zh-CN.md +71 -0
  4. package/README.zh-TW.md +71 -0
  5. package/dist/Auth-BElTC9_X.js +6 -0
  6. package/dist/Auth.d.ts +49 -0
  7. package/dist/Auth.d.ts.map +1 -0
  8. package/dist/Auth.js +8 -0
  9. package/dist/Auth.js.map +1 -0
  10. package/dist/Bootstrap-MCwivGN1.js +9 -0
  11. package/dist/ConfigDirectory-BCC6q2R4.js +5 -0
  12. package/dist/Contract-CcpSIJm4.js +121 -0
  13. package/dist/Contract.d.ts +71 -0
  14. package/dist/Contract.d.ts.map +1 -0
  15. package/dist/Contract.js +1 -0
  16. package/dist/Contract.js.map +1 -0
  17. package/dist/Errors-oPsTLY7l.js +45 -0
  18. package/dist/Errors.d.ts +67 -0
  19. package/dist/Errors.d.ts.map +1 -0
  20. package/dist/Errors.js +47 -0
  21. package/dist/Errors.js.map +1 -0
  22. package/dist/FailureGroup-BcH0XOBi.js +53 -0
  23. package/dist/Framing-zaYdOJnT.js +61 -0
  24. package/dist/Graft-BsYq3F-J.js +985 -0
  25. package/dist/Instance.d.ts +7 -0
  26. package/dist/Instance.d.ts.map +1 -0
  27. package/dist/Instance.js +14 -0
  28. package/dist/Instance.js.map +1 -0
  29. package/dist/Packages-B1TPk8pa.js +111 -0
  30. package/dist/Peer-IJgFK6mD.js +342 -0
  31. package/dist/ProcessObservation-DEGrEMfP.js +38 -0
  32. package/dist/ServeLifecycle-CJM0gG5B.js +24 -0
  33. package/dist/Testing.d.ts +26 -0
  34. package/dist/Testing.d.ts.map +1 -0
  35. package/dist/Testing.js +69 -0
  36. package/dist/Testing.js.map +1 -0
  37. package/dist/Transport-DULzpsgR.js +6 -0
  38. package/dist/Transport.d.ts +39 -0
  39. package/dist/Transport.d.ts.map +1 -0
  40. package/dist/Transport.js +8 -0
  41. package/dist/Transport.js.map +1 -0
  42. package/dist/WatchdogProtocol-BZmVxrBP.js +17 -0
  43. package/dist/_virtual/_rolldown/runtime.js +12 -0
  44. package/dist/bin.d.ts +1 -0
  45. package/dist/bin.js +28275 -0
  46. package/dist/bin.js.map +1 -0
  47. package/dist/childRuntime-DSqtFN9o.js +188 -0
  48. package/dist/cleanup-BPWr-5U9.js +13 -0
  49. package/dist/command-9zrHdbwT.js +7 -0
  50. package/dist/connectionEnded-DUgcobEd.js +9 -0
  51. package/dist/directTransport-DrexuPSm.js +5 -0
  52. package/dist/entry-SYsx9VNb.js +708 -0
  53. package/dist/host/auth/bearer.d.ts +5 -0
  54. package/dist/host/auth/bearer.d.ts.map +1 -0
  55. package/dist/host/auth/bearer.js +4 -0
  56. package/dist/host/auth/bearer.js.map +1 -0
  57. package/dist/host/auth/jwt.d.ts +5 -0
  58. package/dist/host/auth/jwt.d.ts.map +1 -0
  59. package/dist/host/auth/jwt.js +4 -0
  60. package/dist/host/auth/jwt.js.map +1 -0
  61. package/dist/host/auth/mtls.d.ts +8 -0
  62. package/dist/host/auth/mtls.d.ts.map +1 -0
  63. package/dist/host/auth/mtls.js +34 -0
  64. package/dist/host/auth/mtls.js.map +1 -0
  65. package/dist/host/auth/none.d.ts +3 -0
  66. package/dist/host/auth/none.d.ts.map +1 -0
  67. package/dist/host/auth/none.js +13 -0
  68. package/dist/host/auth/none.js.map +1 -0
  69. package/dist/host/command.d.ts +22 -0
  70. package/dist/host/command.d.ts.map +1 -0
  71. package/dist/host/command.js +61 -0
  72. package/dist/host/command.js.map +1 -0
  73. package/dist/host/local.d.ts +4 -0
  74. package/dist/host/local.d.ts.map +1 -0
  75. package/dist/host/local.js +52 -0
  76. package/dist/host/local.js.map +1 -0
  77. package/dist/host/unix.d.ts +11 -0
  78. package/dist/host/unix.d.ts.map +1 -0
  79. package/dist/host/unix.js +39 -0
  80. package/dist/host/unix.js.map +1 -0
  81. package/dist/host/websocket.d.ts +13 -0
  82. package/dist/host/websocket.d.ts.map +1 -0
  83. package/dist/host/websocket.js +36 -0
  84. package/dist/host/websocket.js.map +1 -0
  85. package/dist/host/worker.d.ts +4 -0
  86. package/dist/host/worker.d.ts.map +1 -0
  87. package/dist/host/worker.js +52 -0
  88. package/dist/host/worker.js.map +1 -0
  89. package/dist/importer-D7zSgqxq.js +347 -0
  90. package/dist/index.d.ts +29 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +86 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/internal/Certificates.js +182 -0
  95. package/dist/internal/Certificates.js.map +1 -0
  96. package/dist/internal/FailureGroup.js +55 -0
  97. package/dist/internal/FailureGroup.js.map +1 -0
  98. package/dist/internal/Limits.d.ts +1 -0
  99. package/dist/internal/Limits.js +17 -0
  100. package/dist/internal/Limits.js.map +1 -0
  101. package/dist/internal/Peer.js +6 -0
  102. package/dist/internal/Peer.js.map +1 -0
  103. package/dist/internal/ServeLifecycle.js +26 -0
  104. package/dist/internal/ServeLifecycle.js.map +1 -0
  105. package/dist/internal/Supervisor.js +7 -0
  106. package/dist/internal/Supervisor.js.map +1 -0
  107. package/dist/internal/assembly.d.ts +5 -0
  108. package/dist/internal/assembly.js +138 -0
  109. package/dist/internal/assembly.js.map +1 -0
  110. package/dist/internal/auth/mtls.js +23 -0
  111. package/dist/internal/auth/mtls.js.map +1 -0
  112. package/dist/internal/auth/token.js +19 -0
  113. package/dist/internal/auth/token.js.map +1 -0
  114. package/dist/internal/auth/tokenCredentials.d.ts +8 -0
  115. package/dist/internal/auth/tokenCredentials.d.ts.map +1 -0
  116. package/dist/internal/auth/tokenCredentials.js +33 -0
  117. package/dist/internal/auth/tokenCredentials.js.map +1 -0
  118. package/dist/internal/child.js +125 -0
  119. package/dist/internal/child.js.map +1 -0
  120. package/dist/internal/childErrors.d.ts +33 -0
  121. package/dist/internal/childErrors.d.ts.map +1 -0
  122. package/dist/internal/childErrors.js +29 -0
  123. package/dist/internal/childErrors.js.map +1 -0
  124. package/dist/internal/childRuntime.js +59 -0
  125. package/dist/internal/childRuntime.js.map +1 -0
  126. package/dist/internal/cleanup.js +15 -0
  127. package/dist/internal/cleanup.js.map +1 -0
  128. package/dist/internal/config.js +6 -0
  129. package/dist/internal/config.js.map +1 -0
  130. package/dist/internal/control.js +42 -0
  131. package/dist/internal/control.js.map +1 -0
  132. package/dist/internal/credentials.d.ts +44 -0
  133. package/dist/internal/credentials.d.ts.map +1 -0
  134. package/dist/internal/credentials.js +54 -0
  135. package/dist/internal/credentials.js.map +1 -0
  136. package/dist/internal/directTransport.d.ts +3 -0
  137. package/dist/internal/directTransport.js +7 -0
  138. package/dist/internal/directTransport.js.map +1 -0
  139. package/dist/internal/entry.d.ts +9 -0
  140. package/dist/internal/entry.d.ts.map +1 -0
  141. package/dist/internal/entry.js +144 -0
  142. package/dist/internal/entry.js.map +1 -0
  143. package/dist/internal/entryConfig.d.ts +202 -0
  144. package/dist/internal/entryConfig.d.ts.map +1 -0
  145. package/dist/internal/entryConfig.js +176 -0
  146. package/dist/internal/entryConfig.js.map +1 -0
  147. package/dist/internal/execution.d.ts +5 -0
  148. package/dist/internal/execution.js +43 -0
  149. package/dist/internal/execution.js.map +1 -0
  150. package/dist/internal/follow.js +11 -0
  151. package/dist/internal/follow.js.map +1 -0
  152. package/dist/internal/forkTree.js +7 -0
  153. package/dist/internal/forkTree.js.map +1 -0
  154. package/dist/internal/host/local.js +28 -0
  155. package/dist/internal/host/local.js.map +1 -0
  156. package/dist/internal/host/runner.js +100 -0
  157. package/dist/internal/host/runner.js.map +1 -0
  158. package/dist/internal/host.js +71 -0
  159. package/dist/internal/host.js.map +1 -0
  160. package/dist/internal/importer.js +18 -0
  161. package/dist/internal/importer.js.map +1 -0
  162. package/dist/internal/listenerStartup.js +17 -0
  163. package/dist/internal/listenerStartup.js.map +1 -0
  164. package/dist/internal/loadModule.js +20 -0
  165. package/dist/internal/loadModule.js.map +1 -0
  166. package/dist/internal/platform.js +9 -0
  167. package/dist/internal/platform.js.map +1 -0
  168. package/dist/internal/port.d.ts +13 -0
  169. package/dist/internal/port.d.ts.map +1 -0
  170. package/dist/internal/port.js +275 -0
  171. package/dist/internal/port.js.map +1 -0
  172. package/dist/internal/release.js +25 -0
  173. package/dist/internal/release.js.map +1 -0
  174. package/dist/internal/runner/Bootstrap.js +11 -0
  175. package/dist/internal/runner/Bootstrap.js.map +1 -0
  176. package/dist/internal/runner/Connection.d.ts +11 -0
  177. package/dist/internal/runner/Connection.js +56 -0
  178. package/dist/internal/runner/Connection.js.map +1 -0
  179. package/dist/internal/runner/Execution.js +115 -0
  180. package/dist/internal/runner/Execution.js.map +1 -0
  181. package/dist/internal/runner/Framing.d.ts +11 -0
  182. package/dist/internal/runner/Framing.d.ts.map +1 -0
  183. package/dist/internal/runner/Framing.js +63 -0
  184. package/dist/internal/runner/Framing.js.map +1 -0
  185. package/dist/internal/runner/Local.js +46 -0
  186. package/dist/internal/runner/Local.js.map +1 -0
  187. package/dist/internal/runner/Lock.js +25 -0
  188. package/dist/internal/runner/Lock.js.map +1 -0
  189. package/dist/internal/runner/Multiplex.d.ts +5 -0
  190. package/dist/internal/runner/Multiplex.js +49 -0
  191. package/dist/internal/runner/Multiplex.js.map +1 -0
  192. package/dist/internal/runner/Packages.d.ts +3 -0
  193. package/dist/internal/runner/Packages.js +108 -0
  194. package/dist/internal/runner/Packages.js.map +1 -0
  195. package/dist/internal/runner/ProcessObservation.js +40 -0
  196. package/dist/internal/runner/ProcessObservation.js.map +1 -0
  197. package/dist/internal/runner/Processes.js +83 -0
  198. package/dist/internal/runner/Processes.js.map +1 -0
  199. package/dist/internal/runner/Retention.js +13 -0
  200. package/dist/internal/runner/Retention.js.map +1 -0
  201. package/dist/internal/runner/Store.js +78 -0
  202. package/dist/internal/runner/Store.js.map +1 -0
  203. package/dist/internal/runner/Supervisor.js +375 -0
  204. package/dist/internal/runner/Supervisor.js.map +1 -0
  205. package/dist/internal/runner/Watchdog.js +47 -0
  206. package/dist/internal/runner/Watchdog.js.map +1 -0
  207. package/dist/internal/runner/WatchdogProtocol.js +19 -0
  208. package/dist/internal/runner/WatchdogProtocol.js.map +1 -0
  209. package/dist/internal/runner/Wire.d.ts +5 -0
  210. package/dist/internal/runner/Wire.js +64 -0
  211. package/dist/internal/runner/Wire.js.map +1 -0
  212. package/dist/internal/runner/accept.js +57 -0
  213. package/dist/internal/runner/accept.js.map +1 -0
  214. package/dist/internal/runner/command.js +9 -0
  215. package/dist/internal/runner/command.js.map +1 -0
  216. package/dist/internal/runner/connectionEnded.js +11 -0
  217. package/dist/internal/runner/connectionEnded.js.map +1 -0
  218. package/dist/internal/runner/model.d.ts +17 -0
  219. package/dist/internal/runner/model.d.ts.map +1 -0
  220. package/dist/internal/runner/model.js +231 -0
  221. package/dist/internal/runner/model.js.map +1 -0
  222. package/dist/internal/serve.js +60 -0
  223. package/dist/internal/serve.js.map +1 -0
  224. package/dist/internal/services.d.ts +18 -0
  225. package/dist/internal/services.d.ts.map +1 -0
  226. package/dist/internal/services.js +22 -0
  227. package/dist/internal/services.js.map +1 -0
  228. package/dist/internal/socketChannel.js +24 -0
  229. package/dist/internal/socketChannel.js.map +1 -0
  230. package/dist/internal/stdioChannel.js +23 -0
  231. package/dist/internal/stdioChannel.js.map +1 -0
  232. package/dist/internal/supervisorPlugin.js +1 -0
  233. package/dist/internal/supervisorPlugin.js.map +1 -0
  234. package/dist/internal/transportOptions.d.ts +18 -0
  235. package/dist/internal/transportOptions.d.ts.map +1 -0
  236. package/dist/internal/watchdogMain.d.ts +1 -0
  237. package/dist/internal/watchdogMain.js +55 -0
  238. package/dist/internal/watchdogMain.js.map +1 -0
  239. package/dist/internal/websocket.js +46 -0
  240. package/dist/internal/websocket.js.map +1 -0
  241. package/dist/internal/websocketServer.js +61 -0
  242. package/dist/internal/websocketServer.js.map +1 -0
  243. package/dist/internal/workerBootstrap.js +9 -0
  244. package/dist/internal/workerBootstrap.js.map +1 -0
  245. package/dist/internal/workerChannel.js +24 -0
  246. package/dist/internal/workerChannel.js.map +1 -0
  247. package/dist/internal/workerMain.d.ts +1 -0
  248. package/dist/internal/workerMain.js +18 -0
  249. package/dist/internal/workerMain.js.map +1 -0
  250. package/dist/port-Blbh8tms.js +285 -0
  251. package/dist/release-BY4Mlluy.js +23 -0
  252. package/dist/rolldown-runtime-DYf_mQCB.js +34 -0
  253. package/dist/runner/auth/bearer.d.ts +7 -0
  254. package/dist/runner/auth/bearer.d.ts.map +1 -0
  255. package/dist/runner/auth/bearer.js +42 -0
  256. package/dist/runner/auth/bearer.js.map +1 -0
  257. package/dist/runner/auth/jwt.d.ts +19 -0
  258. package/dist/runner/auth/jwt.d.ts.map +1 -0
  259. package/dist/runner/auth/jwt.js +65 -0
  260. package/dist/runner/auth/jwt.js.map +1 -0
  261. package/dist/runner/auth/mtls.d.ts +11 -0
  262. package/dist/runner/auth/mtls.d.ts.map +1 -0
  263. package/dist/runner/auth/mtls.js +48 -0
  264. package/dist/runner/auth/mtls.js.map +1 -0
  265. package/dist/runner/auth/none.d.ts +3 -0
  266. package/dist/runner/auth/none.d.ts.map +1 -0
  267. package/dist/runner/auth/none.js +13 -0
  268. package/dist/runner/auth/none.js.map +1 -0
  269. package/dist/runner/index.d.ts +3 -0
  270. package/dist/runner/index.d.ts.map +1 -0
  271. package/dist/runner/index.js +71 -0
  272. package/dist/runner/index.js.map +1 -0
  273. package/dist/runner/stdio.d.ts +7 -0
  274. package/dist/runner/stdio.d.ts.map +1 -0
  275. package/dist/runner/stdio.js +39 -0
  276. package/dist/runner/stdio.js.map +1 -0
  277. package/dist/runner/unix.d.ts +7 -0
  278. package/dist/runner/unix.d.ts.map +1 -0
  279. package/dist/runner/unix.js +42 -0
  280. package/dist/runner/unix.js.map +1 -0
  281. package/dist/runner/websocket.d.ts +28 -0
  282. package/dist/runner/websocket.d.ts.map +1 -0
  283. package/dist/runner/websocket.js +77 -0
  284. package/dist/runner/websocket.js.map +1 -0
  285. package/dist/socketChannel-KmtzF-9G.js +3658 -0
  286. package/dist/supervisorPlugin-Inb1S5IV.js +737 -0
  287. package/dist/token-v5ne2I0-.js +17 -0
  288. package/dist/tokenCredentials-WUJm2AKm.js +31 -0
  289. package/dist/websocketServer-MXcCJx0v.js +58 -0
  290. package/dist/workerChannel-P82YdNjh.js +27 -0
  291. package/package.json +71 -11
  292. package/src/Auth.ts +62 -0
  293. package/src/Contract.ts +100 -0
  294. package/src/Errors.ts +82 -0
  295. package/src/Instance.ts +12 -0
  296. package/src/Testing.ts +135 -0
  297. package/src/Transport.ts +52 -0
  298. package/src/bin.ts +145 -0
  299. package/src/host/auth/bearer.ts +10 -0
  300. package/src/host/auth/jwt.ts +10 -0
  301. package/src/host/auth/mtls.ts +48 -0
  302. package/src/host/auth/none.ts +13 -0
  303. package/src/host/command.ts +131 -0
  304. package/src/host/local.ts +41 -0
  305. package/src/host/unix.ts +65 -0
  306. package/src/host/websocket.ts +66 -0
  307. package/src/host/worker.ts +90 -0
  308. package/src/index.ts +115 -0
  309. package/src/internal/Certificates.ts +330 -0
  310. package/src/internal/FailureGroup.ts +176 -0
  311. package/src/internal/Limits.ts +12 -0
  312. package/src/internal/Peer.ts +4 -0
  313. package/src/internal/ServeLifecycle.ts +53 -0
  314. package/src/internal/Supervisor.ts +14 -0
  315. package/src/internal/assembly.ts +337 -0
  316. package/src/internal/auth/mtls.ts +40 -0
  317. package/src/internal/auth/token.ts +26 -0
  318. package/src/internal/auth/tokenCredentials.ts +65 -0
  319. package/src/internal/child.ts +323 -0
  320. package/src/internal/childErrors.ts +44 -0
  321. package/src/internal/childRuntime.ts +111 -0
  322. package/src/internal/cleanup.ts +34 -0
  323. package/src/internal/config.ts +8 -0
  324. package/src/internal/control.ts +49 -0
  325. package/src/internal/credentials.ts +112 -0
  326. package/src/internal/directTransport.ts +38 -0
  327. package/src/internal/entry.ts +288 -0
  328. package/src/internal/entryConfig.ts +190 -0
  329. package/src/internal/execution.ts +121 -0
  330. package/src/internal/follow.ts +17 -0
  331. package/src/internal/forkTree.ts +13 -0
  332. package/src/internal/host/local.ts +53 -0
  333. package/src/internal/host/runner.ts +187 -0
  334. package/src/internal/host.ts +209 -0
  335. package/src/internal/importer.ts +27 -0
  336. package/src/internal/listenerStartup.ts +55 -0
  337. package/src/internal/loadModule.ts +23 -0
  338. package/src/internal/platform.ts +12 -0
  339. package/src/internal/port.ts +623 -0
  340. package/src/internal/release.ts +71 -0
  341. package/src/internal/runner/Bootstrap.ts +9 -0
  342. package/src/internal/runner/Connection.ts +184 -0
  343. package/src/internal/runner/Execution.ts +244 -0
  344. package/src/internal/runner/Framing.ts +122 -0
  345. package/src/internal/runner/Local.ts +73 -0
  346. package/src/internal/runner/Lock.ts +29 -0
  347. package/src/internal/runner/Multiplex.ts +120 -0
  348. package/src/internal/runner/Packages.ts +178 -0
  349. package/src/internal/runner/ProcessObservation.ts +63 -0
  350. package/src/internal/runner/Processes.ts +109 -0
  351. package/src/internal/runner/Retention.ts +49 -0
  352. package/src/internal/runner/Store.ts +160 -0
  353. package/src/internal/runner/Supervisor.ts +901 -0
  354. package/src/internal/runner/Watchdog.ts +83 -0
  355. package/src/internal/runner/WatchdogProtocol.ts +12 -0
  356. package/src/internal/runner/Wire.ts +99 -0
  357. package/src/internal/runner/accept.ts +103 -0
  358. package/src/internal/runner/command.ts +15 -0
  359. package/src/internal/runner/connectionEnded.ts +28 -0
  360. package/src/internal/runner/model.ts +364 -0
  361. package/src/internal/serve.ts +117 -0
  362. package/src/internal/services.ts +74 -0
  363. package/src/internal/socketChannel.ts +64 -0
  364. package/src/internal/stdioChannel.ts +25 -0
  365. package/src/internal/supervisorPlugin.ts +67 -0
  366. package/src/internal/transportOptions.ts +17 -0
  367. package/src/internal/watchdogMain.ts +72 -0
  368. package/src/internal/websocket.ts +81 -0
  369. package/src/internal/websocketServer.ts +152 -0
  370. package/src/internal/workerBootstrap.ts +6 -0
  371. package/src/internal/workerChannel.ts +54 -0
  372. package/src/internal/workerMain.ts +23 -0
  373. package/src/runner/auth/bearer.ts +66 -0
  374. package/src/runner/auth/jwt.ts +114 -0
  375. package/src/runner/auth/mtls.ts +37 -0
  376. package/src/runner/auth/none.ts +13 -0
  377. package/src/runner/index.ts +97 -0
  378. package/src/runner/stdio.ts +76 -0
  379. package/src/runner/unix.ts +53 -0
  380. package/src/runner/websocket.ts +143 -0
@@ -0,0 +1,117 @@
1
+ import { findSubtree, isSubtreeActive } from "./forkTree"
2
+ import { Graft, Plugin, type Fork } from "@graftland/graft"
3
+ import {
4
+ ConfigDirectory,
5
+ Include,
6
+ Loader,
7
+ ModuleResolver,
8
+ } from "@graftland/loader"
9
+ import {
10
+ Array,
11
+ Cause,
12
+ Deferred,
13
+ Effect,
14
+ Exit,
15
+ Logger,
16
+ Option,
17
+ Path,
18
+ Stream,
19
+ } from "effect"
20
+ import * as Importer from "./importer"
21
+
22
+ import { ServeLifecycle, StartupError, make } from "./ServeLifecycle"
23
+ import { cleanupCause } from "./cleanup"
24
+ export { StartupError } from "./ServeLifecycle"
25
+
26
+ const check = (
27
+ trees: ReadonlyArray<Fork.ForkTree>,
28
+ ): Effect.Effect<void, StartupError> =>
29
+ Effect.forEach(
30
+ trees,
31
+ (tree) => {
32
+ if (tree.state._tag === "Failed")
33
+ return Effect.fail(
34
+ StartupError.make({
35
+ detail: `Runner entry ${tree.name} failed`,
36
+ cause: tree.state.cause,
37
+ }),
38
+ )
39
+ return check(tree.children)
40
+ },
41
+ { discard: true },
42
+ )
43
+ const ready = Plugin.make({
44
+ name: "remote-ready",
45
+ inject: [Loader.Loader],
46
+ apply: Effect.fnUntraced(function* () {
47
+ const loader = yield* Loader.Loader
48
+ // A failed authenticator can leave its listener child Pending while the outer plugin is Applying.
49
+ yield* loader.ready.pipe(
50
+ Effect.raceFirst(
51
+ Graft.forks.pipe(
52
+ Stream.map((trees) => findSubtree(trees, loader.root.id)),
53
+ Stream.filter(Option.isSome),
54
+ Stream.runForEach((tree) => check([tree.value])),
55
+ ),
56
+ ),
57
+ )
58
+ // Pending can still be reacting to a provider published during startup.
59
+ // Observe the loader's subtree, excluding this startup observer itself.
60
+ yield* Graft.forks.pipe(
61
+ Stream.map((trees) => findSubtree(trees, loader.root.id)),
62
+ Stream.filter(Option.isSome),
63
+ Stream.map((tree) => tree.value),
64
+ Stream.tap((tree) => check([tree])),
65
+ Stream.filter(isSubtreeActive),
66
+ Stream.runHead,
67
+ )
68
+ }),
69
+ })
70
+
71
+ /** Loader logs child cleanup errors; the supervisor's release signal also reaches the command exit. */
72
+ const close = (entries: Fork.Fork, lifecycle: typeof ServeLifecycle.Service) =>
73
+ Effect.gen(function* () {
74
+ const disposed = yield* Effect.exit(entries.dispose)
75
+ const released = yield* Effect.exit(Deferred.await(lifecycle.released))
76
+ const cause = Array.reduce(
77
+ [disposed, released],
78
+ Cause.empty,
79
+ (cause: Cause.Cause<unknown>, exit: Exit.Exit<void, unknown>) =>
80
+ Exit.isFailure(exit) ? Cause.combine(cause, exit.cause) : cause,
81
+ )
82
+ if (cause.reasons.length > 0)
83
+ return yield* Effect.failCause(cleanupCause(cause))
84
+ })
85
+
86
+ const load = Effect.fnUntraced(function* (config: string, directory: string) {
87
+ const lifecycle = yield* make
88
+ yield* Graft.provide(ServeLifecycle, lifecycle)
89
+ const importer = yield* Importer.make(directory)
90
+ const resolver = yield* ModuleResolver.make({ base: directory, importer })
91
+ yield* Graft.provide(ModuleResolver.ModuleResolver, resolver)
92
+ const entries = yield* Graft.plugin(Include.Include, {
93
+ path: config,
94
+ watch: false,
95
+ })
96
+ yield* entries.await
97
+ const startup = yield* Graft.plugin(ready)
98
+ yield* startup.await
99
+ return yield* Deferred.await(lifecycle.ended).pipe(
100
+ Effect.ensuring(close(entries, lifecycle)),
101
+ )
102
+ })
103
+ export const run = Effect.fnUntraced(function* (config: string) {
104
+ const path = yield* Path.Path
105
+ const file = path.resolve(config)
106
+ const directory = path.dirname(file)
107
+ return yield* load(file, directory).pipe(
108
+ Effect.mapError((error) =>
109
+ StartupError.make({
110
+ detail: "Starting the runner failed",
111
+ cause: Cause.fail(error),
112
+ }),
113
+ ),
114
+ Effect.provideService(ConfigDirectory, Option.some(directory)),
115
+ Effect.provideService(Logger.LogToStderr, true),
116
+ )
117
+ })
@@ -0,0 +1,74 @@
1
+ import { Array, Context, Effect, Record } from "effect"
2
+ import { Graft } from "@graftland/graft"
3
+ import { RpcClient, RpcServer } from "effect/rpc"
4
+ import type { Scope } from "effect"
5
+ import type { Rpc, RpcClientError, RpcGroup } from "effect/rpc"
6
+ import type * as Port from "./port"
7
+
8
+ type Peer = Port.Port
9
+
10
+ /** RPC types stay inside the descriptor; Graft supplies its dynamic activation context. */
11
+ export interface Service<R = never, ClientR = never> {
12
+ readonly key: Context.Key<unknown, unknown>
13
+ readonly group: Pick<RpcGroup.RpcGroup<Rpc.Any>, "requests">
14
+ readonly serve: (
15
+ port: Pick<Peer, "serverProtocol">,
16
+ ) => Effect.Effect<never, never, R>
17
+ readonly publish: (
18
+ port: Pick<Peer, "clientProtocol">,
19
+ ) => Effect.Effect<
20
+ void,
21
+ Graft.ServiceConflict,
22
+ Graft.Graft | Scope.Scope | ClientR
23
+ >
24
+ }
25
+ export const service = <
26
+ I,
27
+ S,
28
+ R extends Rpc.Any,
29
+ H extends RpcGroup.HandlersFrom<R>,
30
+ >(
31
+ key: Context.Key<I, S>,
32
+ group: RpcGroup.RpcGroup<R>,
33
+ mapping: {
34
+ readonly toHandlers: (value: S) => H
35
+ readonly fromClient: (
36
+ client: RpcClient.RpcClient<R, RpcClientError.RpcClientError>,
37
+ ) => S
38
+ },
39
+ ): Service<
40
+ RpcGroup.HandlersServices<R, H> | Rpc.Middleware<R> | Rpc.ServicesServer<R>,
41
+ Rpc.MiddlewareClient<R>
42
+ > => ({
43
+ key,
44
+ group,
45
+ serve: Effect.fnUntraced(function* (port) {
46
+ // Graft admits the activation from this descriptor's key before serving it.
47
+ const context = yield* Effect.context<never>()
48
+ const value = yield* Effect.fromOption(
49
+ Context.getOption(context, key),
50
+ ).pipe(Effect.orDie)
51
+ const handlers = yield* group.toHandlers(mapping.toHandlers(value))
52
+ return yield* RpcServer.make(group, { disableFatalDefects: true }).pipe(
53
+ Effect.provideContext(handlers),
54
+ Effect.provideService(RpcServer.Protocol, port.serverProtocol(key.key)),
55
+ )
56
+ }),
57
+ publish: Effect.fnUntraced(function* (port) {
58
+ const client = yield* RpcClient.make(group).pipe(
59
+ Effect.provideService(RpcClient.Protocol, port.clientProtocol(key.key)),
60
+ )
61
+ yield* Graft.provide(key, mapping.fromClient(client))
62
+ }),
63
+ })
64
+ export const endpoints = (services: ReadonlyArray<Service>) =>
65
+ Record.fromEntries(
66
+ Array.map(
67
+ services,
68
+ (service) =>
69
+ [service.key.key, service.group] satisfies readonly [
70
+ string,
71
+ Service["group"],
72
+ ],
73
+ ),
74
+ )
@@ -0,0 +1,64 @@
1
+ import { Cause, Effect, Exit, Sink, Stream } from "effect"
2
+ import { Socket } from "effect/socket"
3
+ import { TransportError } from "../Errors"
4
+ import { FrameError, isCompleteFrame, decoder, encode } from "./runner/Framing"
5
+
6
+ /** WebSocket boundaries are a wire compatibility detail, not Channel chunk boundaries. */
7
+ export const fromSocket = Effect.fnUntraced(function* (socket: Socket.Socket) {
8
+ const reader = yield* socket.reader
9
+ const writer = yield* socket.writer
10
+ return {
11
+ input: Stream.fromEffectRepeat(reader.pull).pipe(
12
+ Stream.catchTag("SocketError", (error) =>
13
+ error.reason._tag === "SocketCloseError" && error.reason.code === 1000
14
+ ? Stream.empty
15
+ : Stream.fail(error),
16
+ ),
17
+ Stream.flattenIterable,
18
+ Stream.mapEffect((bytes) => {
19
+ if (typeof bytes === "string")
20
+ return Effect.fail(
21
+ FrameError.make({
22
+ detail: "Runner WebSocket messages must be binary",
23
+ }),
24
+ )
25
+ // WebSocket messages preserve one complete framing frame, including its header.
26
+ if (!isCompleteFrame(bytes))
27
+ return Effect.fail(
28
+ FrameError.make({
29
+ detail: "Invalid runner frame header or payload length",
30
+ }),
31
+ )
32
+ return Effect.succeed(bytes)
33
+ }),
34
+ Stream.mapError((error) =>
35
+ TransportError.make({ cause: Cause.fail(error) }),
36
+ ),
37
+ ),
38
+ output: Sink.suspend(() => {
39
+ const frames = decoder()
40
+ return Sink.forEach((bytes: Uint8Array) =>
41
+ Effect.fromResult(frames.decode(bytes)).pipe(
42
+ Effect.flatMap((values) =>
43
+ Effect.forEach(
44
+ values,
45
+ (frame) => encode(frame).pipe(Effect.flatMap(writer.write)),
46
+ { discard: true },
47
+ ),
48
+ ),
49
+ ),
50
+ ).pipe(
51
+ Sink.onExit((exit) =>
52
+ Exit.isSuccess(exit)
53
+ ? frames.finish.pipe(
54
+ Effect.andThen(writer.write(new Socket.CloseEvent(1000))),
55
+ )
56
+ : Effect.void,
57
+ ),
58
+ Sink.mapError((error) =>
59
+ TransportError.make({ cause: Cause.fail(error) }),
60
+ ),
61
+ )
62
+ }),
63
+ }
64
+ })
@@ -0,0 +1,25 @@
1
+ import { NodeSink } from "@effect/platform-node"
2
+ import { Cause, Effect, Stdio, Stream } from "effect"
3
+ // @effect-diagnostics-next-line nodeBuiltinImport:off -- Node's extra inherited descriptor carries protocol output separately from console output.
4
+ import { createWriteStream } from "node:fs"
5
+ import { TransportError } from "../Errors"
6
+ import type * as Port from "./port"
7
+
8
+ export const bytes = Effect.gen(function* () {
9
+ const stdio = yield* Stdio.Stdio
10
+ const output = yield* Effect.sync(() =>
11
+ createWriteStream("", { fd: 3, autoClose: false }),
12
+ )
13
+ return {
14
+ input: stdio.stdin.pipe(
15
+ Stream.mapError((error) =>
16
+ TransportError.make({ cause: Cause.fail(error) }),
17
+ ),
18
+ ),
19
+ output: NodeSink.fromWritable({
20
+ evaluate: () => output,
21
+ onError: (cause) => TransportError.make({ cause: Cause.die(cause) }),
22
+ endOnDone: false,
23
+ }),
24
+ } satisfies Port.Channel
25
+ })
@@ -0,0 +1,67 @@
1
+ import { Graft, Plugin } from "@graftland/graft"
2
+ import {
3
+ Context,
4
+ Effect,
5
+ FileSystem,
6
+ Layer,
7
+ Option,
8
+ Path,
9
+ Schema,
10
+ Scope,
11
+ } from "effect"
12
+ import { Digest, SecureRandom } from "effect/crypto"
13
+ import { ChildProcessSpawner } from "effect/process"
14
+ import { Supervisor } from "./Supervisor"
15
+ import { PositiveDuration } from "./config"
16
+ import { ConfigDirectory } from "@graftland/loader"
17
+ import * as Lock from "./runner/Lock"
18
+ import * as Stores from "./runner/Store"
19
+ import * as Execution from "./runner/Execution"
20
+ import * as Engine from "./runner/Supervisor"
21
+ import { accept } from "./runner/accept"
22
+ import * as Lifecycle from "./ServeLifecycle"
23
+
24
+ export const Config = Schema.Struct({
25
+ root: Schema.String,
26
+ state: Schema.String,
27
+ lease: Schema.optionalKey(PositiveDuration),
28
+ grace: Schema.optionalKey(PositiveDuration),
29
+ })
30
+ export default Plugin.make({
31
+ name: "remote-supervisor",
32
+ config: Config,
33
+ inject: [
34
+ Plugin.optional(Lifecycle.ServeLifecycle),
35
+ FileSystem.FileSystem,
36
+ Path.Path,
37
+ ChildProcessSpawner.ChildProcessSpawner,
38
+ SecureRandom.SecureRandom,
39
+ Digest.Digest,
40
+ ],
41
+ apply: Effect.fnUntraced(function* (options) {
42
+ const lifecycle = yield* Effect.serviceOption(Lifecycle.ServeLifecycle)
43
+ const resources = yield* Lifecycle.resources(lifecycle)
44
+ const path = yield* Path.Path
45
+ const directory = yield* ConfigDirectory.pipe(
46
+ Effect.map(Option.getOrElse(() => ".")),
47
+ )
48
+ const state = path.resolve(directory, options.state)
49
+ yield* Lock.acquire(state).pipe(Scope.provide(resources))
50
+ const services = yield* Layer.build(
51
+ Engine.layer(options).pipe(
52
+ Layer.provideMerge(
53
+ Layer.merge(
54
+ Stores.layerDirectory(state),
55
+ Execution.layer(path.resolve(directory, options.root)),
56
+ ),
57
+ ),
58
+ ),
59
+ ).pipe(Scope.provide(resources))
60
+ const engine = Context.get(services, Engine.Supervisor)
61
+ const execution = Context.get(services, Execution.Channels)
62
+ yield* Plugin.fork(engine.await)
63
+ yield* Graft.provide(Supervisor, {
64
+ accept: (channel) => accept(engine, execution, channel),
65
+ })
66
+ }),
67
+ })
@@ -0,0 +1,17 @@
1
+ import type { Duration } from "effect"
2
+ export interface WebsocketOptions {
3
+ readonly url: string
4
+ readonly tls?: {
5
+ readonly ca?: string
6
+ readonly servername?: string
7
+ }
8
+ readonly openTimeout?: Duration.Input
9
+ }
10
+ export interface UnixOptions {
11
+ readonly path: string
12
+ }
13
+ export interface CommandOptions {
14
+ readonly argv: readonly [string, ...string[]]
15
+ readonly cwd?: string
16
+ readonly env?: Readonly<Record<string, string>>
17
+ }
@@ -0,0 +1,72 @@
1
+ /** Executable worker: fd 4 and the lease timer remain independent of the plugin thread. */
2
+ import { NodeRuntime, NodeStream } from "@effect/platform-node"
3
+ import { Effect, FiberHandle, Ref, Schema, Stream } from "effect"
4
+ import { Socket } from "node:net"
5
+ import { parentPort } from "node:worker_threads"
6
+ import process from "node:process"
7
+ import { CommandJson, Notice } from "./runner/WatchdogProtocol"
8
+
9
+ // The runner machine is trusted. A suspended machine cannot enforce a deadline until it resumes.
10
+ // Killing this live process has no PID lookup; external supervisor kills retain a small PID reuse window.
11
+ const die = Effect.sync(() => process.kill(process.pid, "SIGKILL")).pipe(
12
+ Effect.andThen(Effect.never),
13
+ )
14
+ const notify = (notice: typeof Notice.Type) =>
15
+ Schema.encodeEffect(Notice)(notice).pipe(
16
+ Effect.flatMap((message) =>
17
+ Effect.sync(() => parentPort?.postMessage(message)),
18
+ ),
19
+ )
20
+ const now = Effect.sync(() => performance.now())
21
+ const expire = (deadline: number): Effect.Effect<never> =>
22
+ Effect.gen(function* () {
23
+ const remaining = deadline - (yield* now)
24
+ if (remaining <= 0) return yield* die
25
+ yield* Effect.sleep(Math.min(remaining, 2147483647))
26
+ return yield* expire(deadline)
27
+ })
28
+ const main = Effect.scoped(
29
+ Effect.gen(function* () {
30
+ const lease = yield* Ref.make({
31
+ deadline: (yield* now) + 30000,
32
+ stopped: false,
33
+ })
34
+ const timer = yield* FiberHandle.make<never, never>()
35
+ yield* FiberHandle.run(timer, expire((yield* Ref.get(lease)).deadline))
36
+ const input = yield* Effect.acquireRelease(
37
+ Effect.sync(() => new Socket({ fd: 4, readable: true, writable: false })),
38
+ (socket) =>
39
+ Effect.sync(() => {
40
+ socket.destroy()
41
+ }),
42
+ )
43
+ yield* notify("ready")
44
+ yield* NodeStream.fromReadable({ evaluate: () => input }).pipe(
45
+ Stream.decodeText(),
46
+ Stream.splitLines,
47
+ Stream.mapEffect((line) => Schema.decodeEffect(CommandJson)(line)),
48
+ Stream.runForEach((command) =>
49
+ Effect.gen(function* () {
50
+ if (command.kind === "start") return yield* notify("start")
51
+ if (command.kind === "stop") {
52
+ yield* Ref.update(lease, (state) => ({ ...state, stopped: true }))
53
+ return yield* notify("stop")
54
+ }
55
+ const current = yield* Ref.get(lease)
56
+ const time = yield* now
57
+ if (current.stopped || time >= current.deadline) return yield* die
58
+ const deadline = time + command.millis
59
+ yield* Ref.set(lease, { deadline, stopped: false })
60
+ yield* FiberHandle.run(timer, expire(deadline))
61
+ }),
62
+ ),
63
+ )
64
+ return yield* die
65
+ }),
66
+ ).pipe(
67
+ Effect.catchCause((cause) =>
68
+ Effect.logError("Watchdog failed", cause).pipe(Effect.andThen(die)),
69
+ ),
70
+ )
71
+
72
+ NodeRuntime.runMain(main)
@@ -0,0 +1,81 @@
1
+ import { Array, Effect, Layer, Redacted, Schema } from "effect"
2
+ import { Socket } from "effect/socket"
3
+ import WS from "ws"
4
+ import type { ClientCertificate } from "../Auth"
5
+ import type { Properties } from "../Transport"
6
+ interface PreparedCredentials {
7
+ readonly certificate?: ClientCertificate
8
+ readonly authorization?: Redacted.Redacted<string>
9
+ }
10
+ import type { WebsocketOptions } from "./transportOptions"
11
+ import { headerSize, maxPayload } from "./runner/Framing"
12
+
13
+ export const protocol = "graft-remote.v1"
14
+ export const path = "/graft"
15
+ export const payloadLimit = maxPayload + headerSize
16
+ export const Url = Schema.URLFromString.check(
17
+ Schema.makeFilter(
18
+ (url) => url.protocol === "wss:" || url.protocol === "ws:",
19
+ {
20
+ expected: "a ws:// or wss:// URL",
21
+ },
22
+ ),
23
+ )
24
+
25
+ export const constructor = (
26
+ tls: WebsocketOptions["tls"],
27
+ credentials: PreparedCredentials = {},
28
+ ) =>
29
+ Layer.succeed(Socket.WebSocketConstructor, (url, options) => {
30
+ const settings =
31
+ typeof options === "object" && !Array.isArray(options)
32
+ ? options
33
+ : { protocols: options }
34
+ return new WS(url, settings.protocols, {
35
+ ...tls,
36
+ ...(credentials.certificate === undefined
37
+ ? {}
38
+ : {
39
+ cert: credentials.certificate.cert,
40
+ key: Redacted.value(credentials.certificate.key),
41
+ }),
42
+ headers: {
43
+ ...settings.headers,
44
+ ...(credentials.authorization === undefined
45
+ ? {}
46
+ : { authorization: Redacted.value(credentials.authorization) }),
47
+ },
48
+ perMessageDeflate: false,
49
+ maxPayload: payloadLimit,
50
+ rejectUnauthorized: true,
51
+ minVersion: "TLSv1.3",
52
+ })
53
+ })
54
+
55
+ export const open = Effect.fnUntraced(function* (
56
+ options: WebsocketOptions,
57
+ credentials: PreparedCredentials = {},
58
+ ) {
59
+ const url = yield* Schema.decodeEffect(Url)(options.url)
60
+ if (credentials.certificate !== undefined && url.protocol !== "wss:")
61
+ yield* Schema.decodeEffect(
62
+ Url.check(
63
+ Schema.makeFilter((url) => url.protocol === "wss:", {
64
+ expected: "wss:// for mutual TLS",
65
+ }),
66
+ ),
67
+ )(options.url)
68
+ const address = url.pathname === "/" ? new URL(path, url).href : url.href
69
+ const services = yield* Layer.build(constructor(options.tls, credentials))
70
+ return yield* Socket.makeWebSocket(address, {
71
+ protocols: protocol,
72
+ openTimeout: options.openTimeout ?? "10 seconds",
73
+ }).pipe(Effect.provideContext(services))
74
+ })
75
+
76
+ export const properties = (url: URL): Properties => ({
77
+ protocol: url.protocol === "wss:" ? "wss" : "ws",
78
+ encrypted: url.protocol === "wss:",
79
+ network: true,
80
+ carries: url.protocol === "wss:" ? ["header", "certificate"] : ["header"],
81
+ })
@@ -0,0 +1,152 @@
1
+ import { NodeHttpServer, NodeHttpServerRequest } from "@effect/platform-node"
2
+ import {
3
+ Array,
4
+ Cause,
5
+ Deferred,
6
+ Effect,
7
+ Option,
8
+ Redacted,
9
+ Scope,
10
+ String,
11
+ } from "effect"
12
+ import { HttpServerRequest, HttpServerResponse } from "effect/http"
13
+ import type { Socket } from "effect/socket"
14
+ import { TLSSocket } from "node:tls"
15
+ import { AuthenticationFailed, TransportError } from "../Errors"
16
+ import type { Accept, Handshake, Serving } from "../Transport"
17
+ import * as Channel from "./socketChannel"
18
+ import { path, protocol, payloadLimit } from "./websocket"
19
+
20
+ const rejection = (error: AuthenticationFailed) =>
21
+ Effect.logWarning(
22
+ "Remote host authentication failed",
23
+ {
24
+ reason: error.reason,
25
+ },
26
+ ...(error.cause === undefined ? [] : [error.cause]),
27
+ ).pipe(
28
+ Effect.as(
29
+ HttpServerResponse.empty({
30
+ status: 401,
31
+ headers:
32
+ error.challenge === undefined
33
+ ? {}
34
+ : { "www-authenticate": error.challenge },
35
+ }),
36
+ ),
37
+ )
38
+
39
+ const handshake: Effect.Effect<
40
+ Handshake,
41
+ never,
42
+ HttpServerRequest.HttpServerRequest
43
+ > = Effect.gen(function* () {
44
+ const request = yield* HttpServerRequest.HttpServerRequest
45
+ const { socket } = NodeHttpServerRequest.toIncomingMessage(request)
46
+ // Only a TLS-verified peer is certificate evidence; forwarded headers are not.
47
+ const peer =
48
+ socket instanceof TLSSocket && socket.authorized
49
+ ? Option.some(socket.getPeerCertificate())
50
+ : Option.none()
51
+ return {
52
+ authorization: Option.map(
53
+ Option.fromUndefinedOr(request.headers["authorization"]),
54
+ Redacted.make,
55
+ ),
56
+ certificate: Option.map(peer, (certificate) => ({
57
+ der: certificate.raw,
58
+ subjectAltName: Option.fromUndefinedOr(certificate.subjectaltname),
59
+ })),
60
+ }
61
+ })
62
+
63
+ const open = Effect.fnUntraced(function* (
64
+ createServer: Parameters<typeof NodeHttpServer.make>[0],
65
+ options: Pick<NodeHttpServer.Options, "host" | "port" | "path">,
66
+ ) {
67
+ // The lazily created ws server belongs here, so serving scopes close before ws.close waits.
68
+ const resources = yield* Effect.acquireRelease(Scope.make(), Scope.close)
69
+ const ready = yield* Deferred.make<void>()
70
+ const server = yield* NodeHttpServer.make(createServer, {
71
+ ...options,
72
+ disablePreemptiveShutdown: true,
73
+ websocket: {
74
+ perMessageDeflate: false,
75
+ maxPayload: payloadLimit,
76
+ handleProtocols: (protocols) =>
77
+ protocols.has(protocol) ? protocol : false,
78
+ },
79
+ }).pipe(Scope.provide(resources))
80
+ return {
81
+ address: server.address,
82
+ ready: Deferred.await(ready),
83
+ run: <A, E, R>(
84
+ accept: Effect.Effect<
85
+ (socket: Socket.Socket) => Effect.Effect<A, E, R>,
86
+ AuthenticationFailed,
87
+ HttpServerRequest.HttpServerRequest
88
+ >,
89
+ ) =>
90
+ server
91
+ .serve(
92
+ Effect.gen(function* () {
93
+ const request = yield* HttpServerRequest.HttpServerRequest
94
+ if (request.url !== path)
95
+ return HttpServerResponse.empty({ status: 404 })
96
+ if (
97
+ String.toLowerCase(request.headers["upgrade"] ?? "") !==
98
+ "websocket" ||
99
+ !Array.some(
100
+ String.split(
101
+ request.headers["sec-websocket-protocol"] ?? "",
102
+ ",",
103
+ ),
104
+ (value) => String.trim(value) === protocol,
105
+ )
106
+ )
107
+ return HttpServerResponse.empty({ status: 400 })
108
+ const session = yield* Effect.result(accept)
109
+ if (session._tag === "Failure")
110
+ return yield* rejection(session.failure)
111
+ // Authentication finishes before upgrade; session work is owned by the request scope.
112
+ yield* request.upgrade.pipe(
113
+ Effect.flatMap(session.success),
114
+ Effect.interruptible,
115
+ Effect.scoped,
116
+ )
117
+ return HttpServerResponse.empty()
118
+ }),
119
+ )
120
+ .pipe(
121
+ Effect.andThen(Deferred.succeed(ready, undefined)),
122
+ Effect.andThen(Effect.never),
123
+ ),
124
+ }
125
+ })
126
+
127
+ export const bind = Effect.fnUntraced(function* (
128
+ createServer: Parameters<typeof NodeHttpServer.make>[0],
129
+ options: Pick<NodeHttpServer.Options, "host" | "port" | "path">,
130
+ ) {
131
+ const server = yield* open(createServer, options)
132
+ return {
133
+ address: server.address,
134
+ ready: server.ready,
135
+ serve: (accept: Accept) =>
136
+ server
137
+ .run(
138
+ handshake.pipe(
139
+ Effect.flatMap(accept),
140
+ Effect.map(
141
+ (session) => (socket: Socket.Socket) =>
142
+ Channel.fromSocket(socket).pipe(Effect.flatMap(session)),
143
+ ),
144
+ ),
145
+ )
146
+ .pipe(
147
+ Effect.mapError((error) =>
148
+ TransportError.make({ cause: Cause.fail(error) }),
149
+ ),
150
+ ),
151
+ } satisfies Serving & { readonly address: typeof server.address }
152
+ })
@@ -0,0 +1,6 @@
1
+ import { Schema } from "effect"
2
+
3
+ export const Bootstrap = Schema.Struct({
4
+ contractModule: Schema.String,
5
+ session: Schema.String,
6
+ })