@use-everywhere/core 0.6.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 +376 -57
- package/dist/index.d.cts +118 -13
- package/dist/index.d.ts +118 -13
- package/dist/index.js +373 -57
- package/dist/testing.cjs +1 -0
- package/dist/testing.d.cts +2 -1
- package/dist/testing.d.ts +2 -1
- package/dist/testing.js +1 -0
- package/dist/transport.types-CV1WZOhy.d.cts +20 -0
- package/dist/transport.types-CV1WZOhy.d.ts +20 -0
- package/package.json +9 -8
- package/dist/transport.types-tQ1cu6Xm.d.cts +0 -12
- package/dist/transport.types-tQ1cu6Xm.d.ts +0 -12
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T as Transport } from './transport.types-
|
|
1
|
+
import { T as Transport, a as TransportKind } from './transport.types-CV1WZOhy.cjs';
|
|
2
2
|
|
|
3
3
|
type MessageMap = Record<string, unknown>;
|
|
4
4
|
type PeerKind = 'tab' | 'worker' | (string & {});
|
|
@@ -157,8 +157,15 @@ interface BusOptions extends CommonOptions {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
interface PresenceOptions extends BusOptions {
|
|
160
|
-
/**
|
|
160
|
+
/** How much silence makes a peer suspect. Default 5000ms. It is then probed, not dropped. */
|
|
161
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;
|
|
162
169
|
}
|
|
163
170
|
interface Presence {
|
|
164
171
|
readonly clientId: string;
|
|
@@ -171,7 +178,19 @@ interface Presence {
|
|
|
171
178
|
/**
|
|
172
179
|
* Tracks the other tabs/windows/workers on this bus. Any message from a peer
|
|
173
180
|
* counts as a liveness signal (state patches, events, and presence pings all
|
|
174
|
-
* 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.
|
|
175
194
|
*/
|
|
176
195
|
declare function createPresence(name: string, options?: PresenceOptions): Presence;
|
|
177
196
|
|
|
@@ -180,13 +199,35 @@ declare function createPresence(name: string, options?: PresenceOptions): Presen
|
|
|
180
199
|
* the leader's re-announce interval, which is a different thing from the bus's
|
|
181
200
|
* presence ping. See the note in leader.ts about forwarding to getBus.
|
|
182
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';
|
|
183
214
|
interface LeaderOptions extends CommonOptions {
|
|
184
|
-
/** 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. */
|
|
185
216
|
heartbeatMs?: number;
|
|
186
|
-
/** 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. */
|
|
187
218
|
leaseMs?: number;
|
|
188
219
|
/** May this client hold the leadership? Default true. */
|
|
189
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>;
|
|
190
231
|
}
|
|
191
232
|
interface LeaderSnapshot {
|
|
192
233
|
/** The current leader's clientId, or null while the seat is empty. */
|
|
@@ -195,9 +236,17 @@ interface LeaderSnapshot {
|
|
|
195
236
|
}
|
|
196
237
|
interface Leader {
|
|
197
238
|
readonly clientId: string;
|
|
239
|
+
/** Which mechanism arbitrates this seat — useful in devtools and bug reports. */
|
|
240
|
+
readonly strategy: Exclude<LeaderStrategy, 'auto'>;
|
|
198
241
|
/** Frozen; a new object only when the leader actually changes. */
|
|
199
242
|
getSnapshot(): LeaderSnapshot;
|
|
200
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>;
|
|
201
250
|
/** Give up the seat now. Peers take over immediately rather than waiting for the lease. */
|
|
202
251
|
resign(): void;
|
|
203
252
|
/** Turn candidacy on or off. Eligibility is a property of the tab, not a component. */
|
|
@@ -381,19 +430,23 @@ declare function observeBus(name: string, fn: BusObserver): () => void;
|
|
|
381
430
|
/** Log every wire on a bus to the console. Returns a function to stop. */
|
|
382
431
|
declare function enableDebug(options?: DebugOptions): () => void;
|
|
383
432
|
|
|
384
|
-
/**
|
|
385
|
-
* Get the shared bus for `name`, creating it on first use. Callers must call
|
|
386
|
-
* bus.release() exactly once when done. When a custom transport factory is
|
|
387
|
-
* given (tests), every call creates an isolated bus — one call = one simulated client.
|
|
388
|
-
*/
|
|
389
433
|
/**
|
|
390
434
|
* Names of the buses currently alive on this page. Buses built with a custom
|
|
391
|
-
* transport (tests) bypass the
|
|
435
|
+
* transport (tests) bypass the table, so they are not listed.
|
|
392
436
|
*/
|
|
393
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;
|
|
394
446
|
|
|
395
447
|
/** Same-origin transport over a real BroadcastChannel. */
|
|
396
448
|
declare class BroadcastChannelTransport implements Transport {
|
|
449
|
+
readonly kind: TransportKind;
|
|
397
450
|
private bc;
|
|
398
451
|
private listeners;
|
|
399
452
|
constructor(name: string);
|
|
@@ -407,13 +460,65 @@ declare class BroadcastChannelTransport implements Transport {
|
|
|
407
460
|
* Used for SSR and for state scoped to a single tab.
|
|
408
461
|
*/
|
|
409
462
|
declare class NoopTransport implements Transport {
|
|
463
|
+
readonly kind: TransportKind;
|
|
410
464
|
post(): void;
|
|
411
465
|
subscribe(): () => void;
|
|
412
466
|
close(): void;
|
|
413
467
|
}
|
|
414
468
|
|
|
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;
|
|
494
|
+
private listeners;
|
|
495
|
+
private onStorage;
|
|
496
|
+
private seq;
|
|
497
|
+
constructor(name: string, storage?: Storage);
|
|
498
|
+
post(data: unknown): void;
|
|
499
|
+
subscribe(listener: (data: unknown) => void): () => void;
|
|
500
|
+
close(): void;
|
|
501
|
+
}
|
|
502
|
+
|
|
415
503
|
declare function isBroadcastChannelAvailable(): boolean;
|
|
416
|
-
/**
|
|
504
|
+
/**
|
|
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.
|
|
511
|
+
*/
|
|
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
|
+
*/
|
|
417
522
|
declare function defaultTransport(name: string): Transport;
|
|
418
523
|
|
|
419
|
-
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 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, Transport, type Version, type WebStorageAdapterOptions, WindowClosedError, type WindowEventTarget, type WindowLike, connectToOpener, createChannel, createLeader, createPresence, createSharedStore, defaultTransport, enableDebug, getBusNames, isBroadcastChannelAvailable, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter };
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { T as Transport } from './transport.types-
|
|
1
|
+
import { T as Transport, a as TransportKind } from './transport.types-CV1WZOhy.js';
|
|
2
2
|
|
|
3
3
|
type MessageMap = Record<string, unknown>;
|
|
4
4
|
type PeerKind = 'tab' | 'worker' | (string & {});
|
|
@@ -157,8 +157,15 @@ interface BusOptions extends CommonOptions {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
interface PresenceOptions extends BusOptions {
|
|
160
|
-
/**
|
|
160
|
+
/** How much silence makes a peer suspect. Default 5000ms. It is then probed, not dropped. */
|
|
161
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;
|
|
162
169
|
}
|
|
163
170
|
interface Presence {
|
|
164
171
|
readonly clientId: string;
|
|
@@ -171,7 +178,19 @@ interface Presence {
|
|
|
171
178
|
/**
|
|
172
179
|
* Tracks the other tabs/windows/workers on this bus. Any message from a peer
|
|
173
180
|
* counts as a liveness signal (state patches, events, and presence pings all
|
|
174
|
-
* 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.
|
|
175
194
|
*/
|
|
176
195
|
declare function createPresence(name: string, options?: PresenceOptions): Presence;
|
|
177
196
|
|
|
@@ -180,13 +199,35 @@ declare function createPresence(name: string, options?: PresenceOptions): Presen
|
|
|
180
199
|
* the leader's re-announce interval, which is a different thing from the bus's
|
|
181
200
|
* presence ping. See the note in leader.ts about forwarding to getBus.
|
|
182
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';
|
|
183
214
|
interface LeaderOptions extends CommonOptions {
|
|
184
|
-
/** 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. */
|
|
185
216
|
heartbeatMs?: number;
|
|
186
|
-
/** 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. */
|
|
187
218
|
leaseMs?: number;
|
|
188
219
|
/** May this client hold the leadership? Default true. */
|
|
189
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>;
|
|
190
231
|
}
|
|
191
232
|
interface LeaderSnapshot {
|
|
192
233
|
/** The current leader's clientId, or null while the seat is empty. */
|
|
@@ -195,9 +236,17 @@ interface LeaderSnapshot {
|
|
|
195
236
|
}
|
|
196
237
|
interface Leader {
|
|
197
238
|
readonly clientId: string;
|
|
239
|
+
/** Which mechanism arbitrates this seat — useful in devtools and bug reports. */
|
|
240
|
+
readonly strategy: Exclude<LeaderStrategy, 'auto'>;
|
|
198
241
|
/** Frozen; a new object only when the leader actually changes. */
|
|
199
242
|
getSnapshot(): LeaderSnapshot;
|
|
200
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>;
|
|
201
250
|
/** Give up the seat now. Peers take over immediately rather than waiting for the lease. */
|
|
202
251
|
resign(): void;
|
|
203
252
|
/** Turn candidacy on or off. Eligibility is a property of the tab, not a component. */
|
|
@@ -381,19 +430,23 @@ declare function observeBus(name: string, fn: BusObserver): () => void;
|
|
|
381
430
|
/** Log every wire on a bus to the console. Returns a function to stop. */
|
|
382
431
|
declare function enableDebug(options?: DebugOptions): () => void;
|
|
383
432
|
|
|
384
|
-
/**
|
|
385
|
-
* Get the shared bus for `name`, creating it on first use. Callers must call
|
|
386
|
-
* bus.release() exactly once when done. When a custom transport factory is
|
|
387
|
-
* given (tests), every call creates an isolated bus — one call = one simulated client.
|
|
388
|
-
*/
|
|
389
433
|
/**
|
|
390
434
|
* Names of the buses currently alive on this page. Buses built with a custom
|
|
391
|
-
* transport (tests) bypass the
|
|
435
|
+
* transport (tests) bypass the table, so they are not listed.
|
|
392
436
|
*/
|
|
393
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;
|
|
394
446
|
|
|
395
447
|
/** Same-origin transport over a real BroadcastChannel. */
|
|
396
448
|
declare class BroadcastChannelTransport implements Transport {
|
|
449
|
+
readonly kind: TransportKind;
|
|
397
450
|
private bc;
|
|
398
451
|
private listeners;
|
|
399
452
|
constructor(name: string);
|
|
@@ -407,13 +460,65 @@ declare class BroadcastChannelTransport implements Transport {
|
|
|
407
460
|
* Used for SSR and for state scoped to a single tab.
|
|
408
461
|
*/
|
|
409
462
|
declare class NoopTransport implements Transport {
|
|
463
|
+
readonly kind: TransportKind;
|
|
410
464
|
post(): void;
|
|
411
465
|
subscribe(): () => void;
|
|
412
466
|
close(): void;
|
|
413
467
|
}
|
|
414
468
|
|
|
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;
|
|
494
|
+
private listeners;
|
|
495
|
+
private onStorage;
|
|
496
|
+
private seq;
|
|
497
|
+
constructor(name: string, storage?: Storage);
|
|
498
|
+
post(data: unknown): void;
|
|
499
|
+
subscribe(listener: (data: unknown) => void): () => void;
|
|
500
|
+
close(): void;
|
|
501
|
+
}
|
|
502
|
+
|
|
415
503
|
declare function isBroadcastChannelAvailable(): boolean;
|
|
416
|
-
/**
|
|
504
|
+
/**
|
|
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.
|
|
511
|
+
*/
|
|
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
|
+
*/
|
|
417
522
|
declare function defaultTransport(name: string): Transport;
|
|
418
523
|
|
|
419
|
-
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 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, Transport, type Version, type WebStorageAdapterOptions, WindowClosedError, type WindowEventTarget, type WindowLike, connectToOpener, createChannel, createLeader, createPresence, createSharedStore, defaultTransport, enableDebug, getBusNames, isBroadcastChannelAvailable, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter };
|
|
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 };
|