@effect/platform-browser 0.37.26 → 0.38.0

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.
@@ -1,12 +1,14 @@
1
1
  import { WorkerError } from "@effect/platform/WorkerError"
2
2
  import * as Runner from "@effect/platform/WorkerRunner"
3
- import * as Cause from "effect/Cause"
3
+ import * as Deferred from "effect/Deferred"
4
4
  import * as Effect from "effect/Effect"
5
+ import * as ExecStrategy from "effect/ExecutionStrategy"
6
+ import * as Exit from "effect/Exit"
7
+ import * as FiberId from "effect/FiberId"
5
8
  import * as FiberSet from "effect/FiberSet"
6
9
  import { globalValue } from "effect/GlobalValue"
7
10
  import * as Layer from "effect/Layer"
8
- import * as Queue from "effect/Queue"
9
- import * as Schedule from "effect/Schedule"
11
+ import * as Scope from "effect/Scope"
10
12
 
11
13
  const cachedPorts = globalValue("@effect/platform-browser/Worker/cachedPorts", () => new Set<MessagePort>())
12
14
  function globalHandleConnect(event: MessageEvent) {
@@ -18,92 +20,110 @@ if (typeof self !== "undefined" && "onconnect" in self) {
18
20
 
19
21
  const platformRunnerImpl = Runner.PlatformRunner.of({
20
22
  [Runner.PlatformRunnerTypeId]: Runner.PlatformRunnerTypeId,
21
- start<I, O>(shutdown: Effect.Effect<void>) {
22
- return Effect.gen(function*() {
23
+ start<I, O>() {
24
+ return Effect.sync(() => {
23
25
  let currentPortId = 0
24
26
 
25
- yield* Effect.addFinalizer(() => Effect.sync(() => self.close()))
26
-
27
- const queue = yield* Queue.unbounded<readonly [portId: number, message: I]>()
28
- const runFork = yield* FiberSet.makeRuntime<never>()
29
- const ports = new Map<number, MessagePort>()
27
+ const ports = new Map<number, readonly [MessagePort, Scope.CloseableScope]>()
30
28
  const send = (portId: number, message: O, transfer?: ReadonlyArray<unknown>) =>
31
29
  Effect.sync(() => {
32
- ports.get(portId)?.postMessage([1, message], {
30
+ ;(ports.get(portId)?.[0] ?? self).postMessage([1, message], {
33
31
  transfer: transfer as any
34
32
  })
35
33
  })
36
34
 
37
- function handlePort(port: MessagePort, sharedWorker: boolean) {
38
- const portId = currentPortId++
39
- ports.set(portId, port)
40
-
41
- Effect.async<never, WorkerError, never>((resume) => {
42
- function onMessage(event: MessageEvent) {
43
- const message = (event as MessageEvent).data as Runner.BackingRunner.Message<I>
44
- if (message[0] === 0) {
45
- queue.unsafeOffer([portId, message[1]])
46
- } else if (sharedWorker && ports.size > 1) {
47
- resume(Effect.interrupt)
48
- } else {
49
- Effect.runFork(shutdown)
50
- }
51
- }
52
- function onMessageError(error: ErrorEvent) {
53
- resume(new WorkerError({ reason: "decode", error: error.error ?? error.message }))
54
- }
55
- function onError(error: ErrorEvent) {
56
- resume(new WorkerError({ reason: "unknown", error: error.error ?? error.message }))
57
- }
58
- port.addEventListener("message", onMessage as any)
59
- port.addEventListener("messageerror", onMessageError as any)
60
- port.addEventListener("error", onError as any)
61
-
62
- // ready
63
- if ("start" in port) {
64
- port.start()
65
- }
66
- port.postMessage([0])
67
-
68
- return Effect.sync(() => {
69
- port.removeEventListener("message", onMessage as any)
70
- port.removeEventListener("messageerror", onMessageError as any)
71
- port.removeEventListener("error", onError as any)
72
- })
73
- }).pipe(
74
- Effect.tapErrorCause((cause) => Cause.isInterruptedOnly(cause) ? Effect.void : Effect.logDebug(cause)),
75
- Effect.retry(Schedule.forever),
76
- Effect.annotateLogs({
77
- package: "@effect/platform-browser",
78
- module: "WorkerRunner"
79
- }),
80
- Effect.ensuring(Effect.sync(() => {
81
- ports.delete(portId)
82
- })),
83
- Effect.interruptible,
84
- runFork
85
- )
86
- }
87
-
88
- if ("onconnect" in self) {
89
- self.onconnect = function(event: MessageEvent) {
90
- const port = (event as MessageEvent).ports[0]
91
- handlePort(port, true)
92
- }
93
- yield* Effect.addFinalizer(() =>
94
- Effect.sync(() => {
95
- ;(self as any).onconnect = globalHandleConnect
96
- })
35
+ const run = <A, E, R>(handler: (portId: number, message: I) => Effect.Effect<A, E, R>) =>
36
+ Effect.uninterruptibleMask((restore) =>
37
+ Scope.make().pipe(
38
+ Effect.bindTo("scope"),
39
+ Effect.bind("fiberSet", ({ scope }) => FiberSet.make<any, WorkerError | E>().pipe(Scope.extend(scope))),
40
+ Effect.bind("runFork", ({ fiberSet }) => FiberSet.runtime(fiberSet)<R>()),
41
+ Effect.tap(({ fiberSet, runFork, scope }) => {
42
+ function onMessage(portId: number) {
43
+ return function(event: MessageEvent) {
44
+ const message = (event as MessageEvent).data as Runner.BackingRunner.Message<I>
45
+ if (message[0] === 0) {
46
+ runFork(restore(handler(portId, message[1])))
47
+ } else {
48
+ const port = ports.get(portId)
49
+ if (port) {
50
+ Effect.runFork(Scope.close(port[1], Exit.void))
51
+ }
52
+ ports.delete(portId)
53
+ if (ports.size === 0) {
54
+ Deferred.unsafeDone(fiberSet.deferred, Exit.interrupt(FiberId.none))
55
+ }
56
+ }
57
+ }
58
+ }
59
+ function onMessageError(error: MessageEvent) {
60
+ Deferred.unsafeDone(
61
+ fiberSet.deferred,
62
+ new WorkerError({ reason: "decode", cause: error.data })
63
+ )
64
+ }
65
+ function onError(error: any) {
66
+ Deferred.unsafeDone(
67
+ fiberSet.deferred,
68
+ new WorkerError({ reason: "unknown", cause: error.data })
69
+ )
70
+ }
71
+ function handlePort(port: MessagePort) {
72
+ return Scope.fork(scope, ExecStrategy.sequential).pipe(
73
+ Effect.flatMap((scope) => {
74
+ const portId = currentPortId++
75
+ ports.set(portId, [port, scope])
76
+ const onMsg = onMessage(portId)
77
+ port.addEventListener("message", onMsg)
78
+ port.addEventListener("messageerror", onMessageError)
79
+ if ("start" in port) {
80
+ port.start()
81
+ }
82
+ port.postMessage([0])
83
+ return Scope.addFinalizer(
84
+ scope,
85
+ Effect.sync(() => {
86
+ port.removeEventListener("message", onMsg)
87
+ port.removeEventListener("messageerror", onError)
88
+ })
89
+ )
90
+ }),
91
+ runFork
92
+ )
93
+ }
94
+ self.addEventListener("error", onError)
95
+ if ("onconnect" in self) {
96
+ self.onconnect = function(event: MessageEvent) {
97
+ const port = (event as MessageEvent).ports[0]
98
+ handlePort(port)
99
+ }
100
+ for (const port of cachedPorts) {
101
+ handlePort(port)
102
+ }
103
+ cachedPorts.clear()
104
+ } else {
105
+ handlePort(self as any)
106
+ }
107
+ return Scope.addFinalizer(
108
+ scope,
109
+ Effect.sync(() => {
110
+ self.removeEventListener("error", onError)
111
+ if ("onconnect" in self) {
112
+ self.onconnect = globalHandleConnect
113
+ }
114
+ self.close()
115
+ })
116
+ )
117
+ }),
118
+ Effect.flatMap(({ fiberSet, scope }) =>
119
+ restore(FiberSet.join(fiberSet) as Effect.Effect<never, E | WorkerError>).pipe(
120
+ Effect.ensuring(Scope.close(scope, Exit.void))
121
+ )
122
+ )
123
+ )
97
124
  )
98
- for (const port of cachedPorts) {
99
- handlePort(port, true)
100
- }
101
- cachedPorts.clear()
102
- } else {
103
- handlePort(self as any, false)
104
- }
105
125
 
106
- return { queue, send }
126
+ return { run, send }
107
127
  })
108
128
  }
109
129
  })