@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
@@ -189,8 +189,10 @@ const ownedScope = Effect.fnUntraced(function* (parent, afterClose = Effect.void
189
189
  const started = yield* Deferred.make();
190
190
  const ready = yield* Deferred.make();
191
191
  const completed = yield* Deferred.make();
192
- const start = (exit) => Effect.gen(function* () {
193
- if (!(yield* Deferred.succeed(started, void 0))) return;
192
+ const observer = yield* Ref.make("waiting");
193
+ const reportUnobserved = (result) => Exit.isFailure(result) ? Effect.logError("Fork cleanup failed after its dispose caller was interrupted", result.cause) : Effect.void;
194
+ const start = Effect.fnUntraced(function* (exit) {
195
+ if (!(yield* Deferred.succeed(started, void 0))) return false;
194
196
  yield* Effect.gen(function* () {
195
197
  const result = yield* Effect.exit(Effect.gen(function* () {
196
198
  const closed = yield* Effect.exit(close(scope, exit));
@@ -201,13 +203,15 @@ const ownedScope = Effect.fnUntraced(function* (parent, afterClose = Effect.void
201
203
  yield* Deferred.done(ready, result);
202
204
  yield* close(link, exit).pipe(Effect.catchCause((cause) => Effect.logError("Closing fork ownership failed", cause)));
203
205
  yield* Deferred.done(completed, result);
206
+ if (yield* Ref.modify(observer, (state) => [state === "abandoned", "settled"])) yield* reportUnobserved(result);
204
207
  }).pipe(Effect.forkIn(workers, {
205
208
  uninterruptible: true,
206
209
  startImmediately: false
207
210
  }));
211
+ return true;
208
212
  });
209
- const close$1 = (exit) => Effect.uninterruptibleMask((restore) => start(exit).pipe(Effect.andThen(restore(Deferred.await(completed)))));
210
- yield* Scope.addFinalizerExit(link, (exit) => start(exit).pipe(Effect.andThen(Deferred.await(ready)), Effect.orDie, Effect.ensuring(close(workers, exit))));
213
+ const close$1 = (exit) => Effect.uninterruptibleMask((restore) => start(exit).pipe(Effect.flatMap((owner) => owner ? restore(Deferred.await(completed)).pipe(Effect.onInterrupt(() => Ref.modify(observer, (state) => state === "waiting" ? [false, "abandoned"] : [true, state]).pipe(Effect.flatMap((settled) => settled ? Effect.flatMap(Effect.exit(Deferred.await(completed)), reportUnobserved) : Effect.void)))) : restore(Deferred.await(completed)))));
214
+ yield* Scope.addFinalizerExit(link, (exit) => start(exit).pipe(Effect.flatMap((owner) => owner ? Effect.orDie(Deferred.await(ready)) : Effect.asVoid(Effect.exit(Deferred.await(ready)))), Effect.ensuring(close(workers, exit))));
211
215
  return {
212
216
  scope,
213
217
  close: close$1
@@ -238,7 +242,7 @@ const make$2 = Effect.fnUntraced(function* (parent, spec) {
238
242
  yield* adjustLoading(status, 1);
239
243
  return status;
240
244
  });
241
- const setState = (runtime, id, status, state, spec) => Effect.tx(Effect.gen(function* () {
245
+ const setState = Effect.fnUntraced(function* (runtime, id, status, state, spec) {
242
246
  const current = yield* TxSubscriptionRef.get(status.snapshot);
243
247
  if (current.disposed) return;
244
248
  const next = {
@@ -254,7 +258,7 @@ const setState = (runtime, id, status, state, spec) => Effect.tx(Effect.gen(func
254
258
  ...entry,
255
259
  ...next
256
260
  })));
257
- }));
261
+ }, Effect.tx);
258
262
  /** Called inside the removal transaction, after supervisor cleanup. */
259
263
  const dispose = Effect.fnUntraced(function* (runtime, id, status) {
260
264
  const current = yield* TxSubscriptionRef.get(status.snapshot);
@@ -270,12 +274,24 @@ const dispose = Effect.fnUntraced(function* (runtime, id, status) {
270
274
  yield* TxSubscriptionRef.update(runtime.forks, HashMap.remove(id));
271
275
  });
272
276
  /** Only the subtree's own counter belongs to this waiting transaction's read set. */
273
- const stable = (status) => Effect.tx(Effect.gen(function* () {
277
+ const stable = Effect.fnUntraced(function* (status) {
274
278
  if ((yield* TxRef.get(status.loading)) > 0) return yield* Effect.txRetry;
275
- }));
279
+ }, Effect.tx);
276
280
  // packages/core/src/internal/context.ts
277
281
  /** Strip provision-sourced values before child intercepts read or transform the fallback. */
278
282
  const inheritContext = (parent, inherited) => Context.makeUnsafe(new Map([...Array.filter(parent.fallback.mapUnsafe, ([key]) => HashSet.has(parent.provisionKeys, key)), ...Array.filter(inherited.mapUnsafe, ([key]) => !HashSet.has(parent.provisionKeys, key))]));
283
+ // packages/core/src/internal/runtime.ts
284
+ /** Maps a service key to the realm it resolves in. Absent keys use the root realm. */
285
+ const Realms = Context.Reference("@graftland/graft/Realms", { defaultValue: () => HashMap.empty() });
286
+ /** The realm a key resolves in; the root realm is the empty string. */
287
+ const realmOf = (realms, key) => Option.getOrElse(HashMap.get(realms, key), () => "");
288
+ /** The single constructor used by publication, resolution, and failed-publication retries. */
289
+ const realmKey = (realm, service) => `${realm.length}:${realm}${service}`;
290
+ /** Both public and private context views address this same runtime value. */
291
+ const GraftKey = "@graftland/graft/Graft";
292
+ // packages/core/src/internal/runtimeContext.ts
293
+ /** Internal view of the same context key; the public service does not expose the graph. */
294
+ var RuntimeContext = class extends Context.Service()(GraftKey) {};
279
295
  // packages/core/src/internal/tasks.ts
280
296
  /** One activation owns both the workers and every failure they finish with. */
281
297
  const make$1 = Effect.gen(function* () {
@@ -300,6 +316,11 @@ const run = (group, effect) => FiberSet.run(group.fibers, Effect.uninterruptible
300
316
  return yield* body;
301
317
  })), { startImmediately: false });
302
318
  // packages/core/src/Plugin.ts
319
+ /**
320
+ * Plugins are the unit of composition. A plugin declares the services it
321
+ * needs, decodes its configuration with a schema, and runs `apply` inside a
322
+ * scope that lives exactly as long as the plugin is active.
323
+ */
303
324
  const TypeId = "~@graftland/graft/Plugin";
304
325
  const Contract = "~@graftland/graft/Plugin/Contract";
305
326
  /**
@@ -308,8 +329,8 @@ const Contract = "~@graftland/graft/Plugin/Contract";
308
329
  */
309
330
  var Optional = class extends Data.TaggedClass("Optional") {};
310
331
  /** Starts an activation-owned worker. Its unhandled errors belong to the plugin, not the launch operation. */
311
- const fork = (effect) => Effect.gen(function* () {
312
- const { activation } = yield* Graft;
332
+ const fork = Effect.fnUntraced(function* (effect) {
333
+ const { activation } = yield* RuntimeContext;
313
334
  if (Option.isNone(activation.tasks)) return yield* Effect.die(/* @__PURE__ */ new Error("Plugin.fork requires a plugin activation"));
314
335
  return yield* run(activation.tasks.value, effect);
315
336
  });
@@ -342,13 +363,6 @@ const service = (key, build, options) => make({
342
363
  ...options,
343
364
  apply: () => Effect.flatMap(build, (value) => provide(key, value))
344
365
  });
345
- // packages/core/src/internal/runtime.ts
346
- /** Maps a service key to the realm it resolves in. Absent keys use the root realm. */
347
- const Realms = Context.Reference("@graftland/graft/Realms", { defaultValue: () => HashMap.empty() });
348
- /** The realm a key resolves in; the root realm is the empty string. */
349
- const realmOf = (realms, key) => Option.getOrElse(HashMap.get(realms, key), () => "");
350
- /** The single constructor used by publication, resolution, and failed-publication retries. */
351
- const realmKey = (realm, service) => `${realm.length}:${realm}${service}`;
352
366
  // packages/core/src/internal/activation.ts
353
367
  /**
354
368
  * The supervision loop of a single fork.
@@ -405,12 +419,12 @@ const resolve = Effect.fnUntraced(function* (spec, prepared) {
405
419
  unprovided: Array.map(unresolved, (lookup) => lookup.qualified)
406
420
  };
407
421
  });
408
- const waitDrained = (published) => Effect.tx(Effect.gen(function* () {
422
+ const waitDrained = Effect.fnUntraced(function* (published) {
409
423
  for (const provision of published) {
410
424
  const consumers = yield* TxRef.get(provision.consumers);
411
425
  if (HashSet.size(consumers) > 0) return yield* Effect.txRetry;
412
426
  }
413
- }));
427
+ }, Effect.tx);
414
428
  /**
415
429
  * Withdraws what an activation published, closes it to further publishing,
416
430
  * and waits until no consumer holds a lease on any of it.
@@ -479,12 +493,12 @@ const supervise = Effect.fnUntraced(function* (runtime, id, specRef, processing,
479
493
  });
480
494
  const resolveCurrent = (prepared) => Effect.flatMap(TxRef.get(specRef), (spec) => resolve(spec, prepared));
481
495
  /** Resolves to the new spec if the spec changed, or `None` if a dependency changed. */
482
- const waitChange = (resolution) => Effect.tx(Effect.gen(function* () {
496
+ const waitChange = Effect.fnUntraced(function* (resolution) {
483
497
  const current = yield* TxRef.get(specRef);
484
498
  if (current !== resolution.spec) return Option.some(current);
485
499
  if (resolution.provisions.length === 0 && resolution.unprovided.length === 0) return yield* Effect.txRetry;
486
500
  return (yield* resolutionChanged(resolution)) ? Option.none() : yield* Effect.txRetry;
487
- }));
501
+ }, Effect.tx);
488
502
  const acquire = Effect.gen(function* () {
489
503
  const resolution = yield* Effect.tx(Effect.gen(function* () {
490
504
  const prepared = yield* prepare;
@@ -595,7 +609,7 @@ const supervise = Effect.fnUntraced(function* (runtime, id, specRef, processing,
595
609
  }
596
610
  });
597
611
  /** Every phase finishes before the immutable result is handed to state or disposal. */
598
- const teardown = (running, exit) => Effect.gen(function* () {
612
+ const teardown = Effect.fnUntraced(function* (running, exit) {
599
613
  const closer = yield* Effect.withFiber((self) => Effect.succeed(self.id));
600
614
  yield* Fiber.interrupt(running.fiber);
601
615
  const stopped = yield* Fiber.await(running.fiber);
@@ -621,7 +635,7 @@ const supervise = Effect.fnUntraced(function* (runtime, id, specRef, processing,
621
635
  if (Exit.isFailure(closed)) yield* Effect.logError("Releasing the plugin's resources failed", closed.cause);
622
636
  yield* reportFailure(`Plugin "${running.resolution.spec.plugin.name}" failed`, stopped);
623
637
  return cause.reasons.length === 0 ? Exit.void : Exit.failCause(cause);
624
- }).pipe(Effect.annotateLogs({
638
+ }, (effect, running) => Effect.annotateLogs(effect, {
625
639
  "graft.fork": id,
626
640
  "graft.plugin": running.resolution.spec.plugin.name
627
641
  }));
@@ -715,11 +729,8 @@ const supervise = Effect.fnUntraced(function* (runtime, id, specRef, processing,
715
729
  * The graft runtime: loading plugins, publishing services, replacing
716
730
  * plugins at runtime, and observing the fork tree and its logs.
717
731
  */
718
- /**
719
- * The graft a piece of code runs in: the shared runtime plus the activation
720
- * that owns anything it publishes.
721
- */
722
- var Graft = class extends Context.Service()("@graftland/graft/Graft") {};
732
+ /** Identifies the current runtime. Use the module's operations to access it; its state is private. */
733
+ var Graft = class extends Context.Service()(GraftKey) {};
723
734
  var ServiceConflict = class extends Schema.TaggedError()("ServiceConflict", {
724
735
  service: Schema.String,
725
736
  /** Absent for an existing provision; otherwise explains why this activation cannot publish. */
@@ -741,7 +752,7 @@ var ServiceConflict = class extends Schema.TaggedError()("ServiceConflict", {
741
752
  * consumer to let go of it.
742
753
  */
743
754
  const provide = Effect.fnUntraced(function* (key, value) {
744
- const { runtime, activation } = yield* Graft;
755
+ const { runtime, activation } = yield* RuntimeContext;
745
756
  const realms = yield* Realms;
746
757
  const qualified = realmKey(realmOf(realms, key.key), key.key);
747
758
  yield* Effect.tx(Effect.gen(function* () {
@@ -818,7 +829,7 @@ const runtimeLayer = Layer.effect(Graft, Effect.gen(function* () {
818
829
  };
819
830
  }));
820
831
  /** Copies every log entry into the runtime, so `logs` can replay recent history. */
821
- const loggerLayer = Logger.layer([Effect.map(Graft, ({ runtime }) => Logger.make((options) => {
832
+ const loggerLayer = Logger.layer([Effect.map(RuntimeContext, ({ runtime }) => Logger.make((options) => {
822
833
  PubSub.publishUnsafe(runtime.logs, {
823
834
  date: options.date,
824
835
  level: options.logLevel,
@@ -835,7 +846,7 @@ const isolation = (id) => (entry) => "key" in entry ? [entry.key, `#${id}`] : [e
835
846
  * Changes a fork's spec and waits until its supervisor has acted on it:
836
847
  * updated in place, or begun restarting. A later change also counts.
837
848
  */
838
- const accept = (status, spec, f) => Effect.gen(function* () {
849
+ const accept = Effect.fnUntraced(function* (status, spec, f) {
839
850
  const next = yield* TxRef.modify(spec, (current) => {
840
851
  const next = f(current);
841
852
  return [next, next];
@@ -868,7 +879,7 @@ const makeFork = (status, id, spec, dispose) => {
868
879
  /** Loads a plugin under the current scope. Closing that scope unloads it. */
869
880
  const plugin = Effect.fnUntraced(function* (plugin, input, options) {
870
881
  return yield* Effect.uninterruptible(Effect.gen(function* () {
871
- const { runtime, activation } = yield* Graft;
882
+ const { runtime, activation } = yield* RuntimeContext;
872
883
  const scope = yield* Effect.scope;
873
884
  const termination = yield* Ref.make(Exit.void);
874
885
  const owned = yield* ownedScope(scope, Effect.flatMap(Ref.get(termination), (exit) => Exit.isFailure(exit) ? Effect.fail(CleanupError.make({ cause: exit.cause })) : Effect.void));
@@ -950,7 +961,7 @@ const plugin = Effect.fnUntraced(function* (plugin, input, options) {
950
961
  /** The requested identities cannot be represented by the runtime's flat aliases. */
951
962
  var ReplacementConflict = class extends Schema.TaggedError()("ReplacementConflict", { indices: Schema.Array(Schema.Int) }) {};
952
963
  Effect.fnUntraced(function* (pairs) {
953
- const { runtime } = yield* Graft;
964
+ const { runtime } = yield* RuntimeContext;
954
965
  yield* Effect.tx(Effect.gen(function* () {
955
966
  const conflicts = yield* replace$1(runtime.aliases, pairs);
956
967
  if (conflicts.length > 0) return yield* ReplacementConflict.make({ indices: conflicts });
@@ -980,6 +991,6 @@ const buildTree = (forks) => {
980
991
  return children(void 0);
981
992
  };
982
993
  /** The fork tree, emitted again on every change. */
983
- const forks = Stream.unwrap(Effect.map(Graft, ({ runtime }) => TxSubscriptionRef.changesStream(runtime.forks).pipe(Stream.map(buildTree))));
984
- Stream.unwrap(Effect.map(Graft, ({ runtime }) => Stream.fromPubSub(runtime.logs)));
994
+ const forks = Stream.unwrap(Effect.map(RuntimeContext, ({ runtime }) => TxSubscriptionRef.changesStream(runtime.forks).pipe(Stream.map(buildTree))));
995
+ Stream.unwrap(Effect.map(RuntimeContext, ({ runtime }) => Stream.fromPubSub(runtime.logs)));
985
996
  export { provide as a, isPlugin as c, service as d, CleanupError as f, plugin as i, make as l, intercept as n, fork as o, layer as r, fromModule as s, forks as t, optional as u };
@@ -1,4 +1,4 @@
1
- import { c as PackageError } from "./Peer-IJgFK6mD.js";
1
+ import { c as PackageError } from "./Peer-DXeVrGBh.js";
2
2
  import { Array, Cause, Context, Duration, Effect, FileSystem, Order, Path, Schema, Stream, String } from "effect";
3
3
  import process from "node:process";
4
4
  import { fileURLToPath } from "node:url";
@@ -20,7 +20,7 @@ const digest = Effect.fnUntraced(function* (directory) {
20
20
  const fs = yield* FileSystem.FileSystem;
21
21
  const path = yield* Path.Path;
22
22
  const hash = yield* Effect.sync(() => createHash("sha256").update("graft-package-sha256-v1\0"));
23
- const visit = (relative) => Effect.gen(function* () {
23
+ const visit = Effect.fnUntraced(function* (relative) {
24
24
  const filename = path.join(directory, relative);
25
25
  const info = yield* fs.stat(filename);
26
26
  if (info.type !== "File" && info.type !== "Directory") return yield* PackageError.make({ detail: `Unsupported package entry: ${relative}` });
@@ -83,29 +83,26 @@ const resolve = Effect.fnUntraced(function* (root, specifier, includeDigest = fa
83
83
  cause: Cause.fail(error)
84
84
  }))
85
85
  }));
86
- const resolveEntry = (installation, specifier) => Effect.gen(function* () {
87
- const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
88
- return yield* Effect.scoped(Effect.gen(function* () {
89
- const child = yield* spawner.spawn(ChildProcess.make(process.execPath, [
90
- "--input-type=module",
91
- "--eval",
92
- "process.stdout.write(import.meta.resolve(process.argv[1]))",
93
- "--",
94
- specifier
95
- ], {
96
- cwd: installation,
97
- env: {},
98
- stdin: "ignore",
99
- stdout: "pipe",
100
- stderr: "pipe"
101
- }));
102
- const [code, stdout, stderr] = yield* Effect.all([
103
- child.exitCode,
104
- child.stdout.pipe(Stream.decodeText(), Stream.mkString),
105
- child.stderr.pipe(Stream.decodeText(), Stream.mkString)
106
- ], { concurrency: "unbounded" });
107
- if (code !== 0) return yield* PackageError.make({ detail: `Package resolution failed: ${stderr}` });
108
- return stdout;
86
+ const resolveEntry = Effect.fnUntraced(function* (installation, specifier) {
87
+ const child = yield* (yield* ChildProcessSpawner.ChildProcessSpawner).spawn(ChildProcess.make(process.execPath, [
88
+ "--input-type=module",
89
+ "--eval",
90
+ "process.stdout.write(import.meta.resolve(process.argv[1]))",
91
+ "--",
92
+ specifier
93
+ ], {
94
+ cwd: installation,
95
+ env: {},
96
+ stdin: "ignore",
97
+ stdout: "pipe",
98
+ stderr: "pipe"
109
99
  }));
110
- });
100
+ const [code, stdout, stderr] = yield* Effect.all([
101
+ child.exitCode,
102
+ child.stdout.pipe(Stream.decodeText(), Stream.mkString),
103
+ child.stderr.pipe(Stream.decodeText(), Stream.mkString)
104
+ ], { concurrency: "unbounded" });
105
+ if (code !== 0) return yield* PackageError.make({ detail: `Package resolution failed: ${stderr}` });
106
+ return stdout;
107
+ }, Effect.scoped);
111
108
  export { PositiveDuration as a, Supervisor as i, resolveEntry as n, verify as r, resolve as t };
@@ -1,7 +1,7 @@
1
- import { c as TransportError } from "./Errors-oPsTLY7l.js";
2
- import { i as encode, r as decoder, t as EndOfStream } from "./Framing-zaYdOJnT.js";
3
- import { t as make$1 } from "./FailureGroup-BcH0XOBi.js";
4
- import { a as PositiveInt, i as PositiveFinite, r as Limits, t as Priority } from "./port-Blbh8tms.js";
1
+ import { u as TransportError } from "./Errors-DYhsGZcZ.js";
2
+ import { i as encode, r as decoder, t as EndOfStream } from "./Framing-G9eSaeLb.js";
3
+ import { t as make$1 } from "./FailureGroup-BgT73AaR.js";
4
+ import { a as PositiveInt, i as PositiveFinite, r as Limits, t as Priority } from "./port--3x2zJ4D.js";
5
5
  import { Array, Cause, Context, Data, Duration, Effect, Fiber, Option, Queue, Schema, Sink, Stream, TxQueue } from "effect";
6
6
  import { Rpc, RpcGroup } from "effect/rpc";
7
7
  // packages/remote/src/Instance.ts
@@ -273,7 +273,7 @@ var Store = class extends Context.Service()("@graftland/plugin-remote/internal/r
273
273
  var Execution = class extends Context.Service()("@graftland/plugin-remote/internal/runner/model/Execution") {};
274
274
  var RemoteTerminationUnconfirmed = class extends Schema.TaggedError()("RemoteTerminationUnconfirmed", { handle: Handle }) {};
275
275
  /** Losing contact never becomes a successful stop merely because a lease duration passed. */
276
- const awaitTermination = (handle, confirmation, leaseMillis, marginMillis) => Effect.gen(function* () {
276
+ const awaitTermination = Effect.fnUntraced(function* (handle, confirmation, leaseMillis, marginMillis) {
277
277
  const [elapsed, result] = yield* Effect.exit(Effect.timeoutOption(confirmation, leaseMillis + marginMillis)).pipe(Effect.timed);
278
278
  if (result._tag === "Success") {
279
279
  if (Option.isSome(result.value)) return result.value.value;
@@ -1,4 +1,4 @@
1
- import { t as make } from "./FailureGroup-BcH0XOBi.js";
1
+ import { t as make } from "./FailureGroup-BgT73AaR.js";
2
2
  import { Array, Cause, Effect, Exit, Fiber, Ref, Schedule, Stream, String } from "effect";
3
3
  // packages/remote/src/internal/runner/ProcessObservation.ts
4
4
  /** Time allowed for a direct execution unit or launcher to exit after input EOF. */
@@ -1,4 +1,4 @@
1
- import { n as close } from "./release-BY4Mlluy.js";
1
+ import { n as close } from "./release-DfZPuIcK.js";
2
2
  import { Context, Deferred, Effect, Exit, Option, Schema, Scope } from "effect";
3
3
  // packages/remote/src/internal/ServeLifecycle.ts
4
4
  var StartupError = class extends Schema.TaggedError()("RunnerStartupError", {
package/dist/Testing.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { TransportError } from "./Errors.js";
2
2
  import { Channel } from "./Transport.js";
3
- import { FrameError } from "./internal/runner/Framing.js";
4
3
  import { Plugin } from "@graftland/graft";
5
- import { Effect, Schema } from "effect";
4
+ import { Cause, Effect, Schema } from "effect";
6
5
  declare const ConformanceError_base: Schema.Class<ConformanceError, Schema.TaggedStruct<"ConformanceError", {
7
6
  readonly message: Schema.String;
8
- }>, import("effect/Cause").YieldableError & {
7
+ readonly cause: Schema.optionalKey<Schema.Cause<Schema.Unknown, Schema.Unknown>>;
8
+ }>, Cause.YieldableError & {
9
9
  readonly brand: unique symbol;
10
10
  }>;
11
11
  export declare class ConformanceError extends ConformanceError_base {}
@@ -17,7 +17,7 @@ export interface Capabilities {
17
17
  }
18
18
  /** The fixture supplies addresses/credentials and an echo session; no full runner is required. */
19
19
  export declare const transport: <E, R>(plugins: Capabilities, fixture: (plugins: Capabilities) => Effect.Effect<Channel, E, R>) => {
20
- chunks: Effect.Effect<void, E | ConformanceError | FrameError | TransportError, R | import("effect/Scope").Scope>;
20
+ chunks: Effect.Effect<void, E | ConformanceError | TransportError, R | import("effect/Scope").Scope>;
21
21
  eof: Effect.Effect<void, E | ConformanceError | TransportError, R>;
22
22
  };
23
23
  /** HTTP implementations reject before accepting a session, preserving the challenge. */
@@ -1 +1 @@
1
- {"version":3,"file":"Testing.d.ts","names":["Framing.FrameError","_$__Errors0.TransportError"],"sources":["../src/Testing.ts"],"mappings":";;;;;;;;WAgBmB;;qBAFN,yBAAyB;iBAMrB;WACP,WAAW,OAAO;WAClB,UAAU,OAAO;WACjB,aAAa,OAAO;WACpB,eAAe,OAAO;;;qBAGnB,YAAa,GAAG,GAAC,SACpB,cAAY,UACX,SAAS,iBAAiB,OAAO,OAAO,SAAS,GAAG;EAI7D,QAAM,OAAA,aAAA,IAAA,mBAAAA,aAAAC,gBAAA,2BAAA;EA0DN,KAAG,OAAA,aAAA,IAAA,mBAAAA,gBAAA;;;qBAaQ,YAAa,GAAG,GAAC,UACnB,OAAO,eAAe,GAAG,IAAE,uBACnB,OAAA,eAAA,IAAA,kBAAA"}
1
+ {"version":3,"file":"Testing.d.ts","names":[],"sources":["../src/Testing.ts"],"mappings":";;;;;;;;WAkBmB;;qBAFN,yBAAyB;iBASrB;WACP,WAAW,OAAO;WAClB,UAAU,OAAO;WACjB,aAAa,OAAO;WACpB,eAAe,OAAO;;;qBAGnB,YAAa,GAAG,GAAC,SACpB,cAAY,UACX,SAAS,iBAAiB,OAAO,OAAO,SAAS,GAAG;EAI7D,QAAM,OAAA,aAAA,IAAA,mBAAA,gBAAA,2BAAA;EAmEN,KAAG,OAAA,aAAA,IAAA,mBAAA,gBAAA;;;qBAaQ,YAAa,GAAG,GAAC,UACnB,OAAO,eAAe,GAAG,IAAE,uBACnB,OAAA,eAAA,IAAA,kBAAA"}
package/dist/Testing.js CHANGED
@@ -1,7 +1,10 @@
1
- import { decoder, encode } from "./internal/runner/Framing.js";
2
- import { Array, Deferred, Effect, Equivalence, Fiber, Schema, Stream, String } from "effect";
1
+ import { FrameError, decoder, encode } from "./internal/runner/Framing.js";
2
+ import { Array, Cause, Deferred, Effect, Equivalence, Fiber, Schema, Stream, String } from "effect";
3
3
  // src/Testing.ts
4
- var ConformanceError = class extends Schema.TaggedError()("ConformanceError", { message: Schema.String }) {};
4
+ var ConformanceError = class extends Schema.TaggedError()("ConformanceError", {
5
+ message: Schema.String,
6
+ cause: Schema.optionalKey(Schema.Cause(Schema.Unknown, Schema.Unknown))
7
+ }) {};
5
8
  const check = (condition, message) => condition ? Effect.void : Effect.fail(ConformanceError.make({ message }));
6
9
  /** The fixture supplies addresses/credentials and an echo session; no full runner is required. */
7
10
  const transport = (plugins, fixture) => {
@@ -48,7 +51,10 @@ const transport = (plugins, fixture) => {
48
51
  channel: 1,
49
52
  payload: [4, 5]
50
53
  }]), "The channel changed byte order across split or coalesced chunks");
51
- }),
54
+ }).pipe(Effect.mapError((error) => Schema.is(FrameError)(error) ? ConformanceError.make({
55
+ message: error.message,
56
+ cause: Cause.fail(error)
57
+ }) : error)),
52
58
  eof: Effect.gen(function* () {
53
59
  const channel = yield* connect;
54
60
  yield* Stream.empty.pipe(Stream.run(channel.output));
@@ -1 +1 @@
1
- {"version":3,"file":"Testing.js","names":["Framing.encode","Framing.decoder"],"sources":["../src/Testing.ts"],"sourcesContent":["import type { Plugin } from \"@graftland/graft\"\nimport {\n\tArray,\n\tEquivalence,\n\tDeferred,\n\tEffect,\n\tFiber,\n\tStream,\n\tString,\n\tSchema,\n} from \"effect\"\nimport type { Channel } from \"./Transport\"\nimport * as Framing from \"./internal/runner/Framing\"\n\nexport class ConformanceError extends Schema.TaggedError<\n\tConformanceError,\n\t{ readonly brand: unique symbol }\n>()(\"ConformanceError\", { message: Schema.String }) {}\nconst check = (condition: boolean, message: string) =>\n\tcondition ? Effect.void : Effect.fail(ConformanceError.make({ message }))\nexport interface Capabilities {\n\treadonly connector: Plugin.Plugin\n\treadonly listener: Plugin.Plugin\n\treadonly credentials: Plugin.Plugin\n\treadonly authenticator: Plugin.Plugin\n}\n/** The fixture supplies addresses/credentials and an echo session; no full runner is required. */\nexport const transport = <E, R>(\n\tplugins: Capabilities,\n\tfixture: (plugins: Capabilities) => Effect.Effect<Channel, E, R>,\n) => {\n\tconst connect = fixture(plugins)\n\treturn {\n\t\tchunks: Effect.gen(function* () {\n\t\t\tconst channel = yield* connect\n\t\t\tconst first = yield* Framing.encode({\n\t\t\t\tchannel: 0,\n\t\t\t\tpayload: new Uint8Array([1, 2, 3]),\n\t\t\t})\n\t\t\tconst second = yield* Framing.encode({\n\t\t\t\tchannel: 1,\n\t\t\t\tpayload: new Uint8Array([4, 5]),\n\t\t\t})\n\t\t\tconst combined = new Uint8Array(first.length + second.length)\n\t\t\tcombined.set(first)\n\t\t\tcombined.set(second, first.length)\n\t\t\t// Split the header, then put the remainder and the next frame in one chunk.\n\t\t\tconst done = yield* Deferred.make<void>()\n\t\t\tconst sending = yield* Stream.make(\n\t\t\t\tcombined.subarray(0, 2),\n\t\t\t\tcombined.subarray(2),\n\t\t\t).pipe(\n\t\t\t\tStream.concat(\n\t\t\t\t\tStream.fromEffect(Deferred.await(done)).pipe(Stream.drain),\n\t\t\t\t),\n\t\t\t\tStream.run(channel.output),\n\t\t\t\tEffect.forkScoped,\n\t\t\t)\n\t\t\tconst frames = Framing.decoder()\n\t\t\tconst reply = yield* channel.input.pipe(\n\t\t\t\tStream.mapEffect((chunk) => Effect.fromResult(frames.decode(chunk))),\n\t\t\t\tStream.flattenIterable,\n\t\t\t\tStream.zipWithIndex,\n\t\t\t\tStream.tap(([, index]) =>\n\t\t\t\t\tindex === 1 ? Deferred.succeed(done, undefined) : Effect.void,\n\t\t\t\t),\n\t\t\t\tStream.map(([frame]) => frame),\n\t\t\t\tStream.runCollect,\n\t\t\t)\n\t\t\tyield* frames.finish\n\t\t\tyield* Deferred.succeed(done, undefined)\n\t\t\tyield* Fiber.join(sending)\n\t\t\tyield* check(\n\t\t\t\tArray.makeEquivalence(\n\t\t\t\t\tEquivalence.Struct({\n\t\t\t\t\t\tchannel: Equivalence.Number,\n\t\t\t\t\t\tpayload: Array.makeEquivalence(Equivalence.Number),\n\t\t\t\t\t}),\n\t\t\t\t)(\n\t\t\t\t\tArray.map(reply, (frame) => ({\n\t\t\t\t\t\tchannel: frame.channel,\n\t\t\t\t\t\tpayload: Array.fromIterable(frame.payload),\n\t\t\t\t\t})),\n\t\t\t\t\t[\n\t\t\t\t\t\t{ channel: 0, payload: [1, 2, 3] },\n\t\t\t\t\t\t{ channel: 1, payload: [4, 5] },\n\t\t\t\t\t],\n\t\t\t\t),\n\t\t\t\t\"The channel changed byte order across split or coalesced chunks\",\n\t\t\t)\n\t\t}),\n\t\teof: Effect.gen(function* () {\n\t\t\tconst channel = yield* connect\n\t\t\tyield* Stream.empty.pipe(Stream.run(channel.output))\n\t\t\tconst remaining = yield* Stream.runCollect(channel.input)\n\t\t\tyield* check(\n\t\t\t\tremaining.length === 0,\n\t\t\t\t\"The channel produced bytes after EOF\",\n\t\t\t)\n\t\t}),\n\t}\n}\n\n/** HTTP implementations reject before accepting a session, preserving the challenge. */\nexport const rejection = <E, R>(\n\tresponse: Effect.Effect<string, E, R>,\n\tchallenge?: string,\n) =>\n\tresponse.pipe(\n\t\tEffect.tap((response) =>\n\t\t\tEffect.all(\n\t\t\t\t[\n\t\t\t\t\tcheck(String.includes(\"HTTP/1.1 401\")(response), \"Expected HTTP 401\"),\n\t\t\t\t\tcheck(\n\t\t\t\t\t\t!String.includes(\"101 Switching Protocols\")(response),\n\t\t\t\t\t\t\"Rejected credentials opened a WebSocket\",\n\t\t\t\t\t),\n\t\t\t\t\tcheck(\n\t\t\t\t\t\t!String.includes(\"sec-websocket-accept:\")(\n\t\t\t\t\t\t\tString.toLowerCase(response),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"Rejected credentials received a WebSocket accept header\",\n\t\t\t\t\t),\n\t\t\t\t\tcheck(\n\t\t\t\t\t\tchallenge === undefined ||\n\t\t\t\t\t\t\tString.includes(\n\t\t\t\t\t\t\t\t\"www-authenticate: \" + String.toLowerCase(challenge),\n\t\t\t\t\t\t\t)(String.toLowerCase(response)),\n\t\t\t\t\t\t\"The authentication challenge was lost\",\n\t\t\t\t\t),\n\t\t\t\t],\n\t\t\t\t{ discard: true },\n\t\t\t),\n\t\t),\n\t)\n"],"mappings":"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"}
1
+ {"version":3,"file":"Testing.js","names":["Framing.encode","Framing.decoder","Framing.FrameError"],"sources":["../src/Testing.ts"],"sourcesContent":["import type { Plugin } from \"@graftland/graft\"\nimport {\n\tArray,\n\tCause,\n\tEquivalence,\n\tDeferred,\n\tEffect,\n\tFiber,\n\tStream,\n\tString,\n\tSchema,\n} from \"effect\"\nimport type { TransportError } from \"./Errors\"\nimport type { Channel } from \"./Transport\"\nimport * as Framing from \"./internal/runner/Framing\"\n\nexport class ConformanceError extends Schema.TaggedError<\n\tConformanceError,\n\t{ readonly brand: unique symbol }\n>()(\"ConformanceError\", {\n\tmessage: Schema.String,\n\tcause: Schema.optionalKey(Schema.Cause(Schema.Unknown, Schema.Unknown)),\n}) {}\nconst check = (condition: boolean, message: string) =>\n\tcondition ? Effect.void : Effect.fail(ConformanceError.make({ message }))\nexport interface Capabilities {\n\treadonly connector: Plugin.Plugin\n\treadonly listener: Plugin.Plugin\n\treadonly credentials: Plugin.Plugin\n\treadonly authenticator: Plugin.Plugin\n}\n/** The fixture supplies addresses/credentials and an echo session; no full runner is required. */\nexport const transport = <E, R>(\n\tplugins: Capabilities,\n\tfixture: (plugins: Capabilities) => Effect.Effect<Channel, E, R>,\n) => {\n\tconst connect = fixture(plugins)\n\treturn {\n\t\tchunks: Effect.gen(function* () {\n\t\t\tconst channel = yield* connect\n\t\t\tconst first = yield* Framing.encode({\n\t\t\t\tchannel: 0,\n\t\t\t\tpayload: new Uint8Array([1, 2, 3]),\n\t\t\t})\n\t\t\tconst second = yield* Framing.encode({\n\t\t\t\tchannel: 1,\n\t\t\t\tpayload: new Uint8Array([4, 5]),\n\t\t\t})\n\t\t\tconst combined = new Uint8Array(first.length + second.length)\n\t\t\tcombined.set(first)\n\t\t\tcombined.set(second, first.length)\n\t\t\t// Split the header, then put the remainder and the next frame in one chunk.\n\t\t\tconst done = yield* Deferred.make<void>()\n\t\t\tconst sending = yield* Stream.make(\n\t\t\t\tcombined.subarray(0, 2),\n\t\t\t\tcombined.subarray(2),\n\t\t\t).pipe(\n\t\t\t\tStream.concat(\n\t\t\t\t\tStream.fromEffect(Deferred.await(done)).pipe(Stream.drain),\n\t\t\t\t),\n\t\t\t\tStream.run(channel.output),\n\t\t\t\tEffect.forkScoped,\n\t\t\t)\n\t\t\tconst frames = Framing.decoder()\n\t\t\tconst reply = yield* channel.input.pipe(\n\t\t\t\tStream.mapEffect((chunk) => Effect.fromResult(frames.decode(chunk))),\n\t\t\t\tStream.flattenIterable,\n\t\t\t\tStream.zipWithIndex,\n\t\t\t\tStream.tap(([, index]) =>\n\t\t\t\t\tindex === 1 ? Deferred.succeed(done, undefined) : Effect.void,\n\t\t\t\t),\n\t\t\t\tStream.map(([frame]) => frame),\n\t\t\t\tStream.runCollect,\n\t\t\t)\n\t\t\tyield* frames.finish\n\t\t\tyield* Deferred.succeed(done, undefined)\n\t\t\tyield* Fiber.join(sending)\n\t\t\tyield* check(\n\t\t\t\tArray.makeEquivalence(\n\t\t\t\t\tEquivalence.Struct({\n\t\t\t\t\t\tchannel: Equivalence.Number,\n\t\t\t\t\t\tpayload: Array.makeEquivalence(Equivalence.Number),\n\t\t\t\t\t}),\n\t\t\t\t)(\n\t\t\t\t\tArray.map(reply, (frame) => ({\n\t\t\t\t\t\tchannel: frame.channel,\n\t\t\t\t\t\tpayload: Array.fromIterable(frame.payload),\n\t\t\t\t\t})),\n\t\t\t\t\t[\n\t\t\t\t\t\t{ channel: 0, payload: [1, 2, 3] },\n\t\t\t\t\t\t{ channel: 1, payload: [4, 5] },\n\t\t\t\t\t],\n\t\t\t\t),\n\t\t\t\t\"The channel changed byte order across split or coalesced chunks\",\n\t\t\t)\n\t\t}).pipe(\n\t\t\tEffect.mapError((error): E | ConformanceError | TransportError =>\n\t\t\t\tSchema.is(Framing.FrameError)(error)\n\t\t\t\t\t? ConformanceError.make({\n\t\t\t\t\t\t\tmessage: error.message,\n\t\t\t\t\t\t\tcause: Cause.fail(error),\n\t\t\t\t\t\t})\n\t\t\t\t\t: error,\n\t\t\t),\n\t\t),\n\t\teof: Effect.gen(function* () {\n\t\t\tconst channel = yield* connect\n\t\t\tyield* Stream.empty.pipe(Stream.run(channel.output))\n\t\t\tconst remaining = yield* Stream.runCollect(channel.input)\n\t\t\tyield* check(\n\t\t\t\tremaining.length === 0,\n\t\t\t\t\"The channel produced bytes after EOF\",\n\t\t\t)\n\t\t}),\n\t}\n}\n\n/** HTTP implementations reject before accepting a session, preserving the challenge. */\nexport const rejection = <E, R>(\n\tresponse: Effect.Effect<string, E, R>,\n\tchallenge?: string,\n) =>\n\tresponse.pipe(\n\t\tEffect.tap((response) =>\n\t\t\tEffect.all(\n\t\t\t\t[\n\t\t\t\t\tcheck(String.includes(\"HTTP/1.1 401\")(response), \"Expected HTTP 401\"),\n\t\t\t\t\tcheck(\n\t\t\t\t\t\t!String.includes(\"101 Switching Protocols\")(response),\n\t\t\t\t\t\t\"Rejected credentials opened a WebSocket\",\n\t\t\t\t\t),\n\t\t\t\t\tcheck(\n\t\t\t\t\t\t!String.includes(\"sec-websocket-accept:\")(\n\t\t\t\t\t\t\tString.toLowerCase(response),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"Rejected credentials received a WebSocket accept header\",\n\t\t\t\t\t),\n\t\t\t\t\tcheck(\n\t\t\t\t\t\tchallenge === undefined ||\n\t\t\t\t\t\t\tString.includes(\n\t\t\t\t\t\t\t\t\"www-authenticate: \" + String.toLowerCase(challenge),\n\t\t\t\t\t\t\t)(String.toLowerCase(response)),\n\t\t\t\t\t\t\"The authentication challenge was lost\",\n\t\t\t\t\t),\n\t\t\t\t],\n\t\t\t\t{ discard: true },\n\t\t\t),\n\t\t),\n\t)\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { AuthenticationFailed, TransportError } from "./Errors.js";
1
+ import { AuthenticationFailed, AuthenticatorUnavailable, TransportError } from "./Errors.js";
2
2
  import { Prepared, TlsPolicy, VerifiedCertificate } from "./Auth.js";
3
3
  import { Context, Effect, Option, Redacted, Scope, Sink, Stream } from "effect";
4
4
  /** Ordered reliable bytes. Chunk boundaries have no meaning; EOF is not exit confirmation. */
@@ -17,7 +17,7 @@ export interface Handshake {
17
17
  readonly certificate: Option.Option<VerifiedCertificate>;
18
18
  }
19
19
  export type Session = (channel: Channel) => Effect.Effect<void, TransportError, Scope.Scope>;
20
- export type Accept = (handshake: Handshake) => Effect.Effect<Session, AuthenticationFailed>;
20
+ export type Accept = (handshake: Handshake) => Effect.Effect<Session, AuthenticationFailed | AuthenticatorUnavailable>;
21
21
  export interface Serving {
22
22
  /** Completes after serve has registered its accept loop, not merely after bind. */
23
23
  readonly ready: Effect.Effect<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"Transport.d.ts","names":[],"sources":["../src/Transport.ts"],"mappings":";;;;iBAMiB;WACP,OAAO,OAAO,OAAO,YAAY;WACjC,QAAQ,KAAK,cAAc,qBAAqB;;iBAGzC;WACP;WACA;WACA;WACA,SAAS;;iBAEF;WACP,eAAe,OAAO,OAAO,SAAS;WACtC,aAAa,OAAO,OAAO;;YAEzB,WACX,SAAS,YACL,OAAO,aAAa,gBAAgB,MAAM;YACnC,UACX,WAAW,cACP,OAAO,OAAO,SAAS;iBACX;;WAEP,OAAO,OAAO;WACd,QACR,QAAQ,WACJ,OAAO,aAAa,gBAAgB,MAAM;;iBAE/B;WACP,YAAY;WACZ,UACR,aAAa,aACT,OAAO,OAAO,SAAS,gBAAgB,MAAM;;iBAElC;WACP,YAAY;WACZ,OACR,KAAK,OAAO,OAAO,eACf,OAAO,OAAO,SAAS,gBAAgB,MAAM;;;qBAEtC,kBAAkB;;qBAGlB,iBAAiB"}
1
+ {"version":3,"file":"Transport.d.ts","names":[],"sources":["../src/Transport.ts"],"mappings":";;;;iBAUiB;WACP,OAAO,OAAO,OAAO,YAAY;WACjC,QAAQ,KAAK,cAAc,qBAAqB;;iBAGzC;WACP;WACA;WACA;WACA,SAAS;;iBAEF;WACP,eAAe,OAAO,OAAO,SAAS;WACtC,aAAa,OAAO,OAAO;;YAEzB,WACX,SAAS,YACL,OAAO,aAAa,gBAAgB,MAAM;YACnC,UACX,WAAW,cACP,OAAO,OAAO,SAAS,uBAAuB;iBAClC;;WAEP,OAAO,OAAO;WACd,QACR,QAAQ,WACJ,OAAO,aAAa,gBAAgB,MAAM;;iBAE/B;WACP,YAAY;WACZ,UACR,aAAa,aACT,OAAO,OAAO,SAAS,gBAAgB,MAAM;;iBAElC;WACP,YAAY;WACZ,OACR,KAAK,OAAO,OAAO,eACf,OAAO,OAAO,SAAS,gBAAgB,MAAM;;;qBAEtC,kBAAkB;;qBAGlB,iBAAiB"}
@@ -1 +1 @@
1
- {"version":3,"file":"Transport.js","names":[],"sources":["../src/Transport.ts"],"sourcesContent":["/** Execution and byte-channel capabilities; implementations live in host plugins. */\nimport { Context } from \"effect\"\nimport type { Effect, Option, Scope, Stream, Sink, Redacted } from \"effect\"\nimport type { AuthenticationFailed, TransportError } from \"./Errors\"\nimport type { Prepared, VerifiedCertificate, TlsPolicy } from \"./Auth\"\n/** Ordered reliable bytes. Chunk boundaries have no meaning; EOF is not exit confirmation. */\nexport interface Channel {\n\treadonly input: Stream.Stream<Uint8Array, TransportError>\n\treadonly output: Sink.Sink<unknown, Uint8Array, unknown, TransportError>\n}\n\nexport interface Properties {\n\treadonly protocol: string\n\treadonly encrypted: boolean\n\treadonly network: boolean\n\treadonly carries: ReadonlyArray<\"header\" | \"certificate\">\n}\nexport interface Handshake {\n\treadonly authorization: Option.Option<Redacted.Redacted<string>>\n\treadonly certificate: Option.Option<VerifiedCertificate>\n}\nexport type Session = (\n\tchannel: Channel,\n) => Effect.Effect<void, TransportError, Scope.Scope>\nexport type Accept = (\n\thandshake: Handshake,\n) => Effect.Effect<Session, AuthenticationFailed>\nexport interface Serving {\n\t/** Completes after serve has registered its accept loop, not merely after bind. */\n\treadonly ready: Effect.Effect<void>\n\treadonly serve: (\n\t\taccept: Accept,\n\t) => Effect.Effect<void, TransportError, Scope.Scope>\n}\nexport interface ConnectorApi {\n\treadonly properties: Properties\n\treadonly connect: (\n\t\tcredentials: Prepared,\n\t) => Effect.Effect<Channel, TransportError, Scope.Scope>\n}\nexport interface ListenerApi {\n\treadonly properties: Properties\n\treadonly bind: (\n\t\ttls: Option.Option<TlsPolicy>,\n\t) => Effect.Effect<Serving, TransportError, Scope.Scope>\n}\nexport class Connector extends Context.Service<Connector, ConnectorApi>()(\n\t\"@graftland/plugin-remote/Transport/Connector\",\n) {}\nexport class Listener extends Context.Service<Listener, ListenerApi>()(\n\t\"@graftland/plugin-remote/Transport/Listener\",\n) {}\n"],"mappings":";;;AA8CA,IAAa,YAAb,cAA+B,QAAQ,QAAiC,CAAC,CACxE,8CACD,CAAC,CAAC,CAAC;AACH,IAAa,WAAb,cAA8B,QAAQ,QAA+B,CAAC,CACrE,6CACD,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"Transport.js","names":[],"sources":["../src/Transport.ts"],"sourcesContent":["/** Execution and byte-channel capabilities; implementations live in host plugins. */\nimport { Context } from \"effect\"\nimport type { Effect, Option, Scope, Stream, Sink, Redacted } from \"effect\"\nimport type {\n\tAuthenticationFailed,\n\tAuthenticatorUnavailable,\n\tTransportError,\n} from \"./Errors\"\nimport type { Prepared, VerifiedCertificate, TlsPolicy } from \"./Auth\"\n/** Ordered reliable bytes. Chunk boundaries have no meaning; EOF is not exit confirmation. */\nexport interface Channel {\n\treadonly input: Stream.Stream<Uint8Array, TransportError>\n\treadonly output: Sink.Sink<unknown, Uint8Array, unknown, TransportError>\n}\n\nexport interface Properties {\n\treadonly protocol: string\n\treadonly encrypted: boolean\n\treadonly network: boolean\n\treadonly carries: ReadonlyArray<\"header\" | \"certificate\">\n}\nexport interface Handshake {\n\treadonly authorization: Option.Option<Redacted.Redacted<string>>\n\treadonly certificate: Option.Option<VerifiedCertificate>\n}\nexport type Session = (\n\tchannel: Channel,\n) => Effect.Effect<void, TransportError, Scope.Scope>\nexport type Accept = (\n\thandshake: Handshake,\n) => Effect.Effect<Session, AuthenticationFailed | AuthenticatorUnavailable>\nexport interface Serving {\n\t/** Completes after serve has registered its accept loop, not merely after bind. */\n\treadonly ready: Effect.Effect<void>\n\treadonly serve: (\n\t\taccept: Accept,\n\t) => Effect.Effect<void, TransportError, Scope.Scope>\n}\nexport interface ConnectorApi {\n\treadonly properties: Properties\n\treadonly connect: (\n\t\tcredentials: Prepared,\n\t) => Effect.Effect<Channel, TransportError, Scope.Scope>\n}\nexport interface ListenerApi {\n\treadonly properties: Properties\n\treadonly bind: (\n\t\ttls: Option.Option<TlsPolicy>,\n\t) => Effect.Effect<Serving, TransportError, Scope.Scope>\n}\nexport class Connector extends Context.Service<Connector, ConnectorApi>()(\n\t\"@graftland/plugin-remote/Transport/Connector\",\n) {}\nexport class Listener extends Context.Service<Listener, ListenerApi>()(\n\t\"@graftland/plugin-remote/Transport/Listener\",\n) {}\n"],"mappings":";;;AAkDA,IAAa,YAAb,cAA+B,QAAQ,QAAiC,CAAC,CACxE,8CACD,CAAC,CAAC,CAAC;AACH,IAAa,WAAb,cAA8B,QAAQ,QAA+B,CAAC,CACrE,6CACD,CAAC,CAAC,CAAC"}