@effect/platform 0.37.1 → 0.37.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.
@@ -12,6 +12,8 @@ import { identity, pipe } from "effect/Function"
12
12
  import * as Layer from "effect/Layer"
13
13
  import * as Pool from "effect/Pool"
14
14
  import * as Queue from "effect/Queue"
15
+ import * as ReadonlyArray from "effect/ReadonlyArray"
16
+ import * as Schedule from "effect/Schedule"
15
17
  import type * as Scope from "effect/Scope"
16
18
  import * as Stream from "effect/Stream"
17
19
  import * as Transferable from "../Transferable.js"
@@ -66,18 +68,60 @@ export const makeManager = Effect.gen(function*(_) {
66
68
  return Effect.gen(function*(_) {
67
69
  const id = idCounter++
68
70
  let requestIdCounter = 0
69
- const readyLatch = yield* _(Deferred.make<never, void>())
70
71
  const semaphore = yield* _(Effect.makeSemaphore(permits))
71
72
  const requestMap = new Map<
72
73
  number,
73
- readonly [Queue.Queue<Exit.Exit<E | WorkerError, O>>, Deferred.Deferred<never, void>]
74
+ readonly [Queue.Queue<Exit.Exit<E | WorkerError, ReadonlyArray<O>>>, Deferred.Deferred<never, void>]
74
75
  >()
76
+ const sendQueue = yield* _(Effect.acquireRelease(
77
+ Queue.unbounded<readonly [message: Worker.Worker.Request, transfers?: ReadonlyArray<unknown>]>(),
78
+ Queue.shutdown
79
+ ))
75
80
 
76
81
  const outbound = queue ?? (yield* _(defaultQueue<I>()))
77
82
  yield* _(Effect.addFinalizer(() => outbound.shutdown))
78
83
 
79
- const backing = yield* _(
80
- platform.spawn<Worker.Worker.Request, Worker.Worker.Response<E, O>>(spawn(id))
84
+ yield* _(
85
+ Effect.gen(function*(_) {
86
+ const readyLatch = yield* _(Deferred.make<never, void>())
87
+ const backing = yield* _(
88
+ platform.spawn<Worker.Worker.Request, Worker.Worker.Response<E, O>>(spawn(id))
89
+ )
90
+ const send = pipe(
91
+ sendQueue.take,
92
+ Effect.flatMap(([message, transfers]) => backing.send(message, transfers)),
93
+ Effect.forever
94
+ )
95
+ const take = pipe(
96
+ Queue.take(backing.queue),
97
+ Effect.flatMap((msg) => {
98
+ if (msg[0] === 0) {
99
+ return Deferred.complete(readyLatch, Effect.unit)
100
+ }
101
+ return handleMessage(msg[1])
102
+ }),
103
+ Effect.forever
104
+ )
105
+ return yield* _(Effect.all([
106
+ Fiber.join(backing.fiber),
107
+ Effect.zipRight(Deferred.await(readyLatch), send),
108
+ take
109
+ ], { concurrency: "unbounded" }))
110
+ }),
111
+ Effect.scoped,
112
+ Effect.onError((cause) =>
113
+ Effect.forEach(requestMap.values(), ([queue]) => Queue.offer(queue, Exit.failCause(cause)))
114
+ ),
115
+ Effect.retry(
116
+ Schedule.exponential("250 millis").pipe(
117
+ Schedule.union(Schedule.spaced("30 seconds"))
118
+ )
119
+ ),
120
+ Effect.annotateLogs({
121
+ package: "@effect/platform",
122
+ module: "Worker"
123
+ }),
124
+ Effect.forkScoped
81
125
  )
82
126
 
83
127
  yield* _(Effect.addFinalizer(() =>
@@ -89,42 +133,34 @@ export const makeManager = Effect.gen(function*(_) {
89
133
  )
90
134
  ))
91
135
 
92
- const handleMessage = (msg: Worker.BackingWorker.Message<Worker.Worker.Response<E, O>>) =>
136
+ const handleMessage = (response: Worker.Worker.Response<E, O>) =>
93
137
  Effect.suspend(() => {
94
- switch (msg[0]) {
138
+ const queue = requestMap.get(response[0])
139
+ if (!queue) return Effect.unit
140
+
141
+ switch (response[1]) {
142
+ // data
95
143
  case 0: {
96
- return Deferred.complete(readyLatch, Effect.unit)
144
+ return Queue.offer(queue[0], Exit.succeed(response[2]))
97
145
  }
146
+ // end
98
147
  case 1: {
99
- const response = msg[1]
100
- const queue = requestMap.get(response[0])
101
- if (!queue) return Effect.unit
102
-
103
- switch (response[1]) {
104
- // data
105
- case 0: {
106
- return Queue.offer(queue[0], Exit.succeed(response[2]))
107
- }
108
- // end
109
- case 1: {
110
- return response.length === 2 ?
111
- Queue.offer(queue[0], Exit.failCause(Cause.empty)) :
112
- Effect.zipRight(
113
- Queue.offer(queue[0], Exit.succeed(response[2])),
114
- Queue.offer(queue[0], Exit.failCause(Cause.empty))
115
- )
116
- }
117
- // error / defect
118
- case 2:
119
- case 3: {
120
- return Queue.offer(
121
- queue[0],
122
- response[1] === 2
123
- ? Exit.fail(response[2])
124
- : Exit.die(response[2])
125
- )
126
- }
127
- }
148
+ return response.length === 2 ?
149
+ Queue.offer(queue[0], Exit.failCause(Cause.empty)) :
150
+ Effect.zipRight(
151
+ Queue.offer(queue[0], Exit.succeed(response[2])),
152
+ Queue.offer(queue[0], Exit.failCause(Cause.empty))
153
+ )
154
+ }
155
+ // error / defect
156
+ case 2:
157
+ case 3: {
158
+ return Queue.offer(
159
+ queue[0],
160
+ response[1] === 2
161
+ ? Exit.fail(response[2])
162
+ : Exit.die(response[2])
163
+ )
128
164
  }
129
165
  }
130
166
  })
@@ -133,7 +169,7 @@ export const makeManager = Effect.gen(function*(_) {
133
169
  Effect.tap(
134
170
  Effect.all([
135
171
  Effect.sync(() => requestIdCounter++),
136
- Queue.unbounded<Exit.Exit<E | WorkerError, O>>(),
172
+ Queue.unbounded<Exit.Exit<E | WorkerError, ReadonlyArray<O>>>(),
137
173
  Deferred.make<never, void>()
138
174
  ]),
139
175
  ([id, queue, deferred]) =>
@@ -144,7 +180,11 @@ export const makeManager = Effect.gen(function*(_) {
144
180
  )
145
181
 
146
182
  const executeRelease = (
147
- [id, , deferred]: [number, Queue.Queue<Exit.Exit<E | WorkerError, O>>, Deferred.Deferred<never, void>],
183
+ [id, , deferred]: [
184
+ number,
185
+ Queue.Queue<Exit.Exit<E | WorkerError, ReadonlyArray<O>>>,
186
+ Deferred.Deferred<never, void>
187
+ ],
148
188
  exit: Exit.Exit<unknown, unknown>
149
189
  ) => {
150
190
  const release = Effect.zipRight(
@@ -152,10 +192,7 @@ export const makeManager = Effect.gen(function*(_) {
152
192
  Effect.sync(() => requestMap.delete(id))
153
193
  )
154
194
  return Exit.isInterrupted(exit) ?
155
- Effect.zipRight(
156
- Effect.ignore(backing.send([id, 1])),
157
- release
158
- ) :
195
+ Effect.zipRight(sendQueue.offer([[id, 1]]), release) :
159
196
  release
160
197
  }
161
198
 
@@ -171,7 +208,7 @@ export const makeManager = Effect.gen(function*(_) {
171
208
  Queue.take(queue),
172
209
  Exit.match({
173
210
  onFailure: (cause) => Cause.isEmpty(cause) ? Channel.unit : Channel.failCause(cause),
174
- onSuccess: (value) => Channel.flatMap(Channel.write(Chunk.of(value)), () => loop)
211
+ onSuccess: (value) => Channel.flatMap(Channel.write(Chunk.unsafeFromArray(value)), () => loop)
175
212
  })
176
213
  )
177
214
  return Stream.fromChannel(loop)
@@ -181,18 +218,11 @@ export const makeManager = Effect.gen(function*(_) {
181
218
  const executeEffect = (request: I) =>
182
219
  Effect.acquireUseRelease(
183
220
  executeAcquire(request),
184
- ([, queue]) => Effect.flatten(Queue.take(queue)),
221
+ ([, queue]) => Effect.flatMap(Queue.take(queue), Exit.map(ReadonlyArray.unsafeGet(0))),
185
222
  executeRelease
186
223
  )
187
224
 
188
- const handleMessages = yield* _(
189
- Queue.take(backing.queue),
190
- Effect.flatMap(handleMessage),
191
- Effect.forever,
192
- Effect.forkScoped
193
- )
194
-
195
- const postMessages = pipe(
225
+ yield* _(
196
226
  semaphore.take(1),
197
227
  Effect.zipRight(outbound.take),
198
228
  Effect.flatMap(([id, request]) =>
@@ -204,7 +234,7 @@ export const makeManager = Effect.gen(function*(_) {
204
234
  return pipe(
205
235
  Effect.flatMap(
206
236
  encode ? encode(request) : Effect.succeed(request),
207
- (payload) => backing.send([id, 0, payload], transferables)
237
+ (payload) => sendQueue.offer([[id, 0, payload], transferables])
208
238
  ),
209
239
  Effect.catchAllCause((cause) => Queue.offer(result[0], Exit.failCause(cause))),
210
240
  Effect.zipRight(Deferred.await(result[1]))
@@ -214,21 +244,10 @@ export const makeManager = Effect.gen(function*(_) {
214
244
  Effect.fork
215
245
  )
216
246
  ),
217
- Effect.forever
218
- )
219
-
220
- const postMessagesFiber = yield* _(
221
- Deferred.await(readyLatch),
222
- Effect.zipRight(postMessages),
247
+ Effect.forever,
223
248
  Effect.forkScoped
224
249
  )
225
250
 
226
- const join = Fiber.joinAll([backing.fiber, handleMessages, postMessagesFiber]) as Effect.Effect<
227
- never,
228
- WorkerError,
229
- never
230
- >
231
-
232
251
  if (initialMessage) {
233
252
  yield* _(
234
253
  Effect.sync(initialMessage),
@@ -237,7 +256,7 @@ export const makeManager = Effect.gen(function*(_) {
237
256
  )
238
257
  }
239
258
 
240
- return { id, join, execute, executeEffect }
259
+ return { id, execute, executeEffect }
241
260
  }).pipe(Effect.parallelFinalizers)
242
261
  }
243
262
  })
@@ -343,7 +362,6 @@ export const makeSerialized = <
343
362
  }
344
363
  return identity<Worker.SerializedWorker<I>>({
345
364
  id: backing.id,
346
- join: backing.join,
347
365
  execute: execute as any,
348
366
  executeEffect: executeEffect as any
349
367
  })
@@ -1,11 +1,14 @@
1
1
  import * as Schema from "@effect/schema/Schema"
2
2
  import * as Serializable from "@effect/schema/Serializable"
3
3
  import * as Cause from "effect/Cause"
4
+ import * as Chunk from "effect/Chunk"
4
5
  import * as Context from "effect/Context"
5
6
  import * as Effect from "effect/Effect"
6
7
  import * as Either from "effect/Either"
7
8
  import * as Fiber from "effect/Fiber"
8
9
  import { pipe } from "effect/Function"
10
+ import * as Layer from "effect/Layer"
11
+ import * as Option from "effect/Option"
9
12
  import * as Predicate from "effect/Predicate"
10
13
  import * as Queue from "effect/Queue"
11
14
  import type * as Scope from "effect/Scope"
@@ -34,8 +37,9 @@ export const make = <I, R, E, O>(
34
37
  const platform = yield* _(PlatformRunner)
35
38
  const backing = yield* _(platform.start<Worker.Worker.Request<I>, Worker.Worker.Response<E>>())
36
39
  const fiberMap = new Map<number, Fiber.Fiber<never, void>>()
40
+ const parentFiber = Option.getOrThrow(Fiber.getCurrentFiber())
37
41
 
38
- const handleRequests = pipe(
42
+ yield* _(
39
43
  Queue.take(backing.queue),
40
44
  Effect.tap((req) => {
41
45
  const id = req[0]
@@ -65,17 +69,29 @@ export const make = <I, R, E, O>(
65
69
  const transfers = options?.transfers ? options.transfers(data) : undefined
66
70
  return pipe(
67
71
  options?.encodeOutput ? options.encodeOutput(data) : Effect.succeed(data),
68
- Effect.flatMap((payload) => backing.send([id, 0, payload], transfers)),
72
+ Effect.flatMap((payload) => backing.send([id, 0, [payload]], transfers)),
69
73
  Effect.catchAllCause((cause) => backing.send([id, 3, Cause.squash(cause)]))
70
74
  )
71
75
  }
72
76
  }) :
73
77
  pipe(
74
78
  stream,
79
+ Stream.chunks,
75
80
  Stream.tap((data) => {
76
- const transfers = options?.transfers ? options.transfers(data) : undefined
81
+ if (options?.encodeOutput === undefined) {
82
+ const payload = Chunk.toReadonlyArray(data)
83
+ const transfers = options?.transfers ? payload.flatMap(options.transfers) : undefined
84
+ return backing.send([id, 0, payload], transfers)
85
+ }
86
+
87
+ const transfers: Array<unknown> = []
77
88
  return Effect.flatMap(
78
- options?.encodeOutput ? Effect.orDie(options.encodeOutput(data)) : Effect.succeed(data),
89
+ Effect.forEach(data, (data) => {
90
+ if (options?.transfers) {
91
+ transfers.push(...options.transfers(data))
92
+ }
93
+ return Effect.orDie(options.encodeOutput!(data))
94
+ }),
79
95
  (payload) => backing.send([id, 0, payload], transfers)
80
96
  )
81
97
  }),
@@ -104,17 +120,19 @@ export const make = <I, R, E, O>(
104
120
  Effect.tap((fiber) => Effect.sync(() => fiberMap.set(id, fiber)))
105
121
  )
106
122
  }),
107
- Effect.forever
108
- )
109
-
110
- return yield* _(
111
- Effect.all([
112
- handleRequests,
113
- Fiber.join(backing.fiber)
114
- ], { concurrency: "unbounded", discard: true }) as Effect.Effect<R, WorkerError.WorkerError, never>
123
+ Effect.forever,
124
+ Effect.onInterrupt(() => Fiber.interrupt(parentFiber)),
125
+ Effect.forkScoped
115
126
  )
116
127
  })
117
128
 
129
+ /** @internal */
130
+ export const layer = <I, R, E, O>(
131
+ process: (request: I) => Stream.Stream<R, E, O> | Effect.Effect<R, E, O>,
132
+ options?: WorkerRunner.Runner.Options<E, O>
133
+ ): Layer.Layer<WorkerRunner.PlatformRunner | R, WorkerError.WorkerError, never> =>
134
+ Layer.scopedDiscard(make(process, options))
135
+
118
136
  /** @internal */
119
137
  export const makeSerialized = <
120
138
  I,
@@ -133,7 +151,7 @@ export const makeSerialized = <
133
151
  | Scope.Scope
134
152
  | (ReturnType<Handlers[keyof Handlers]> extends Stream.Stream<infer R, infer _E, infer _A> ? R : never),
135
153
  WorkerError.WorkerError,
136
- never
154
+ void
137
155
  > => {
138
156
  const parseRequest = Schema.decode(schema)
139
157
  const effectTags = new Set<string>()
@@ -178,3 +196,23 @@ export const makeSerialized = <
178
196
  }
179
197
  })
180
198
  }
199
+
200
+ /** @internal */
201
+ export const layerSerialized = <
202
+ I,
203
+ A extends Schema.TaggedRequest.Any,
204
+ const Handlers extends {
205
+ readonly [K in A["_tag"]]: Extract<A, { readonly _tag: K }> extends
206
+ Serializable.SerializableWithResult<infer _IS, infer S, infer _IE, infer E, infer _IO, infer O>
207
+ ? (_: S) => Stream.Stream<any, E, O> | Effect.Effect<any, E, O> :
208
+ never
209
+ }
210
+ >(
211
+ schema: Schema.Schema<I, A>,
212
+ handlers: Handlers
213
+ ): Layer.Layer<
214
+ | WorkerRunner.PlatformRunner
215
+ | (ReturnType<Handlers[keyof Handlers]> extends Stream.Stream<infer R, infer _E, infer _A> ? R : never),
216
+ WorkerError.WorkerError,
217
+ never
218
+ > => Layer.scopedDiscard(makeSerialized(schema, handlers))