@use-everywhere/core 0.9.0 → 0.10.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 +40 -0
- package/dist/index.d.cts +93 -1
- package/dist/index.d.ts +93 -1
- package/dist/index.js +38 -0
- package/dist/shared-worker.cjs +53 -0
- package/dist/shared-worker.d.cts +46 -0
- package/dist/shared-worker.d.ts +46 -0
- package/dist/shared-worker.js +28 -0
- package/package.json +30 -2
package/dist/index.cjs
CHANGED
|
@@ -25,6 +25,7 @@ __export(src_exports, {
|
|
|
25
25
|
DEFAULT_NAME: () => DEFAULT_NAME,
|
|
26
26
|
HandshakeTimeoutError: () => HandshakeTimeoutError,
|
|
27
27
|
NoopTransport: () => NoopTransport,
|
|
28
|
+
SharedWorkerTransport: () => SharedWorkerTransport,
|
|
28
29
|
StorageTransport: () => StorageTransport,
|
|
29
30
|
WIRE_VERSION: () => WIRE_VERSION,
|
|
30
31
|
WindowClosedError: () => WindowClosedError,
|
|
@@ -42,6 +43,7 @@ __export(src_exports, {
|
|
|
42
43
|
getWireSkew: () => getWireSkew,
|
|
43
44
|
indexedDbAdapter: () => indexedDbAdapter,
|
|
44
45
|
isBroadcastChannelAvailable: () => isBroadcastChannelAvailable,
|
|
46
|
+
isSharedWorkerAvailable: () => isSharedWorkerAvailable,
|
|
45
47
|
isStorageEventAvailable: () => isStorageEventAvailable,
|
|
46
48
|
jsonSerializer: () => jsonSerializer,
|
|
47
49
|
localStorageAdapter: () => localStorageAdapter,
|
|
@@ -1890,6 +1892,42 @@ function createNamespace(namespace) {
|
|
|
1890
1892
|
createLeader: (name, options) => createLeader(busName(name), options)
|
|
1891
1893
|
};
|
|
1892
1894
|
}
|
|
1895
|
+
|
|
1896
|
+
// src/transport/shared-worker-transport.ts
|
|
1897
|
+
var SharedWorkerTransport = class {
|
|
1898
|
+
constructor(options) {
|
|
1899
|
+
this.kind = "shared-worker";
|
|
1900
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
1901
|
+
this.closed = false;
|
|
1902
|
+
const name = options.name ?? "use-everywhere";
|
|
1903
|
+
const factory = options.factory ?? ((url, workerName) => new SharedWorker(url, { name: workerName }));
|
|
1904
|
+
const worker = factory(String(options.url), name);
|
|
1905
|
+
this.port = worker.port;
|
|
1906
|
+
this.onMessage = (event) => {
|
|
1907
|
+
for (const listener of this.listeners) listener(event.data);
|
|
1908
|
+
};
|
|
1909
|
+
this.port.addEventListener("message", this.onMessage);
|
|
1910
|
+
this.port.start?.();
|
|
1911
|
+
}
|
|
1912
|
+
post(data) {
|
|
1913
|
+
if (this.closed) return;
|
|
1914
|
+
this.port.postMessage(data);
|
|
1915
|
+
}
|
|
1916
|
+
subscribe(listener) {
|
|
1917
|
+
this.listeners.add(listener);
|
|
1918
|
+
return () => this.listeners.delete(listener);
|
|
1919
|
+
}
|
|
1920
|
+
close() {
|
|
1921
|
+
if (this.closed) return;
|
|
1922
|
+
this.closed = true;
|
|
1923
|
+
this.listeners.clear();
|
|
1924
|
+
this.port.removeEventListener("message", this.onMessage);
|
|
1925
|
+
this.port.close();
|
|
1926
|
+
}
|
|
1927
|
+
};
|
|
1928
|
+
function isSharedWorkerAvailable() {
|
|
1929
|
+
return typeof SharedWorker !== "undefined";
|
|
1930
|
+
}
|
|
1893
1931
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1894
1932
|
0 && (module.exports = {
|
|
1895
1933
|
BroadcastChannelTransport,
|
|
@@ -1897,6 +1935,7 @@ function createNamespace(namespace) {
|
|
|
1897
1935
|
DEFAULT_NAME,
|
|
1898
1936
|
HandshakeTimeoutError,
|
|
1899
1937
|
NoopTransport,
|
|
1938
|
+
SharedWorkerTransport,
|
|
1900
1939
|
StorageTransport,
|
|
1901
1940
|
WIRE_VERSION,
|
|
1902
1941
|
WindowClosedError,
|
|
@@ -1914,6 +1953,7 @@ function createNamespace(namespace) {
|
|
|
1914
1953
|
getWireSkew,
|
|
1915
1954
|
indexedDbAdapter,
|
|
1916
1955
|
isBroadcastChannelAvailable,
|
|
1956
|
+
isSharedWorkerAvailable,
|
|
1917
1957
|
isStorageEventAvailable,
|
|
1918
1958
|
jsonSerializer,
|
|
1919
1959
|
localStorageAdapter,
|
package/dist/index.d.cts
CHANGED
|
@@ -1090,6 +1090,98 @@ declare class StorageTransport implements Transport {
|
|
|
1090
1090
|
close(): void;
|
|
1091
1091
|
}
|
|
1092
1092
|
|
|
1093
|
+
/**
|
|
1094
|
+
* The subset of `SharedWorker` this transport uses. Narrower than the DOM type
|
|
1095
|
+
* on purpose: tests supply a fake, and a fake that has to implement
|
|
1096
|
+
* `EventTarget` to be accepted is a fake nobody writes.
|
|
1097
|
+
*/
|
|
1098
|
+
interface SharedWorkerLike {
|
|
1099
|
+
readonly port: MessagePortLike;
|
|
1100
|
+
}
|
|
1101
|
+
interface MessagePortLike {
|
|
1102
|
+
postMessage(data: unknown): void;
|
|
1103
|
+
start?: () => void;
|
|
1104
|
+
close(): void;
|
|
1105
|
+
addEventListener(type: 'message', listener: (event: {
|
|
1106
|
+
data: unknown;
|
|
1107
|
+
}) => void): void;
|
|
1108
|
+
removeEventListener(type: 'message', listener: (event: {
|
|
1109
|
+
data: unknown;
|
|
1110
|
+
}) => void): void;
|
|
1111
|
+
}
|
|
1112
|
+
interface SharedWorkerTransportOptions {
|
|
1113
|
+
/**
|
|
1114
|
+
* The relay script. A `URL` rather than a string in the common case, because
|
|
1115
|
+
* `new URL('./relay.js', import.meta.url)` is what survives a bundler.
|
|
1116
|
+
*
|
|
1117
|
+
* It must be a *stable* URL, and that is the whole reason this option exists
|
|
1118
|
+
* rather than the library inlining a worker from a Blob the way it could for
|
|
1119
|
+
* a dedicated one: a SharedWorker's identity is its script URL plus its name,
|
|
1120
|
+
* and every tab that builds its own Blob URL gets its own worker. Inlining
|
|
1121
|
+
* would produce N private workers that share nothing — the failure this
|
|
1122
|
+
* transport exists to prevent, wearing the costume of a fix.
|
|
1123
|
+
*/
|
|
1124
|
+
url: string | URL;
|
|
1125
|
+
/**
|
|
1126
|
+
* Distinguishes workers loaded from the same script. Defaults to the bus
|
|
1127
|
+
* name, which is what makes two buses on one origin independent.
|
|
1128
|
+
*/
|
|
1129
|
+
name?: string;
|
|
1130
|
+
/**
|
|
1131
|
+
* Constructs the worker. Only tests should pass this; the default is
|
|
1132
|
+
* `globalThis.SharedWorker`.
|
|
1133
|
+
*/
|
|
1134
|
+
factory?: (url: string, name: string) => SharedWorkerLike;
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Cross-tab delivery through one SharedWorker per origin.
|
|
1138
|
+
*
|
|
1139
|
+
* BroadcastChannel is the better default for pure fan-out and stays the
|
|
1140
|
+
* default. This transport buys something else: a single place that is not a
|
|
1141
|
+
* tab. The relay outlives any individual tab, so the thing every "the leader
|
|
1142
|
+
* owns the socket" design actually wants — one connection, held somewhere no
|
|
1143
|
+
* user can close mid-flight — becomes possible without electing a leader at
|
|
1144
|
+
* all.
|
|
1145
|
+
*
|
|
1146
|
+
* What it does not buy: durability. The worker is torn down when the last port
|
|
1147
|
+
* closes, exactly like a BroadcastChannel with no listeners. State still lives
|
|
1148
|
+
* in the tabs; this moves the *wire*, not the source of truth.
|
|
1149
|
+
*
|
|
1150
|
+
* The relay script is three lines and ships with the library:
|
|
1151
|
+
*
|
|
1152
|
+
* ```js
|
|
1153
|
+
* // sw-relay.js — your app hosts this file
|
|
1154
|
+
* import 'use-everywhere/shared-worker';
|
|
1155
|
+
* ```
|
|
1156
|
+
*
|
|
1157
|
+
* ```ts
|
|
1158
|
+
* createSharedStore('cart', {
|
|
1159
|
+
* transport: new SharedWorkerTransport({
|
|
1160
|
+
* url: new URL('./sw-relay.js', import.meta.url),
|
|
1161
|
+
* }),
|
|
1162
|
+
* });
|
|
1163
|
+
* ```
|
|
1164
|
+
*/
|
|
1165
|
+
declare class SharedWorkerTransport implements Transport {
|
|
1166
|
+
readonly kind: TransportKind;
|
|
1167
|
+
private port;
|
|
1168
|
+
private listeners;
|
|
1169
|
+
private onMessage;
|
|
1170
|
+
private closed;
|
|
1171
|
+
constructor(options: SharedWorkerTransportOptions);
|
|
1172
|
+
post(data: unknown): void;
|
|
1173
|
+
subscribe(listener: (data: unknown) => void): () => void;
|
|
1174
|
+
close(): void;
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Whether this context can construct a SharedWorker at all.
|
|
1178
|
+
*
|
|
1179
|
+
* Notably false in every dedicated worker (they cannot nest a SharedWorker) and
|
|
1180
|
+
* in Chrome on Android. Worth checking before choosing this transport, since
|
|
1181
|
+
* the constructor throws rather than degrading.
|
|
1182
|
+
*/
|
|
1183
|
+
declare function isSharedWorkerAvailable(): boolean;
|
|
1184
|
+
|
|
1093
1185
|
declare function isBroadcastChannelAvailable(): boolean;
|
|
1094
1186
|
/**
|
|
1095
1187
|
* Can we hear other tabs through the `storage` event?
|
|
@@ -1111,4 +1203,4 @@ declare function isStorageEventAvailable(): boolean;
|
|
|
1111
1203
|
*/
|
|
1112
1204
|
declare function defaultTransport(name: string): Transport;
|
|
1113
1205
|
|
|
1114
|
-
export { type AskOptions, BroadcastChannelTransport, type BusEvent, type BusObserver, type BusWire, CID_PARAM, type Channel, type ChannelOptions, type CommonOptions, type ConnectToOpenerOptions, DEFAULT_NAME, type DebugOptions, HandshakeTimeoutError, type IndexedDbAdapterOptions, type InvalidPayload, type Leader, type LeaderOptions, type LeaderSnapshot, type LeaderStrategy, type LockManagerLike, type MessageEventLike, type MessageMap, type MessageMeta, type Namespace, NoopTransport, type OnInvalid, type OnOptions, type OpenWindowOptions, type OpenedWindow, type OpenerConnection, type Peer, type PeerKind, type PersistAdapter, type PersistOptions, type Persisted, type PostOptions, type Presence, type PresenceOptions, type ReplyMap, type RestoreError, type SchemaMap, type SchemaOptions, type Serializer, type SharedReducer, type SharedReducerOptions, type SharedStore, type SharedStoreOptions, type StandardSchemaV1, type StorageLike, StorageTransport, Transport, TransportKind, type Version, WIRE_VERSION, type WebStorageAdapterOptions, WindowClosedError, type WindowEventTarget, type WindowLike, connectToOpener, createChannel, createLeader, createNamespace, createPresence, createSharedReducer, createSharedStore, defaultTransport, enableDebug, getBusNames, getTransportKind, getWireSkew, indexedDbAdapter, isBroadcastChannelAvailable, isStorageEventAvailable, jsonSerializer, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter };
|
|
1206
|
+
export { type AskOptions, BroadcastChannelTransport, type BusEvent, type BusObserver, type BusWire, CID_PARAM, type Channel, type ChannelOptions, type CommonOptions, type ConnectToOpenerOptions, DEFAULT_NAME, type DebugOptions, HandshakeTimeoutError, type IndexedDbAdapterOptions, type InvalidPayload, type Leader, type LeaderOptions, type LeaderSnapshot, type LeaderStrategy, type LockManagerLike, type MessageEventLike, type MessageMap, type MessageMeta, type MessagePortLike, type Namespace, NoopTransport, type OnInvalid, type OnOptions, type OpenWindowOptions, type OpenedWindow, type OpenerConnection, type Peer, type PeerKind, type PersistAdapter, type PersistOptions, type Persisted, type PostOptions, type Presence, type PresenceOptions, type ReplyMap, type RestoreError, type SchemaMap, type SchemaOptions, type Serializer, type SharedReducer, type SharedReducerOptions, type SharedStore, type SharedStoreOptions, type SharedWorkerLike, SharedWorkerTransport, type SharedWorkerTransportOptions, type StandardSchemaV1, type StorageLike, StorageTransport, Transport, TransportKind, type Version, WIRE_VERSION, type WebStorageAdapterOptions, WindowClosedError, type WindowEventTarget, type WindowLike, connectToOpener, createChannel, createLeader, createNamespace, createPresence, createSharedReducer, createSharedStore, defaultTransport, enableDebug, getBusNames, getTransportKind, getWireSkew, indexedDbAdapter, isBroadcastChannelAvailable, isSharedWorkerAvailable, isStorageEventAvailable, jsonSerializer, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter };
|
package/dist/index.d.ts
CHANGED
|
@@ -1090,6 +1090,98 @@ declare class StorageTransport implements Transport {
|
|
|
1090
1090
|
close(): void;
|
|
1091
1091
|
}
|
|
1092
1092
|
|
|
1093
|
+
/**
|
|
1094
|
+
* The subset of `SharedWorker` this transport uses. Narrower than the DOM type
|
|
1095
|
+
* on purpose: tests supply a fake, and a fake that has to implement
|
|
1096
|
+
* `EventTarget` to be accepted is a fake nobody writes.
|
|
1097
|
+
*/
|
|
1098
|
+
interface SharedWorkerLike {
|
|
1099
|
+
readonly port: MessagePortLike;
|
|
1100
|
+
}
|
|
1101
|
+
interface MessagePortLike {
|
|
1102
|
+
postMessage(data: unknown): void;
|
|
1103
|
+
start?: () => void;
|
|
1104
|
+
close(): void;
|
|
1105
|
+
addEventListener(type: 'message', listener: (event: {
|
|
1106
|
+
data: unknown;
|
|
1107
|
+
}) => void): void;
|
|
1108
|
+
removeEventListener(type: 'message', listener: (event: {
|
|
1109
|
+
data: unknown;
|
|
1110
|
+
}) => void): void;
|
|
1111
|
+
}
|
|
1112
|
+
interface SharedWorkerTransportOptions {
|
|
1113
|
+
/**
|
|
1114
|
+
* The relay script. A `URL` rather than a string in the common case, because
|
|
1115
|
+
* `new URL('./relay.js', import.meta.url)` is what survives a bundler.
|
|
1116
|
+
*
|
|
1117
|
+
* It must be a *stable* URL, and that is the whole reason this option exists
|
|
1118
|
+
* rather than the library inlining a worker from a Blob the way it could for
|
|
1119
|
+
* a dedicated one: a SharedWorker's identity is its script URL plus its name,
|
|
1120
|
+
* and every tab that builds its own Blob URL gets its own worker. Inlining
|
|
1121
|
+
* would produce N private workers that share nothing — the failure this
|
|
1122
|
+
* transport exists to prevent, wearing the costume of a fix.
|
|
1123
|
+
*/
|
|
1124
|
+
url: string | URL;
|
|
1125
|
+
/**
|
|
1126
|
+
* Distinguishes workers loaded from the same script. Defaults to the bus
|
|
1127
|
+
* name, which is what makes two buses on one origin independent.
|
|
1128
|
+
*/
|
|
1129
|
+
name?: string;
|
|
1130
|
+
/**
|
|
1131
|
+
* Constructs the worker. Only tests should pass this; the default is
|
|
1132
|
+
* `globalThis.SharedWorker`.
|
|
1133
|
+
*/
|
|
1134
|
+
factory?: (url: string, name: string) => SharedWorkerLike;
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Cross-tab delivery through one SharedWorker per origin.
|
|
1138
|
+
*
|
|
1139
|
+
* BroadcastChannel is the better default for pure fan-out and stays the
|
|
1140
|
+
* default. This transport buys something else: a single place that is not a
|
|
1141
|
+
* tab. The relay outlives any individual tab, so the thing every "the leader
|
|
1142
|
+
* owns the socket" design actually wants — one connection, held somewhere no
|
|
1143
|
+
* user can close mid-flight — becomes possible without electing a leader at
|
|
1144
|
+
* all.
|
|
1145
|
+
*
|
|
1146
|
+
* What it does not buy: durability. The worker is torn down when the last port
|
|
1147
|
+
* closes, exactly like a BroadcastChannel with no listeners. State still lives
|
|
1148
|
+
* in the tabs; this moves the *wire*, not the source of truth.
|
|
1149
|
+
*
|
|
1150
|
+
* The relay script is three lines and ships with the library:
|
|
1151
|
+
*
|
|
1152
|
+
* ```js
|
|
1153
|
+
* // sw-relay.js — your app hosts this file
|
|
1154
|
+
* import 'use-everywhere/shared-worker';
|
|
1155
|
+
* ```
|
|
1156
|
+
*
|
|
1157
|
+
* ```ts
|
|
1158
|
+
* createSharedStore('cart', {
|
|
1159
|
+
* transport: new SharedWorkerTransport({
|
|
1160
|
+
* url: new URL('./sw-relay.js', import.meta.url),
|
|
1161
|
+
* }),
|
|
1162
|
+
* });
|
|
1163
|
+
* ```
|
|
1164
|
+
*/
|
|
1165
|
+
declare class SharedWorkerTransport implements Transport {
|
|
1166
|
+
readonly kind: TransportKind;
|
|
1167
|
+
private port;
|
|
1168
|
+
private listeners;
|
|
1169
|
+
private onMessage;
|
|
1170
|
+
private closed;
|
|
1171
|
+
constructor(options: SharedWorkerTransportOptions);
|
|
1172
|
+
post(data: unknown): void;
|
|
1173
|
+
subscribe(listener: (data: unknown) => void): () => void;
|
|
1174
|
+
close(): void;
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Whether this context can construct a SharedWorker at all.
|
|
1178
|
+
*
|
|
1179
|
+
* Notably false in every dedicated worker (they cannot nest a SharedWorker) and
|
|
1180
|
+
* in Chrome on Android. Worth checking before choosing this transport, since
|
|
1181
|
+
* the constructor throws rather than degrading.
|
|
1182
|
+
*/
|
|
1183
|
+
declare function isSharedWorkerAvailable(): boolean;
|
|
1184
|
+
|
|
1093
1185
|
declare function isBroadcastChannelAvailable(): boolean;
|
|
1094
1186
|
/**
|
|
1095
1187
|
* Can we hear other tabs through the `storage` event?
|
|
@@ -1111,4 +1203,4 @@ declare function isStorageEventAvailable(): boolean;
|
|
|
1111
1203
|
*/
|
|
1112
1204
|
declare function defaultTransport(name: string): Transport;
|
|
1113
1205
|
|
|
1114
|
-
export { type AskOptions, BroadcastChannelTransport, type BusEvent, type BusObserver, type BusWire, CID_PARAM, type Channel, type ChannelOptions, type CommonOptions, type ConnectToOpenerOptions, DEFAULT_NAME, type DebugOptions, HandshakeTimeoutError, type IndexedDbAdapterOptions, type InvalidPayload, type Leader, type LeaderOptions, type LeaderSnapshot, type LeaderStrategy, type LockManagerLike, type MessageEventLike, type MessageMap, type MessageMeta, type Namespace, NoopTransport, type OnInvalid, type OnOptions, type OpenWindowOptions, type OpenedWindow, type OpenerConnection, type Peer, type PeerKind, type PersistAdapter, type PersistOptions, type Persisted, type PostOptions, type Presence, type PresenceOptions, type ReplyMap, type RestoreError, type SchemaMap, type SchemaOptions, type Serializer, type SharedReducer, type SharedReducerOptions, type SharedStore, type SharedStoreOptions, type StandardSchemaV1, type StorageLike, StorageTransport, Transport, TransportKind, type Version, WIRE_VERSION, type WebStorageAdapterOptions, WindowClosedError, type WindowEventTarget, type WindowLike, connectToOpener, createChannel, createLeader, createNamespace, createPresence, createSharedReducer, createSharedStore, defaultTransport, enableDebug, getBusNames, getTransportKind, getWireSkew, indexedDbAdapter, isBroadcastChannelAvailable, isStorageEventAvailable, jsonSerializer, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter };
|
|
1206
|
+
export { type AskOptions, BroadcastChannelTransport, type BusEvent, type BusObserver, type BusWire, CID_PARAM, type Channel, type ChannelOptions, type CommonOptions, type ConnectToOpenerOptions, DEFAULT_NAME, type DebugOptions, HandshakeTimeoutError, type IndexedDbAdapterOptions, type InvalidPayload, type Leader, type LeaderOptions, type LeaderSnapshot, type LeaderStrategy, type LockManagerLike, type MessageEventLike, type MessageMap, type MessageMeta, type MessagePortLike, type Namespace, NoopTransport, type OnInvalid, type OnOptions, type OpenWindowOptions, type OpenedWindow, type OpenerConnection, type Peer, type PeerKind, type PersistAdapter, type PersistOptions, type Persisted, type PostOptions, type Presence, type PresenceOptions, type ReplyMap, type RestoreError, type SchemaMap, type SchemaOptions, type Serializer, type SharedReducer, type SharedReducerOptions, type SharedStore, type SharedStoreOptions, type SharedWorkerLike, SharedWorkerTransport, type SharedWorkerTransportOptions, type StandardSchemaV1, type StorageLike, StorageTransport, Transport, TransportKind, type Version, WIRE_VERSION, type WebStorageAdapterOptions, WindowClosedError, type WindowEventTarget, type WindowLike, connectToOpener, createChannel, createLeader, createNamespace, createPresence, createSharedReducer, createSharedStore, defaultTransport, enableDebug, getBusNames, getTransportKind, getWireSkew, indexedDbAdapter, isBroadcastChannelAvailable, isSharedWorkerAvailable, isStorageEventAvailable, jsonSerializer, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter };
|
package/dist/index.js
CHANGED
|
@@ -1835,12 +1835,49 @@ function createNamespace(namespace) {
|
|
|
1835
1835
|
createLeader: (name, options) => createLeader(busName(name), options)
|
|
1836
1836
|
};
|
|
1837
1837
|
}
|
|
1838
|
+
|
|
1839
|
+
// src/transport/shared-worker-transport.ts
|
|
1840
|
+
var SharedWorkerTransport = class {
|
|
1841
|
+
constructor(options) {
|
|
1842
|
+
this.kind = "shared-worker";
|
|
1843
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
1844
|
+
this.closed = false;
|
|
1845
|
+
const name = options.name ?? "use-everywhere";
|
|
1846
|
+
const factory = options.factory ?? ((url, workerName) => new SharedWorker(url, { name: workerName }));
|
|
1847
|
+
const worker = factory(String(options.url), name);
|
|
1848
|
+
this.port = worker.port;
|
|
1849
|
+
this.onMessage = (event) => {
|
|
1850
|
+
for (const listener of this.listeners) listener(event.data);
|
|
1851
|
+
};
|
|
1852
|
+
this.port.addEventListener("message", this.onMessage);
|
|
1853
|
+
this.port.start?.();
|
|
1854
|
+
}
|
|
1855
|
+
post(data) {
|
|
1856
|
+
if (this.closed) return;
|
|
1857
|
+
this.port.postMessage(data);
|
|
1858
|
+
}
|
|
1859
|
+
subscribe(listener) {
|
|
1860
|
+
this.listeners.add(listener);
|
|
1861
|
+
return () => this.listeners.delete(listener);
|
|
1862
|
+
}
|
|
1863
|
+
close() {
|
|
1864
|
+
if (this.closed) return;
|
|
1865
|
+
this.closed = true;
|
|
1866
|
+
this.listeners.clear();
|
|
1867
|
+
this.port.removeEventListener("message", this.onMessage);
|
|
1868
|
+
this.port.close();
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
function isSharedWorkerAvailable() {
|
|
1872
|
+
return typeof SharedWorker !== "undefined";
|
|
1873
|
+
}
|
|
1838
1874
|
export {
|
|
1839
1875
|
BroadcastChannelTransport,
|
|
1840
1876
|
CID_PARAM,
|
|
1841
1877
|
DEFAULT_NAME,
|
|
1842
1878
|
HandshakeTimeoutError,
|
|
1843
1879
|
NoopTransport,
|
|
1880
|
+
SharedWorkerTransport,
|
|
1844
1881
|
StorageTransport,
|
|
1845
1882
|
WIRE_VERSION,
|
|
1846
1883
|
WindowClosedError,
|
|
@@ -1858,6 +1895,7 @@ export {
|
|
|
1858
1895
|
getWireSkew,
|
|
1859
1896
|
indexedDbAdapter,
|
|
1860
1897
|
isBroadcastChannelAvailable,
|
|
1898
|
+
isSharedWorkerAvailable,
|
|
1861
1899
|
isStorageEventAvailable,
|
|
1862
1900
|
jsonSerializer,
|
|
1863
1901
|
localStorageAdapter,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/shared-worker.ts
|
|
21
|
+
var shared_worker_exports = {};
|
|
22
|
+
__export(shared_worker_exports, {
|
|
23
|
+
startRelay: () => startRelay
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(shared_worker_exports);
|
|
26
|
+
function startRelay(scope) {
|
|
27
|
+
const ports = /* @__PURE__ */ new Set();
|
|
28
|
+
scope.onconnect = (event) => {
|
|
29
|
+
const port = event.ports[0];
|
|
30
|
+
if (!port) return;
|
|
31
|
+
ports.add(port);
|
|
32
|
+
port.addEventListener("message", (message) => {
|
|
33
|
+
const dead = [];
|
|
34
|
+
for (const peer of ports) {
|
|
35
|
+
if (peer === port) continue;
|
|
36
|
+
try {
|
|
37
|
+
peer.postMessage(message.data);
|
|
38
|
+
} catch {
|
|
39
|
+
dead.push(peer);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const corpse of dead) ports.delete(corpse);
|
|
43
|
+
});
|
|
44
|
+
port.start?.();
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (typeof self !== "undefined" && "onconnect" in self) {
|
|
48
|
+
startRelay(self);
|
|
49
|
+
}
|
|
50
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
51
|
+
0 && (module.exports = {
|
|
52
|
+
startRelay
|
|
53
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The relay that runs *inside* a SharedWorker. Host a one-line module and point
|
|
3
|
+
* `SharedWorkerTransport` at it:
|
|
4
|
+
*
|
|
5
|
+
* ```js
|
|
6
|
+
* // sw-relay.js
|
|
7
|
+
* import '@use-everywhere/core/shared-worker';
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* Importing this module installs the `connect` handler, which is why the entry
|
|
11
|
+
* point is a side effect rather than a function you call: a SharedWorker script
|
|
12
|
+
* has one job, and making the caller remember to invoke it adds a way to get a
|
|
13
|
+
* worker that accepts connections and forwards nothing.
|
|
14
|
+
*
|
|
15
|
+
* `startRelay` is exported anyway, for a worker that also does other work — an
|
|
16
|
+
* app whose relay owns the WebSocket, say — and for tests, which cannot install
|
|
17
|
+
* a global `onconnect`.
|
|
18
|
+
*/
|
|
19
|
+
interface RelayPort {
|
|
20
|
+
postMessage(data: unknown): void;
|
|
21
|
+
start?: () => void;
|
|
22
|
+
addEventListener(type: 'message', listener: (event: {
|
|
23
|
+
data: unknown;
|
|
24
|
+
}) => void): void;
|
|
25
|
+
}
|
|
26
|
+
interface RelayScope {
|
|
27
|
+
onconnect: ((event: {
|
|
28
|
+
ports: readonly RelayPort[];
|
|
29
|
+
}) => void) | null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fan every message out to the other connected ports.
|
|
33
|
+
*
|
|
34
|
+
* Two properties the buses above this depend on:
|
|
35
|
+
*
|
|
36
|
+
* 1. **No self-echo.** A sender never receives its own message, matching
|
|
37
|
+
* BroadcastChannel exactly. Without it every store would apply its own
|
|
38
|
+
* writes twice and every presence roster would count each tab as two.
|
|
39
|
+
* 2. **A dead port is dropped, not thrown over.** A tab that goes away without
|
|
40
|
+
* closing its port leaves an entangled port whose `postMessage` throws; one
|
|
41
|
+
* of those must not stop delivery to the tabs still listening, so the send
|
|
42
|
+
* loop is individually guarded and the corpse is pruned.
|
|
43
|
+
*/
|
|
44
|
+
declare function startRelay(scope: RelayScope): void;
|
|
45
|
+
|
|
46
|
+
export { type RelayPort, type RelayScope, startRelay };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The relay that runs *inside* a SharedWorker. Host a one-line module and point
|
|
3
|
+
* `SharedWorkerTransport` at it:
|
|
4
|
+
*
|
|
5
|
+
* ```js
|
|
6
|
+
* // sw-relay.js
|
|
7
|
+
* import '@use-everywhere/core/shared-worker';
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* Importing this module installs the `connect` handler, which is why the entry
|
|
11
|
+
* point is a side effect rather than a function you call: a SharedWorker script
|
|
12
|
+
* has one job, and making the caller remember to invoke it adds a way to get a
|
|
13
|
+
* worker that accepts connections and forwards nothing.
|
|
14
|
+
*
|
|
15
|
+
* `startRelay` is exported anyway, for a worker that also does other work — an
|
|
16
|
+
* app whose relay owns the WebSocket, say — and for tests, which cannot install
|
|
17
|
+
* a global `onconnect`.
|
|
18
|
+
*/
|
|
19
|
+
interface RelayPort {
|
|
20
|
+
postMessage(data: unknown): void;
|
|
21
|
+
start?: () => void;
|
|
22
|
+
addEventListener(type: 'message', listener: (event: {
|
|
23
|
+
data: unknown;
|
|
24
|
+
}) => void): void;
|
|
25
|
+
}
|
|
26
|
+
interface RelayScope {
|
|
27
|
+
onconnect: ((event: {
|
|
28
|
+
ports: readonly RelayPort[];
|
|
29
|
+
}) => void) | null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fan every message out to the other connected ports.
|
|
33
|
+
*
|
|
34
|
+
* Two properties the buses above this depend on:
|
|
35
|
+
*
|
|
36
|
+
* 1. **No self-echo.** A sender never receives its own message, matching
|
|
37
|
+
* BroadcastChannel exactly. Without it every store would apply its own
|
|
38
|
+
* writes twice and every presence roster would count each tab as two.
|
|
39
|
+
* 2. **A dead port is dropped, not thrown over.** A tab that goes away without
|
|
40
|
+
* closing its port leaves an entangled port whose `postMessage` throws; one
|
|
41
|
+
* of those must not stop delivery to the tabs still listening, so the send
|
|
42
|
+
* loop is individually guarded and the corpse is pruned.
|
|
43
|
+
*/
|
|
44
|
+
declare function startRelay(scope: RelayScope): void;
|
|
45
|
+
|
|
46
|
+
export { type RelayPort, type RelayScope, startRelay };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/shared-worker.ts
|
|
2
|
+
function startRelay(scope) {
|
|
3
|
+
const ports = /* @__PURE__ */ new Set();
|
|
4
|
+
scope.onconnect = (event) => {
|
|
5
|
+
const port = event.ports[0];
|
|
6
|
+
if (!port) return;
|
|
7
|
+
ports.add(port);
|
|
8
|
+
port.addEventListener("message", (message) => {
|
|
9
|
+
const dead = [];
|
|
10
|
+
for (const peer of ports) {
|
|
11
|
+
if (peer === port) continue;
|
|
12
|
+
try {
|
|
13
|
+
peer.postMessage(message.data);
|
|
14
|
+
} catch {
|
|
15
|
+
dead.push(peer);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
for (const corpse of dead) ports.delete(corpse);
|
|
19
|
+
});
|
|
20
|
+
port.start?.();
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (typeof self !== "undefined" && "onconnect" in self) {
|
|
24
|
+
startRelay(self);
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
startRelay
|
|
28
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@use-everywhere/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Cross-tab shared state, events, presence, and cross-origin window channels",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Jonatan Kruszewski <jonakrusze@gmail.com>",
|
|
@@ -24,7 +24,10 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"type": "module",
|
|
27
|
-
"sideEffects":
|
|
27
|
+
"sideEffects": [
|
|
28
|
+
"./dist/shared-worker.js",
|
|
29
|
+
"./dist/shared-worker.cjs"
|
|
30
|
+
],
|
|
28
31
|
"main": "./dist/index.cjs",
|
|
29
32
|
"module": "./dist/index.js",
|
|
30
33
|
"types": "./dist/index.d.ts",
|
|
@@ -39,6 +42,16 @@
|
|
|
39
42
|
"default": "./dist/index.cjs"
|
|
40
43
|
}
|
|
41
44
|
},
|
|
45
|
+
"./shared-worker": {
|
|
46
|
+
"import": {
|
|
47
|
+
"types": "./dist/shared-worker.d.ts",
|
|
48
|
+
"default": "./dist/shared-worker.js"
|
|
49
|
+
},
|
|
50
|
+
"require": {
|
|
51
|
+
"types": "./dist/shared-worker.d.cts",
|
|
52
|
+
"default": "./dist/shared-worker.cjs"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
42
55
|
"./testing": {
|
|
43
56
|
"import": {
|
|
44
57
|
"types": "./dist/testing.d.ts",
|
|
@@ -120,6 +133,18 @@
|
|
|
120
133
|
"import": "{ indexedDbAdapter }",
|
|
121
134
|
"limit": "447 B"
|
|
122
135
|
},
|
|
136
|
+
{
|
|
137
|
+
"name": "SharedWorkerTransport",
|
|
138
|
+
"path": "dist/index.js",
|
|
139
|
+
"import": "{ SharedWorkerTransport }",
|
|
140
|
+
"limit": "380 B"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"name": "SharedWorker relay (shared-worker subpath)",
|
|
144
|
+
"path": "dist/shared-worker.js",
|
|
145
|
+
"import": "*",
|
|
146
|
+
"limit": "280 B"
|
|
147
|
+
},
|
|
123
148
|
{
|
|
124
149
|
"name": "MemoryHub (testing subpath)",
|
|
125
150
|
"path": "dist/testing.js",
|
|
@@ -145,6 +170,9 @@
|
|
|
145
170
|
},
|
|
146
171
|
"typesVersions": {
|
|
147
172
|
"*": {
|
|
173
|
+
"shared-worker": [
|
|
174
|
+
"./dist/shared-worker.d.ts"
|
|
175
|
+
],
|
|
148
176
|
"testing": [
|
|
149
177
|
"./dist/testing.d.ts"
|
|
150
178
|
]
|