@rebasepro/server 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 CHANGED
@@ -16001,6 +16001,20 @@ function extractMessageError(message) {
16001
16001
  };
16002
16002
  }
16003
16003
  /**
16004
+ * Broadcast and presence frames.
16005
+ *
16006
+ * Fire-and-forget (the server sends no response envelope), and exempt from the
16007
+ * client-side auth gate — a public channel is usable without an account.
16008
+ */
16009
+ var CHANNEL_MESSAGE_TYPES = new Set([
16010
+ "join_channel",
16011
+ "leave_channel",
16012
+ "broadcast",
16013
+ "presence_track",
16014
+ "presence_untrack",
16015
+ "presence_state"
16016
+ ]);
16017
+ /**
16004
16018
  * Low-level realtime WebSocket client.
16005
16019
  *
16006
16020
  * @internal Not a stable app-facing API. `createRebaseClient()` constructs and
@@ -16017,6 +16031,19 @@ var RebaseWebSocketClient = class {
16017
16031
  listeners = /* @__PURE__ */ new Map();
16018
16032
  /** Channel-name → handlers, for broadcast and presence frames. */
16019
16033
  channelHandlers = /* @__PURE__ */ new Map();
16034
+ /** Set by `close()`. Blocks any later operation from silently redialling. */
16035
+ closedByCaller = false;
16036
+ /**
16037
+ * Whether a socket exists at all (open or still opening).
16038
+ *
16039
+ * Lets callers distinguish "authenticate the live socket" from "there is
16040
+ * nothing to authenticate yet", without that question forcing a dial.
16041
+ */
16042
+ get hasSocket() {
16043
+ return this.ws !== null;
16044
+ }
16045
+ /** So the "no WebSocket in this environment" warning is said once, not per call. */
16046
+ warnedNoWebSocket = false;
16020
16047
  /** Subscribe to broadcast/presence frames for one channel. */
16021
16048
  onChannelMessage(channel, handler) {
16022
16049
  if (!this.channelHandlers.has(channel)) this.channelHandlers.set(channel, /* @__PURE__ */ new Set());
@@ -16062,8 +16089,25 @@ var RebaseWebSocketClient = class {
16062
16089
  this.getAuthToken = config.getAuthToken;
16063
16090
  this.onUnauthorized = config.onUnauthorized;
16064
16091
  this.WebSocketConstructor = config.WebSocket || (typeof WebSocket !== "undefined" ? WebSocket : void 0);
16065
- if (!this.WebSocketConstructor) console.warn("WebSocket is not defined in this environment. Realtime subscriptions will not work unless you provide a WebSocket implementation in the config.");
16066
- else this.initWebSocket();
16092
+ }
16093
+ /**
16094
+ * Open the socket if it is not open (or opening) already.
16095
+ *
16096
+ * Idempotent, synchronous, and safe to call on every operation that needs a
16097
+ * live socket — `initWebSocket` already no-ops on an open socket and is
16098
+ * re-entrant, since the reconnect path has always called it.
16099
+ */
16100
+ ensureConnected() {
16101
+ if (this.closedByCaller) return;
16102
+ if (!this.WebSocketConstructor) {
16103
+ if (!this.warnedNoWebSocket) {
16104
+ this.warnedNoWebSocket = true;
16105
+ console.warn("WebSocket is not defined in this environment. Realtime subscriptions will not work unless you provide a WebSocket implementation in the config.");
16106
+ }
16107
+ return;
16108
+ }
16109
+ if (this.ws || this.reconnectTimeout) return;
16110
+ this.initWebSocket();
16067
16111
  }
16068
16112
  /**
16069
16113
  * Authenticate the WebSocket connection
@@ -16113,7 +16157,16 @@ var RebaseWebSocketClient = class {
16113
16157
  });
16114
16158
  }
16115
16159
  }
16116
- disconnect() {
16160
+ /**
16161
+ * Drop the socket.
16162
+ *
16163
+ * `permanent` distinguishes the two callers. Signing out drops the socket
16164
+ * but the client stays usable — a later subscribe should reconnect
16165
+ * anonymously. `client.close()` is the caller saying they are done, and
16166
+ * must not be undone by a stray queued frame.
16167
+ */
16168
+ disconnect(permanent = false) {
16169
+ if (permanent) this.closedByCaller = true;
16117
16170
  this.isAuthenticated = false;
16118
16171
  this.authPromise = null;
16119
16172
  if (this.reconnectTimeout) {
@@ -16502,18 +16555,21 @@ var RebaseWebSocketClient = class {
16502
16555
  sendMessage(message) {
16503
16556
  const queuedMsg = message;
16504
16557
  if (queuedMsg._queuedResolve && queuedMsg._queuedReject) return this.doSendMessage(message, queuedMsg._queuedResolve, queuedMsg._queuedReject);
16505
- if (!this.isConnected || !this.ws) return new Promise((resolve, reject) => {
16506
- const queueable = message;
16507
- queueable._queuedResolve = resolve;
16508
- queueable._queuedReject = reject;
16509
- this.messageQueue.push(message);
16510
- });
16558
+ if (!this.isConnected || !this.ws) {
16559
+ this.ensureConnected();
16560
+ return new Promise((resolve, reject) => {
16561
+ const queueable = message;
16562
+ queueable._queuedResolve = resolve;
16563
+ queueable._queuedReject = reject;
16564
+ this.messageQueue.push(message);
16565
+ });
16566
+ }
16511
16567
  return new Promise((resolve, reject) => {
16512
16568
  this.doSendMessage(message, resolve, reject);
16513
16569
  });
16514
16570
  }
16515
16571
  async doSendMessage(message, resolve, reject) {
16516
- if (message.type !== "AUTHENTICATE" && this.getAuthToken && !this.isAuthenticated) try {
16572
+ if (message.type !== "AUTHENTICATE" && !CHANNEL_MESSAGE_TYPES.has(message.type) && this.getAuthToken && !this.isAuthenticated) try {
16517
16573
  await this.ensureAuthenticated();
16518
16574
  } catch (error) {
16519
16575
  reject(new RebaseApiError(error instanceof Error ? error.message : "Authentication required"));
@@ -16521,17 +16577,7 @@ var RebaseWebSocketClient = class {
16521
16577
  }
16522
16578
  const requestId = message.requestId || `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
16523
16579
  message.requestId = requestId;
16524
- const expectsResponse = ![
16525
- "subscribe_collection",
16526
- "subscribe_one",
16527
- "unsubscribe",
16528
- "join_channel",
16529
- "leave_channel",
16530
- "broadcast",
16531
- "presence_track",
16532
- "presence_untrack",
16533
- "presence_state"
16534
- ].includes(message.type);
16580
+ const expectsResponse = !(message.type === "subscribe_collection" || message.type === "subscribe_one" || message.type === "unsubscribe" || CHANNEL_MESSAGE_TYPES.has(message.type));
16535
16581
  if (expectsResponse && !this.pendingRequests.has(requestId)) {
16536
16582
  const timeoutHandle = setTimeout(() => {
16537
16583
  if (this.pendingRequests.has(requestId)) {
@@ -16760,6 +16806,7 @@ var RebaseWebSocketClient = class {
16760
16806
  });
16761
16807
  }
16762
16808
  listenCollection(props, onUpdate, onError) {
16809
+ this.ensureConnected();
16763
16810
  const subscriptionKey = this.createCollectionSubscriptionKey(props);
16764
16811
  const callbackId = `callback_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
16765
16812
  const existingSubscription = this.collectionSubscriptions.get(subscriptionKey);
@@ -16821,6 +16868,7 @@ var RebaseWebSocketClient = class {
16821
16868
  };
16822
16869
  }
16823
16870
  listenOne(props, onUpdate, onError) {
16871
+ this.ensureConnected();
16824
16872
  const subscriptionKey = this.createSingleSubscriptionKey(props);
16825
16873
  const callbackId = `callback_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
16826
16874
  const existingSubscription = this.singleSubscriptions.get(subscriptionKey);
@@ -17334,7 +17382,7 @@ function createRebaseClient(options) {
17334
17382
  if (!ws) return;
17335
17383
  if (event === "SIGNED_OUT") ws.disconnect();
17336
17384
  else if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED") {
17337
- if (session?.accessToken) ws.authenticate(session.accessToken).catch(console.warn);
17385
+ if (session?.accessToken && ws.hasSocket) ws.authenticate(session.accessToken).catch(console.warn);
17338
17386
  }
17339
17387
  });
17340
17388
  }
@@ -17449,7 +17497,7 @@ channel: (name) => {
17449
17497
  close: () => {
17450
17498
  for (const channel of realtimeChannels.values()) channel.leave();
17451
17499
  realtimeChannels.clear();
17452
- ws?.disconnect();
17500
+ ws?.disconnect(true);
17453
17501
  },
17454
17502
  setToken: transport.setToken,
17455
17503
  setAuthTokenGetter: transport.setAuthTokenGetter,