@graftland/plugin-remote 0.3.0 → 0.3.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 (209) hide show
  1. package/dist/Auth.d.ts +2 -2
  2. package/dist/Auth.d.ts.map +1 -1
  3. package/dist/Auth.js.map +1 -1
  4. package/dist/{Bootstrap-MCwivGN1.js → Bootstrap-H9gDsgsC.js} +2 -2
  5. package/dist/{Contract-CcpSIJm4.js → Contract-DsJtisvL.js} +8 -46
  6. package/dist/Contract.d.ts +50 -53
  7. package/dist/Contract.d.ts.map +1 -1
  8. package/dist/Contract.js +1 -1
  9. package/dist/Contract.js.map +1 -1
  10. package/dist/{Errors-oPsTLY7l.js → Errors-DYhsGZcZ.js} +14 -1
  11. package/dist/Errors.d.ts +19 -0
  12. package/dist/Errors.d.ts.map +1 -1
  13. package/dist/Errors.js +14 -1
  14. package/dist/Errors.js.map +1 -1
  15. package/dist/{FailureGroup-BcH0XOBi.js → FailureGroup-BgT73AaR.js} +14 -4
  16. package/dist/{Framing-zaYdOJnT.js → Framing-G9eSaeLb.js} +1 -1
  17. package/dist/{Graft-BsYq3F-J.js → Graft-B71CPA2Y.js} +46 -35
  18. package/dist/{Packages-B1TPk8pa.js → Packages-AgoCGotL.js} +23 -26
  19. package/dist/{Peer-IJgFK6mD.js → Peer-DXeVrGBh.js} +5 -5
  20. package/dist/{ProcessObservation-DEGrEMfP.js → ProcessObservation-gLgGirYv.js} +1 -1
  21. package/dist/{ServeLifecycle-CJM0gG5B.js → ServeLifecycle-Bd1FPJ6e.js} +1 -1
  22. package/dist/Testing.d.ts +4 -4
  23. package/dist/Testing.d.ts.map +1 -1
  24. package/dist/Testing.js +10 -4
  25. package/dist/Testing.js.map +1 -1
  26. package/dist/Transport.d.ts +2 -2
  27. package/dist/Transport.d.ts.map +1 -1
  28. package/dist/Transport.js.map +1 -1
  29. package/dist/bin.js +48 -47
  30. package/dist/bin.js.map +1 -1
  31. package/dist/{childRuntime-DSqtFN9o.js → childRuntime-Bm3iSg_N.js} +30 -26
  32. package/dist/connectionEnded-BwcxQ7W8.js +54 -0
  33. package/dist/{entry-SYsx9VNb.js → entry-psOpX3Bj.js} +76 -48
  34. package/dist/host/auth/bearer.d.ts +1 -2
  35. package/dist/host/auth/bearer.d.ts.map +1 -1
  36. package/dist/host/auth/bearer.js +1 -1
  37. package/dist/host/auth/bearer.js.map +1 -1
  38. package/dist/host/auth/jwt.d.ts +1 -2
  39. package/dist/host/auth/jwt.d.ts.map +1 -1
  40. package/dist/host/auth/jwt.js +1 -1
  41. package/dist/host/auth/jwt.js.map +1 -1
  42. package/dist/host/auth/mtls.js +2 -2
  43. package/dist/host/auth/none.js +1 -1
  44. package/dist/host/command.d.ts +1 -1
  45. package/dist/host/command.d.ts.map +1 -1
  46. package/dist/host/command.js +4 -4
  47. package/dist/host/command.js.map +1 -1
  48. package/dist/host/local.d.ts +1 -2
  49. package/dist/host/local.d.ts.map +1 -1
  50. package/dist/host/local.js +3 -3
  51. package/dist/host/local.js.map +1 -1
  52. package/dist/host/unix.d.ts +1 -1
  53. package/dist/host/unix.d.ts.map +1 -1
  54. package/dist/host/unix.js +3 -3
  55. package/dist/host/unix.js.map +1 -1
  56. package/dist/host/websocket.d.ts +1 -1
  57. package/dist/host/websocket.d.ts.map +1 -1
  58. package/dist/host/websocket.js +3 -3
  59. package/dist/host/websocket.js.map +1 -1
  60. package/dist/host/worker.d.ts +1 -2
  61. package/dist/host/worker.d.ts.map +1 -1
  62. package/dist/host/worker.js +4 -5
  63. package/dist/host/worker.js.map +1 -1
  64. package/dist/{importer-D7zSgqxq.js → importer-Cvf88eAY.js} +5 -8
  65. package/dist/index.d.ts +2 -3
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +10 -10
  68. package/dist/index.js.map +1 -1
  69. package/dist/internal/Certificates.js +4 -4
  70. package/dist/internal/Certificates.js.map +1 -1
  71. package/dist/internal/FailureGroup.js +3 -2
  72. package/dist/internal/FailureGroup.js.map +1 -1
  73. package/dist/internal/assembly.js +9 -2
  74. package/dist/internal/assembly.js.map +1 -1
  75. package/dist/internal/auth/mtls.js +1 -1
  76. package/dist/internal/auth/mtls.js.map +1 -1
  77. package/dist/internal/auth/tokenCredentials.d.ts +0 -1
  78. package/dist/internal/auth/tokenCredentials.d.ts.map +1 -1
  79. package/dist/internal/auth/tokenCredentials.js.map +1 -1
  80. package/dist/internal/child.js +20 -17
  81. package/dist/internal/child.js.map +1 -1
  82. package/dist/internal/childRuntime.js +6 -4
  83. package/dist/internal/childRuntime.js.map +1 -1
  84. package/dist/internal/contract.js +26 -0
  85. package/dist/internal/contract.js.map +1 -0
  86. package/dist/internal/credentials.d.ts +6 -6
  87. package/dist/internal/credentials.d.ts.map +1 -1
  88. package/dist/internal/credentials.js.map +1 -1
  89. package/dist/internal/directTransport.d.ts +1 -1
  90. package/dist/internal/entry.d.ts +1 -2
  91. package/dist/internal/entry.d.ts.map +1 -1
  92. package/dist/internal/entry.js +4 -14
  93. package/dist/internal/entry.js.map +1 -1
  94. package/dist/internal/execution.js +44 -7
  95. package/dist/internal/execution.js.map +1 -1
  96. package/dist/internal/host/runner.js +7 -6
  97. package/dist/internal/host/runner.js.map +1 -1
  98. package/dist/internal/host.js +5 -5
  99. package/dist/internal/host.js.map +1 -1
  100. package/dist/internal/port.d.ts +2 -10
  101. package/dist/internal/port.js +5 -5
  102. package/dist/internal/port.js.map +1 -1
  103. package/dist/internal/release.js +1 -1
  104. package/dist/internal/release.js.map +1 -1
  105. package/dist/internal/runner/Connection.d.ts +1 -1
  106. package/dist/internal/runner/Connection.js +4 -4
  107. package/dist/internal/runner/Connection.js.map +1 -1
  108. package/dist/internal/runner/Execution.js +3 -3
  109. package/dist/internal/runner/Execution.js.map +1 -1
  110. package/dist/internal/runner/Framing.d.ts +1 -11
  111. package/dist/internal/runner/Framing.js +1 -1
  112. package/dist/internal/runner/Framing.js.map +1 -1
  113. package/dist/internal/runner/Local.js +1 -1
  114. package/dist/internal/runner/Local.js.map +1 -1
  115. package/dist/internal/runner/Lock.js +1 -1
  116. package/dist/internal/runner/Lock.js.map +1 -1
  117. package/dist/internal/runner/Packages.d.ts +1 -1
  118. package/dist/internal/runner/Packages.js +22 -25
  119. package/dist/internal/runner/Packages.js.map +1 -1
  120. package/dist/internal/runner/Processes.js +7 -9
  121. package/dist/internal/runner/Processes.js.map +1 -1
  122. package/dist/internal/runner/Store.js +3 -3
  123. package/dist/internal/runner/Store.js.map +1 -1
  124. package/dist/internal/runner/Supervisor.js +46 -47
  125. package/dist/internal/runner/Supervisor.js.map +1 -1
  126. package/dist/internal/runner/accept.js +3 -3
  127. package/dist/internal/runner/accept.js.map +1 -1
  128. package/dist/internal/runner/model.js +1 -1
  129. package/dist/internal/runner/model.js.map +1 -1
  130. package/dist/internal/serve.js +1 -1
  131. package/dist/internal/serve.js.map +1 -1
  132. package/dist/internal/services.d.ts +2 -12
  133. package/dist/internal/services.d.ts.map +1 -1
  134. package/dist/internal/services.js.map +1 -1
  135. package/dist/internal/supervisorPlugin.js +1 -1
  136. package/dist/internal/watchdogMain.js +5 -5
  137. package/dist/internal/watchdogMain.js.map +1 -1
  138. package/dist/internal/websocketServer.js +3 -1
  139. package/dist/internal/websocketServer.js.map +1 -1
  140. package/dist/internal/workerMain.js +3 -3
  141. package/dist/{port-Blbh8tms.js → port--3x2zJ4D.js} +8 -8
  142. package/dist/{release-BY4Mlluy.js → release-DfZPuIcK.js} +1 -1
  143. package/dist/runner/auth/bearer.js +2 -2
  144. package/dist/runner/auth/jwt.js +4 -4
  145. package/dist/runner/auth/jwt.js.map +1 -1
  146. package/dist/runner/auth/mtls.js +3 -3
  147. package/dist/runner/auth/none.js +1 -1
  148. package/dist/runner/index.js +5 -5
  149. package/dist/runner/stdio.js +3 -3
  150. package/dist/runner/unix.js +3 -3
  151. package/dist/runner/websocket.js +4 -4
  152. package/dist/{socketChannel-KmtzF-9G.js → socketChannel-CJvL2DiB.js} +2 -2
  153. package/dist/{supervisorPlugin-Inb1S5IV.js → supervisorPlugin-t6Z6PrhD.js} +72 -76
  154. package/dist/{token-v5ne2I0-.js → token-CYBNNlhS.js} +1 -1
  155. package/dist/{tokenCredentials-WUJm2AKm.js → tokenCredentials-CbCuk-Rt.js} +3 -3
  156. package/dist/{websocketServer-MXcCJx0v.js → websocketServer-NQhODlEt.js} +5 -3
  157. package/dist/{workerChannel-P82YdNjh.js → workerChannel-C8hhBJzA.js} +1 -1
  158. package/package.json +3 -3
  159. package/src/Auth.ts +9 -2
  160. package/src/Contract.ts +67 -51
  161. package/src/Errors.ts +23 -0
  162. package/src/Testing.ts +16 -2
  163. package/src/Transport.ts +6 -2
  164. package/src/bin.ts +28 -30
  165. package/src/host/auth/bearer.ts +1 -6
  166. package/src/host/auth/jwt.ts +1 -6
  167. package/src/host/command.ts +2 -0
  168. package/src/host/local.ts +5 -9
  169. package/src/host/unix.ts +2 -0
  170. package/src/host/websocket.ts +2 -0
  171. package/src/host/worker.ts +4 -6
  172. package/src/index.ts +8 -4
  173. package/src/internal/Certificates.ts +10 -13
  174. package/src/internal/FailureGroup.ts +128 -121
  175. package/src/internal/assembly.ts +19 -10
  176. package/src/internal/auth/mtls.ts +19 -20
  177. package/src/internal/auth/tokenCredentials.ts +1 -7
  178. package/src/internal/child.ts +249 -240
  179. package/src/internal/childRuntime.ts +84 -81
  180. package/src/internal/contract.ts +47 -0
  181. package/src/internal/credentials.ts +12 -9
  182. package/src/internal/entry.ts +11 -25
  183. package/src/internal/execution.ts +73 -14
  184. package/src/internal/host/runner.ts +153 -153
  185. package/src/internal/host.ts +123 -130
  186. package/src/internal/port.ts +100 -103
  187. package/src/internal/release.ts +3 -2
  188. package/src/internal/runner/Connection.ts +52 -60
  189. package/src/internal/runner/Execution.ts +163 -164
  190. package/src/internal/runner/Framing.ts +9 -10
  191. package/src/internal/runner/Local.ts +26 -27
  192. package/src/internal/runner/Lock.ts +23 -25
  193. package/src/internal/runner/Packages.ts +67 -70
  194. package/src/internal/runner/Processes.ts +89 -92
  195. package/src/internal/runner/Store.ts +110 -114
  196. package/src/internal/runner/Supervisor.ts +392 -405
  197. package/src/internal/runner/accept.ts +76 -84
  198. package/src/internal/runner/model.ts +20 -21
  199. package/src/internal/serve.ts +15 -13
  200. package/src/internal/services.ts +1 -19
  201. package/src/internal/watchdogMain.ts +16 -11
  202. package/src/internal/websocketServer.ts +18 -3
  203. package/src/runner/auth/jwt.ts +10 -6
  204. package/dist/cleanup-BPWr-5U9.js +0 -13
  205. package/dist/connectionEnded-DUgcobEd.js +0 -9
  206. package/dist/internal/childErrors.d.ts +0 -33
  207. package/dist/internal/childErrors.d.ts.map +0 -1
  208. package/dist/internal/port.d.ts.map +0 -1
  209. package/dist/internal/runner/Framing.d.ts.map +0 -1
@@ -19,169 +19,169 @@ import * as Multiplex from "../runner/Multiplex"
19
19
  import * as Packages from "../runner/Packages"
20
20
  import * as Wire from "../runner/Wire"
21
21
  import { awaitTermination, defaults, childOf } from "../runner/model"
22
- export const connect = (
22
+ export const connect = Effect.fnUntraced(function* (
23
23
  target: ConfiguredRunner,
24
24
  contractModule: string,
25
25
  options: Port.Options,
26
26
  resources: Scope.Scope,
27
27
  processScope: Scope.Scope,
28
- ) =>
29
- Effect.gen(function* () {
30
- const instance = target.instance
28
+ ) {
29
+ const instance = target.instance
31
30
 
32
- const margin = Duration.toMillis(target.margin ?? "5 seconds")
33
- const channel = yield* connectCapability(target.explicitAuth).pipe(
34
- Scope.provide(processScope),
35
- )
36
- const mux = yield* Multiplex.make(channel).pipe(Scope.provide(resources))
37
- const control = yield* Port.make(Wire.channel(mux, 0), {
38
- session: "session-v1",
39
- serve: {},
40
- call: { session: Wire.Session },
41
- }).pipe(Scope.provide(resources))
42
- const session = yield* RpcClient.make(Wire.Session).pipe(
43
- Effect.provideService(
44
- RpcClient.Protocol,
45
- control.clientProtocol("session"),
46
- ),
47
- Scope.provide(resources),
48
- )
49
- const expectedGeneration = yield* session.HighWater({ instance })
50
- const record = yield* session.Open({
51
- instance,
52
- expectedGeneration,
53
- requestId: options.session,
54
- limits: defaults,
55
- ...(target.lease === undefined
56
- ? {}
57
- : {
58
- leaseMillis: Duration.toMillis(target.lease),
59
- }),
60
- })
61
- const failures = yield* FailureGroup.make<TransportError>().pipe(
62
- Scope.provide(resources),
63
- )
64
- const finishStarted = yield* Ref.make(false)
65
- const terminate = yield* Effect.cached(
66
- Effect.gen(function* () {
67
- yield* Ref.set(finishStarted, true)
68
- yield* failures.close
69
- const result = yield* awaitTermination(
70
- record.handle,
71
- session.Dispose(record.handle),
72
- record.leaseMillis,
73
- margin,
31
+ const margin = Duration.toMillis(target.margin ?? "5 seconds")
32
+ const channel = yield* connectCapability(target.explicitAuth).pipe(
33
+ Scope.provide(processScope),
34
+ )
35
+ const mux = yield* Multiplex.make(channel).pipe(Scope.provide(resources))
36
+ const control = yield* Port.make(Wire.channel(mux, 0), {
37
+ session: "session-v1",
38
+ serve: {},
39
+ call: { session: Wire.Session },
40
+ }).pipe(Scope.provide(resources))
41
+ const session = yield* RpcClient.make(Wire.Session).pipe(
42
+ Effect.provideService(
43
+ RpcClient.Protocol,
44
+ control.clientProtocol("session"),
45
+ ),
46
+ Scope.provide(resources),
47
+ )
48
+ const expectedGeneration = yield* session.HighWater({ instance })
49
+ const record = yield* session.Open({
50
+ instance,
51
+ expectedGeneration,
52
+ requestId: options.session,
53
+ limits: defaults,
54
+ ...(target.lease === undefined
55
+ ? {}
56
+ : {
57
+ leaseMillis: Duration.toMillis(target.lease),
58
+ }),
59
+ })
60
+ const failures = yield* FailureGroup.make<TransportError>().pipe(
61
+ Scope.provide(resources),
62
+ )
63
+ const finishStarted = yield* Ref.make(false)
64
+ const terminate = yield* Effect.cached(
65
+ Effect.gen(function* () {
66
+ yield* Ref.set(finishStarted, true)
67
+ // A failed close must not skip Dispose; both outcomes are reported together below.
68
+ const closed = yield* Effect.exit(failures.close)
69
+ const result = yield* awaitTermination(
70
+ record.handle,
71
+ session.Dispose(record.handle),
72
+ record.leaseMillis,
73
+ margin,
74
+ )
75
+ const { code, signal, stderr, cause } = result.exit
76
+ yield* Effect.logInfo("Remote execution terminated", {
77
+ code,
78
+ ...(signal === null ? {} : { signal }),
79
+ ...(stderr === undefined || stderr === "" ? {} : { stderr }),
80
+ ...(cause === undefined || cause.reasons.length === 0 ? {} : { cause }),
81
+ }).pipe(
82
+ Effect.annotateLogs({
83
+ "remote.instance": record.handle.key.instance,
84
+ "remote.generation": record.handle.generation,
85
+ "remote.termination": result.kind,
86
+ }),
87
+ )
88
+ const failure = Exit.isFailure(closed)
89
+ ? Cause.combine(closed.cause, result.cause)
90
+ : result.cause
91
+ if (failure.reasons.length > 0)
92
+ return yield* Effect.failCause(cleanupCause(failure))
93
+ }),
94
+ )
95
+ yield* Scope.addFinalizer(
96
+ processScope,
97
+ Effect.gen(function* () {
98
+ if (!(yield* Ref.get(finishStarted)))
99
+ yield* terminate.pipe(
100
+ Effect.catchCause((cause) => Effect.failCause(cleanupCause(cause))),
74
101
  )
75
- const { code, signal, stderr, cause } = result.exit
76
- yield* Effect.logInfo("Remote execution terminated", {
77
- code,
78
- ...(signal === null ? {} : { signal }),
79
- ...(stderr === undefined || stderr === "" ? {} : { stderr }),
80
- ...(cause === undefined || cause.reasons.length === 0
81
- ? {}
82
- : { cause }),
83
- }).pipe(
84
- Effect.annotateLogs({
85
- "remote.instance": record.handle.key.instance,
86
- "remote.generation": record.handle.generation,
87
- "remote.termination": result.kind,
88
- }),
102
+ }),
103
+ )
104
+ const renewed = yield* Effect.forever(
105
+ Effect.sleep(record.leaseMillis / 3).pipe(
106
+ Effect.andThen(session.Renew(record.handle)),
107
+ ),
108
+ ).pipe(Effect.forkIn(resources))
109
+ const code = yield* session.Load({
110
+ handle: record.handle,
111
+ specifier: contractModule,
112
+ digest: target.expect?.digest !== undefined,
113
+ })
114
+ yield* Packages.verify(code, target.expect)
115
+ const loaded = yield* session.Status(record.handle)
116
+ const peer = yield* Port.make(Wire.channel(mux, 1), {
117
+ ...options,
118
+ session: record.handle.token,
119
+ limits: record.limits,
120
+ }).pipe(Scope.provide(resources))
121
+ // RPC forwarding cancellation is expected; resource cleanup is collected by Connection's scopes.
122
+ const report = (cause: Cause.Cause<unknown>) =>
123
+ Cause.hasInterruptsOnly(cause)
124
+ ? Effect.void
125
+ : failures.failCause(
126
+ Cause.map(cause, (error) =>
127
+ TransportError.make({ cause: Cause.fail(error) }),
128
+ ),
89
129
  )
90
- if (result.cause.reasons.length > 0)
91
- return yield* Effect.failCause(cleanupCause(result.cause))
92
- }),
93
- )
94
- yield* Scope.addFinalizer(
95
- processScope,
96
- Effect.gen(function* () {
97
- if (!(yield* Ref.get(finishStarted)))
98
- yield* terminate.pipe(
99
- Effect.catchCause((cause) => Effect.failCause(cleanupCause(cause))),
100
- )
101
- }),
102
- )
103
- const renewed = yield* Effect.forever(
104
- Effect.sleep(record.leaseMillis / 3).pipe(
105
- Effect.andThen(session.Renew(record.handle)),
106
- ),
107
- ).pipe(Effect.forkIn(resources))
108
- const code = yield* session.Load({
109
- handle: record.handle,
110
- specifier: contractModule,
111
- digest: target.expect?.digest !== undefined,
112
- })
113
- yield* Packages.verify(code, target.expect)
114
- const loaded = yield* session.Status(record.handle)
115
- const peer = yield* Port.make(Wire.channel(mux, 1), {
116
- ...options,
117
- session: record.handle.token,
118
- limits: record.limits,
119
- }).pipe(Scope.provide(resources))
120
- // RPC forwarding cancellation is expected; resource cleanup is collected by Connection's scopes.
121
- const report = (cause: Cause.Cause<unknown>) =>
130
+ yield* failures.await.pipe(
131
+ Effect.catchCause((cause) =>
122
132
  Cause.hasInterruptsOnly(cause)
123
133
  ? Effect.void
124
- : failures.failCause(
125
- Cause.map(cause, (error) =>
126
- TransportError.make({ cause: Cause.fail(error) }),
127
- ),
128
- )
129
- yield* failures.await.pipe(
130
- Effect.catchCause((cause) =>
131
- Cause.hasInterruptsOnly(cause)
132
- ? Effect.void
133
- : peer.fail(TransportError.make({ cause })),
134
- ),
135
- Effect.forkIn(resources),
136
- )
134
+ : peer.fail(TransportError.make({ cause })),
135
+ ),
136
+ Effect.forkIn(resources),
137
+ )
137
138
 
138
- yield* failures.run(
139
- Fiber.join(renewed).pipe(
140
- Effect.catchCause((cause) =>
141
- Effect.gen(function* () {
142
- if (Cause.hasInterruptsOnly(cause)) return
143
- const status = yield* Effect.exit(
144
- session.Status(record.handle).pipe(Effect.timeout("5 seconds")),
145
- )
146
- if (Exit.isSuccess(status) && status.value.phase === "Stopped") {
147
- const exit = status.value.termination.exit
148
- yield* report(
149
- Cause.combine(
150
- cause,
151
- Cause.fail(
152
- ProcessExited.make({
153
- pid: Option.map(childOf(loaded), (child) => child.pid),
154
- exitCode: exit.code,
155
- stderr: exit.stderr ?? "",
156
- statusCause: exit.cause ?? Cause.empty,
157
- }),
158
- ),
139
+ yield* failures.run(
140
+ Fiber.join(renewed).pipe(
141
+ Effect.catchCause(
142
+ Effect.fnUntraced(function* (cause) {
143
+ if (Cause.hasInterruptsOnly(cause)) return
144
+ const status = yield* Effect.exit(
145
+ session.Status(record.handle).pipe(Effect.timeout("5 seconds")),
146
+ )
147
+ if (Exit.isSuccess(status) && status.value.phase === "Stopped") {
148
+ const exit = status.value.termination.exit
149
+ yield* report(
150
+ Cause.combine(
151
+ cause,
152
+ Cause.fail(
153
+ ProcessExited.make({
154
+ pid: Option.map(childOf(loaded), (child) => child.pid),
155
+ exitCode: exit.code,
156
+ stderr: exit.stderr ?? "",
157
+ statusCause: exit.cause ?? Cause.empty,
158
+ }),
159
159
  ),
160
- )
161
- } else
162
- yield* report(
163
- Exit.isFailure(status)
164
- ? Cause.combine(cause, status.cause)
165
- : cause,
166
- )
167
- }),
168
- ),
169
- ),
170
- )
171
- return {
172
- resources,
173
- pid: Option.map(childOf(loaded), (child) => child.pid),
174
- diagnostics: failures.cause,
175
- terminate: () =>
176
- Effect.gen(function* () {
177
- // No renewal may extend a disconnected session while termination waits out its lease.
178
- yield* Fiber.interrupt(renewed)
179
- yield* terminate
160
+ ),
161
+ )
162
+ } else
163
+ yield* report(
164
+ Exit.isFailure(status)
165
+ ? Cause.combine(cause, status.cause)
166
+ : cause,
167
+ )
180
168
  }),
181
- transport: {
182
- ...peer,
183
- failure: peer.await,
184
- report,
185
- },
186
- } satisfies Connected
187
- })
169
+ ),
170
+ ),
171
+ )
172
+ return {
173
+ resources,
174
+ pid: Option.map(childOf(loaded), (child) => child.pid),
175
+ diagnostics: failures.cause,
176
+ terminate: Effect.fnUntraced(function* () {
177
+ // No renewal may extend a disconnected session while termination waits out its lease.
178
+ yield* Fiber.interrupt(renewed)
179
+ yield* terminate
180
+ }),
181
+ transport: {
182
+ ...peer,
183
+ failure: peer.await,
184
+ report,
185
+ },
186
+ } satisfies Connected
187
+ })
@@ -16,15 +16,16 @@ import { RemoteWithdrawn } from "../Errors"
16
16
  import * as Control from "./control"
17
17
  import { cleanupCause } from "./cleanup"
18
18
  import { Connection } from "./runner/Connection"
19
- import type { Service } from "./services"
19
+ import type { Service } from "../Contract"
20
+ import type { ChildFailure } from "./childErrors"
21
+ import type { prepare } from "./contract"
20
22
 
21
23
  type ErrorSchema = Schema.Top & {
22
24
  readonly DecodingServices: never
23
25
  readonly EncodingServices: never
24
26
  }
25
- type ControlGroup<E extends ErrorSchema> = ContractModule.Contract<
26
- Schema.Codec<unknown, Schema.Json>,
27
- E
27
+ type ControlGroup<E extends ErrorSchema> = ReturnType<
28
+ typeof prepare<Schema.Codec<unknown, Schema.Json>, E>
28
29
  >["control"]
29
30
  interface Contract<E extends ErrorSchema> {
30
31
  readonly control: ControlGroup<E>
@@ -32,9 +33,7 @@ interface Contract<E extends ErrorSchema> {
32
33
  readonly decodeCause: (
33
34
  input: unknown,
34
35
  ) => Effect.Effect<
35
- Cause.Cause<
36
- E["Type"] | ContractModule.UndeclaredFailure | ContractModule.ChildFailure
37
- >,
36
+ Cause.Cause<E["Type"] | ContractModule.UndeclaredFailure | ChildFailure>,
38
37
  Schema.SchemaError
39
38
  >
40
39
  }
@@ -47,121 +46,118 @@ const race = <A, E, R>(
47
46
  effect: Effect.Effect<A, E, R>,
48
47
  ) => Effect.raceFirst(effect, connection.transport.failure)
49
48
 
50
- const forwardEvents = <E extends ErrorSchema>(
49
+ const forwardEvents = Effect.fnUntraced(function* <E extends ErrorSchema>(
51
50
  contract: Contract<E>,
52
51
  client: Client<E>,
53
52
  connection: Connection["Service"],
54
- ) =>
55
- Effect.gen(function* () {
56
- const stopped = yield* Deferred.make<void>()
57
- const remoteFailure = yield* FailureGroup.make<
58
- | E["Type"]
59
- | ContractModule.UndeclaredFailure
60
- | ContractModule.ChildFailure
61
- | RemoteWithdrawn
62
- >()
63
- const events = yield* race(
64
- connection,
65
- client.Events().pipe(
66
- Stream.runForEach((event) => {
67
- switch (event._tag) {
68
- case "Stopped":
69
- return Deferred.succeed(stopped, undefined)
70
- case "Withdrawing":
71
- return remoteFailure.fail(RemoteWithdrawn.make({}))
72
- case "Failed":
73
- return contract
74
- .decodeCause(event.cause)
75
- .pipe(Effect.flatMap((cause) => remoteFailure.failCause(cause)))
76
- }
77
- }),
78
- ),
79
- ).pipe(
80
- Effect.tapCause(connection.transport.report),
81
- Effect.forkIn(connection.resources),
82
- )
83
- return { stopped, remoteFailure, events }
84
- })
85
- const forwardLogs = <E extends ErrorSchema>(
53
+ ) {
54
+ const stopped = yield* Deferred.make<void>()
55
+ const remoteFailure = yield* FailureGroup.make<
56
+ | E["Type"]
57
+ | ContractModule.UndeclaredFailure
58
+ | ChildFailure
59
+ | RemoteWithdrawn
60
+ >()
61
+ const events = yield* race(
62
+ connection,
63
+ client.Events().pipe(
64
+ Stream.runForEach((event) => {
65
+ switch (event._tag) {
66
+ case "Stopped":
67
+ return Deferred.succeed(stopped, undefined)
68
+ case "Withdrawing":
69
+ return remoteFailure.fail(RemoteWithdrawn.make({}))
70
+ case "Failed":
71
+ return contract
72
+ .decodeCause(event.cause)
73
+ .pipe(Effect.flatMap((cause) => remoteFailure.failCause(cause)))
74
+ }
75
+ }),
76
+ ),
77
+ ).pipe(
78
+ Effect.tapCause(connection.transport.report),
79
+ Effect.forkIn(connection.resources),
80
+ )
81
+ return { stopped, remoteFailure, events }
82
+ })
83
+ const forwardLogs = Effect.fnUntraced(function* <E extends ErrorSchema>(
86
84
  contract: Contract<E>,
87
85
  client: Client<E>,
88
86
  connection: Connection["Service"],
89
- ) =>
90
- Effect.gen(function* () {
91
- const logsEnded = yield* Deferred.make<void>()
92
- const reportedDrops = yield* Ref.make(0)
93
- yield* race(
94
- connection,
95
- client.Logs().pipe(
96
- Stream.runForEach((event) =>
97
- Effect.gen(function* () {
98
- const previous = yield* Ref.getAndSet(reportedDrops, event.dropped)
99
- if (event.dropped > previous)
100
- yield* Effect.logWarning("Remote logs were dropped").pipe(
101
- Effect.annotateLogs({
102
- ...(Option.isSome(connection.pid)
103
- ? { "remote.pid": connection.pid.value }
104
- : {}),
105
- "remote.logs.dropped": event.dropped - previous,
106
- "remote.logs.droppedTotal": event.dropped,
107
- }),
108
- )
109
- if (event._tag === "LogsEnded") {
110
- yield* Deferred.succeed(logsEnded, undefined)
111
- return
112
- }
113
- const entry = event.entry
114
- const cause = yield* contract.decodeCause(entry.cause)
115
- yield* Effect.logWithLevel(
116
- entry.level === "All" || entry.level === "None"
117
- ? "Info"
118
- : entry.level,
119
- )(
120
- ...(Array.isArray(entry.message)
121
- ? entry.message
122
- : [entry.message]),
123
- cause,
124
- ).pipe(
87
+ ) {
88
+ const logsEnded = yield* Deferred.make<void>()
89
+ const reportedDrops = yield* Ref.make(0)
90
+ yield* race(
91
+ connection,
92
+ client.Logs().pipe(
93
+ Stream.runForEach(
94
+ Effect.fnUntraced(function* (event) {
95
+ const previous = yield* Ref.getAndSet(reportedDrops, event.dropped)
96
+ if (event.dropped > previous)
97
+ yield* Effect.logWarning("Remote logs were dropped").pipe(
125
98
  Effect.annotateLogs({
126
99
  ...(Option.isSome(connection.pid)
127
100
  ? { "remote.pid": connection.pid.value }
128
101
  : {}),
129
- ...(connection.threadId === undefined
130
- ? {}
131
- : { "remote.threadId": connection.threadId }),
132
- "remote.annotations": entry.annotations,
102
+ "remote.logs.dropped": event.dropped - previous,
103
+ "remote.logs.droppedTotal": event.dropped,
133
104
  }),
134
105
  )
135
- }),
136
- ),
137
- ),
138
- ).pipe(
139
- Effect.tapCause(connection.transport.report),
140
- Effect.forkIn(connection.resources),
141
- )
142
- return logsEnded
143
- })
144
- export const initialize = <E extends ErrorSchema>(contract: Contract<E>) =>
145
- Effect.gen(function* () {
146
- const connection = yield* Connection
147
- const client = yield* RpcClient.make(contract.control).pipe(
148
- Effect.provideService(
149
- RpcClient.Protocol,
150
- connection.transport.clientProtocol(Control.endpoint),
106
+ if (event._tag === "LogsEnded") {
107
+ yield* Deferred.succeed(logsEnded, undefined)
108
+ return
109
+ }
110
+ const entry = event.entry
111
+ const cause = yield* contract.decodeCause(entry.cause)
112
+ yield* Effect.logWithLevel(
113
+ entry.level === "All" || entry.level === "None"
114
+ ? "Info"
115
+ : entry.level,
116
+ )(
117
+ ...(Array.isArray(entry.message) ? entry.message : [entry.message]),
118
+ cause,
119
+ ).pipe(
120
+ Effect.annotateLogs({
121
+ ...(Option.isSome(connection.pid)
122
+ ? { "remote.pid": connection.pid.value }
123
+ : {}),
124
+ ...(connection.threadId === undefined
125
+ ? {}
126
+ : { "remote.threadId": connection.threadId }),
127
+ "remote.annotations": entry.annotations,
128
+ }),
129
+ )
130
+ }),
151
131
  ),
152
- Scope.provide(connection.resources),
153
- )
154
- for (const service of contract.inject)
155
- yield* service
156
- .serve(connection.transport)
157
- .pipe(
158
- Effect.tapCause(connection.transport.report),
159
- Effect.forkIn(connection.resources),
160
- )
161
- const events = yield* forwardEvents(contract, client, connection)
162
- const logsEnded = yield* forwardLogs(contract, client, connection)
163
- return { connection, client, ...events, logsEnded }
164
- })
132
+ ),
133
+ ).pipe(
134
+ Effect.tapCause(connection.transport.report),
135
+ Effect.forkIn(connection.resources),
136
+ )
137
+ return logsEnded
138
+ })
139
+ export const initialize = Effect.fnUntraced(function* <E extends ErrorSchema>(
140
+ contract: Contract<E>,
141
+ ) {
142
+ const connection = yield* Connection
143
+ const client = yield* RpcClient.make(contract.control).pipe(
144
+ Effect.provideService(
145
+ RpcClient.Protocol,
146
+ connection.transport.clientProtocol(Control.endpoint),
147
+ ),
148
+ Scope.provide(connection.resources),
149
+ )
150
+ for (const service of contract.inject)
151
+ yield* service
152
+ .serve(connection.transport)
153
+ .pipe(
154
+ Effect.tapCause(connection.transport.report),
155
+ Effect.forkIn(connection.resources),
156
+ )
157
+ const events = yield* forwardEvents(contract, client, connection)
158
+ const logsEnded = yield* forwardLogs(contract, client, connection)
159
+ return { connection, client, ...events, logsEnded }
160
+ })
165
161
  type Active<E extends ErrorSchema> = Effect.Success<
166
162
  ReturnType<typeof initialize<E>>
167
163
  >
@@ -179,9 +175,7 @@ export const load = <E extends ErrorSchema>(
179
175
  (
180
176
  cause,
181
177
  ): cause is Cause.Cause<
182
- | E["Type"]
183
- | ContractModule.UndeclaredFailure
184
- | ContractModule.ChildFailure
178
+ E["Type"] | ContractModule.UndeclaredFailure | ChildFailure
185
179
  > => Cause.isCause(cause),
186
180
  Effect.failCause,
187
181
  ),
@@ -189,21 +183,20 @@ export const load = <E extends ErrorSchema>(
189
183
  active.remoteFailure.await,
190
184
  ),
191
185
  )
192
- export const dispose = <E extends ErrorSchema>(active: Active<E>) =>
193
- Effect.gen(function* () {
194
- const result = yield* race(
195
- active.connection,
196
- active.client
197
- .Dispose()
198
- .pipe(Effect.catchIf(Cause.isCause, Effect.succeed)),
199
- )
200
- const cause = Cause.isCause(result) ? result : Cause.empty
201
- // A remote cleanup failure still permits the bounded event and log tail to drain.
202
- yield* race(
203
- active.connection,
204
- Deferred.await(active.stopped).pipe(
205
- Effect.andThen(Deferred.await(active.logsEnded)),
206
- ),
207
- ).pipe(Effect.onError(() => Effect.failCause(cleanupCause(cause))))
208
- return cause
209
- })
186
+ export const dispose = Effect.fnUntraced(function* <E extends ErrorSchema>(
187
+ active: Active<E>,
188
+ ) {
189
+ const result = yield* race(
190
+ active.connection,
191
+ active.client.Dispose().pipe(Effect.catchIf(Cause.isCause, Effect.succeed)),
192
+ )
193
+ const cause = Cause.isCause(result) ? result : Cause.empty
194
+ // A remote cleanup failure still permits the bounded event and log tail to drain.
195
+ yield* race(
196
+ active.connection,
197
+ Deferred.await(active.stopped).pipe(
198
+ Effect.andThen(Deferred.await(active.logsEnded)),
199
+ ),
200
+ ).pipe(Effect.onError(() => Effect.failCause(cleanupCause(cause))))
201
+ return cause
202
+ })