@effect/platform-browser 0.37.27 → 0.38.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/dist/cjs/BrowserWorkerRunner.js +11 -1
- package/dist/cjs/BrowserWorkerRunner.js.map +1 -1
- package/dist/cjs/Geolocation.js +10 -6
- package/dist/cjs/Geolocation.js.map +1 -1
- package/dist/cjs/internal/httpClient.js +5 -5
- package/dist/cjs/internal/httpClient.js.map +1 -1
- package/dist/cjs/internal/worker.js +35 -50
- package/dist/cjs/internal/worker.js.map +1 -1
- package/dist/cjs/internal/workerRunner.js +91 -67
- package/dist/cjs/internal/workerRunner.js.map +1 -1
- package/dist/dts/BrowserWorkerRunner.d.ts +10 -0
- package/dist/dts/BrowserWorkerRunner.d.ts.map +1 -1
- package/dist/dts/Geolocation.d.ts +3 -4
- package/dist/dts/Geolocation.d.ts.map +1 -1
- package/dist/esm/BrowserWorkerRunner.js +10 -0
- package/dist/esm/BrowserWorkerRunner.js.map +1 -1
- package/dist/esm/Geolocation.js +11 -7
- package/dist/esm/Geolocation.js.map +1 -1
- package/dist/esm/internal/httpClient.js +5 -5
- package/dist/esm/internal/httpClient.js.map +1 -1
- package/dist/esm/internal/worker.js +35 -50
- package/dist/esm/internal/worker.js.map +1 -1
- package/dist/esm/internal/workerRunner.js +88 -66
- package/dist/esm/internal/workerRunner.js.map +1 -1
- package/package.json +3 -3
- package/src/BrowserWorkerRunner.ts +13 -0
- package/src/Geolocation.ts +13 -8
- package/src/internal/httpClient.ts +5 -5
- package/src/internal/worker.ts +36 -51
- package/src/internal/workerRunner.ts +117 -90
package/src/Geolocation.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { TypeIdError } from "@effect/platform/Error"
|
|
5
5
|
import * as Context from "effect/Context"
|
|
6
6
|
import * as Effect from "effect/Effect"
|
|
7
7
|
import * as Either from "effect/Either"
|
|
@@ -64,9 +64,14 @@ export type ErrorTypeId = typeof ErrorTypeId
|
|
|
64
64
|
* @since 1.0.0
|
|
65
65
|
* @category errors
|
|
66
66
|
*/
|
|
67
|
-
export class GeolocationError extends
|
|
67
|
+
export class GeolocationError extends TypeIdError(ErrorTypeId, "GeolocationError")<{
|
|
68
68
|
readonly reason: "PositionUnavailable" | "PermissionDenied" | "Timeout"
|
|
69
|
-
|
|
69
|
+
readonly cause: unknown
|
|
70
|
+
}> {
|
|
71
|
+
get message() {
|
|
72
|
+
return this.reason
|
|
73
|
+
}
|
|
74
|
+
}
|
|
70
75
|
|
|
71
76
|
const makeQueue = (
|
|
72
77
|
options:
|
|
@@ -81,11 +86,11 @@ const makeQueue = (
|
|
|
81
86
|
Effect.sync(() =>
|
|
82
87
|
navigator.geolocation.watchPosition(
|
|
83
88
|
(position) => queue.unsafeOffer(Either.right(position)),
|
|
84
|
-
(
|
|
85
|
-
if (
|
|
86
|
-
queue.unsafeOffer(Either.left(new GeolocationError({ reason: "PermissionDenied",
|
|
87
|
-
} else if (
|
|
88
|
-
queue.unsafeOffer(Either.left(new GeolocationError({ reason: "Timeout",
|
|
89
|
+
(cause) => {
|
|
90
|
+
if (cause.code === cause.PERMISSION_DENIED) {
|
|
91
|
+
queue.unsafeOffer(Either.left(new GeolocationError({ reason: "PermissionDenied", cause })))
|
|
92
|
+
} else if (cause.code === cause.TIMEOUT) {
|
|
93
|
+
queue.unsafeOffer(Either.left(new GeolocationError({ reason: "Timeout", cause })))
|
|
89
94
|
}
|
|
90
95
|
},
|
|
91
96
|
options
|
|
@@ -65,7 +65,7 @@ export const makeXMLHttpRequest = Client.makeDefault((request, url, signal, fibe
|
|
|
65
65
|
new Error.RequestError({
|
|
66
66
|
request,
|
|
67
67
|
reason: "Transport",
|
|
68
|
-
|
|
68
|
+
cause: xhr.statusText
|
|
69
69
|
})
|
|
70
70
|
))
|
|
71
71
|
}
|
|
@@ -98,12 +98,12 @@ const sendBody = (
|
|
|
98
98
|
return next
|
|
99
99
|
}),
|
|
100
100
|
{
|
|
101
|
-
onFailure: (
|
|
101
|
+
onFailure: (cause) =>
|
|
102
102
|
Effect.fail(
|
|
103
103
|
new Error.RequestError({
|
|
104
104
|
request,
|
|
105
105
|
reason: "Encode",
|
|
106
|
-
|
|
106
|
+
cause
|
|
107
107
|
})
|
|
108
108
|
),
|
|
109
109
|
onSuccess: (body) => Effect.sync(() => xhr.send(body))
|
|
@@ -286,12 +286,12 @@ class ClientResponseImpl extends IncomingMessageImpl<Error.ResponseError> implem
|
|
|
286
286
|
readonly request: ClientRequest.HttpClientRequest,
|
|
287
287
|
source: XMLHttpRequest
|
|
288
288
|
) {
|
|
289
|
-
super(source, (
|
|
289
|
+
super(source, (cause) =>
|
|
290
290
|
new Error.ResponseError({
|
|
291
291
|
request,
|
|
292
292
|
response: this,
|
|
293
293
|
reason: "Decode",
|
|
294
|
-
|
|
294
|
+
cause
|
|
295
295
|
}))
|
|
296
296
|
this[ClientResponse.TypeId] = ClientResponse.TypeId
|
|
297
297
|
}
|
package/src/internal/worker.ts
CHANGED
|
@@ -3,58 +3,43 @@ import { WorkerError } from "@effect/platform/WorkerError"
|
|
|
3
3
|
import * as Deferred from "effect/Deferred"
|
|
4
4
|
import * as Effect from "effect/Effect"
|
|
5
5
|
import * as Layer from "effect/Layer"
|
|
6
|
-
import * as
|
|
7
|
-
|
|
8
|
-
const platformWorkerImpl = Worker.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return Effect.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} else {
|
|
17
|
-
port = worker
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
yield* _(Effect.addFinalizer(() => Effect.sync(() => port.postMessage([1]))))
|
|
21
|
-
|
|
22
|
-
const queue = yield* _(Queue.unbounded<Worker.BackingWorker.Message<O>>())
|
|
23
|
-
const latch = yield* Deferred.make<void>()
|
|
24
|
-
|
|
25
|
-
const fiber = yield* _(
|
|
26
|
-
Effect.async<never, WorkerError, never>((resume) => {
|
|
27
|
-
function onMessage(event: MessageEvent) {
|
|
28
|
-
queue.unsafeOffer((event as MessageEvent).data)
|
|
29
|
-
}
|
|
30
|
-
function onError(event: ErrorEvent) {
|
|
31
|
-
resume(new WorkerError({ reason: "unknown", error: event.error ?? event.message }))
|
|
32
|
-
}
|
|
33
|
-
port.addEventListener("message", onMessage as any)
|
|
34
|
-
port.addEventListener("error", onError as any)
|
|
35
|
-
Deferred.unsafeDone(latch, Effect.void)
|
|
36
|
-
return Effect.sync(() => {
|
|
37
|
-
port.removeEventListener("message", onMessage as any)
|
|
38
|
-
port.removeEventListener("error", onError as any)
|
|
39
|
-
})
|
|
40
|
-
}),
|
|
41
|
-
Effect.interruptible,
|
|
42
|
-
Effect.forkScoped
|
|
43
|
-
)
|
|
44
|
-
yield* Deferred.await(latch)
|
|
45
|
-
|
|
46
|
-
if ("start" in port) {
|
|
47
|
-
port.start()
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const send = (message: I, transfers?: ReadonlyArray<unknown>) =>
|
|
51
|
-
Effect.try({
|
|
52
|
-
try: () => port.postMessage([0, message], transfers as any),
|
|
53
|
-
catch: (error) => new WorkerError({ reason: "send", error })
|
|
6
|
+
import * as Scope from "effect/Scope"
|
|
7
|
+
|
|
8
|
+
const platformWorkerImpl = Worker.makePlatform<globalThis.SharedWorker | globalThis.Worker | MessagePort>()({
|
|
9
|
+
setup({ scope, worker }) {
|
|
10
|
+
const port = "port" in worker ? worker.port : worker
|
|
11
|
+
return Effect.as(
|
|
12
|
+
Scope.addFinalizer(
|
|
13
|
+
scope,
|
|
14
|
+
Effect.sync(() => {
|
|
15
|
+
port.postMessage([1])
|
|
54
16
|
})
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
17
|
+
),
|
|
18
|
+
port
|
|
19
|
+
)
|
|
20
|
+
},
|
|
21
|
+
listen({ deferred, emit, port, scope }) {
|
|
22
|
+
function onMessage(event: MessageEvent) {
|
|
23
|
+
emit(event.data)
|
|
24
|
+
}
|
|
25
|
+
function onError(event: ErrorEvent) {
|
|
26
|
+
Deferred.unsafeDone(
|
|
27
|
+
deferred,
|
|
28
|
+
new WorkerError({ reason: "unknown", cause: event.error ?? event.message })
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
port.addEventListener("message", onMessage as any)
|
|
32
|
+
port.addEventListener("error", onError as any)
|
|
33
|
+
if ("start" in port) {
|
|
34
|
+
port.start()
|
|
35
|
+
}
|
|
36
|
+
return Scope.addFinalizer(
|
|
37
|
+
scope,
|
|
38
|
+
Effect.sync(() => {
|
|
39
|
+
port.removeEventListener("message", onMessage as any)
|
|
40
|
+
port.removeEventListener("error", onError as any)
|
|
41
|
+
})
|
|
42
|
+
)
|
|
58
43
|
}
|
|
59
44
|
})
|
|
60
45
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { WorkerError } from "@effect/platform/WorkerError"
|
|
2
2
|
import * as Runner from "@effect/platform/WorkerRunner"
|
|
3
|
-
import * as
|
|
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
|
|
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) {
|
|
@@ -16,97 +18,122 @@ if (typeof self !== "undefined" && "onconnect" in self) {
|
|
|
16
18
|
self.onconnect = globalHandleConnect
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
/** @internal */
|
|
22
|
+
export const make = (self: MessagePort | Window) =>
|
|
23
|
+
Runner.PlatformRunner.of({
|
|
24
|
+
[Runner.PlatformRunnerTypeId]: Runner.PlatformRunnerTypeId,
|
|
25
|
+
start<I, O>() {
|
|
26
|
+
return Effect.sync(() => {
|
|
27
|
+
let currentPortId = 0
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
transfer: transfer as any
|
|
29
|
+
const ports = new Map<number, readonly [MessagePort, Scope.CloseableScope]>()
|
|
30
|
+
const send = (portId: number, message: O, transfer?: ReadonlyArray<unknown>) =>
|
|
31
|
+
Effect.sync(() => {
|
|
32
|
+
;(ports.get(portId)?.[0] ?? self).postMessage([1, message], {
|
|
33
|
+
transfer: transfer as any
|
|
34
|
+
})
|
|
34
35
|
})
|
|
35
|
-
})
|
|
36
|
-
|
|
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
36
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
37
|
+
const run = <A, E, R>(handler: (portId: number, message: I) => Effect.Effect<A, E, R>) =>
|
|
38
|
+
Effect.uninterruptibleMask((restore) =>
|
|
39
|
+
Scope.make().pipe(
|
|
40
|
+
Effect.bindTo("scope"),
|
|
41
|
+
Effect.bind("fiberSet", ({ scope }) => FiberSet.make<any, WorkerError | E>().pipe(Scope.extend(scope))),
|
|
42
|
+
Effect.bind("runFork", ({ fiberSet }) => FiberSet.runtime(fiberSet)<R>()),
|
|
43
|
+
Effect.tap(({ fiberSet, runFork, scope }) => {
|
|
44
|
+
function onMessage(portId: number) {
|
|
45
|
+
return function(event: MessageEvent) {
|
|
46
|
+
const message = (event as MessageEvent).data as Runner.BackingRunner.Message<I>
|
|
47
|
+
if (message[0] === 0) {
|
|
48
|
+
runFork(restore(handler(portId, message[1])))
|
|
49
|
+
} else {
|
|
50
|
+
const port = ports.get(portId)
|
|
51
|
+
if (port) {
|
|
52
|
+
Effect.runFork(Scope.close(port[1], Exit.void))
|
|
53
|
+
}
|
|
54
|
+
ports.delete(portId)
|
|
55
|
+
if (ports.size === 0) {
|
|
56
|
+
Deferred.unsafeDone(fiberSet.deferred, Exit.interrupt(FiberId.none))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function onMessageError(error: MessageEvent) {
|
|
62
|
+
Deferred.unsafeDone(
|
|
63
|
+
fiberSet.deferred,
|
|
64
|
+
new WorkerError({ reason: "decode", cause: error.data })
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
function onError(error: any) {
|
|
68
|
+
Deferred.unsafeDone(
|
|
69
|
+
fiberSet.deferred,
|
|
70
|
+
new WorkerError({ reason: "unknown", cause: error.data })
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
function handlePort(port: MessagePort) {
|
|
74
|
+
return Scope.fork(scope, ExecStrategy.sequential).pipe(
|
|
75
|
+
Effect.flatMap((scope) => {
|
|
76
|
+
const portId = currentPortId++
|
|
77
|
+
ports.set(portId, [port, scope])
|
|
78
|
+
const onMsg = onMessage(portId)
|
|
79
|
+
port.addEventListener("message", onMsg)
|
|
80
|
+
port.addEventListener("messageerror", onMessageError)
|
|
81
|
+
if ("start" in port) {
|
|
82
|
+
port.start()
|
|
83
|
+
}
|
|
84
|
+
port.postMessage([0])
|
|
85
|
+
return Scope.addFinalizer(
|
|
86
|
+
scope,
|
|
87
|
+
Effect.sync(() => {
|
|
88
|
+
port.removeEventListener("message", onMsg)
|
|
89
|
+
port.removeEventListener("messageerror", onError)
|
|
90
|
+
})
|
|
91
|
+
)
|
|
92
|
+
}),
|
|
93
|
+
runFork
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
self.addEventListener("error", onError)
|
|
97
|
+
let prevOnConnect: unknown | undefined
|
|
98
|
+
if ("onconnect" in self) {
|
|
99
|
+
prevOnConnect = self.onconnect
|
|
100
|
+
self.onconnect = function(event: MessageEvent) {
|
|
101
|
+
const port = (event as MessageEvent).ports[0]
|
|
102
|
+
handlePort(port)
|
|
103
|
+
}
|
|
104
|
+
for (const port of cachedPorts) {
|
|
105
|
+
handlePort(port)
|
|
106
|
+
}
|
|
107
|
+
cachedPorts.clear()
|
|
108
|
+
} else {
|
|
109
|
+
handlePort(self as any)
|
|
110
|
+
}
|
|
111
|
+
return Scope.addFinalizer(
|
|
112
|
+
scope,
|
|
113
|
+
Effect.sync(() => {
|
|
114
|
+
self.removeEventListener("error", onError)
|
|
115
|
+
if ("onconnect" in self) {
|
|
116
|
+
self.onconnect = prevOnConnect
|
|
117
|
+
}
|
|
118
|
+
self.close()
|
|
119
|
+
})
|
|
120
|
+
)
|
|
121
|
+
}),
|
|
122
|
+
Effect.flatMap(({ fiberSet, scope }) =>
|
|
123
|
+
restore(FiberSet.join(fiberSet) as Effect.Effect<never, E | WorkerError>).pipe(
|
|
124
|
+
Effect.ensuring(Scope.close(scope, Exit.void))
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
)
|
|
67
129
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
}
|
|
130
|
+
return { run, send }
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
})
|
|
87
134
|
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
})
|
|
97
|
-
)
|
|
98
|
-
for (const port of cachedPorts) {
|
|
99
|
-
handlePort(port, true)
|
|
100
|
-
}
|
|
101
|
-
cachedPorts.clear()
|
|
102
|
-
} else {
|
|
103
|
-
handlePort(self as any, false)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return { queue, send }
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
})
|
|
135
|
+
/** @internal */
|
|
136
|
+
export const layerMessagePort = (port: MessagePort | Window) => Layer.succeed(Runner.PlatformRunner, make(port))
|
|
110
137
|
|
|
111
138
|
/** @internal */
|
|
112
|
-
export const layer = Layer.succeed(Runner.PlatformRunner,
|
|
139
|
+
export const layer = Layer.succeed(Runner.PlatformRunner, make(self))
|