@liveblocks/react 1.9.2 → 1.9.4

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.js CHANGED
@@ -5,7 +5,7 @@ var _core = require('@liveblocks/core');
5
5
 
6
6
  // src/version.ts
7
7
  var PKG_NAME = "@liveblocks/react";
8
- var PKG_VERSION = "1.9.2";
8
+ var PKG_VERSION = "1.9.4";
9
9
  var PKG_FORMAT = "cjs";
10
10
 
11
11
  // src/ClientSideSuspense.tsx
@@ -41,8 +41,8 @@ var _nanoid = require('nanoid');
41
41
 
42
42
 
43
43
 
44
+ var _shim = require('use-sync-external-store/shim');
44
45
 
45
- var _indexjs = require('use-sync-external-store/shim/index.js');
46
46
 
47
47
  // src/comments/errors.ts
48
48
  var CreateThreadError = class extends Error {
@@ -106,7 +106,7 @@ var RemoveReactionError = class extends Error {
106
106
 
107
107
 
108
108
  // src/comments/lib/use-is-document-visible.ts
109
-
109
+ var _indexjs = require('use-sync-external-store/shim/index.js');
110
110
  function useIsDocumentVisible() {
111
111
  const isVisible = _indexjs.useSyncExternalStore.call(void 0, subscribe, getSnapshot, getSnapshot);
112
112
  return isVisible;
@@ -283,122 +283,17 @@ function useMutate(manager, revalidateCache) {
283
283
  );
284
284
  return mutate;
285
285
  }
286
- function useAutomaticRevalidation(manager, revalidateCache, options = {}) {
287
- const isOnline = useIsOnline();
288
- const isDocumentVisible = useIsDocumentVisible();
289
- const {
290
- revalidateOnFocus = true,
291
- revalidateOnReconnect = true,
292
- refreshInterval = 0
293
- } = options;
294
- _react.useEffect.call(void 0, () => {
295
- let revalidationTimerId;
296
- function scheduleRevalidation() {
297
- if (refreshInterval === 0)
298
- return;
299
- revalidationTimerId = window.setTimeout(() => {
300
- if (isOnline && isDocumentVisible && !manager.getError()) {
301
- void revalidateCache({ shouldDedupe: true }).then(
302
- scheduleRevalidation
303
- );
304
- return;
305
- }
306
- scheduleRevalidation();
307
- }, refreshInterval);
308
- }
309
- scheduleRevalidation();
310
- return () => {
311
- window.clearTimeout(revalidationTimerId);
312
- };
313
- }, [revalidateCache, refreshInterval, isOnline, isDocumentVisible, manager]);
314
- _react.useEffect.call(void 0, () => {
315
- function handleIsOnline() {
316
- if (revalidateOnReconnect && isDocumentVisible) {
317
- void revalidateCache({ shouldDedupe: true });
318
- }
319
- }
320
- window.addEventListener("online", handleIsOnline);
321
- return () => {
322
- window.removeEventListener("online", handleIsOnline);
323
- };
324
- }, [revalidateCache, revalidateOnReconnect, isDocumentVisible]);
325
- _react.useEffect.call(void 0, () => {
326
- function handleVisibilityChange() {
327
- const isVisible = document.visibilityState === "visible";
328
- if (revalidateOnFocus && isVisible && isOnline) {
329
- void revalidateCache({ shouldDedupe: true });
330
- }
331
- }
332
- document.addEventListener("visibilitychange", handleVisibilityChange);
333
- return () => {
334
- document.removeEventListener("visibilitychange", handleVisibilityChange);
335
- };
336
- }, [revalidateCache, revalidateOnFocus, isOnline]);
337
- }
338
286
 
339
287
  // src/comments/CommentsRoom.tsx
340
- var POLLING_INTERVAL_REALTIME = 3e4;
341
- var POLLING_INTERVAL = 5e3;
342
288
  var THREAD_ID_PREFIX = "th";
343
289
  var COMMENT_ID_PREFIX = "cm";
290
+ var POLLING_INTERVAL_REALTIME = 3e4;
291
+ var POLLING_INTERVAL = 5e3;
344
292
  function createCommentsRoom(errorEventSource) {
345
- const manager = createThreadsCacheManager();
346
- const filterOptions = /* @__PURE__ */ new Map();
347
- const cacheStates = /* @__PURE__ */ new Map();
348
- const revalidationManagers = /* @__PURE__ */ new Map();
349
- function createThreadsRevalidationManager(key) {
350
- let request;
351
- let error;
352
- return {
353
- getCache() {
354
- return void 0;
355
- },
356
- setCache(value) {
357
- const cache = new Map(
358
- (_nullishCoalesce(manager.getCache(), () => ( []))).map((thread) => [thread.id, thread])
359
- );
360
- for (const thread of value) {
361
- cache.set(thread.id, thread);
362
- }
363
- setCache(key, {
364
- isLoading: false,
365
- data: value
366
- });
367
- manager.setCache(Array.from(cache.values()));
368
- },
369
- // Request
370
- getRequest() {
371
- return request;
372
- },
373
- setRequest(value) {
374
- request = value;
375
- },
376
- // Error
377
- getError() {
378
- return error;
379
- },
380
- setError(err) {
381
- error = err;
382
- manager.setError(err);
383
- },
384
- // Mutation
385
- getMutation() {
386
- return void 0;
387
- },
388
- setMutation() {
389
- }
390
- };
391
- }
392
- const eventSource = _core.makeEventSource.call(void 0, );
393
- const subscribe2 = eventSource.subscribe;
394
- const getCache = (key) => cacheStates.get(key);
395
- const setCache = (key, value) => {
396
- cacheStates.set(key, value);
397
- };
293
+ const store = createClientCacheStore();
398
294
  const FetcherContext = _react.createContext.call(void 0, null);
399
- const CommentsEventSubscriptionContext = _react.createContext.call(void 0, () => {
400
- });
401
- function getThreads() {
295
+ const RoomManagerContext = _react.createContext.call(void 0, null);
296
+ function getThreads(manager) {
402
297
  const threads = manager.getCache();
403
298
  if (!threads) {
404
299
  throw new Error(
@@ -411,84 +306,118 @@ function createCommentsRoom(errorEventSource) {
411
306
  room,
412
307
  children
413
308
  }) {
414
- const commentsEventSubscribersCountRef = _react.useRef.call(void 0, 0);
415
- const commentsEventDisposerRef = _react.useRef.call(void 0, );
416
- const fetcher = _react.useCallback.call(void 0, async () => {
309
+ const manager = _react.useMemo.call(void 0, () => {
310
+ return createRoomRevalidationManager(room.id, {
311
+ getCache: store.getThreads,
312
+ setCache: store.setThreads
313
+ });
314
+ }, [room.id]);
315
+ const fetcher = React.default.useCallback(async () => {
316
+ const options = manager.getRevalidationManagers().filter(([key]) => manager.getReferenceCount(key) > 0).map(([_, manager2]) => manager2.getOptions());
417
317
  const responses = await Promise.all(
418
- Array.from(filterOptions.values()).map((info) => {
419
- return room.getThreads(info.options);
318
+ options.map(async (option) => {
319
+ return await room.getThreads(option);
420
320
  })
421
321
  );
422
322
  const threads = Array.from(
423
323
  new Map(responses.flat().map((thread) => [thread.id, thread])).values()
424
324
  );
425
325
  return threads;
426
- }, [room]);
326
+ }, [room, manager]);
427
327
  const revalidateCache = useRevalidateCache(manager, fetcher);
428
- const subscribeToCommentEvents = _react.useCallback.call(void 0, () => {
429
- const commentsEventSubscribersCount = commentsEventSubscribersCountRef.current;
430
- if (commentsEventSubscribersCount === 0) {
431
- const unsubscribe = room.events.comments.subscribe(() => {
432
- void revalidateCache({ shouldDedupe: true });
433
- });
434
- commentsEventDisposerRef.current = unsubscribe;
328
+ const status = _shim.useSyncExternalStore.call(void 0,
329
+ room.events.status.subscribe,
330
+ room.getStatus,
331
+ room.getStatus
332
+ );
333
+ const isOnline = useIsOnline();
334
+ const isDocumentVisible = useIsDocumentVisible();
335
+ const refreshInterval = getPollingInterval(
336
+ isOnline,
337
+ isDocumentVisible,
338
+ status === "connected"
339
+ );
340
+ _react.useEffect.call(void 0, () => {
341
+ let revalidationTimerId;
342
+ function scheduleRevalidation() {
343
+ if (refreshInterval === 0)
344
+ return;
345
+ revalidationTimerId = window.setTimeout(() => {
346
+ if (isOnline && isDocumentVisible && !manager.getError() && manager.getTotalReferenceCount() > 0) {
347
+ void revalidateCache({ shouldDedupe: true }).then(
348
+ scheduleRevalidation
349
+ );
350
+ return;
351
+ }
352
+ scheduleRevalidation();
353
+ }, refreshInterval);
435
354
  }
436
- commentsEventSubscribersCountRef.current = commentsEventSubscribersCount + 1;
355
+ scheduleRevalidation();
437
356
  return () => {
438
- commentsEventSubscribersCountRef.current = commentsEventSubscribersCountRef.current - 1;
439
- if (commentsEventSubscribersCountRef.current > 0)
440
- return;
441
- _optionalChain([commentsEventDisposerRef, 'access', _ => _.current, 'optionalCall', _2 => _2()]);
442
- commentsEventDisposerRef.current = void 0;
357
+ window.clearTimeout(revalidationTimerId);
443
358
  };
444
- }, [revalidateCache, room]);
359
+ }, [
360
+ revalidateCache,
361
+ refreshInterval,
362
+ isOnline,
363
+ isDocumentVisible,
364
+ manager
365
+ ]);
445
366
  _react.useEffect.call(void 0, () => {
446
- const unsubscribe = manager.subscribe("cache", (threads) => {
447
- for (const [key, info] of filterOptions.entries()) {
448
- const filtered = threads.filter((thread) => {
449
- const query = info.options.query;
450
- if (!query)
451
- return true;
452
- for (const key2 in query.metadata) {
453
- if (thread.metadata[key2] !== query.metadata[key2]) {
454
- return false;
455
- }
456
- }
457
- return true;
458
- });
459
- setCache(key, {
460
- isLoading: false,
461
- data: filtered
462
- });
463
- }
464
- for (const [key] of cacheStates.entries()) {
465
- if (filterOptions.has(key))
466
- continue;
467
- cacheStates.delete(key);
367
+ function handleIsOnline() {
368
+ if (isDocumentVisible) {
369
+ void revalidateCache({ shouldDedupe: true });
468
370
  }
469
- eventSource.notify(threads);
470
- });
371
+ }
372
+ window.addEventListener("online", handleIsOnline);
471
373
  return () => {
472
- unsubscribe();
374
+ window.removeEventListener("online", handleIsOnline);
473
375
  };
474
- }, []);
376
+ }, [revalidateCache, isDocumentVisible]);
475
377
  _react.useEffect.call(void 0, () => {
476
- const unsubscribe = manager.subscribe("error", (error) => {
477
- for (const state of cacheStates.values()) {
478
- state.error = error;
378
+ function handleVisibilityChange() {
379
+ const isVisible = document.visibilityState === "visible";
380
+ if (isVisible && isOnline) {
381
+ void revalidateCache({ shouldDedupe: true });
479
382
  }
383
+ }
384
+ document.addEventListener("visibilitychange", handleVisibilityChange);
385
+ return () => {
386
+ document.removeEventListener(
387
+ "visibilitychange",
388
+ handleVisibilityChange
389
+ );
390
+ };
391
+ }, [revalidateCache, isOnline]);
392
+ _react.useEffect.call(void 0, () => {
393
+ const unsubscribe = room.events.comments.subscribe(() => {
394
+ void revalidateCache({ shouldDedupe: false });
480
395
  });
481
396
  return () => {
482
397
  unsubscribe();
483
398
  };
484
- }, []);
485
- return /* @__PURE__ */ React.default.createElement(FetcherContext.Provider, { value: fetcher }, /* @__PURE__ */ React.default.createElement(
486
- CommentsEventSubscriptionContext.Provider,
487
- {
488
- value: subscribeToCommentEvents
489
- },
490
- children
491
- ));
399
+ }, [room, revalidateCache]);
400
+ return /* @__PURE__ */ React.default.createElement(FetcherContext.Provider, { value: fetcher }, /* @__PURE__ */ React.default.createElement(RoomManagerContext.Provider, { value: manager }, children));
401
+ }
402
+ function useRoomManager() {
403
+ const manager = _react.useContext.call(void 0, RoomManagerContext);
404
+ if (manager === null) {
405
+ throw new Error("CommentsRoomProvider is missing from the React tree.");
406
+ }
407
+ return manager;
408
+ }
409
+ function getUseThreadsRevalidationManager(options, roomManager) {
410
+ const key = _core.stringify.call(void 0, options);
411
+ const revalidationManager = roomManager.getRevalidationManager(key);
412
+ if (!revalidationManager) {
413
+ const useThreadsRevalidationManager = createUseThreadsRevalidationManager(
414
+ options,
415
+ roomManager
416
+ );
417
+ roomManager.setRevalidationmanager(key, useThreadsRevalidationManager);
418
+ return useThreadsRevalidationManager;
419
+ }
420
+ return revalidationManager;
492
421
  }
493
422
  function useThreadsFetcher() {
494
423
  const fetcher = _react.useContext.call(void 0, FetcherContext);
@@ -497,156 +426,126 @@ function createCommentsRoom(errorEventSource) {
497
426
  }
498
427
  return fetcher;
499
428
  }
500
- function _useThreads(room, key) {
501
- const fetcher = useThreadsFetcher();
502
- const revalidateCache = useRevalidateCache(manager, fetcher);
503
- const status = _indexjs.useSyncExternalStore.call(void 0,
504
- room.events.status.subscribe,
505
- room.getStatus,
506
- room.getStatus
507
- );
508
- const isOnline = useIsOnline();
509
- const isDocumentVisible = useIsDocumentVisible();
510
- const subscribeToCommentEvents = _react.useContext.call(void 0,
511
- CommentsEventSubscriptionContext
512
- );
513
- const interval = getPollingInterval(
514
- isOnline,
515
- isDocumentVisible,
516
- status === "connected"
517
- );
518
- useAutomaticRevalidation(manager, revalidateCache, {
519
- revalidateOnFocus: true,
520
- revalidateOnReconnect: true,
521
- refreshInterval: interval
522
- });
523
- _react.useEffect.call(void 0, subscribeToCommentEvents, [subscribeToCommentEvents]);
524
- const cache = _indexjs.useSyncExternalStore.call(void 0,
525
- subscribe2,
526
- () => getCache(key),
527
- () => getCache(key)
528
- );
529
- if (!cache || cache.isLoading) {
530
- return { isLoading: true };
531
- }
532
- return {
533
- isLoading: cache.isLoading,
534
- threads: cache.data || [],
535
- error: cache.error
536
- };
537
- }
538
429
  function useThreads(room, options = { query: { metadata: {} } }) {
539
430
  const key = _react.useMemo.call(void 0, () => _core.stringify.call(void 0, options), [options]);
540
- let revalidationManager = revalidationManagers.get(key);
541
- if (!revalidationManager) {
542
- revalidationManager = createThreadsRevalidationManager(key);
543
- revalidationManagers.set(key, revalidationManager);
544
- }
545
- const fetcher = _react.useCallback.call(void 0,
431
+ const manager = useRoomManager();
432
+ const useThreadsRevalidationManager = getUseThreadsRevalidationManager(
433
+ options,
434
+ manager
435
+ );
436
+ const fetcher = React.default.useCallback(
546
437
  () => {
547
438
  return room.getThreads(options);
548
439
  },
549
- // eslint-disable-next-line react-hooks/exhaustive-deps
550
- [key]
440
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- The missing dependency is `options` but `key` and `normalized` are analogous, so we only include `key` as dependency. This helps minimize the number of re-renders as `options` can change on each render
441
+ [key, room]
551
442
  );
552
- const revalidateCache = useRevalidateCache(revalidationManager, fetcher);
553
- _react.useEffect.call(void 0,
554
- () => {
555
- const info = filterOptions.get(key);
556
- if (info) {
557
- info.count += 1;
558
- } else {
559
- filterOptions.set(key, {
560
- options,
561
- count: 1
562
- });
563
- cacheStates.set(key, { isLoading: true });
564
- }
565
- return () => {
566
- const info2 = filterOptions.get(key);
567
- if (!info2)
568
- return;
569
- info2.count -= 1;
570
- if (info2.count > 0)
571
- return;
572
- filterOptions.delete(key);
573
- };
574
- },
575
- // eslint-disable-next-line react-hooks/exhaustive-deps
576
- [key]
443
+ const revalidateCache = useRevalidateCache(
444
+ useThreadsRevalidationManager,
445
+ fetcher
577
446
  );
578
447
  _react.useEffect.call(void 0, () => {
579
448
  void revalidateCache({ shouldDedupe: true });
580
449
  }, [revalidateCache]);
581
- return _useThreads(room, key);
450
+ _react.useEffect.call(void 0, () => {
451
+ manager.incrementReferenceCount(key);
452
+ return () => {
453
+ manager.decrementReferenceCount(key);
454
+ };
455
+ }, [manager, key]);
456
+ const cache = _useThreads(room, options);
457
+ return cache;
582
458
  }
583
- function useThreadsSuspense(room, options = {}) {
459
+ function useThreadsSuspense(room, options = { query: { metadata: {} } }) {
584
460
  const key = _react.useMemo.call(void 0, () => _core.stringify.call(void 0, options), [options]);
585
- let revalidationManager = revalidationManagers.get(key);
586
- if (!revalidationManager) {
587
- revalidationManager = createThreadsRevalidationManager(key);
588
- revalidationManagers.set(key, revalidationManager);
589
- }
590
- const fetcher = _react.useCallback.call(void 0,
461
+ const manager = useRoomManager();
462
+ const useThreadsRevalidationManager = getUseThreadsRevalidationManager(
463
+ options,
464
+ manager
465
+ );
466
+ const fetcher = React.default.useCallback(
591
467
  () => {
592
468
  return room.getThreads(options);
593
469
  },
594
- // eslint-disable-next-line react-hooks/exhaustive-deps
595
- [key]
470
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- The missing dependency is `options` but `key` and `normalized` are analogous, so we only include `key` as dependency. This helps minimize the number of re-renders as `options` can change on each render
471
+ [key, room]
596
472
  );
597
- const info = filterOptions.get(key);
598
- if (!info) {
599
- filterOptions.set(key, {
600
- options,
601
- count: 0
602
- });
603
- cacheStates.set(key, { isLoading: true });
604
- }
605
- const revalidateCache = useRevalidateCache(revalidationManager, fetcher);
606
- _react.useEffect.call(void 0,
607
- () => {
608
- const info2 = filterOptions.get(key);
609
- if (info2) {
610
- info2.count += 1;
611
- } else {
612
- filterOptions.set(key, {
613
- options,
614
- count: 1
615
- });
616
- }
617
- return () => {
618
- const info3 = filterOptions.get(key);
619
- if (!info3)
620
- return;
621
- info3.count -= 1;
622
- if (info3.count > 0)
623
- return;
624
- filterOptions.delete(key);
625
- };
626
- },
627
- // eslint-disable-next-line react-hooks/exhaustive-deps
628
- [key]
473
+ const revalidateCache = useRevalidateCache(
474
+ useThreadsRevalidationManager,
475
+ fetcher
629
476
  );
630
- const cache = _useThreads(room, key);
477
+ _react.useEffect.call(void 0, () => {
478
+ void revalidateCache({ shouldDedupe: true });
479
+ }, [revalidateCache]);
480
+ _react.useEffect.call(void 0, () => {
481
+ manager.incrementReferenceCount(key);
482
+ return () => {
483
+ manager.decrementReferenceCount(key);
484
+ };
485
+ }, [manager, key]);
486
+ const cache = _useThreads(room, options);
631
487
  if (cache.error) {
632
488
  throw cache.error;
633
489
  }
634
490
  if (cache.isLoading || !cache.threads) {
635
- throw revalidateCache({ shouldDedupe: true });
491
+ throw revalidateCache({
492
+ shouldDedupe: true
493
+ });
636
494
  }
637
495
  return {
496
+ isLoading: false,
638
497
  threads: cache.threads,
639
- isLoading: false
498
+ error: cache.error
640
499
  };
641
500
  }
501
+ function _useThreads(room, options) {
502
+ const manager = useRoomManager();
503
+ const useThreadsRevalidationManager = getUseThreadsRevalidationManager(
504
+ options,
505
+ manager
506
+ );
507
+ return _withselectorjs.useSyncExternalStoreWithSelector.call(void 0,
508
+ store.subscribe,
509
+ () => store.getThreads(),
510
+ () => store.getThreads(),
511
+ (state) => {
512
+ const isLoading = useThreadsRevalidationManager.getIsLoading();
513
+ if (isLoading) {
514
+ return {
515
+ isLoading: true
516
+ };
517
+ }
518
+ const options2 = useThreadsRevalidationManager.getOptions();
519
+ const error = useThreadsRevalidationManager.getError();
520
+ const filtered = state.filter((thread) => {
521
+ if (thread.roomId !== room.id)
522
+ return false;
523
+ const query = _nullishCoalesce(options2.query, () => ( {}));
524
+ for (const key in query.metadata) {
525
+ if (thread.metadata[key] !== query.metadata[key]) {
526
+ return false;
527
+ }
528
+ }
529
+ return true;
530
+ });
531
+ return {
532
+ isLoading: false,
533
+ threads: filtered,
534
+ error
535
+ };
536
+ }
537
+ );
538
+ }
642
539
  function useEditThreadMetadata(room) {
643
- const revalidate = useRevalidateCache(manager, room.getThreads);
540
+ const manager = useRoomManager();
541
+ const fetcher = useThreadsFetcher();
542
+ const revalidate = useRevalidateCache(manager, fetcher);
644
543
  const mutate = useMutate(manager, revalidate);
645
544
  const editThreadMetadata = _react.useCallback.call(void 0,
646
545
  (options) => {
647
546
  const threadId = options.threadId;
648
547
  const metadata = "metadata" in options ? options.metadata : {};
649
- const threads = getThreads();
548
+ const threads = getThreads(manager);
650
549
  const optimisticData = threads.map(
651
550
  (thread) => thread.id === threadId ? {
652
551
  ...thread,
@@ -672,11 +571,12 @@ function createCommentsRoom(errorEventSource) {
672
571
  );
673
572
  });
674
573
  },
675
- [room, mutate]
574
+ [room, mutate, manager]
676
575
  );
677
576
  return editThreadMetadata;
678
577
  }
679
578
  function useCreateThread(room) {
579
+ const manager = useRoomManager();
680
580
  const fetcher = useThreadsFetcher();
681
581
  const revalidate = useRevalidateCache(manager, fetcher);
682
582
  const mutate = useMutate(manager, revalidate);
@@ -684,9 +584,9 @@ function createCommentsRoom(errorEventSource) {
684
584
  (options) => {
685
585
  const body = options.body;
686
586
  const metadata = "metadata" in options ? options.metadata : {};
687
- const threads = getThreads();
688
- const threadId = createOptimisticId(THREAD_ID_PREFIX);
689
- const commentId = createOptimisticId(COMMENT_ID_PREFIX);
587
+ const threads = getThreads(manager);
588
+ const threadId = createThreadId();
589
+ const commentId = createCommentId();
690
590
  const now = /* @__PURE__ */ new Date();
691
591
  const newComment = {
692
592
  id: commentId,
@@ -725,18 +625,19 @@ function createCommentsRoom(errorEventSource) {
725
625
  });
726
626
  return newThread;
727
627
  },
728
- [room, mutate]
628
+ [room, mutate, manager]
729
629
  );
730
630
  return createThread;
731
631
  }
732
632
  function useCreateComment(room) {
633
+ const manager = useRoomManager();
733
634
  const fetcher = useThreadsFetcher();
734
635
  const revalidate = useRevalidateCache(manager, fetcher);
735
636
  const mutate = useMutate(manager, revalidate);
736
637
  const createComment = _react.useCallback.call(void 0,
737
638
  ({ threadId, body }) => {
738
- const threads = getThreads();
739
- const commentId = createOptimisticId(COMMENT_ID_PREFIX);
639
+ const threads = getThreads(manager);
640
+ const commentId = createCommentId();
740
641
  const now = /* @__PURE__ */ new Date();
741
642
  const comment = {
742
643
  id: commentId,
@@ -772,16 +673,18 @@ function createCommentsRoom(errorEventSource) {
772
673
  });
773
674
  return comment;
774
675
  },
775
- [room, mutate]
676
+ [room, mutate, manager]
776
677
  );
777
678
  return createComment;
778
679
  }
779
680
  function useEditComment(room) {
780
- const revalidate = useRevalidateCache(manager, room.getThreads);
681
+ const manager = useRoomManager();
682
+ const fetcher = useThreadsFetcher();
683
+ const revalidate = useRevalidateCache(manager, fetcher);
781
684
  const mutate = useMutate(manager, revalidate);
782
685
  const editComment = _react.useCallback.call(void 0,
783
686
  ({ threadId, commentId, body }) => {
784
- const threads = getThreads();
687
+ const threads = getThreads(manager);
785
688
  const now = /* @__PURE__ */ new Date();
786
689
  const optimisticData = threads.map(
787
690
  (thread) => thread.id === threadId ? {
@@ -812,16 +715,18 @@ function createCommentsRoom(errorEventSource) {
812
715
  );
813
716
  });
814
717
  },
815
- [room, mutate]
718
+ [room, mutate, manager]
816
719
  );
817
720
  return editComment;
818
721
  }
819
722
  function useDeleteComment(room) {
820
- const revalidate = useRevalidateCache(manager, room.getThreads);
723
+ const manager = useRoomManager();
724
+ const fetcher = useThreadsFetcher();
725
+ const revalidate = useRevalidateCache(manager, fetcher);
821
726
  const mutate = useMutate(manager, revalidate);
822
727
  const deleteComment = _react.useCallback.call(void 0,
823
728
  ({ threadId, commentId }) => {
824
- const threads = getThreads();
729
+ const threads = getThreads(manager);
825
730
  const now = /* @__PURE__ */ new Date();
826
731
  const newThreads = [];
827
732
  for (const thread of threads) {
@@ -861,16 +766,18 @@ function createCommentsRoom(errorEventSource) {
861
766
  );
862
767
  });
863
768
  },
864
- [room, mutate]
769
+ [room, mutate, manager]
865
770
  );
866
771
  return deleteComment;
867
772
  }
868
773
  function useAddReaction(room) {
869
- const revalidate = useRevalidateCache(manager, room.getThreads);
774
+ const manager = useRoomManager();
775
+ const fetcher = useThreadsFetcher();
776
+ const revalidate = useRevalidateCache(manager, fetcher);
870
777
  const mutate = useMutate(manager, revalidate);
871
778
  const createComment = _react.useCallback.call(void 0,
872
779
  ({ threadId, commentId, emoji }) => {
873
- const threads = getThreads();
780
+ const threads = getThreads(manager);
874
781
  const now = /* @__PURE__ */ new Date();
875
782
  const userId = getCurrentUserId(room);
876
783
  const optimisticData = threads.map(
@@ -924,16 +831,18 @@ function createCommentsRoom(errorEventSource) {
924
831
  );
925
832
  });
926
833
  },
927
- [room, mutate]
834
+ [room, mutate, manager]
928
835
  );
929
836
  return createComment;
930
837
  }
931
838
  function useRemoveReaction(room) {
932
- const revalidate = useRevalidateCache(manager, room.getThreads);
839
+ const manager = useRoomManager();
840
+ const fetcher = useThreadsFetcher();
841
+ const revalidate = useRevalidateCache(manager, fetcher);
933
842
  const mutate = useMutate(manager, revalidate);
934
843
  const createComment = _react.useCallback.call(void 0,
935
844
  ({ threadId, commentId, emoji }) => {
936
- const threads = getThreads();
845
+ const threads = getThreads(manager);
937
846
  const userId = getCurrentUserId(room);
938
847
  const optimisticData = threads.map(
939
848
  (thread) => thread.id === threadId ? {
@@ -985,7 +894,7 @@ function createCommentsRoom(errorEventSource) {
985
894
  );
986
895
  });
987
896
  },
988
- [room, mutate]
897
+ [room, mutate, manager]
989
898
  );
990
899
  return createComment;
991
900
  }
@@ -1005,6 +914,12 @@ function createCommentsRoom(errorEventSource) {
1005
914
  function createOptimisticId(prefix) {
1006
915
  return `${prefix}_${_nanoid.nanoid.call(void 0, )}`;
1007
916
  }
917
+ function createThreadId() {
918
+ return createOptimisticId(THREAD_ID_PREFIX);
919
+ }
920
+ function createCommentId() {
921
+ return createOptimisticId(COMMENT_ID_PREFIX);
922
+ }
1008
923
  function getCurrentUserId(room) {
1009
924
  const self = room.getSelf();
1010
925
  if (self === null || self.id === void 0) {
@@ -1015,37 +930,41 @@ function getCurrentUserId(room) {
1015
930
  }
1016
931
  function handleCommentsApiError(err) {
1017
932
  const message = `Request failed with status ${err.status}: ${err.message}`;
1018
- if (_optionalChain([err, 'access', _3 => _3.details, 'optionalAccess', _4 => _4.error]) === "FORBIDDEN") {
933
+ if (_optionalChain([err, 'access', _2 => _2.details, 'optionalAccess', _3 => _3.error]) === "FORBIDDEN") {
1019
934
  const detailedMessage = [message, err.details.suggestion, err.details.docs].filter(Boolean).join("\n");
1020
935
  console.error(detailedMessage);
1021
936
  }
1022
937
  return new Error(message);
1023
938
  }
1024
- function getPollingInterval(isBrowserOnline, isDocumentVisible, isRoomConnected) {
1025
- if (!isBrowserOnline || !isDocumentVisible)
1026
- return;
1027
- if (isRoomConnected)
1028
- return POLLING_INTERVAL_REALTIME;
1029
- return POLLING_INTERVAL;
1030
- }
1031
- function createThreadsCacheManager() {
1032
- let cache;
939
+ function createRoomRevalidationManager(roomId, {
940
+ getCache,
941
+ setCache
942
+ }) {
1033
943
  let request;
1034
944
  let error;
1035
945
  let mutation;
1036
- const cacheEventSource = _core.makeEventSource.call(void 0, );
1037
- const errorEventSource = _core.makeEventSource.call(void 0, );
946
+ const revalidationManagerByOptions = /* @__PURE__ */ new Map();
947
+ const referenceCountByOptions = /* @__PURE__ */ new Map();
1038
948
  return {
1039
949
  // Cache
1040
950
  getCache() {
1041
- return cache;
951
+ const threads = getCache();
952
+ const filtered = threads.filter((thread) => thread.roomId === roomId);
953
+ return filtered;
1042
954
  },
1043
955
  setCache(value) {
956
+ for (const key of revalidationManagerByOptions.keys()) {
957
+ if (referenceCountByOptions.get(key) === 0) {
958
+ revalidationManagerByOptions.delete(key);
959
+ referenceCountByOptions.delete(key);
960
+ }
961
+ }
1044
962
  const sorted = value.sort(
1045
963
  (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
1046
964
  );
1047
- cache = sorted;
1048
- cacheEventSource.notify(cache);
965
+ const threads = getCache();
966
+ const newThreads = threads.filter((thread) => thread.roomId !== roomId).concat(sorted);
967
+ setCache(newThreads);
1049
968
  },
1050
969
  // Request
1051
970
  getRequest() {
@@ -1060,7 +979,6 @@ function createThreadsCacheManager() {
1060
979
  },
1061
980
  setError(err) {
1062
981
  error = err;
1063
- errorEventSource.notify(err);
1064
982
  },
1065
983
  // Mutation
1066
984
  getMutation() {
@@ -1069,19 +987,111 @@ function createThreadsCacheManager() {
1069
987
  setMutation(info) {
1070
988
  mutation = info;
1071
989
  },
1072
- // Subscription
1073
- subscribe(type, callback) {
1074
- switch (type) {
1075
- case "cache":
1076
- return cacheEventSource.subscribe(
1077
- callback
1078
- );
1079
- case "error":
1080
- return errorEventSource.subscribe(callback);
990
+ getRevalidationManagers() {
991
+ return Array.from(revalidationManagerByOptions.entries());
992
+ },
993
+ getRevalidationManager(key) {
994
+ return revalidationManagerByOptions.get(key);
995
+ },
996
+ setRevalidationmanager(key, manager) {
997
+ revalidationManagerByOptions.set(key, manager);
998
+ },
999
+ getTotalReferenceCount() {
1000
+ return Array.from(referenceCountByOptions.values()).reduce(
1001
+ (acc, count) => acc + count,
1002
+ 0
1003
+ );
1004
+ },
1005
+ incrementReferenceCount(key) {
1006
+ const count = _nullishCoalesce(referenceCountByOptions.get(key), () => ( 0));
1007
+ referenceCountByOptions.set(key, count + 1);
1008
+ },
1009
+ decrementReferenceCount(key) {
1010
+ const count = _nullishCoalesce(referenceCountByOptions.get(key), () => ( 0));
1011
+ referenceCountByOptions.set(key, count - 1);
1012
+ },
1013
+ getReferenceCount(key) {
1014
+ return _nullishCoalesce(referenceCountByOptions.get(key), () => ( 0));
1015
+ }
1016
+ };
1017
+ }
1018
+ function createClientCacheStore() {
1019
+ let threads = [];
1020
+ const threadsEventSource = _core.makeEventSource.call(void 0, );
1021
+ return {
1022
+ getThreads() {
1023
+ return threads;
1024
+ },
1025
+ setThreads(value) {
1026
+ threads = value;
1027
+ threadsEventSource.notify(threads);
1028
+ },
1029
+ subscribe(callback) {
1030
+ return threadsEventSource.subscribe(callback);
1031
+ }
1032
+ };
1033
+ }
1034
+ function createUseThreadsRevalidationManager(options, manager) {
1035
+ let isLoading = true;
1036
+ let request;
1037
+ let error;
1038
+ return {
1039
+ // Cache
1040
+ getCache() {
1041
+ return void 0;
1042
+ },
1043
+ setCache(value) {
1044
+ const cache = new Map(
1045
+ (_nullishCoalesce(manager.getCache(), () => ( []))).map((thread) => [thread.id, thread])
1046
+ );
1047
+ for (const thread of value) {
1048
+ cache.set(thread.id, thread);
1081
1049
  }
1050
+ manager.setCache(Array.from(cache.values()));
1051
+ isLoading = false;
1052
+ },
1053
+ // Request
1054
+ getRequest() {
1055
+ return request;
1056
+ },
1057
+ setRequest(value) {
1058
+ request = value;
1059
+ },
1060
+ // Error
1061
+ getError() {
1062
+ return error;
1063
+ },
1064
+ setError(err) {
1065
+ error = err;
1066
+ isLoading = false;
1067
+ const cache = manager.getCache();
1068
+ manager.setCache(cache);
1069
+ },
1070
+ // Mutation
1071
+ getMutation() {
1072
+ return void 0;
1073
+ },
1074
+ setMutation(_) {
1075
+ return;
1076
+ },
1077
+ getOptions() {
1078
+ return options;
1079
+ },
1080
+ getIsLoading() {
1081
+ return isLoading;
1082
+ },
1083
+ setIsLoading(value) {
1084
+ isLoading = value;
1082
1085
  }
1083
1086
  };
1084
1087
  }
1088
+ function getPollingInterval(isBrowserOnline, isDocumentVisible, isRoomConnected) {
1089
+ if (!isBrowserOnline || !isDocumentVisible)
1090
+ return;
1091
+ if (isRoomConnected)
1092
+ return POLLING_INTERVAL_REALTIME;
1093
+ return POLLING_INTERVAL;
1094
+ }
1085
1095
 
1086
1096
  // src/comments/lib/use-debounce.ts
1087
1097
 
@@ -1136,14 +1146,14 @@ function useAsyncCache(cache, key, options) {
1136
1146
  return cacheItem2;
1137
1147
  }, [cache, key]);
1138
1148
  const subscribe2 = _react.useCallback.call(void 0,
1139
- (callback) => _nullishCoalesce(_optionalChain([cacheItem, 'optionalAccess', _5 => _5.subscribe, 'call', _6 => _6(callback)]), () => ( noop)),
1149
+ (callback) => _nullishCoalesce(_optionalChain([cacheItem, 'optionalAccess', _4 => _4.subscribe, 'call', _5 => _5(callback)]), () => ( noop)),
1140
1150
  [cacheItem]
1141
1151
  );
1142
1152
  const getState = _react.useCallback.call(void 0,
1143
- () => _nullishCoalesce(_optionalChain([cacheItem, 'optionalAccess', _7 => _7.getState, 'call', _8 => _8()]), () => ( INITIAL_ASYNC_STATE)),
1153
+ () => _nullishCoalesce(_optionalChain([cacheItem, 'optionalAccess', _6 => _6.getState, 'call', _7 => _7()]), () => ( INITIAL_ASYNC_STATE)),
1144
1154
  [cacheItem]
1145
1155
  );
1146
- const revalidate = _react.useCallback.call(void 0, () => _optionalChain([cacheItem, 'optionalAccess', _9 => _9.revalidate, 'call', _10 => _10()]), [cacheItem]);
1156
+ const revalidate = _react.useCallback.call(void 0, () => _optionalChain([cacheItem, 'optionalAccess', _8 => _8.revalidate, 'call', _9 => _9()]), [cacheItem]);
1147
1157
  const state = _indexjs.useSyncExternalStore.call(void 0, subscribe2, getState, getState);
1148
1158
  const previousData = _react.useRef.call(void 0, );
1149
1159
  let data = state.data;
@@ -1159,7 +1169,7 @@ function useAsyncCache(cache, key, options) {
1159
1169
  revalidate
1160
1170
  };
1161
1171
  }
1162
- if (_optionalChain([frozenOptions, 'optionalAccess', _11 => _11.suspense])) {
1172
+ if (_optionalChain([frozenOptions, 'optionalAccess', _10 => _10.suspense])) {
1163
1173
  const error = getState().error;
1164
1174
  if (error) {
1165
1175
  throw error;
@@ -1173,7 +1183,7 @@ function useAsyncCache(cache, key, options) {
1173
1183
  });
1174
1184
  }
1175
1185
  }
1176
- if (state.isLoading && _optionalChain([frozenOptions, 'optionalAccess', _12 => _12.keepPreviousDataWhileLoading]) && typeof state.data === "undefined" && _optionalChain([previousData, 'access', _13 => _13.current, 'optionalAccess', _14 => _14.key]) !== key && typeof _optionalChain([previousData, 'access', _15 => _15.current, 'optionalAccess', _16 => _16.data]) !== "undefined") {
1186
+ if (state.isLoading && _optionalChain([frozenOptions, 'optionalAccess', _11 => _11.keepPreviousDataWhileLoading]) && typeof state.data === "undefined" && _optionalChain([previousData, 'access', _12 => _12.current, 'optionalAccess', _13 => _13.key]) !== key && typeof _optionalChain([previousData, 'access', _14 => _14.current, 'optionalAccess', _15 => _15.data]) !== "undefined") {
1177
1187
  data = previousData.current.data;
1178
1188
  }
1179
1189
  return {
@@ -1567,7 +1577,7 @@ function createRoomContext(client, options) {
1567
1577
  function onRootChange() {
1568
1578
  const newValue = root.get(key);
1569
1579
  if (newValue !== curr) {
1570
- _optionalChain([unsubCurr, 'optionalCall', _17 => _17()]);
1580
+ _optionalChain([unsubCurr, 'optionalCall', _16 => _16()]);
1571
1581
  curr = newValue;
1572
1582
  subscribeToCurr();
1573
1583
  rerender();
@@ -1578,7 +1588,7 @@ function createRoomContext(client, options) {
1578
1588
  const unsubscribeRoot = room.subscribe(root, onRootChange);
1579
1589
  return () => {
1580
1590
  unsubscribeRoot();
1581
- _optionalChain([unsubCurr, 'optionalCall', _18 => _18()]);
1591
+ _optionalChain([unsubCurr, 'optionalCall', _17 => _17()]);
1582
1592
  };
1583
1593
  }, [rootOrNull, room, key, rerender]);
1584
1594
  if (rootOrNull === null) {
@@ -1743,7 +1753,7 @@ function createRoomContext(client, options) {
1743
1753
  const users = await resolveUsers(
1744
1754
  JSON.parse(stringifiedOptions)
1745
1755
  );
1746
- return _optionalChain([users, 'optionalAccess', _19 => _19[0]]);
1756
+ return _optionalChain([users, 'optionalAccess', _18 => _18[0]]);
1747
1757
  }) : void 0;
1748
1758
  function useUser(userId) {
1749
1759
  const room = useRoom();