@rebasepro/client 0.9.1-canary.baa7a6b → 0.9.1-canary.c0d4c07
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.es.js +71 -23
- package/dist/index.es.js.map +1 -1
- package/dist/websocket.d.ts +28 -1
- package/package.json +4 -4
- package/src/index.ts +14 -2
- package/src/realtime-channel.test.ts +44 -0
- package/src/realtime-optout.test.ts +128 -2
- package/src/websocket.ts +102 -17
package/dist/index.es.js
CHANGED
|
@@ -1405,6 +1405,20 @@ function extractMessageError(message) {
|
|
|
1405
1405
|
};
|
|
1406
1406
|
}
|
|
1407
1407
|
/**
|
|
1408
|
+
* Broadcast and presence frames.
|
|
1409
|
+
*
|
|
1410
|
+
* Fire-and-forget (the server sends no response envelope), and exempt from the
|
|
1411
|
+
* client-side auth gate — a public channel is usable without an account.
|
|
1412
|
+
*/
|
|
1413
|
+
var CHANNEL_MESSAGE_TYPES = new Set([
|
|
1414
|
+
"join_channel",
|
|
1415
|
+
"leave_channel",
|
|
1416
|
+
"broadcast",
|
|
1417
|
+
"presence_track",
|
|
1418
|
+
"presence_untrack",
|
|
1419
|
+
"presence_state"
|
|
1420
|
+
]);
|
|
1421
|
+
/**
|
|
1408
1422
|
* Low-level realtime WebSocket client.
|
|
1409
1423
|
*
|
|
1410
1424
|
* @internal Not a stable app-facing API. `createRebaseClient()` constructs and
|
|
@@ -1421,6 +1435,19 @@ var RebaseWebSocketClient = class {
|
|
|
1421
1435
|
listeners = /* @__PURE__ */ new Map();
|
|
1422
1436
|
/** Channel-name → handlers, for broadcast and presence frames. */
|
|
1423
1437
|
channelHandlers = /* @__PURE__ */ new Map();
|
|
1438
|
+
/** Set by `close()`. Blocks any later operation from silently redialling. */
|
|
1439
|
+
closedByCaller = false;
|
|
1440
|
+
/**
|
|
1441
|
+
* Whether a socket exists at all (open or still opening).
|
|
1442
|
+
*
|
|
1443
|
+
* Lets callers distinguish "authenticate the live socket" from "there is
|
|
1444
|
+
* nothing to authenticate yet", without that question forcing a dial.
|
|
1445
|
+
*/
|
|
1446
|
+
get hasSocket() {
|
|
1447
|
+
return this.ws !== null;
|
|
1448
|
+
}
|
|
1449
|
+
/** So the "no WebSocket in this environment" warning is said once, not per call. */
|
|
1450
|
+
warnedNoWebSocket = false;
|
|
1424
1451
|
/** Subscribe to broadcast/presence frames for one channel. */
|
|
1425
1452
|
onChannelMessage(channel, handler) {
|
|
1426
1453
|
if (!this.channelHandlers.has(channel)) this.channelHandlers.set(channel, /* @__PURE__ */ new Set());
|
|
@@ -1466,8 +1493,25 @@ var RebaseWebSocketClient = class {
|
|
|
1466
1493
|
this.getAuthToken = config.getAuthToken;
|
|
1467
1494
|
this.onUnauthorized = config.onUnauthorized;
|
|
1468
1495
|
this.WebSocketConstructor = config.WebSocket || (typeof WebSocket !== "undefined" ? WebSocket : void 0);
|
|
1469
|
-
|
|
1470
|
-
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* Open the socket if it is not open (or opening) already.
|
|
1499
|
+
*
|
|
1500
|
+
* Idempotent, synchronous, and safe to call on every operation that needs a
|
|
1501
|
+
* live socket — `initWebSocket` already no-ops on an open socket and is
|
|
1502
|
+
* re-entrant, since the reconnect path has always called it.
|
|
1503
|
+
*/
|
|
1504
|
+
ensureConnected() {
|
|
1505
|
+
if (this.closedByCaller) return;
|
|
1506
|
+
if (!this.WebSocketConstructor) {
|
|
1507
|
+
if (!this.warnedNoWebSocket) {
|
|
1508
|
+
this.warnedNoWebSocket = true;
|
|
1509
|
+
console.warn("WebSocket is not defined in this environment. Realtime subscriptions will not work unless you provide a WebSocket implementation in the config.");
|
|
1510
|
+
}
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
if (this.ws || this.reconnectTimeout) return;
|
|
1514
|
+
this.initWebSocket();
|
|
1471
1515
|
}
|
|
1472
1516
|
/**
|
|
1473
1517
|
* Authenticate the WebSocket connection
|
|
@@ -1517,7 +1561,16 @@ var RebaseWebSocketClient = class {
|
|
|
1517
1561
|
});
|
|
1518
1562
|
}
|
|
1519
1563
|
}
|
|
1520
|
-
|
|
1564
|
+
/**
|
|
1565
|
+
* Drop the socket.
|
|
1566
|
+
*
|
|
1567
|
+
* `permanent` distinguishes the two callers. Signing out drops the socket
|
|
1568
|
+
* but the client stays usable — a later subscribe should reconnect
|
|
1569
|
+
* anonymously. `client.close()` is the caller saying they are done, and
|
|
1570
|
+
* must not be undone by a stray queued frame.
|
|
1571
|
+
*/
|
|
1572
|
+
disconnect(permanent = false) {
|
|
1573
|
+
if (permanent) this.closedByCaller = true;
|
|
1521
1574
|
this.isAuthenticated = false;
|
|
1522
1575
|
this.authPromise = null;
|
|
1523
1576
|
if (this.reconnectTimeout) {
|
|
@@ -1906,18 +1959,21 @@ var RebaseWebSocketClient = class {
|
|
|
1906
1959
|
sendMessage(message) {
|
|
1907
1960
|
const queuedMsg = message;
|
|
1908
1961
|
if (queuedMsg._queuedResolve && queuedMsg._queuedReject) return this.doSendMessage(message, queuedMsg._queuedResolve, queuedMsg._queuedReject);
|
|
1909
|
-
if (!this.isConnected || !this.ws)
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1962
|
+
if (!this.isConnected || !this.ws) {
|
|
1963
|
+
this.ensureConnected();
|
|
1964
|
+
return new Promise((resolve, reject) => {
|
|
1965
|
+
const queueable = message;
|
|
1966
|
+
queueable._queuedResolve = resolve;
|
|
1967
|
+
queueable._queuedReject = reject;
|
|
1968
|
+
this.messageQueue.push(message);
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1915
1971
|
return new Promise((resolve, reject) => {
|
|
1916
1972
|
this.doSendMessage(message, resolve, reject);
|
|
1917
1973
|
});
|
|
1918
1974
|
}
|
|
1919
1975
|
async doSendMessage(message, resolve, reject) {
|
|
1920
|
-
if (message.type !== "AUTHENTICATE" && this.getAuthToken && !this.isAuthenticated) try {
|
|
1976
|
+
if (message.type !== "AUTHENTICATE" && !CHANNEL_MESSAGE_TYPES.has(message.type) && this.getAuthToken && !this.isAuthenticated) try {
|
|
1921
1977
|
await this.ensureAuthenticated();
|
|
1922
1978
|
} catch (error) {
|
|
1923
1979
|
reject(new RebaseApiError$1(error instanceof Error ? error.message : "Authentication required"));
|
|
@@ -1925,17 +1981,7 @@ var RebaseWebSocketClient = class {
|
|
|
1925
1981
|
}
|
|
1926
1982
|
const requestId = message.requestId || `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
1927
1983
|
message.requestId = requestId;
|
|
1928
|
-
const expectsResponse = !
|
|
1929
|
-
"subscribe_collection",
|
|
1930
|
-
"subscribe_one",
|
|
1931
|
-
"unsubscribe",
|
|
1932
|
-
"join_channel",
|
|
1933
|
-
"leave_channel",
|
|
1934
|
-
"broadcast",
|
|
1935
|
-
"presence_track",
|
|
1936
|
-
"presence_untrack",
|
|
1937
|
-
"presence_state"
|
|
1938
|
-
].includes(message.type);
|
|
1984
|
+
const expectsResponse = !(message.type === "subscribe_collection" || message.type === "subscribe_one" || message.type === "unsubscribe" || CHANNEL_MESSAGE_TYPES.has(message.type));
|
|
1939
1985
|
if (expectsResponse && !this.pendingRequests.has(requestId)) {
|
|
1940
1986
|
const timeoutHandle = setTimeout(() => {
|
|
1941
1987
|
if (this.pendingRequests.has(requestId)) {
|
|
@@ -2164,6 +2210,7 @@ var RebaseWebSocketClient = class {
|
|
|
2164
2210
|
});
|
|
2165
2211
|
}
|
|
2166
2212
|
listenCollection(props, onUpdate, onError) {
|
|
2213
|
+
this.ensureConnected();
|
|
2167
2214
|
const subscriptionKey = this.createCollectionSubscriptionKey(props);
|
|
2168
2215
|
const callbackId = `callback_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
2169
2216
|
const existingSubscription = this.collectionSubscriptions.get(subscriptionKey);
|
|
@@ -2225,6 +2272,7 @@ var RebaseWebSocketClient = class {
|
|
|
2225
2272
|
};
|
|
2226
2273
|
}
|
|
2227
2274
|
listenOne(props, onUpdate, onError) {
|
|
2275
|
+
this.ensureConnected();
|
|
2228
2276
|
const subscriptionKey = this.createSingleSubscriptionKey(props);
|
|
2229
2277
|
const callbackId = `callback_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
2230
2278
|
const existingSubscription = this.singleSubscriptions.get(subscriptionKey);
|
|
@@ -2742,7 +2790,7 @@ function createRebaseClient(options) {
|
|
|
2742
2790
|
if (!ws) return;
|
|
2743
2791
|
if (event === "SIGNED_OUT") ws.disconnect();
|
|
2744
2792
|
else if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED") {
|
|
2745
|
-
if (session?.accessToken) ws.authenticate(session.accessToken).catch(console.warn);
|
|
2793
|
+
if (session?.accessToken && ws.hasSocket) ws.authenticate(session.accessToken).catch(console.warn);
|
|
2746
2794
|
}
|
|
2747
2795
|
});
|
|
2748
2796
|
}
|
|
@@ -2857,7 +2905,7 @@ channel: (name) => {
|
|
|
2857
2905
|
close: () => {
|
|
2858
2906
|
for (const channel of realtimeChannels.values()) channel.leave();
|
|
2859
2907
|
realtimeChannels.clear();
|
|
2860
|
-
ws?.disconnect();
|
|
2908
|
+
ws?.disconnect(true);
|
|
2861
2909
|
},
|
|
2862
2910
|
setToken: transport.setToken,
|
|
2863
2911
|
setAuthTokenGetter: transport.setAuthTokenGetter,
|