@liveblocks/react 2.15.1 → 2.16.0-toolbars1

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.
@@ -85,6 +85,18 @@ function useSyncExternalStoreWithSelector(subscribe, getSnapshot, getServerSnaps
85
85
  return value;
86
86
  }
87
87
 
88
+ // src/use-signal.ts
89
+ var identity = (value) => value;
90
+ function useSignal(signal, selector, isEqual) {
91
+ return useSyncExternalStoreWithSelector(
92
+ signal.subscribe,
93
+ signal.get,
94
+ signal.get,
95
+ selector ?? identity,
96
+ isEqual
97
+ );
98
+ }
99
+
88
100
  // src/liveblocks.tsx
89
101
  import {
90
102
  assert,
@@ -121,19 +133,30 @@ var config = {
121
133
  NOTIFICATION_SETTINGS_MAX_STALE_TIME: 5 * SECONDS
122
134
  };
123
135
 
124
- // src/lib/shallow2.ts
125
- import { isPlainObject, shallow } from "@liveblocks/core";
126
- function shallow2(a, b) {
127
- if (!isPlainObject(a) || !isPlainObject(b)) {
128
- return shallow(a, b);
136
+ // src/lib/AsyncResult.ts
137
+ var ASYNC_LOADING = Object.freeze({ isLoading: true });
138
+ var ASYNC_ERR = (error) => Object.freeze({ isLoading: false, error });
139
+ function ASYNC_OK(fieldOrData, data) {
140
+ if (arguments.length === 1) {
141
+ return Object.freeze({ isLoading: false, data: fieldOrData });
142
+ } else {
143
+ return Object.freeze({ isLoading: false, [fieldOrData]: data });
129
144
  }
130
- const keysA = Object.keys(a);
131
- if (keysA.length !== Object.keys(b).length) {
132
- return false;
145
+ }
146
+
147
+ // src/lib/itertools.ts
148
+ function find(it, predicate) {
149
+ for (const item of it) {
150
+ if (predicate(item)) return item;
133
151
  }
134
- return keysA.every(
135
- (key) => Object.prototype.hasOwnProperty.call(b, key) && shallow(a[key], b[key])
136
- );
152
+ return void 0;
153
+ }
154
+ function count(it, predicate) {
155
+ let total = 0;
156
+ for (const item of it) {
157
+ if (predicate(item)) total++;
158
+ }
159
+ return total;
137
160
  }
138
161
 
139
162
  // src/lib/use-initial.ts
@@ -199,16 +222,17 @@ import {
199
222
  batch as batch2,
200
223
  compactObject,
201
224
  console as console2,
225
+ DefaultMap,
202
226
  DerivedSignal,
203
227
  HttpError,
204
228
  kInternal,
205
- makeEventSource,
206
229
  MutableSignal as MutableSignal2,
207
230
  nanoid,
208
231
  nn,
209
232
  shallow as shallow3,
210
233
  Signal,
211
- stringify
234
+ stringify,
235
+ unstringify
212
236
  } from "@liveblocks/core";
213
237
 
214
238
  // src/lib/autobind.ts
@@ -228,27 +252,26 @@ function autobind(self) {
228
252
  } while ((obj = Reflect.getPrototypeOf(obj)) && obj !== Object.prototype);
229
253
  }
230
254
 
231
- // src/lib/itertools.ts
232
- function find(it, predicate) {
233
- for (const item of it) {
234
- if (predicate(item)) return item;
255
+ // src/lib/shallow2.ts
256
+ import { isPlainObject, shallow } from "@liveblocks/core";
257
+ function shallow2(a, b) {
258
+ if (!isPlainObject(a) || !isPlainObject(b)) {
259
+ return shallow(a, b);
235
260
  }
236
- return void 0;
261
+ const keysA = Object.keys(a);
262
+ if (keysA.length !== Object.keys(b).length) {
263
+ return false;
264
+ }
265
+ return keysA.every(
266
+ (key) => Object.prototype.hasOwnProperty.call(b, key) && shallow(a[key], b[key])
267
+ );
237
268
  }
238
269
 
239
270
  // src/ThreadDB.ts
240
271
  import { batch, MutableSignal, SortedList } from "@liveblocks/core";
241
272
 
242
- // src/lib/guards.ts
243
- import { isPlainObject as isPlainObject2 } from "@liveblocks/core";
244
- function isStartsWith(blob) {
245
- return isPlainObject2(blob) && isString(blob.startsWith);
246
- }
247
- function isString(value) {
248
- return typeof value === "string";
249
- }
250
-
251
273
  // src/lib/querying.ts
274
+ import { isStartsWithOperator } from "@liveblocks/core";
252
275
  function makeThreadsFilter(query) {
253
276
  return (thread) => matchesQuery(thread, query) && matchesMetadata(thread, query);
254
277
  }
@@ -268,8 +291,8 @@ function matchesMetadata(thread, q) {
268
291
  );
269
292
  }
270
293
  function matchesOperator(value, op) {
271
- if (isStartsWith(op)) {
272
- return isString(value) && value.startsWith(op.startsWith);
294
+ if (isStartsWithOperator(op)) {
295
+ return typeof value === "string" && value.startsWith(op.startsWith);
273
296
  } else {
274
297
  return value === op;
275
298
  }
@@ -355,12 +378,12 @@ var ThreadDB = class _ThreadDB {
355
378
  this.upsert(thread);
356
379
  }
357
380
  }
358
- applyDelta(updates) {
381
+ applyDelta(newThreads, deletedThreads) {
359
382
  batch(() => {
360
- for (const thread of updates.newThreads) {
383
+ for (const thread of newThreads) {
361
384
  this.upsertIfNewer(thread);
362
385
  }
363
- for (const { id, deletedAt } of updates.deletedThreads) {
386
+ for (const { id, deletedAt } of deletedThreads) {
364
387
  const existing = this.getEvenIfDeleted(id);
365
388
  if (!existing) continue;
366
389
  this.delete(id, deletedAt);
@@ -401,16 +424,10 @@ var ThreadDB = class _ThreadDB {
401
424
 
402
425
  // src/umbrella-store.ts
403
426
  function makeRoomThreadsQueryKey(roomId, query) {
404
- return `${roomId}-${stringify(query ?? {})}`;
427
+ return stringify([roomId, query ?? {}]);
405
428
  }
406
429
  function makeUserThreadsQueryKey(query) {
407
- return `USER_THREADS:${stringify(query ?? {})}`;
408
- }
409
- function makeNotificationSettingsQueryKey(roomId) {
410
- return `${roomId}:NOTIFICATION_SETTINGS`;
411
- }
412
- function makeVersionsQueryKey(roomId) {
413
- return `${roomId}-VERSIONS`;
430
+ return stringify(query ?? {});
414
431
  }
415
432
  function usify(promise) {
416
433
  if ("status" in promise) {
@@ -431,53 +448,50 @@ function usify(promise) {
431
448
  return usable;
432
449
  }
433
450
  var noop2 = Promise.resolve();
434
- var ASYNC_LOADING = Object.freeze({ isLoading: true });
435
451
  var PaginatedResource = class {
436
- observable;
437
- #eventSource;
452
+ #signal;
453
+ signal;
438
454
  #fetchPage;
439
- #paginationState;
440
- // Should be null while in loading or error state!
441
455
  #pendingFetchMore;
442
456
  constructor(fetchPage) {
443
- this.#paginationState = null;
457
+ this.#signal = new Signal(ASYNC_LOADING);
444
458
  this.#fetchPage = fetchPage;
445
- this.#eventSource = makeEventSource();
446
459
  this.#pendingFetchMore = null;
447
- this.observable = this.#eventSource.observable;
460
+ this.signal = this.#signal.asReadonly();
448
461
  autobind(this);
449
462
  }
450
- #patchPaginationState(patch) {
451
- const state = this.#paginationState;
452
- if (state === null) return;
453
- this.#paginationState = { ...state, ...patch };
454
- this.#eventSource.notify();
463
+ get() {
464
+ return this.#signal.get();
465
+ }
466
+ #patch(patch) {
467
+ const state = this.#signal.get();
468
+ if (state.data === void 0) return;
469
+ this.#signal.set(ASYNC_OK({ ...state.data, ...patch }));
455
470
  }
456
471
  async #fetchMore() {
457
- const state = this.#paginationState;
458
- if (!state?.cursor) {
472
+ const state = this.#signal.get();
473
+ if (!state.data?.cursor || state.data.isFetchingMore) {
459
474
  return;
460
475
  }
461
- this.#patchPaginationState({ isFetchingMore: true });
476
+ this.#patch({ isFetchingMore: true });
462
477
  try {
463
- const nextCursor = await this.#fetchPage(state.cursor);
464
- this.#patchPaginationState({
478
+ const nextCursor = await this.#fetchPage(state.data.cursor);
479
+ this.#patch({
465
480
  cursor: nextCursor,
481
+ hasFetchedAll: nextCursor === null,
466
482
  fetchMoreError: void 0,
467
483
  isFetchingMore: false
468
484
  });
469
485
  } catch (err) {
470
- this.#patchPaginationState({
486
+ this.#patch({
471
487
  isFetchingMore: false,
472
488
  fetchMoreError: err
473
489
  });
474
490
  }
475
491
  }
476
492
  fetchMore() {
477
- const state = this.#paginationState;
478
- if (state?.cursor === null) {
479
- return noop2;
480
- }
493
+ const state = this.#signal.get();
494
+ if (!state.data?.cursor) return noop2;
481
495
  if (!this.#pendingFetchMore) {
482
496
  this.#pendingFetchMore = this.#fetchMore().finally(() => {
483
497
  this.#pendingFetchMore = null;
@@ -485,31 +499,12 @@ var PaginatedResource = class {
485
499
  }
486
500
  return this.#pendingFetchMore;
487
501
  }
488
- get() {
489
- const usable = this.#cachedPromise;
490
- if (usable === null || usable.status === "pending") {
491
- return ASYNC_LOADING;
492
- }
493
- if (usable.status === "rejected") {
494
- return { isLoading: false, error: usable.reason };
495
- }
496
- const state = this.#paginationState;
497
- return {
498
- isLoading: false,
499
- data: {
500
- fetchMore: this.fetchMore,
501
- isFetchingMore: state.isFetchingMore,
502
- fetchMoreError: state.fetchMoreError,
503
- hasFetchedAll: state.cursor === null
504
- }
505
- };
506
- }
507
502
  #cachedPromise = null;
508
503
  waitUntilLoaded() {
509
504
  if (this.#cachedPromise) {
510
505
  return this.#cachedPromise;
511
506
  }
512
- const initialFetcher = autoRetry(
507
+ const initialPageFetch$ = autoRetry(
513
508
  () => this.#fetchPage(
514
509
  /* cursor */
515
510
  void 0
@@ -517,67 +512,64 @@ var PaginatedResource = class {
517
512
  5,
518
513
  [5e3, 5e3, 1e4, 15e3]
519
514
  );
520
- const promise = usify(
521
- initialFetcher.then((cursor) => {
522
- this.#paginationState = {
523
- cursor,
524
- isFetchingMore: false,
525
- fetchMoreError: void 0
526
- };
527
- })
528
- );
515
+ const promise = usify(initialPageFetch$);
529
516
  promise.then(
530
- () => this.#eventSource.notify(),
531
- () => {
532
- this.#eventSource.notify();
517
+ (cursor) => {
518
+ this.#signal.set(
519
+ ASYNC_OK({
520
+ cursor,
521
+ hasFetchedAll: cursor === null,
522
+ isFetchingMore: false,
523
+ fetchMoreError: void 0,
524
+ fetchMore: this.fetchMore
525
+ })
526
+ );
527
+ },
528
+ (err) => {
529
+ this.#signal.set(ASYNC_ERR(err));
533
530
  setTimeout(() => {
534
531
  this.#cachedPromise = null;
535
- this.#eventSource.notify();
532
+ this.#signal.set(ASYNC_LOADING);
536
533
  }, 5e3);
537
534
  }
538
535
  );
539
536
  this.#cachedPromise = promise;
540
- return promise;
537
+ return this.#cachedPromise;
541
538
  }
542
539
  };
543
540
  var SinglePageResource = class {
544
- observable;
545
- #eventSource;
541
+ #signal;
542
+ signal;
546
543
  #fetchPage;
547
544
  constructor(fetchPage) {
545
+ this.#signal = new Signal(ASYNC_LOADING);
546
+ this.signal = this.#signal.asReadonly();
548
547
  this.#fetchPage = fetchPage;
549
- this.#eventSource = makeEventSource();
550
- this.observable = this.#eventSource.observable;
551
548
  autobind(this);
552
549
  }
553
550
  get() {
554
- const usable = this.#cachedPromise;
555
- if (usable === null || usable.status === "pending") {
556
- return ASYNC_LOADING;
557
- } else if (usable.status === "rejected") {
558
- return { isLoading: false, error: usable.reason };
559
- } else {
560
- return { isLoading: false, data: void 0 };
561
- }
551
+ return this.#signal.get();
562
552
  }
563
553
  #cachedPromise = null;
564
554
  waitUntilLoaded() {
565
555
  if (this.#cachedPromise) {
566
556
  return this.#cachedPromise;
567
557
  }
568
- const initialFetcher = autoRetry(
558
+ const initialFetcher$ = autoRetry(
569
559
  () => this.#fetchPage(),
570
560
  5,
571
561
  [5e3, 5e3, 1e4, 15e3]
572
562
  );
573
- const promise = usify(initialFetcher);
563
+ const promise = usify(initialFetcher$);
574
564
  promise.then(
575
- () => this.#eventSource.notify(),
576
565
  () => {
577
- this.#eventSource.notify();
566
+ this.#signal.set(ASYNC_OK(void 0));
567
+ },
568
+ (err) => {
569
+ this.#signal.set(ASYNC_ERR(err));
578
570
  setTimeout(() => {
579
571
  this.#cachedPromise = null;
580
- this.#eventSource.notify();
572
+ this.#signal.set(ASYNC_LOADING);
581
573
  }, 5e3);
582
574
  }
583
575
  );
@@ -610,10 +602,10 @@ function createStore_forNotifications() {
610
602
  function clear() {
611
603
  signal.mutate((lut) => lut.clear());
612
604
  }
613
- function applyDelta(newInboxNotifications, deletedNotifications) {
605
+ function applyDelta(newNotifications, deletedNotifications) {
614
606
  signal.mutate((lut) => {
615
607
  let mutated = false;
616
- for (const n of newInboxNotifications) {
608
+ for (const n of newNotifications) {
617
609
  const existing = lut.get(n.id);
618
610
  if (existing) {
619
611
  const result = compareInboxNotifications(existing, n);
@@ -644,6 +636,11 @@ function createStore_forNotifications() {
644
636
  return true;
645
637
  });
646
638
  }
639
+ function upsert(notification) {
640
+ signal.mutate((lut) => {
641
+ lut.set(notification.id, notification);
642
+ });
643
+ }
647
644
  return {
648
645
  signal: signal.asReadonly(),
649
646
  // Mutations
@@ -653,66 +650,70 @@ function createStore_forNotifications() {
653
650
  applyDelta,
654
651
  clear,
655
652
  updateAssociatedNotification,
656
- // XXX_vincent Remove this eventually
657
- force_set: (mutationCallback) => signal.mutate(mutationCallback),
658
- invalidate: () => signal.mutate()
653
+ upsert
659
654
  };
660
655
  }
661
- function createStore_forRoomNotificationSettings() {
662
- const signal = new MutableSignal2(/* @__PURE__ */ new Map());
656
+ function createStore_forRoomNotificationSettings(updates) {
657
+ const baseSignal = new MutableSignal2(/* @__PURE__ */ new Map());
663
658
  function update(roomId, settings) {
664
- signal.mutate((lut) => {
659
+ baseSignal.mutate((lut) => {
665
660
  lut.set(roomId, settings);
666
661
  });
667
662
  }
668
663
  return {
669
- signal: signal.asReadonly(),
664
+ signal: DerivedSignal.from(
665
+ baseSignal,
666
+ updates,
667
+ (base, updates2) => applyOptimisticUpdates_forSettings(base, updates2)
668
+ ),
670
669
  // Mutations
671
- update,
672
- // XXX_vincent Remove this eventually
673
- invalidate: () => signal.mutate()
670
+ update
674
671
  };
675
672
  }
676
673
  function createStore_forHistoryVersions() {
677
- const signal = new MutableSignal2(/* @__PURE__ */ new Map());
674
+ const baseSignal = new MutableSignal2(
675
+ new DefaultMap(() => /* @__PURE__ */ new Map())
676
+ );
678
677
  function update(roomId, versions) {
679
- signal.mutate((lut) => {
680
- const versionsById = lut.get(roomId) ?? (lut.set(roomId, /* @__PURE__ */ new Map()), lut.get(roomId));
678
+ baseSignal.mutate((lut) => {
679
+ const versionsById = lut.getOrCreate(roomId);
681
680
  for (const version of versions) {
682
681
  versionsById.set(version.id, version);
683
682
  }
684
683
  });
685
684
  }
686
685
  return {
687
- signal: signal.asReadonly(),
686
+ signal: DerivedSignal.from(
687
+ baseSignal,
688
+ (hv) => Object.fromEntries(
689
+ [...hv].map(([roomId, versions]) => [
690
+ roomId,
691
+ Object.fromEntries(versions)
692
+ ])
693
+ )
694
+ ),
688
695
  // Mutations
689
- update,
690
- // XXX_vincent Remove these eventually
691
- force_set: (callback) => signal.mutate(callback),
692
- invalidate: () => signal.mutate()
696
+ update
693
697
  };
694
698
  }
695
699
  function createStore_forPermissionHints() {
696
- const signal = new Signal({});
700
+ const signal = new MutableSignal2(
701
+ new DefaultMap(() => /* @__PURE__ */ new Set())
702
+ );
697
703
  function update(newHints) {
698
- signal.set((prev) => {
699
- const permissionsByRoom = { ...prev };
704
+ signal.mutate((lut) => {
700
705
  for (const [roomId, newPermissions] of Object.entries(newHints)) {
701
- const existing = permissionsByRoom[roomId] ?? /* @__PURE__ */ new Set();
706
+ const existing = lut.getOrCreate(roomId);
702
707
  for (const permission of newPermissions) {
703
708
  existing.add(permission);
704
709
  }
705
- permissionsByRoom[roomId] = existing;
706
710
  }
707
- return permissionsByRoom;
708
711
  });
709
712
  }
710
713
  return {
711
714
  signal: signal.asReadonly(),
712
715
  // Mutations
713
- update,
714
- // XXX_vincent Remove this eventually
715
- invalidate: () => signal.set((store) => ({ ...store }))
716
+ update
716
717
  };
717
718
  }
718
719
  function createStore_forOptimistic(client) {
@@ -736,9 +737,7 @@ function createStore_forOptimistic(client) {
736
737
  signal: signal.asReadonly(),
737
738
  // Mutations
738
739
  add,
739
- remove,
740
- // XXX_vincent Remove this eventually
741
- invalidate: () => signal.set((store) => [...store])
740
+ remove
742
741
  };
743
742
  }
744
743
  var UmbrellaStore = class {
@@ -788,48 +787,37 @@ var UmbrellaStore = class {
788
787
  // threads and notifications separately, but the threadifications signal will
789
788
  // be updated whenever either of them change.
790
789
  //
791
- // XXX_vincent APIs like getRoomThreadsLoadingState should really also be modeled as output signals.
792
- //
793
790
  outputs;
794
791
  // Notifications
795
792
  #notificationsLastRequestedAt = null;
796
793
  // Keeps track of when we successfully requested an inbox notifications update for the last time. Will be `null` as long as the first successful fetch hasn't happened yet.
797
- #notifications;
794
+ #notificationsPaginationState;
798
795
  // Room Threads
799
796
  #roomThreadsLastRequestedAtByRoom = /* @__PURE__ */ new Map();
800
- #roomThreads = /* @__PURE__ */ new Map();
801
797
  // User Threads
802
798
  #userThreadsLastRequestedAt = null;
803
- #userThreads = /* @__PURE__ */ new Map();
804
799
  // Room versions
805
- #roomVersions = /* @__PURE__ */ new Map();
806
800
  #roomVersionsLastRequestedAtByRoom = /* @__PURE__ */ new Map();
807
- // Room notification settings
808
- #roomNotificationSettings = /* @__PURE__ */ new Map();
809
801
  constructor(client) {
810
802
  this.#client = client[kInternal].as();
811
803
  this.optimisticUpdates = createStore_forOptimistic(this.#client);
812
804
  this.permissionHints = createStore_forPermissionHints();
813
- const inboxFetcher = async (cursor) => {
814
- const result = await this.#client.getInboxNotifications({ cursor });
815
- this.updateThreadifications(result.threads, result.inboxNotifications);
816
- if (this.#notificationsLastRequestedAt === null) {
817
- this.#notificationsLastRequestedAt = result.requestedAt;
805
+ this.#notificationsPaginationState = new PaginatedResource(
806
+ async (cursor) => {
807
+ const result = await this.#client.getInboxNotifications({ cursor });
808
+ this.updateThreadifications(result.threads, result.inboxNotifications);
809
+ if (this.#notificationsLastRequestedAt === null) {
810
+ this.#notificationsLastRequestedAt = result.requestedAt;
811
+ }
812
+ const nextCursor = result.nextCursor;
813
+ return nextCursor;
818
814
  }
819
- const nextCursor = result.nextCursor;
820
- return nextCursor;
821
- };
822
- this.#notifications = new PaginatedResource(inboxFetcher);
823
- this.#notifications.observable.subscribe(
824
- () => (
825
- // Note that the store itself does not change, but it's only vehicle at
826
- // the moment to trigger a re-render, so we'll do a no-op update here.
827
- this.invalidateEntireStore()
828
- )
829
815
  );
830
816
  this.threads = new ThreadDB();
831
817
  this.notifications = createStore_forNotifications();
832
- this.roomNotificationSettings = createStore_forRoomNotificationSettings();
818
+ this.roomNotificationSettings = createStore_forRoomNotificationSettings(
819
+ this.optimisticUpdates.signal
820
+ );
833
821
  this.historyVersions = createStore_forHistoryVersions();
834
822
  const threadifications = DerivedSignal.from(
835
823
  this.threads.signal,
@@ -837,13 +825,7 @@ var UmbrellaStore = class {
837
825
  this.optimisticUpdates.signal,
838
826
  (ts, ns, updates) => applyOptimisticUpdates_forThreadifications(ts, ns, updates)
839
827
  );
840
- const threads = DerivedSignal.from(
841
- threadifications,
842
- (s) => ({
843
- threadsDB: s.threadsDB
844
- }),
845
- shallow3
846
- );
828
+ const threads = DerivedSignal.from(threadifications, (s) => s.threadsDB);
847
829
  const notifications = DerivedSignal.from(
848
830
  threadifications,
849
831
  (s) => ({
@@ -852,177 +834,180 @@ var UmbrellaStore = class {
852
834
  }),
853
835
  shallow3
854
836
  );
855
- const settingsByRoomId = DerivedSignal.from(
856
- this.roomNotificationSettings.signal,
857
- this.optimisticUpdates.signal,
858
- (settings, updates) => applyOptimisticUpdates_forSettings(settings, updates)
837
+ const loadingUserThreads = new DefaultMap(
838
+ (queryKey) => {
839
+ const query = unstringify(queryKey);
840
+ const resource = new PaginatedResource(async (cursor) => {
841
+ const result = await this.#client[kInternal].httpClient.getUserThreads_experimental({
842
+ cursor,
843
+ query
844
+ });
845
+ this.updateThreadifications(
846
+ result.threads,
847
+ result.inboxNotifications
848
+ );
849
+ this.permissionHints.update(result.permissionHints);
850
+ if (this.#userThreadsLastRequestedAt === null) {
851
+ this.#userThreadsLastRequestedAt = result.requestedAt;
852
+ }
853
+ return result.nextCursor;
854
+ });
855
+ const signal = DerivedSignal.from(() => {
856
+ const result = resource.get();
857
+ if (result.isLoading || result.error) {
858
+ return result;
859
+ }
860
+ const threads2 = this.outputs.threads.get().findMany(
861
+ void 0,
862
+ // Do _not_ filter by roomId
863
+ query ?? {},
864
+ "desc"
865
+ );
866
+ const page = result.data;
867
+ return {
868
+ isLoading: false,
869
+ threads: threads2,
870
+ hasFetchedAll: page.hasFetchedAll,
871
+ isFetchingMore: page.isFetchingMore,
872
+ fetchMoreError: page.fetchMoreError,
873
+ fetchMore: page.fetchMore
874
+ };
875
+ }, shallow2);
876
+ return { signal, waitUntilLoaded: resource.waitUntilLoaded };
877
+ }
859
878
  );
860
- const versionsByRoomId = DerivedSignal.from(
861
- this.historyVersions.signal,
862
- (hv) => Object.fromEntries(
863
- [...hv].map(([roomId, versions]) => [
864
- roomId,
865
- Object.fromEntries(versions)
866
- ])
867
- )
879
+ const loadingRoomThreads = new DefaultMap(
880
+ (queryKey) => {
881
+ const [roomId, query] = unstringify(queryKey);
882
+ const resource = new PaginatedResource(async (cursor) => {
883
+ const result = await this.#client[kInternal].httpClient.getThreads({
884
+ roomId,
885
+ cursor,
886
+ query
887
+ });
888
+ this.updateThreadifications(
889
+ result.threads,
890
+ result.inboxNotifications
891
+ );
892
+ this.permissionHints.update(result.permissionHints);
893
+ const lastRequestedAt = this.#roomThreadsLastRequestedAtByRoom.get(roomId);
894
+ if (lastRequestedAt === void 0 || lastRequestedAt > result.requestedAt) {
895
+ this.#roomThreadsLastRequestedAtByRoom.set(
896
+ roomId,
897
+ result.requestedAt
898
+ );
899
+ }
900
+ return result.nextCursor;
901
+ });
902
+ const signal = DerivedSignal.from(() => {
903
+ const result = resource.get();
904
+ if (result.isLoading || result.error) {
905
+ return result;
906
+ }
907
+ const threads2 = this.outputs.threads.get().findMany(roomId, query ?? {}, "asc");
908
+ const page = result.data;
909
+ return {
910
+ isLoading: false,
911
+ threads: threads2,
912
+ hasFetchedAll: page.hasFetchedAll,
913
+ isFetchingMore: page.isFetchingMore,
914
+ fetchMoreError: page.fetchMoreError,
915
+ fetchMore: page.fetchMore
916
+ };
917
+ }, shallow2);
918
+ return { signal, waitUntilLoaded: resource.waitUntilLoaded };
919
+ }
920
+ );
921
+ const loadingNotifications = {
922
+ signal: DerivedSignal.from(() => {
923
+ const resource = this.#notificationsPaginationState;
924
+ const result = resource.get();
925
+ if (result.isLoading || result.error) {
926
+ return result;
927
+ }
928
+ const page = result.data;
929
+ return {
930
+ isLoading: false,
931
+ inboxNotifications: this.outputs.notifications.get().sortedNotifications,
932
+ hasFetchedAll: page.hasFetchedAll,
933
+ isFetchingMore: page.isFetchingMore,
934
+ fetchMoreError: page.fetchMoreError,
935
+ fetchMore: page.fetchMore
936
+ };
937
+ }),
938
+ waitUntilLoaded: this.#notificationsPaginationState.waitUntilLoaded
939
+ };
940
+ const settingsByRoomId = new DefaultMap((roomId) => {
941
+ const resource = new SinglePageResource(async () => {
942
+ const room = this.#client.getRoom(roomId);
943
+ if (room === null) {
944
+ throw new HttpError(
945
+ `Room '${roomId}' is not available on client`,
946
+ 479
947
+ );
948
+ }
949
+ const result = await room.getNotificationSettings();
950
+ this.roomNotificationSettings.update(roomId, result);
951
+ });
952
+ const signal = DerivedSignal.from(() => {
953
+ const result = resource.get();
954
+ if (result.isLoading || result.error) {
955
+ return result;
956
+ } else {
957
+ return ASYNC_OK(
958
+ "settings",
959
+ nn(this.roomNotificationSettings.signal.get()[roomId])
960
+ );
961
+ }
962
+ }, shallow3);
963
+ return { signal, waitUntilLoaded: resource.waitUntilLoaded };
964
+ });
965
+ const versionsByRoomId = new DefaultMap(
966
+ (roomId) => {
967
+ const resource = new SinglePageResource(async () => {
968
+ const room = this.#client.getRoom(roomId);
969
+ if (room === null) {
970
+ throw new HttpError(
971
+ `Room '${roomId}' is not available on client`,
972
+ 479
973
+ );
974
+ }
975
+ const result = await room[kInternal].listTextVersions();
976
+ this.historyVersions.update(roomId, result.versions);
977
+ const lastRequestedAt = this.#roomVersionsLastRequestedAtByRoom.get(roomId);
978
+ if (lastRequestedAt === void 0 || lastRequestedAt > result.requestedAt) {
979
+ this.#roomVersionsLastRequestedAtByRoom.set(
980
+ roomId,
981
+ result.requestedAt
982
+ );
983
+ }
984
+ });
985
+ const signal = DerivedSignal.from(() => {
986
+ const result = resource.get();
987
+ if (result.isLoading || result.error) {
988
+ return result;
989
+ } else {
990
+ return ASYNC_OK(
991
+ "versions",
992
+ Object.values(this.historyVersions.signal.get()[roomId] ?? {})
993
+ );
994
+ }
995
+ }, shallow3);
996
+ return { signal, waitUntilLoaded: resource.waitUntilLoaded };
997
+ }
868
998
  );
869
999
  this.outputs = {
870
1000
  threadifications,
871
1001
  threads,
1002
+ loadingRoomThreads,
1003
+ loadingUserThreads,
872
1004
  notifications,
1005
+ loadingNotifications,
873
1006
  settingsByRoomId,
874
1007
  versionsByRoomId
875
1008
  };
876
1009
  autobind(this);
877
1010
  }
878
- get1_both() {
879
- return this.outputs.threadifications.get();
880
- }
881
- subscribe1_both(callback) {
882
- return this.outputs.threadifications.subscribe(callback);
883
- }
884
- get1_threads() {
885
- return this.outputs.threads.get();
886
- }
887
- subscribe1_threads(callback) {
888
- return this.outputs.threads.subscribe(callback);
889
- }
890
- get1_notifications() {
891
- return this.outputs.notifications.get();
892
- }
893
- subscribe1_notifications(callback) {
894
- return this.outputs.notifications.subscribe(callback);
895
- }
896
- get2() {
897
- return this.outputs.settingsByRoomId.get();
898
- }
899
- subscribe2(callback) {
900
- return this.outputs.settingsByRoomId.subscribe(callback);
901
- }
902
- get3() {
903
- return this.outputs.versionsByRoomId.get();
904
- }
905
- subscribe3(callback) {
906
- return this.outputs.versionsByRoomId.subscribe(callback);
907
- }
908
- /**
909
- * Returns the async result of the given query and room id. If the query is success,
910
- * then it will return the threads that match that provided query and room id.
911
- *
912
- */
913
- getRoomThreadsLoadingState(roomId, query) {
914
- const queryKey = makeRoomThreadsQueryKey(roomId, query);
915
- const paginatedResource = this.#roomThreads.get(queryKey);
916
- if (paginatedResource === void 0) {
917
- return ASYNC_LOADING;
918
- }
919
- const asyncResult = paginatedResource.get();
920
- if (asyncResult.isLoading || asyncResult.error) {
921
- return asyncResult;
922
- }
923
- const threads = this.get1_threads().threadsDB.findMany(
924
- roomId,
925
- query ?? {},
926
- "asc"
927
- );
928
- const page = asyncResult.data;
929
- return {
930
- isLoading: false,
931
- threads,
932
- hasFetchedAll: page.hasFetchedAll,
933
- isFetchingMore: page.isFetchingMore,
934
- fetchMoreError: page.fetchMoreError,
935
- fetchMore: page.fetchMore
936
- };
937
- }
938
- getUserThreadsLoadingState(query) {
939
- const queryKey = makeUserThreadsQueryKey(query);
940
- const paginatedResource = this.#userThreads.get(queryKey);
941
- if (paginatedResource === void 0) {
942
- return ASYNC_LOADING;
943
- }
944
- const asyncResult = paginatedResource.get();
945
- if (asyncResult.isLoading || asyncResult.error) {
946
- return asyncResult;
947
- }
948
- const threads = this.get1_threads().threadsDB.findMany(
949
- void 0,
950
- // Do _not_ filter by roomId
951
- query ?? {},
952
- "desc"
953
- );
954
- const page = asyncResult.data;
955
- return {
956
- isLoading: false,
957
- threads,
958
- hasFetchedAll: page.hasFetchedAll,
959
- isFetchingMore: page.isFetchingMore,
960
- fetchMoreError: page.fetchMoreError,
961
- fetchMore: page.fetchMore
962
- };
963
- }
964
- // NOTE: This will read the async result, but WILL NOT start loading at the moment!
965
- getInboxNotificationsLoadingState() {
966
- const asyncResult = this.#notifications.get();
967
- if (asyncResult.isLoading || asyncResult.error) {
968
- return asyncResult;
969
- }
970
- const page = asyncResult.data;
971
- return {
972
- isLoading: false,
973
- inboxNotifications: this.get1_notifications().sortedNotifications,
974
- hasFetchedAll: page.hasFetchedAll,
975
- isFetchingMore: page.isFetchingMore,
976
- fetchMoreError: page.fetchMoreError,
977
- fetchMore: page.fetchMore
978
- };
979
- }
980
- // NOTE: This will read the async result, but WILL NOT start loading at the moment!
981
- // XXX_vincent This should really be a derived Signal!
982
- getNotificationSettingsLoadingState(roomId) {
983
- const queryKey = makeNotificationSettingsQueryKey(roomId);
984
- const resource = this.#roomNotificationSettings.get(queryKey);
985
- if (resource === void 0) {
986
- return ASYNC_LOADING;
987
- }
988
- const asyncResult = resource.get();
989
- if (asyncResult.isLoading || asyncResult.error) {
990
- return asyncResult;
991
- }
992
- return {
993
- isLoading: false,
994
- settings: nn(this.get2()[roomId])
995
- };
996
- }
997
- getRoomVersionsLoadingState(roomId) {
998
- const queryKey = makeVersionsQueryKey(roomId);
999
- const resource = this.#roomVersions.get(queryKey);
1000
- if (resource === void 0) {
1001
- return ASYNC_LOADING;
1002
- }
1003
- const asyncResult = resource.get();
1004
- if (asyncResult.isLoading || asyncResult.error) {
1005
- return asyncResult;
1006
- }
1007
- return {
1008
- isLoading: false,
1009
- versions: Object.values(this.get3()[roomId] ?? {})
1010
- };
1011
- }
1012
- /** @internal - Only call this method from unit tests. */
1013
- force_set_versions(callback) {
1014
- batch2(() => {
1015
- this.historyVersions.force_set(callback);
1016
- this.invalidateEntireStore();
1017
- });
1018
- }
1019
- /** @internal - Only call this method from unit tests. */
1020
- force_set_notifications(callback) {
1021
- batch2(() => {
1022
- this.notifications.force_set(callback);
1023
- this.invalidateEntireStore();
1024
- });
1025
- }
1026
1011
  /**
1027
1012
  * Updates an existing inbox notification with a new value, replacing the
1028
1013
  * corresponding optimistic update.
@@ -1164,7 +1149,7 @@ var UmbrellaStore = class {
1164
1149
  }
1165
1150
  updateThreadifications(threads, notifications, deletedThreads = [], deletedNotifications = []) {
1166
1151
  batch2(() => {
1167
- this.threads.applyDelta({ newThreads: threads, deletedThreads });
1152
+ this.threads.applyDelta(threads, deletedThreads);
1168
1153
  this.notifications.applyDelta(notifications, deletedNotifications);
1169
1154
  });
1170
1155
  }
@@ -1197,39 +1182,6 @@ var UmbrellaStore = class {
1197
1182
  result.inboxNotifications.deleted
1198
1183
  );
1199
1184
  }
1200
- waitUntilNotificationsLoaded() {
1201
- return this.#notifications.waitUntilLoaded();
1202
- }
1203
- waitUntilRoomThreadsLoaded(roomId, query) {
1204
- const threadsFetcher = async (cursor) => {
1205
- const result = await this.#client[kInternal].httpClient.getThreads({
1206
- roomId,
1207
- cursor,
1208
- query
1209
- });
1210
- this.updateThreadifications(result.threads, result.inboxNotifications);
1211
- this.permissionHints.update(result.permissionHints);
1212
- const lastRequestedAt = this.#roomThreadsLastRequestedAtByRoom.get(roomId);
1213
- if (lastRequestedAt === void 0 || lastRequestedAt > result.requestedAt) {
1214
- this.#roomThreadsLastRequestedAtByRoom.set(roomId, result.requestedAt);
1215
- }
1216
- return result.nextCursor;
1217
- };
1218
- const queryKey = makeRoomThreadsQueryKey(roomId, query);
1219
- let paginatedResource = this.#roomThreads.get(queryKey);
1220
- if (paginatedResource === void 0) {
1221
- paginatedResource = new PaginatedResource(threadsFetcher);
1222
- }
1223
- paginatedResource.observable.subscribe(
1224
- () => (
1225
- // Note that the store itself does not change, but it's only vehicle at
1226
- // the moment to trigger a re-render, so we'll do a no-op update here.
1227
- this.invalidateEntireStore()
1228
- )
1229
- );
1230
- this.#roomThreads.set(queryKey, paginatedResource);
1231
- return paginatedResource.waitUntilLoaded();
1232
- }
1233
1185
  async fetchRoomThreadsDeltaUpdate(roomId, signal) {
1234
1186
  const lastRequestedAt = this.#roomThreadsLastRequestedAtByRoom.get(roomId);
1235
1187
  if (lastRequestedAt === void 0) {
@@ -1251,45 +1203,6 @@ var UmbrellaStore = class {
1251
1203
  this.#roomThreadsLastRequestedAtByRoom.set(roomId, updates.requestedAt);
1252
1204
  }
1253
1205
  }
1254
- waitUntilUserThreadsLoaded(query) {
1255
- const queryKey = makeUserThreadsQueryKey(query);
1256
- const threadsFetcher = async (cursor) => {
1257
- const result = await this.#client[kInternal].httpClient.getUserThreads_experimental({
1258
- cursor,
1259
- query
1260
- });
1261
- this.updateThreadifications(result.threads, result.inboxNotifications);
1262
- this.permissionHints.update(result.permissionHints);
1263
- if (this.#userThreadsLastRequestedAt === null) {
1264
- this.#userThreadsLastRequestedAt = result.requestedAt;
1265
- }
1266
- return result.nextCursor;
1267
- };
1268
- let paginatedResource = this.#userThreads.get(queryKey);
1269
- if (paginatedResource === void 0) {
1270
- paginatedResource = new PaginatedResource(threadsFetcher);
1271
- }
1272
- paginatedResource.observable.subscribe(
1273
- () => (
1274
- // Note that the store itself does not change, but it's only vehicle at
1275
- // the moment to trigger a re-render, so we'll do a no-op update here.
1276
- this.invalidateEntireStore()
1277
- )
1278
- );
1279
- this.#userThreads.set(queryKey, paginatedResource);
1280
- return paginatedResource.waitUntilLoaded();
1281
- }
1282
- // XXX_vincent We should really be going over all call sites, and replace this call
1283
- // with a more specific invalidation!
1284
- invalidateEntireStore() {
1285
- batch2(() => {
1286
- this.historyVersions.invalidate();
1287
- this.notifications.invalidate();
1288
- this.optimisticUpdates.invalidate();
1289
- this.permissionHints.invalidate();
1290
- this.roomNotificationSettings.invalidate();
1291
- });
1292
- }
1293
1206
  async fetchUserThreadsDeltaUpdate(signal) {
1294
1207
  const lastRequestedAt = this.#userThreadsLastRequestedAt;
1295
1208
  if (lastRequestedAt === null) {
@@ -1310,40 +1223,6 @@ var UmbrellaStore = class {
1310
1223
  );
1311
1224
  this.permissionHints.update(result.permissionHints);
1312
1225
  }
1313
- waitUntilRoomVersionsLoaded(roomId) {
1314
- const queryKey = makeVersionsQueryKey(roomId);
1315
- let resource = this.#roomVersions.get(queryKey);
1316
- if (resource === void 0) {
1317
- const versionsFetcher = async () => {
1318
- const room = this.#client.getRoom(roomId);
1319
- if (room === null) {
1320
- throw new HttpError(
1321
- `Room '${roomId}' is not available on client`,
1322
- 479
1323
- );
1324
- }
1325
- const result = await room[kInternal].listTextVersions();
1326
- this.historyVersions.update(roomId, result.versions);
1327
- const lastRequestedAt = this.#roomVersionsLastRequestedAtByRoom.get(roomId);
1328
- if (lastRequestedAt === void 0 || lastRequestedAt > result.requestedAt) {
1329
- this.#roomVersionsLastRequestedAtByRoom.set(
1330
- roomId,
1331
- result.requestedAt
1332
- );
1333
- }
1334
- };
1335
- resource = new SinglePageResource(versionsFetcher);
1336
- }
1337
- resource.observable.subscribe(
1338
- () => (
1339
- // Note that the store itself does not change, but it's only vehicle at
1340
- // the moment to trigger a re-render, so we'll do a no-op update here.
1341
- this.invalidateEntireStore()
1342
- )
1343
- );
1344
- this.#roomVersions.set(queryKey, resource);
1345
- return resource.waitUntilLoaded();
1346
- }
1347
1226
  async fetchRoomVersionsDeltaUpdate(roomId, signal) {
1348
1227
  const lastRequestedAt = this.#roomVersionsLastRequestedAtByRoom.get(roomId);
1349
1228
  if (lastRequestedAt === void 0) {
@@ -1362,33 +1241,6 @@ var UmbrellaStore = class {
1362
1241
  this.#roomVersionsLastRequestedAtByRoom.set(roomId, updates.requestedAt);
1363
1242
  }
1364
1243
  }
1365
- waitUntilRoomNotificationSettingsLoaded(roomId) {
1366
- const queryKey = makeNotificationSettingsQueryKey(roomId);
1367
- let resource = this.#roomNotificationSettings.get(queryKey);
1368
- if (resource === void 0) {
1369
- const notificationSettingsFetcher = async () => {
1370
- const room = this.#client.getRoom(roomId);
1371
- if (room === null) {
1372
- throw new HttpError(
1373
- `Room '${roomId}' is not available on client`,
1374
- 479
1375
- );
1376
- }
1377
- const result = await room.getNotificationSettings();
1378
- this.roomNotificationSettings.update(roomId, result);
1379
- };
1380
- resource = new SinglePageResource(notificationSettingsFetcher);
1381
- }
1382
- resource.observable.subscribe(
1383
- () => (
1384
- // Note that the store itself does not change, but it's only vehicle at
1385
- // the moment to trigger a re-render, so we'll do a no-op update here.
1386
- this.invalidateEntireStore()
1387
- )
1388
- );
1389
- this.#roomNotificationSettings.set(queryKey, resource);
1390
- return resource.waitUntilLoaded();
1391
- }
1392
1244
  async refreshRoomNotificationSettings(roomId, signal) {
1393
1245
  const room = nn(
1394
1246
  this.#client.getRoom(roomId),
@@ -1760,29 +1612,23 @@ function missingRoomInfoError(roomId) {
1760
1612
  `resolveRoomsInfo didn't return anything for room '${roomId}'`
1761
1613
  );
1762
1614
  }
1763
- function identity(x) {
1615
+ function identity2(x) {
1764
1616
  return x;
1765
1617
  }
1766
1618
  var _umbrellaStores = /* @__PURE__ */ new WeakMap();
1767
1619
  var _extras = /* @__PURE__ */ new WeakMap();
1768
1620
  var _bundles = /* @__PURE__ */ new WeakMap();
1769
- function selectUnreadInboxNotificationsCount(inboxNotifications) {
1770
- let count = 0;
1771
- for (const notification of inboxNotifications) {
1772
- if (notification.readAt === null || notification.readAt < notification.notifiedAt) {
1773
- count++;
1774
- }
1775
- }
1776
- return count;
1777
- }
1778
1621
  function selectorFor_useUnreadInboxNotificationsCount(result) {
1779
1622
  if (!result.inboxNotifications) {
1780
1623
  return result;
1781
1624
  }
1782
- return {
1783
- isLoading: false,
1784
- count: selectUnreadInboxNotificationsCount(result.inboxNotifications)
1785
- };
1625
+ return ASYNC_OK(
1626
+ "count",
1627
+ count(
1628
+ result.inboxNotifications,
1629
+ (n) => n.readAt === null || n.readAt < n.notifiedAt
1630
+ )
1631
+ );
1786
1632
  }
1787
1633
  function selectorFor_useUser(state, userId) {
1788
1634
  if (state === void 0 || state?.isLoading) {
@@ -1889,7 +1735,7 @@ function makeLiveblocksContextBundle(client) {
1889
1735
  const shared = createSharedContext(client);
1890
1736
  const bundle = {
1891
1737
  LiveblocksProvider: LiveblocksProvider2,
1892
- useInboxNotifications: () => useInboxNotifications_withClient(client, identity, shallow4),
1738
+ useInboxNotifications: () => useInboxNotifications_withClient(client, identity2, shallow4),
1893
1739
  useUnreadInboxNotificationsCount: () => useUnreadInboxNotificationsCount_withClient(client),
1894
1740
  useMarkInboxNotificationAsRead: useMarkInboxNotificationAsRead2,
1895
1741
  useMarkAllInboxNotificationsAsRead: useMarkAllInboxNotificationsAsRead2,
@@ -1915,9 +1761,17 @@ function makeLiveblocksContextBundle(client) {
1915
1761
  }
1916
1762
  function useInboxNotifications_withClient(client, selector, isEqual) {
1917
1763
  const { store, notificationsPoller: poller } = getLiveblocksExtrasForClient(client);
1918
- useEffect3(() => {
1919
- void store.waitUntilNotificationsLoaded();
1920
- });
1764
+ useEffect3(
1765
+ () => void store.outputs.loadingNotifications.waitUntilLoaded()
1766
+ // NOTE: Deliberately *not* using a dependency array here!
1767
+ //
1768
+ // It is important to call waitUntil on *every* render.
1769
+ // This is harmless though, on most renders, except:
1770
+ // 1. The very first render, in which case we'll want to trigger the initial page fetch.
1771
+ // 2. All other subsequent renders now "just" return the same promise (a quick operation).
1772
+ // 3. If ever the promise would fail, then after 5 seconds it would reset, and on the very
1773
+ // *next* render after that, a *new* fetch/promise will get created.
1774
+ );
1921
1775
  useEffect3(() => {
1922
1776
  poller.inc();
1923
1777
  poller.pollNowIfStale();
@@ -1925,18 +1779,16 @@ function useInboxNotifications_withClient(client, selector, isEqual) {
1925
1779
  poller.dec();
1926
1780
  };
1927
1781
  }, [poller]);
1928
- return useSyncExternalStoreWithSelector(
1929
- store.subscribe1_notifications,
1930
- store.getInboxNotificationsLoadingState,
1931
- store.getInboxNotificationsLoadingState,
1782
+ return useSignal(
1783
+ store.outputs.loadingNotifications.signal,
1932
1784
  selector,
1933
1785
  isEqual
1934
1786
  );
1935
1787
  }
1936
1788
  function useInboxNotificationsSuspense_withClient(client) {
1937
1789
  const store = getLiveblocksExtrasForClient(client).store;
1938
- use(store.waitUntilNotificationsLoaded());
1939
- const result = useInboxNotifications_withClient(client, identity, shallow4);
1790
+ use(store.outputs.loadingNotifications.waitUntilLoaded());
1791
+ const result = useInboxNotifications_withClient(client, identity2, shallow4);
1940
1792
  assert(!result.error, "Did not expect error");
1941
1793
  assert(!result.isLoading, "Did not expect loading");
1942
1794
  return result;
@@ -1950,7 +1802,7 @@ function useUnreadInboxNotificationsCount_withClient(client) {
1950
1802
  }
1951
1803
  function useUnreadInboxNotificationsCountSuspense_withClient(client) {
1952
1804
  const store = getLiveblocksExtrasForClient(client).store;
1953
- use(store.waitUntilNotificationsLoaded());
1805
+ use(store.outputs.loadingNotifications.waitUntilLoaded());
1954
1806
  const result = useUnreadInboxNotificationsCount_withClient(client);
1955
1807
  assert(!result.isLoading, "Did not expect loading");
1956
1808
  assert(!result.error, "Did not expect error");
@@ -2042,34 +1894,31 @@ function useDeleteAllInboxNotifications_withClient(client) {
2042
1894
  }
2043
1895
  function useInboxNotificationThread_withClient(client, inboxNotificationId) {
2044
1896
  const { store } = getLiveblocksExtrasForClient(client);
2045
- const getter = store.get1_both;
2046
- const selector = useCallback2(
2047
- (state) => {
2048
- const inboxNotification = state.notificationsById[inboxNotificationId] ?? raise(`Inbox notification with ID "${inboxNotificationId}" not found`);
2049
- if (inboxNotification.kind !== "thread") {
2050
- raise(
2051
- `Inbox notification with ID "${inboxNotificationId}" is not of kind "thread"`
1897
+ return useSignal(
1898
+ store.outputs.threadifications,
1899
+ useCallback2(
1900
+ (state) => {
1901
+ const inboxNotification = state.notificationsById[inboxNotificationId] ?? raise(
1902
+ `Inbox notification with ID "${inboxNotificationId}" not found`
2052
1903
  );
2053
- }
2054
- const thread = state.threadsDB.get(inboxNotification.threadId) ?? raise(
2055
- `Thread with ID "${inboxNotification.threadId}" not found, this inbox notification might not be of kind "thread"`
2056
- );
2057
- return thread;
2058
- },
2059
- [inboxNotificationId]
2060
- );
2061
- return useSyncExternalStoreWithSelector(
2062
- store.subscribe1_both,
2063
- // Re-evaluate if we need to update any time the notification changes over time
2064
- getter,
2065
- getter,
2066
- selector
1904
+ if (inboxNotification.kind !== "thread") {
1905
+ raise(
1906
+ `Inbox notification with ID "${inboxNotificationId}" is not of kind "thread"`
1907
+ );
1908
+ }
1909
+ const thread = state.threadsDB.get(inboxNotification.threadId) ?? raise(
1910
+ `Thread with ID "${inboxNotification.threadId}" not found, this inbox notification might not be of kind "thread"`
1911
+ );
1912
+ return thread;
1913
+ },
1914
+ [inboxNotificationId]
1915
+ )
2067
1916
  );
2068
1917
  }
2069
1918
  function useUser_withClient(client, userId) {
2070
1919
  const usersStore = client[kInternal2].usersStore;
2071
1920
  const getUserState = useCallback2(
2072
- () => usersStore.getState(userId),
1921
+ () => usersStore.getItemState(userId),
2073
1922
  [usersStore, userId]
2074
1923
  );
2075
1924
  const selector = useCallback2(
@@ -2083,20 +1932,29 @@ function useUser_withClient(client, userId) {
2083
1932
  selector,
2084
1933
  shallow4
2085
1934
  );
2086
- useEffect3(() => {
2087
- void usersStore.get(userId);
2088
- }, [usersStore, userId, result]);
1935
+ useEffect3(
1936
+ () => void usersStore.enqueue(userId)
1937
+ // NOTE: Deliberately *not* using a dependency array here!
1938
+ //
1939
+ // It is important to call usersStore.enqueue on *every* render.
1940
+ // This is harmless though, on most renders, except:
1941
+ // 1. The very first render, in which case we'll want to trigger evaluation
1942
+ // of the userId.
1943
+ // 2. All other subsequent renders now are a no-op (from the implementation
1944
+ // of .enqueue)
1945
+ // 3. If ever the userId gets invalidated, the user would be fetched again.
1946
+ );
2089
1947
  return result;
2090
1948
  }
2091
1949
  function useUserSuspense_withClient(client, userId) {
2092
1950
  const usersStore = client[kInternal2].usersStore;
2093
1951
  const getUserState = useCallback2(
2094
- () => usersStore.getState(userId),
1952
+ () => usersStore.getItemState(userId),
2095
1953
  [usersStore, userId]
2096
1954
  );
2097
1955
  const userState = getUserState();
2098
1956
  if (!userState || userState.isLoading) {
2099
- throw usersStore.get(userId);
1957
+ throw usersStore.enqueue(userId);
2100
1958
  }
2101
1959
  if (userState.error) {
2102
1960
  throw userState.error;
@@ -2121,7 +1979,7 @@ function useUserSuspense_withClient(client, userId) {
2121
1979
  function useRoomInfo_withClient(client, roomId) {
2122
1980
  const roomsInfoStore = client[kInternal2].roomsInfoStore;
2123
1981
  const getRoomInfoState = useCallback2(
2124
- () => roomsInfoStore.getState(roomId),
1982
+ () => roomsInfoStore.getItemState(roomId),
2125
1983
  [roomsInfoStore, roomId]
2126
1984
  );
2127
1985
  const selector = useCallback2(
@@ -2135,20 +1993,29 @@ function useRoomInfo_withClient(client, roomId) {
2135
1993
  selector,
2136
1994
  shallow4
2137
1995
  );
2138
- useEffect3(() => {
2139
- void roomsInfoStore.get(roomId);
2140
- }, [roomsInfoStore, roomId, result]);
1996
+ useEffect3(
1997
+ () => void roomsInfoStore.enqueue(roomId)
1998
+ // NOTE: Deliberately *not* using a dependency array here!
1999
+ //
2000
+ // It is important to call roomsInfoStore.enqueue on *every* render.
2001
+ // This is harmless though, on most renders, except:
2002
+ // 1. The very first render, in which case we'll want to trigger evaluation
2003
+ // of the roomId.
2004
+ // 2. All other subsequent renders now are a no-op (from the implementation
2005
+ // of .enqueue)
2006
+ // 3. If ever the roomId gets invalidated, the room info would be fetched again.
2007
+ );
2141
2008
  return result;
2142
2009
  }
2143
2010
  function useRoomInfoSuspense_withClient(client, roomId) {
2144
2011
  const roomsInfoStore = client[kInternal2].roomsInfoStore;
2145
2012
  const getRoomInfoState = useCallback2(
2146
- () => roomsInfoStore.getState(roomId),
2013
+ () => roomsInfoStore.getItemState(roomId),
2147
2014
  [roomsInfoStore, roomId]
2148
2015
  );
2149
2016
  const roomInfoState = getRoomInfoState();
2150
2017
  if (!roomInfoState || roomInfoState.isLoading) {
2151
- throw roomsInfoStore.get(roomId);
2018
+ throw roomsInfoStore.enqueue(roomId);
2152
2019
  }
2153
2020
  if (roomInfoState.error) {
2154
2021
  throw roomInfoState.error;
@@ -2243,17 +2110,12 @@ function LiveblocksProvider(props) {
2243
2110
  function createLiveblocksContext(client) {
2244
2111
  return getOrCreateContextBundle(client);
2245
2112
  }
2246
- function useUserThreads_experimental(options = {
2247
- query: {
2248
- metadata: {}
2249
- }
2250
- }) {
2113
+ function useUserThreads_experimental(options = {}) {
2251
2114
  const client = useClient();
2252
2115
  const { store, userThreadsPoller: poller } = getLiveblocksExtrasForClient(client);
2116
+ const queryKey = makeUserThreadsQueryKey(options.query);
2253
2117
  useEffect3(
2254
- () => {
2255
- void store.waitUntilUserThreadsLoaded(options.query);
2256
- }
2118
+ () => void store.outputs.loadingUserThreads.getOrCreate(queryKey).waitUntilLoaded()
2257
2119
  // NOTE: Deliberately *not* using a dependency array here!
2258
2120
  //
2259
2121
  // It is important to call waitUntil on *every* render.
@@ -2270,34 +2132,22 @@ function useUserThreads_experimental(options = {
2270
2132
  poller.dec();
2271
2133
  };
2272
2134
  }, [poller]);
2273
- const getter = useCallback2(
2274
- () => store.getUserThreadsLoadingState(options.query),
2275
- [store, options.query]
2276
- );
2277
- return useSyncExternalStoreWithSelector(
2278
- store.subscribe1_threads,
2279
- getter,
2280
- getter,
2281
- identity,
2282
- shallow2
2283
- // NOTE: Using 2-level-deep shallow check here, because the result of selectThreads() is not stable!
2135
+ return useSignal(
2136
+ store.outputs.loadingUserThreads.getOrCreate(queryKey).signal
2284
2137
  );
2285
2138
  }
2286
- function useUserThreadsSuspense_experimental(options = {
2287
- query: {
2288
- metadata: {}
2289
- }
2290
- }) {
2139
+ function useUserThreadsSuspense_experimental(options = {}) {
2291
2140
  const client = useClient();
2292
2141
  const { store } = getLiveblocksExtrasForClient(client);
2293
- use(store.waitUntilUserThreadsLoaded(options.query));
2142
+ const queryKey = makeUserThreadsQueryKey(options.query);
2143
+ use(store.outputs.loadingUserThreads.getOrCreate(queryKey).waitUntilLoaded());
2294
2144
  const result = useUserThreads_experimental(options);
2295
2145
  assert(!result.error, "Did not expect error");
2296
2146
  assert(!result.isLoading, "Did not expect loading");
2297
2147
  return result;
2298
2148
  }
2299
2149
  function useInboxNotifications() {
2300
- return useInboxNotifications_withClient(useClient(), identity, shallow4);
2150
+ return useInboxNotifications_withClient(useClient(), identity2, shallow4);
2301
2151
  }
2302
2152
  function useInboxNotificationsSuspense() {
2303
2153
  return useInboxNotificationsSuspense_withClient(useClient());
@@ -2484,18 +2334,6 @@ var UpdateNotificationSettingsError = class extends Error {
2484
2334
  }
2485
2335
  };
2486
2336
 
2487
- // src/use-signal.ts
2488
- var identity2 = (value) => value;
2489
- function useSignal(signal, selector, isEqual) {
2490
- return useSyncExternalStoreWithSelector(
2491
- signal.subscribe,
2492
- signal.get,
2493
- signal.get,
2494
- selector ?? identity2,
2495
- isEqual
2496
- );
2497
- }
2498
-
2499
2337
  // src/room.tsx
2500
2338
  import { shallow as shallow5 } from "@liveblocks/client";
2501
2339
  import {
@@ -2503,10 +2341,11 @@ import {
2503
2341
  console as console3,
2504
2342
  createCommentId,
2505
2343
  createThreadId,
2344
+ DefaultMap as DefaultMap2,
2506
2345
  errorIf,
2507
2346
  HttpError as HttpError2,
2508
2347
  kInternal as kInternal3,
2509
- makeEventSource as makeEventSource2,
2348
+ makeEventSource,
2510
2349
  makePoller as makePoller2,
2511
2350
  ServerMsgCode
2512
2351
  } from "@liveblocks/core";
@@ -2628,7 +2467,7 @@ function getRoomExtrasForClient(client) {
2628
2467
  }
2629
2468
  function makeRoomExtrasForClient(client) {
2630
2469
  const store = getUmbrellaStoreForClient(client);
2631
- const commentsErrorEventSource = makeEventSource2();
2470
+ const commentsErrorEventSource = makeEventSource();
2632
2471
  function onMutationFailure(innerError, optimisticId, createPublicError) {
2633
2472
  store.optimisticUpdates.remove(optimisticId);
2634
2473
  if (innerError instanceof HttpError2) {
@@ -2642,73 +2481,59 @@ function makeRoomExtrasForClient(client) {
2642
2481
  }
2643
2482
  throw innerError;
2644
2483
  }
2645
- const threadsPollersByRoomId = /* @__PURE__ */ new Map();
2646
- const versionsPollersByRoomId = /* @__PURE__ */ new Map();
2647
- const roomNotificationSettingsPollersByRoomId = /* @__PURE__ */ new Map();
2648
- function getOrCreateThreadsPollerForRoomId(roomId) {
2649
- let poller = threadsPollersByRoomId.get(roomId);
2650
- if (!poller) {
2651
- poller = makePoller2(
2652
- async (signal) => {
2653
- try {
2654
- return await store.fetchRoomThreadsDeltaUpdate(roomId, signal);
2655
- } catch (err) {
2656
- console3.warn(`Polling new threads for '${roomId}' failed: ${String(err)}`);
2657
- throw err;
2658
- }
2659
- },
2660
- config.ROOM_THREADS_POLL_INTERVAL,
2661
- { maxStaleTimeMs: config.ROOM_THREADS_MAX_STALE_TIME }
2662
- );
2663
- threadsPollersByRoomId.set(roomId, poller);
2664
- }
2665
- return poller;
2666
- }
2667
- function getOrCreateVersionsPollerForRoomId(roomId) {
2668
- let poller = versionsPollersByRoomId.get(roomId);
2669
- if (!poller) {
2670
- poller = makePoller2(
2671
- async (signal) => {
2672
- try {
2673
- return await store.fetchRoomVersionsDeltaUpdate(roomId, signal);
2674
- } catch (err) {
2675
- console3.warn(`Polling new history versions for '${roomId}' failed: ${String(err)}`);
2676
- throw err;
2677
- }
2678
- },
2679
- config.HISTORY_VERSIONS_POLL_INTERVAL,
2680
- { maxStaleTimeMs: config.HISTORY_VERSIONS_MAX_STALE_TIME }
2681
- );
2682
- versionsPollersByRoomId.set(roomId, poller);
2683
- }
2684
- return poller;
2685
- }
2686
- function getOrCreateNotificationsSettingsPollerForRoomId(roomId) {
2687
- let poller = roomNotificationSettingsPollersByRoomId.get(roomId);
2688
- if (!poller) {
2689
- poller = makePoller2(
2690
- async (signal) => {
2691
- try {
2692
- return await store.refreshRoomNotificationSettings(roomId, signal);
2693
- } catch (err) {
2694
- console3.warn(`Polling notification settings for '${roomId}' failed: ${String(err)}`);
2695
- throw err;
2696
- }
2697
- },
2698
- config.NOTIFICATION_SETTINGS_POLL_INTERVAL,
2699
- { maxStaleTimeMs: config.NOTIFICATION_SETTINGS_MAX_STALE_TIME }
2700
- );
2701
- roomNotificationSettingsPollersByRoomId.set(roomId, poller);
2702
- }
2703
- return poller;
2704
- }
2484
+ const threadsPollersByRoomId = new DefaultMap2(
2485
+ (roomId) => makePoller2(
2486
+ async (signal) => {
2487
+ try {
2488
+ return await store.fetchRoomThreadsDeltaUpdate(roomId, signal);
2489
+ } catch (err) {
2490
+ console3.warn(`Polling new threads for '${roomId}' failed: ${String(err)}`);
2491
+ throw err;
2492
+ }
2493
+ },
2494
+ config.ROOM_THREADS_POLL_INTERVAL,
2495
+ { maxStaleTimeMs: config.ROOM_THREADS_MAX_STALE_TIME }
2496
+ )
2497
+ );
2498
+ const versionsPollersByRoomId = new DefaultMap2(
2499
+ (roomId) => makePoller2(
2500
+ async (signal) => {
2501
+ try {
2502
+ return await store.fetchRoomVersionsDeltaUpdate(roomId, signal);
2503
+ } catch (err) {
2504
+ console3.warn(`Polling new history versions for '${roomId}' failed: ${String(err)}`);
2505
+ throw err;
2506
+ }
2507
+ },
2508
+ config.HISTORY_VERSIONS_POLL_INTERVAL,
2509
+ { maxStaleTimeMs: config.HISTORY_VERSIONS_MAX_STALE_TIME }
2510
+ )
2511
+ );
2512
+ const roomNotificationSettingsPollersByRoomId = new DefaultMap2(
2513
+ (roomId) => makePoller2(
2514
+ async (signal) => {
2515
+ try {
2516
+ return await store.refreshRoomNotificationSettings(roomId, signal);
2517
+ } catch (err) {
2518
+ console3.warn(`Polling notification settings for '${roomId}' failed: ${String(err)}`);
2519
+ throw err;
2520
+ }
2521
+ },
2522
+ config.NOTIFICATION_SETTINGS_POLL_INTERVAL,
2523
+ { maxStaleTimeMs: config.NOTIFICATION_SETTINGS_MAX_STALE_TIME }
2524
+ )
2525
+ );
2705
2526
  return {
2706
2527
  store,
2707
2528
  commentsErrorEventSource: commentsErrorEventSource.observable,
2708
2529
  onMutationFailure,
2709
- getOrCreateThreadsPollerForRoomId,
2710
- getOrCreateVersionsPollerForRoomId,
2711
- getOrCreateNotificationsSettingsPollerForRoomId
2530
+ getOrCreateThreadsPollerForRoomId: threadsPollersByRoomId.getOrCreate.bind(
2531
+ threadsPollersByRoomId
2532
+ ),
2533
+ getOrCreateVersionsPollerForRoomId: versionsPollersByRoomId.getOrCreate.bind(versionsPollersByRoomId),
2534
+ getOrCreateNotificationsSettingsPollerForRoomId: roomNotificationSettingsPollersByRoomId.getOrCreate.bind(
2535
+ roomNotificationSettingsPollersByRoomId
2536
+ )
2712
2537
  };
2713
2538
  }
2714
2539
  function makeRoomContextBundle(client) {
@@ -2852,7 +2677,7 @@ function RoomProviderInner(props) {
2852
2677
  "RoomProvider id property is required. For more information: https://liveblocks.io/docs/errors/liveblocks-react/RoomProvider-id-property-is-required"
2853
2678
  );
2854
2679
  }
2855
- if (!isString(roomId)) {
2680
+ if (typeof roomId !== "string") {
2856
2681
  throw new Error("RoomProvider id property should be a string.");
2857
2682
  }
2858
2683
  const majorReactVersion = parseInt(reactVersion) || 1;
@@ -2887,7 +2712,7 @@ function RoomProviderInner(props) {
2887
2712
  return;
2888
2713
  }
2889
2714
  const { thread, inboxNotification: maybeNotification } = info;
2890
- const existingThread = store.get1_threads().threadsDB.getEvenIfDeleted(message.threadId);
2715
+ const existingThread = store.outputs.threads.get().getEvenIfDeleted(message.threadId);
2891
2716
  switch (message.type) {
2892
2717
  case ServerMsgCode.COMMENT_EDITED:
2893
2718
  case ServerMsgCode.THREAD_METADATA_UPDATED:
@@ -3258,18 +3083,15 @@ function useMutation(callback, deps) {
3258
3083
  [room, ...deps]
3259
3084
  );
3260
3085
  }
3261
- function useThreads(options = {
3262
- query: { metadata: {} }
3263
- }) {
3086
+ function useThreads(options = {}) {
3264
3087
  const { scrollOnLoad = true } = options;
3265
3088
  const client = useClient();
3266
3089
  const room = useRoom();
3267
3090
  const { store, getOrCreateThreadsPollerForRoomId } = getRoomExtrasForClient(client);
3091
+ const queryKey = makeRoomThreadsQueryKey(room.id, options.query);
3268
3092
  const poller = getOrCreateThreadsPollerForRoomId(room.id);
3269
3093
  useEffect5(
3270
- () => {
3271
- void store.waitUntilRoomThreadsLoaded(room.id, options.query);
3272
- }
3094
+ () => void store.outputs.loadingRoomThreads.getOrCreate(queryKey).waitUntilLoaded()
3273
3095
  // NOTE: Deliberately *not* using a dependency array here!
3274
3096
  //
3275
3097
  // It is important to call waitUntil on *every* render.
@@ -3284,20 +3106,11 @@ function useThreads(options = {
3284
3106
  poller.pollNowIfStale();
3285
3107
  return () => poller.dec();
3286
3108
  }, [poller]);
3287
- const getter = useCallback3(
3288
- () => store.getRoomThreadsLoadingState(room.id, options.query),
3289
- [store, room.id, options.query]
3109
+ const result = useSignal(
3110
+ store.outputs.loadingRoomThreads.getOrCreate(queryKey).signal
3290
3111
  );
3291
- const state = useSyncExternalStoreWithSelector(
3292
- store.subscribe1_threads,
3293
- getter,
3294
- getter,
3295
- identity3,
3296
- shallow2
3297
- // NOTE: Using 2-level-deep shallow check here, because the result of selectThreads() is not stable!
3298
- );
3299
- useScrollToCommentOnLoadEffect(scrollOnLoad, state);
3300
- return state;
3112
+ useScrollToCommentOnLoadEffect(scrollOnLoad, result);
3113
+ return result;
3301
3114
  }
3302
3115
  function useCommentsErrorListener(callback) {
3303
3116
  const client = useClient();
@@ -3385,7 +3198,7 @@ function useDeleteRoomThread(roomId) {
3385
3198
  (threadId) => {
3386
3199
  const { store, onMutationFailure } = getRoomExtrasForClient(client);
3387
3200
  const userId = getCurrentUserId(client);
3388
- const existing = store.get1_threads().threadsDB.get(threadId);
3201
+ const existing = store.outputs.threads.get().get(threadId);
3389
3202
  if (existing?.comments?.[0]?.userId !== userId) {
3390
3203
  throw new Error("Only the thread creator can delete the thread");
3391
3204
  }
@@ -3503,7 +3316,7 @@ function useEditRoomComment(roomId) {
3503
3316
  ({ threadId, commentId, body, attachments }) => {
3504
3317
  const editedAt = /* @__PURE__ */ new Date();
3505
3318
  const { store, onMutationFailure } = getRoomExtrasForClient(client);
3506
- const existing = store.get1_threads().threadsDB.getEvenIfDeleted(threadId);
3319
+ const existing = store.outputs.threads.get().getEvenIfDeleted(threadId);
3507
3320
  if (existing === void 0) {
3508
3321
  console3.warn(
3509
3322
  `Internal unexpected behavior. Cannot edit comment in thread "${threadId}" because the thread does not exist in the cache.`
@@ -3680,7 +3493,7 @@ function useMarkRoomThreadAsRead(roomId) {
3680
3493
  (threadId) => {
3681
3494
  const { store, onMutationFailure } = getRoomExtrasForClient(client);
3682
3495
  const inboxNotification = Object.values(
3683
- store.get1_notifications().notificationsById
3496
+ store.outputs.notifications.get().notificationsById
3684
3497
  ).find(
3685
3498
  (inboxNotification2) => inboxNotification2.kind === "thread" && inboxNotification2.threadId === threadId
3686
3499
  );
@@ -3818,9 +3631,7 @@ function useRoomNotificationSettings() {
3818
3631
  const { store, getOrCreateNotificationsSettingsPollerForRoomId } = getRoomExtrasForClient(client);
3819
3632
  const poller = getOrCreateNotificationsSettingsPollerForRoomId(room.id);
3820
3633
  useEffect5(
3821
- () => {
3822
- void store.waitUntilRoomNotificationSettingsLoaded(room.id);
3823
- }
3634
+ () => void store.outputs.settingsByRoomId.getOrCreate(room.id).waitUntilLoaded()
3824
3635
  // NOTE: Deliberately *not* using a dependency array here!
3825
3636
  //
3826
3637
  // It is important to call waitUntil on *every* render.
@@ -3837,16 +3648,8 @@ function useRoomNotificationSettings() {
3837
3648
  poller.dec();
3838
3649
  };
3839
3650
  }, [poller]);
3840
- const getter = useCallback3(
3841
- () => store.getNotificationSettingsLoadingState(room.id),
3842
- [store, room.id]
3843
- );
3844
- const settings = useSyncExternalStoreWithSelector(
3845
- store.subscribe2,
3846
- getter,
3847
- getter,
3848
- identity3,
3849
- shallow2
3651
+ const settings = useSignal(
3652
+ store.outputs.settingsByRoomId.getOrCreate(room.id).signal
3850
3653
  );
3851
3654
  return useMemo3(() => {
3852
3655
  return [settings, updateRoomNotificationSettings];
@@ -3856,7 +3659,7 @@ function useRoomNotificationSettingsSuspense() {
3856
3659
  const client = useClient();
3857
3660
  const store = getRoomExtrasForClient(client).store;
3858
3661
  const room = useRoom();
3859
- use(store.waitUntilRoomNotificationSettingsLoaded(room.id));
3662
+ use(store.outputs.settingsByRoomId.getOrCreate(room.id).waitUntilLoaded());
3860
3663
  const [settings, updateRoomNotificationSettings] = useRoomNotificationSettings();
3861
3664
  assert2(!settings.error, "Did not expect error");
3862
3665
  assert2(!settings.isLoading, "Did not expect loading");
@@ -3903,14 +3706,8 @@ function useHistoryVersions() {
3903
3706
  poller.pollNowIfStale();
3904
3707
  return () => poller.dec();
3905
3708
  }, [poller]);
3906
- const getter = useCallback3(
3907
- () => store.getRoomVersionsLoadingState(room.id),
3908
- [store, room.id]
3909
- );
3910
3709
  useEffect5(
3911
- () => {
3912
- void store.waitUntilRoomVersionsLoaded(room.id);
3913
- }
3710
+ () => void store.outputs.versionsByRoomId.getOrCreate(room.id).waitUntilLoaded()
3914
3711
  // NOTE: Deliberately *not* using a dependency array here!
3915
3712
  //
3916
3713
  // It is important to call waitUntil on *every* render.
@@ -3920,20 +3717,13 @@ function useHistoryVersions() {
3920
3717
  // 3. If ever the promise would fail, then after 5 seconds it would reset, and on the very
3921
3718
  // *next* render after that, a *new* fetch/promise will get created.
3922
3719
  );
3923
- const state = useSyncExternalStoreWithSelector(
3924
- store.subscribe3,
3925
- getter,
3926
- getter,
3927
- identity3,
3928
- shallow2
3929
- );
3930
- return state;
3720
+ return useSignal(store.outputs.versionsByRoomId.getOrCreate(room.id).signal);
3931
3721
  }
3932
3722
  function useHistoryVersionsSuspense() {
3933
3723
  const client = useClient();
3934
3724
  const room = useRoom();
3935
3725
  const store = getRoomExtrasForClient(client).store;
3936
- use(store.waitUntilRoomVersionsLoaded(room.id));
3726
+ use(store.outputs.versionsByRoomId.getOrCreate(room.id).waitUntilLoaded());
3937
3727
  const result = useHistoryVersions();
3938
3728
  assert2(!result.error, "Did not expect error");
3939
3729
  assert2(!result.isLoading, "Did not expect loading");
@@ -4020,13 +3810,12 @@ function useStorageStatusSuspense(options) {
4020
3810
  useSuspendUntilStorageReady();
4021
3811
  return useStorageStatus(options);
4022
3812
  }
4023
- function useThreadsSuspense(options = {
4024
- query: { metadata: {} }
4025
- }) {
3813
+ function useThreadsSuspense(options = {}) {
4026
3814
  const client = useClient();
4027
3815
  const room = useRoom();
4028
3816
  const { store } = getRoomExtrasForClient(client);
4029
- use(store.waitUntilRoomThreadsLoaded(room.id, options.query));
3817
+ const queryKey = makeRoomThreadsQueryKey(room.id, options.query);
3818
+ use(store.outputs.loadingRoomThreads.getOrCreate(queryKey).waitUntilLoaded());
4030
3819
  const result = useThreads(options);
4031
3820
  assert2(!result.error, "Did not expect error");
4032
3821
  assert2(!result.isLoading, "Did not expect loading");
@@ -4053,11 +3842,11 @@ function useRoomAttachmentUrl(attachmentId, roomId) {
4053
3842
  const client = useClient();
4054
3843
  const store = client[kInternal3].httpClient.getOrCreateAttachmentUrlsStore(roomId);
4055
3844
  const getAttachmentUrlState = useCallback3(
4056
- () => store.getState(attachmentId),
3845
+ () => store.getItemState(attachmentId),
4057
3846
  [store, attachmentId]
4058
3847
  );
4059
3848
  useEffect5(() => {
4060
- void store.get(attachmentId);
3849
+ void store.enqueue(attachmentId);
4061
3850
  }, [store, attachmentId]);
4062
3851
  return useSyncExternalStoreWithSelector(
4063
3852
  store.subscribe,
@@ -4071,12 +3860,12 @@ function useAttachmentUrlSuspense(attachmentId) {
4071
3860
  const room = useRoom();
4072
3861
  const { attachmentUrlsStore } = room[kInternal3];
4073
3862
  const getAttachmentUrlState = useCallback3(
4074
- () => attachmentUrlsStore.getState(attachmentId),
3863
+ () => attachmentUrlsStore.getItemState(attachmentId),
4075
3864
  [attachmentUrlsStore, attachmentId]
4076
3865
  );
4077
3866
  const attachmentUrlState = getAttachmentUrlState();
4078
3867
  if (!attachmentUrlState || attachmentUrlState.isLoading) {
4079
- throw attachmentUrlsStore.get(attachmentId);
3868
+ throw attachmentUrlsStore.enqueue(attachmentId);
4080
3869
  }
4081
3870
  if (attachmentUrlState.error) {
4082
3871
  throw attachmentUrlState.error;
@@ -4095,12 +3884,13 @@ function useAttachmentUrlSuspense(attachmentId) {
4095
3884
  error: void 0
4096
3885
  };
4097
3886
  }
3887
+ var NO_PERMISSIONS = /* @__PURE__ */ new Set();
4098
3888
  function useRoomPermissions(roomId) {
4099
3889
  const client = useClient();
4100
3890
  const store = getRoomExtrasForClient(client).store;
4101
3891
  return useSignal(
4102
3892
  store.permissionHints.signal,
4103
- (hints) => hints[roomId] ?? /* @__PURE__ */ new Set()
3893
+ (hints) => hints.get(roomId) ?? NO_PERMISSIONS
4104
3894
  );
4105
3895
  }
4106
3896
  function createRoomContext(client) {
@@ -4149,6 +3939,7 @@ export {
4149
3939
  RoomContext,
4150
3940
  useRoomOrNull,
4151
3941
  useSyncExternalStoreWithSelector,
3942
+ useSignal,
4152
3943
  ClientContext,
4153
3944
  getUmbrellaStoreForClient,
4154
3945
  useClientOrNull,
@@ -4172,7 +3963,6 @@ export {
4172
3963
  _useUserThreadsSuspense_experimental,
4173
3964
  useSyncStatus,
4174
3965
  CreateThreadError,
4175
- useSignal,
4176
3966
  useStatus,
4177
3967
  useReportTextEditor,
4178
3968
  useYjsProvider,
@@ -4250,4 +4040,4 @@ export {
4250
4040
  _useStorageRoot,
4251
4041
  _useUpdateMyPresence
4252
4042
  };
4253
- //# sourceMappingURL=chunk-GMKB6I6V.mjs.map
4043
+ //# sourceMappingURL=chunk-FQKGWA7O.mjs.map