@effect/platform-browser 4.0.0-beta.7 → 4.0.0-beta.71
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/BrowserCrypto.d.ts +81 -0
- package/dist/BrowserCrypto.d.ts.map +1 -0
- package/dist/BrowserCrypto.js +119 -0
- package/dist/BrowserCrypto.js.map +1 -0
- package/dist/BrowserHttpClient.d.ts +40 -16
- package/dist/BrowserHttpClient.d.ts.map +1 -1
- package/dist/BrowserHttpClient.js +74 -18
- package/dist/BrowserHttpClient.js.map +1 -1
- package/dist/BrowserKeyValueStore.d.ts +37 -14
- package/dist/BrowserKeyValueStore.d.ts.map +1 -1
- package/dist/BrowserKeyValueStore.js +185 -10
- package/dist/BrowserKeyValueStore.js.map +1 -1
- package/dist/BrowserPersistence.d.ts +40 -0
- package/dist/BrowserPersistence.d.ts.map +1 -0
- package/dist/BrowserPersistence.js +207 -0
- package/dist/BrowserPersistence.js.map +1 -0
- package/dist/BrowserRuntime.d.ts +79 -4
- package/dist/BrowserRuntime.d.ts.map +1 -1
- package/dist/BrowserRuntime.js +19 -1
- package/dist/BrowserRuntime.js.map +1 -1
- package/dist/BrowserSocket.d.ts +64 -6
- package/dist/BrowserSocket.d.ts.map +1 -1
- package/dist/BrowserSocket.js +64 -6
- package/dist/BrowserSocket.js.map +1 -1
- package/dist/BrowserStream.d.ts +48 -5
- package/dist/BrowserStream.d.ts.map +1 -1
- package/dist/BrowserStream.js +48 -5
- package/dist/BrowserStream.js.map +1 -1
- package/dist/BrowserWorker.d.ts +25 -4
- package/dist/BrowserWorker.d.ts.map +1 -1
- package/dist/BrowserWorker.js +62 -6
- package/dist/BrowserWorker.js.map +1 -1
- package/dist/BrowserWorkerRunner.d.ts +31 -6
- package/dist/BrowserWorkerRunner.d.ts.map +1 -1
- package/dist/BrowserWorkerRunner.js +65 -10
- package/dist/BrowserWorkerRunner.js.map +1 -1
- package/dist/Clipboard.d.ts +77 -13
- package/dist/Clipboard.d.ts.map +1 -1
- package/dist/Clipboard.js +57 -11
- package/dist/Clipboard.js.map +1 -1
- package/dist/Geolocation.d.ts +101 -24
- package/dist/Geolocation.d.ts.map +1 -1
- package/dist/Geolocation.js +69 -16
- package/dist/Geolocation.js.map +1 -1
- package/dist/IndexedDb.d.ts +89 -0
- package/dist/IndexedDb.d.ts.map +1 -0
- package/dist/IndexedDb.js +99 -0
- package/dist/IndexedDb.js.map +1 -0
- package/dist/IndexedDbDatabase.d.ts +169 -0
- package/dist/IndexedDbDatabase.d.ts.map +1 -0
- package/dist/IndexedDbDatabase.js +343 -0
- package/dist/IndexedDbDatabase.js.map +1 -0
- package/dist/IndexedDbQueryBuilder.d.ts +438 -0
- package/dist/IndexedDbQueryBuilder.d.ts.map +1 -0
- package/dist/IndexedDbQueryBuilder.js +935 -0
- package/dist/IndexedDbQueryBuilder.js.map +1 -0
- package/dist/IndexedDbTable.d.ts +191 -0
- package/dist/IndexedDbTable.d.ts.map +1 -0
- package/dist/IndexedDbTable.js +96 -0
- package/dist/IndexedDbTable.js.map +1 -0
- package/dist/IndexedDbVersion.d.ts +94 -0
- package/dist/IndexedDbVersion.d.ts.map +1 -0
- package/dist/IndexedDbVersion.js +39 -0
- package/dist/IndexedDbVersion.js.map +1 -0
- package/dist/Permissions.d.ts +76 -16
- package/dist/Permissions.d.ts.map +1 -1
- package/dist/Permissions.js +66 -11
- package/dist/Permissions.js.map +1 -1
- package/dist/index.d.ts +38 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -10
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/BrowserCrypto.ts +129 -0
- package/src/BrowserHttpClient.ts +84 -24
- package/src/BrowserKeyValueStore.ts +201 -12
- package/src/BrowserPersistence.ts +376 -0
- package/src/BrowserRuntime.ts +79 -4
- package/src/BrowserSocket.ts +64 -6
- package/src/BrowserStream.ts +48 -5
- package/src/BrowserWorker.ts +62 -6
- package/src/BrowserWorkerRunner.ts +65 -10
- package/src/Clipboard.ts +74 -13
- package/src/Geolocation.ts +97 -20
- package/src/IndexedDb.ts +127 -0
- package/src/IndexedDbDatabase.ts +664 -0
- package/src/IndexedDbQueryBuilder.ts +2048 -0
- package/src/IndexedDbTable.ts +285 -0
- package/src/IndexedDbVersion.ts +133 -0
- package/src/Permissions.ts +71 -14
- package/src/index.ts +45 -10
package/src/BrowserWorker.ts
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Parent-side browser platform for running Effect workers.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the `WorkerPlatform` used by browser code that spawns or
|
|
5
|
+
* connects to `Worker`, `SharedWorker`, and `MessagePort` endpoints through
|
|
6
|
+
* Effect's worker protocol. Pair it with `BrowserWorkerRunner` in the worker
|
|
7
|
+
* entrypoint when building worker-backed RPC clients, moving CPU-bound work off
|
|
8
|
+
* the main thread, isolating browser-only services, or adapting an existing
|
|
9
|
+
* `MessageChannel` in tests.
|
|
10
|
+
*
|
|
11
|
+
* **Mental model**
|
|
12
|
+
*
|
|
13
|
+
* Browser worker communication is normalized to a message port. Dedicated
|
|
14
|
+
* workers post messages on the worker object itself, shared workers post
|
|
15
|
+
* through `worker.port`, and raw `MessagePort` values can be supplied directly.
|
|
16
|
+
* `layerPlatform` provides only the browser platform, while `layer` combines
|
|
17
|
+
* the platform with a `Spawner` that creates a worker endpoint for each worker
|
|
18
|
+
* id.
|
|
19
|
+
*
|
|
20
|
+
* **Common tasks**
|
|
21
|
+
*
|
|
22
|
+
* Use `layer` when parent code should both provide browser worker messaging and
|
|
23
|
+
* define how workers are spawned. Use `layerPlatform` when a spawner is already
|
|
24
|
+
* provided elsewhere. Return a `MessagePort` from the spawner for tests or
|
|
25
|
+
* custom channels, a `Worker` for dedicated workers, or a `SharedWorker` when
|
|
26
|
+
* multiple browser contexts should share one worker runtime.
|
|
27
|
+
*
|
|
28
|
+
* **Gotchas**
|
|
29
|
+
*
|
|
30
|
+
* Messages use the browser structured-clone algorithm, so payloads and transfer
|
|
31
|
+
* lists must be accepted by the target runtime. Transferring an `ArrayBuffer` or
|
|
32
|
+
* `MessagePort` moves ownership away from the sender. Scope finalization sends
|
|
33
|
+
* the worker close signal over the port, but code that created a dedicated
|
|
34
|
+
* `Worker` remains responsible for broader lifecycle concerns such as
|
|
35
|
+
* terminating it.
|
|
36
|
+
*
|
|
37
|
+
* @since 4.0.0
|
|
3
38
|
*/
|
|
4
39
|
import * as Deferred from "effect/Deferred"
|
|
5
40
|
import * as Effect from "effect/Effect"
|
|
@@ -9,8 +44,27 @@ import * as Worker from "effect/unstable/workers/Worker"
|
|
|
9
44
|
import { WorkerError, WorkerReceiveError } from "effect/unstable/workers/WorkerError"
|
|
10
45
|
|
|
11
46
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
47
|
+
* Creates browser worker layers by combining the default `WorkerPlatform` with a spawner for `Worker`, `SharedWorker`, or `MessagePort` instances.
|
|
48
|
+
*
|
|
49
|
+
* **When to use**
|
|
50
|
+
*
|
|
51
|
+
* Use when browser parent or client code needs both the browser
|
|
52
|
+
* `WorkerPlatform` and a `Spawner` from one layer.
|
|
53
|
+
*
|
|
54
|
+
* **Details**
|
|
55
|
+
*
|
|
56
|
+
* The `spawn` callback receives the numeric worker id and may return a
|
|
57
|
+
* `Worker`, `SharedWorker`, or `MessagePort`.
|
|
58
|
+
*
|
|
59
|
+
* **Gotchas**
|
|
60
|
+
*
|
|
61
|
+
* Scope finalization sends the worker close protocol over the port. Dedicated
|
|
62
|
+
* workers created by `spawn` are not terminated by this layer.
|
|
63
|
+
*
|
|
64
|
+
* @see {@link layerPlatform} for providing only the browser worker platform
|
|
65
|
+
*
|
|
66
|
+
* @category layers
|
|
67
|
+
* @since 4.0.0
|
|
14
68
|
*/
|
|
15
69
|
export const layer = (
|
|
16
70
|
spawn: (id: number) => Worker | SharedWorker | MessagePort
|
|
@@ -21,8 +75,10 @@ export const layer = (
|
|
|
21
75
|
)
|
|
22
76
|
|
|
23
77
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
78
|
+
* Layer that provides the browser `WorkerPlatform` for `Worker`, `SharedWorker`, and `MessagePort` communication.
|
|
79
|
+
*
|
|
80
|
+
* @category layers
|
|
81
|
+
* @since 4.0.0
|
|
26
82
|
*/
|
|
27
83
|
export const layerPlatform: Layer.Layer<Worker.WorkerPlatform> = Layer.succeed(Worker.WorkerPlatform)(
|
|
28
84
|
Worker.makePlatform<globalThis.SharedWorker | globalThis.Worker | MessagePort>()({
|
|
@@ -50,7 +106,7 @@ export const layerPlatform: Layer.Layer<Worker.WorkerPlatform> = Layer.succeed(W
|
|
|
50
106
|
message: "An error event was emitter",
|
|
51
107
|
cause: event.error ?? event.message
|
|
52
108
|
})
|
|
53
|
-
})
|
|
109
|
+
})
|
|
54
110
|
)
|
|
55
111
|
}
|
|
56
112
|
port.addEventListener("message", onMessage as any)
|
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Runner-side browser platform for Effect worker handlers.
|
|
3
|
+
*
|
|
4
|
+
* This module is for code already executing inside a browser worker, or for
|
|
5
|
+
* tests and adapters that pass a `MessagePort` or `Window` endpoint directly.
|
|
6
|
+
* It provides the `WorkerRunnerPlatform` used by `WorkerRunner` and protocols
|
|
7
|
+
* such as `RpcServer.layerProtocolWorkerRunner` to receive parent or client
|
|
8
|
+
* requests, run Effect handlers, and post responses back through the browser
|
|
9
|
+
* messaging channel.
|
|
10
|
+
*
|
|
11
|
+
* **Mental model**
|
|
12
|
+
*
|
|
13
|
+
* Dedicated workers use the ambient `self` endpoint. Shared workers receive one
|
|
14
|
+
* `MessagePort` per `onconnect` event, and this module caches ports that arrive
|
|
15
|
+
* before the runner layer starts. `layer` reads from the global worker `self`;
|
|
16
|
+
* `make` and `layerMessagePort` are for explicit endpoints supplied by tests,
|
|
17
|
+
* custom channels, or adapter code.
|
|
18
|
+
*
|
|
19
|
+
* **Common tasks**
|
|
20
|
+
*
|
|
21
|
+
* Pair this module with `BrowserWorker` in the parent page when moving RPC
|
|
22
|
+
* handlers, CPU-bound computations, or browser-only services into a dedicated
|
|
23
|
+
* worker or shared worker. Use `layer` in normal worker entry points and
|
|
24
|
+
* `layerMessagePort` when a test or integration already owns the transport.
|
|
25
|
+
*
|
|
26
|
+
* **Gotchas**
|
|
27
|
+
*
|
|
28
|
+
* Payloads and transfer lists must satisfy the browser structured-clone
|
|
29
|
+
* algorithm. `messageerror` and `error` events fail the runner, and each
|
|
30
|
+
* connected `MessagePort` is closed when its scope finalizes.
|
|
31
|
+
*
|
|
32
|
+
* @since 4.0.0
|
|
3
33
|
*/
|
|
4
34
|
import * as Cause from "effect/Cause"
|
|
5
35
|
import * as Deferred from "effect/Deferred"
|
|
@@ -22,8 +52,10 @@ if (typeof self !== "undefined" && "onconnect" in self) {
|
|
|
22
52
|
}
|
|
23
53
|
|
|
24
54
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
55
|
+
* Creates a `WorkerRunnerPlatform` service that runs worker handlers over a `MessagePort` or `Window`.
|
|
56
|
+
*
|
|
57
|
+
* @category constructors
|
|
58
|
+
* @since 4.0.0
|
|
27
59
|
*/
|
|
28
60
|
export const make = (self: MessagePort | Window): WorkerRunner.WorkerRunnerPlatform["Service"] => ({
|
|
29
61
|
start: Effect.fnUntraced(function*<O = unknown, I = unknown>() {
|
|
@@ -44,7 +76,7 @@ export const make = (self: MessagePort | Window): WorkerRunner.WorkerRunnerPlatf
|
|
|
44
76
|
Effect.scopedWith(Effect.fnUntraced(function*(scope) {
|
|
45
77
|
const closeLatch = Deferred.makeUnsafe<void, WorkerError>()
|
|
46
78
|
const trackFiber = Fiber.runIn(scope)
|
|
47
|
-
const services = yield* Effect.
|
|
79
|
+
const services = yield* Effect.context<R>()
|
|
48
80
|
const runFork = Effect.runForkWith(services)
|
|
49
81
|
const onExit = (exit: Exit.Exit<any, E>) => {
|
|
50
82
|
if (exit._tag === "Failure" && !Cause.hasInterruptsOnly(exit.cause)) {
|
|
@@ -83,7 +115,7 @@ export const make = (self: MessagePort | Window): WorkerRunner.WorkerRunnerPlatf
|
|
|
83
115
|
message: "An messageerror event was emitted",
|
|
84
116
|
cause: error.data
|
|
85
117
|
})
|
|
86
|
-
})
|
|
118
|
+
})
|
|
87
119
|
)
|
|
88
120
|
}
|
|
89
121
|
function onError(error: any) {
|
|
@@ -94,7 +126,7 @@ export const make = (self: MessagePort | Window): WorkerRunner.WorkerRunnerPlatf
|
|
|
94
126
|
message: "An error event was emitted",
|
|
95
127
|
cause: error.data
|
|
96
128
|
})
|
|
97
|
-
})
|
|
129
|
+
})
|
|
98
130
|
)
|
|
99
131
|
}
|
|
100
132
|
function handlePort(port: MessagePort) {
|
|
@@ -151,16 +183,39 @@ export const make = (self: MessagePort | Window): WorkerRunner.WorkerRunnerPlatf
|
|
|
151
183
|
})
|
|
152
184
|
|
|
153
185
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
186
|
+
* Layer that provides a browser `WorkerRunnerPlatform` using the global `self` worker context.
|
|
187
|
+
*
|
|
188
|
+
* **When to use**
|
|
189
|
+
*
|
|
190
|
+
* Use when a browser worker entry point uses the ambient `self` object as the
|
|
191
|
+
* worker transport.
|
|
192
|
+
*
|
|
193
|
+
* **Details**
|
|
194
|
+
*
|
|
195
|
+
* Delegates to `make(self)` and provides the runner-side platform used by
|
|
196
|
+
* protocols such as `RpcServer.layerProtocolWorkerRunner`.
|
|
197
|
+
*
|
|
198
|
+
* **Gotchas**
|
|
199
|
+
*
|
|
200
|
+
* This layer depends on the browser worker global `self`. Use
|
|
201
|
+
* `layerMessagePort` when the transport is an explicit `MessagePort` or
|
|
202
|
+
* `Window`.
|
|
203
|
+
*
|
|
204
|
+
* @see {@link make} for constructing a runner platform from an explicit endpoint
|
|
205
|
+
* @see {@link layerMessagePort} for providing a platform from an explicit endpoint
|
|
206
|
+
*
|
|
207
|
+
* @category layers
|
|
208
|
+
* @since 4.0.0
|
|
156
209
|
*/
|
|
157
210
|
export const layer: Layer.Layer<WorkerRunner.WorkerRunnerPlatform> = Layer.sync(WorkerRunner.WorkerRunnerPlatform)(() =>
|
|
158
211
|
make(self)
|
|
159
212
|
)
|
|
160
213
|
|
|
161
214
|
/**
|
|
162
|
-
*
|
|
163
|
-
*
|
|
215
|
+
* Layer that provides a `WorkerRunnerPlatform` using the supplied `MessagePort` or `Window`.
|
|
216
|
+
*
|
|
217
|
+
* @category layers
|
|
218
|
+
* @since 4.0.0
|
|
164
219
|
*/
|
|
165
220
|
export const layerMessagePort = (port: MessagePort | Window): Layer.Layer<WorkerRunner.WorkerRunnerPlatform> =>
|
|
166
221
|
Layer.succeed(WorkerRunner.WorkerRunnerPlatform)(make(port))
|
package/src/Clipboard.ts
CHANGED
|
@@ -1,17 +1,64 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Browser clipboard integration for Effect programs.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a `Clipboard` service backed by `navigator.clipboard`.
|
|
5
|
+
* It keeps copy, paste, clear, and rich clipboard operations inside the Effect
|
|
6
|
+
* environment so browser UI code can require clipboard capability without
|
|
7
|
+
* calling the global API directly. Text helpers cover portable copy and paste
|
|
8
|
+
* flows, while `read`, `write`, and `writeBlob` expose `ClipboardItem` payloads
|
|
9
|
+
* for browsers that support richer MIME types.
|
|
10
|
+
*
|
|
11
|
+
* **Mental model**
|
|
12
|
+
*
|
|
13
|
+
* `Clipboard` is a capability service. Application code depends on the service
|
|
14
|
+
* tag, {@link layer} supplies the live browser implementation, and {@link make}
|
|
15
|
+
* builds custom implementations for tests, unsupported browsers, or constrained
|
|
16
|
+
* capabilities. Browser failures are converted to {@link ClipboardError}.
|
|
17
|
+
*
|
|
18
|
+
* **Common tasks**
|
|
19
|
+
*
|
|
20
|
+
* - Provide {@link layer} near the browser application edge.
|
|
21
|
+
* - Use `writeString` for copy buttons and generated text.
|
|
22
|
+
* - Use `readString` for paste or import workflows.
|
|
23
|
+
* - Use `write` or `writeBlob` for rich clipboard payloads when
|
|
24
|
+
* `ClipboardItem` is available.
|
|
25
|
+
* - Use `clear` to replace the clipboard with an empty string.
|
|
26
|
+
*
|
|
27
|
+
* **Gotchas**
|
|
28
|
+
*
|
|
29
|
+
* Clipboard access requires a secure context in modern browsers and may also
|
|
30
|
+
* require user activation, permissions, and a focused document. Support differs
|
|
31
|
+
* between reads, writes, text, and custom MIME payloads, so feature detection or
|
|
32
|
+
* graceful fallback is often needed around `ClipboardItem` usage.
|
|
33
|
+
*
|
|
34
|
+
* @since 4.0.0
|
|
3
35
|
*/
|
|
36
|
+
import * as Context from "effect/Context"
|
|
4
37
|
import * as Data from "effect/Data"
|
|
5
38
|
import * as Effect from "effect/Effect"
|
|
6
39
|
import * as Layer from "effect/Layer"
|
|
7
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
8
40
|
|
|
9
41
|
const TypeId = "~@effect/platform-browser/Clipboard"
|
|
10
42
|
const ErrorTypeId = "~@effect/platform-browser/Clipboard/ClipboardError"
|
|
11
43
|
|
|
12
44
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
45
|
+
* Service interface for reading from, writing to, and clearing the browser clipboard.
|
|
46
|
+
*
|
|
47
|
+
* **Details**
|
|
48
|
+
*
|
|
49
|
+
* `read` and `write` work with `ClipboardItem` arrays. `readString` and
|
|
50
|
+
* `writeString` use text, `writeBlob` writes one `Blob`, and `clear` writes an
|
|
51
|
+
* empty string.
|
|
52
|
+
*
|
|
53
|
+
* **Gotchas**
|
|
54
|
+
*
|
|
55
|
+
* Clipboard access generally requires a secure context and may require user
|
|
56
|
+
* activation, permissions, or a focused document. `ClipboardItem` and non-text
|
|
57
|
+
* MIME type support varies by browser. Failed browser operations are surfaced
|
|
58
|
+
* as `ClipboardError`.
|
|
59
|
+
*
|
|
60
|
+
* @category models
|
|
61
|
+
* @since 4.0.0
|
|
15
62
|
*/
|
|
16
63
|
export interface Clipboard {
|
|
17
64
|
readonly [TypeId]: typeof TypeId
|
|
@@ -24,8 +71,10 @@ export interface Clipboard {
|
|
|
24
71
|
}
|
|
25
72
|
|
|
26
73
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
74
|
+
* Tagged error raised when a browser clipboard operation fails.
|
|
75
|
+
*
|
|
76
|
+
* @category errors
|
|
77
|
+
* @since 4.0.0
|
|
29
78
|
*/
|
|
30
79
|
export class ClipboardError extends Data.TaggedError("ClipboardError")<{
|
|
31
80
|
readonly message: string
|
|
@@ -35,14 +84,26 @@ export class ClipboardError extends Data.TaggedError("ClipboardError")<{
|
|
|
35
84
|
}
|
|
36
85
|
|
|
37
86
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
87
|
+
* Service tag for the browser `Clipboard` service.
|
|
88
|
+
*
|
|
89
|
+
* **When to use**
|
|
90
|
+
*
|
|
91
|
+
* Use when an Effect needs to require or provide clipboard capabilities through
|
|
92
|
+
* the context.
|
|
93
|
+
*
|
|
94
|
+
* @see {@link make} for building a custom clipboard service
|
|
95
|
+
* @see {@link layer} for providing the browser-backed clipboard service
|
|
96
|
+
*
|
|
97
|
+
* @category services
|
|
98
|
+
* @since 4.0.0
|
|
40
99
|
*/
|
|
41
|
-
export const Clipboard:
|
|
100
|
+
export const Clipboard: Context.Service<Clipboard, Clipboard> = Context.Service<Clipboard>(TypeId)
|
|
42
101
|
|
|
43
102
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
103
|
+
* Builds a `Clipboard` service from primitive read and write operations, deriving `clear` and `writeBlob` helpers.
|
|
104
|
+
*
|
|
105
|
+
* @category constructors
|
|
106
|
+
* @since 4.0.0
|
|
46
107
|
*/
|
|
47
108
|
export const make = (
|
|
48
109
|
impl: Omit<Clipboard, "clear" | "writeBlob" | typeof TypeId>
|
|
@@ -57,8 +118,8 @@ export const make = (
|
|
|
57
118
|
/**
|
|
58
119
|
* A layer that directly interfaces with the navigator.clipboard api
|
|
59
120
|
*
|
|
60
|
-
* @
|
|
61
|
-
* @
|
|
121
|
+
* @category layers
|
|
122
|
+
* @since 4.0.0
|
|
62
123
|
*/
|
|
63
124
|
export const layer: Layer.Layer<Clipboard> = Layer.succeed(
|
|
64
125
|
Clipboard,
|
package/src/Geolocation.ts
CHANGED
|
@@ -1,20 +1,74 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Browser geolocation integration for Effect programs.
|
|
3
|
+
*
|
|
4
|
+
* This module exposes a `Geolocation` service backed by
|
|
5
|
+
* `navigator.geolocation`. Use it for browser features that need a single
|
|
6
|
+
* position fix, such as nearby search or delivery estimates, or a stream of
|
|
7
|
+
* position updates for navigation, tracking, and location-aware interfaces.
|
|
8
|
+
* Browser failures are represented as typed `GeolocationError` values instead
|
|
9
|
+
* of raw callback errors.
|
|
10
|
+
*
|
|
11
|
+
* **Mental model**
|
|
12
|
+
*
|
|
13
|
+
* - The service delegates to the browser Geolocation API and follows the
|
|
14
|
+
* browser's permission, privacy, origin, and secure-context rules.
|
|
15
|
+
* - `getCurrentPosition` reads one position fix from the service.
|
|
16
|
+
* - {@link watchPosition} turns browser position callbacks into a `Stream` and
|
|
17
|
+
* clears the underlying browser watch when the stream is finalized.
|
|
18
|
+
*
|
|
19
|
+
* **Common tasks**
|
|
20
|
+
*
|
|
21
|
+
* - Provide the live browser implementation with {@link layer}.
|
|
22
|
+
* - Read a one-shot position from the `Geolocation` service.
|
|
23
|
+
* - Stream position updates with {@link watchPosition}.
|
|
24
|
+
* - Handle denied permissions, timeouts, and unavailable position data with
|
|
25
|
+
* {@link GeolocationError}.
|
|
26
|
+
*
|
|
27
|
+
* **Gotchas**
|
|
28
|
+
*
|
|
29
|
+
* - Browsers may prompt the user, reject access outside secure contexts, block
|
|
30
|
+
* access by permissions policy, or report that position data is unavailable.
|
|
31
|
+
* - {@link watchPosition} uses a sliding buffer; increase `bufferSize` if slow
|
|
32
|
+
* consumers must not skip older positions.
|
|
33
|
+
*
|
|
34
|
+
* @since 4.0.0
|
|
3
35
|
*/
|
|
4
36
|
import * as Cause from "effect/Cause"
|
|
37
|
+
import * as Context from "effect/Context"
|
|
5
38
|
import * as Data from "effect/Data"
|
|
6
39
|
import * as Effect from "effect/Effect"
|
|
7
40
|
import * as Layer from "effect/Layer"
|
|
8
41
|
import * as Queue from "effect/Queue"
|
|
9
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
10
42
|
import * as Stream from "effect/Stream"
|
|
11
43
|
|
|
12
44
|
const TypeId = "~@effect/platform-browser/Geolocation"
|
|
13
45
|
const ErrorTypeId = "~@effect/platform-browser/Geolocation/GeolocationError"
|
|
14
46
|
|
|
15
47
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
48
|
+
* Service interface for browser geolocation, providing effects for the current position and streams of watched positions.
|
|
49
|
+
*
|
|
50
|
+
* **When to use**
|
|
51
|
+
*
|
|
52
|
+
* Use when browser code needs a typed Effect service for one-shot location
|
|
53
|
+
* reads or streamed location updates.
|
|
54
|
+
*
|
|
55
|
+
* **Details**
|
|
56
|
+
*
|
|
57
|
+
* `getCurrentPosition` returns one position effect. `watchPosition` returns a
|
|
58
|
+
* stream and accepts the browser `PositionOptions` plus an optional sliding
|
|
59
|
+
* `bufferSize`.
|
|
60
|
+
*
|
|
61
|
+
* **Gotchas**
|
|
62
|
+
*
|
|
63
|
+
* Browser permission prompts, denied permissions, timeouts, unavailable
|
|
64
|
+
* position data, secure-context restrictions, and policy restrictions are
|
|
65
|
+
* surfaced as `GeolocationError`.
|
|
66
|
+
*
|
|
67
|
+
* @see {@link GeolocationError} for represented browser geolocation failures
|
|
68
|
+
* @see {@link layer} for the browser-backed service implementation
|
|
69
|
+
*
|
|
70
|
+
* @category models
|
|
71
|
+
* @since 4.0.0
|
|
18
72
|
*/
|
|
19
73
|
export interface Geolocation {
|
|
20
74
|
readonly [TypeId]: typeof TypeId
|
|
@@ -31,14 +85,25 @@ export interface Geolocation {
|
|
|
31
85
|
}
|
|
32
86
|
|
|
33
87
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
88
|
+
* Service tag for the browser `Geolocation` service.
|
|
89
|
+
*
|
|
90
|
+
* **When to use**
|
|
91
|
+
*
|
|
92
|
+
* Use when an Effect needs to access or provide geolocation capabilities
|
|
93
|
+
* through the context.
|
|
94
|
+
*
|
|
95
|
+
* @see {@link layer} for providing the browser-backed geolocation service
|
|
96
|
+
*
|
|
97
|
+
* @category services
|
|
98
|
+
* @since 4.0.0
|
|
36
99
|
*/
|
|
37
|
-
export const Geolocation:
|
|
100
|
+
export const Geolocation: Context.Service<Geolocation, Geolocation> = Context.Service<Geolocation>(TypeId)
|
|
38
101
|
|
|
39
102
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
103
|
+
* Tagged error wrapping a browser geolocation failure reason.
|
|
104
|
+
*
|
|
105
|
+
* @category errors
|
|
106
|
+
* @since 4.0.0
|
|
42
107
|
*/
|
|
43
108
|
export class GeolocationError extends Data.TaggedError("GeolocationError")<{
|
|
44
109
|
readonly reason: GeolocationErrorReason
|
|
@@ -60,8 +125,10 @@ export class GeolocationError extends Data.TaggedError("GeolocationError")<{
|
|
|
60
125
|
}
|
|
61
126
|
|
|
62
127
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
128
|
+
* Error reason for the browser geolocation `POSITION_UNAVAILABLE` failure.
|
|
129
|
+
*
|
|
130
|
+
* @category errors
|
|
131
|
+
* @since 4.0.0
|
|
65
132
|
*/
|
|
66
133
|
export class PositionUnavailable extends Data.TaggedError("PositionUnavailable")<{
|
|
67
134
|
readonly cause: unknown
|
|
@@ -72,8 +139,10 @@ export class PositionUnavailable extends Data.TaggedError("PositionUnavailable")
|
|
|
72
139
|
}
|
|
73
140
|
|
|
74
141
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
142
|
+
* Error reason for the browser geolocation `PERMISSION_DENIED` failure.
|
|
143
|
+
*
|
|
144
|
+
* @category errors
|
|
145
|
+
* @since 4.0.0
|
|
77
146
|
*/
|
|
78
147
|
export class PermissionDenied extends Data.TaggedError("PermissionDenied")<{
|
|
79
148
|
readonly cause: unknown
|
|
@@ -84,8 +153,10 @@ export class PermissionDenied extends Data.TaggedError("PermissionDenied")<{
|
|
|
84
153
|
}
|
|
85
154
|
|
|
86
155
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
156
|
+
* Error reason for the browser geolocation `TIMEOUT` failure.
|
|
157
|
+
*
|
|
158
|
+
* @category errors
|
|
159
|
+
* @since 4.0.0
|
|
89
160
|
*/
|
|
90
161
|
export class Timeout extends Data.TaggedError("Timeout")<{
|
|
91
162
|
readonly cause: unknown
|
|
@@ -96,8 +167,10 @@ export class Timeout extends Data.TaggedError("Timeout")<{
|
|
|
96
167
|
}
|
|
97
168
|
|
|
98
169
|
/**
|
|
99
|
-
*
|
|
100
|
-
*
|
|
170
|
+
* Union of browser geolocation error reasons represented by the service.
|
|
171
|
+
*
|
|
172
|
+
* @category errors
|
|
173
|
+
* @since 4.0.0
|
|
101
174
|
*/
|
|
102
175
|
export type GeolocationErrorReason = PositionUnavailable | PermissionDenied | Timeout
|
|
103
176
|
|
|
@@ -141,8 +214,10 @@ const makeQueue = (
|
|
|
141
214
|
)
|
|
142
215
|
|
|
143
216
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
217
|
+
* Layer that provides `Geolocation` using `navigator.geolocation`, with watched positions buffered in a sliding queue.
|
|
218
|
+
*
|
|
219
|
+
* @category layers
|
|
220
|
+
* @since 4.0.0
|
|
146
221
|
*/
|
|
147
222
|
export const layer: Layer.Layer<Geolocation> = Layer.succeed(
|
|
148
223
|
Geolocation,
|
|
@@ -162,8 +237,10 @@ export const layer: Layer.Layer<Geolocation> = Layer.succeed(
|
|
|
162
237
|
)
|
|
163
238
|
|
|
164
239
|
/**
|
|
165
|
-
*
|
|
240
|
+
* Streams positions from the `Geolocation` service using `watchPosition`, with an optional sliding buffer size.
|
|
241
|
+
*
|
|
166
242
|
* @category Accessors
|
|
243
|
+
* @since 4.0.0
|
|
167
244
|
*/
|
|
168
245
|
export const watchPosition = (
|
|
169
246
|
options?:
|
package/src/IndexedDb.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser IndexedDB primitives and key schemas for Effect applications.
|
|
3
|
+
*
|
|
4
|
+
* This module is the low-level bridge used by the platform-browser IndexedDB
|
|
5
|
+
* integration. It provides an `IndexedDb` service around the browser
|
|
6
|
+
* `indexedDB` factory and `IDBKeyRange` constructor, a `layerWindow` layer for
|
|
7
|
+
* wiring those primitives from `window`, and schemas for the key shapes accepted
|
|
8
|
+
* by IndexedDB object stores and indexes.
|
|
9
|
+
*
|
|
10
|
+
* Use it when building typed local persistence for browser caches,
|
|
11
|
+
* offline-first state, background queues, drafts, or other client-side data
|
|
12
|
+
* that should be validated before it reaches IndexedDB. Higher-level database,
|
|
13
|
+
* version, table, and query modules build on these primitives for migrations
|
|
14
|
+
* and typed transactions.
|
|
15
|
+
*
|
|
16
|
+
* IndexedDB still follows the browser rules: schema changes happen only during
|
|
17
|
+
* version upgrades, upgrades may be blocked by other open tabs or connections,
|
|
18
|
+
* and reads or writes must run in transactions scoped to the object stores they
|
|
19
|
+
* touch. The `layerWindow` constructor should be used only where browser
|
|
20
|
+
* globals are available, and code that also runs during SSR or in restricted
|
|
21
|
+
* browser contexts should account for `indexedDB` or `IDBKeyRange` being
|
|
22
|
+
* missing.
|
|
23
|
+
*
|
|
24
|
+
* @since 4.0.0
|
|
25
|
+
*/
|
|
26
|
+
import * as Context from "effect/Context"
|
|
27
|
+
import * as Effect from "effect/Effect"
|
|
28
|
+
import * as Layer from "effect/Layer"
|
|
29
|
+
import * as Schema from "effect/Schema"
|
|
30
|
+
|
|
31
|
+
const TypeId = "~@effect/platform-browser/IndexedDb"
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Service interface that provides the browser `indexedDB` factory and `IDBKeyRange` constructor.
|
|
35
|
+
*
|
|
36
|
+
* @category models
|
|
37
|
+
* @since 4.0.0
|
|
38
|
+
*/
|
|
39
|
+
export interface IndexedDb {
|
|
40
|
+
readonly [TypeId]: typeof TypeId
|
|
41
|
+
readonly indexedDB: globalThis.IDBFactory
|
|
42
|
+
readonly IDBKeyRange: typeof globalThis.IDBKeyRange
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Service tag for browser IndexedDB primitives.
|
|
47
|
+
*
|
|
48
|
+
* @category tag
|
|
49
|
+
* @since 4.0.0
|
|
50
|
+
*/
|
|
51
|
+
export const IndexedDb: Context.Service<IndexedDb, IndexedDb> = Context.Service<IndexedDb, IndexedDb>(TypeId)
|
|
52
|
+
|
|
53
|
+
/** @internal */
|
|
54
|
+
const IDBFlatKey = Schema.Union([
|
|
55
|
+
Schema.String,
|
|
56
|
+
Schema.Number.check(Schema.makeFilter((input) => !Number.isNaN(input))),
|
|
57
|
+
Schema.DateValid,
|
|
58
|
+
Schema.declare(
|
|
59
|
+
(input): input is BufferSource =>
|
|
60
|
+
input instanceof ArrayBuffer ||
|
|
61
|
+
(ArrayBuffer.isView(input) && input.buffer instanceof ArrayBuffer)
|
|
62
|
+
)
|
|
63
|
+
])
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Schema for IndexedDB keys: strings, non-NaN numbers, valid dates, buffer sources, or arrays of those flat key values.
|
|
67
|
+
*
|
|
68
|
+
* @category schemas
|
|
69
|
+
* @since 4.0.0
|
|
70
|
+
*/
|
|
71
|
+
export const IDBValidKey = Schema.Union([IDBFlatKey, Schema.Array(IDBFlatKey)])
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Schema for auto-incremented IndexedDB keys, accepting integers from 1 through `2 ** 53`.
|
|
75
|
+
*
|
|
76
|
+
* **When to use**
|
|
77
|
+
*
|
|
78
|
+
* Use when defining numeric key-path fields for `IndexedDbTable` definitions
|
|
79
|
+
* that use IndexedDB auto-increment keys.
|
|
80
|
+
*
|
|
81
|
+
* **Details**
|
|
82
|
+
*
|
|
83
|
+
* The schema accepts integer values from `1` through `2 ** 53`, matching the
|
|
84
|
+
* range used for generated IndexedDB auto-increment keys.
|
|
85
|
+
*
|
|
86
|
+
* @see {@link IDBValidKey} for the broader IndexedDB key schema
|
|
87
|
+
*
|
|
88
|
+
* @category schemas
|
|
89
|
+
* @since 4.0.0
|
|
90
|
+
*/
|
|
91
|
+
export const AutoIncrement = Schema.Int.check(
|
|
92
|
+
Schema.isBetween({ minimum: 1, maximum: 2 ** 53 })
|
|
93
|
+
).annotate({
|
|
94
|
+
identifier: "AutoIncrement",
|
|
95
|
+
title: "autoIncrement",
|
|
96
|
+
description: "Defines a valid autoIncrement key path for the IndexedDb table"
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Creates an `IndexedDb` service from an `IDBFactory` and `IDBKeyRange` constructor.
|
|
101
|
+
*
|
|
102
|
+
* @category constructors
|
|
103
|
+
* @since 4.0.0
|
|
104
|
+
*/
|
|
105
|
+
export const make = (impl: Omit<IndexedDb, typeof TypeId>): IndexedDb => IndexedDb.of({ [TypeId]: TypeId, ...impl })
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Layer that provides `IndexedDb` from `window.indexedDB` and `window.IDBKeyRange`, failing with a config error when they are unavailable.
|
|
109
|
+
*
|
|
110
|
+
* @category constructors
|
|
111
|
+
* @since 4.0.0
|
|
112
|
+
*/
|
|
113
|
+
export const layerWindow: Layer.Layer<IndexedDb> = Layer.effect(
|
|
114
|
+
IndexedDb,
|
|
115
|
+
Effect.suspend(() => {
|
|
116
|
+
if (window.indexedDB && window.IDBKeyRange) {
|
|
117
|
+
return Effect.succeed(
|
|
118
|
+
make({
|
|
119
|
+
indexedDB: window.indexedDB,
|
|
120
|
+
IDBKeyRange: window.IDBKeyRange
|
|
121
|
+
})
|
|
122
|
+
)
|
|
123
|
+
} else {
|
|
124
|
+
return Effect.die(new Error("window.indexedDB is not available"))
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
)
|