@effect/platform 0.22.0 → 0.22.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.
- package/Worker/dist/effect-platform-Worker.cjs.d.mts +2 -0
- package/Worker/dist/effect-platform-Worker.cjs.d.mts.map +1 -0
- package/Worker/dist/effect-platform-Worker.cjs.d.ts +2 -0
- package/Worker/dist/effect-platform-Worker.cjs.d.ts.map +1 -0
- package/Worker/dist/effect-platform-Worker.cjs.dev.js +291 -0
- package/Worker/dist/effect-platform-Worker.cjs.js +7 -0
- package/Worker/dist/effect-platform-Worker.cjs.mjs +10 -0
- package/Worker/dist/effect-platform-Worker.cjs.prod.js +291 -0
- package/Worker/dist/effect-platform-Worker.esm.js +252 -0
- package/Worker/package.json +4 -0
- package/WorkerError/dist/effect-platform-WorkerError.cjs.d.mts +2 -0
- package/WorkerError/dist/effect-platform-WorkerError.cjs.d.mts.map +1 -0
- package/WorkerError/dist/effect-platform-WorkerError.cjs.d.ts +2 -0
- package/WorkerError/dist/effect-platform-WorkerError.cjs.d.ts.map +1 -0
- package/WorkerError/dist/effect-platform-WorkerError.cjs.dev.js +65 -0
- package/WorkerError/dist/effect-platform-WorkerError.cjs.js +7 -0
- package/WorkerError/dist/effect-platform-WorkerError.cjs.mjs +4 -0
- package/WorkerError/dist/effect-platform-WorkerError.cjs.prod.js +65 -0
- package/WorkerError/dist/effect-platform-WorkerError.esm.js +40 -0
- package/WorkerError/package.json +4 -0
- package/WorkerRunner/dist/effect-platform-WorkerRunner.cjs.d.mts +2 -0
- package/WorkerRunner/dist/effect-platform-WorkerRunner.cjs.d.mts.map +1 -0
- package/WorkerRunner/dist/effect-platform-WorkerRunner.cjs.d.ts +2 -0
- package/WorkerRunner/dist/effect-platform-WorkerRunner.cjs.d.ts.map +1 -0
- package/WorkerRunner/dist/effect-platform-WorkerRunner.cjs.dev.js +129 -0
- package/WorkerRunner/dist/effect-platform-WorkerRunner.cjs.js +7 -0
- package/WorkerRunner/dist/effect-platform-WorkerRunner.cjs.mjs +5 -0
- package/WorkerRunner/dist/effect-platform-WorkerRunner.cjs.prod.js +129 -0
- package/WorkerRunner/dist/effect-platform-WorkerRunner.esm.js +97 -0
- package/WorkerRunner/package.json +4 -0
- package/dist/declarations/src/Worker.d.ts +170 -0
- package/dist/declarations/src/Worker.d.ts.map +1 -0
- package/dist/declarations/src/WorkerError.d.ts +30 -0
- package/dist/declarations/src/WorkerError.d.ts.map +1 -0
- package/dist/declarations/src/WorkerRunner.d.ts +72 -0
- package/dist/declarations/src/WorkerRunner.d.ts.map +1 -0
- package/dist/declarations/src/index.d.ts +12 -0
- package/dist/declarations/src/index.d.ts.map +1 -1
- package/dist/effect-platform.cjs.dev.js +9 -0
- package/dist/effect-platform.cjs.mjs +4 -1
- package/dist/effect-platform.cjs.prod.js +9 -0
- package/dist/effect-platform.esm.js +6 -0
- package/package.json +24 -3
- package/src/Worker.ts +207 -0
- package/src/WorkerError.ts +34 -0
- package/src/WorkerRunner.ts +83 -0
- package/src/index.ts +15 -0
- package/src/internal/worker.ts +262 -0
- package/src/internal/workerError.ts +16 -0
- package/src/internal/workerRunner.ts +83 -0
package/src/Worker.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import type { Effect } from "effect"
|
|
5
|
+
import type * as Context from "effect/Context"
|
|
6
|
+
import type * as Duration from "effect/Duration"
|
|
7
|
+
import type * as Layer from "effect/Layer"
|
|
8
|
+
import type * as Pool from "effect/Pool"
|
|
9
|
+
import type * as Queue from "effect/Queue"
|
|
10
|
+
import type * as Scope from "effect/Scope"
|
|
11
|
+
import type * as Stream from "effect/Stream"
|
|
12
|
+
import * as internal from "./internal/worker"
|
|
13
|
+
import type { WorkerError } from "./WorkerError"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
* @category models
|
|
18
|
+
*/
|
|
19
|
+
export interface BackingWorker<I, O> {
|
|
20
|
+
readonly join: Effect.Effect<never, WorkerError, never>
|
|
21
|
+
readonly send: (message: I, transfers?: ReadonlyArray<unknown>) => Effect.Effect<never, never, void>
|
|
22
|
+
readonly queue: Queue.Dequeue<BackingWorker.Message<O>>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @since 1.0.0
|
|
27
|
+
* @category models
|
|
28
|
+
*/
|
|
29
|
+
export declare namespace BackingWorker {
|
|
30
|
+
/**
|
|
31
|
+
* @since 1.0.0
|
|
32
|
+
* @category models
|
|
33
|
+
*/
|
|
34
|
+
export type Message<O> = readonly [ready: 0] | readonly [data: 1, O]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @since 1.0.0
|
|
39
|
+
* @category type ids
|
|
40
|
+
*/
|
|
41
|
+
export const PlatformWorkerTypeId: unique symbol = internal.PlatformWorkerTypeId
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @since 1.0.0
|
|
45
|
+
* @category type ids
|
|
46
|
+
*/
|
|
47
|
+
export type PlatformWorkerTypeId = typeof PlatformWorkerTypeId
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @since 1.0.0
|
|
51
|
+
* @category models
|
|
52
|
+
*/
|
|
53
|
+
export interface PlatformWorker {
|
|
54
|
+
readonly [PlatformWorkerTypeId]: PlatformWorkerTypeId
|
|
55
|
+
readonly spawn: <I, O>(worker: unknown) => Effect.Effect<Scope.Scope, WorkerError, BackingWorker<I, O>>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @since 1.0.0
|
|
60
|
+
* @category tags
|
|
61
|
+
*/
|
|
62
|
+
export const PlatformWorker: Context.Tag<PlatformWorker, PlatformWorker> = internal.PlatformWorker
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @since 1.0.0
|
|
66
|
+
* @category models
|
|
67
|
+
*/
|
|
68
|
+
export interface Worker<I, E, O> {
|
|
69
|
+
readonly id: number
|
|
70
|
+
readonly join: Effect.Effect<never, WorkerError, never>
|
|
71
|
+
readonly execute: (message: I) => Stream.Stream<never, E, O>
|
|
72
|
+
readonly executeEffect: (message: I) => Effect.Effect<never, E, O>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @since 1.0.0
|
|
77
|
+
* @category models
|
|
78
|
+
*/
|
|
79
|
+
export declare namespace Worker {
|
|
80
|
+
/**
|
|
81
|
+
* @since 1.0.0
|
|
82
|
+
* @category models
|
|
83
|
+
*/
|
|
84
|
+
export interface Options<I, W = unknown> {
|
|
85
|
+
readonly spawn: (id: number) => W
|
|
86
|
+
readonly transfers?: (message: I) => ReadonlyArray<unknown>
|
|
87
|
+
readonly permits?: number
|
|
88
|
+
readonly queue?: WorkerQueue<I>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @since 1.0.0
|
|
93
|
+
* @category models
|
|
94
|
+
*/
|
|
95
|
+
export type Request<I> = readonly [id: number, data: 0, I] | readonly [id: number, interrupt: 1]
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @since 1.0.0
|
|
99
|
+
* @category models
|
|
100
|
+
*/
|
|
101
|
+
export type Response<E, O> =
|
|
102
|
+
| readonly [id: number, data: 0, O]
|
|
103
|
+
| readonly [id: number, end: 1]
|
|
104
|
+
| readonly [id: number, end: 1, O]
|
|
105
|
+
| readonly [id: number, error: 2, E]
|
|
106
|
+
| readonly [id: number, defect: 3, unknown]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @since 1.0.0
|
|
111
|
+
* @category models
|
|
112
|
+
*/
|
|
113
|
+
export interface WorkerPool<I, E, O> {
|
|
114
|
+
readonly backing: Pool.Pool<WorkerError, Worker<I, E, O>>
|
|
115
|
+
readonly execute: (message: I) => Stream.Stream<never, E | WorkerError, O>
|
|
116
|
+
readonly executeEffect: (message: I) => Effect.Effect<never, E | WorkerError, O>
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @since 1.0.0
|
|
121
|
+
* @category models
|
|
122
|
+
*/
|
|
123
|
+
export declare namespace WorkerPool {
|
|
124
|
+
/**
|
|
125
|
+
* @since 1.0.0
|
|
126
|
+
* @category models
|
|
127
|
+
*/
|
|
128
|
+
export type Options<I, W = unknown> =
|
|
129
|
+
& Worker.Options<I, W>
|
|
130
|
+
& ({
|
|
131
|
+
readonly size: number
|
|
132
|
+
} | {
|
|
133
|
+
readonly minSize: number
|
|
134
|
+
readonly maxSize: number
|
|
135
|
+
readonly timeToLive: Duration.DurationInput
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @category models
|
|
141
|
+
* @since 1.0.0
|
|
142
|
+
*/
|
|
143
|
+
export interface WorkerQueue<I> {
|
|
144
|
+
readonly offer: (id: number, item: I) => Effect.Effect<never, never, void>
|
|
145
|
+
readonly take: Effect.Effect<never, never, readonly [id: number, item: I]>
|
|
146
|
+
readonly shutdown: Effect.Effect<never, never, void>
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @since 1.0.0
|
|
151
|
+
* @category type ids
|
|
152
|
+
*/
|
|
153
|
+
export const WorkerManagerTypeId: unique symbol = internal.WorkerManagerTypeId
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @since 1.0.0
|
|
157
|
+
* @category type ids
|
|
158
|
+
*/
|
|
159
|
+
export type WorkerManagerTypeId = typeof WorkerManagerTypeId
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @since 1.0.0
|
|
163
|
+
* @category models
|
|
164
|
+
*/
|
|
165
|
+
export interface WorkerManager {
|
|
166
|
+
readonly [WorkerManagerTypeId]: WorkerManagerTypeId
|
|
167
|
+
readonly spawn: <I, E, O>(
|
|
168
|
+
options: Worker.Options<I>
|
|
169
|
+
) => Effect.Effect<Scope.Scope, WorkerError, Worker<I, E, O>>
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @since 1.0.0
|
|
174
|
+
* @category tags
|
|
175
|
+
*/
|
|
176
|
+
export const WorkerManager: Context.Tag<WorkerManager, WorkerManager> = internal.WorkerManager
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @since 1.0.0
|
|
180
|
+
* @category constructors
|
|
181
|
+
*/
|
|
182
|
+
export const makeManager: Effect.Effect<PlatformWorker, never, WorkerManager> = internal.makeManager
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @since 1.0.0
|
|
186
|
+
* @category layers
|
|
187
|
+
*/
|
|
188
|
+
export const layerManager: Layer.Layer<PlatformWorker, never, WorkerManager> = internal.layerManager
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @since 1.0.0
|
|
192
|
+
* @category constructors
|
|
193
|
+
*/
|
|
194
|
+
export const makePool: <W>() => <I, E, O>(
|
|
195
|
+
options: WorkerPool.Options<I, W>
|
|
196
|
+
) => Effect.Effect<WorkerManager | Scope.Scope, never, WorkerPool<I, E, O>> = internal.makePool
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @since 1.0.0
|
|
200
|
+
* @category constructors
|
|
201
|
+
*/
|
|
202
|
+
export const makePoolLayer: <W>(
|
|
203
|
+
managerLayer: Layer.Layer<never, never, WorkerManager>
|
|
204
|
+
) => <Tag, I, E, O>(
|
|
205
|
+
tag: Context.Tag<Tag, WorkerPool<I, E, O>>,
|
|
206
|
+
options: WorkerPool.Options<I, W>
|
|
207
|
+
) => Layer.Layer<never, never, Tag> = internal.makePoolLayer
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import type * as Data from "effect/Data"
|
|
5
|
+
import * as internal from "./internal/workerError"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @since 1.0.0
|
|
9
|
+
* @category type ids
|
|
10
|
+
*/
|
|
11
|
+
export const WorkerErrorTypeId: unique symbol = internal.WorkerErrorTypeId
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
* @category type ids
|
|
16
|
+
*/
|
|
17
|
+
export type WorkerErrorTypeId = typeof WorkerErrorTypeId
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @since 1.0.0
|
|
21
|
+
* @category errors
|
|
22
|
+
*/
|
|
23
|
+
export interface WorkerError extends Data.Case {
|
|
24
|
+
readonly [WorkerErrorTypeId]: WorkerErrorTypeId
|
|
25
|
+
readonly _tag: "WorkerError"
|
|
26
|
+
readonly reason: "spawn" | "decode" | "unknown"
|
|
27
|
+
readonly error: unknown
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @since 1.0.0
|
|
32
|
+
* @category errors
|
|
33
|
+
*/
|
|
34
|
+
export const WorkerError: (reason: "spawn" | "decode" | "unknown", error: unknown) => WorkerError = internal.WorkerError
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import type { Effect } from "effect"
|
|
5
|
+
import type * as Context from "effect/Context"
|
|
6
|
+
import type * as Fiber from "effect/Fiber"
|
|
7
|
+
import type * as Queue from "effect/Queue"
|
|
8
|
+
import type * as Scope from "effect/Scope"
|
|
9
|
+
import type * as Stream from "effect/Stream"
|
|
10
|
+
import * as internal from "./internal/workerRunner"
|
|
11
|
+
import type { WorkerError } from "./WorkerError"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
* @category models
|
|
16
|
+
*/
|
|
17
|
+
export interface BackingRunner<I, O> {
|
|
18
|
+
readonly fiber: Fiber.Fiber<WorkerError, void>
|
|
19
|
+
readonly queue: Queue.Dequeue<I>
|
|
20
|
+
readonly send: (message: O, transfers?: ReadonlyArray<unknown>) => Effect.Effect<never, never, void>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @since 1.0.0
|
|
25
|
+
* @category models
|
|
26
|
+
*/
|
|
27
|
+
export declare namespace BackingRunner {
|
|
28
|
+
/**
|
|
29
|
+
* @since 1.0.0
|
|
30
|
+
* @category models
|
|
31
|
+
*/
|
|
32
|
+
export type Message<I> = readonly [request: 0, I] | readonly [close: 1]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @since 1.0.0
|
|
37
|
+
* @category type ids
|
|
38
|
+
*/
|
|
39
|
+
export const PlatformRunnerTypeId: unique symbol = internal.PlatformRunnerTypeId
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @since 1.0.0
|
|
43
|
+
* @category type ids
|
|
44
|
+
*/
|
|
45
|
+
export type PlatformRunnerTypeId = typeof PlatformRunnerTypeId
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @since 1.0.0
|
|
49
|
+
* @category models
|
|
50
|
+
*/
|
|
51
|
+
export interface PlatformRunner {
|
|
52
|
+
readonly [PlatformRunnerTypeId]: PlatformRunnerTypeId
|
|
53
|
+
readonly start: <I, O>() => Effect.Effect<Scope.Scope, WorkerError, BackingRunner<I, O>>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
* @category tags
|
|
59
|
+
*/
|
|
60
|
+
export const PlatformRunner: Context.Tag<PlatformRunner, PlatformRunner> = internal.PlatformRunner
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @since 1.0.0
|
|
64
|
+
* @category models
|
|
65
|
+
*/
|
|
66
|
+
export declare namespace Runner {
|
|
67
|
+
/**
|
|
68
|
+
* @since 1.0.0
|
|
69
|
+
* @category models
|
|
70
|
+
*/
|
|
71
|
+
export interface Options<O> {
|
|
72
|
+
readonly transfers?: (message: O) => ReadonlyArray<unknown>
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @since 1.0.0
|
|
78
|
+
* @category constructors
|
|
79
|
+
*/
|
|
80
|
+
export const make: <I, R, E, O>(
|
|
81
|
+
process: (request: I) => Stream.Stream<R, E, O> | Effect.Effect<R, E, O>,
|
|
82
|
+
options?: Runner.Options<O> | undefined
|
|
83
|
+
) => Effect.Effect<Scope.Scope | R | PlatformRunner, WorkerError, void> = internal.make
|
package/src/index.ts
CHANGED
|
@@ -47,3 +47,18 @@ export * as Path from "@effect/platform/Path"
|
|
|
47
47
|
* @since 1.0.0
|
|
48
48
|
*/
|
|
49
49
|
export * as Runtime from "@effect/platform/Runtime"
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @since 1.0.0
|
|
53
|
+
*/
|
|
54
|
+
export * as Worker from "@effect/platform/Worker"
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
*/
|
|
59
|
+
export * as WorkerError from "@effect/platform/WorkerError"
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @since 1.0.0
|
|
63
|
+
*/
|
|
64
|
+
export * as WorkerRunner from "@effect/platform/WorkerRunner"
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import * as Context from "effect/Context"
|
|
2
|
+
import * as Deferred from "effect/Deferred"
|
|
3
|
+
import * as Effect from "effect/Effect"
|
|
4
|
+
import * as Exit from "effect/Exit"
|
|
5
|
+
import * as Fiber from "effect/Fiber"
|
|
6
|
+
import { pipe } from "effect/Function"
|
|
7
|
+
import * as Layer from "effect/Layer"
|
|
8
|
+
import * as Pool from "effect/Pool"
|
|
9
|
+
import * as Queue from "effect/Queue"
|
|
10
|
+
import * as Stream from "effect/Stream"
|
|
11
|
+
import type * as Worker from "../Worker"
|
|
12
|
+
import type { WorkerError } from "../WorkerError"
|
|
13
|
+
|
|
14
|
+
/** @internal */
|
|
15
|
+
export const defaultQueue = <I>() =>
|
|
16
|
+
Effect.map(
|
|
17
|
+
Queue.unbounded<readonly [id: number, item: I]>(),
|
|
18
|
+
(queue): Worker.WorkerQueue<I> => ({
|
|
19
|
+
offer: (id, item) => Queue.offer(queue, [id, item]),
|
|
20
|
+
take: Queue.take(queue),
|
|
21
|
+
shutdown: Queue.shutdown(queue)
|
|
22
|
+
})
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
/** @internal */
|
|
26
|
+
export const PlatformWorkerTypeId: Worker.PlatformWorkerTypeId = Symbol.for(
|
|
27
|
+
"@effect/platform/Worker/PlatformWorker"
|
|
28
|
+
) as Worker.PlatformWorkerTypeId
|
|
29
|
+
|
|
30
|
+
/** @internal */
|
|
31
|
+
export const PlatformWorker = Context.Tag<Worker.PlatformWorker>(
|
|
32
|
+
PlatformWorkerTypeId
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
/** @internal */
|
|
36
|
+
export const WorkerManagerTypeId: Worker.WorkerManagerTypeId = Symbol.for(
|
|
37
|
+
"@effect/platform/Worker/WorkerManager"
|
|
38
|
+
) as Worker.WorkerManagerTypeId
|
|
39
|
+
|
|
40
|
+
/** @internal */
|
|
41
|
+
export const WorkerManager = Context.Tag<Worker.WorkerManager>(
|
|
42
|
+
WorkerManagerTypeId
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
/** @internal */
|
|
46
|
+
export const makeManager = Effect.gen(function*(_) {
|
|
47
|
+
const platform = yield* _(PlatformWorker)
|
|
48
|
+
let idCounter = 0
|
|
49
|
+
return WorkerManager.of({
|
|
50
|
+
[WorkerManagerTypeId]: WorkerManagerTypeId,
|
|
51
|
+
spawn<I, E, O>({ permits = 1, queue, spawn, transfers = (_) => [] }: Worker.Worker.Options<I>) {
|
|
52
|
+
return Effect.gen(function*(_) {
|
|
53
|
+
const id = idCounter++
|
|
54
|
+
const fiberId = yield* _(Effect.fiberId)
|
|
55
|
+
let requestIdCounter = 0
|
|
56
|
+
const readyLatch = yield* _(Deferred.make<never, void>())
|
|
57
|
+
const semaphore = yield* _(Effect.makeSemaphore(permits))
|
|
58
|
+
const requestMap = new Map<number, readonly [Queue.Queue<Exit.Exit<E, O>>, Deferred.Deferred<never, void>]>()
|
|
59
|
+
|
|
60
|
+
const outbound = queue ?? (yield* _(defaultQueue<I>()))
|
|
61
|
+
yield* _(Effect.addFinalizer(() => outbound.shutdown))
|
|
62
|
+
|
|
63
|
+
const backing = yield* _(
|
|
64
|
+
platform.spawn<Worker.Worker.Request<I>, Worker.Worker.Response<E, O>>(spawn(id))
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
yield* _(Effect.addFinalizer(() =>
|
|
68
|
+
Effect.zipRight(
|
|
69
|
+
Effect.forEach(requestMap.values(), ([queue]) => Queue.shutdown(queue), { discard: true }),
|
|
70
|
+
Effect.sync(() => requestMap.clear())
|
|
71
|
+
)
|
|
72
|
+
))
|
|
73
|
+
|
|
74
|
+
const handleMessage = (msg: Worker.BackingWorker.Message<Worker.Worker.Response<E, O>>) =>
|
|
75
|
+
Effect.suspend(() => {
|
|
76
|
+
switch (msg[0]) {
|
|
77
|
+
case 0: {
|
|
78
|
+
return Deferred.complete(readyLatch, Effect.unit)
|
|
79
|
+
}
|
|
80
|
+
case 1: {
|
|
81
|
+
const response = msg[1]
|
|
82
|
+
const queue = requestMap.get(response[0])
|
|
83
|
+
if (!queue) return Effect.unit
|
|
84
|
+
|
|
85
|
+
switch (response[1]) {
|
|
86
|
+
// data
|
|
87
|
+
case 0: {
|
|
88
|
+
return Queue.offer(queue[0], Exit.succeed(response[2]))
|
|
89
|
+
}
|
|
90
|
+
// end
|
|
91
|
+
case 1: {
|
|
92
|
+
return response.length === 2 ?
|
|
93
|
+
Queue.shutdown(queue[0]) :
|
|
94
|
+
Effect.zipRight(
|
|
95
|
+
Queue.offer(queue[0], Exit.succeed(response[2])),
|
|
96
|
+
Queue.shutdown(queue[0])
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
// error / defect
|
|
100
|
+
case 2:
|
|
101
|
+
case 3: {
|
|
102
|
+
return Effect.zipRight(
|
|
103
|
+
Queue.offer(
|
|
104
|
+
queue[0],
|
|
105
|
+
response[1] === 2
|
|
106
|
+
? Exit.fail(response[2])
|
|
107
|
+
: Exit.die(response[2])
|
|
108
|
+
),
|
|
109
|
+
Queue.shutdown(queue[0])
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const executeAcquire = (request: I) =>
|
|
118
|
+
Effect.tap(
|
|
119
|
+
Effect.all([
|
|
120
|
+
Effect.sync(() => requestIdCounter++),
|
|
121
|
+
Queue.unbounded<Exit.Exit<E, O>>(),
|
|
122
|
+
Deferred.make<never, void>()
|
|
123
|
+
]),
|
|
124
|
+
([id, queue, deferred]) =>
|
|
125
|
+
Effect.suspend(() => {
|
|
126
|
+
requestMap.set(id, [queue, deferred])
|
|
127
|
+
return outbound.offer(id, request)
|
|
128
|
+
})
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
const executeRelease = (
|
|
132
|
+
[id, , deferred]: [number, Queue.Queue<Exit.Exit<E, O>>, Deferred.Deferred<never, void>],
|
|
133
|
+
exit: Exit.Exit<unknown, unknown>
|
|
134
|
+
) => {
|
|
135
|
+
const release = Effect.zipRight(
|
|
136
|
+
Deferred.complete(deferred, Effect.unit),
|
|
137
|
+
Effect.sync(() => requestMap.delete(id))
|
|
138
|
+
)
|
|
139
|
+
return Exit.isInterrupted(exit) ?
|
|
140
|
+
Effect.zipRight(
|
|
141
|
+
backing.send([id, 1]),
|
|
142
|
+
release
|
|
143
|
+
) :
|
|
144
|
+
release
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const execute = (request: I) =>
|
|
148
|
+
Stream.flatMap(
|
|
149
|
+
Stream.acquireRelease(
|
|
150
|
+
executeAcquire(request),
|
|
151
|
+
executeRelease
|
|
152
|
+
),
|
|
153
|
+
([, queue]) => Stream.flatten(Stream.fromQueue(queue))
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
const executeEffect = (request: I) =>
|
|
157
|
+
Effect.acquireUseRelease(
|
|
158
|
+
executeAcquire(request),
|
|
159
|
+
([, queue]) => Effect.flatten(Queue.take(queue)),
|
|
160
|
+
executeRelease
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
const handleMessages = yield* _(
|
|
164
|
+
Queue.take(backing.queue),
|
|
165
|
+
Effect.flatMap(handleMessage),
|
|
166
|
+
Effect.forever,
|
|
167
|
+
Effect.forkDaemon
|
|
168
|
+
)
|
|
169
|
+
yield* _(Effect.addFinalizer(() => handleMessages.interruptAsFork(fiberId)))
|
|
170
|
+
|
|
171
|
+
const postMessages = yield* _(
|
|
172
|
+
semaphore.take(1),
|
|
173
|
+
Effect.zipRight(outbound.take),
|
|
174
|
+
Effect.flatMap(([id, request]) =>
|
|
175
|
+
pipe(
|
|
176
|
+
Effect.suspend(() => {
|
|
177
|
+
const result = requestMap.get(id)
|
|
178
|
+
if (!result) return Effect.unit
|
|
179
|
+
const transferables = transfers(request)
|
|
180
|
+
return Effect.zipRight(
|
|
181
|
+
backing.send([id, 0, request], transferables),
|
|
182
|
+
Deferred.await(result[1])
|
|
183
|
+
)
|
|
184
|
+
}),
|
|
185
|
+
Effect.ensuring(semaphore.release(1)),
|
|
186
|
+
Effect.fork
|
|
187
|
+
)
|
|
188
|
+
),
|
|
189
|
+
Effect.forever,
|
|
190
|
+
Effect.forkDaemon
|
|
191
|
+
)
|
|
192
|
+
yield* _(Effect.addFinalizer(() => postMessages.interruptAsFork(fiberId)))
|
|
193
|
+
|
|
194
|
+
const join = Effect.race(
|
|
195
|
+
Fiber.joinAll([
|
|
196
|
+
handleMessages,
|
|
197
|
+
postMessages
|
|
198
|
+
]),
|
|
199
|
+
backing.join
|
|
200
|
+
) as Effect.Effect<never, WorkerError, never>
|
|
201
|
+
|
|
202
|
+
return { id, join, execute, executeEffect }
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
/** @internal */
|
|
209
|
+
export const layerManager = Layer.effect(WorkerManager, makeManager)
|
|
210
|
+
|
|
211
|
+
/** @internal */
|
|
212
|
+
export const makePool = <W>() =>
|
|
213
|
+
<I, E, O>(
|
|
214
|
+
options: Worker.WorkerPool.Options<I, W>
|
|
215
|
+
) =>
|
|
216
|
+
Effect.gen(function*(_) {
|
|
217
|
+
const manager = yield* _(WorkerManager)
|
|
218
|
+
const backing = yield* _(
|
|
219
|
+
"timeToLive" in options ?
|
|
220
|
+
Pool.makeWithTTL({
|
|
221
|
+
acquire: manager.spawn<I, E, O>(options),
|
|
222
|
+
min: options.minSize,
|
|
223
|
+
max: options.maxSize,
|
|
224
|
+
timeToLive: options.timeToLive
|
|
225
|
+
}) :
|
|
226
|
+
Pool.make({
|
|
227
|
+
acquire: manager.spawn<I, E, O>(options),
|
|
228
|
+
size: options.size
|
|
229
|
+
})
|
|
230
|
+
)
|
|
231
|
+
const pool: Worker.WorkerPool<I, E, O> = {
|
|
232
|
+
backing,
|
|
233
|
+
execute: (message: I) =>
|
|
234
|
+
Stream.unwrap(
|
|
235
|
+
Effect.map(
|
|
236
|
+
Effect.scoped(backing.get()),
|
|
237
|
+
(worker) => worker.execute(message)
|
|
238
|
+
)
|
|
239
|
+
),
|
|
240
|
+
executeEffect: (message: I) =>
|
|
241
|
+
Effect.flatMap(
|
|
242
|
+
Effect.scoped(backing.get()),
|
|
243
|
+
(worker) => worker.executeEffect(message)
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return pool
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
/** @internal */
|
|
251
|
+
export const makePoolLayer = <W>(managerLayer: Layer.Layer<never, never, Worker.WorkerManager>) =>
|
|
252
|
+
<Tag, I, E, O>(
|
|
253
|
+
tag: Context.Tag<Tag, Worker.WorkerPool<I, E, O>>,
|
|
254
|
+
options: Worker.WorkerPool.Options<I, W>
|
|
255
|
+
) =>
|
|
256
|
+
Layer.provide(
|
|
257
|
+
managerLayer,
|
|
258
|
+
Layer.scoped(
|
|
259
|
+
tag,
|
|
260
|
+
makePool<W>()(options)
|
|
261
|
+
)
|
|
262
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as Data from "effect/Data"
|
|
2
|
+
import type * as WorkerError_ from "../WorkerError"
|
|
3
|
+
|
|
4
|
+
/** @internal */
|
|
5
|
+
export const WorkerErrorTypeId: WorkerError_.WorkerErrorTypeId = Symbol.for(
|
|
6
|
+
"@effect-ts/platform/Worker/WorkerError"
|
|
7
|
+
) as WorkerError_.WorkerErrorTypeId
|
|
8
|
+
|
|
9
|
+
/** @internal */
|
|
10
|
+
export const WorkerError = (reason: WorkerError_.WorkerError["reason"], error: unknown): WorkerError_.WorkerError =>
|
|
11
|
+
Data.struct({
|
|
12
|
+
[WorkerErrorTypeId]: WorkerErrorTypeId,
|
|
13
|
+
_tag: "WorkerError",
|
|
14
|
+
reason,
|
|
15
|
+
error
|
|
16
|
+
})
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause"
|
|
2
|
+
import * as Context from "effect/Context"
|
|
3
|
+
import * as Effect from "effect/Effect"
|
|
4
|
+
import * as Either from "effect/Either"
|
|
5
|
+
import * as Fiber from "effect/Fiber"
|
|
6
|
+
import { pipe } from "effect/Function"
|
|
7
|
+
import * as Queue from "effect/Queue"
|
|
8
|
+
import * as Stream from "effect/Stream"
|
|
9
|
+
import type * as Worker from "../Worker"
|
|
10
|
+
import type * as WorkerRunner from "../WorkerRunner"
|
|
11
|
+
|
|
12
|
+
/** @internal */
|
|
13
|
+
export const PlatformRunnerTypeId: WorkerRunner.PlatformRunnerTypeId = Symbol.for(
|
|
14
|
+
"@effect/platform/Runner/PlatformRunner"
|
|
15
|
+
) as WorkerRunner.PlatformRunnerTypeId
|
|
16
|
+
|
|
17
|
+
/** @internal */
|
|
18
|
+
export const PlatformRunner = Context.Tag<WorkerRunner.PlatformRunner>(
|
|
19
|
+
PlatformRunnerTypeId
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
/** @internal */
|
|
23
|
+
export const make = <I, R, E, O>(
|
|
24
|
+
process: (request: I) => Stream.Stream<R, E, O> | Effect.Effect<R, E, O>,
|
|
25
|
+
options?: WorkerRunner.Runner.Options<O>
|
|
26
|
+
) =>
|
|
27
|
+
Effect.gen(function*(_) {
|
|
28
|
+
const platform = yield* _(PlatformRunner)
|
|
29
|
+
const backing = yield* _(platform.start<Worker.Worker.Request<I>, Worker.Worker.Response<E, O>>())
|
|
30
|
+
const fiberMap = new Map<number, Fiber.Fiber<never, void>>()
|
|
31
|
+
|
|
32
|
+
const handleRequests = pipe(
|
|
33
|
+
Queue.take(backing.queue),
|
|
34
|
+
Effect.tap((req) => {
|
|
35
|
+
const id = req[0]
|
|
36
|
+
if (req[1] === 1) {
|
|
37
|
+
const fiber = fiberMap.get(id)
|
|
38
|
+
if (!fiber) return Effect.unit
|
|
39
|
+
return Fiber.interrupt(fiber)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const stream = process(req[2])
|
|
43
|
+
|
|
44
|
+
const effect = Effect.isEffect(stream) ?
|
|
45
|
+
Effect.matchCauseEffect(stream, {
|
|
46
|
+
onFailure: (cause) =>
|
|
47
|
+
Either.match(Cause.failureOrCause(cause), {
|
|
48
|
+
onLeft: (error) => backing.send([id, 2, error]),
|
|
49
|
+
onRight: (cause) => backing.send([id, 3, Cause.squash(cause)])
|
|
50
|
+
}),
|
|
51
|
+
onSuccess: (data) => backing.send([id, 1, data])
|
|
52
|
+
}) :
|
|
53
|
+
pipe(
|
|
54
|
+
stream,
|
|
55
|
+
Stream.tap((item) => backing.send([id, 0, item], options?.transfers ? options.transfers(item) : undefined)),
|
|
56
|
+
Stream.runDrain,
|
|
57
|
+
Effect.matchCauseEffect({
|
|
58
|
+
onFailure: (cause) =>
|
|
59
|
+
Either.match(Cause.failureOrCause(cause), {
|
|
60
|
+
onLeft: (error) => backing.send([id, 2, error]),
|
|
61
|
+
onRight: (cause) => backing.send([id, 3, Cause.squash(cause)])
|
|
62
|
+
}),
|
|
63
|
+
onSuccess: () => backing.send([id, 1])
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return pipe(
|
|
68
|
+
effect,
|
|
69
|
+
Effect.ensuring(Effect.sync(() => fiberMap.delete(id))),
|
|
70
|
+
Effect.fork,
|
|
71
|
+
Effect.tap((fiber) => Effect.sync(() => fiberMap.set(id, fiber)))
|
|
72
|
+
)
|
|
73
|
+
}),
|
|
74
|
+
Effect.forever
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return yield* _(
|
|
78
|
+
Effect.all([
|
|
79
|
+
handleRequests,
|
|
80
|
+
Fiber.join(backing.fiber)
|
|
81
|
+
], { concurrency: "unbounded", discard: true })
|
|
82
|
+
)
|
|
83
|
+
})
|