@use-everywhere/core 0.5.0 → 0.7.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.
- package/dist/index.cjs +379 -117
- package/dist/index.d.cts +112 -43
- package/dist/index.d.ts +112 -43
- package/dist/index.js +373 -112
- package/dist/testing.cjs +85 -0
- package/dist/testing.d.cts +31 -0
- package/dist/testing.d.ts +31 -0
- package/dist/testing.js +57 -0
- package/dist/transport.types-CV1WZOhy.d.cts +20 -0
- package/dist/transport.types-CV1WZOhy.d.ts +20 -0
- package/package.json +32 -14
package/dist/index.d.cts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Minimal message bus. Implementations: BroadcastChannelTransport (same-origin),
|
|
3
|
-
* NoopTransport (SSR / local-only), MemoryTransport (tests). A transport never
|
|
4
|
-
* echoes a client's own posts back to it.
|
|
5
|
-
*/
|
|
6
|
-
interface Transport {
|
|
7
|
-
post(data: unknown): void;
|
|
8
|
-
subscribe(listener: (data: unknown) => void): () => void;
|
|
9
|
-
close(): void;
|
|
10
|
-
}
|
|
1
|
+
import { T as Transport, a as TransportKind } from './transport.types-CV1WZOhy.cjs';
|
|
11
2
|
|
|
12
3
|
type MessageMap = Record<string, unknown>;
|
|
13
4
|
type PeerKind = 'tab' | 'worker' | (string & {});
|
|
@@ -166,8 +157,15 @@ interface BusOptions extends CommonOptions {
|
|
|
166
157
|
}
|
|
167
158
|
|
|
168
159
|
interface PresenceOptions extends BusOptions {
|
|
169
|
-
/**
|
|
160
|
+
/** How much silence makes a peer suspect. Default 5000ms. It is then probed, not dropped. */
|
|
170
161
|
pruneAfterMs?: number;
|
|
162
|
+
/**
|
|
163
|
+
* How long a probed peer has to answer before it is dropped. Default 1000ms.
|
|
164
|
+
*
|
|
165
|
+
* This is a round trip on a same-origin channel, not a heartbeat interval, so
|
|
166
|
+
* it can be short: a peer that is merely throttled still answers at once.
|
|
167
|
+
*/
|
|
168
|
+
probeGraceMs?: number;
|
|
171
169
|
}
|
|
172
170
|
interface Presence {
|
|
173
171
|
readonly clientId: string;
|
|
@@ -180,7 +178,19 @@ interface Presence {
|
|
|
180
178
|
/**
|
|
181
179
|
* Tracks the other tabs/windows/workers on this bus. Any message from a peer
|
|
182
180
|
* counts as a liveness signal (state patches, events, and presence pings all
|
|
183
|
-
* piggyback); explicit 'bye'
|
|
181
|
+
* piggyback); an explicit 'bye' removes them at once.
|
|
182
|
+
*
|
|
183
|
+
* Silence, though, is not proof of death — and treating it that way is what
|
|
184
|
+
* made the roster flap. Browsers clamp a hidden tab's timers to roughly one
|
|
185
|
+
* tick a minute, so a perfectly healthy backgrounded peer stops heartbeating,
|
|
186
|
+
* gets pruned, pings once, is re-added, and disappears again: a peer count
|
|
187
|
+
* oscillating once a minute for no reason. Waking a laptop does it to every
|
|
188
|
+
* peer at once.
|
|
189
|
+
*
|
|
190
|
+
* What saves it is that *message handlers are not throttled* — only timers are.
|
|
191
|
+
* A hidden tab still answers a hello the instant it arrives. So a peer that
|
|
192
|
+
* goes quiet is probed rather than dropped, and only silence that survives the
|
|
193
|
+
* probe counts as gone.
|
|
184
194
|
*/
|
|
185
195
|
declare function createPresence(name: string, options?: PresenceOptions): Presence;
|
|
186
196
|
|
|
@@ -189,13 +199,35 @@ declare function createPresence(name: string, options?: PresenceOptions): Presen
|
|
|
189
199
|
* the leader's re-announce interval, which is a different thing from the bus's
|
|
190
200
|
* presence ping. See the note in leader.ts about forwarding to getBus.
|
|
191
201
|
*/
|
|
202
|
+
/**
|
|
203
|
+
* How the seat is arbitrated.
|
|
204
|
+
*
|
|
205
|
+
* - `'web-locks'` — the browser's Web Locks API owns the queue. The lock is
|
|
206
|
+
* released by the browser itself when a tab dies, and holding it does not
|
|
207
|
+
* depend on a timer, so a backgrounded tab cannot be deposed for being
|
|
208
|
+
* throttled. No heartbeat traffic at all.
|
|
209
|
+
* - `'heartbeat'` — lease-and-claim over the bus. Works anywhere, including
|
|
210
|
+
* plain-http origins where `navigator.locks` does not exist.
|
|
211
|
+
* - `'auto'` (default) — Web Locks when available, heartbeat otherwise.
|
|
212
|
+
*/
|
|
213
|
+
type LeaderStrategy = 'auto' | 'web-locks' | 'heartbeat';
|
|
192
214
|
interface LeaderOptions extends CommonOptions {
|
|
193
|
-
/** How often the leader re-announces itself, in ms. Default 1000. */
|
|
215
|
+
/** How often the leader re-announces itself, in ms. Default 1000. Heartbeat strategy only. */
|
|
194
216
|
heartbeatMs?: number;
|
|
195
|
-
/** How long a follower tolerates silence before calling the seat empty, in ms. Default 3000. */
|
|
217
|
+
/** How long a follower tolerates silence before calling the seat empty, in ms. Default 3000. Heartbeat strategy only. */
|
|
196
218
|
leaseMs?: number;
|
|
197
219
|
/** May this client hold the leadership? Default true. */
|
|
198
220
|
eligible?: boolean;
|
|
221
|
+
/** How to arbitrate the seat. Default 'auto'. */
|
|
222
|
+
strategy?: LeaderStrategy;
|
|
223
|
+
/** @internal Test seam for the Web Locks manager. Defaults to navigator.locks. */
|
|
224
|
+
locks?: LockManagerLike;
|
|
225
|
+
}
|
|
226
|
+
/** The slice of the Web Locks API this library uses. */
|
|
227
|
+
interface LockManagerLike {
|
|
228
|
+
request(name: string, options: {
|
|
229
|
+
signal?: AbortSignal;
|
|
230
|
+
}, callback: () => Promise<void>): Promise<void>;
|
|
199
231
|
}
|
|
200
232
|
interface LeaderSnapshot {
|
|
201
233
|
/** The current leader's clientId, or null while the seat is empty. */
|
|
@@ -204,9 +236,17 @@ interface LeaderSnapshot {
|
|
|
204
236
|
}
|
|
205
237
|
interface Leader {
|
|
206
238
|
readonly clientId: string;
|
|
239
|
+
/** Which mechanism arbitrates this seat — useful in devtools and bug reports. */
|
|
240
|
+
readonly strategy: Exclude<LeaderStrategy, 'auto'>;
|
|
207
241
|
/** Frozen; a new object only when the leader actually changes. */
|
|
208
242
|
getSnapshot(): LeaderSnapshot;
|
|
209
243
|
subscribe(fn: () => void): () => void;
|
|
244
|
+
/**
|
|
245
|
+
* Resolves the moment this client holds the seat, or immediately if it
|
|
246
|
+
* already does. Rejects if the leader is closed while still waiting — so an
|
|
247
|
+
* `await` in a torn-down tab does not hang forever.
|
|
248
|
+
*/
|
|
249
|
+
waitForLeadership(): Promise<void>;
|
|
210
250
|
/** Give up the seat now. Peers take over immediately rather than waiting for the lease. */
|
|
211
251
|
resign(): void;
|
|
212
252
|
/** Turn candidacy on or off. Eligibility is a property of the tab, not a component. */
|
|
@@ -390,19 +430,23 @@ declare function observeBus(name: string, fn: BusObserver): () => void;
|
|
|
390
430
|
/** Log every wire on a bus to the console. Returns a function to stop. */
|
|
391
431
|
declare function enableDebug(options?: DebugOptions): () => void;
|
|
392
432
|
|
|
393
|
-
/**
|
|
394
|
-
* Get the shared bus for `name`, creating it on first use. Callers must call
|
|
395
|
-
* bus.release() exactly once when done. When a custom transport factory is
|
|
396
|
-
* given (tests), every call creates an isolated bus — one call = one simulated client.
|
|
397
|
-
*/
|
|
398
433
|
/**
|
|
399
434
|
* Names of the buses currently alive on this page. Buses built with a custom
|
|
400
|
-
* transport (tests) bypass the
|
|
435
|
+
* transport (tests) bypass the table, so they are not listed.
|
|
401
436
|
*/
|
|
402
437
|
declare function getBusNames(): string[];
|
|
438
|
+
/**
|
|
439
|
+
* What is actually carrying this bus's traffic, or null if it has no bus yet.
|
|
440
|
+
*
|
|
441
|
+
* Answers the question a developer asks when nothing is syncing and the code
|
|
442
|
+
* looks right: *is anything even connected?* `'none'` means no — writes are
|
|
443
|
+
* local and no peer will ever see them.
|
|
444
|
+
*/
|
|
445
|
+
declare function getTransportKind(name: string): TransportKind | null;
|
|
403
446
|
|
|
404
447
|
/** Same-origin transport over a real BroadcastChannel. */
|
|
405
448
|
declare class BroadcastChannelTransport implements Transport {
|
|
449
|
+
readonly kind: TransportKind;
|
|
406
450
|
private bc;
|
|
407
451
|
private listeners;
|
|
408
452
|
constructor(name: string);
|
|
@@ -416,40 +460,65 @@ declare class BroadcastChannelTransport implements Transport {
|
|
|
416
460
|
* Used for SSR and for state scoped to a single tab.
|
|
417
461
|
*/
|
|
418
462
|
declare class NoopTransport implements Transport {
|
|
463
|
+
readonly kind: TransportKind;
|
|
419
464
|
post(): void;
|
|
420
465
|
subscribe(): () => void;
|
|
421
466
|
close(): void;
|
|
422
467
|
}
|
|
423
468
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
469
|
+
/**
|
|
470
|
+
* Cross-tab delivery over the `storage` event, for browsers with no
|
|
471
|
+
* `BroadcastChannel`.
|
|
472
|
+
*
|
|
473
|
+
* The mechanism is a quirk turned to advantage: writing to `localStorage` fires
|
|
474
|
+
* a `storage` event in *every other* same-origin tab and never in the writer —
|
|
475
|
+
* exactly BroadcastChannel's no-self-echo semantics, for free.
|
|
476
|
+
*
|
|
477
|
+
* Two differences from the real thing, both deliberate and both documented:
|
|
478
|
+
*
|
|
479
|
+
* 1. **Fidelity is JSON, not structured clone.** `localStorage` holds strings.
|
|
480
|
+
* A `Date` arrives as an ISO string and a `Map` as `{}`. Values that cannot
|
|
481
|
+
* be represented at all — functions, symbols — are rejected rather than
|
|
482
|
+
* silently dropped, so a write still cannot leave this tab holding something
|
|
483
|
+
* its peers never received.
|
|
484
|
+
* 2. **The entry is removed immediately after writing.** Peers have already been
|
|
485
|
+
* notified by then (the event carries the value), and leaving application
|
|
486
|
+
* state sitting in `localStorage` would be both a quota cost and a privacy
|
|
487
|
+
* one. The removal fires a second event with a null `newValue`, which
|
|
488
|
+
* receivers ignore.
|
|
489
|
+
*/
|
|
490
|
+
declare class StorageTransport implements Transport {
|
|
491
|
+
readonly kind: TransportKind;
|
|
492
|
+
private key;
|
|
493
|
+
private storage;
|
|
431
494
|
private listeners;
|
|
432
|
-
private
|
|
433
|
-
|
|
495
|
+
private onStorage;
|
|
496
|
+
private seq;
|
|
497
|
+
constructor(name: string, storage?: Storage);
|
|
434
498
|
post(data: unknown): void;
|
|
435
499
|
subscribe(listener: (data: unknown) => void): () => void;
|
|
436
500
|
close(): void;
|
|
437
|
-
/** @internal */
|
|
438
|
-
deliver(data: unknown): void;
|
|
439
501
|
}
|
|
440
502
|
|
|
503
|
+
declare function isBroadcastChannelAvailable(): boolean;
|
|
441
504
|
/**
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
505
|
+
* Can we hear other tabs through the `storage` event?
|
|
506
|
+
*
|
|
507
|
+
* Reading `localStorage` is itself what throws when storage is blocked — a
|
|
508
|
+
* sandboxed iframe, third-party cookies off — so the check has to happen inside
|
|
509
|
+
* a try, not around a property test. Availability is also not writability:
|
|
510
|
+
* Safari's old private mode exposed the object and threw on every setItem.
|
|
445
511
|
*/
|
|
446
|
-
declare
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
512
|
+
declare function isStorageEventAvailable(): boolean;
|
|
513
|
+
/**
|
|
514
|
+
* Pick the best wire this browser can offer, and say so when it is not the
|
|
515
|
+
* good one.
|
|
516
|
+
*
|
|
517
|
+
* The chain matters more than it looks. Before it existed, a context without
|
|
518
|
+
* `BroadcastChannel` got a silent no-op: every hook kept working, every write
|
|
519
|
+
* appeared to succeed, and nothing was ever shared with anybody. That is the
|
|
520
|
+
* worst failure this library can have, because it looks exactly like success.
|
|
521
|
+
*/
|
|
522
|
+
declare function defaultTransport(name: string): Transport;
|
|
454
523
|
|
|
455
|
-
export { BroadcastChannelTransport, type BusEvent, type BusObserver, type BusWire, CID_PARAM, type Channel, type CommonOptions, type ConnectToOpenerOptions, DEFAULT_NAME, type DebugOptions, HandshakeTimeoutError, type Leader, type LeaderOptions, type LeaderSnapshot,
|
|
524
|
+
export { BroadcastChannelTransport, type BusEvent, type BusObserver, type BusWire, CID_PARAM, type Channel, type CommonOptions, type ConnectToOpenerOptions, DEFAULT_NAME, type DebugOptions, HandshakeTimeoutError, type Leader, type LeaderOptions, type LeaderSnapshot, type LeaderStrategy, type MessageEventLike, type MessageMap, type MessageMeta, NoopTransport, type OpenWindowOptions, type OpenedWindow, type OpenerConnection, type Peer, type PeerKind, type PersistAdapter, type PersistOptions, type Persisted, type Presence, type PresenceOptions, type SharedStore, type SharedStoreOptions, type StorageLike, StorageTransport, Transport, TransportKind, type Version, type WebStorageAdapterOptions, WindowClosedError, type WindowEventTarget, type WindowLike, connectToOpener, createChannel, createLeader, createPresence, createSharedStore, defaultTransport, enableDebug, getBusNames, getTransportKind, isBroadcastChannelAvailable, isStorageEventAvailable, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Minimal message bus. Implementations: BroadcastChannelTransport (same-origin),
|
|
3
|
-
* NoopTransport (SSR / local-only), MemoryTransport (tests). A transport never
|
|
4
|
-
* echoes a client's own posts back to it.
|
|
5
|
-
*/
|
|
6
|
-
interface Transport {
|
|
7
|
-
post(data: unknown): void;
|
|
8
|
-
subscribe(listener: (data: unknown) => void): () => void;
|
|
9
|
-
close(): void;
|
|
10
|
-
}
|
|
1
|
+
import { T as Transport, a as TransportKind } from './transport.types-CV1WZOhy.js';
|
|
11
2
|
|
|
12
3
|
type MessageMap = Record<string, unknown>;
|
|
13
4
|
type PeerKind = 'tab' | 'worker' | (string & {});
|
|
@@ -166,8 +157,15 @@ interface BusOptions extends CommonOptions {
|
|
|
166
157
|
}
|
|
167
158
|
|
|
168
159
|
interface PresenceOptions extends BusOptions {
|
|
169
|
-
/**
|
|
160
|
+
/** How much silence makes a peer suspect. Default 5000ms. It is then probed, not dropped. */
|
|
170
161
|
pruneAfterMs?: number;
|
|
162
|
+
/**
|
|
163
|
+
* How long a probed peer has to answer before it is dropped. Default 1000ms.
|
|
164
|
+
*
|
|
165
|
+
* This is a round trip on a same-origin channel, not a heartbeat interval, so
|
|
166
|
+
* it can be short: a peer that is merely throttled still answers at once.
|
|
167
|
+
*/
|
|
168
|
+
probeGraceMs?: number;
|
|
171
169
|
}
|
|
172
170
|
interface Presence {
|
|
173
171
|
readonly clientId: string;
|
|
@@ -180,7 +178,19 @@ interface Presence {
|
|
|
180
178
|
/**
|
|
181
179
|
* Tracks the other tabs/windows/workers on this bus. Any message from a peer
|
|
182
180
|
* counts as a liveness signal (state patches, events, and presence pings all
|
|
183
|
-
* piggyback); explicit 'bye'
|
|
181
|
+
* piggyback); an explicit 'bye' removes them at once.
|
|
182
|
+
*
|
|
183
|
+
* Silence, though, is not proof of death — and treating it that way is what
|
|
184
|
+
* made the roster flap. Browsers clamp a hidden tab's timers to roughly one
|
|
185
|
+
* tick a minute, so a perfectly healthy backgrounded peer stops heartbeating,
|
|
186
|
+
* gets pruned, pings once, is re-added, and disappears again: a peer count
|
|
187
|
+
* oscillating once a minute for no reason. Waking a laptop does it to every
|
|
188
|
+
* peer at once.
|
|
189
|
+
*
|
|
190
|
+
* What saves it is that *message handlers are not throttled* — only timers are.
|
|
191
|
+
* A hidden tab still answers a hello the instant it arrives. So a peer that
|
|
192
|
+
* goes quiet is probed rather than dropped, and only silence that survives the
|
|
193
|
+
* probe counts as gone.
|
|
184
194
|
*/
|
|
185
195
|
declare function createPresence(name: string, options?: PresenceOptions): Presence;
|
|
186
196
|
|
|
@@ -189,13 +199,35 @@ declare function createPresence(name: string, options?: PresenceOptions): Presen
|
|
|
189
199
|
* the leader's re-announce interval, which is a different thing from the bus's
|
|
190
200
|
* presence ping. See the note in leader.ts about forwarding to getBus.
|
|
191
201
|
*/
|
|
202
|
+
/**
|
|
203
|
+
* How the seat is arbitrated.
|
|
204
|
+
*
|
|
205
|
+
* - `'web-locks'` — the browser's Web Locks API owns the queue. The lock is
|
|
206
|
+
* released by the browser itself when a tab dies, and holding it does not
|
|
207
|
+
* depend on a timer, so a backgrounded tab cannot be deposed for being
|
|
208
|
+
* throttled. No heartbeat traffic at all.
|
|
209
|
+
* - `'heartbeat'` — lease-and-claim over the bus. Works anywhere, including
|
|
210
|
+
* plain-http origins where `navigator.locks` does not exist.
|
|
211
|
+
* - `'auto'` (default) — Web Locks when available, heartbeat otherwise.
|
|
212
|
+
*/
|
|
213
|
+
type LeaderStrategy = 'auto' | 'web-locks' | 'heartbeat';
|
|
192
214
|
interface LeaderOptions extends CommonOptions {
|
|
193
|
-
/** How often the leader re-announces itself, in ms. Default 1000. */
|
|
215
|
+
/** How often the leader re-announces itself, in ms. Default 1000. Heartbeat strategy only. */
|
|
194
216
|
heartbeatMs?: number;
|
|
195
|
-
/** How long a follower tolerates silence before calling the seat empty, in ms. Default 3000. */
|
|
217
|
+
/** How long a follower tolerates silence before calling the seat empty, in ms. Default 3000. Heartbeat strategy only. */
|
|
196
218
|
leaseMs?: number;
|
|
197
219
|
/** May this client hold the leadership? Default true. */
|
|
198
220
|
eligible?: boolean;
|
|
221
|
+
/** How to arbitrate the seat. Default 'auto'. */
|
|
222
|
+
strategy?: LeaderStrategy;
|
|
223
|
+
/** @internal Test seam for the Web Locks manager. Defaults to navigator.locks. */
|
|
224
|
+
locks?: LockManagerLike;
|
|
225
|
+
}
|
|
226
|
+
/** The slice of the Web Locks API this library uses. */
|
|
227
|
+
interface LockManagerLike {
|
|
228
|
+
request(name: string, options: {
|
|
229
|
+
signal?: AbortSignal;
|
|
230
|
+
}, callback: () => Promise<void>): Promise<void>;
|
|
199
231
|
}
|
|
200
232
|
interface LeaderSnapshot {
|
|
201
233
|
/** The current leader's clientId, or null while the seat is empty. */
|
|
@@ -204,9 +236,17 @@ interface LeaderSnapshot {
|
|
|
204
236
|
}
|
|
205
237
|
interface Leader {
|
|
206
238
|
readonly clientId: string;
|
|
239
|
+
/** Which mechanism arbitrates this seat — useful in devtools and bug reports. */
|
|
240
|
+
readonly strategy: Exclude<LeaderStrategy, 'auto'>;
|
|
207
241
|
/** Frozen; a new object only when the leader actually changes. */
|
|
208
242
|
getSnapshot(): LeaderSnapshot;
|
|
209
243
|
subscribe(fn: () => void): () => void;
|
|
244
|
+
/**
|
|
245
|
+
* Resolves the moment this client holds the seat, or immediately if it
|
|
246
|
+
* already does. Rejects if the leader is closed while still waiting — so an
|
|
247
|
+
* `await` in a torn-down tab does not hang forever.
|
|
248
|
+
*/
|
|
249
|
+
waitForLeadership(): Promise<void>;
|
|
210
250
|
/** Give up the seat now. Peers take over immediately rather than waiting for the lease. */
|
|
211
251
|
resign(): void;
|
|
212
252
|
/** Turn candidacy on or off. Eligibility is a property of the tab, not a component. */
|
|
@@ -390,19 +430,23 @@ declare function observeBus(name: string, fn: BusObserver): () => void;
|
|
|
390
430
|
/** Log every wire on a bus to the console. Returns a function to stop. */
|
|
391
431
|
declare function enableDebug(options?: DebugOptions): () => void;
|
|
392
432
|
|
|
393
|
-
/**
|
|
394
|
-
* Get the shared bus for `name`, creating it on first use. Callers must call
|
|
395
|
-
* bus.release() exactly once when done. When a custom transport factory is
|
|
396
|
-
* given (tests), every call creates an isolated bus — one call = one simulated client.
|
|
397
|
-
*/
|
|
398
433
|
/**
|
|
399
434
|
* Names of the buses currently alive on this page. Buses built with a custom
|
|
400
|
-
* transport (tests) bypass the
|
|
435
|
+
* transport (tests) bypass the table, so they are not listed.
|
|
401
436
|
*/
|
|
402
437
|
declare function getBusNames(): string[];
|
|
438
|
+
/**
|
|
439
|
+
* What is actually carrying this bus's traffic, or null if it has no bus yet.
|
|
440
|
+
*
|
|
441
|
+
* Answers the question a developer asks when nothing is syncing and the code
|
|
442
|
+
* looks right: *is anything even connected?* `'none'` means no — writes are
|
|
443
|
+
* local and no peer will ever see them.
|
|
444
|
+
*/
|
|
445
|
+
declare function getTransportKind(name: string): TransportKind | null;
|
|
403
446
|
|
|
404
447
|
/** Same-origin transport over a real BroadcastChannel. */
|
|
405
448
|
declare class BroadcastChannelTransport implements Transport {
|
|
449
|
+
readonly kind: TransportKind;
|
|
406
450
|
private bc;
|
|
407
451
|
private listeners;
|
|
408
452
|
constructor(name: string);
|
|
@@ -416,40 +460,65 @@ declare class BroadcastChannelTransport implements Transport {
|
|
|
416
460
|
* Used for SSR and for state scoped to a single tab.
|
|
417
461
|
*/
|
|
418
462
|
declare class NoopTransport implements Transport {
|
|
463
|
+
readonly kind: TransportKind;
|
|
419
464
|
post(): void;
|
|
420
465
|
subscribe(): () => void;
|
|
421
466
|
close(): void;
|
|
422
467
|
}
|
|
423
468
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
469
|
+
/**
|
|
470
|
+
* Cross-tab delivery over the `storage` event, for browsers with no
|
|
471
|
+
* `BroadcastChannel`.
|
|
472
|
+
*
|
|
473
|
+
* The mechanism is a quirk turned to advantage: writing to `localStorage` fires
|
|
474
|
+
* a `storage` event in *every other* same-origin tab and never in the writer —
|
|
475
|
+
* exactly BroadcastChannel's no-self-echo semantics, for free.
|
|
476
|
+
*
|
|
477
|
+
* Two differences from the real thing, both deliberate and both documented:
|
|
478
|
+
*
|
|
479
|
+
* 1. **Fidelity is JSON, not structured clone.** `localStorage` holds strings.
|
|
480
|
+
* A `Date` arrives as an ISO string and a `Map` as `{}`. Values that cannot
|
|
481
|
+
* be represented at all — functions, symbols — are rejected rather than
|
|
482
|
+
* silently dropped, so a write still cannot leave this tab holding something
|
|
483
|
+
* its peers never received.
|
|
484
|
+
* 2. **The entry is removed immediately after writing.** Peers have already been
|
|
485
|
+
* notified by then (the event carries the value), and leaving application
|
|
486
|
+
* state sitting in `localStorage` would be both a quota cost and a privacy
|
|
487
|
+
* one. The removal fires a second event with a null `newValue`, which
|
|
488
|
+
* receivers ignore.
|
|
489
|
+
*/
|
|
490
|
+
declare class StorageTransport implements Transport {
|
|
491
|
+
readonly kind: TransportKind;
|
|
492
|
+
private key;
|
|
493
|
+
private storage;
|
|
431
494
|
private listeners;
|
|
432
|
-
private
|
|
433
|
-
|
|
495
|
+
private onStorage;
|
|
496
|
+
private seq;
|
|
497
|
+
constructor(name: string, storage?: Storage);
|
|
434
498
|
post(data: unknown): void;
|
|
435
499
|
subscribe(listener: (data: unknown) => void): () => void;
|
|
436
500
|
close(): void;
|
|
437
|
-
/** @internal */
|
|
438
|
-
deliver(data: unknown): void;
|
|
439
501
|
}
|
|
440
502
|
|
|
503
|
+
declare function isBroadcastChannelAvailable(): boolean;
|
|
441
504
|
/**
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
505
|
+
* Can we hear other tabs through the `storage` event?
|
|
506
|
+
*
|
|
507
|
+
* Reading `localStorage` is itself what throws when storage is blocked — a
|
|
508
|
+
* sandboxed iframe, third-party cookies off — so the check has to happen inside
|
|
509
|
+
* a try, not around a property test. Availability is also not writability:
|
|
510
|
+
* Safari's old private mode exposed the object and threw on every setItem.
|
|
445
511
|
*/
|
|
446
|
-
declare
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
512
|
+
declare function isStorageEventAvailable(): boolean;
|
|
513
|
+
/**
|
|
514
|
+
* Pick the best wire this browser can offer, and say so when it is not the
|
|
515
|
+
* good one.
|
|
516
|
+
*
|
|
517
|
+
* The chain matters more than it looks. Before it existed, a context without
|
|
518
|
+
* `BroadcastChannel` got a silent no-op: every hook kept working, every write
|
|
519
|
+
* appeared to succeed, and nothing was ever shared with anybody. That is the
|
|
520
|
+
* worst failure this library can have, because it looks exactly like success.
|
|
521
|
+
*/
|
|
522
|
+
declare function defaultTransport(name: string): Transport;
|
|
454
523
|
|
|
455
|
-
export { BroadcastChannelTransport, type BusEvent, type BusObserver, type BusWire, CID_PARAM, type Channel, type CommonOptions, type ConnectToOpenerOptions, DEFAULT_NAME, type DebugOptions, HandshakeTimeoutError, type Leader, type LeaderOptions, type LeaderSnapshot,
|
|
524
|
+
export { BroadcastChannelTransport, type BusEvent, type BusObserver, type BusWire, CID_PARAM, type Channel, type CommonOptions, type ConnectToOpenerOptions, DEFAULT_NAME, type DebugOptions, HandshakeTimeoutError, type Leader, type LeaderOptions, type LeaderSnapshot, type LeaderStrategy, type MessageEventLike, type MessageMap, type MessageMeta, NoopTransport, type OpenWindowOptions, type OpenedWindow, type OpenerConnection, type Peer, type PeerKind, type PersistAdapter, type PersistOptions, type Persisted, type Presence, type PresenceOptions, type SharedStore, type SharedStoreOptions, type StorageLike, StorageTransport, Transport, TransportKind, type Version, type WebStorageAdapterOptions, WindowClosedError, type WindowEventTarget, type WindowLike, connectToOpener, createChannel, createLeader, createPresence, createSharedStore, defaultTransport, enableDebug, getBusNames, getTransportKind, isBroadcastChannelAvailable, isStorageEventAvailable, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter };
|