@effect/platform 0.58.27 → 0.59.1

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 (107) hide show
  1. package/dist/cjs/Error.js +1 -25
  2. package/dist/cjs/Error.js.map +1 -1
  3. package/dist/cjs/HttpApp.js +4 -1
  4. package/dist/cjs/HttpApp.js.map +1 -1
  5. package/dist/cjs/HttpClientError.js +5 -6
  6. package/dist/cjs/HttpClientError.js.map +1 -1
  7. package/dist/cjs/HttpServerError.js +6 -5
  8. package/dist/cjs/HttpServerError.js.map +1 -1
  9. package/dist/cjs/HttpServerResponse.js.map +1 -1
  10. package/dist/cjs/Multipart.js.map +1 -1
  11. package/dist/cjs/Socket.js +11 -11
  12. package/dist/cjs/Socket.js.map +1 -1
  13. package/dist/cjs/Worker.js +10 -1
  14. package/dist/cjs/Worker.js.map +1 -1
  15. package/dist/cjs/WorkerError.js +13 -4
  16. package/dist/cjs/WorkerError.js.map +1 -1
  17. package/dist/cjs/WorkerRunner.js +6 -1
  18. package/dist/cjs/WorkerRunner.js.map +1 -1
  19. package/dist/cjs/internal/httpClient.js +7 -7
  20. package/dist/cjs/internal/httpClient.js.map +1 -1
  21. package/dist/cjs/internal/httpClientResponse.js +13 -13
  22. package/dist/cjs/internal/httpClientResponse.js.map +1 -1
  23. package/dist/cjs/internal/httpServerRequest.js +16 -16
  24. package/dist/cjs/internal/httpServerRequest.js.map +1 -1
  25. package/dist/cjs/internal/httpServerResponse.js +4 -3
  26. package/dist/cjs/internal/httpServerResponse.js.map +1 -1
  27. package/dist/cjs/internal/multipart.js +16 -16
  28. package/dist/cjs/internal/multipart.js.map +1 -1
  29. package/dist/cjs/internal/path.js +461 -11
  30. package/dist/cjs/internal/path.js.map +1 -1
  31. package/dist/cjs/internal/worker.js +126 -59
  32. package/dist/cjs/internal/worker.js.map +1 -1
  33. package/dist/cjs/internal/workerRunner.js +39 -65
  34. package/dist/cjs/internal/workerRunner.js.map +1 -1
  35. package/dist/dts/Error.d.ts +0 -10
  36. package/dist/dts/Error.d.ts.map +1 -1
  37. package/dist/dts/HttpApp.d.ts +1 -1
  38. package/dist/dts/HttpApp.d.ts.map +1 -1
  39. package/dist/dts/HttpClientError.d.ts +6 -8
  40. package/dist/dts/HttpClientError.d.ts.map +1 -1
  41. package/dist/dts/HttpServerError.d.ts +10 -13
  42. package/dist/dts/HttpServerError.d.ts.map +1 -1
  43. package/dist/dts/HttpServerResponse.d.ts +5 -1
  44. package/dist/dts/HttpServerResponse.d.ts.map +1 -1
  45. package/dist/dts/Multipart.d.ts +3 -2
  46. package/dist/dts/Multipart.d.ts.map +1 -1
  47. package/dist/dts/Socket.d.ts +2 -4
  48. package/dist/dts/Socket.d.ts.map +1 -1
  49. package/dist/dts/Worker.d.ts +27 -27
  50. package/dist/dts/Worker.d.ts.map +1 -1
  51. package/dist/dts/WorkerError.d.ts +2 -2
  52. package/dist/dts/WorkerError.d.ts.map +1 -1
  53. package/dist/dts/WorkerRunner.d.ts +8 -5
  54. package/dist/dts/WorkerRunner.d.ts.map +1 -1
  55. package/dist/esm/Error.js +0 -23
  56. package/dist/esm/Error.js.map +1 -1
  57. package/dist/esm/HttpApp.js +4 -1
  58. package/dist/esm/HttpApp.js.map +1 -1
  59. package/dist/esm/HttpClientError.js +5 -6
  60. package/dist/esm/HttpClientError.js.map +1 -1
  61. package/dist/esm/HttpServerError.js +7 -6
  62. package/dist/esm/HttpServerError.js.map +1 -1
  63. package/dist/esm/HttpServerResponse.js.map +1 -1
  64. package/dist/esm/Multipart.js.map +1 -1
  65. package/dist/esm/Socket.js +12 -12
  66. package/dist/esm/Socket.js.map +1 -1
  67. package/dist/esm/Worker.js +9 -0
  68. package/dist/esm/Worker.js.map +1 -1
  69. package/dist/esm/WorkerError.js +13 -4
  70. package/dist/esm/WorkerError.js.map +1 -1
  71. package/dist/esm/WorkerRunner.js +5 -0
  72. package/dist/esm/WorkerRunner.js.map +1 -1
  73. package/dist/esm/internal/httpClient.js +7 -7
  74. package/dist/esm/internal/httpClient.js.map +1 -1
  75. package/dist/esm/internal/httpClientResponse.js +13 -13
  76. package/dist/esm/internal/httpClientResponse.js.map +1 -1
  77. package/dist/esm/internal/httpServerRequest.js +16 -16
  78. package/dist/esm/internal/httpServerRequest.js.map +1 -1
  79. package/dist/esm/internal/httpServerResponse.js +4 -3
  80. package/dist/esm/internal/httpServerResponse.js.map +1 -1
  81. package/dist/esm/internal/multipart.js +17 -17
  82. package/dist/esm/internal/multipart.js.map +1 -1
  83. package/dist/esm/internal/path.js +461 -10
  84. package/dist/esm/internal/path.js.map +1 -1
  85. package/dist/esm/internal/worker.js +124 -57
  86. package/dist/esm/internal/worker.js.map +1 -1
  87. package/dist/esm/internal/workerRunner.js +38 -65
  88. package/dist/esm/internal/workerRunner.js.map +1 -1
  89. package/package.json +5 -6
  90. package/src/Error.ts +0 -38
  91. package/src/HttpApp.ts +2 -2
  92. package/src/HttpClientError.ts +13 -6
  93. package/src/HttpServerError.ts +17 -6
  94. package/src/HttpServerResponse.ts +8 -1
  95. package/src/Multipart.ts +3 -2
  96. package/src/Socket.ts +10 -9
  97. package/src/Worker.ts +32 -29
  98. package/src/WorkerError.ts +15 -8
  99. package/src/WorkerRunner.ts +15 -8
  100. package/src/internal/httpClient.ts +7 -7
  101. package/src/internal/httpClientResponse.ts +13 -13
  102. package/src/internal/httpServerRequest.ts +15 -15
  103. package/src/internal/httpServerResponse.ts +7 -3
  104. package/src/internal/multipart.ts +16 -15
  105. package/src/internal/path.ts +510 -14
  106. package/src/internal/worker.ts +193 -146
  107. package/src/internal/workerRunner.ts +106 -139
@@ -1,6 +1,5 @@
1
1
  import * as Schema from "@effect/schema/Schema"
2
2
  import * as Serializable from "@effect/schema/Serializable"
3
- import * as Arr from "effect/Array"
4
3
  import * as Cause from "effect/Cause"
5
4
  import * as Channel from "effect/Channel"
6
5
  import * as Chunk from "effect/Chunk"
@@ -8,31 +7,21 @@ import * as Context from "effect/Context"
8
7
  import * as Deferred from "effect/Deferred"
9
8
  import * as Effect from "effect/Effect"
10
9
  import * as Exit from "effect/Exit"
11
- import * as Fiber from "effect/Fiber"
10
+ import * as FiberRef from "effect/FiberRef"
11
+ import * as FiberSet from "effect/FiberSet"
12
12
  import { identity, pipe } from "effect/Function"
13
13
  import * as Layer from "effect/Layer"
14
14
  import * as Option from "effect/Option"
15
15
  import * as Pool from "effect/Pool"
16
16
  import * as Queue from "effect/Queue"
17
17
  import * as Schedule from "effect/Schedule"
18
- import type * as Scope from "effect/Scope"
18
+ import * as Scope from "effect/Scope"
19
19
  import * as Stream from "effect/Stream"
20
20
  import * as Tracer from "effect/Tracer"
21
21
  import * as Transferable from "../Transferable.js"
22
22
  import type * as Worker from "../Worker.js"
23
23
  import { WorkerError } from "../WorkerError.js"
24
24
 
25
- /** @internal */
26
- export const defaultQueue = <I>() =>
27
- Effect.map(
28
- Queue.unbounded<readonly [id: number, item: I, span: Option.Option<Tracer.Span>]>(),
29
- (queue): Worker.WorkerQueue<I> => ({
30
- offer: (id, item, span) => Queue.offer(queue, [id, item, span]),
31
- take: Queue.take(queue),
32
- shutdown: Queue.shutdown(queue)
33
- })
34
- )
35
-
36
25
  /** @internal */
37
26
  export const PlatformWorkerTypeId: Worker.PlatformWorkerTypeId = Symbol.for(
38
27
  "@effect/platform/Worker/PlatformWorker"
@@ -66,22 +55,15 @@ export const makeManager = Effect.gen(function*() {
66
55
  [WorkerManagerTypeId]: WorkerManagerTypeId,
67
56
  spawn<I, O, E>({
68
57
  encode,
69
- initialMessage,
70
- queue,
71
- transfers = (_) => []
58
+ initialMessage
72
59
  }: Worker.Worker.Options<I>) {
73
60
  return Effect.gen(function*(_) {
74
- const spawn = yield* _(Spawner)
75
61
  const id = idCounter++
76
62
  let requestIdCounter = 0
77
63
  const requestMap = new Map<
78
64
  number,
79
- readonly [Queue.Queue<Exit.Exit<ReadonlyArray<O>, E | WorkerError>>, Deferred.Deferred<void>]
65
+ Queue.Queue<Exit.Exit<ReadonlyArray<O>, E | WorkerError>> | Deferred.Deferred<O, E | WorkerError>
80
66
  >()
81
- const sendQueue = yield* Effect.acquireRelease(
82
- Queue.unbounded<readonly [message: Worker.Worker.Request, transfers?: ReadonlyArray<unknown>]>(),
83
- Queue.shutdown
84
- )
85
67
 
86
68
  const collector = Transferable.unsafeMakeCollector()
87
69
  const wrappedEncode = encode ?
@@ -92,36 +74,20 @@ export const makeManager = Effect.gen(function*() {
92
74
  )) :
93
75
  Effect.succeed
94
76
 
95
- const outbound = queue ?? (yield* defaultQueue<I>())
96
- yield* Effect.addFinalizer(() => outbound.shutdown)
77
+ const readyLatch = yield* Deferred.make<void>()
78
+ const backing = yield* platform.spawn<Worker.Worker.Request, Worker.Worker.Response<E, O>>(id)
97
79
 
98
- yield* Effect.gen(function*() {
99
- const readyLatch = yield* Deferred.make<void>()
100
- const backing = yield* platform.spawn<Worker.Worker.Request, Worker.Worker.Response<E, O>>(spawn(id))
101
- const send = pipe(
102
- sendQueue.take,
103
- Effect.flatMap(([message, transfers]) => backing.send(message, transfers)),
104
- Effect.forever
105
- )
106
- const take = pipe(
107
- Queue.take(backing.queue),
108
- Effect.flatMap((msg) => {
109
- if (msg[0] === 0) {
110
- return Deferred.complete(readyLatch, Effect.void)
111
- }
112
- return handleMessage(msg[1])
113
- }),
114
- Effect.forever
115
- )
116
- return yield* Effect.all([
117
- Fiber.join(backing.fiber),
118
- Effect.zipRight(Deferred.await(readyLatch), send),
119
- take
120
- ], { concurrency: "unbounded" })
80
+ yield* backing.run((message) => {
81
+ if (message[0] === 0) {
82
+ return Deferred.complete(readyLatch, Effect.void)
83
+ }
84
+ return handleMessage(message[1])
121
85
  }).pipe(
122
- Effect.scoped,
123
86
  Effect.onError((cause) =>
124
- Effect.forEach(requestMap.values(), ([queue]) => Queue.offer(queue, Exit.failCause(cause)))
87
+ Effect.forEach(requestMap.values(), (queue) =>
88
+ Deferred.DeferredTypeId in queue
89
+ ? Deferred.failCause(queue, cause)
90
+ : Queue.offer(queue, Exit.failCause(cause)))
125
91
  ),
126
92
  Effect.retry(Schedule.spaced(1000)),
127
93
  Effect.annotateLogs({
@@ -134,7 +100,10 @@ export const makeManager = Effect.gen(function*() {
134
100
 
135
101
  yield* Effect.addFinalizer(() =>
136
102
  Effect.zipRight(
137
- Effect.forEach(requestMap.values(), ([queue]) => Queue.offer(queue, Exit.failCause(Cause.empty)), {
103
+ Effect.forEach(requestMap.values(), (queue) =>
104
+ Deferred.DeferredTypeId in queue
105
+ ? Deferred.interrupt(queue)
106
+ : Queue.offer(queue, Exit.failCause(Cause.empty)), {
138
107
  discard: true
139
108
  }),
140
109
  Effect.sync(() => requestMap.clear())
@@ -149,131 +118,117 @@ export const makeManager = Effect.gen(function*() {
149
118
  switch (response[1]) {
150
119
  // data
151
120
  case 0: {
152
- return Queue.offer(queue[0], Exit.succeed(response[2]))
121
+ return Deferred.DeferredTypeId in queue
122
+ ? Deferred.succeed(queue, response[2][0])
123
+ : Queue.offer(queue, Exit.succeed(response[2]))
153
124
  }
154
125
  // end
155
126
  case 1: {
156
- return response.length === 2 ?
157
- Queue.offer(queue[0], Exit.failCause(Cause.empty)) :
158
- Effect.zipRight(
159
- Queue.offer(queue[0], Exit.succeed(response[2])),
160
- Queue.offer(queue[0], Exit.failCause(Cause.empty))
127
+ if (response.length === 2) {
128
+ return Deferred.DeferredTypeId in queue
129
+ ? Deferred.interrupt(queue)
130
+ : Queue.offer(queue, Exit.failCause(Cause.empty))
131
+ }
132
+ return Deferred.DeferredTypeId in queue
133
+ ? Deferred.succeed(queue, response[2][0])
134
+ : Effect.zipRight(
135
+ Queue.offer(queue, Exit.succeed(response[2])),
136
+ Queue.offer(queue, Exit.failCause(Cause.empty))
161
137
  )
162
138
  }
163
139
  // error / defect
164
140
  case 2:
165
141
  case 3: {
166
- return Queue.offer(
167
- queue[0],
168
- response[1] === 2
169
- ? Exit.fail(response[2])
170
- : Exit.failCause(WorkerError.decodeCause(response[2]))
171
- )
142
+ if (response[1] === 2) {
143
+ return Deferred.DeferredTypeId in queue
144
+ ? Deferred.fail(queue, response[2])
145
+ : Queue.offer(queue, Exit.fail(response[2]))
146
+ }
147
+ const cause = WorkerError.decodeCause(response[2])
148
+ return Deferred.DeferredTypeId in queue
149
+ ? Deferred.failCause(queue, cause)
150
+ : Queue.offer(queue, Exit.failCause(cause))
172
151
  }
173
152
  }
174
153
  })
175
154
 
176
- const executeAcquire = (request: I) =>
177
- Effect.tap(
178
- Effect.all([
179
- Effect.sync(() => requestIdCounter++),
180
- Queue.unbounded<Exit.Exit<ReadonlyArray<O>, E | WorkerError>>(),
181
- Deferred.make<void>(),
182
- Effect.map(
183
- Effect.serviceOption(Tracer.ParentSpan),
184
- Option.filter((span): span is Tracer.Span => span._tag === "Span")
185
- )
186
- ]),
187
- ([id, queue, deferred, span]) =>
188
- Effect.suspend(() => {
189
- requestMap.set(id, [queue, deferred])
190
- return outbound.offer(id, request, span)
191
- })
192
- )
155
+ const executeAcquire = <
156
+ Q extends Queue.Queue<Exit.Exit<ReadonlyArray<O>, E | WorkerError>> | Deferred.Deferred<O, E | WorkerError>
157
+ >(request: I, makeQueue: Effect.Effect<Q>) =>
158
+ Effect.withFiberRuntime<{
159
+ readonly id: number
160
+ readonly queue: Q
161
+ }>((fiber) => {
162
+ const context = fiber.getFiberRef(FiberRef.currentContext)
163
+ const span = Context.getOption(context, Tracer.ParentSpan).pipe(
164
+ Option.filter((span): span is Tracer.Span => span._tag === "Span")
165
+ )
166
+ const id = requestIdCounter++
167
+ return makeQueue.pipe(
168
+ Effect.tap((queue) => {
169
+ requestMap.set(id, queue)
170
+ return wrappedEncode(request).pipe(
171
+ Effect.tap((payload) =>
172
+ backing.send([
173
+ id,
174
+ 0,
175
+ payload,
176
+ span._tag === "Some" ? [span.value.traceId, span.value.spanId, span.value.sampled] : undefined
177
+ ], collector.unsafeRead())
178
+ ),
179
+ Effect.catchAllCause((cause) =>
180
+ Deferred.DeferredTypeId in queue ?
181
+ Deferred.failCause(queue, cause) :
182
+ Queue.offer(queue, Exit.failCause(cause))
183
+ )
184
+ )
185
+ }),
186
+ Effect.map((queue) => ({ id, queue }))
187
+ )
188
+ })
193
189
 
194
- const executeRelease = (
195
- [id, , deferred]: [
196
- number,
197
- Queue.Queue<Exit.Exit<ReadonlyArray<O>, E | WorkerError>>,
198
- Deferred.Deferred<void>,
199
- Option.Option<Tracer.Span>
200
- ],
201
- exit: Exit.Exit<unknown, unknown>
202
- ) => {
203
- const release = Effect.zipRight(
204
- Deferred.complete(deferred, Effect.void),
205
- Effect.sync(() => requestMap.delete(id))
206
- )
190
+ const executeRelease = ({ id }: { readonly id: number }, exit: Exit.Exit<unknown, unknown>) => {
191
+ const release = Effect.sync(() => requestMap.delete(id))
207
192
  return Exit.isFailure(exit) ?
208
- Effect.zipRight(sendQueue.offer([[id, 1]]), release) :
193
+ Effect.zipRight(Effect.orDie(backing.send([id, 1])), release) :
209
194
  release
210
195
  }
211
196
 
212
197
  const execute = (request: I) =>
213
- Stream.flatMap(
214
- Stream.acquireRelease(
215
- executeAcquire(request),
198
+ Stream.fromChannel(
199
+ Channel.acquireUseRelease(
200
+ executeAcquire(request, Queue.unbounded<Exit.Exit<ReadonlyArray<O>, E | WorkerError>>()),
201
+ ({ queue }) => {
202
+ const loop: Channel.Channel<Chunk.Chunk<O>, unknown, E | WorkerError, unknown, void, unknown> = Channel
203
+ .flatMap(
204
+ Queue.take(queue),
205
+ Exit.match({
206
+ onFailure: (cause) => Cause.isEmpty(cause) ? Channel.void : Channel.failCause(cause),
207
+ onSuccess: (value) => Channel.flatMap(Channel.write(Chunk.unsafeFromArray(value)), () => loop)
208
+ })
209
+ )
210
+ return loop
211
+ },
216
212
  executeRelease
217
- ),
218
- ([, queue]) => {
219
- const loop: Channel.Channel<Chunk.Chunk<O>, unknown, E | WorkerError, unknown, void, unknown> = Channel
220
- .flatMap(
221
- Queue.take(queue),
222
- Exit.match({
223
- onFailure: (cause) => Cause.isEmpty(cause) ? Channel.void : Channel.failCause(cause),
224
- onSuccess: (value) => Channel.flatMap(Channel.write(Chunk.unsafeFromArray(value)), () => loop)
225
- })
226
- )
227
- return Stream.fromChannel(loop)
228
- }
213
+ )
229
214
  )
230
215
 
231
216
  const executeEffect = (request: I) =>
232
217
  Effect.acquireUseRelease(
233
- executeAcquire(request),
234
- ([, queue]) => Effect.flatMap(Queue.take(queue), Exit.map(Arr.unsafeGet(0))),
218
+ executeAcquire(request, Deferred.make<O, WorkerError | E>()),
219
+ ({ queue }) => Deferred.await(queue),
235
220
  executeRelease
236
221
  )
237
222
 
238
- yield* outbound.take.pipe(
239
- Effect.flatMap(([id, request, span]) =>
240
- Effect.fork(
241
- Effect.suspend(() => {
242
- const result = requestMap.get(id)
243
- if (!result) return Effect.void
244
- const transferables = transfers(request)
245
- const spanTuple = Option.getOrUndefined(
246
- Option.map(span, (span) => [span.traceId, span.spanId, span.sampled] as const)
247
- )
248
- return pipe(
249
- Effect.flatMap(
250
- wrappedEncode(request),
251
- (payload) =>
252
- sendQueue.offer([[id, 0, payload, spanTuple], [
253
- ...transferables,
254
- ...collector.unsafeRead()
255
- ]])
256
- ),
257
- Effect.catchAllCause((cause) => Queue.offer(result[0], Exit.failCause(cause))),
258
- Effect.zipRight(Deferred.await(result[1]))
259
- )
260
- })
261
- )
262
- ),
263
- Effect.forever,
264
- Effect.forkScoped,
265
- Effect.interruptible
266
- )
267
-
268
223
  if (initialMessage) {
269
224
  yield* Effect.sync(initialMessage).pipe(
270
225
  Effect.flatMap(executeEffect),
271
- Effect.mapError((error) => new WorkerError({ reason: "spawn", error }))
226
+ Effect.mapError((cause) => new WorkerError({ reason: "spawn", cause }))
272
227
  )
273
228
  }
274
229
 
275
230
  return { id, execute, executeEffect }
276
- }).pipe(Effect.parallelFinalizers)
231
+ })
277
232
  }
278
233
  })
279
234
  })
@@ -290,8 +245,12 @@ export const makePool = <I, O, E>(
290
245
  const workers = new Set<Worker.Worker<I, O, E>>()
291
246
  const acquire = pipe(
292
247
  manager.spawn<I, O, E>(options),
293
- Effect.tap((worker) => Effect.sync(() => workers.add(worker))),
294
- Effect.tap((worker) => Effect.addFinalizer(() => Effect.sync(() => workers.delete(worker)))),
248
+ Effect.tap((worker) =>
249
+ Effect.acquireRelease(
250
+ Effect.sync(() => workers.add(worker)),
251
+ () => Effect.sync(() => workers.delete(worker))
252
+ )
253
+ ),
295
254
  options.onCreate ? Effect.tap(options.onCreate) : identity
296
255
  )
297
256
  const backing = "minSize" in options ?
@@ -353,7 +312,7 @@ export const makeSerialized = <
353
312
  encode(message) {
354
313
  return Effect.mapError(
355
314
  Serializable.serialize(message as any),
356
- (error) => new WorkerError({ reason: "encode", error })
315
+ (cause) => new WorkerError({ reason: "encode", cause })
357
316
  )
358
317
  }
359
318
  })
@@ -445,3 +404,91 @@ export const layerSpawner = <W = unknown>(spawner: Worker.SpawnerFn<W>) =>
445
404
  Spawner,
446
405
  spawner
447
406
  )
407
+
408
+ /** @internal */
409
+ export const makePlatform = <W>() =>
410
+ <
411
+ P extends {
412
+ readonly postMessage: (message: any, transfers?: any | undefined) => void
413
+ }
414
+ >(options: {
415
+ readonly setup: (options: {
416
+ readonly worker: W
417
+ readonly scope: Scope.Scope
418
+ }) => Effect.Effect<P>
419
+ readonly listen: (options: {
420
+ readonly port: P
421
+ readonly emit: (data: any) => void
422
+ readonly deferred: Deferred.Deferred<never, WorkerError>
423
+ readonly scope: Scope.Scope
424
+ }) => Effect.Effect<void>
425
+ }) =>
426
+ PlatformWorker.of({
427
+ [PlatformWorkerTypeId]: PlatformWorkerTypeId,
428
+ spawn<I, O>(id: number) {
429
+ return Effect.gen(function*(_) {
430
+ const spawn = (yield* Spawner) as Worker.SpawnerFn<W>
431
+ let currentPort: P | undefined
432
+ const buffer: Array<[I, ReadonlyArray<unknown> | undefined]> = []
433
+
434
+ const run = <A, E, R>(handler: (_: Worker.BackingWorker.Message<O>) => Effect.Effect<A, E, R>) =>
435
+ Effect.uninterruptibleMask((restore) =>
436
+ Scope.make().pipe(
437
+ Effect.bindTo("scope"),
438
+ Effect.bind("port", ({ scope }) => options.setup({ worker: spawn(id), scope })),
439
+ Effect.tap(({ port, scope }) => {
440
+ currentPort = port
441
+ return Scope.addFinalizer(
442
+ scope,
443
+ Effect.sync(() => {
444
+ currentPort = undefined
445
+ })
446
+ )
447
+ }),
448
+ Effect.bind("fiberSet", ({ scope }) =>
449
+ FiberSet.make<any, WorkerError | E>().pipe(
450
+ Scope.extend(scope)
451
+ )),
452
+ Effect.bind("runFork", ({ fiberSet }) => FiberSet.runtime(fiberSet)<R>()),
453
+ Effect.tap(({ fiberSet, port, runFork, scope }) =>
454
+ options.listen({
455
+ port,
456
+ scope,
457
+ emit(data) {
458
+ runFork(handler(data))
459
+ },
460
+ deferred: fiberSet.deferred as any
461
+ })
462
+ ),
463
+ Effect.tap(({ port }) => {
464
+ if (buffer.length > 0) {
465
+ for (const [message, transfers] of buffer) {
466
+ port.postMessage([0, message], transfers as any)
467
+ }
468
+ buffer.length = 0
469
+ }
470
+ }),
471
+ Effect.flatMap(({ fiberSet, scope }) =>
472
+ (restore(FiberSet.join(fiberSet)) as Effect.Effect<never, WorkerError | E>).pipe(
473
+ Effect.ensuring(Scope.close(scope, Exit.void))
474
+ )
475
+ )
476
+ )
477
+ )
478
+
479
+ const send = (message: I, transfers?: ReadonlyArray<unknown>) =>
480
+ Effect.try({
481
+ try: () => {
482
+ if (currentPort === undefined) {
483
+ buffer.push([message, transfers])
484
+ } else {
485
+ currentPort.postMessage([0, message], transfers as any)
486
+ }
487
+ },
488
+ catch: (cause) => new WorkerError({ reason: "send", cause })
489
+ })
490
+
491
+ return { run, send }
492
+ })
493
+ }
494
+ })