@graftland/plugin-remote 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/README.md +18 -54
  2. package/README.zh-CN.md +17 -51
  3. package/README.zh-TW.md +17 -51
  4. package/dist/Auth.d.ts.map +1 -1
  5. package/dist/Auth.js.map +1 -1
  6. package/dist/Config.d.ts +1151 -0
  7. package/dist/Config.d.ts.map +1 -0
  8. package/dist/Config.js +183 -0
  9. package/dist/Config.js.map +1 -0
  10. package/dist/Contract.d.ts +1 -1
  11. package/dist/Contract.d.ts.map +1 -1
  12. package/dist/Contract.js +32 -1
  13. package/dist/Contract.js.map +1 -1
  14. package/dist/Instance.d.ts.map +1 -1
  15. package/dist/Instance.js.map +1 -1
  16. package/dist/Testing.d.ts +1 -1
  17. package/dist/Testing.d.ts.map +1 -1
  18. package/dist/Testing.js.map +1 -1
  19. package/dist/Transport.d.ts +1 -1
  20. package/dist/Transport.d.ts.map +1 -1
  21. package/dist/Transport.js.map +1 -1
  22. package/dist/cli.d.ts +4 -0
  23. package/dist/cli.d.ts.map +1 -0
  24. package/dist/cli.js +25 -0
  25. package/dist/cli.js.map +1 -0
  26. package/dist/host/auth/bearer.d.ts.map +1 -1
  27. package/dist/host/auth/bearer.js +4 -2
  28. package/dist/host/auth/bearer.js.map +1 -1
  29. package/dist/host/auth/jwt.d.ts.map +1 -1
  30. package/dist/host/auth/jwt.js +4 -2
  31. package/dist/host/auth/jwt.js.map +1 -1
  32. package/dist/host/auth/mtls.d.ts +3 -7
  33. package/dist/host/auth/mtls.d.ts.map +1 -1
  34. package/dist/host/auth/mtls.js +13 -14
  35. package/dist/host/auth/mtls.js.map +1 -1
  36. package/dist/host/auth/none.d.ts +1 -1
  37. package/dist/host/auth/none.js +7 -5
  38. package/dist/host/auth/none.js.map +1 -1
  39. package/dist/host/command.d.ts +1 -1
  40. package/dist/host/command.d.ts.map +1 -1
  41. package/dist/host/command.js +11 -9
  42. package/dist/host/command.js.map +1 -1
  43. package/dist/host/local.d.ts.map +1 -1
  44. package/dist/host/local.js +9 -30
  45. package/dist/host/local.js.map +1 -1
  46. package/dist/host/unix.d.ts +1 -1
  47. package/dist/host/unix.d.ts.map +1 -1
  48. package/dist/host/unix.js +13 -9
  49. package/dist/host/unix.js.map +1 -1
  50. package/dist/host/websocket.d.ts +1 -1
  51. package/dist/host/websocket.d.ts.map +1 -1
  52. package/dist/host/websocket.js +11 -8
  53. package/dist/host/websocket.js.map +1 -1
  54. package/dist/host/worker.d.ts +1 -1
  55. package/dist/host/worker.d.ts.map +1 -1
  56. package/dist/host/worker.js +13 -9
  57. package/dist/host/worker.js.map +1 -1
  58. package/dist/index.d.ts +6 -8
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +30 -64
  61. package/dist/index.js.map +1 -1
  62. package/dist/internal/Certificates.js.map +1 -1
  63. package/dist/internal/FailureGroup.js +6 -6
  64. package/dist/internal/FailureGroup.js.map +1 -1
  65. package/dist/internal/Peer.js.map +1 -1
  66. package/dist/internal/{ServeLifecycle.js → RunnerLifecycle.js} +6 -6
  67. package/dist/internal/RunnerLifecycle.js.map +1 -0
  68. package/dist/internal/Supervisor.js.map +1 -1
  69. package/dist/internal/assembly.d.ts +3 -3
  70. package/dist/internal/assembly.js +8 -8
  71. package/dist/internal/assembly.js.map +1 -1
  72. package/dist/internal/auth/config.d.ts +27 -0
  73. package/dist/internal/auth/config.d.ts.map +1 -0
  74. package/dist/internal/auth/config.js +29 -0
  75. package/dist/internal/auth/config.js.map +1 -0
  76. package/dist/internal/auth/mtls.js.map +1 -1
  77. package/dist/internal/auth/token.js.map +1 -1
  78. package/dist/internal/auth/tokenCredentials.d.ts +1 -1
  79. package/dist/internal/auth/tokenCredentials.d.ts.map +1 -1
  80. package/dist/internal/auth/tokenCredentials.js +1 -1
  81. package/dist/internal/auth/tokenCredentials.js.map +1 -1
  82. package/dist/internal/child.js +3 -3
  83. package/dist/internal/child.js.map +1 -1
  84. package/dist/internal/childMain.js +47 -0
  85. package/dist/internal/childMain.js.map +1 -0
  86. package/dist/internal/childRuntime.js +8 -8
  87. package/dist/internal/childRuntime.js.map +1 -1
  88. package/dist/internal/cleanup.js +3 -3
  89. package/dist/internal/cleanup.js.map +1 -1
  90. package/dist/internal/contract.js.map +1 -1
  91. package/dist/internal/credentials.d.ts +3 -2
  92. package/dist/internal/credentials.d.ts.map +1 -1
  93. package/dist/internal/credentials.js +1 -1
  94. package/dist/internal/credentials.js.map +1 -1
  95. package/dist/internal/directTransport.js.map +1 -1
  96. package/dist/internal/entry.d.ts +104 -26
  97. package/dist/internal/entry.d.ts.map +1 -1
  98. package/dist/internal/entry.js +22 -9
  99. package/dist/internal/entry.js.map +1 -1
  100. package/dist/internal/entryConfig.d.ts +2 -202
  101. package/dist/internal/entryConfig.js +2 -136
  102. package/dist/internal/entryConfig.js.map +1 -1
  103. package/dist/internal/execution.d.ts +4 -4
  104. package/dist/internal/execution.js +11 -11
  105. package/dist/internal/execution.js.map +1 -1
  106. package/dist/internal/follow.js +1 -1
  107. package/dist/internal/follow.js.map +1 -1
  108. package/dist/internal/forkTree.js.map +1 -1
  109. package/dist/internal/host/local.js.map +1 -1
  110. package/dist/internal/host/runner.js +4 -4
  111. package/dist/internal/host/runner.js.map +1 -1
  112. package/dist/internal/host.js +2 -2
  113. package/dist/internal/host.js.map +1 -1
  114. package/dist/internal/importer.js +1 -1
  115. package/dist/internal/importer.js.map +1 -1
  116. package/dist/internal/listenerStartup.js +1 -1
  117. package/dist/internal/listenerStartup.js.map +1 -1
  118. package/dist/internal/loadModule.js.map +1 -1
  119. package/dist/internal/platform.js +1 -9
  120. package/dist/internal/port.d.ts +1 -1
  121. package/dist/internal/port.js +2 -2
  122. package/dist/internal/port.js.map +1 -1
  123. package/dist/internal/runner/Bootstrap.js.map +1 -1
  124. package/dist/internal/runner/Connection.d.ts +3 -2
  125. package/dist/internal/runner/Connection.js +6 -6
  126. package/dist/internal/runner/Connection.js.map +1 -1
  127. package/dist/internal/runner/Execution.js +6 -6
  128. package/dist/internal/runner/Execution.js.map +1 -1
  129. package/dist/internal/runner/Local.js +1 -1
  130. package/dist/internal/runner/Local.js.map +1 -1
  131. package/dist/internal/runner/Lock.js.map +1 -1
  132. package/dist/internal/runner/Multiplex.js.map +1 -1
  133. package/dist/internal/runner/Packages.js.map +1 -1
  134. package/dist/internal/runner/ProcessObservation.js.map +1 -1
  135. package/dist/internal/runner/Processes.js.map +1 -1
  136. package/dist/internal/runner/Retention.js.map +1 -1
  137. package/dist/internal/runner/Store.js.map +1 -1
  138. package/dist/internal/runner/Supervisor.js +5 -5
  139. package/dist/internal/runner/Supervisor.js.map +1 -1
  140. package/dist/internal/runner/Watchdog.js.map +1 -1
  141. package/dist/internal/runner/Wire.d.ts +1 -1
  142. package/dist/internal/runner/Wire.js.map +1 -1
  143. package/dist/internal/runner/accept.js +2 -2
  144. package/dist/internal/runner/accept.js.map +1 -1
  145. package/dist/internal/runner/command.js +1 -1
  146. package/dist/internal/runner/command.js.map +1 -1
  147. package/dist/internal/runner/connectionEnded.js +1 -1
  148. package/dist/internal/runner/connectionEnded.js.map +1 -1
  149. package/dist/internal/runner/model.d.ts.map +1 -1
  150. package/dist/internal/runner/model.js.map +1 -1
  151. package/dist/internal/services.d.ts.map +1 -1
  152. package/dist/internal/services.js +1 -1
  153. package/dist/internal/services.js.map +1 -1
  154. package/dist/internal/socketChannel.js.map +1 -1
  155. package/dist/internal/stdioChannel.js.map +1 -1
  156. package/dist/internal/supervisorPlugin.js +48 -1
  157. package/dist/internal/supervisorPlugin.js.map +1 -1
  158. package/dist/internal/transportOptions.d.ts.map +1 -1
  159. package/dist/internal/watchdogMain.js +5 -3
  160. package/dist/internal/watchdogMain.js.map +1 -1
  161. package/dist/internal/websocket.js.map +1 -1
  162. package/dist/internal/websocketServer.js +1 -1
  163. package/dist/internal/websocketServer.js.map +1 -1
  164. package/dist/internal/workerChannel.js.map +1 -1
  165. package/dist/internal/workerMain.js +13 -6
  166. package/dist/internal/workerMain.js.map +1 -1
  167. package/dist/runner/auth/bearer.d.ts +3 -5
  168. package/dist/runner/auth/bearer.d.ts.map +1 -1
  169. package/dist/runner/auth/bearer.js +13 -11
  170. package/dist/runner/auth/bearer.js.map +1 -1
  171. package/dist/runner/auth/jwt.d.ts +4 -18
  172. package/dist/runner/auth/jwt.d.ts.map +1 -1
  173. package/dist/runner/auth/jwt.js +16 -28
  174. package/dist/runner/auth/jwt.js.map +1 -1
  175. package/dist/runner/auth/mtls.d.ts +3 -6
  176. package/dist/runner/auth/mtls.d.ts.map +1 -1
  177. package/dist/runner/auth/mtls.js +13 -29
  178. package/dist/runner/auth/mtls.js.map +1 -1
  179. package/dist/runner/auth/none.d.ts +1 -1
  180. package/dist/runner/auth/none.js +7 -5
  181. package/dist/runner/auth/none.js.map +1 -1
  182. package/dist/runner/index.d.ts +3 -308
  183. package/dist/runner/index.d.ts.map +1 -1
  184. package/dist/runner/index.js +62 -30
  185. package/dist/runner/index.js.map +1 -1
  186. package/dist/runner/stdio.d.ts +1 -1
  187. package/dist/runner/stdio.d.ts.map +1 -1
  188. package/dist/runner/stdio.js +11 -9
  189. package/dist/runner/stdio.js.map +1 -1
  190. package/dist/runner/unix.d.ts +1 -1
  191. package/dist/runner/unix.d.ts.map +1 -1
  192. package/dist/runner/unix.js +10 -8
  193. package/dist/runner/unix.js.map +1 -1
  194. package/dist/runner/websocket.d.ts +1 -1
  195. package/dist/runner/websocket.d.ts.map +1 -1
  196. package/dist/runner/websocket.js +12 -10
  197. package/dist/runner/websocket.js.map +1 -1
  198. package/package.json +14 -12
  199. package/schema.json +2269 -0
  200. package/src/Auth.ts +8 -2
  201. package/src/Config.ts +196 -0
  202. package/src/Contract.ts +12 -3
  203. package/src/Instance.ts +1 -0
  204. package/src/Testing.ts +6 -4
  205. package/src/Transport.ts +12 -3
  206. package/src/cli.ts +60 -0
  207. package/src/host/auth/bearer.ts +1 -0
  208. package/src/host/auth/jwt.ts +1 -0
  209. package/src/host/auth/mtls.ts +7 -12
  210. package/src/host/auth/none.ts +3 -1
  211. package/src/host/command.ts +8 -5
  212. package/src/host/local.ts +4 -2
  213. package/src/host/unix.ts +11 -7
  214. package/src/host/websocket.ts +10 -7
  215. package/src/host/worker.ts +8 -5
  216. package/src/index.ts +36 -23
  217. package/src/internal/Certificates.ts +1 -0
  218. package/src/internal/FailureGroup.ts +5 -3
  219. package/src/internal/Peer.ts +1 -0
  220. package/src/internal/{ServeLifecycle.ts → RunnerLifecycle.ts} +7 -5
  221. package/src/internal/Supervisor.ts +5 -4
  222. package/src/internal/assembly.ts +11 -9
  223. package/src/internal/auth/config.ts +44 -0
  224. package/src/internal/auth/mtls.ts +1 -0
  225. package/src/internal/auth/token.ts +1 -0
  226. package/src/internal/auth/tokenCredentials.ts +4 -2
  227. package/src/internal/child.ts +8 -6
  228. package/src/{bin.ts → internal/childMain.ts} +16 -61
  229. package/src/internal/childRuntime.ts +12 -10
  230. package/src/internal/cleanup.ts +3 -2
  231. package/src/internal/contract.ts +1 -0
  232. package/src/internal/credentials.ts +3 -1
  233. package/src/internal/directTransport.ts +9 -2
  234. package/src/internal/entry.ts +53 -16
  235. package/src/internal/entryConfig.ts +10 -132
  236. package/src/internal/execution.ts +17 -15
  237. package/src/internal/follow.ts +2 -1
  238. package/src/internal/forkTree.ts +2 -1
  239. package/src/internal/host/local.ts +1 -0
  240. package/src/internal/host/runner.ts +7 -5
  241. package/src/internal/host.ts +9 -8
  242. package/src/internal/importer.ts +3 -2
  243. package/src/internal/listenerStartup.ts +4 -2
  244. package/src/internal/loadModule.ts +1 -0
  245. package/src/internal/platform.ts +1 -12
  246. package/src/internal/port.ts +11 -6
  247. package/src/internal/runner/Bootstrap.ts +1 -0
  248. package/src/internal/runner/Connection.ts +13 -11
  249. package/src/internal/runner/Execution.ts +14 -13
  250. package/src/internal/runner/Local.ts +3 -2
  251. package/src/internal/runner/Lock.ts +1 -0
  252. package/src/internal/runner/Multiplex.ts +2 -1
  253. package/src/internal/runner/Packages.ts +7 -5
  254. package/src/internal/runner/ProcessObservation.ts +2 -1
  255. package/src/internal/runner/Processes.ts +3 -1
  256. package/src/internal/runner/Retention.ts +1 -0
  257. package/src/internal/runner/Store.ts +3 -2
  258. package/src/internal/runner/Supervisor.ts +22 -20
  259. package/src/internal/runner/Watchdog.ts +3 -1
  260. package/src/internal/runner/Wire.ts +13 -12
  261. package/src/internal/runner/accept.ts +6 -5
  262. package/src/internal/runner/command.ts +3 -2
  263. package/src/internal/runner/connectionEnded.ts +2 -1
  264. package/src/internal/runner/model.ts +5 -3
  265. package/src/internal/services.ts +9 -2
  266. package/src/internal/socketChannel.ts +2 -1
  267. package/src/internal/stdioChannel.ts +4 -2
  268. package/src/internal/supervisorPlugin.ts +10 -8
  269. package/src/internal/transportOptions.ts +1 -0
  270. package/src/internal/watchdogMain.ts +5 -3
  271. package/src/internal/websocket.ts +5 -2
  272. package/src/internal/websocketServer.ts +6 -4
  273. package/src/internal/workerChannel.ts +1 -0
  274. package/src/internal/workerMain.ts +21 -5
  275. package/src/runner/auth/bearer.ts +10 -6
  276. package/src/runner/auth/jwt.ts +10 -41
  277. package/src/runner/auth/mtls.ts +6 -3
  278. package/src/runner/auth/none.ts +3 -1
  279. package/src/runner/index.ts +102 -11
  280. package/src/runner/stdio.ts +15 -10
  281. package/src/runner/unix.ts +7 -4
  282. package/src/runner/websocket.ts +9 -7
  283. package/dist/Auth-BElTC9_X.js +0 -6
  284. package/dist/Bootstrap-H9gDsgsC.js +0 -9
  285. package/dist/ConfigDirectory-BCC6q2R4.js +0 -5
  286. package/dist/Contract-DsJtisvL.js +0 -83
  287. package/dist/Errors-DYhsGZcZ.js +0 -58
  288. package/dist/FailureGroup-BgT73AaR.js +0 -63
  289. package/dist/Framing-G9eSaeLb.js +0 -61
  290. package/dist/Graft-B71CPA2Y.js +0 -996
  291. package/dist/Packages-AgoCGotL.js +0 -108
  292. package/dist/Peer-DXeVrGBh.js +0 -342
  293. package/dist/ProcessObservation-gLgGirYv.js +0 -38
  294. package/dist/ServeLifecycle-Bd1FPJ6e.js +0 -24
  295. package/dist/Transport-DULzpsgR.js +0 -6
  296. package/dist/WatchdogProtocol-BZmVxrBP.js +0 -17
  297. package/dist/bin.js +0 -28276
  298. package/dist/bin.js.map +0 -1
  299. package/dist/childRuntime-Bm3iSg_N.js +0 -192
  300. package/dist/command-9zrHdbwT.js +0 -7
  301. package/dist/connectionEnded-BwcxQ7W8.js +0 -54
  302. package/dist/directTransport-DrexuPSm.js +0 -5
  303. package/dist/entry-psOpX3Bj.js +0 -736
  304. package/dist/importer-Cvf88eAY.js +0 -344
  305. package/dist/internal/ServeLifecycle.js.map +0 -1
  306. package/dist/internal/entryConfig.d.ts.map +0 -1
  307. package/dist/internal/platform.js.map +0 -1
  308. package/dist/internal/release.js +0 -25
  309. package/dist/internal/release.js.map +0 -1
  310. package/dist/internal/serve.js +0 -60
  311. package/dist/internal/serve.js.map +0 -1
  312. package/dist/port--3x2zJ4D.js +0 -285
  313. package/dist/release-DfZPuIcK.js +0 -23
  314. package/dist/rolldown-runtime-DYf_mQCB.js +0 -34
  315. package/dist/socketChannel-CJvL2DiB.js +0 -3658
  316. package/dist/supervisorPlugin-t6Z6PrhD.js +0 -733
  317. package/dist/token-CYBNNlhS.js +0 -17
  318. package/dist/tokenCredentials-CbCuk-Rt.js +0 -31
  319. package/dist/websocketServer-NQhODlEt.js +0 -60
  320. package/dist/workerChannel-C8hhBJzA.js +0 -27
  321. package/src/internal/release.ts +0 -72
  322. package/src/internal/serve.ts +0 -119
  323. /package/dist/{bin.d.ts → internal/childMain.d.ts} +0 -0
@@ -1,733 +0,0 @@
1
- import { a as provide, l as make$2, o as fork, u as optional } from "./Graft-B71CPA2Y.js";
2
- import { a as PositiveDuration, i as Supervisor$1, t as resolve } from "./Packages-AgoCGotL.js";
3
- import { t as ConfigDirectory } from "./ConfigDirectory-BCC6q2R4.js";
4
- import { C as negotiate, E as make$3, S as keyOf, T as stoppingRecord, _ as StateStorageError, a as ExecutionError, d as SessionAccessDenied, f as SessionConflict, g as StateDirectoryLocked, h as StaleSession, i as Execution, m as SessionStopped, n as Session$1, o as Key, p as SessionRecord, r as channel, s as LeaseExpired, t as Peer, u as RunnerClosing, v as Store, w as stoppedRecord, x as childOf, y as TerminationUnconfirmed } from "./Peer-DXeVrGBh.js";
5
- import { t as observe } from "./ProcessObservation-gLgGirYv.js";
6
- import { r as cleanupCause, t as make$4 } from "./FailureGroup-BgT73AaR.js";
7
- import { t as CommandJson } from "./WatchdogProtocol-BZmVxrBP.js";
8
- import { u as TransportError } from "./Errors-DYhsGZcZ.js";
9
- import { t as Bootstrap } from "./Bootstrap-H9gDsgsC.js";
10
- import { t as command } from "./command-9zrHdbwT.js";
11
- import { n as close } from "./release-DfZPuIcK.js";
12
- import { n as make$5, o as defaults } from "./port--3x2zJ4D.js";
13
- import { i as resources, t as ServeLifecycle } from "./ServeLifecycle-Bd1FPJ6e.js";
14
- import { Array, Cause, Clock, Context, Data, Deferred, Duration, Effect, Equal, Exit, FiberHandle, FiberSet, FileSystem, HashMap, HashSet, Layer, Option, Path, Queue, RcMap, Ref, Result, Schedule, Schema, Scope, Semaphore, Sink, Stream, String, SynchronizedRef } from "effect";
15
- import process from "node:process";
16
- import { Digest, SecureRandom } from "effect/crypto";
17
- import { RpcServer } from "effect/rpc";
18
- import { ChildProcess, ChildProcessSpawner } from "effect/process";
19
- // packages/remote/src/internal/runner/Lock.ts
20
- /** SQLite owns the local directory lock for the enclosing scope, including failed startup. */
21
- const acquire = Effect.fnUntraced(function* (directory) {
22
- const fs = yield* FileSystem.FileSystem;
23
- const path = yield* Path.Path;
24
- yield* fs.makeDirectory(directory, { recursive: true }).pipe(Effect.uninterruptible);
25
- const { DatabaseSync } = yield* Effect.promise(() => import("node:sqlite")).pipe(Effect.uninterruptible);
26
- const locked = (cause) => StateDirectoryLocked.make({
27
- detail: "The runner state directory is already owned or cannot be locked",
28
- cause: Cause.die(cause)
29
- });
30
- const db = yield* Effect.acquireRelease(Effect.try({
31
- try: () => new DatabaseSync(path.join(directory, "owner.sqlite")),
32
- catch: locked
33
- }), (db) => Effect.sync(() => db.close()));
34
- yield* Effect.try({
35
- try: () => db.exec("BEGIN EXCLUSIVE"),
36
- catch: locked
37
- });
38
- });
39
- // packages/remote/src/internal/runner/Retention.ts
40
- /** A terminal high-water record fences every older Open, even after its request id is forgotten. */
41
- const retain = (records) => {
42
- const highWater = Array.reduce(records, HashMap.empty(), (water, record) => HashMap.set(water, keyOf(record.handle.key), Math.max(Option.getOrElse(HashMap.get(water, keyOf(record.handle.key)), () => 0), record.handle.generation)));
43
- return Array.filter(records, (record) => record.phase !== "Stopped" || record.handle.generation === Option.getOrUndefined(HashMap.get(highWater, keyOf(record.handle.key))));
44
- };
45
- /** Apply the same retention rule to both in-memory stores. */
46
- const save = (records, record) => HashMap.fromIterable(Array.map(retain(Array.fromIterable(HashMap.values(HashMap.set(records, record.handle.token, record)))), (record) => [record.handle.token, record]));
47
- // packages/remote/src/internal/runner/Store.ts
48
- /** Injectable store used by deterministic supervisor tests. */
49
- const memory = Effect.gen(function* () {
50
- const records = yield* Ref.make(HashMap.empty());
51
- return Store.of({
52
- list: Ref.get(records).pipe(Effect.map((records) => Array.fromIterable(HashMap.values(records)))),
53
- save: (record) => Ref.update(records, (all) => save(all, record))
54
- });
55
- });
56
- /**
57
- * Atomically replaced records retain unfinished sessions and each key's high-water mark.
58
- * Older terminal request ids expire; Open fences their retries with expectedGeneration.
59
- * Requires exclusive directory ownership; it is not a multi-writer database.
60
- */
61
- const directory = Effect.fnUntraced(function* (root) {
62
- const fs = yield* FileSystem.FileSystem;
63
- const path = yield* Path.Path;
64
- const digest = yield* Digest.Digest;
65
- const json = Schema.fromJsonString(Schema.toCodecJson(SessionRecord));
66
- const random = yield* SecureRandom.SecureRandom;
67
- const storage = (effect) => effect.pipe(Effect.mapError((error) => error._tag === "StateStorageError" ? error : StateStorageError.make({
68
- detail: "Runner state storage failed",
69
- cause: Cause.fail(error)
70
- })));
71
- yield* storage(fs.makeDirectory(root, { recursive: true }).pipe(Effect.uninterruptible));
72
- const mutex = yield* Semaphore.make(1);
73
- const read = Effect.gen(function* () {
74
- const files = yield* fs.readDirectory(root);
75
- return yield* Effect.forEach(Array.filter(files, String.endsWith(".json")), (file) => fs.readFileString(path.join(root, file)).pipe(Effect.flatMap(Schema.decodeEffect(json)), Effect.map((record) => ({
76
- file,
77
- record
78
- }))));
79
- });
80
- const compact = Effect.scoped(Effect.gen(function* () {
81
- const rows = yield* read;
82
- const retained = retain(Array.map(rows, (row) => row.record));
83
- const tokens = HashSet.fromIterable(Array.map(retained, (record) => record.handle.token));
84
- const obsolete = Array.filter(rows, (row) => !HashSet.has(tokens, row.record.handle.token));
85
- yield* Effect.forEach(obsolete, (row) => fs.remove(path.join(root, row.file)).pipe(Effect.uninterruptible), { discard: true });
86
- if (obsolete.length > 0) yield* (yield* fs.open(root, { flag: "r" })).sync.pipe(Effect.uninterruptible);
87
- }));
88
- yield* storage(compact);
89
- return Store.of({
90
- list: storage(mutex.withPermit(read.pipe(Effect.map((rows) => Array.map(rows, (row) => row.record))))),
91
- save: Effect.fnUntraced(function* (record) {
92
- const encoded = yield* Schema.encodeEffect(json)(record);
93
- const address = yield* Schema.encodeEffect(Schema.fromJsonString(Schema.Struct({
94
- key: Key,
95
- generation: Schema.Int
96
- })))({
97
- key: record.handle.key,
98
- generation: record.handle.generation
99
- });
100
- const name = yield* digest.digestHex(Digest.sha256, new TextEncoder().encode(address));
101
- const nonce = yield* random.uuidV4();
102
- const temporary = path.join(root, `.${name}.${nonce}.tmp`);
103
- const destination = path.join(root, name + ".json");
104
- yield* Effect.acquireRelease(fs.open(temporary, {
105
- flag: "wx",
106
- mode: 384
107
- }), () => fs.remove(temporary, { force: true }).pipe(Effect.orDie)).pipe(Effect.flatMap((file) => file.writeAll(new TextEncoder().encode(encoded)).pipe(Effect.uninterruptible, Effect.andThen(file.sync.pipe(Effect.uninterruptible)))));
108
- yield* Effect.uninterruptible(Effect.gen(function* () {
109
- yield* fs.rename(temporary, destination);
110
- yield* (yield* fs.open(root, { flag: "r" })).sync.pipe(Effect.uninterruptible);
111
- }));
112
- yield* compact;
113
- }, Effect.scoped, mutex.withPermit, storage)
114
- });
115
- });
116
- Layer.effect(Store, memory);
117
- const layerDirectory = (root) => Layer.effect(Store, directory(root));
118
- // packages/remote/src/internal/runner/Processes.ts
119
- const inspect = Effect.fnUntraced(function* (pid) {
120
- const child = yield* (yield* ChildProcessSpawner.ChildProcessSpawner).spawn(ChildProcess.make("ps", [
121
- "-p",
122
- `${pid}`,
123
- "-o",
124
- "lstart=",
125
- "-o",
126
- "stat="
127
- ], {
128
- env: { LC_ALL: "C" },
129
- extendEnv: true,
130
- stdin: "ignore"
131
- }));
132
- const [code, output, stderr] = yield* Effect.all([
133
- child.exitCode,
134
- child.stdout.pipe(Stream.decodeText(), Stream.mkString),
135
- child.stderr.pipe(Stream.decodeText(), Stream.mkString)
136
- ], { concurrency: "unbounded" });
137
- if (code === 1 && String.trim(output) === "") return Option.none();
138
- const [, born, status] = /^(.{24})\s+(\S+)$/m.exec(String.trim(output)) ?? [];
139
- if (code !== 0 || born === void 0 || status === void 0) return yield* ExecutionError.make({ detail: "Cannot inspect process identity: " + stderr });
140
- if (String.startsWith("Z")(status)) return Option.none();
141
- return Option.some({ born });
142
- }, Effect.scoped, Effect.mapError((error) => error._tag === "ExecutionError" ? error : ExecutionError.make({
143
- detail: "Inspecting process identity failed",
144
- cause: Cause.fail(error)
145
- })));
146
- const identity = Effect.fnUntraced(function* (pid) {
147
- const current = yield* inspect(pid);
148
- if (Option.isNone(current)) return yield* ExecutionError.make({ detail: "The child exited before its identity could be recorded" });
149
- return {
150
- pid,
151
- start: current.value
152
- };
153
- });
154
- const matches = Effect.fnUntraced(function* (child) {
155
- const current = yield* inspect(child.pid);
156
- if (Option.isNone(current) || current.value.born !== child.start.born) return false;
157
- return true;
158
- });
159
- const recover$1 = Effect.fnUntraced(function* (child) {
160
- if (!(yield* matches(child))) return {
161
- kind: "Exited",
162
- exit: {
163
- code: null,
164
- signal: null
165
- },
166
- cause: Cause.empty
167
- };
168
- const sent = yield* Effect.exit(Effect.try({
169
- try: () => process.kill(child.pid, "SIGKILL"),
170
- catch: (cause) => ExecutionError.make({
171
- detail: "Signaling the orphan failed",
172
- cause: Cause.die(cause)
173
- })
174
- }));
175
- const confirmed = yield* matches(child).pipe(Effect.repeat({
176
- schedule: Schedule.spaced("50 millis"),
177
- while: (value) => value
178
- }), Effect.timeoutOption("10 seconds"));
179
- if (Option.isNone(confirmed)) return yield* TerminationUnconfirmed.make({
180
- detail: "The orphan did not exit after recovery",
181
- cause: sent._tag === "Failure" ? sent.cause : Cause.empty
182
- });
183
- return {
184
- kind: "Killed",
185
- exit: {
186
- code: null,
187
- signal: "SIGKILL"
188
- },
189
- cause: sent._tag === "Failure" ? sent.cause : Cause.empty
190
- };
191
- });
192
- // packages/remote/src/internal/runner/Execution.ts
193
- var Channels = class extends Context.Service()("@graftland/plugin-remote/internal/runner/Execution/Channels") {};
194
- const make$1 = Effect.fnUntraced(function* (root) {
195
- const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
196
- const fs = yield* FileSystem.FileSystem;
197
- const path = yield* Path.Path;
198
- const platform = Context.make(ChildProcessSpawner.ChildProcessSpawner, spawner).pipe(Context.add(FileSystem.FileSystem, fs), Context.add(Path.Path, path));
199
- const peers = yield* Ref.make(HashMap.empty());
200
- const claimed = yield* Ref.make(HashSet.empty());
201
- return {
202
- driver: Execution.of({
203
- recover: (child) => recover$1(child).pipe(Effect.provideContext(platform)),
204
- spawn: Effect.fnUntraced(function* (handle, request, limits) {
205
- const resolved = yield* resolve(root, request.specifier, request.digest);
206
- const argv = yield* command();
207
- const child = yield* spawner.spawn(ChildProcess.make(argv[0], [
208
- ...Array.drop(argv, 1),
209
- "child",
210
- resolved.entry,
211
- handle.token,
212
- yield* Schema.encodeEffect(Schema.fromJsonString(Bootstrap))({
213
- identity: {
214
- id: handle.key.instance,
215
- generation: handle.generation
216
- },
217
- limits
218
- })
219
- ], {
220
- stdin: "pipe",
221
- stdout: "pipe",
222
- stderr: "pipe",
223
- additionalFds: {
224
- fd3: { type: "output" },
225
- fd4: { type: "input" }
226
- },
227
- forceKillAfter: "1 second"
228
- }));
229
- const observation = yield* observe(child);
230
- const exited = observation.exited.pipe(Effect.mapError((error) => ExecutionError.make({
231
- detail: "Observing child exit failed",
232
- cause: Cause.fail(error)
233
- })));
234
- const failures = yield* make$4();
235
- yield* failures.run(observation.failure.pipe(Effect.mapError((error) => ExecutionError.make({
236
- detail: "Reading child output failed",
237
- cause: Cause.fail(error)
238
- }))));
239
- const controls = yield* Queue.bounded(4);
240
- yield* failures.run(Stream.fromQueue(controls).pipe(Stream.map((line) => new TextEncoder().encode(line + "\n")), Stream.run(child.getInputFd(4)), Effect.mapError((error) => ExecutionError.make({
241
- detail: "Child supervision channel failed",
242
- cause: Cause.fail(error)
243
- }))));
244
- const send = (value) => Effect.raceFirst(Schema.encodeEffect(CommandJson)(value).pipe(Effect.flatMap((line) => Queue.offer(controls, line)), Effect.asVoid, Effect.mapError((error) => ExecutionError.make({
245
- detail: "Encoding watchdog command failed",
246
- cause: Cause.fail(error)
247
- }))), failures.await);
248
- const peer = yield* make$3({
249
- input: child.getOutputFd(3).pipe(Stream.mapError((error) => TransportError.make({ cause: Cause.fail(error) }))),
250
- output: child.stdin.pipe(Sink.mapError((error) => TransportError.make({ cause: Cause.fail(error) })))
251
- });
252
- const identity$1 = yield* identity(child.pid);
253
- yield* Ref.update(peers, HashMap.set(handle.token, peer));
254
- yield* Effect.addFinalizer(() => Ref.update(peers, HashMap.remove(handle.token)));
255
- return {
256
- code: resolved.identity,
257
- start: send({ kind: "start" }),
258
- identity: identity$1,
259
- exited,
260
- interrupt: send({ kind: "stop" }),
261
- kill: child.kill({ killSignal: "SIGKILL" }).pipe(Effect.mapError((error) => ExecutionError.make({
262
- detail: "Killing the child failed",
263
- cause: Cause.fail(error)
264
- }))),
265
- renew: (millis) => send({
266
- kind: "renew",
267
- millis
268
- })
269
- };
270
- }, Effect.provideContext(platform), Effect.catchTags({
271
- PlatformError: (error) => Effect.fail(ExecutionError.make({
272
- detail: "Starting the child failed",
273
- cause: Cause.fail(error)
274
- })),
275
- SchemaError: (error) => Effect.fail(ExecutionError.make({
276
- detail: "Encoding child bootstrap failed",
277
- cause: Cause.fail(error)
278
- }))
279
- }))
280
- }),
281
- peer: (token) => Effect.acquireRelease(Effect.gen(function* () {
282
- const peer = yield* Ref.get(peers).pipe(Effect.flatMap((all) => {
283
- const peer = HashMap.get(all, token);
284
- return peer._tag === "Some" ? Effect.succeed(peer.value) : Effect.fail(SessionStopped.make({ detail: "The execution channel is no longer available" }));
285
- }));
286
- if (yield* Ref.modify(claimed, (all) => [HashSet.has(all, token), HashSet.add(all, token)])) return yield* SessionConflict.make({ detail: "The instance channel is already attached" });
287
- return peer;
288
- }), () => Ref.update(claimed, HashSet.remove(token)))
289
- };
290
- });
291
- const layer$1 = (root) => Layer.effectContext(make$1(root).pipe(Effect.map((execution) => Context.make(Execution, execution.driver).pipe(Context.add(Channels, { peer: execution.peer })))));
292
- // packages/remote/src/internal/runner/Supervisor.ts
293
- var Supervisor = class extends Context.Service()("@graftland/plugin-remote/internal/runner/Supervisor") {};
294
- const Launch = Data.taggedEnum();
295
- const ExecutionState = Data.taggedEnum();
296
- const Session = Data.taggedEnum();
297
- function snapshot(state) {
298
- return state._tag === "Loading" ? {
299
- ...state.record,
300
- phase: "Loading",
301
- load: state.request,
302
- launch: state.launch._tag === "Starting" ? { _tag: "Starting" } : {
303
- _tag: "Registered",
304
- child: state.launch.unit.identity
305
- }
306
- } : state.record;
307
- }
308
- const live = (state, now) => {
309
- if (state._tag === "Stopping" || state._tag === "Stopped") return Result.fail(state.record.reason._tag === "Expired" ? LeaseExpired.make({ detail: "The session lease has expired" }) : SessionStopped.make({ detail: "The session is stopping or stopped" }));
310
- return now >= state.deadline ? Result.fail(LeaseExpired.make({ detail: "The session lease has expired" })) : Result.succeed(state);
311
- };
312
- const executionOf = (state) => {
313
- switch (state._tag) {
314
- case "Open": return ExecutionState.Absent();
315
- case "Loading": return state.launch._tag === "Registered" ? ExecutionState.Running({ unit: state.launch.unit }) : ExecutionState.Absent();
316
- case "Loaded": return ExecutionState.Running({ unit: state.unit });
317
- }
318
- };
319
- /** All state transitions are pure; the caller serializes persistence and publication. */
320
- const beginLoad = (state, request, reply) => Session.Loading({
321
- record: state.record,
322
- deadline: state.deadline,
323
- request,
324
- reply,
325
- launch: Launch.Starting()
326
- });
327
- const registerUnit = (state, unit) => {
328
- if (state._tag !== "Loading") return Result.fail(SessionStopped.make({ detail: "The session is stopping or stopped" }));
329
- if (state.launch._tag === "Registered") return Result.fail(ExecutionError.make({ detail: "An execution adapter registered more than one child" }));
330
- return Result.succeed(Session.Loading({
331
- ...state,
332
- launch: Launch.Registered({ unit })
333
- }));
334
- };
335
- const loaded = (state, code) => {
336
- if (state._tag !== "Loading") return Result.fail(SessionStopped.make({ detail: "The session is stopping or stopped" }));
337
- if (state.launch._tag !== "Registered") return Result.fail(ExecutionError.make({ detail: "Execution adapter returned without registering its child" }));
338
- return Result.succeed(Session.Loaded({
339
- deadline: state.deadline,
340
- unit: state.launch.unit,
341
- record: {
342
- ...state.record,
343
- phase: "Loaded",
344
- load: state.request,
345
- code,
346
- child: state.launch.unit.identity
347
- }
348
- }));
349
- };
350
- const beginStop = (state, reason, reply, exit) => Session.Stopping({
351
- record: stoppingRecord(snapshot(state), reason),
352
- reply,
353
- execution: Option.match(exit, {
354
- onNone: () => executionOf(state),
355
- onSome: (exit) => ExecutionState.Exited({ exit })
356
- })
357
- });
358
- const nanos = (millis) => BigInt(Math.ceil(millis * 1e6));
359
- const emptyTermination = {
360
- kind: "Exited",
361
- exit: {
362
- code: 0,
363
- signal: null
364
- },
365
- cause: Cause.empty
366
- };
367
- const exitCause = (exit) => Exit.isFailure(exit) ? exit.cause : Cause.empty;
368
- /** A signal is a request; only the exited effect confirms that the execution has stopped. */
369
- const terminate = Effect.fnUntraced(function* (execution, grace, confirmation) {
370
- if (execution._tag === "Absent") return emptyTermination;
371
- if (execution._tag === "Exited") return {
372
- ...emptyTermination,
373
- exit: execution.exit
374
- };
375
- const child = execution.unit;
376
- const graceful = yield* child.interrupt.pipe(Effect.andThen(child.exited), Effect.timeoutOption(grace), Effect.exit);
377
- if (Exit.isSuccess(graceful) && Option.isSome(graceful.value)) return {
378
- ...emptyTermination,
379
- exit: graceful.value.value
380
- };
381
- const killed = yield* Effect.exit(child.kill);
382
- const forced = yield* child.exited.pipe(Effect.timeoutOption(confirmation), Effect.exit);
383
- const cause = Cause.combine(Cause.combine(exitCause(graceful), exitCause(killed)), exitCause(forced));
384
- if (Exit.isFailure(forced) || Option.isNone(forced.value)) return yield* TerminationUnconfirmed.make({
385
- detail: "The child exit could not be confirmed",
386
- cause
387
- });
388
- return {
389
- kind: "Killed",
390
- exit: forced.value.value,
391
- cause
392
- };
393
- });
394
- const initialize = Effect.fnUntraced(function* (options = {}) {
395
- const store = yield* Store;
396
- const execution = yield* Execution;
397
- const random = yield* SecureRandom.SecureRandom;
398
- const parent = yield* Effect.scope;
399
- const owner = yield* Scope.fork(parent);
400
- return {
401
- store,
402
- execution,
403
- random,
404
- owner,
405
- workers: yield* FiberSet.make().pipe(Scope.provide(owner)),
406
- locks: yield* RcMap.make({ lookup: (_key) => Semaphore.make(1) }).pipe(Scope.provide(owner)),
407
- lease: Duration.toMillis(options.lease ?? "15 seconds"),
408
- grace: Duration.toMillis(options.grace ?? "5 seconds"),
409
- confirmation: Duration.toMillis(options.confirmationTimeout ?? "5 seconds"),
410
- limits: options.limits ?? defaults,
411
- records: yield* Ref.make(HashMap.empty()),
412
- entries: yield* Ref.make(HashMap.empty()),
413
- health: yield* make$4().pipe(Scope.provide(owner)),
414
- closing: yield* Ref.make(false)
415
- };
416
- });
417
- const operations = (runtime) => {
418
- const { store, execution, workers, grace, confirmation, records, entries, health, closing } = runtime;
419
- const poison = health.failCause;
420
- const observe = (effect) => effect.pipe(Effect.asVoid, Effect.catchCause(poison));
421
- const healthy = health.check.pipe(Effect.andThen(Effect.gen(function* () {
422
- if (yield* Ref.get(closing)) return yield* RunnerClosing.make({ detail: "The runner is closing" });
423
- })));
424
- const persist = (record) => store.save(record).pipe(Effect.tapCause(poison), Effect.andThen(Ref.update(records, (all) => save(all, record))));
425
- const status = (handle) => Ref.get(records).pipe(Effect.flatMap((all) => {
426
- const found = HashMap.get(all, handle.token);
427
- return Option.isSome(found) && Equal.equals(keyOf(found.value.handle.key), keyOf(handle.key)) && found.value.handle.generation === handle.generation ? Effect.succeed(found.value) : Effect.fail(StaleSession.make({ detail: "Unknown session or generation" }));
428
- }));
429
- const latest = (key) => Ref.get(records).pipe(Effect.map((all) => Array.reduce(Array.fromIterable(HashMap.values(all)), 0, (value, record) => Equal.equals(keyOf(record.handle.key), keyOf(key)) ? Math.max(value, record.handle.generation) : value)));
430
- const entryFor = Effect.fnUntraced(function* (handle) {
431
- yield* status(handle);
432
- const all = yield* Ref.get(entries);
433
- const found = HashMap.get(all, handle.token);
434
- if (Option.isNone(found)) return yield* SessionStopped.make({ detail: "The session was retired during recovery" });
435
- return found.value;
436
- });
437
- const commit = (entry, transition) => SynchronizedRef.updateEffect(entry.state, Effect.fnUntraced(function* (state) {
438
- const next = yield* Effect.fromResult(transition(state));
439
- yield* persist(snapshot(next));
440
- return next;
441
- }));
442
- const finishStop = Effect.fnUntraced(function* (entry, state, saved) {
443
- yield* FiberHandle.clear(entry.loading);
444
- const result = yield* terminate(state.execution, grace, confirmation);
445
- const closed = yield* Effect.exit(close(entry.unitScope, Exit.void));
446
- const termination = {
447
- ...result,
448
- cause: Cause.combine(result.cause, exitCause(closed))
449
- };
450
- const record = stoppedRecord(state.record.previous, state.record.reason, termination);
451
- yield* SynchronizedRef.updateEffect(entry.state, () => persist(record).pipe(Effect.as(Session.Stopped({
452
- record,
453
- reply: state.reply
454
- }))));
455
- if (Exit.isFailure(saved)) return yield* Effect.failCause(saved.cause);
456
- return termination;
457
- });
458
- /** Called with the session ref locked; the worker starts after its transition is published. */
459
- const stopLocked = Effect.fnUntraced(function* (entry, state, reason, exit) {
460
- if (state._tag === "Stopping" || state._tag === "Stopped") return [state.reply, state];
461
- const reply = yield* Deferred.make();
462
- const next = beginStop(state, reason, reply, exit);
463
- const saved = yield* Effect.exit(persist(next.record));
464
- yield* FiberSet.run(workers, finishStop(entry, next, saved).pipe(Effect.onExit((result) => Deferred.done(reply, result)), observe), { startImmediately: false });
465
- return [reply, next];
466
- });
467
- const stop = (entry, reason, exit = Option.none()) => SynchronizedRef.modifyEffect(entry.state, (state) => stopLocked(entry, state, reason, exit)).pipe(Effect.uninterruptible);
468
- const scheduleLease = (entry, deadline) => FiberHandle.run(entry.timer, observe(Effect.gen(function* () {
469
- const now = yield* Clock.monotonicTimeNanos;
470
- yield* Effect.sleep(Duration.nanos(deadline > now ? deadline - now : 0n));
471
- yield* SynchronizedRef.updateEffect(entry.state, (state) => {
472
- if (state._tag === "Stopping" || state._tag === "Stopped" || state.deadline !== deadline) return Effect.succeed(state);
473
- return stopLocked(entry, state, { _tag: "Expired" }, Option.none()).pipe(Effect.map(([, next]) => next));
474
- });
475
- })), { startImmediately: false });
476
- const register = (entry, unit) => SynchronizedRef.updateEffect(entry.state, Effect.fnUntraced(function* (state) {
477
- const now = yield* Clock.monotonicTimeNanos;
478
- const current = yield* Effect.fromResult(live(state, now));
479
- const next = yield* Effect.fromResult(registerUnit(current, unit));
480
- yield* unit.renew(Number(current.deadline - (yield* Clock.monotonicTimeNanos)) / 1e6);
481
- yield* persist(snapshot(next));
482
- yield* FiberSet.run(workers, observe(unit.exited.pipe(Effect.flatMap((exit) => stop(entry, { _tag: "Exited" }, Option.some(exit))))));
483
- return next;
484
- })).pipe(Effect.uninterruptible);
485
- const runLoad = (entry, state) => execution.spawn(state.record.handle, state.request, state.record.limits).pipe(Scope.provide(entry.unitScope), Effect.flatMap((unit) => register(entry, unit).pipe(Effect.andThen(unit.start), Effect.as(unit.code))), Effect.tap((code) => commit(entry, (current) => loaded(current, code))), Effect.onExit(Effect.fnUntraced(function* (result) {
486
- if (Exit.isSuccess(result)) {
487
- yield* Deferred.done(state.reply, result);
488
- return;
489
- }
490
- if ((yield* SynchronizedRef.get(entry.state))._tag === "Loading") yield* stop(entry, {
491
- _tag: "LoadFailed",
492
- cause: result.cause
493
- });
494
- const healthCause = yield* health.cause;
495
- yield* Deferred.failCause(state.reply, Cause.combine(result.cause, healthCause));
496
- })), Effect.exit, Effect.asVoid);
497
- return {
498
- persist,
499
- status,
500
- latest,
501
- entryFor,
502
- stop,
503
- scheduleLease,
504
- runLoad,
505
- healthy
506
- };
507
- };
508
- const recover = Effect.fnUntraced(function* (runtime, actions) {
509
- const { store, execution, confirmation, records } = runtime;
510
- const { persist } = actions;
511
- const stored = yield* store.list;
512
- yield* Ref.set(records, HashMap.fromIterable(Array.map(stored, (record) => [record.handle.token, record])));
513
- for (const record of stored) {
514
- if (record.phase === "Stopped") continue;
515
- const child = childOf(record);
516
- const termination = Option.isNone(child) ? emptyTermination : yield* execution.recover(child.value).pipe(Effect.timeout(confirmation), Effect.catchTags({
517
- ExecutionError: (error) => Effect.fail(TerminationUnconfirmed.make({
518
- detail: "Orphan recovery did not confirm exit",
519
- cause: Cause.fail(error)
520
- })),
521
- TimeoutError: (error) => Effect.fail(TerminationUnconfirmed.make({
522
- detail: "Orphan recovery did not confirm exit",
523
- cause: Cause.fail(error)
524
- }))
525
- }));
526
- yield* persist(stoppedRecord(record.phase === "Stopping" ? record.previous : record, { _tag: "Recovery" }, termination));
527
- }
528
- });
529
- const ownShutdown = Effect.fnUntraced(function* (runtime, actions) {
530
- const { entries, health, closing } = runtime;
531
- const { stop } = actions;
532
- yield* Effect.addFinalizer(Effect.fnUntraced(function* () {
533
- yield* Ref.set(closing, true);
534
- const all = yield* Ref.get(entries);
535
- const exits = yield* Effect.forEach(HashMap.values(all), (entry) => stop(entry, { _tag: "Shutdown" }).pipe(Effect.flatMap(Deferred.await), Effect.exit));
536
- const initial = yield* health.cause;
537
- const cause = Array.reduce(exits, initial, (cause, exit) => Cause.combine(cause, Exit.isFailure(exit) ? exit.cause : exit.value.cause));
538
- if (cause.reasons.length > 0) return yield* Effect.failCause(cleanupCause(cause));
539
- }));
540
- });
541
- const protocol = Effect.fnUntraced(function* (runtime, actions) {
542
- const { random, owner, locks, lease, limits, records, entries, health } = runtime;
543
- const { persist, status, latest, entryFor, stop, scheduleLease, runLoad, healthy } = actions;
544
- return Supervisor.of({
545
- await: health.await,
546
- status,
547
- highWater: (key) => latest(key).pipe(Effect.map((value) => value === 0 ? null : value)),
548
- open: Effect.fnUntraced(function* (request) {
549
- yield* healthy;
550
- const leaseMillis = Duration.toMillis(request.lease ?? lease);
551
- const negotiated = negotiate(limits, request.limits ?? defaults);
552
- const lock = yield* RcMap.get(locks, keyOf(request.key));
553
- return yield* Effect.gen(function* () {
554
- yield* healthy;
555
- const allRecords = yield* Ref.get(records);
556
- const same = Array.filter(Array.fromIterable(HashMap.values(allRecords)), (row) => Equal.equals(keyOf(row.handle.key), keyOf(request.key)));
557
- const prior = Array.findFirst(same, (row) => row.requestId === request.requestId);
558
- if (Option.isSome(prior)) return prior.value;
559
- const generation = yield* latest(request.key);
560
- if (generation !== (request.expectedGeneration ?? 0)) return yield* StaleSession.make({ detail: "Open expectedGeneration does not match the generation high-water mark" });
561
- for (const record of same) {
562
- const allEntries = yield* Ref.get(entries);
563
- const entry = HashMap.get(allEntries, record.handle.token);
564
- if (Option.isSome(entry)) yield* stop(entry.value, { _tag: "Superseded" }).pipe(Effect.flatMap(Deferred.await), Effect.tap((result) => result.cause.reasons.length === 0 ? Effect.void : Effect.logWarning("Superseded remote execution cleanup failed", result.cause).pipe(Effect.annotateLogs({
565
- "remote.identity": record.handle.key.identity,
566
- "remote.instance": record.handle.key.instance,
567
- "remote.generation": record.handle.generation
568
- }))));
569
- }
570
- yield* healthy;
571
- const token = yield* random.uuidV4();
572
- const record = {
573
- version: 1,
574
- phase: "Open",
575
- handle: {
576
- key: request.key,
577
- token,
578
- generation: generation + 1
579
- },
580
- requestId: request.requestId,
581
- leaseMillis,
582
- limits: negotiated
583
- };
584
- return yield* Effect.uninterruptible(Effect.gen(function* () {
585
- yield* persist(record);
586
- const deadline = (yield* Clock.monotonicTimeNanos) + nanos(leaseMillis);
587
- const scope = yield* Scope.fork(owner);
588
- const entry = {
589
- state: yield* SynchronizedRef.make(Session.Open({
590
- record,
591
- deadline
592
- })),
593
- scope,
594
- unitScope: yield* Scope.fork(scope),
595
- timer: yield* FiberHandle.make().pipe(Scope.provide(scope)),
596
- loading: yield* FiberHandle.make().pipe(Scope.provide(scope))
597
- };
598
- const retained = yield* Ref.get(records);
599
- const old = yield* Ref.modify(entries, (all) => [Array.filter(Array.fromIterable(HashMap.entries(all)), ([key]) => !HashMap.has(retained, key)), HashMap.set(HashMap.filter(all, (_, key) => HashMap.has(retained, key)), token, entry)]);
600
- for (const [, retired] of old) yield* close(retired.scope, Exit.void);
601
- yield* scheduleLease(entry, deadline);
602
- return record;
603
- }));
604
- }).pipe(lock.withPermit);
605
- }, Effect.scoped),
606
- renew: Effect.fnUntraced(function* (handle) {
607
- yield* healthy;
608
- const entry = yield* entryFor(handle);
609
- return yield* Effect.uninterruptibleMask((restore) => SynchronizedRef.modifyEffect(entry.state, Effect.fnUntraced(function* (state) {
610
- const now = yield* Clock.monotonicTimeNanos;
611
- const current = yield* Effect.fromResult(live(state, now));
612
- const running = executionOf(current);
613
- if (running._tag === "Running") yield* restore(running.unit.renew(current.record.leaseMillis));
614
- const deadline = now + nanos(current.record.leaseMillis);
615
- yield* scheduleLease(entry, deadline);
616
- return [current.record.leaseMillis, {
617
- ...current,
618
- deadline
619
- }];
620
- })));
621
- }),
622
- load: Effect.fnUntraced(function* (handle, request) {
623
- yield* healthy;
624
- const entry = yield* entryFor(handle);
625
- return yield* yield* SynchronizedRef.modifyEffect(entry.state, Effect.fnUntraced(function* (state) {
626
- const now = yield* Clock.monotonicTimeNanos;
627
- const current = yield* Effect.fromResult(live(state, now));
628
- if (current._tag !== "Open") {
629
- if ((current._tag === "Loading" ? current.request : current.record.load).specifier !== request.specifier) return yield* SessionConflict.make({ detail: "Load was already requested with a different specifier" });
630
- return [current._tag === "Loading" ? Deferred.await(current.reply) : Effect.succeed(current.record.code), current];
631
- }
632
- const done = yield* Deferred.make();
633
- const next = beginLoad(current, request, done);
634
- yield* persist(snapshot(next));
635
- if (next._tag === "Loading") yield* FiberHandle.run(entry.loading, runLoad(entry, next), { startImmediately: false });
636
- return [Deferred.await(done), next];
637
- })).pipe(Effect.uninterruptible);
638
- }),
639
- dispose: Effect.fnUntraced(function* (handle) {
640
- const record = yield* status(handle);
641
- if (record.phase === "Stopped") return record.termination;
642
- const entry = yield* entryFor(handle);
643
- return yield* stop(entry, { _tag: "Disposed" }).pipe(Effect.flatMap(Deferred.await));
644
- })
645
- });
646
- });
647
- const make = Effect.fnUntraced(function* (options = {}) {
648
- const runtime = yield* initialize(options);
649
- const actions = operations(runtime);
650
- yield* recover(runtime, actions);
651
- yield* ownShutdown(runtime, actions);
652
- return yield* protocol(runtime, actions);
653
- });
654
- const layer = (options = {}) => Layer.effect(Supervisor, make(options));
655
- // packages/remote/src/internal/runner/accept.ts
656
- const accept = Effect.fnUntraced(function* (supervisor, execution, channel$1) {
657
- const identity = yield* Peer;
658
- const scope = yield* Effect.scope;
659
- const mux = yield* make$3(channel$1);
660
- const peer = yield* make$5(channel(mux, 0), {
661
- session: "session-v1",
662
- serve: { session: Session$1 },
663
- call: {}
664
- });
665
- const attached = yield* Ref.make(Option.none());
666
- const loading = yield* Semaphore.make(1);
667
- const authorize = (handle) => handle.key.identity === identity ? Effect.void : Effect.fail(SessionAccessDenied.make({ detail: "The session belongs to another authenticated identity" }));
668
- const handlers = yield* Session$1.toHandlers({
669
- HighWater: ({ instance }) => supervisor.highWater({
670
- identity,
671
- instance
672
- }),
673
- Open: ({ instance, requestId, expectedGeneration, leaseMillis, limits }) => supervisor.open({
674
- key: {
675
- identity,
676
- instance
677
- },
678
- requestId,
679
- expectedGeneration,
680
- ...leaseMillis === void 0 ? {} : { lease: leaseMillis },
681
- limits
682
- }),
683
- Load: Effect.fnUntraced(function* ({ handle, ...request }) {
684
- yield* authorize(handle);
685
- const attachment = yield* Ref.get(attached);
686
- if (Option.isSome(attachment) && attachment.value !== handle.token) return yield* SessionConflict.make({ detail: "A connection can attach only one execution" });
687
- const code = yield* supervisor.load(handle, request);
688
- if (Option.isNone(attachment)) {
689
- const child = yield* execution.peer(handle.token).pipe(Scope.provide(scope));
690
- yield* Ref.set(attached, Option.some(handle.token));
691
- yield* Effect.all([Effect.forever(Effect.flatMap(mux.take(1), (bytes) => child.send(1, bytes))), Effect.forever(Effect.flatMap(child.take(1), (bytes) => mux.send(1, bytes)))], { concurrency: "unbounded" }).pipe(Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause) ? Effect.void : child.fail(TransportError.make({ cause }))), Effect.forkIn(scope));
692
- }
693
- return code;
694
- }, loading.withPermit),
695
- Renew: (handle) => authorize(handle).pipe(Effect.andThen(supervisor.renew(handle))),
696
- Dispose: (handle) => authorize(handle).pipe(Effect.andThen(supervisor.dispose(handle))),
697
- Status: (handle) => authorize(handle).pipe(Effect.andThen(supervisor.status(handle)))
698
- });
699
- return yield* RpcServer.make(Session$1, { disableFatalDefects: true }).pipe(Effect.provideContext(handlers), Effect.provideService(RpcServer.Protocol, peer.serverProtocol("session")), Effect.raceFirst(mux.await));
700
- });
701
- // packages/remote/src/internal/supervisorPlugin.ts
702
- const Config$1 = Schema.Struct({
703
- root: Schema.String,
704
- state: Schema.String,
705
- lease: Schema.optionalKey(PositiveDuration),
706
- grace: Schema.optionalKey(PositiveDuration)
707
- });
708
- var supervisorPlugin_default = make$2({
709
- name: "remote-supervisor",
710
- config: Config$1,
711
- inject: [
712
- optional(ServeLifecycle),
713
- FileSystem.FileSystem,
714
- Path.Path,
715
- ChildProcessSpawner.ChildProcessSpawner,
716
- SecureRandom.SecureRandom,
717
- Digest.Digest
718
- ],
719
- apply: Effect.fnUntraced(function* (options) {
720
- const lifecycle = yield* Effect.serviceOption(ServeLifecycle);
721
- const resources$1 = yield* resources(lifecycle);
722
- const path = yield* Path.Path;
723
- const directory = yield* ConfigDirectory.pipe(Effect.map(Option.getOrElse(() => ".")));
724
- const state = path.resolve(directory, options.state);
725
- yield* acquire(state).pipe(Scope.provide(resources$1));
726
- const services = yield* Layer.build(layer(options).pipe(Layer.provideMerge(Layer.merge(layerDirectory(state), layer$1(path.resolve(directory, options.root)))))).pipe(Scope.provide(resources$1));
727
- const engine = Context.get(services, Supervisor);
728
- const execution = Context.get(services, Channels);
729
- yield* fork(engine.await);
730
- yield* provide(Supervisor$1, { accept: (channel) => accept(engine, execution, channel) });
731
- })
732
- });
733
- export { supervisorPlugin_default as n, Config$1 as t };