@graftland/plugin-remote 0.0.0-stage → 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 +76 -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 +69 -3
  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,901 @@
1
+ import * as FailureGroup from "../FailureGroup"
2
+ import {
3
+ Array,
4
+ Cause,
5
+ Clock,
6
+ Context,
7
+ Data,
8
+ Deferred,
9
+ Duration,
10
+ Effect,
11
+ Equal,
12
+ Exit,
13
+ FiberHandle,
14
+ FiberSet,
15
+ HashMap,
16
+ Layer,
17
+ Option,
18
+ RcMap,
19
+ Ref,
20
+ Result,
21
+ Scope,
22
+ Semaphore,
23
+ SynchronizedRef,
24
+ } from "effect"
25
+ import * as Release from "../release"
26
+ import { SecureRandom } from "effect/crypto"
27
+ import * as Retention from "./Retention"
28
+ import { cleanupCause } from "../cleanup"
29
+ import {
30
+ Execution,
31
+ Store,
32
+ StaleSession,
33
+ LeaseExpired,
34
+ SessionStopped,
35
+ SessionConflict,
36
+ StateStorageError,
37
+ ExecutionError,
38
+ PackageError,
39
+ TerminationUnconfirmed,
40
+ RunnerClosing,
41
+ childOf,
42
+ defaults,
43
+ keyOf,
44
+ negotiate,
45
+ stoppingRecord,
46
+ stoppedRecord,
47
+ type ActiveRecord,
48
+ type CodeIdentity,
49
+ type ExitStatus,
50
+ type Handle,
51
+ type Key,
52
+ type Limits,
53
+ type Load,
54
+ type SessionRecord,
55
+ type SessionKey,
56
+ type StopReason,
57
+ type Termination,
58
+ type Unit,
59
+ type OpenRecord,
60
+ type LoadedRecord,
61
+ type StoppingRecord,
62
+ type StoppedRecord,
63
+ } from "./model"
64
+
65
+ export type SupervisorError =
66
+ | StaleSession
67
+ | LeaseExpired
68
+ | SessionStopped
69
+ | SessionConflict
70
+ | StateStorageError
71
+ | ExecutionError
72
+ | PackageError
73
+ | TerminationUnconfirmed
74
+ | RunnerClosing
75
+ export interface Options {
76
+ readonly lease?: Duration.Input
77
+ readonly grace?: Duration.Input
78
+ readonly confirmationTimeout?: Duration.Input
79
+ readonly limits?: Limits
80
+ }
81
+ export interface Open {
82
+ readonly key: Key
83
+ readonly requestId: string
84
+ readonly expectedGeneration?: number | null
85
+ readonly lease?: Duration.Input
86
+ readonly limits?: Limits
87
+ }
88
+ export class Supervisor extends Context.Service<
89
+ Supervisor,
90
+ {
91
+ readonly await: Effect.Effect<never, SupervisorError>
92
+ readonly status: (
93
+ handle: Handle,
94
+ ) => Effect.Effect<SessionRecord, StaleSession>
95
+ readonly highWater: (key: Key) => Effect.Effect<number | null>
96
+ readonly open: (
97
+ request: Open,
98
+ ) => Effect.Effect<SessionRecord, SupervisorError>
99
+ readonly renew: (handle: Handle) => Effect.Effect<number, SupervisorError>
100
+ readonly load: (
101
+ handle: Handle,
102
+ request: Load,
103
+ ) => Effect.Effect<CodeIdentity, SupervisorError>
104
+ readonly dispose: (
105
+ handle: Handle,
106
+ ) => Effect.Effect<Termination, SupervisorError>
107
+ }
108
+ >()("@graftland/plugin-remote/internal/runner/Supervisor") {}
109
+
110
+ type Launch = Data.TaggedEnum<{
111
+ Starting: {}
112
+ Registered: { readonly unit: Unit }
113
+ }>
114
+ const Launch = Data.taggedEnum<Launch>()
115
+ type ExecutionState = Data.TaggedEnum<{
116
+ Absent: {}
117
+ Running: { readonly unit: Unit }
118
+ Exited: { readonly exit: ExitStatus }
119
+ }>
120
+ const ExecutionState = Data.taggedEnum<ExecutionState>()
121
+ type LoadReply = Deferred.Deferred<CodeIdentity, SupervisorError>
122
+ type StopReply = Deferred.Deferred<Termination, SupervisorError>
123
+ type Session = Data.TaggedEnum<{
124
+ Open: { readonly record: typeof OpenRecord.Type; readonly deadline: bigint }
125
+ Loading: {
126
+ readonly record: typeof OpenRecord.Type
127
+ readonly request: Load
128
+ readonly launch: Launch
129
+ readonly reply: LoadReply
130
+ readonly deadline: bigint
131
+ }
132
+ Loaded: {
133
+ readonly record: typeof LoadedRecord.Type
134
+ readonly unit: Unit
135
+ readonly deadline: bigint
136
+ }
137
+ Stopping: {
138
+ readonly record: typeof StoppingRecord.Type
139
+ readonly execution: ExecutionState
140
+ readonly reply: StopReply
141
+ }
142
+ Stopped: {
143
+ readonly record: typeof StoppedRecord.Type
144
+ readonly reply: StopReply
145
+ }
146
+ }>
147
+ const Session = Data.taggedEnum<Session>()
148
+ type Live = Extract<Session, { readonly _tag: "Open" | "Loading" | "Loaded" }>
149
+ type Loading = Extract<Session, { readonly _tag: "Loading" }>
150
+ type Stopping = Extract<Session, { readonly _tag: "Stopping" }>
151
+ interface Entry {
152
+ readonly state: SynchronizedRef.SynchronizedRef<Session>
153
+ readonly timer: FiberHandle.FiberHandle<void, never>
154
+ readonly loading: FiberHandle.FiberHandle<void, never>
155
+ readonly scope: Scope.Closeable
156
+ readonly unitScope: Scope.Closeable
157
+ }
158
+ function snapshot(state: Live): ActiveRecord
159
+ function snapshot(state: Session): SessionRecord
160
+ function snapshot(state: Session): SessionRecord {
161
+ return state._tag === "Loading"
162
+ ? {
163
+ ...state.record,
164
+ phase: "Loading",
165
+ load: state.request,
166
+ launch:
167
+ state.launch._tag === "Starting"
168
+ ? { _tag: "Starting" }
169
+ : { _tag: "Registered", child: state.launch.unit.identity },
170
+ }
171
+ : state.record
172
+ }
173
+ const live = (
174
+ state: Session,
175
+ now: bigint,
176
+ ): Result.Result<Live, LeaseExpired | SessionStopped> => {
177
+ if (state._tag === "Stopping" || state._tag === "Stopped")
178
+ return Result.fail(
179
+ state.record.reason._tag === "Expired"
180
+ ? LeaseExpired.make({
181
+ detail: "The session lease has expired",
182
+ })
183
+ : SessionStopped.make({
184
+ detail: "The session is stopping or stopped",
185
+ }),
186
+ )
187
+ return now >= state.deadline
188
+ ? Result.fail(
189
+ LeaseExpired.make({
190
+ detail: "The session lease has expired",
191
+ }),
192
+ )
193
+ : Result.succeed(state)
194
+ }
195
+ const executionOf = (state: Live): ExecutionState => {
196
+ switch (state._tag) {
197
+ case "Open":
198
+ return ExecutionState.Absent()
199
+ case "Loading":
200
+ return state.launch._tag === "Registered"
201
+ ? ExecutionState.Running({ unit: state.launch.unit })
202
+ : ExecutionState.Absent()
203
+ case "Loaded":
204
+ return ExecutionState.Running({ unit: state.unit })
205
+ }
206
+ }
207
+ /** All state transitions are pure; the caller serializes persistence and publication. */
208
+ const beginLoad = (
209
+ state: Extract<Session, { _tag: "Open" }>,
210
+ request: Load,
211
+ reply: LoadReply,
212
+ ): Loading =>
213
+ Session.Loading({
214
+ record: state.record,
215
+ deadline: state.deadline,
216
+ request,
217
+ reply,
218
+ launch: Launch.Starting(),
219
+ })
220
+ const registerUnit = (
221
+ state: Session,
222
+ unit: Unit,
223
+ ): Result.Result<Session, SessionStopped | ExecutionError> => {
224
+ if (state._tag !== "Loading")
225
+ return Result.fail(
226
+ SessionStopped.make({
227
+ detail: "The session is stopping or stopped",
228
+ }),
229
+ )
230
+ if (state.launch._tag === "Registered")
231
+ return Result.fail(
232
+ ExecutionError.make({
233
+ detail: "An execution adapter registered more than one child",
234
+ }),
235
+ )
236
+ return Result.succeed(
237
+ Session.Loading({ ...state, launch: Launch.Registered({ unit }) }),
238
+ )
239
+ }
240
+ const loaded = (
241
+ state: Session,
242
+ code: CodeIdentity,
243
+ ): Result.Result<Session, SessionStopped | ExecutionError> => {
244
+ if (state._tag !== "Loading")
245
+ return Result.fail(
246
+ SessionStopped.make({
247
+ detail: "The session is stopping or stopped",
248
+ }),
249
+ )
250
+ if (state.launch._tag !== "Registered")
251
+ return Result.fail(
252
+ ExecutionError.make({
253
+ detail: "Execution adapter returned without registering its child",
254
+ }),
255
+ )
256
+ return Result.succeed(
257
+ Session.Loaded({
258
+ deadline: state.deadline,
259
+ unit: state.launch.unit,
260
+ record: {
261
+ ...state.record,
262
+ phase: "Loaded",
263
+ load: state.request,
264
+ code,
265
+ child: state.launch.unit.identity,
266
+ },
267
+ }),
268
+ )
269
+ }
270
+ const beginStop = (
271
+ state: Live,
272
+ reason: StopReason,
273
+ reply: StopReply,
274
+ exit: Option.Option<ExitStatus>,
275
+ ): Stopping =>
276
+ Session.Stopping({
277
+ record: stoppingRecord(snapshot(state), reason),
278
+ reply,
279
+ execution: Option.match(exit, {
280
+ onNone: () => executionOf(state),
281
+ onSome: (exit) => ExecutionState.Exited({ exit }),
282
+ }),
283
+ })
284
+ const nanos = (millis: number) => BigInt(Math.ceil(millis * 1_000_000))
285
+ const emptyTermination: Termination = {
286
+ kind: "Exited",
287
+ exit: { code: 0, signal: null },
288
+ cause: Cause.empty,
289
+ }
290
+ const exitCause = <A, E>(exit: Exit.Exit<A, E>) =>
291
+ Exit.isFailure(exit) ? exit.cause : Cause.empty
292
+
293
+ /** A signal is a request; only the exited effect confirms that the execution has stopped. */
294
+ const terminate = (
295
+ execution: ExecutionState,
296
+ grace: number,
297
+ confirmation: number,
298
+ ): Effect.Effect<Termination, TerminationUnconfirmed> => {
299
+ if (execution._tag === "Absent") return Effect.succeed(emptyTermination)
300
+ if (execution._tag === "Exited")
301
+ return Effect.succeed({ ...emptyTermination, exit: execution.exit })
302
+ const child = execution.unit
303
+ return Effect.gen(function* () {
304
+ const graceful = yield* child.interrupt.pipe(
305
+ Effect.andThen(child.exited),
306
+ Effect.timeoutOption(grace),
307
+ Effect.exit,
308
+ )
309
+ if (Exit.isSuccess(graceful) && Option.isSome(graceful.value))
310
+ return { ...emptyTermination, exit: graceful.value.value }
311
+ const killed = yield* Effect.exit(child.kill)
312
+ const forced = yield* child.exited.pipe(
313
+ Effect.timeoutOption(confirmation),
314
+ Effect.exit,
315
+ )
316
+ const cause = Cause.combine(
317
+ Cause.combine(exitCause(graceful), exitCause(killed)),
318
+ exitCause(forced),
319
+ )
320
+ if (Exit.isFailure(forced) || Option.isNone(forced.value))
321
+ return yield* TerminationUnconfirmed.make({
322
+ detail: "The child exit could not be confirmed",
323
+ cause,
324
+ })
325
+ return {
326
+ kind: "Killed",
327
+ exit: forced.value.value,
328
+ cause,
329
+ } satisfies Termination
330
+ })
331
+ }
332
+
333
+ const initialize = (options: Options = {}) =>
334
+ Effect.gen(function* () {
335
+ const store = yield* Store
336
+ const execution = yield* Execution
337
+ const random = yield* SecureRandom.SecureRandom
338
+ const parent = yield* Effect.scope
339
+ const owner = yield* Scope.fork(parent)
340
+ // Cleanup must continue after a health failure so every registered process is reaped.
341
+ const workers = yield* FiberSet.make<void, never>().pipe(
342
+ Scope.provide(owner),
343
+ )
344
+ const locks = yield* RcMap.make({
345
+ lookup: (_key: SessionKey) => Semaphore.make(1),
346
+ }).pipe(Scope.provide(owner))
347
+ const lease = Duration.toMillis(options.lease ?? "15 seconds")
348
+ const grace = Duration.toMillis(options.grace ?? "5 seconds")
349
+ const confirmation = Duration.toMillis(
350
+ options.confirmationTimeout ?? "5 seconds",
351
+ )
352
+ const limits = options.limits ?? defaults
353
+ const records = yield* Ref.make(HashMap.empty<string, SessionRecord>())
354
+ const entries = yield* Ref.make(HashMap.empty<string, Entry>())
355
+ const health = yield* FailureGroup.make<SupervisorError>().pipe(
356
+ Scope.provide(owner),
357
+ )
358
+ const closing = yield* Ref.make(false)
359
+ return {
360
+ store,
361
+ execution,
362
+ random,
363
+ owner,
364
+ workers,
365
+ locks,
366
+ lease,
367
+ grace,
368
+ confirmation,
369
+ limits,
370
+ records,
371
+ entries,
372
+ health,
373
+ closing,
374
+ }
375
+ })
376
+ type Runtime = Effect.Success<ReturnType<typeof initialize>>
377
+ const operations = (runtime: Runtime) => {
378
+ const {
379
+ store,
380
+ execution,
381
+ workers,
382
+ grace,
383
+ confirmation,
384
+ records,
385
+ entries,
386
+ health,
387
+ closing,
388
+ } = runtime
389
+
390
+ const poison = health.failCause
391
+ const observe = <A>(effect: Effect.Effect<A, SupervisorError>) =>
392
+ effect.pipe(Effect.asVoid, Effect.catchCause(poison))
393
+ const healthy = health.check.pipe(
394
+ Effect.andThen(
395
+ Effect.gen(function* () {
396
+ if (yield* Ref.get(closing))
397
+ return yield* RunnerClosing.make({ detail: "The runner is closing" })
398
+ }),
399
+ ),
400
+ )
401
+ const persist = (record: SessionRecord) =>
402
+ store
403
+ .save(record)
404
+ .pipe(
405
+ Effect.tapCause(poison),
406
+ Effect.andThen(
407
+ Ref.update(records, (all) => Retention.save(all, record)),
408
+ ),
409
+ )
410
+ const status = (handle: Handle) =>
411
+ Ref.get(records).pipe(
412
+ Effect.flatMap((all) => {
413
+ const found = HashMap.get(all, handle.token)
414
+ return Option.isSome(found) &&
415
+ Equal.equals(keyOf(found.value.handle.key), keyOf(handle.key)) &&
416
+ found.value.handle.generation === handle.generation
417
+ ? Effect.succeed(found.value)
418
+ : Effect.fail(
419
+ StaleSession.make({
420
+ detail: "Unknown session or generation",
421
+ }),
422
+ )
423
+ }),
424
+ )
425
+ const latest = (key: Key) =>
426
+ Ref.get(records).pipe(
427
+ Effect.map((all) =>
428
+ Array.reduce(
429
+ Array.fromIterable(HashMap.values(all)),
430
+ 0,
431
+ (value, record) =>
432
+ Equal.equals(keyOf(record.handle.key), keyOf(key))
433
+ ? Math.max(value, record.handle.generation)
434
+ : value,
435
+ ),
436
+ ),
437
+ )
438
+ const entryFor = (handle: Handle) =>
439
+ Effect.gen(function* () {
440
+ yield* status(handle)
441
+ const all = yield* Ref.get(entries)
442
+ const found = HashMap.get(all, handle.token)
443
+ if (Option.isNone(found))
444
+ return yield* SessionStopped.make({
445
+ detail: "The session was retired during recovery",
446
+ })
447
+ return found.value
448
+ })
449
+ const commit = (
450
+ entry: Entry,
451
+ transition: (
452
+ state: Session,
453
+ ) => Result.Result<Session, ExecutionError | SessionStopped>,
454
+ ) =>
455
+ SynchronizedRef.updateEffect(entry.state, (state) =>
456
+ Effect.gen(function* () {
457
+ const next = yield* Effect.fromResult(transition(state))
458
+ yield* persist(snapshot(next))
459
+ return next
460
+ }),
461
+ )
462
+ const finishStop = (
463
+ entry: Entry,
464
+ state: Stopping,
465
+ saved: Exit.Exit<void, StateStorageError>,
466
+ ) =>
467
+ Effect.gen(function* () {
468
+ yield* FiberHandle.clear(entry.loading)
469
+ const result = yield* terminate(state.execution, grace, confirmation)
470
+ const closed = yield* Effect.exit(
471
+ Release.close(entry.unitScope, Exit.void),
472
+ )
473
+ const termination: Termination = {
474
+ ...result,
475
+ cause: Cause.combine(result.cause, exitCause(closed)),
476
+ }
477
+ const record = stoppedRecord(
478
+ state.record.previous,
479
+ state.record.reason,
480
+ termination,
481
+ )
482
+ yield* SynchronizedRef.updateEffect(entry.state, () =>
483
+ persist(record).pipe(
484
+ Effect.as(Session.Stopped({ record, reply: state.reply })),
485
+ ),
486
+ )
487
+ if (Exit.isFailure(saved)) return yield* Effect.failCause(saved.cause)
488
+ return termination
489
+ })
490
+ /** Called with the session ref locked; the worker starts after its transition is published. */
491
+ const stopLocked = (
492
+ entry: Entry,
493
+ state: Session,
494
+ reason: StopReason,
495
+ exit: Option.Option<ExitStatus>,
496
+ ): Effect.Effect<readonly [StopReply, Session]> =>
497
+ Effect.gen(function* () {
498
+ if (state._tag === "Stopping" || state._tag === "Stopped")
499
+ return [state.reply, state]
500
+ const reply = yield* Deferred.make<Termination, SupervisorError>()
501
+ const next = beginStop(state, reason, reply, exit)
502
+ // Fence in memory even when the journal cannot acknowledge the transition.
503
+ const saved = yield* Effect.exit(persist(next.record))
504
+ yield* FiberSet.run(
505
+ workers,
506
+ finishStop(entry, next, saved).pipe(
507
+ Effect.onExit((result) => Deferred.done(reply, result)),
508
+ observe,
509
+ ),
510
+ { startImmediately: false },
511
+ )
512
+ return [reply, next]
513
+ })
514
+ const stop = (
515
+ entry: Entry,
516
+ reason: StopReason,
517
+ exit: Option.Option<ExitStatus> = Option.none(),
518
+ ) =>
519
+ SynchronizedRef.modifyEffect(entry.state, (state) =>
520
+ stopLocked(entry, state, reason, exit),
521
+ ).pipe(Effect.uninterruptible)
522
+ const scheduleLease = (entry: Entry, deadline: bigint) =>
523
+ FiberHandle.run(
524
+ entry.timer,
525
+ observe(
526
+ Effect.gen(function* () {
527
+ const now = yield* Clock.monotonicTimeNanos
528
+ yield* Effect.sleep(
529
+ Duration.nanos(deadline > now ? deadline - now : 0n),
530
+ )
531
+ yield* SynchronizedRef.updateEffect(entry.state, (state) => {
532
+ if (
533
+ state._tag === "Stopping" ||
534
+ state._tag === "Stopped" ||
535
+ state.deadline !== deadline
536
+ )
537
+ return Effect.succeed(state)
538
+ return stopLocked(
539
+ entry,
540
+ state,
541
+ { _tag: "Expired" },
542
+ Option.none(),
543
+ ).pipe(Effect.map(([, next]) => next))
544
+ })
545
+ }),
546
+ ),
547
+ { startImmediately: false },
548
+ )
549
+ const register = (entry: Entry, unit: Unit) =>
550
+ SynchronizedRef.updateEffect(entry.state, (state) =>
551
+ Effect.gen(function* () {
552
+ const now = yield* Clock.monotonicTimeNanos
553
+ const current = yield* Effect.fromResult(live(state, now))
554
+ const next = yield* Effect.fromResult(registerUnit(current, unit))
555
+ yield* unit.renew(
556
+ Number(current.deadline - (yield* Clock.monotonicTimeNanos)) /
557
+ 1_000_000,
558
+ )
559
+ yield* persist(snapshot(next))
560
+ yield* FiberSet.run(
561
+ workers,
562
+ observe(
563
+ unit.exited.pipe(
564
+ Effect.flatMap((exit) =>
565
+ stop(entry, { _tag: "Exited" }, Option.some(exit)),
566
+ ),
567
+ ),
568
+ ),
569
+ )
570
+ return next
571
+ }),
572
+ ).pipe(Effect.uninterruptible)
573
+ const runLoad = (entry: Entry, state: Loading) =>
574
+ execution
575
+ .spawn(state.record.handle, state.request, state.record.limits)
576
+ .pipe(
577
+ Scope.provide(entry.unitScope),
578
+ Effect.flatMap((unit) =>
579
+ register(entry, unit).pipe(
580
+ Effect.andThen(unit.start),
581
+ Effect.as(unit.code),
582
+ ),
583
+ ),
584
+ Effect.tap((code) => commit(entry, (current) => loaded(current, code))),
585
+ Effect.onExit((result) =>
586
+ Effect.gen(function* () {
587
+ if (Exit.isSuccess(result)) {
588
+ yield* Deferred.done(state.reply, result)
589
+ return
590
+ }
591
+ const current = yield* SynchronizedRef.get(entry.state)
592
+ if (current._tag === "Loading")
593
+ yield* stop(entry, { _tag: "LoadFailed", cause: result.cause })
594
+ const healthCause = yield* health.cause
595
+ yield* Deferred.failCause(
596
+ state.reply,
597
+ Cause.combine(result.cause, healthCause),
598
+ )
599
+ }),
600
+ ),
601
+ // Load's caller observes its complete exit through the reply, including cancellation.
602
+ Effect.exit,
603
+ Effect.asVoid,
604
+ )
605
+
606
+ return {
607
+ persist,
608
+ status,
609
+ latest,
610
+ entryFor,
611
+ stop,
612
+ scheduleLease,
613
+ runLoad,
614
+ healthy,
615
+ }
616
+ }
617
+ type Operations = ReturnType<typeof operations>
618
+ const recover = (runtime: Runtime, actions: Operations) =>
619
+ Effect.gen(function* () {
620
+ const { store, execution, confirmation, records } = runtime
621
+ const { persist } = actions
622
+ // Recover before the service is published. Construction does not resume old leases.
623
+ const stored = yield* store.list
624
+ yield* Ref.set(
625
+ records,
626
+ HashMap.fromIterable(
627
+ Array.map(stored, (record) => [record.handle.token, record] as const),
628
+ ),
629
+ )
630
+ for (const record of stored) {
631
+ if (record.phase === "Stopped") continue
632
+ const child = childOf(record)
633
+ const termination = Option.isNone(child)
634
+ ? emptyTermination
635
+ : yield* execution.recover(child.value).pipe(
636
+ Effect.timeout(confirmation),
637
+ Effect.catchTags({
638
+ ExecutionError: (error) =>
639
+ Effect.fail(
640
+ TerminationUnconfirmed.make({
641
+ detail: "Orphan recovery did not confirm exit",
642
+ cause: Cause.fail(error),
643
+ }),
644
+ ),
645
+ TimeoutError: (error) =>
646
+ Effect.fail(
647
+ TerminationUnconfirmed.make({
648
+ detail: "Orphan recovery did not confirm exit",
649
+ cause: Cause.fail(error),
650
+ }),
651
+ ),
652
+ }),
653
+ )
654
+ yield* persist(
655
+ stoppedRecord(
656
+ record.phase === "Stopping" ? record.previous : record,
657
+ { _tag: "Recovery" },
658
+ termination,
659
+ ),
660
+ )
661
+ }
662
+ })
663
+ const ownShutdown = (runtime: Runtime, actions: Operations) =>
664
+ Effect.gen(function* () {
665
+ const { entries, health, closing } = runtime
666
+ const { stop } = actions
667
+ yield* Effect.addFinalizer(() =>
668
+ Effect.gen(function* () {
669
+ yield* Ref.set(closing, true)
670
+ const all = yield* Ref.get(entries)
671
+ const exits = yield* Effect.forEach(HashMap.values(all), (entry) =>
672
+ stop(entry, { _tag: "Shutdown" }).pipe(
673
+ Effect.flatMap(Deferred.await),
674
+ Effect.exit,
675
+ ),
676
+ )
677
+ const initial: Cause.Cause<unknown> = yield* health.cause
678
+ const cause = Array.reduce(exits, initial, (cause, exit) =>
679
+ Cause.combine(
680
+ cause,
681
+ Exit.isFailure(exit) ? exit.cause : exit.value.cause,
682
+ ),
683
+ )
684
+ if (cause.reasons.length > 0)
685
+ return yield* Effect.failCause(cleanupCause(cause))
686
+ }),
687
+ )
688
+ })
689
+ const protocol = (runtime: Runtime, actions: Operations) =>
690
+ Effect.gen(function* () {
691
+ const { random, owner, locks, lease, limits, records, entries, health } =
692
+ runtime
693
+ const {
694
+ persist,
695
+ status,
696
+ latest,
697
+ entryFor,
698
+ stop,
699
+ scheduleLease,
700
+ runLoad,
701
+ healthy,
702
+ } = actions
703
+ return Supervisor.of({
704
+ await: health.await,
705
+ status,
706
+ highWater: (key) =>
707
+ latest(key).pipe(Effect.map((value) => (value === 0 ? null : value))),
708
+ open: (request) =>
709
+ Effect.scoped(
710
+ Effect.gen(function* () {
711
+ yield* healthy
712
+ const leaseMillis = Duration.toMillis(request.lease ?? lease)
713
+ const negotiated = negotiate(limits, request.limits ?? defaults)
714
+ const lock = yield* RcMap.get(locks, keyOf(request.key))
715
+ return yield* lock.withPermit(
716
+ Effect.gen(function* () {
717
+ yield* healthy
718
+ const allRecords = yield* Ref.get(records)
719
+ const same = Array.filter(
720
+ Array.fromIterable(HashMap.values(allRecords)),
721
+ (row) =>
722
+ Equal.equals(keyOf(row.handle.key), keyOf(request.key)),
723
+ )
724
+ const prior = Array.findFirst(
725
+ same,
726
+ (row) => row.requestId === request.requestId,
727
+ )
728
+ if (Option.isSome(prior)) return prior.value
729
+ const generation = yield* latest(request.key)
730
+ if (generation !== (request.expectedGeneration ?? 0))
731
+ return yield* StaleSession.make({
732
+ detail:
733
+ "Open expectedGeneration does not match the generation high-water mark",
734
+ })
735
+ for (const record of same) {
736
+ const allEntries = yield* Ref.get(entries)
737
+ const entry = HashMap.get(allEntries, record.handle.token)
738
+ if (Option.isSome(entry))
739
+ yield* stop(entry.value, { _tag: "Superseded" }).pipe(
740
+ Effect.flatMap(Deferred.await),
741
+ Effect.tap((result) =>
742
+ result.cause.reasons.length === 0
743
+ ? Effect.void
744
+ : Effect.logWarning(
745
+ "Superseded remote execution cleanup failed",
746
+ result.cause,
747
+ ).pipe(
748
+ Effect.annotateLogs({
749
+ "remote.identity": record.handle.key.identity,
750
+ "remote.instance": record.handle.key.instance,
751
+ "remote.generation": record.handle.generation,
752
+ }),
753
+ ),
754
+ ),
755
+ )
756
+ }
757
+ yield* healthy
758
+ const token = yield* random.uuidV4()
759
+ const record: typeof OpenRecord.Type = {
760
+ version: 1,
761
+ phase: "Open",
762
+ handle: {
763
+ key: request.key,
764
+ token,
765
+ generation: generation + 1,
766
+ },
767
+ requestId: request.requestId,
768
+ leaseMillis,
769
+ limits: negotiated,
770
+ }
771
+ return yield* Effect.uninterruptible(
772
+ Effect.gen(function* () {
773
+ yield* persist(record)
774
+ const deadline =
775
+ (yield* Clock.monotonicTimeNanos) + nanos(leaseMillis)
776
+ const scope = yield* Scope.fork(owner)
777
+ const entry: Entry = {
778
+ state: yield* SynchronizedRef.make<Session>(
779
+ Session.Open({ record, deadline }),
780
+ ),
781
+ scope,
782
+ unitScope: yield* Scope.fork(scope),
783
+ timer: yield* FiberHandle.make<void, never>().pipe(
784
+ Scope.provide(scope),
785
+ ),
786
+ loading: yield* FiberHandle.make<void, never>().pipe(
787
+ Scope.provide(scope),
788
+ ),
789
+ }
790
+ // The store's retention policy is also the in-memory eviction policy.
791
+ const retained = yield* Ref.get(records)
792
+ const old = yield* Ref.modify(
793
+ entries,
794
+ (all) =>
795
+ [
796
+ Array.filter(
797
+ Array.fromIterable(HashMap.entries(all)),
798
+ ([key]) => !HashMap.has(retained, key),
799
+ ),
800
+ HashMap.set(
801
+ HashMap.filter(all, (_, key) =>
802
+ HashMap.has(retained, key),
803
+ ),
804
+ token,
805
+ entry,
806
+ ),
807
+ ] as const,
808
+ )
809
+ for (const [, retired] of old)
810
+ yield* Release.close(retired.scope, Exit.void)
811
+ yield* scheduleLease(entry, deadline)
812
+ return record
813
+ }),
814
+ )
815
+ }),
816
+ )
817
+ }),
818
+ ),
819
+ renew: (handle) =>
820
+ Effect.gen(function* () {
821
+ yield* healthy
822
+ const entry = yield* entryFor(handle)
823
+ return yield* SynchronizedRef.modifyEffect(entry.state, (state) =>
824
+ Effect.gen(function* () {
825
+ const now = yield* Clock.monotonicTimeNanos
826
+ const current = yield* Effect.fromResult(live(state, now))
827
+ const running = executionOf(current)
828
+ if (running._tag === "Running")
829
+ yield* running.unit.renew(current.record.leaseMillis)
830
+ const deadline = now + nanos(current.record.leaseMillis)
831
+ yield* scheduleLease(entry, deadline)
832
+ return [
833
+ current.record.leaseMillis,
834
+ { ...current, deadline },
835
+ ] as const
836
+ }),
837
+ )
838
+ }),
839
+ load: (handle, request) =>
840
+ Effect.gen(function* () {
841
+ yield* healthy
842
+ const entry = yield* entryFor(handle)
843
+ const reply = yield* SynchronizedRef.modifyEffect(
844
+ entry.state,
845
+ (state) =>
846
+ Effect.gen(function* () {
847
+ const now = yield* Clock.monotonicTimeNanos
848
+ const current = yield* Effect.fromResult(live(state, now))
849
+ if (current._tag !== "Open") {
850
+ const before =
851
+ current._tag === "Loading"
852
+ ? current.request
853
+ : current.record.load
854
+ if (before.specifier !== request.specifier)
855
+ return yield* SessionConflict.make({
856
+ detail:
857
+ "Load was already requested with a different specifier",
858
+ })
859
+ return [
860
+ current._tag === "Loading"
861
+ ? Deferred.await(current.reply)
862
+ : Effect.succeed(current.record.code),
863
+ current,
864
+ ] as const
865
+ }
866
+ const done = yield* Deferred.make<
867
+ CodeIdentity,
868
+ SupervisorError
869
+ >()
870
+ const next = beginLoad(current, request, done)
871
+ yield* persist(snapshot(next))
872
+ if (next._tag === "Loading")
873
+ yield* FiberHandle.run(entry.loading, runLoad(entry, next), {
874
+ startImmediately: false,
875
+ })
876
+ return [Deferred.await(done), next] as const
877
+ }),
878
+ ).pipe(Effect.uninterruptible)
879
+ return yield* reply
880
+ }),
881
+ dispose: (handle) =>
882
+ Effect.gen(function* () {
883
+ const record = yield* status(handle)
884
+ if (record.phase === "Stopped") return record.termination
885
+ const entry = yield* entryFor(handle)
886
+ return yield* stop(entry, { _tag: "Disposed" }).pipe(
887
+ Effect.flatMap(Deferred.await),
888
+ )
889
+ }),
890
+ })
891
+ })
892
+ export const make = (options: Options = {}) =>
893
+ Effect.gen(function* () {
894
+ const runtime = yield* initialize(options)
895
+ const actions = operations(runtime)
896
+ yield* recover(runtime, actions)
897
+ yield* ownShutdown(runtime, actions)
898
+ return yield* protocol(runtime, actions)
899
+ })
900
+ export const layer = (options: Options = {}) =>
901
+ Layer.effect(Supervisor, make(options))