@rebasepro/client 0.9.1-canary.ed943fa → 0.9.1-canary.f2f61da

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.d.ts CHANGED
@@ -27,8 +27,6 @@ export type { CreateBackupsOptions } from "./backups";
27
27
  export type { ApiKeyMasked, ApiKeyPermission, ApiKeyWithSecret, CreateApiKeyRequest, CreateApiKeysOptions, UpdateApiKeyRequest } from "./api-keys";
28
28
  export type { FunctionInvokeOptions, FunctionsClient } from "./functions";
29
29
  export { RebaseWebSocketClient } from "./websocket";
30
- export { RebaseRealtimeChannel } from "./realtime-channel";
31
- export type { PresenceState, PresenceDiff, BroadcastEvent, ChannelTransport } from "./realtime-channel";
32
30
  export interface CreateRebaseClientOptions extends RebaseClientConfig {
33
31
  auth?: CreateAuthOptions;
34
32
  admin?: CreateAdminOptions;
package/dist/index.es.js CHANGED
@@ -1397,11 +1397,9 @@ var ClientStorageSourceRegistry = class ClientStorageSourceRegistry {
1397
1397
  function extractMessageError(message) {
1398
1398
  const payload = message.payload;
1399
1399
  const errPayload = payload?.error;
1400
- const errorMessage = typeof errPayload === "object" ? errPayload.message : payload?.message || (typeof errPayload === "string" ? errPayload : void 0) || message.error || "Unknown error";
1401
- const errorCode = typeof errPayload === "object" ? errPayload.code : payload?.code;
1402
1400
  return {
1403
- errorMessage: typeof errorMessage === "string" ? errorMessage : errorMessage == null ? "Unknown error" : JSON.stringify(errorMessage),
1404
- errorCode
1401
+ errorMessage: typeof errPayload === "object" ? errPayload.message : payload?.message || (typeof errPayload === "string" ? errPayload : void 0) || message.error || "Unknown error",
1402
+ errorCode: typeof errPayload === "object" ? errPayload.code : payload?.code
1405
1403
  };
1406
1404
  }
1407
1405
  /**
@@ -1419,23 +1417,6 @@ var RebaseWebSocketClient = class {
1419
1417
  getAuthToken;
1420
1418
  subscriptions = /* @__PURE__ */ new Map();
1421
1419
  listeners = /* @__PURE__ */ new Map();
1422
- /** Channel-name → handlers, for broadcast and presence frames. */
1423
- channelHandlers = /* @__PURE__ */ new Map();
1424
- /** Subscribe to broadcast/presence frames for one channel. */
1425
- onChannelMessage(channel, handler) {
1426
- if (!this.channelHandlers.has(channel)) this.channelHandlers.set(channel, /* @__PURE__ */ new Set());
1427
- this.channelHandlers.get(channel).add(handler);
1428
- return () => {
1429
- const handlers = this.channelHandlers.get(channel);
1430
- if (!handlers) return;
1431
- handlers.delete(handler);
1432
- if (handlers.size === 0) this.channelHandlers.delete(channel);
1433
- };
1434
- }
1435
- /** Notified after the socket comes back, so channels can re-join. */
1436
- onReconnect(handler) {
1437
- return this.on("reconnect", handler);
1438
- }
1439
1420
  on(event, cb) {
1440
1421
  if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
1441
1422
  this.listeners.get(event).add(cb);
@@ -1454,7 +1435,6 @@ var RebaseWebSocketClient = class {
1454
1435
  isConnected = false;
1455
1436
  messageQueue = [];
1456
1437
  requestTimeoutMs = 3e4;
1457
- subscriptionTimeoutMs = 3e4;
1458
1438
  reconnectTimeout = null;
1459
1439
  isAuthenticated = false;
1460
1440
  authPromise = null;
@@ -1560,7 +1540,6 @@ var RebaseWebSocketClient = class {
1560
1540
  this.emit(wasReconnect ? "reconnect" : "connect");
1561
1541
  this.processMessageQueue();
1562
1542
  if (wasReconnect) this.resubscribeAll();
1563
- this.armPendingSubscribeWatchdogs();
1564
1543
  };
1565
1544
  this.ws.onmessage = (event) => {
1566
1545
  try {
@@ -1575,7 +1554,6 @@ var RebaseWebSocketClient = class {
1575
1554
  this.isConnected = false;
1576
1555
  this.isAuthenticated = false;
1577
1556
  this.authPromise = null;
1578
- this.suspendSubscribeWatchdogs();
1579
1557
  this.emit("disconnect");
1580
1558
  for (const [reqId, request] of this.pendingRequests.entries()) {
1581
1559
  if (reqId.startsWith("auth_")) request.reject(/* @__PURE__ */ new Error("Connection closed during authentication"));
@@ -1607,7 +1585,6 @@ var RebaseWebSocketClient = class {
1607
1585
  attemptReconnect() {
1608
1586
  if (this.reconnectAttempts >= this.maxReconnectAttempts) {
1609
1587
  console.error("Max reconnection attempts reached");
1610
- this.failAllPendingSubscriptions(new RebaseApiError$1("Connection lost", { code: "CONNECTION_LOST" }));
1611
1588
  return;
1612
1589
  }
1613
1590
  this.reconnectAttempts++;
@@ -1662,18 +1639,29 @@ var RebaseWebSocketClient = class {
1662
1639
  subscription.backendSubscriptionId = newBackendId;
1663
1640
  backendKeyMap.delete(oldBackendId);
1664
1641
  backendKeyMap.set(newBackendId, subscriptionKey);
1665
- if (messageType === "subscribe_collection") this.sendCollectionSubscribe(subscriptionKey);
1666
- else this.sendEntitySubscribe(subscriptionKey);
1667
- return;
1642
+ this.sendMessage({
1643
+ type: messageType,
1644
+ payload: {
1645
+ ...subscription.props,
1646
+ subscriptionId: newBackendId
1647
+ }
1648
+ }).catch((error) => {
1649
+ console.error(`[WS] Failed to re-subscribe ${idPrefix} after auth refresh:`, subscriptionKey, error);
1650
+ subscription.callbacks.forEach((callback) => {
1651
+ if (callback.onError) callback.onError(error);
1652
+ });
1653
+ });
1654
+ } else {
1655
+ const { errorMessage, errorCode } = extractMessageError(message);
1656
+ const error = new RebaseApiError$1(errorMessage, { code: errorCode });
1657
+ subscription.callbacks.forEach((callback) => {
1658
+ if (callback.onError) callback.onError(error);
1659
+ });
1668
1660
  }
1669
- const { errorMessage, errorCode } = extractMessageError(message);
1670
- const error = new RebaseApiError$1(errorMessage, { code: errorCode });
1671
- if (messageType === "subscribe_collection") this.failCollectionSubscription(subscriptionKey, error);
1672
- else this.failEntitySubscription(subscriptionKey, error);
1673
1661
  }).catch((err) => {
1674
- const error = err instanceof Error ? err : new Error(String(err));
1675
- if (messageType === "subscribe_collection") this.failCollectionSubscription(subscriptionKey, error);
1676
- else this.failEntitySubscription(subscriptionKey, error);
1662
+ subscription.callbacks.forEach((callback) => {
1663
+ if (callback.onError) callback.onError(err);
1664
+ });
1677
1665
  });
1678
1666
  }
1679
1667
  handleWebSocketMessage(message) {
@@ -1702,15 +1690,6 @@ var RebaseWebSocketClient = class {
1702
1690
  }
1703
1691
  return;
1704
1692
  }
1705
- if (typeof message.channel === "string" && (type === "broadcast" || type === "presence_state" || type === "presence_diff")) {
1706
- const handlers = this.channelHandlers.get(message.channel);
1707
- if (handlers) for (const handler of [...handlers]) try {
1708
- handler(message);
1709
- } catch (error) {
1710
- console.error("Error in channel handler:", error);
1711
- }
1712
- return;
1713
- }
1714
1693
  if (subscriptionId && type === "collection_update") {
1715
1694
  const subscriptionKey = this.backendToCollectionKey.get(subscriptionId);
1716
1695
  if (subscriptionKey) {
@@ -1723,9 +1702,6 @@ var RebaseWebSocketClient = class {
1723
1702
  collectionSub.latestData = rows;
1724
1703
  collectionSub.lastUpdated = Date.now();
1725
1704
  collectionSub.isInitialDataReceived = true;
1726
- if (collectionSub.subscribeTimeout) clearTimeout(collectionSub.subscribeTimeout);
1727
- collectionSub.subscribeTimeout = void 0;
1728
- collectionSub.subscribeInFlight = false;
1729
1705
  collectionSub.callbacks.forEach((callback) => {
1730
1706
  try {
1731
1707
  callback.onUpdate(rows);
@@ -1781,9 +1757,6 @@ var RebaseWebSocketClient = class {
1781
1757
  entitySub.latestData = row;
1782
1758
  entitySub.lastUpdated = Date.now();
1783
1759
  entitySub.isInitialDataReceived = true;
1784
- if (entitySub.subscribeTimeout) clearTimeout(entitySub.subscribeTimeout);
1785
- entitySub.subscribeTimeout = void 0;
1786
- entitySub.subscribeInFlight = false;
1787
1760
  entitySub.callbacks.forEach((callback) => {
1788
1761
  try {
1789
1762
  callback.onUpdate(row);
@@ -1805,9 +1778,6 @@ var RebaseWebSocketClient = class {
1805
1778
  this.resubscribeAfterAuthRefresh(message, collectionSub, collectionKey, "collection", this.backendToCollectionKey, "subscribe_collection");
1806
1779
  return;
1807
1780
  }
1808
- if (collectionSub.subscribeTimeout) clearTimeout(collectionSub.subscribeTimeout);
1809
- collectionSub.subscribeTimeout = void 0;
1810
- collectionSub.subscribeInFlight = false;
1811
1781
  const { errorMessage, errorCode } = extractMessageError(message);
1812
1782
  const error = new RebaseApiError$1(errorMessage, { code: errorCode });
1813
1783
  collectionSub.callbacks.forEach((callback) => {
@@ -1824,9 +1794,6 @@ var RebaseWebSocketClient = class {
1824
1794
  this.resubscribeAfterAuthRefresh(message, entitySub, entityKey, "row", this.backendToEntityKey, "subscribe_one");
1825
1795
  return;
1826
1796
  }
1827
- if (entitySub.subscribeTimeout) clearTimeout(entitySub.subscribeTimeout);
1828
- entitySub.subscribeTimeout = void 0;
1829
- entitySub.subscribeInFlight = false;
1830
1797
  const { errorMessage, errorCode } = extractMessageError(message);
1831
1798
  const error = new RebaseApiError$1(errorMessage, { code: errorCode });
1832
1799
  entitySub.callbacks.forEach((callback) => {
@@ -1899,10 +1866,6 @@ var RebaseWebSocketClient = class {
1899
1866
  throw error;
1900
1867
  }
1901
1868
  }
1902
- /**
1903
- * Public because `RebaseRealtimeChannel` sends channel frames through it.
1904
- * Not part of the stable surface — prefer `client.realtime.channel(name)`.
1905
- */
1906
1869
  sendMessage(message) {
1907
1870
  const queuedMsg = message;
1908
1871
  if (queuedMsg._queuedResolve && queuedMsg._queuedReject) return this.doSendMessage(message, queuedMsg._queuedResolve, queuedMsg._queuedReject);
@@ -2179,12 +2142,9 @@ var RebaseWebSocketClient = class {
2179
2142
  console.error("Error in collection subscription callback:", error);
2180
2143
  if (onError) onError(error instanceof Error ? error : new Error(String(error)));
2181
2144
  }
2182
- else if (!existingSubscription.subscribeInFlight) this.sendCollectionSubscribe(subscriptionKey);
2183
2145
  return () => {
2184
2146
  callbackMap.delete(callbackId);
2185
2147
  if (callbackMap.size === 0) {
2186
- if (this.collectionSubscriptions.get(subscriptionKey) !== existingSubscription) return;
2187
- if (existingSubscription.subscribeTimeout) clearTimeout(existingSubscription.subscribeTimeout);
2188
2148
  this.collectionSubscriptions.delete(subscriptionKey);
2189
2149
  this.backendToCollectionKey.delete(existingSubscription.backendSubscriptionId);
2190
2150
  if (this.isConnected && this.ws) this.sendMessage({
@@ -2206,14 +2166,21 @@ var RebaseWebSocketClient = class {
2206
2166
  props
2207
2167
  });
2208
2168
  this.backendToCollectionKey.set(backendSubscriptionId, subscriptionKey);
2209
- this.sendCollectionSubscribe(subscriptionKey);
2169
+ this.sendMessage({
2170
+ type: "subscribe_collection",
2171
+ payload: {
2172
+ ...props,
2173
+ subscriptionId: backendSubscriptionId
2174
+ }
2175
+ }).catch((error) => {
2176
+ if (onError) onError(error);
2177
+ });
2210
2178
  return () => {
2211
2179
  const subscription = this.collectionSubscriptions.get(subscriptionKey);
2212
2180
  if (subscription) {
2213
2181
  const callbacks = subscription.callbacks;
2214
2182
  callbacks.delete(callbackId);
2215
2183
  if (callbacks.size === 0) {
2216
- if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);
2217
2184
  this.collectionSubscriptions.delete(subscriptionKey);
2218
2185
  this.backendToCollectionKey.delete(subscription.backendSubscriptionId);
2219
2186
  if (this.isConnected && this.ws) this.sendMessage({
@@ -2240,12 +2207,9 @@ var RebaseWebSocketClient = class {
2240
2207
  console.error("Error in row subscription callback:", error);
2241
2208
  if (onError) onError(error instanceof Error ? error : new Error(String(error)));
2242
2209
  }
2243
- else if (!existingSubscription.subscribeInFlight) this.sendEntitySubscribe(subscriptionKey);
2244
2210
  return () => {
2245
2211
  callbackMap.delete(callbackId);
2246
2212
  if (callbackMap.size === 0) {
2247
- if (this.singleSubscriptions.get(subscriptionKey) !== existingSubscription) return;
2248
- if (existingSubscription.subscribeTimeout) clearTimeout(existingSubscription.subscribeTimeout);
2249
2213
  this.singleSubscriptions.delete(subscriptionKey);
2250
2214
  this.backendToEntityKey.delete(existingSubscription.backendSubscriptionId);
2251
2215
  if (this.isConnected && this.ws) this.sendMessage({
@@ -2267,7 +2231,15 @@ var RebaseWebSocketClient = class {
2267
2231
  props
2268
2232
  });
2269
2233
  this.backendToEntityKey.set(backendSubscriptionId, subscriptionKey);
2270
- this.sendEntitySubscribe(subscriptionKey);
2234
+ this.sendMessage({
2235
+ type: "subscribe_one",
2236
+ payload: {
2237
+ ...props,
2238
+ subscriptionId: backendSubscriptionId
2239
+ }
2240
+ }).catch((error) => {
2241
+ if (onError) onError(error);
2242
+ });
2271
2243
  return () => {
2272
2244
  const subscription = this.singleSubscriptions.get(subscriptionKey);
2273
2245
  if (subscription) {
@@ -2285,157 +2257,6 @@ var RebaseWebSocketClient = class {
2285
2257
  };
2286
2258
  }
2287
2259
  /**
2288
- * Send a `subscribe_collection` for an already-registered subscription and
2289
- * arm its watchdog.
2290
- *
2291
- * Every path that registers a collection subscription goes through here, so
2292
- * that a subscribe which never lands — a rejected send, or a server that
2293
- * never answers — always ends up in `failCollectionSubscription` rather than
2294
- * leaving the entry parked with `isInitialDataReceived === false` forever.
2295
- */
2296
- sendCollectionSubscribe(subscriptionKey) {
2297
- const subscription = this.collectionSubscriptions.get(subscriptionKey);
2298
- if (!subscription) return;
2299
- const backendSubscriptionId = subscription.backendSubscriptionId;
2300
- subscription.subscribeInFlight = true;
2301
- if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);
2302
- subscription.subscribeTimeout = void 0;
2303
- if (this.isConnected) this.sendCollectionSubscribeWatchdog(subscriptionKey);
2304
- this.sendMessage({
2305
- type: "subscribe_collection",
2306
- payload: {
2307
- ...subscription.props,
2308
- subscriptionId: backendSubscriptionId
2309
- }
2310
- }).catch((error) => {
2311
- const current = this.collectionSubscriptions.get(subscriptionKey);
2312
- if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;
2313
- this.failCollectionSubscription(subscriptionKey, error instanceof Error ? error : new Error(String(error)));
2314
- });
2315
- }
2316
- /** The `listenOne` counterpart of {@link sendCollectionSubscribe}. */
2317
- sendEntitySubscribe(subscriptionKey) {
2318
- const subscription = this.singleSubscriptions.get(subscriptionKey);
2319
- if (!subscription) return;
2320
- const backendSubscriptionId = subscription.backendSubscriptionId;
2321
- subscription.subscribeInFlight = true;
2322
- if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);
2323
- subscription.subscribeTimeout = void 0;
2324
- if (this.isConnected) this.sendEntitySubscribeWatchdog(subscriptionKey);
2325
- this.sendMessage({
2326
- type: "subscribe_one",
2327
- payload: {
2328
- ...subscription.props,
2329
- subscriptionId: backendSubscriptionId
2330
- }
2331
- }).catch((error) => {
2332
- const current = this.singleSubscriptions.get(subscriptionKey);
2333
- if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;
2334
- this.failEntitySubscription(subscriptionKey, error instanceof Error ? error : new Error(String(error)));
2335
- });
2336
- }
2337
- /**
2338
- * Report a subscribe failure to every listener and drop the registration.
2339
- *
2340
- * Dropping it is the point: the callbacks stay live (their components are
2341
- * still mounted and have been told), but the next `listenCollection` for
2342
- * these params finds no entry and issues a fresh subscribe instead of
2343
- * silently attaching to a dead one.
2344
- */
2345
- failCollectionSubscription(subscriptionKey, error) {
2346
- const subscription = this.collectionSubscriptions.get(subscriptionKey);
2347
- if (!subscription) return;
2348
- if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);
2349
- subscription.subscribeInFlight = false;
2350
- this.collectionSubscriptions.delete(subscriptionKey);
2351
- this.backendToCollectionKey.delete(subscription.backendSubscriptionId);
2352
- subscription.callbacks.forEach((callback) => {
2353
- if (callback.onError) try {
2354
- callback.onError(error);
2355
- } catch (callbackError) {
2356
- console.error("Error in collection subscription error callback:", callbackError);
2357
- }
2358
- });
2359
- }
2360
- /** The `listenOne` counterpart of {@link failCollectionSubscription}. */
2361
- failEntitySubscription(subscriptionKey, error) {
2362
- const subscription = this.singleSubscriptions.get(subscriptionKey);
2363
- if (!subscription) return;
2364
- if (subscription.subscribeTimeout) clearTimeout(subscription.subscribeTimeout);
2365
- subscription.subscribeInFlight = false;
2366
- this.singleSubscriptions.delete(subscriptionKey);
2367
- this.backendToEntityKey.delete(subscription.backendSubscriptionId);
2368
- subscription.callbacks.forEach((callback) => {
2369
- if (callback.onError) try {
2370
- callback.onError(error);
2371
- } catch (callbackError) {
2372
- console.error("Error in row subscription error callback:", callbackError);
2373
- }
2374
- });
2375
- }
2376
- /**
2377
- * Stop the watchdogs without failing anything — used when the socket drops,
2378
- * since the reconnect path re-subscribes everything anyway and a watchdog
2379
- * firing mid-reconnect would tear down healthy subscriptions.
2380
- */
2381
- suspendSubscribeWatchdogs() {
2382
- for (const sub of this.collectionSubscriptions.values()) {
2383
- if (sub.subscribeTimeout) clearTimeout(sub.subscribeTimeout);
2384
- sub.subscribeTimeout = void 0;
2385
- sub.subscribeInFlight = false;
2386
- }
2387
- for (const sub of this.singleSubscriptions.values()) {
2388
- if (sub.subscribeTimeout) clearTimeout(sub.subscribeTimeout);
2389
- sub.subscribeTimeout = void 0;
2390
- sub.subscribeInFlight = false;
2391
- }
2392
- }
2393
- /**
2394
- * Arm watchdogs for subscribes that were requested while offline and have
2395
- * just been flushed to the socket. Their timers were deliberately not set at
2396
- * request time, so without this they would have no timeout at all.
2397
- */
2398
- armPendingSubscribeWatchdogs() {
2399
- for (const [key, sub] of this.collectionSubscriptions.entries()) if (sub.subscribeInFlight && !sub.subscribeTimeout) this.sendCollectionSubscribeWatchdog(key);
2400
- for (const [key, sub] of this.singleSubscriptions.entries()) if (sub.subscribeInFlight && !sub.subscribeTimeout) this.sendEntitySubscribeWatchdog(key);
2401
- }
2402
- sendCollectionSubscribeWatchdog(subscriptionKey) {
2403
- const subscription = this.collectionSubscriptions.get(subscriptionKey);
2404
- if (!subscription) return;
2405
- const backendSubscriptionId = subscription.backendSubscriptionId;
2406
- subscription.subscribeTimeout = setTimeout(() => {
2407
- const current = this.collectionSubscriptions.get(subscriptionKey);
2408
- if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;
2409
- if (!current.subscribeInFlight) return;
2410
- this.failCollectionSubscription(subscriptionKey, new RebaseApiError$1("Subscription timed out", { code: "SUBSCRIPTION_TIMEOUT" }));
2411
- }, this.subscriptionTimeoutMs);
2412
- }
2413
- sendEntitySubscribeWatchdog(subscriptionKey) {
2414
- const subscription = this.singleSubscriptions.get(subscriptionKey);
2415
- if (!subscription) return;
2416
- const backendSubscriptionId = subscription.backendSubscriptionId;
2417
- subscription.subscribeTimeout = setTimeout(() => {
2418
- const current = this.singleSubscriptions.get(subscriptionKey);
2419
- if (!current || current.backendSubscriptionId !== backendSubscriptionId) return;
2420
- if (!current.subscribeInFlight) return;
2421
- this.failEntitySubscription(subscriptionKey, new RebaseApiError$1("Subscription timed out", { code: "SUBSCRIPTION_TIMEOUT" }));
2422
- }, this.subscriptionTimeoutMs);
2423
- }
2424
- /**
2425
- * Fail every subscription that never received data. Called when reconnection
2426
- * is given up on, so views surface an error instead of spinning forever.
2427
- */
2428
- failAllPendingSubscriptions(error) {
2429
- for (const key of [...this.collectionSubscriptions.keys()]) {
2430
- const sub = this.collectionSubscriptions.get(key);
2431
- if (sub && !sub.isInitialDataReceived) this.failCollectionSubscription(key, error);
2432
- }
2433
- for (const key of [...this.singleSubscriptions.keys()]) {
2434
- const sub = this.singleSubscriptions.get(key);
2435
- if (sub && !sub.isInitialDataReceived) this.failEntitySubscription(key, error);
2436
- }
2437
- }
2438
- /**
2439
2260
  * Re-send all active subscriptions to the backend after a reconnect.
2440
2261
  * The server wipes subscription state when a client disconnects, so
2441
2262
  * we need to re-register everything to resume receiving updates.
@@ -2448,7 +2269,15 @@ var RebaseWebSocketClient = class {
2448
2269
  sub.backendSubscriptionId = newBackendId;
2449
2270
  this.backendToCollectionKey.delete(oldBackendId);
2450
2271
  this.backendToCollectionKey.set(newBackendId, key);
2451
- this.sendCollectionSubscribe(key);
2272
+ this.sendMessage({
2273
+ type: "subscribe_collection",
2274
+ payload: {
2275
+ ...sub.props,
2276
+ subscriptionId: newBackendId
2277
+ }
2278
+ }).catch((error) => {
2279
+ console.error("[WS] Failed to re-subscribe collection:", key, error);
2280
+ });
2452
2281
  }
2453
2282
  for (const [key, sub] of this.singleSubscriptions.entries()) {
2454
2283
  const oldBackendId = sub.backendSubscriptionId;
@@ -2456,7 +2285,15 @@ var RebaseWebSocketClient = class {
2456
2285
  sub.backendSubscriptionId = newBackendId;
2457
2286
  this.backendToEntityKey.delete(oldBackendId);
2458
2287
  this.backendToEntityKey.set(newBackendId, key);
2459
- this.sendEntitySubscribe(key);
2288
+ this.sendMessage({
2289
+ type: "subscribe_one",
2290
+ payload: {
2291
+ ...sub.props,
2292
+ subscriptionId: newBackendId
2293
+ }
2294
+ }).catch((error) => {
2295
+ console.error("[WS] Failed to re-subscribe row:", key, error);
2296
+ });
2460
2297
  }
2461
2298
  }
2462
2299
  createCollectionSubscriptionKey(props) {
@@ -2483,190 +2320,6 @@ var RebaseWebSocketClient = class {
2483
2320
  }
2484
2321
  };
2485
2322
  //#endregion
2486
- //#region src/realtime-channel.ts
2487
- /**
2488
- * Re-send presence comfortably inside the server's 30s expiry.
2489
- *
2490
- * Two-thirds of the window: one lost heartbeat still leaves time for the next
2491
- * before the entry is reaped, so a single dropped frame is not a disappearance.
2492
- */
2493
- var PRESENCE_HEARTBEAT_MS = 2e4;
2494
- var RebaseRealtimeChannel = class {
2495
- name;
2496
- transport;
2497
- presenceHandlers = /* @__PURE__ */ new Set();
2498
- broadcastHandlers = /* @__PURE__ */ new Set();
2499
- unsubscribers = [];
2500
- /** Last known roster, kept so handlers always get a full picture. */
2501
- presences = {};
2502
- /** What this client last tracked, replayed on reconnect and heartbeat. */
2503
- trackedState = null;
2504
- heartbeat = null;
2505
- joined = false;
2506
- constructor(name, transport) {
2507
- this.name = name;
2508
- this.transport = transport;
2509
- }
2510
- /**
2511
- * Join the channel and ask for the current roster.
2512
- *
2513
- * Called automatically by `track`, `broadcast`, `onPresence` and
2514
- * `onBroadcast`; calling it directly is only needed to start receiving
2515
- * before there is anything to send.
2516
- */
2517
- async join() {
2518
- if (this.joined) return;
2519
- this.joined = true;
2520
- this.unsubscribers.push(this.transport.onChannelMessage(this.name, (message) => this.handle(message)));
2521
- this.unsubscribers.push(this.transport.onReconnect(() => {
2522
- this.rejoin();
2523
- }));
2524
- await this.transport.sendMessage({
2525
- type: "join_channel",
2526
- channel: this.name
2527
- });
2528
- await this.transport.sendMessage({
2529
- type: "presence_state",
2530
- channel: this.name
2531
- });
2532
- }
2533
- async rejoin() {
2534
- try {
2535
- await this.transport.sendMessage({
2536
- type: "join_channel",
2537
- channel: this.name
2538
- });
2539
- await this.transport.sendMessage({
2540
- type: "presence_state",
2541
- channel: this.name
2542
- });
2543
- if (this.trackedState) await this.transport.sendMessage({
2544
- type: "presence_track",
2545
- channel: this.name,
2546
- state: this.trackedState
2547
- });
2548
- } catch {}
2549
- }
2550
- /**
2551
- * Publish this client's presence state, and keep publishing it.
2552
- *
2553
- * Calling `track` again replaces the state (and restarts the heartbeat),
2554
- * which is how you update e.g. a cursor position.
2555
- */
2556
- async track(state) {
2557
- await this.join();
2558
- this.trackedState = state;
2559
- await this.transport.sendMessage({
2560
- type: "presence_track",
2561
- channel: this.name,
2562
- state
2563
- });
2564
- if (!this.heartbeat) {
2565
- this.heartbeat = setInterval(() => {
2566
- if (!this.trackedState) return;
2567
- this.transport.sendMessage({
2568
- type: "presence_track",
2569
- channel: this.name,
2570
- state: this.trackedState
2571
- }).catch(() => {});
2572
- }, PRESENCE_HEARTBEAT_MS);
2573
- this.heartbeat.unref?.();
2574
- }
2575
- }
2576
- /** Stop publishing presence, without leaving the channel. */
2577
- async untrack() {
2578
- this.stopHeartbeat();
2579
- this.trackedState = null;
2580
- if (this.joined) await this.transport.sendMessage({
2581
- type: "presence_untrack",
2582
- channel: this.name
2583
- });
2584
- }
2585
- /**
2586
- * Observe the roster. The handler fires immediately with what is already
2587
- * known, then on every change.
2588
- */
2589
- onPresence(handler) {
2590
- this.presenceHandlers.add(handler);
2591
- this.join();
2592
- if (Object.keys(this.presences).length > 0) handler({ ...this.presences });
2593
- return () => this.presenceHandlers.delete(handler);
2594
- }
2595
- /** Send a broadcast. The sender does not receive its own message. */
2596
- async broadcast(event, payload) {
2597
- await this.join();
2598
- await this.transport.sendMessage({
2599
- type: "broadcast",
2600
- channel: this.name,
2601
- event,
2602
- payload
2603
- });
2604
- }
2605
- onBroadcast(eventOrHandler, maybeHandler) {
2606
- const wrapped = typeof eventOrHandler === "string" ? (e) => {
2607
- if (e.event === eventOrHandler) maybeHandler(e.payload);
2608
- } : eventOrHandler;
2609
- this.broadcastHandlers.add(wrapped);
2610
- this.join();
2611
- return () => this.broadcastHandlers.delete(wrapped);
2612
- }
2613
- /** Leave the channel and release every listener and timer. */
2614
- async leave() {
2615
- this.stopHeartbeat();
2616
- this.trackedState = null;
2617
- this.presences = {};
2618
- this.presenceHandlers.clear();
2619
- this.broadcastHandlers.clear();
2620
- for (const off of this.unsubscribers) off();
2621
- this.unsubscribers = [];
2622
- if (this.joined) {
2623
- this.joined = false;
2624
- await this.transport.sendMessage({
2625
- type: "leave_channel",
2626
- channel: this.name
2627
- });
2628
- }
2629
- }
2630
- stopHeartbeat() {
2631
- if (this.heartbeat) {
2632
- clearInterval(this.heartbeat);
2633
- this.heartbeat = null;
2634
- }
2635
- }
2636
- /** Fold an incoming frame into the roster and fan it out. */
2637
- handle(message) {
2638
- switch (message.type) {
2639
- case "presence_state":
2640
- this.presences = message.presences ?? {};
2641
- this.emitPresence();
2642
- break;
2643
- case "presence_diff": {
2644
- const joins = message.joins ?? {};
2645
- const leaves = message.leaves ?? {};
2646
- for (const [id, state] of Object.entries(joins)) this.presences[id] = state;
2647
- for (const id of Object.keys(leaves)) delete this.presences[id];
2648
- this.emitPresence({
2649
- joins,
2650
- leaves
2651
- });
2652
- break;
2653
- }
2654
- case "broadcast": {
2655
- const event = {
2656
- event: message.event,
2657
- payload: message.payload
2658
- };
2659
- for (const handler of this.broadcastHandlers) handler(event);
2660
- break;
2661
- }
2662
- }
2663
- }
2664
- emitPresence(diff) {
2665
- const snapshot = { ...this.presences };
2666
- for (const handler of this.presenceHandlers) handler(snapshot, diff);
2667
- }
2668
- };
2669
- //#endregion
2670
2323
  //#region src/index.ts
2671
2324
  /**
2672
2325
  * Derive a WebSocket URL from an HTTP base URL.
@@ -2717,8 +2370,6 @@ function createRebaseClient(options) {
2717
2370
  };
2718
2371
  const resolvedWsUrl = options.realtime !== false ? options.websocketUrl ?? deriveWebSocketUrl(options.baseUrl) : void 0;
2719
2372
  let ws;
2720
- /** One channel object per name — see `realtime.channel`. */
2721
- const realtimeChannels = /* @__PURE__ */ new Map();
2722
2373
  if (resolvedWsUrl) {
2723
2374
  ws = new RebaseWebSocketClient({
2724
2375
  websocketUrl: resolvedWsUrl,
@@ -2829,24 +2480,6 @@ function createRebaseClient(options) {
2829
2480
  createStorageSource,
2830
2481
  fetchStorageSources,
2831
2482
  ws,
2832
- realtime: {
2833
- /**
2834
- * Join a broadcast/presence channel.
2835
- *
2836
- * Repeated calls with the same name return the same channel, so
2837
- * separate components can attach handlers without each opening its
2838
- * own membership — and `leave()` from one would otherwise silently
2839
- * cut off the others.
2840
- */
2841
- channel: (name) => {
2842
- if (!ws) throw new RebaseClientError("Realtime is disabled on this client (realtime: false), so channels are unavailable.");
2843
- let existing = realtimeChannels.get(name);
2844
- if (!existing) {
2845
- existing = new RebaseRealtimeChannel(name, ws);
2846
- realtimeChannels.set(name, existing);
2847
- }
2848
- return existing;
2849
- } },
2850
2483
  /**
2851
2484
  * Release the realtime socket and its reconnect timer.
2852
2485
  *
@@ -2855,8 +2488,6 @@ channel: (name) => {
2855
2488
  * was never started, and safe to call twice.
2856
2489
  */
2857
2490
  close: () => {
2858
- for (const channel of realtimeChannels.values()) channel.leave();
2859
- realtimeChannels.clear();
2860
2491
  ws?.disconnect();
2861
2492
  },
2862
2493
  setToken: transport.setToken,
@@ -2877,6 +2508,6 @@ channel: (name) => {
2877
2508
  };
2878
2509
  }
2879
2510
  //#endregion
2880
- export { QueryBuilder, RebaseApiError, RebaseClientError, RebaseRealtimeChannel, RebaseWebSocketClient, and, cond, createBackups, createCookieStorage, createMemoryStorage, createRebaseClient, or };
2511
+ export { QueryBuilder, RebaseApiError, RebaseClientError, RebaseWebSocketClient, and, cond, createBackups, createCookieStorage, createMemoryStorage, createRebaseClient, or };
2881
2512
 
2882
2513
  //# sourceMappingURL=index.es.js.map